使用Crypto(node-js)进行AES加密 / 使用Pycrypto(python)进行解密

19
我写这个问题+答案,是因为我在使用node或python加密/解密时遇到了很多困难(可能是因为缺乏经验),迷失在许多不同的加密/解密方式中。 我想也许我的情况可以帮助未来的人们。
我需要做的事情:
从表单中获取数据,使用Crypto(node-js)进行加密 将加密后的数据传递给Python,并使用PyCrypto解密它。 我选择使用AES加密。
以下是我的起步方式(我不会详细介绍我尝试过的所有方法):
我按照这个页面末尾的例子操作:
(这可能是javascript和coffeescript之间非常糟糕的混合)
crypto = require "crypto"
[...]
key = "mykeywhatever"
cipher = crypto.createCipher('aes192', key)
cipher.update('string i want to encode', 'binary', 'hex')
encoded_string = cipher.final('hex')
[...]

这很好地编码了我的字符串。

  • 然后,我编写了python脚本来解密这个字符串,使用了PyCrypto的GitHub页面上的readme

  • from Crypto.Cipher import AES
    [...]
    my_string = data_coming_from_rabbitmq
    obj = AES.new('mykeywhatever', AES.MODE_CBC)
    obj.decrypt(ciphertext)
    [...]
    

    显然这没起作用:在自述文件中有一个IV,但由于我在节点脚本中没有提供它,为什么我要在Python中提供一个呢?

    经过进一步搜索,我了解到node的Crypto使用OpenSSL,而PyCrypto显然不使用。所以我研究了一下,并找到了这些页面:

    所以事情变得复杂了,没有人使用相同的方法来解密数据,我迷失了方向,请求帮助。

    答案就是我和我的同事想出来的(嗯,大部分是我的同事)。

    2个回答

    16

    所以我们从"How can i decrypt... OpenSSL"的答案开始。

    • 我们需要修改加密脚本,得到:

    crypto = require "crypto"
    [...]
    var iv = new Buffer('asdfasdfasdfasdf')
    var key = new Buffer('asdfasdfasdfasdfasdfasdfasdfasdf')
    var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    cipher.update(new Buffer("mystring"));
    var enc = cipher.final('base64');
    [...]
    

    iv 需要16字节长,key 是32字节长。我们将 createCipher 改为了 createCipheriv

  • 回到 Python 解密脚本:

    我们简单地阅读了 PyCrypto 的文档,然后与我们起始的代码进行比较

    然后我们决定只按照 API 手册操作,从头开始编写。结果如下:

    from base64 import b64decode
    from Crypto.Cipher import AES
    [...]
    iv = 'asdfasdfasdfasdf'
    key = 'asdfasdfasdfasdfasdfasdfasdfasdf'
    encoded = b64decode('my_encrypted_string')
    
    dec = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
    value = dec.decrypt(encoded)
    
  • 就是这么简单... 希望对你们有所帮助!

    更新:

    正如Perseids在他的回答评论中写道,IV必须对于每个消息都是随机且不同的。


    8

    你正在构建的系统可能不安全

    除了存储之外,你基本上不仅要加密数据,还需要进行身份验证。在这个背景下,身份验证意味着只有知道密钥的人才能生成有效的消息。一种广泛使用的身份验证方案是HMAC

    如果你不对消息进行身份验证,任何人都可以向你的服务输入数据。攻击者可能无法完全控制解密后的结果,但他/她仍然可能非常危险。例如,如果你使用CBC(你确实使用了),并且使用最常见的填充方案(AES是块密码,只能加密128位数据块),并且攻击者可以区分填充错误和其他错误,那么攻击者就可以解密所有你的消息。这被称为填充预言攻击,而这种情况太普遍了。

    为了防止这类攻击,你可以使用身份验证加密方案,例如GCM块密码模式。

    此外,你还必须防范重放攻击。考虑一个银行应用程序,你正在传输的数据是一笔银行转账订单。除了任何TAN之外,攻击者可能记录前面的交易,并将这个交易一遍又一遍地重新发送到你的服务中,从而转移客户最初想要的多倍资金。

    你从哪里获取数据表单?是否通过HTTPS传输?如果没有:密钥是否会被攻击者窃听?用户如何知道他从你那里获取了表单而不是其他任何人(SSL/TLS与身份验证和机密性同样重要)。

    可能我忘记了一些简单CBC加密提供的其他攻击向量。

    替代方案

    可能防止这些攻击最简单的方法是通过HTTPS传输表单数据。 SSL/TLS旨在防止上述所有攻击,并且客户端和服务器端实现已经成熟了很长时间。


    好的,我刚刚提到了加密操作,我不会谈论所有其他安全系统,因为这跟Node.js/Python加密/解密兼容性并不相关。 但是感谢您的回答,非常有趣。 - nnaelle
    @Perseids 注意,填充预言攻击是针对CBC模式的攻击(尽管它使用填充函数作为预言)。 - ntoskrnl
    1
    @nnaelle 安全始终关乎整体。一个系统的安全性取决于其最薄弱的环节。即使是微小的问题也可能完全破坏整个系统。这就是为什么你不能孤立地处理它的不同部分。 - ntoskrnl
    @ntoskrnl:谢谢,我忘记在答案中写了。nnaelle正在使用CBC。 - Perseids
    4
    如果你知道自己在做什么那就没问题。问题是其他很多读者可能不知道。我曾看到过一些代码,人们在网上看到需要使用加密来保护其服务,然后只是复制了一些代码,而没有得到正确的使用说明。例如,这样的读者可能不知道你发布中的 IV 需要是随机的,并且对于每条消息都应该是不同的(暂时忽略填充预言攻击)。结果通常和不使用任何密码学一样糟糕。 - Perseids
    不知道 IV 必须是随机的。我会把这个加到我的答案里! - nnaelle

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接