当前位置: 首页 > 知识库问答 >
问题:

用PyCrypto解密AES和HMAC

魏旭
2023-03-14

让AES密码文本解密有点困难。

在这个特定的场景中,我使用Crypto-JS在客户端加密数据,并使用PyCrypto在python服务器上解密数据。

加密。js:

  var password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP';
  var data = 'mytext';

  var masterKey = CryptoJS.SHA256(password).toString();

  // Derive keys for AES and HMAC
  var length = masterKey.toString().length / 2
  var encryptionKey = masterKey.substr(0, length);
  var hmacKey = masterKey.substr(length);

  var iv = CryptoJS.lib.WordArray.random(64/8);

  var encrypted = CryptoJS.AES.encrypt(
    data,
    encryptionKey,
    {
      iv: iv,
      mode: CryptoJS.mode.CFB
    }
  );

  var concat = iv + encrypted;

  // Calculate HMAC using iv and cipher text
  var hash = CryptoJS.HmacSHA256(concat, hmacKey);

  // Put it all together
  var registrationKey = iv + encrypted + hash;

  // Encode in Base64
  var basemessage = btoa(registrationKey);

解密。py:

class AESCipher:
    def __init__(self, key):
        key_hash = SHA256.new(key).hexdigest()
        # Derive keys
        encryption_key = key_hash[:len(key_hash)/2]
        self.key = encryption_key            
        self.hmac_key = key_hash[len(key_hash)/2:]


    def verify_hmac(self, input_cipher, hmac_key):
        # Calculate hash using inputted key
        new_hash = HMAC.new(hmac_key, digestmod=SHA256)
        new_hash.update(input_cipher)
        digest = new_hash.hexdigest()

        # Calculate hash using derived key from local password
        local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
        local_hash.update(input_cipher)
        local_digest = local_hash.hexdigest()

        return True if digest == local_digest else False


    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:16]
        hmac = enc[60:]
        cipher_text = enc[16:60]

        # Verify HMAC using concatenation of iv + cipher like in js
        verified_hmac = self.verify_hmac((iv+cipher_text), self.hmac_key)

        if verified_hmac:
            cipher = AES.new(self.key, AES.MODE_CFB, iv)
            return cipher.decrypt(cipher_text)


password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP'

input = 'long base64 registrationKey...'

cipher = AESCipher(password)
decrypted = cipher.decrypt(input)

我成功地重新计算了HMAC,但当我尝试然后解密密码时,我得到了一些似乎用密码加密的东西�'结果如何。

我在密码文本的输入长度方面遇到了错误,但当我切换到CFB模式时,修复了它,所以我认为这不是一个填充问题。

共有1个答案

唐弘和
2023-03-14

你的代码有很多问题。

