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

Node.js Crypto库cipher.update异常及AES加密填充问题咨询

Node.js Crypto模块AES加密填充问题排查与解决

我用Node.js的crypto模块做字符串加密,原本以为当输入字符串长度不是16字节时,cipher.update()会自动填充成16字节,但调试发现这个方法返回空字符串,而且加密结果和预期对不上。想知道crypto模块还有其他实现填充的方式吗?

Node.js加密代码

function paymentIdencryption(text) {
    const secret_key = "EiE0BVQle0xFjZvYOupKjFGHAcAwBaTjlZ7G7rryNos=";
    const secretKeyWordArray = CCrypto.enc.Utf8.parse(secret_key); // 此处CCrypto未定义,属于无效代码

    const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(secret_key, 'base64'), iv);
    let encrypted = cipher.update(JSON.stringify(text), 'utf8', 'base64');
    encrypted += cipher.final('base64');
    const encryptedData = { iv: iv.toString('base64'), encrypted: encodeURIComponent(encrypted)};
    return encryptedData.encrypted;
}

console.log(paymentIdencryption("1384220_8089105"));

结果对比

  • 预期结果:GwJPNUfPXZZsuc0iqOFhn%2BYhMJKxXBUGl9g3iKqL8CE%3D
  • 实际返回结果:jJtKhwjqS5N4ABIrZev8Ng%3D%3D

Java解密代码

var imp = new JavaImporter(com.vordel.mime,org.json.simple.JSONObject,com.vordel.trace);
 
with(imp) {
function invoke(msg) {            
    var encodedKey = "${env.SPL.paymentMethodId.key}";
    Trace.info ("env.SPL.paymentMethodId.key -- encodedKey  = " + encodedKey); 
    var encodedPaymentInfo= msg.get("cvs.paymentMethodId");
 
    var encryptedPaymentInfo = java.net.URLDecoder.decode(encodedPaymentInfo,"UTF-8");

    var decodedKey = java.util.Base64.getDecoder().decode(encodedKey.getBytes("UTF-8"));
    var decodedcipher = java.util.Base64.getDecoder().decode(encryptedPaymentInfo.getBytes("UTF-8"));
 
    var cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding"); 
    var secretKey = new javax.crypto.spec.SecretKeySpec(decodedKey, "AES"); 
    var iv = new javax.crypto.spec.IvParameterSpec(decodedcipher,0,16);
    cipher.init(javax.crypto.Cipher.DECRYPT_MODE,secretKey,iv);
 
    var decryptedPaymentInfo = cipher.doFinal(decodedcipher, 16, decodedcipher.length-16);
    var paymentInfo = new java.lang.String(decryptedPaymentInfo,"UTF-8"); 
    var patientId = paymentInfo.substring(paymentInfo.indexOf("_")+1, paymentInfo.length());
    var paymentId = paymentInfo.substring(0, paymentInfo.indexOf("_"));
 
    msg.put("cvs.spl.patientId",patientId);
    msg.put("cvs.spl.paymentId",paymentId);
 
    return true;         
}
};

问题根源与解决方案

核心问题

  1. IV未初始化且结构不匹配:Node.js代码中iv变量未定义,且加密后将IV单独存储;但Java端是从密文的前16字节提取IV,两者逻辑完全冲突。
  2. 明文处理错误:调用JSON.stringify(text)会给字符串添加引号,而Java端解密后直接按下划线分割原始字符串,导致明文不一致。
  3. 填充误解:Node.js的createCipheriv默认使用PKCS7填充,和Java的PKCS5Padding在AES的16字节块大小下完全兼容,填充本身不是问题,问题出在其他逻辑错误。

修正后的Node.js代码

const crypto = require('crypto');

function paymentIdencryption(text) {
    const secret_key = "EiE0BVQle0xFjZvYOupKjFGHAcAwBaTjlZ7G7rryNos=";
    // 生成16字节随机IV,符合AES-CBC要求
    const iv = crypto.randomBytes(16);
    // 解码base64格式的密钥
    const key = Buffer.from(secret_key, 'base64');
    // 创建加密器,默认PKCS7填充与Java PKCS5Padding兼容
    const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    
    // 直接使用原始明文,无需JSON.stringify
    let encryptedBuffer = cipher.update(text, 'utf8');
    encryptedBuffer = Buffer.concat([encryptedBuffer, cipher.final()]);
    
    // 将IV与密文拼接后转base64,匹配Java端解密逻辑
    const combinedBuffer = Buffer.concat([iv, encryptedBuffer]);
    const encryptedBase64 = combinedBuffer.toString('base64');
    
    return encodeURIComponent(encryptedBase64);
}

console.log(paymentIdencryption("1384220_8089105"));

关键说明

  • IV处理:生成标准16字节随机IV,并拼接在密文头部,确保Java端能正确提取。
  • 明文对齐:去掉多余的JSON.stringify,直接传递原始字符串,保证解密后内容与Java端处理逻辑一致。
  • 填充兼容:PKCS5Padding是PKCS7Padding的子集,当块大小为16字节(AES默认)时,两者填充规则完全相同,无需额外配置填充方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:44:55