在 Coldfusion 中加密,在 Node.js 中解密

Encrypt in Coldfusion, Decrypt in Node.js

我在 ColdFusion 中加密字符串

enc_string = '7001010000006aaaaaabbbbbb';
uid = encrypt(enc_string,'WTq8zYcZfaWVvMncigHqwQ==','AES','Hex'); 
// secret key for tests only

结果:

DAEB003D7C9DBDB042C63ED214E85854EAB92A5C1EC555765B565CD8723F9655

稍后我想在 Node 中解密那个字符串(只是一个例子)

uid='DAEB003D7C9DBDB042C63ED214E85854EAB92A5C1EC555765B565CD8723F9655'
decipher = crypto.createDecipher('aes-192-ecb', 'WTq8zYcZfaWVvMncigHqwQ==')
decipher.setAutoPadding(false);
dec = decipher.update(uid, 'hex', 'utf8')
dec += decipher.final('utf8')

我尝试了一些密码但没有成功。我不想修改 ColdFusion 代码以使其工作,但如果没有其他机会,我会这样做。我想使用 GET 从一个站点向另一个站点发送一些加密数据。有什么建议吗?

编辑: 我尝试了所有 AES、DES、带 IV、不带 IV、带和不带填充。也试过base64。也没有运气。

您用于加密和解密的密码不相同。

为了让 Node 将您的结果解密为预期的字符串,您应该首先确保在 Node 中加密初始字符串会得到相同的加密结果。

考虑以下内容,运行通过 Node 中所有已知的(对我来说)AES 密码,并尝试获得与从 Coldfusion 获得的相同的加密结果:

var crypto = require('crypto');
var key = 'WTq8zYcZfaWVvMncigHqwQ==';
var algorithm;

var ciphers = [
  'aes-128-cbc',
  'aes-128-cbc-hmac-sha1',
  'aes-128-cfb',
  'aes-128-cfb1',
  'aes-128-cfb8',
  'aes-128-ctr',
  'aes-128-ecb',
  'aes-128-gcm',
  'aes-128-ofb',
  'aes-128-xts',
  'aes-192-cbc',
  'aes-192-cfb',
  'aes-192-cfb1',
  'aes-192-cfb8',
  'aes-192-ctr',
  'aes-192-ecb',
  'aes-192-gcm',
  'aes-192-ofb',
  'aes-256-cbc',
  'aes-256-cbc-hmac-sha1',
  'aes-256-cfb',
  'aes-256-cfb1',
  'aes-256-cfb8',
  'aes-256-ctr',
  'aes-256-ecb',
  'aes-256-gcm',
  'aes-256-ofb',
  'aes-256-xts',
  'aes128',
  'aes192',
  'aes256'
]

function encrypt(text){
  var cipher = crypto.createCipher(algorithm, key);
  var crypted = cipher.update(text,'utf8','hex');
  crypted += cipher.final('hex');
  return crypted;
}

for (var i = 0; i < ciphers.length; i++) {
  algorithm = ciphers[i];
  console.log(encrypt("7001010000006aaaaaabbbbbb"));
}

如果你运行这个你会得到以下输出:

ab1e8ddd6be53040fcfdf07578704ed9831c4e962eddd36899fc3819b51d6ade
ab1e8ddd6be53040fcfdf07578704ed9831c4e962eddd36899fc3819b51d6ade
ff19a0b91dad25671632581655f53139ac1f5554383951e255
e4756965c26df5b2e7e2e5291f5a2b1bc835b523ae7e39da0d
ff93cfff713798bcf94ff60fb61a6d9d4ae0a7ad6672e77a22
ff19a0b91dad25671632581655f5313940ed1d69d874cf04d7
70ef98bda47bd95e64221c144c4fdec1e5ad1422ca9f4589653214577adf9d9a
918559eaab9a983f91160dbdb2f093f55b0a2bc011fbe1b309
ff19a0b91dad25671632581655f53139cb62004d669030b400
2c4e36eb6b08107bbdf9c79c2f93160211128977181fee45ab
37fed7d50a56f42fa26805a69c38b12b519e59116702a9f0d15a437791600b3a
01f4d909c587684862ea9e27598f5d5c489028a223cc79be1a
0c482981e6aefa068b0c0429ba1e46894c39d7e7f27d114651
01c9d7545c3bfe8594ebf5aef182f5d4930db0555708057785
01f4d909c587684862ea9e27598f5d5c7aa4939a9008ea18c4
6fb304a32b676bc3ec39575e73752ad71255f7615a94ed93f78e6d367281ee41
7494a477258946d781cb53c9b37622248e0ba84a48c577c9df
01f4d909c587684862ea9e27598f5d5c889a935648f5f7061f
ea16ecf9ad13756f9bd8ad3fcff2a9e06778647d763f88e679dde519e7155cd6
ea16ecf9ad13756f9bd8ad3fcff2a9e06778647d763f88e679dde519e7155cd6
d0688b6632962acf7905ede7e4f9bd7b2d557e3b828a855208
c0119ab62e5c7a3d932042648291f7cd97c30c9b42c9fa1779
d0f72742cc0415a74e201fcc649f90cf9506eac14e24fd96a9
d0688b6632962acf7905ede7e4f9bd7b5e4921830c30ae8223
d6cd01243405e8741e4010698ab2943526f741cfdb2696b5a6d4e7c14479eccf
2592fb4b19fd100c691598c4bdb82188b6e9d6a6b308d0d627
d0688b6632962acf7905ede7e4f9bd7bf375251be38e1d1e08
d9ae0f940e7c40dcb3a620a5e2a1341819632124af5014bf2f
ab1e8ddd6be53040fcfdf07578704ed9831c4e962eddd36899fc3819b51d6ade
37fed7d50a56f42fa26805a69c38b12b519e59116702a9f0d15a437791600b3a
ea16ecf9ad13756f9bd8ad3fcff2a9e06778647d763f88e679dde519e7155cd6

您从 Coldfusion 得到的加密结果出现在上面的输出中。

因此,使用 Node 中可用的 AES 密码,加密结果总是与 Coldfusion 的加密结果不同。如果加密后的结果总是不同,则无法解密为相同的值。

Coldfusion Encryption Docs 在简单地指定 "AES" 时,对于准确描述使用哪种算法不是很有帮助。我强烈建议指定要使用的精确算法,包括要使用的密钥大小,并选择一个在 Node 中具有相应算法的算法。

使用 IV 的 ColdFusion 加密

enc_string = '7001010000006aaaaaabbbbbb';
myKey = Tobase64("abcdefghijkl1234");
myIV = charsetDecode("abcdefghijkl9876", "utf-8");
uid=encrypt(enc_string,myKey,'AES/CBC/PKCS5Padding','hex',myIV);

加密后的uid值为:

614981D0BC6F19A3022FD92CD6EDD3B289214E80D74823C3279E90EBCEF75D90

现在我们把它带到节点:

var Crypto = require('crypto');

var key = new Buffer('abcdefghijkl1234');
var iv = new Buffer('abcdefghijkl9876');
var encrypted = new Buffer('614981D0BC6F19A3022FD92CD6EDD3B289214E80D74823C3279E90EBCEF75D90', 'hex');
var decipher = Crypto.createDecipheriv('aes-128-cbc', key, iv);
var decrypted = decipher.update(encrypted);
var clearText = Buffer.concat([decrypted, decipher.final()]).toString();

console.log(clearText);

结果是:

7001010000006aaaaaabbbbbb

符合预期。


问题的来源

最初在 Coldfusion 中我使用的是生成的密钥:

GenerateSecretKey(algorithm [,keysize]);

生成加密方法所需的 base64 密钥。并且没有'secret'从中生成。

在 Node Crypto 方法中,createDecipheriv 获取 Buffer 作为参数。缓冲区需要秘密,而不是密钥。我不确定为什么没有 IV 它就不能工作。

Coldfusion 中需要更改的内容

  1. 如果你想用 CF 以外的其他语言解密,请不要使用 GenerateSecretKey
  2. 使用Tobase64(secret)生成密钥
  3. 使用IV并使用charsetDecode(ivSecret, "utf-8")生成它
  4. 算法:AES/CBC/PKCS5Padding
  5. 对于AES/ECB看