>

  • AES的块大小为128位,CFB模式期望IV有一个完整的块。使用

    var iv = CryptoJS.lib.WordArray.random(128/8);
    

    ivhash变量是WordArray对象,但加密的不是。当您通过连接它们()强制将它们转换为字符串时,ivhash是十六进制编码的,但是加密的是以OpenSSL兼容格式格式化的,并且是Base64编码的。您需要访问ciphertext属性以获取加密的WordArray:

    var concat = iv + encrypted.ciphertext;
    

    var registrationKey = iv + encrypted.ciphertext + hash;
    

    registrationKey是十六进制编码的。无需再次使用Base64对其进行编码,并进一步膨胀:

    var basemessage = registrationKey;
    

    如果要将十六进制编码的注册密钥转换为base64编码,请使用:

    var basemessage = CryptoJS.enc.Hex.parse(registrationKey).toString(CryptoJS.enc.Base64);
    

    concat是由IV和密文组成的十六进制编码字符串,因为您通过“添加”(IV加密来强制字符串化。HmacSHA256()函数接受WordArray对象或字符串。当您传入一个字符串时,它会假定数据是UTF-8编码的,并尝试将其解码为UTF-8。您需要自己将数据解析到WordArray中:

    var hash = CryptoJS.HmacSHA256(CryptoJS.enc.Hex.parse(concat), hmacKey);
    

    加密()和加密JS. HmacSHA256()期望密钥作为WordArray对象或字符串。如前所述,如果以字符串形式提供密钥,则假定使用UTF-8编码,但此处不存在这种情况。您最好自己将字符串解析为WordArray:

    var encryptionKey = CryptoJS.enc.Hex.parse(masterKey.substr(0, length));
    var hmacKey = CryptoJS.enc.Hex.parse(masterKey.substr(length));
    

    >

  • 您没有在verify\hmac()中验证任何内容。使用同一密钥对同一数据进行两次散列。您需要做的是对IV密文进行散列,并将结果与从完整密文中切掉的散列(称为标记或HMAC标记)进行比较。

    def verify_hmac(self, input_cipher, mac):
        # Calculate hash using derived key from local password
        local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
        local_hash.update(input_cipher)
        local_digest = local_hash.digest()
    
        return mac == local_digest
    

    稍后在decrypt()中:

    verified_hmac = self.verify_hmac((iv+cipher_text), hmac)
    

    您需要正确地切掉MAC。硬编码的60是个坏主意。由于您使用的是SHA-256,MAC的长度为32字节,因此您可以这样做

    hmac = enc[-32:]
    cipher_text = enc[16:-32]
    

    CFB模式实际上是一组类似的模式。实际模式由段大小决定。CryptoJS仅支持128位的段。因此,您需要告诉pycrypto使用与CryptoJS中相同的模式:

    cipher = AES.new(self.key, AES.MODE_CFB, iv, segment_size=128)
    

    如果你想使用CFB模式,段大小为8位(默认为pyCrypto),你可以在CryptoJS中使用CFB的修改版本

    完整客户端代码:

    var password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP';
    var data = 'mytext';
    
    var masterKey = CryptoJS.SHA256(password).toString();
    var length = masterKey.length / 2
    var encryptionKey = CryptoJS.enc.Hex.parse(masterKey.substr(0, length));
    var hmacKey = CryptoJS.enc.Hex.parse(masterKey.substr(length));
    
    var iv = CryptoJS.lib.WordArray.random(128/8);
    
    var encrypted = CryptoJS.AES.encrypt(
        data,
        encryptionKey,
        {
          iv: iv,
          mode: CryptoJS.mode.CFB
        }
    );
    
    var concat = iv + encrypted.ciphertext; 
    var hash = CryptoJS.HmacSHA256(CryptoJS.enc.Hex.parse(concat), hmacKey);
    var registrationKey = iv + encrypted.ciphertext + hash;
    console.log(CryptoJS.enc.Hex.parse(registrationKey).toString(CryptoJS.enc.Base64));
    

    完整服务器代码:

    from Crypto.Cipher import AES
    from Crypto.Hash import HMAC, SHA256
    import base64
    import binascii
    
    class AESCipher:
        def __init__(self, key):
            key_hash = SHA256.new(key).hexdigest()
            self.hmac_key = binascii.unhexlify(key_hash[len(key_hash)/2:])
            self.key = binascii.unhexlify(key_hash[:len(key_hash)/2])
    
        def verify_hmac(self, input_cipher, mac):
            local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
            local_hash.update(input_cipher)
            local_digest = local_hash.digest()
    
            return SHA256.new(mac).digest() == SHA256.new(local_digest).digest() # more or less constant-time comparison
    
        def decrypt(self, enc):
            enc = base64.b64decode(enc)
            iv = enc[:16]
            hmac = enc[-32:]
            cipher_text = enc[16:-32]
    
            verified_hmac = self.verify_hmac((iv+cipher_text), hmac)
    
            if verified_hmac:
                cipher = AES.new(self.key, AES.MODE_CFB, iv, segment_size=128)
                return cipher.decrypt(cipher_text)
            else:
                return 'Bad Verify'
    
    
    password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP'
    
    input = "btu0CCFbvdYV4B/j7hezAra6Q6u6KB8n5QcyA32JFLU8QRd+jLGW0GxMQsTqxaNaNkcU2I9r1ls4QUPUpaLPQg=="
    
    obj = AESCipher(password)
    decryption = obj.decrypt(input)
    
    print 'Decrypted message:', decryption
    

  •  类似资料:
    • 问题内容: 我正在尝试使用PyCrypto构建两个接受两个参数的函数:消息和密钥,然后对消息进行加密/解密。 我在网络上找到了几个链接可以帮助我,但是每个链接都有缺陷: 在codekoala上的此代码使用了os.urandom,PyCrypto不建议这样做。 此外,我不能保证给函数的键具有预期的确切长度。我该怎么做才能做到这一点? 另外,有几种模式,推荐哪种?我不知道该怎么用:/ 最后,IV到底是

    • 我试图使用PyCrypto构建两个函数,它们接受两个参数:消息和密钥,然后加密/解密消息。 我在网上找到了几个帮助我的链接,但每一个都有缺陷: 编辑:删除了代码部分,因为它不安全。

    • 作为一个自学练习,我正在尝试学习如何使用一些pycrypto库。我需要使用AES在CBC_模式下解密密文字符串。I密文、密钥和IV都已给出。以下是我编写的代码: 当我运行这个,我得到以下错误: ValueError:IV的长度必须为16字节 我知道IV字符串是32个十六进制字符,因此是16个字节。我认为这可能是一个打字问题,但我不知道如何纠正它。有人能帮忙吗? 非常感谢。

    • 这里是一个JavaScript部分,它用AES加密解码字符串 我试图用python编写一个类似的解码函数,并导入AES。 谁能帮我做这个吗。我无法找出js到python的所有等效代码。 我查了这个页面Python AES解密例程(代码帮助)和 AES-加密与加密(node-js)/解密与Pycrypto(python) 不确定他们的代码是否与我这里的js相似 这在python中是什么意思 我从另一

    • 我已经看过了AES-Encryption with Crypto(node js)/decryption with Pycrypto(python)的文章,因为我正试图做完全相反的事情,但我似乎做得不对。这是我到目前为止试过的。。。 Python加密 节点。js解密 每次我尝试运行节点时。js解密,我最终得到错误消息:

    • 问题内容: 我今天刚找到pycrypto,并且一直在研究AES加密课程。不幸的是,它只能成功一半。self.h.md5以十六进制格式输出md5哈希,并且为32byte。这是输出。似乎是对消息进行了解密,但是它在解密后放置了随机字符,在这种情况下,是\ n \ n \ n …我认为self.data的块大小有问题,有人知道如何解决此问题吗? Jans-MacBook-Pro:test2 jan $