Python Web AES加密前后端对接

与前端对接登录接口的时候,约定用户密码使用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

当然了前后端有一端调整即可,总之来说,还是要保持加密参数一致,加密结果才能一致。涉及多端对接加密不一致问题,找一个中间平台来做验证,终归是靠谱的。

我没有创造知识,只是串联大佬们的经验,解决了自己的问题,记录下来。

你可能感兴趣的:(python,web,前端,javascript,安全,python)