Django集成CCAvenue支付网关时遇AES密钥大小无效错误
解决Django集成CCAvenue支付网关时的AES密钥长度错误问题
问题场景
在Django 4.2.1项目中集成CCAvenue支付网关,使用ccav_app/ccav_utils.py中的加解密函数处理数据与密钥,POST请求访问/cc/路径时触发ValueError: Invalid key size (352) for AES错误。
问题根源
- 密钥长度异常:
derive_key函数中,PBKDF2HMAC已生成符合AES-256要求的32字节(256位)密钥,但后续执行base64.urlsafe_b64encode将其编码为44字节(352位)的字符串,而AES仅支持128/192/256位密钥,导致密钥长度不符合要求。 - salt不一致:
derive_key每次调用都会生成随机salt,导致加密和解密时派生的密钥完全不同,即使去掉base64编码,也会出现解密失败的问题。 - padding导入冲突:代码中同时导入了非对称加密的
padding和对称加密的sym_padding,但pad/unpad函数错误使用了非对称加密的padding,可能引发潜在问题。
修复方案
修改核心函数逻辑
- 修正
derive_key:去掉base64编码,直接返回派生的字节密钥;同时返回salt,确保加密解密使用同一salt。 - 调整
encrypt函数:将salt、iv、加密数据拼接后再base64编码,让解密端能获取到salt。 - 调整
decrypt函数:先从加密数据中提取salt,再用该salt派生密钥,确保密钥一致。 - 修正padding引用:
pad/unpad函数使用sym_padding而非非对称加密的padding。
修改后的完整代码
# ccav_app/ccav_utils.py from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import padding as sym_padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend import base64 import os def pad(data): padder = sym_padding.PKCS7(128).padder() padded_data = padder.update(data) + padder.finalize() return padded_data def unpad(data): unpadder = sym_padding.PKCS7(128).unpadder() unpadded_data = unpadder.update(data) + unpadder.finalize() return unpadded_data def derive_key(secret_key, salt=None): if salt is None: salt = os.urandom(16) # 生成随机salt(16字节) kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), iterations=100000, salt=salt, length=32, # 生成32字节(256位)密钥,符合AES-256要求 backend=default_backend() ) key = kdf.derive(secret_key.encode()) return key, salt def encrypt(plain_text, secret_key): key, salt = derive_key(secret_key) iv = os.urandom(16) cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend()) encryptor = cipher.encryptor() # 先对明文做填充 padded_plain = pad(plain_text.encode()) encrypted_text = encryptor.update(padded_plain) + encryptor.finalize() # 拼接salt + iv + 加密数据,再base64编码 return base64.b64encode(salt + iv + encrypted_text).decode() def decrypt(cipher_text, secret_key): data = base64.b64decode(cipher_text.encode()) # 提取前16字节的salt salt = data[:16] # 提取接下来16字节的iv iv = data[16:32] # 剩余部分是加密数据 encrypted_data = data[32:] # 使用提取的salt派生密钥 key, _ = derive_key(secret_key, salt=salt) cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend()) decryptor = cipher.decryptor() decrypted_padded = decryptor.update(encrypted_data) + decryptor.finalize() # 去掉填充后解码为字符串 return unpad(decrypted_padded).decode()
额外说明
- 确保CCAvenue网关的密钥配置与代码中使用的
secret_key一致,大小写和特殊字符完全匹配。 - 测试时可先通过本地调用
encrypt和decrypt函数验证加解密逻辑是否正常,再集成到Django的视图中。
内容的提问来源于stack exchange,提问作者Nikhil Patil
相关产品推荐
相关产品推荐

