Node.js AES加密与Java/C#不一致的解决方案

作者:后端茶话时间:2026-10-27 23:29:14浏览:177

本文详细解析Node.js中AES加密结果与Java、C#不一致的原因及解决方法。通过对比示例,指出Node.js默认实现差异,并给出使用向量(IV)和密钥MD5哈希处理的具体代码方案,确保跨平台加解密数据一致。

Node.js AES加密与Java/C#不一致的原因

示例一:

近期遇到一个棘手问题:Node.js生成的AES加密结果与Java、C#不一致,导致无法解密。经过反复排查,最终通过查阅源码找到原因。网上有观点认为Node.js的AES实现与其他语言存在差异,这确实是一个常见痛点。关键在于Node.js的crypto模块使用方式。

代码如下:

var crypto = require('crypto');
var data = "156156165152165156156";
 console.log('Original cleartext: ' + data);
 var algorithm = 'aes-128-ecb';
 var key = '78541561566';
 var clearEncoding = 'utf8';
 //var cipherEncoding = 'hex';
 //If the next line is uncommented, the final cleartext is wrong.
 var cipherEncoding = 'base64';
/*加密*/
 var cipher = crypto.createCipher(algorithm, key);
var cipherChunks = [];
 cipherChunks.push(cipher.update(data, clearEncoding, cipherEncoding));
 cipherChunks.push(cipher.final(cipherEncoding));
 console.log(cipherEncoding + ' ciphertext: ' + cipherChunks.join(''));
/*解密*/
 var decipher = crypto.createDecipher(algorithm, key);
 var plainChunks = [];
 for (var i = 0;i < cipherChunks.length;i++) {
 plainChunks.push(decipher.update(cipherChunks[i], cipherEncoding, clearEncoding));
}
 plainChunks.push(decipher.final(clearEncoding));
 console.log("UTF8 plaintext deciphered: " + plainChunks.join(''));

上述代码运行无误,加密解密均成功,但结果与Java、C#生成的密文不同。这让人困惑。实际上,只需添加一个初始化向量(IV),即可使结果保持一致。由于相关资料较少,导致排查耗时较长。

正确代码是:

代码如下:

var crypto = require('crypto');
var data = "156156165152165156156";
 console.log('Original cleartext: ' + data);
 var algorithm = 'aes-128-ecb';
 var key = '78541561566';
 var clearEncoding = 'utf8';
 var iv = "";
 //var cipherEncoding = 'hex';
 //If the next line is uncommented, the final cleartext is wrong.
 var cipherEncoding = 'base64';
 var cipher = crypto.createCipheriv(algorithm, key,iv);
var cipherChunks = [];
 cipherChunks.push(cipher.update(data, clearEncoding, cipherEncoding));
 cipherChunks.push(cipher.final(cipherEncoding));
 console.log(cipherEncoding + ' ciphertext: ' + cipherChunks.join(''));
var decipher = crypto.createDecipheriv(algorithm, key,iv);
 var plainChunks = [];
 for (var i = 0;i < cipherChunks.length;i++) {
 plainChunks.push(decipher.update(cipherChunks[i], cipherEncoding, clearEncoding));
}
 plainChunks.push(decipher.final(clearEncoding));
 console.log("UTF8 plaintext deciphered: " + plainChunks.join(''));

对比发现,加密结果现在与Java、C#一致。问题解决。

跨平台AES加密密钥处理方案

示例二:

在实际工作中,Node.js端通过AES加密,Android客户端使用Java解密,同时Node.js也需要解密Android端加密的内容。发现两端加密结果不一致,经查阅资料发现,Java端需要对密钥进行MD5加密处理。以下是AES ECB模式的代码示例,若使用CBC模式,同样需要对密钥进行MD5处理:

Node.js端代码:

代码如下:

/** 
* aes加密
* @param data
* @param secretKey
 */  
encryptUtils.aesEncrypt = function(data, secretKey) {
var cipher = crypto.createCipher('aes-128-ecb',secretKey);
return cipher.update(data,'utf8','hex') + cipher.final('hex');
}  
/** 
* aes解密
* @param data
* @param secretKey
* @returns {*}
 */  
encryptUtils.aesDecrypt = function(data, secretKey) {
var cipher = crypto.createDecipher('aes-128-ecb',secretKey);
return cipher.update(data,'hex','utf8') + cipher.final('utf8');
}  

Java端代码:


代码如下:

package com.iofamily.util;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/** 
* AES加密,与Nodejs 保持一致
* @author lmiky
* @date 2014-2-25
 */  
public class AESForNodejs {
public static final String DEFAULT_CODING = "utf-8";
    /** 
* 解密
* @author lmiky
* @date 2014-2-25
* @param encrypted
* @param seed
* @return
* @throws Exception
     */  
private static String decrypt(String encrypted, String seed) throws Exception {
byte[] keyb = seed.getBytes(DEFAULT_CODING);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(keyb);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
Cipher dcipher = Cipher.getInstance("AES");
dcipher.init(Cipher.DECRYPT_MODE, skey);
byte[] clearbyte = dcipher.doFinal(toByte(encrypted));
return new String(clearbyte);
    }  
    /** 
* 加密
* @author lmiky
* @date 2014-2-25
* @param content
* @param key
* @return
* @throws Exception
     */  
public static String encrypt(String content, String key) throws Exception {
byte[] input = content.getBytes(DEFAULT_CODING);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(key.getBytes(DEFAULT_CODING));
SecretKeySpec skc = new SecretKeySpec(thedigest, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skc);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return parseByte2HexStr(cipherText);
    }  
    /** 
* 字符串转字节数组
* @author lmiky
* @date 2014-2-25
* @param hexString
* @return
     */  
private static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++) {
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
        }  
return result;
    }  
    /** 
* 字节转16进制数组
* @author lmiky
* @date 2014-2-25
* @param buf
* @return
     */  
private static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
            }  
sb.append(hex);
        }  
return sb.toString();
    }  
public static void main(String[] args) throws Exception {
System.out.println(AESForNodejs.encrypt("fsadfsdafsdafsdafsadfsadfsadf", "1234fghjnmlkiuhA"));
System.out.println(AESForNodejs.decrypt("5b8e85b7a86ad15a275a7cb61fe4c0606005e8741f68797718a3e90d74b5092a", "1234fghjnmlkiuhA"));
    }  
}
作者最新文章