NodeJS加密结果与Python cryptography.fernet加密结果不匹配问题问询
问题根因
1. 密钥长度报错原因
你传入的key是base64编码的字符串,长度为44个字符,并非算法需要的原始字节长度。AES-128-CBC要求密钥为16字节,而Fernet生成的base64密钥解码后是32字节,直接传入编码后的字符串自然会触发长度错误。
2. Fernet密钥拆分必要性
Fernet的32字节原始密钥确实分为两部分:前16字节是signing_key,用于HMAC签名校验;后16字节是encryption_key,用于AES加密。如果只做AES加密不处理签名逻辑,Python的Fernet解密时会因为签名校验失败直接报错。
Fernet密文有固定格式要求,最终输出不是单纯的AES密文,完整结构如下:
- 固定版本号(1字节,值为
0x80) - 时间戳(8字节,大端序存储)
- 16字节随机IV
- PKCS7填充后的AES密文
- 32字节HMAC-SHA256签名(签名范围为前面所有字段的拼接结果,用
signing_key计算) - 以上所有内容拼接后做base64url编码得到最终输出
完整兼容实现代码
const crypto = require("crypto"); // 处理原始Fernet密钥 const fernetKeyB64 = '6saGtiTFEXej729GUWSeAyQdIpRFdGhfY2XFUDpvsu8='; // 将base64编码的密钥解码为32字节原始Buffer const fernetKeyBuf = Buffer.from(fernetKeyB64, 'base64'); // 拆分签名密钥和加密密钥 const signingKey = fernetKeyBuf.subarray(0, 16); const encryptionKey = fernetKeyBuf.subarray(16, 32); // 加密逻辑 const message = 'Hello World'; const iv = crypto.randomBytes(16); // IV必须为16字节随机值 const cipher = crypto.createCipheriv('aes-128-cbc', encryptionKey, iv); // NodeJS默认开启PKCS7填充,无需额外处理 const ciphertext = Buffer.concat([cipher.update(message, 'utf8'), cipher.final()]); // 组装Fernet格式前缀 const version = Buffer.from([0x80]); const timestamp = Buffer.alloc(8); timestamp.writeBigUInt64BE(BigInt(Math.floor(Date.now() / 1000))); const payloadPrefix = Buffer.concat([version, timestamp, iv, ciphertext]); // 计算HMAC签名 const hmac = crypto.createHmac('sha256', signingKey); hmac.update(payloadPrefix); const signature = hmac.digest(); // 拼接所有内容后做base64url编码得到最终密文 const finalFernetBuf = Buffer.concat([payloadPrefix, signature]); const encryptedStr = finalFernetBuf.toString('base64url'); console.log(encryptedStr);
上述代码生成的密文可直接用Python的Fernet库正常解密,若不需要校验时间戳,Python解密时传入ttl=None即可避免时间差导致的解密失败。
内容的提问来源于stack exchange,提问作者EhMaMuffin
相关产品推荐
相关产品推荐

