与 java 相比,节点中的 AES 加密结果是不同的输出
AES encryption in node resulting is different output compared to java
我正在尝试将 AES ECB 加密的 Java 代码移植到 node.js
问题是 java 和节点的输出不一样。
这是 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
并使用此 java脚本代码
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 块大小也无济于事。
我遗漏了什么导致不同的输出?
关于您的密钥,您必须使用缓冲区将您的十六进制字符串转换为二进制数据(参见 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 字节密钥等等
如评论中所述ECB
不是安全模式(参见 https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption)。
我不知道 Rijndael/ECB/NoPadding
和 AES/ECB/NoPadding
在 Java 中的密码实例化方面是否真的有区别。在我的测试用例中,至少结果是相同的。因此,对于 nodejs 代码,选择 aes-128-ecb
(对于 16 字节密钥)或 aes-256-ecb
(对于 32 字节密钥)应该有效。
我正在尝试将 AES ECB 加密的 Java 代码移植到 node.js
问题是 java 和节点的输出不一样。
这是 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
并使用此 java脚本代码
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 块大小也无济于事。
我遗漏了什么导致不同的输出?
关于您的密钥,您必须使用缓冲区将您的十六进制字符串转换为二进制数据(参见 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 字节密钥等等
如评论中所述ECB
不是安全模式(参见 https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption)。
我不知道 Rijndael/ECB/NoPadding
和 AES/ECB/NoPadding
在 Java 中的密码实例化方面是否真的有区别。在我的测试用例中,至少结果是相同的。因此,对于 nodejs 代码,选择 aes-128-ecb
(对于 16 字节密钥)或 aes-256-ecb
(对于 32 字节密钥)应该有效。