【问题标题】:Encrypt in Coldfusion, Decrypt in Node.js在 Coldfusion 中加密,在 Node.js 中解密
【发布时间】:2016-04-07 10:38:30
【问题描述】:

我在 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.js encryption coldfusion aes


    【解决方案1】:

    ColdFusion 加密与 IV

    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密钥。并且没有从中生成“秘密”。

    在 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 看@Leigh answer

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

    我认为这个简短的教程也可以帮助那些在其他语言中遇到同样问题的人,例如 cf->php 或 cf->python。

    【讨论】:

    • 感谢@Bonanza 分享您的知识,您能否解释一下为什么您的原始解决方案不起作用并且效果很好?这将有助于读者更好地理解问题!
    • 更新了答案。希望这就是您的要求。
    • 现在看起来很完美。谢谢!
    • @Bonanza - 感谢您发布更多详细信息,但第 1 项和第 2 项并不完全正确。见my answer for details
    • @Leigh 感谢您的澄清。不知道那个。将编辑我的答案并锚定您的答案。
    【解决方案2】:

    accepted answer的一些澄清和更正

    简答:

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

    更长的答案:

    • 如果你想用其他语言解密,不要使用 GenerateSecretKey

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

    GenerateSecretKey() 为指定算法生成cryptographically random key。 (虽然 CF 生成 base64 编码的密钥字符串,但它也可以很容易地进行十六进制编码。密钥的 binary 值很重要。)生成的密钥适用于任何实现相同的语言加密算法和密钥大小。然而,正如我在earlier comments 中提到的,对称加密仅在一切都匹配时才有效。您必须使用相同的密钥、相同的算法、相同的 iv 等进行加密和解密。在原始代码中,“key”和“algorithm”的值都是不同的。这就是解密失败的原因。

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

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

    要导出 CF 中的“密码”,将密钥字符串解码为二进制并生成 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。此外,密码字符串首先被解码为二进制。创建 cipher 对象时,需要指明输入字符串是 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),它生成的可预测输出更少,因此更安全。

    • 比CF使用Tobase64(secret)生成密钥

    按照同样的思路,虽然从技术上讲,您可以使用任意字符串(即“abcdefghijkl1234”)来生成密钥,但不要这样做。强加密的一个非常重要的部分是使用"truly random and contain sufficient entropy" 的密钥。所以不要只是自己做。使用经过验证的函数或库,例如专为该任务设计的 GenerateSecretKey()。

    【讨论】:

    • 总是用特殊方法生成秘密。永远不要提交公共存储库:)
    • 嗯...不知道你在最后一部分中在说什么 {cough}{whistle} ;-)
    • 这很好地解释了事情。我几乎确信GenerateSecretKey() 与nodeJs 端的解密问题有关,但现在我知道如何正确使用它了:)
    • 加密互操作就像让两个交战派系同意和平。一旦每个人最终都说同一种语言,并就条款达成一致,它就会顺利进行。达到这一点......可能有点挑战;-) 通常这是因为 CF 默认为 ECB 模式,并且大多数库使用 CBC,导致问题,但这次不是。
    【解决方案3】:

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

    为了让 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 中具有相应算法的算法。

    【讨论】:

    • 是的,Strong encryption in ColdFusion 是一个更好的资源。默认值为“AES/ECB/PKCS5Padding”。我认为差异是由于 node.js 使用 128 位密钥作为“密码”,而不是实际密钥。 stackoverflow.com/questions/7787773/…
    • 试过什么?仅当双方都使用相同的所有内容、算法、模式、填充和 iv 时,它才会起作用。在原始代码中,他们没有这样做。
    • 在 iv 中使用“AES/ECB/PKCS5Padding”应该可以。您能否更新您的帖子,说明您尝试使用这些设置以及结果,我们将从那里开始。
    • @Leigh 您的建议使用“AES/ECB/PKCS5Padding”和 iv 在Bonanza 的新答案中更新。如果您可以发布另一个答案并解释为什么这种方法有效,这将有助于未来的读者理解您提供的问题和解决方案。
    • (编辑)@Anurag - 谢谢。出于好奇,我发布了一个解释,并对原始问题进行了一些澄清。旁注,我刚刚注意到一个错字。我输入了 EBC,但显然是指 CBC ;-)
    猜你喜欢
    • 2021-06-10
    • 1970-01-01
    • 2015-05-09
    • 2012-12-11
    • 1970-01-01
    • 1970-01-01
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多