在 Node 中,每个输入都是 Buffer。

我认为这个简短的教程也可以帮助那些使用其他语言如 cf->php 或 cf->python.

有同样问题的人

的一些澄清和更正

简答:

  • 使用 GenerateSecretKey() 生成的 "crytographically random" 密钥,而不是使用 Tobase64(secret) 创建的密钥。
  • 虽然从技术上讲 ECB 模式有效(见下文),但 CBC 模式是更安全的方法。对于 CBC,请参阅我的完整示例:Encrypt in ColdFusion, Decrypt in Node.js

更长的答案:

  • Don't use GenerateSecretKey if you want to decrypt in other language

不,将生成的值与其他语言的加密函数一起使用是完全没问题的 - 只要它们符合规范。这并不意味着这些值可以完全用于任何语言 "as is"。它可能需要调整以符合语言 X 或 Y 的实现。 (例如,X 语言中的函数可能期望键是十六进制字符串,而不是 base64。因此您可能需要先转换键值)。这在原始代码中并没有完全发生,这就是解密不起作用的原因。

GenerateSecretKey() produces a cryptographically random key for the specified algorithm. (While CF generates base64 encoded key strings, it could just as easily be hex encoded. The binary value of the key is what matters.) The generated key is suitable for use with any language that implements the same encryption algorithms and key sizes. However, as I mentioned in the ,对称加密只有在一切匹配时才有效。您必须使用相同的密钥、相同的算法、相同的 iv 等来进行加密和解密。在原始代码中,"key" 和 "algorithm" 值都不同。这就是解密失败的原因。

原码使用crypto.createCipher(algorithm, password)Per the API、"password"用于推导出密匙。换句话说,Node.js 代码使用了与 CF 代码完全不同的密钥。此外,Node.js 被配置为使用 192 位密钥,而 CF 代码使用的是 128 位密钥。

回答您最初的问题,是的 - 您可以使用 ECB 模式(尽管强烈建议不要这样做)。但是,它需要修改 CF 代码以派生相同的 password Node.js 将使用。 (另一个方向是不可能的,因为它涉及单向散列。)

要在CF 中导出"password",将密钥字符串解码为二进制并生成md5 散列。然后将哈希解码为二进制并将其重新编码为 base64 以使 encrypt() 函数满意。

CF:

plainText = "7001010000006aaaaaabbbbbb";
secretKey = "WTq8zYcZfaWVvMncigHqwQ==";
keyHash = hash(binaryDecode(secretKey, "base64"), "md5");
nodeJSPassword = binaryEncode(binaryDecode(keyHash, "hex"), "base64");
encryptedText = encrypt(plainText, nodeJSPassword, "AES/ECB/PKCS5Padding", "Hex"); 
writeOutput(encryptedText);

结果:

C43E1179C15CD962373A6E28486D6F4ADB12FBB6731EF99C9212474E18D51C70

在 Node.js 端,修改代码以使用 128 位密钥,而不是 192 位。此外,密码字符串首先被解码为二进制。创建密码对象时,您需要指明输入字符串是 base64 编码的,以确保它被正确解释。

Node.js

var password = 'WTq8zYcZfaWVvMncigHqwQ==';
var passwordBinary = new Buffer(password, "base64");
var encrypted = 'C43E1179C15CD962373A6E28486D6F4ADB12FBB6731EF99C9212474E18D51C70'
var crypto = require('crypto');
var decipher = crypto.createDecipher('aes-128-ecb', passwordBinary );
var decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);

结果:

7001010000006aaaaaabbbbbb

话虽如此,Using ECB mode is NOT recommended。首选方法是 CBC 模式(随机 iv),它生成的输出不可预测,因此更安全。

  • than CF Use Tobase64(secret) to generate key

沿着同样的路线,虽然您可以在技术上使用任意字符串,即 "abcdefghijkl1234" 来生成密钥 - 不要。强加密的一个非常重要的部分是使用 "truly random and contain sufficient entropy" 的密钥。所以不要自己动手。使用经过验证的函数或库,例如专为该任务设计的 GenerateSecretKey()。