Web-加密算法应用目录
一、接口加密技术
参考:第三方接口签名加密
在调用第三方接口时,需要用签名去验证安全性,对数据加密等,这个是在双方都已经定好密钥/(公私钥)、加密算法、加密字段的情况下,这里用的是对称加密和MD5。
如果要涉及到传输密钥时,会用到https,这个比较复杂了,其核心就是如何安全的在网络间让服务端和客户端商量一个密钥。
1.1 背景简介
http是明文传输,对于一些敏感信息,容易被人盗取、篡改,所以一般不会进行明文传输需要加密。像你在七牛云、微信小程序的中注册成功后会有发给你密钥,然后就是接口的调用,为了保证安全就需要加密。这里的加密的前提是需要已经约定好对应的密钥,对于非对称的就是私钥和公钥。
加密过程一般是对字段进行:
签名生成数字签名
,业务数据进行加密
,时间戳等
,
因此,除了业务数据字段,在发送的消息体中一般还会有如下字段:
{
"appid": "12",#调用方身份ID,接口提供方来识别不同的调用者,该参数是API基本规范的一部分,请详见API公共规范,对加密其实没什么大的作用。
"nonce": "YSBuLCgvmdSdB3oS", #随机字符串,用于生成更可靠安全的签名,不一定会有
"timestamp": 1552900602924, #时间戳
"signature": "T21XNKgXE3siFGPJimjbtlolVQw4Dy1IHwGDSBOnSN5mUPduljYmo", #签名,用于验证的可靠性的
"data": "+TNHbffwj9PzubtX65PtVg==", #加密后的业务数据
}
时间戳的作用:
接口提供方接到响应后,判断时间戳是不是在有效时间内(这个时间间隔根据你的安全范围可以是10分钟,5分钟,20秒等,过期失效,前提是需要保证接口提供方和调用方的服务器时间为准确的网络同步时间)
1.2 数字签名
数字签名算法要求能够验证数据完整性
、认证数据来源
、抗否认
的作用,数字签名的生成会用到摘要算法(不可逆算法/hash算法)、对称加密/非对称加密算法。我这里直接用的对称加密,好像一般是非对称加密。
生成步骤
细节上可能有所不同,但大致方向是一样的,百度百科也有。
简易数字签名的实现过程可概述为:
1.发送方使用单向散列函数对要发送的信息进行运算,生成信息摘要。
2.发送方用自己的私钥对消息摘要进行非对称加密,生成发送方的一个数字签名。
3.发送方将数字签名和信息本身发送给接收方。
4.接收方使用与发送方相同的单向散列函数,对收到的信息进行运算,重新生成信息摘要。
5.接收方使用发送方的公钥对接收的信息摘要解密。
6.将解密的信息摘要与重新生成的信息摘要进行比较,以判断信息在发送过程中是否被篡改过,保证信息传输过程中数据的完整性,实现对发送方的身份认证,防止信息交换中的抵赖现象的发生。
#业务数据
{
"username": "zhangsan",
"password": "123"
}
#商量的密钥
"secret": "abc"
第一步:排序
对除签名外的所有请求参数按key做的升序排列,例如:有c=3,b=2,a=1
三个参数,另加上时间戳后, 按key排序后为:a=1,b=2,c=3,timeStamp=123456789。
第二步:拼接字符串
把参数名和参数值连接成字符串,得到拼接字符:a1b2c3timeStamp123456789,再在两边加上密钥,则拼接字符:abca1b2c3timeStamp123456789abc
第三步:摘要算法加密
对上面的字符串摘要算法加密,生成摘要digest,如果没有需要逆推的(如盐值),可以将其转换为大写,保障安全。一般最后会编码为字符串
第四步:对称加密
将digest进行对称加密生成签名signature,非对称加密一样。
第五步:Base64编码
上述对称加密结果是字节数组,可以对其进行Base64编码。
注意
在加密过程中的第一步可能还会加入随机字符串,以增加其安全性。
1.3 业务数据加密
第一步:转json字符串
一般将多个业务数据字段转换为json格式的字符串
第二步:对称加密
将json字符串进行对称加密,非对称加密一样;
第三步:Base64编码
将加密结果进行Base64编码
1.4 验证数字签名
验证数字签名就是为了验证消息的安全性
第一步:Base64解码
与上面步骤对应,对业务数据和签名进行Base64解码。
第二步:对称解密
利用对称解密将签名解成摘要,将业务数据解成对应json格式字符串。
第三步:拼接字符串
按同样规则拼接字符串
第四步:验证
将字符串同样的摘要算法加密,与上面的摘要对比即可。
1.5 案例参考
用户登录验证需要调用去第三方平台验证,调第三方接口时需要保证数据安全,双方已经商量好密钥,这里用的对称对称加密(AES)(非对称加密类比),摘要算法(MD5)。相应的的工具类是自己封装的可以自己替换。这里是A系统加密数据,调B系统的接口,并验证B系统的返回数据,当然B也会对A的数据校验,这没写,过程一样。
public Result login(@RequestBody LoginVO loginVO) {
String callbackUrl = "http://localhost:8066/single/login";//接口地址
String keySecret = "123";//约定好的密钥
//1.获取签名
//利用json来将Bean转换为TreeMap,利用TreeMap来排序,这个方法会自动把值为null的字段去掉。
//loginVO包含username和password
TreeMap treeMap = JSONObject.parseObject(JSONObject.toJSONString(loginVO), TreeMap.class);
long timeStamp = new Date().getTime();//时间戳
treeMap.put("timeStamp",timeStamp);
//拼接字符串,获取signatureStr
String signatureStr = keySecret;
Set keySet = treeMap.keySet();
for (Object o : keySet) {
signatureStr += o + "" + treeMap.get(o);
}
signatureStr += keySecret;
//对signatureStr摘要算法(哈希算法/不可逆算法,MD5)加密为信息摘要digest
String digest = Md5Crypt.md5Crypt(signatureStr.getBytes());
//将摘要进行对称加密(AES)生成签名
String signature = null;
try {
signature = AesUtils.aesPKCS7PaddingEncrypt(digest, appSecret);
} catch (Exception e) {
e.printStackTrace();
}
//2.对业务数据信息进行对称加密
treeMap.remove("timeStamp");//去掉非业务数据
String json = JSON.toJSONString(treeMap);
String data = null;
try {
data = AesUtils.aesPKCS7PaddingEncrypt(json, keySecret);
} catch (Exception e) {
e.printStackTrace();
}
//4.加密数据完成,封装参数
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("timeStamp", timeStamp);//时间戳
paramMap.put("signature", signature);//签名
paramMap.put("data", data);//对称加密后的业务数据
//5.发送请求,接收回复的数据
HttpHeaders headers = new HttpHeaders();
//设置请求行参数application/json
headers.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<HashMap> userResponseEntity = restTemplate.postForEntity(callbackUrl, paramMap, HashMap.class);
if (userResponseEntity == null) {
throw new BussinessException(ExceptionEnums.OTHER_ERROR, "登录失败,请再稍后再试");
}
//6.解密数据
HashMap<String, Object> responseMap = (HashMap<String, Object>) userResponseEntity.getBody();
String responseData = null;
try {
responseData = AesUtils.aesPKCS7PaddingDecrypt((String) responseMap.get("data"), appSecret);
} catch (Exception e) {
e.printStackTrace();
}
//json格式字符串转换为对象
JSONObject jsonObject = JSON.parseObject(responseData);
//获取信息
int code = (int) jsonObject.get("code");//登录结果码
Object timeStamp1 = responseMap.get("timeStamp");//时间戳
String signature1 = (String) responseMap.get("signature");//签名
//7.验证安全性
String signatureStr1 = appSecret + code + timeStamp1 + appSecret;//数据较少这里直接手动排了
//对称解密签名成摘要
digest = AesUtils.aesPKCS7PaddingDecrypt((String) responseMap.get("signature"), keySecret);
//摘要算法生成摘要
String digest1 = Md5Crypt.md5Crypt(temp1.getBytes(), MD5CryptUtil.getSalts(signature1));
boolean isSafe = digest1.equals(digest);
if (!isSafe) {
throw new BussinessException(ExceptionEnums.OTHER_ERROR, "数据不安全");
}
}