首页 > 解决方案 > 与 java 相比,节点中的 AES 加密是不同的输出

问题描述

我正在尝试将用于 AES ECB 加密的 Java 代码移植到 node.js

问题是java和node的输出不一样。

这是java代码

 public static final String DEFAULT_ENCODING = "UTF-8";
 public static final String SEC_PROVIDER = "SunJCE";
 public static final String AES_ALGORITHM = "AES";
 public static final String RIJNDAEL_CIPHER = **"Rijndael/ECB/NoPadding"**;
 public static final int **CIPHER_PAD_SIZE = 32**;
 public static final String HEX_KEY = "3b6ce332ca3b6519eac769710f41ca5c";

public static String encryptData(String text, String hexKey) throws 
    Exception {
     byte[] b1 = Hex.decodeHex(HEX_KEY.toCharArray());
     SecretKey key = new SecretKeySpec(b1, AES_ALGORITHM); 
     Cipher cipher = Cipher.getInstance(RIJNDAEL_CIPHER, SEC_PROVIDER); 
     text = padRightToMod(text, CIPHER_PAD_SIZE); 
     byte[] buf = text.getBytes(DEFAULT_ENCODING); 
     cipher.init(Cipher.ENCRYPT_MODE, key);
     buf = cipher.doFinal(buf); 
     String result = new String(Hex.encodeHex(buf)); 
     result = result.toUpperCase(); 
     return result;
}

// ensure block size of 32
public static String padRightToMod(String text, int mod) {
    if (text == null || mod <= 0) {
        return text;
    }
    final int len = text.length();
    StringBuilder buf = new StringBuilder(512);
    buf.append(text);
    for (int i = len; i % mod > 0; i++) {
        buf.append(" ");
    }
    String rs = buf.toString();
    System.out.println(rs.length());
    return rs;
}

// Call to the encrypt function 

   String encText = encryptData("Hello", HEX_KEY);

结果是 CC0AC95B5FFD4758DBFA40F909C285F0F86A8F19ED1A12C1BFC098348A2AC683

并使用此 javascript 代码

crypto = require('crypto');

function encrypt(data,key) {
var cipher = crypto.createCipher('**aes-128-ecb**', key); //create aes cipher 
var encrypted = cipher.update(data,'utf8', 'hex'); //output as hex
return encrypted;
}

function padRightTo32(str) // ensure block size of 32
  {
    len=str.length;
    for(i=len; i%32>0; i++){
    str=str +" ";
  }
  return str;
 }

// call to encryption function
hexkey="3b6ce332ca3b6519eac769710f41ca5c"
encStr=encrypt(padRightTo32("Hello"),hexKey);
console.log(encStr);

结果是 1B928CF3C18D53BA5138DD1484D181939FD2B7BB2A17AE6A79664488B5C12652

==== 更新 ======

我用这个代码尝试了https://github.com/Snack-X/rijndael-js实现形式 github

const Rijndael = require("./node_modules/node-rijndael-master");
function padRightTo32(str)
{
    len=str.length;

    for(i=len; i%32>0; i++){
        str=str +" ";
    }
    console.log(str);
    console.log(str.length);
return str;
}
let key = "3b6ce332ca3b6519eac769710f41ca5c";
let original = padRightTo32("Hello");
let cipher = new Rijndael(key, "ecb");
let ciphertext = cipher.encrypt(original, 128);
console.log(ciphertext.toString("hex"));

我得到这个结果 e97282fb5838a9c78e6df1f1b4aad108aa010418ec573d74b9c991f4e897e752 但不是我在 java 中得到的加密文本。尝试 256 块大小也无济于事。

我缺少什么导致不同的输出?

标签: javascriptjavanode.jsencryptionaes

解决方案


关于您的密钥,您必须使用缓冲区将您的十六进制字符串转换为二进制数据(参见例如在 node.js 上使用 aes-ecb 加密二进制数据)。

此外,您必须使用该方法crypto.createCipheriv来实例化密码(参见例如https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options)。当前使用的(已弃用)方法crypto.creataCipher需要密码并从密码生成密钥(参见例如https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options)。

以下代码

crypto = require('crypto');

function encrypt(data,key) {
    var cipher = crypto.createCipheriv('aes-128-ecb', key,''); //create aes-128 cipher 
    var encrypted = cipher.update(data,'utf8', 'hex'); //output as hex
    return encrypted;
}

function padRightTo32(str) { // ensure block size of 32

    len=str.length;
    for(i=len; i%32>0; i++) {
        str=str +" ";
    }
    return str;
}

// call to encryption function
var hexKey = new Buffer('3b6ce332ca3b6519eac769710f41ca5c', 'hex'); // 16 Byte-key
encStr=encrypt(padRightTo32("Hello"),hexKey);
console.log(encStr);  

有输出

cc0ac95b5ffd4758dbfa40f909c285f0f86a8f19ed1a12c1bfc098348a2ac683

这等于 Java 代码的输出。

在 Java 中,密钥的长度定义了使用的 AES 变体,例如,如果您选择 16 字节密钥,则使用 AES-128,如果选择 32 字节密钥,则使用 AES-256。在 nodejs 代码中,您必须明确指定 AES 变体,即aes-128-ecb16 字节密钥和aes-256-ecb32 字节密钥等。

正如评论中已经提到的,ECB不是安全模式(参见例如https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption)。

我不知道Java 中的密码实例化Rijndael/ECB/NoPadding之间是否真的存在差异。AES/ECB/NoPadding在我的测试用例中,至少结果是相同的。因此,对于 nodejs 代码,选择aes-128-ecb(对于 16 字节密钥)或aes-256-ecb(对于 32 字节密钥)应该有效。


推荐阅读