写在前面
通信加密的方案最终形态是HTTPS通信协议
关于密钥
RSA密钥交换+AES对等加密:
服务端:
提供接口,供客户端获取RSA公钥
获取client请求头中经RSA公钥加密过的AES密钥,并使用RSA私钥解密得到AES密钥
使用AES密钥解密请求数据
使用AES密钥加密请求数据
客户端:
生成AES密钥
使用AES密钥加密请求数据
使用RSA公钥加密AES密钥,并将结果添加到httphead中
使用AES密钥解密返回数据
密钥生成策略:
- 系统使用一套固定的RSA密钥,公钥进行存放到客户端,避免公钥通信,后期服务端密钥变更受客户端约束,公钥无过期机制,密钥泄露无法,快速更新密钥。
-
每次请求前获取最新公钥,客户端通过上次请求公钥的过期时间确定密钥是否过期,服务端密钥对,并在监控过期前获取下次需要的密钥
- 不同客户端获取的RSA公钥相同
- 需要多一个http公钥请求
- 服务端需要处理过期机制,复杂
- 客户端登录前获取一次
- 每次请求获取最新公钥
单向/双向加密
http请求头中添加的一个参数,用以标识加密方式
- 不同客户端获取的RSA公钥相同
- EncryptType可以修改为一个唯一约定的值,建议使用一个固定的uuid值,避免与业务处理逻辑参数冲突
-
EncryptType=req
标识只加密请求参数,服务端返回不加密
-
EncryptType=resp
标识请求参数不加密,服务端返回加密
-
EncryptType=both
标识同时加密请求参数和服务端返回
-
EncryptType=none时
标识不处理请求参数和服务端返回
-
EncryptType不存在时
如果httphead中有AES密钥加密后的值,则默认为EncryptType=both,否则为EncryptType= none
客户端:
- 使用密钥加密请求数据为,请求参数串为:data={encryptdata},encryptdata为请求参数串的密文
- 在请求头中添加参数:encrypttype=req/resp/both,encrypt标识请求参数加密,供后台服务端识别并进行解密
-
接收到服务端返回后,如果需要客户端进行解密,使用密钥进行解密,并交下游业务处理类处理。
服务端:
- 在Controller预处理器中,检查encrpt的值,对为true的请求,使用密钥对请求密文进行解密,供下游Controller进行进一步的业务处理。
-
在Controller后处理器中,对Controller进行业务处理后,根据预处理请求中的encrypt加密标识,将返回结果中的实体类、Json对象、文本进行加密后返回。
具体实现:
RSA及AES的密钥生成,加密,解密方法实现
公共代码库
获取公钥接口:
Return {rsapublickey:'{content}’,period:’7200′,uuid:1234-2343-1344-2343};
与Redis整合实现密钥存储过期机制:
存放密钥对:
Set timeout = 7260;//两小时过期,针对可能出现的临界时间请求,多设置60秒时间,避免服务端返回时获取密钥失败
Redsi.put(uuid,‘RSAPublicKey’,rsaPublicKey,timeout);
Redsi.put(uuid,‘RSAPrivateKey’,rsaPrivatelicKey,timeout);
与SpringMVC整合实现:
继承HandlerInterceptorAdapter实现Springmvc预处理解密和后处理加密
预处理加密:
Request.getHeader(“encryptKey”)
Request.getHeader(“encryptType”)
Uuid = Request.getHeader(“encryptUuid”);
Request.getPrameter(“encryptData”);
Decode aeskey = RSADecode(ecnryptkey,Redis.get(“RSAPrivateKey”)+uuid);
Data = AESDecode(encryptData,aesKey);
后处理解密:
Request.getHeader(“encryptKey”)
Request.getHeader(“encryptType”)
Uuid = Request.getHeader(“encryptUuid”);
Request.getPrameter(“encryptData”);
Decode aeskey = RSADecode(ecnryptkey,Redis.get(“RSAPrivateKey”)+uuid);
Data = AESDecode(data,aesKey);
存在的问题:
无法处理rest风格url中的参数