You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Fernet加密数据在Node.js aes-128-cbc解密时提示IV长度无效

问题原因与修复方案

核心错误点

  • 对Fernet格式理解错误:Fernet的32字节密钥base64解码后,前16字节是HMAC签名密钥,后16字节才是AES加密密钥,IV不存在于密钥中,而是固定存储在密文头部。
  • 传参类型错误:Node.js的crypto.createDecipheriv要求密钥、IV传入Buffer类型,你将Buffer转成binary字符串传入,长度计算失准,直接触发IV长度无效报错。
  • 编码逻辑错误:Python端Fernet.encrypt()返回值本身已经是base64字符串,你又额外做了一次base64编码,等于双重编码;同时请求头声明application/json但实际传form表单数据,格式不匹配。

Fernet标准密文结构

Fernet密文base64解码后按字节拆分如下:

  • 第1字节:固定版本号0x80
  • 第2-9字节:8字节时间戳
  • 第10-25字节:16字节AES-CBC初始向量IV
  • 第26字节到倒数32字节:AES加密后的实际密文
  • 最后32字节:HMAC-SHA256签名,用于校验密文完整性

修复后代码

Python端

import requests
from cryptography.fernet import Fernet

def get_key():
    return Fernet.generate_key()

def enc(input_text, key):
    f = Fernet(key)
    return f.encrypt(input_text.encode())

msg = "hello world"
key = get_key()
cypher = enc(msg, key)
print("msg: " + msg)
print("key: " + key.decode())
print("cyp: " + cypher.decode())

url='http://127.0.0.1:8088/test'
# 传form表单无需加json请求头,去掉多余的二次base64编码
r = requests.post(url, data={"key": key.decode(), "msg": cypher.decode()})

Node.js端

const express = require('express');
const crypto = require('crypto');

const app = express();
const port = 8088;

app.use(express.urlencoded({extended: true, limit:'100mb', parameterLimit:1000000}));
app.disable('x-powered-by');

function decrypt(fernetKeyB64, ciphertextB64) {
    // 解码密钥,拆分HMAC签名密钥与AES密钥
    const rawKey = Buffer.from(fernetKeyB64, 'base64');
    const signingKey = rawKey.slice(0, 16);
    const aesKey = rawKey.slice(16, 32);

    // 解码密文,按Fernet规范拆分各段
    const rawCipher = Buffer.from(ciphertextB64, 'base64');
    if (rawCipher[0] !== 0x80) throw new Error('无效的Fernet版本标识');
    const iv = rawCipher.slice(9, 25); // 从密文头部提取16字节IV
    const ciphertext = rawCipher.slice(25, rawCipher.length - 32);
    const hmacSign = rawCipher.slice(rawCipher.length - 32);

    // 校验HMAC签名,防止密文被篡改
    const hmac = crypto.createHmac('sha256', signingKey);
    hmac.update(rawCipher.slice(0, rawCipher.length - 32));
    const calcSign = hmac.digest();
    if (!crypto.timingSafeEqual(calcSign, hmacSign)) throw new Error('密文签名校验失败');

    // AES-128-CBC解密,自动处理PKCS7填充
    const decipher = crypto.createDecipheriv('aes-128-cbc', aesKey, iv);
    const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
    return decrypted.toString('utf8');
}

app.post("/test", function(req, res) {
    const fernetKey = req.body.key;
    const cipherMsg = req.body.msg;
    console.log("收到key: " + fernetKey);
    console.log("收到密文: " + cipherMsg);
    
    try {
        const decResult = decrypt(fernetKey, cipherMsg);
        console.log("解密结果: " + decResult);
        res.writeHead(200, {"Content-Type": "application/json"});
        res.end(JSON.stringify({"status": "OK", "data": decResult}));
    } catch (e) {
        console.error("解密失败:", e.message);
        res.writeHead(500, {"Content-Type": "application/json"});
        res.end(JSON.stringify({"status": "ERROR", "msg": e.message}));
    }
});

app.listen(port, () => {
    console.log("Listening on localhost:" + port);
});

内容的提问来源于stack exchange,提问作者Siedler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:39:19