【问题标题】:AES encryption in node resulting is different output compared to java与 java 相比,节点中的 AES 加密是不同的输出
【发布时间】:2019-07-19 07:42:55
【问题描述】:

我正在尝试将用于 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 块大小也无济于事。

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

【问题讨论】:

  • Rijndael 和 AES 不一样。您的 Java 代码付出了很多努力来确保 256 位块大小。 AES 的块大小为 128 位。您没有使用相同的加密算法。
  • 另请注意,您拥有的代码非常不安全...它使用 ECB 模式并且未经身份验证。你为什么要使用这个?
  • @LukeJoshuaPark 感谢您抽出宝贵时间。我认为 Rijndeal 是基于 Wikipedia en.wikipedia.org/wiki/Advanced_Encryption_Standard 的 AES。
  • 代码来自我们正在集成的供应商,我需要在“Rijndael/ECB/NoPadding”模式下对一些数据进行加密。通信将仅使用列入白名单的 IP。
  • Rijndael 有很多 NodeJS 包。

标签: javascript java node.js encryption aes


【解决方案1】:

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

此外,您必须使用方法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-ecb 用于 16 字节密钥,aes-256-ecb 用于 32 字节密钥等。

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

我不知道Rijndael/ECB/NoPaddingAES/ECB/NoPadding 在Java 中的密码实例化方面是否真的有区别。在我的测试用例中,至少结果是相同的。因此,对于 nodejs 代码,选择 aes-128-ecb(用于 16 字节密钥)或aes-256-ecb(用于 32 字节密钥)应该可以工作。

【讨论】:

  • 具有讽刺意味的是,他们已经围绕 256 位块大小进行了所有这些努力,但最终却得到了 128 位块和一些消息膨胀......这就是密码学应该由密码学家完成的原因。很好的答案。
  • @Topaco。这非常有帮助,我今天学到了一些新东西。非常感谢。
  • @LukeJoshuaPark 也谢谢你。我猜 AES 毕竟是 Rijndael。
  • @Narshal Rijndael 不是 AES... 128 位块大小的 Rijndael 是 AES。 Rijndael 还提供 192 位和 256 位块大小。它们相同。编写原始代码的开发人员认为他们正在使用具有 256 位块大小的 Rijndael,但实际上他们只是将明文扩展为使用 2 128 位块的倍数,这就是为什么这个答案工作。
  • @LukeJoshuaPark 你是对的。我将密钥大小与块大小混淆了。当我将 CIPHER_PAD_SIZE 设置为 16 时,加密和解密工作。我不确定为什么开发人员会采用这种方法,但我只是在评估一个产品,所以我畅通无阻并且开明了。谢谢你:)
猜你喜欢
  • 1970-01-01
  • 2017-08-31
  • 2016-12-17
  • 2021-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-12
  • 2014-01-31
相关资源
最近更新 更多