与前端对接登录接口的时候,约定用户密码使用AES加密方式进行存储。但是在测试对接时,发现前端生成的AES加密字符串,后端无法正常解密,前端和后端生成的加密字符串也不一致。经检查,key、iv、加密模式(CBC)、填充方式(PKCS7)、密码字符串编码方式都一样,但就是加密结果不一样。我们一起看看问题出在哪里吧。
1.找在线加密平台验证
AES在线加密/解密 我找了这个在线平台,首先进行加密字符串验证,确认谁的加密字符串有问题。经验证,后端加密字符串与在线加密平台生成结果一致,也可以正常解密。那就是前端加密代码存在理解不一致的地方了,但是前端反馈说,这个加密算法已经和其他平台进行过对接,一直使用的就是这套加密算法。这样我就只能自己动刀来找前后端的问题了,别的后端都能对接,我岂曰不能。
2.前端加密代码检查
前端在线验证平台 前端使用的加密库是CryptoJS,有在线测试平台,这就方便多了。前端代码拉来检查一遍,感觉key、iv、加密模式(CBC)、填充方式(PKCS7)、密码字符串编码方式都一样,执行加密后,加密结果就是不一样。
前端加密代码:
import CryptoJS from "crypto-js";
export const AESencrypt = (str: string) => {
//加密
const iwxKey = "CBj6GIsL4h52nU1NolYDqAtR7wJ0KdSi";
const key = CryptoJS.enc.Utf8.parse(iwxKey);
const iv = CryptoJS.enc.Hex.parse("0000000000000000");
const encRes = CryptoJS.AES.encrypt(str, key, {
iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
return encRes.toString();
};
对比看下后端加密代码:
# 密钥(key), 密斯偏移量(iv), CBC模式加密, 默认PKCS7填充
BLOCK_SIZE = 16 # Bytes
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
# utf8编码偏移
vi = '0000000000000000'
key = 'CBj6GIsL4h52nU1NolYDqAtR7wJ0KdSi'
def aes_encrypt(data):
data = pad(data)
# 字符串补位
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
encryptedbytes = cipher.encrypt(data.encode('utf8'))
# 加密后得到的是bytes类型的数据,使用Base64进行编码,返回byte字符串
encodestrs = base64.b64encode(encryptedbytes)
# 对byte字符串按utf-8进行解码
enctext = encodestrs.decode('utf8')
return enctext
在咬文嚼字之后,才发现iv编码前后端是不一致的, 前端为16进制字符串const iv = CryptoJS.enc.Hex.parse("0000000000000000") 后端为utf-8编码字符串vi.encode('utf8') 尽管在调试可视化查看或者输出时,都显示为0,但是不同编码的存储值应该是不一样的,因此影响了加密结果。
3.vi编码调整
找到问题了,这就什么都好说了,前端调整或者后端调整,都不是什么大问题。
前端调整
import CryptoJS from "crypto-js";
export const AESencrypt = (str: string) => {
//加密
const iwxKey = "CBj6GIsL4h52nU1NolYDqAtR7wJ0KdSi";
const key = CryptoJS.enc.Utf8.parse(iwxKey);
const iv = CryptoJS.enc.Utf8.parse("0000000000000000"); //更换iv编码格式
// const iv = CryptoJS.enc.Hex.parse("0000000000000000");
const encRes = CryptoJS.AES.encrypt(str, key, {
iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
return encRes.toString();
};
后端调整
# python没有找到直接进行字符串转16进制编码的方式,这里直接手动设置为16进制字节了
vi = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
def aes_decrypt(data):
data = data.encode('utf8')
encodebytes = base64.decodebytes(data)
# 将加密数据转换位bytes类型数据
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi)
text_decrypted = cipher.decrypt(encodebytes)
# 去补位
text_decrypted = unpad(text_decrypted)
text_decrypted = text_decrypted.decode('utf8')
return text_decrypted
当然了前后端有一端调整即可,总之来说,还是要保持加密参数一致,加密结果才能一致。涉及多端对接加密不一致问题,找一个中间平台来做验证,终归是靠谱的。
我没有创造知识,只是串联大佬们的经验,解决了自己的问题,记录下来。