Encode in Laravel, decode in Python

后端 未结 3 1013
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-23 16:56

I\'m using Laravel\'s encryptString method to encrypt some data on my website. This uses OpenSSL\'s 256-bit AES-CBC encryption without any serialization. I\'m now trying to decr

相关标签:
3条回答
  • 2021-01-23 17:07

    Full Working Code with MAC Validation

    Adding to Gonzalo's submission, Laravel's encrypted message is a base64 json-encoded array consisting of the following key pairs:

    [
          'iv' => 'generated initialization vector (iv)',
          'value' => 'encrypted, base64ed, signed value',
          'mac'  => 'message authentication code (mac)'
    ]
    

    The 'value' is signed using a message authentication code (MAC) to verify that the value has not changed during transit.

    The MAC extracted from the payload (encrypted message) should be checked against the mac extracted from the 'value' (this can be done using the key, iv, and value). Laravel's encryption scheme can be found on GitHub: src/Illuminate/Encryption/Encrypter.php

    Referencing a discussion thread on Laravel, I traced a partial solution on Github: orian/crypt.py (which is a fork of fideloper/crypt.py).

    I have forked off of Orian's code and fixed an input parameter issue. The code works as expected as long as the encryption key (passed in as an input to decrypt()) is base64 decoded and does not include the 'base64:' that is typically prepended to the APP_KEY environmental variable string assignment found in the .env file.

    Solution: pilatuspc12ng/crypt.py

    The code snippet of the crypt.py is shown below:

    import base64
    import json
    from Crypto.Cipher import AES
    from phpserialize import loads
    import hashlib
    import hmac
    
    
    def decrypt(payload, key):
        """
        Decrypt strings that have been encrypted using Laravel's encrypter (AES-256 encryption).
        Plain text is encrypted in Laravel using the following code:
        >>> ciphertext = Crypt::encrypt('hello world');
        The ciphertext is a base64's json-encoded array consisting of the following keys:
           [
              'iv' => 'generated initialization vector (iv)',
              'value' => 'encrypted, base64ed, signed value',
              'mac'  => 'message authentication code (mac)'
           ]
        The 'value' is signed using a message authentication code (MAC) so verify that the value has not changed during
        transit.
        Parameters:
        payload (str): Laravel encrypted text.
        key (str): Encryption key (base64 decoded). Make sure 'base64:' has been removed from string.
        Returns:
        str: plaintext
        """
    
        data = json.loads(base64.b64decode(payload))
        if not valid_mac(key, data):
            return None
    
        value = base64.b64decode(data['value'])
        iv = base64.b64decode(data['iv'])
    
        return unserialize(mcrypt_decrypt(value, iv, key)).decode("utf-8")
    
    def mcrypt_decrypt(value, iv, key):
        AES.key_size=128
        crypt_object=AES.new(key=key,mode=AES.MODE_CBC,IV=iv)
        return crypt_object.decrypt(value)
    
    def unserialize(serialized):
        return loads(serialized)
    
    def valid_mac(key, data):
        dig = hmac.new(key, digestmod=hashlib.sha256)
        dig.update(data['iv'].encode('utf8'))
        dig.update(data['value'].encode('utf8'))
        dig = dig.hexdigest()
        return dig==data['mac']
    
    0 讨论(0)
  • 2021-01-23 17:22

    Question: ...trying to decrypt that data in Python but I keep getting errors about key length

    I can use your key, in the code of the linked answer, after doing .b64decode(....
    The example code .encode(... and decode(... works as expecte.
    Conclusion: There is nothing wrong, with your Key!

    key = b"/AZejP0lh3McL/+Vy5yZcADdTcR65qnx5Jqinuw7raK="
    key = base64.b64decode(key)
    

    But with your code, I got TypeError, related to the IV parameter:

      expect_byte_string(iv)
    File "/usr/local/lib/python3.4/dist-packages/Crypto/Util/_raw_api.py", line 172, in expect_byte_string
      TypeError: Only byte strings can be passed to C code
    

    Fixed with IV = 16 * '\x00'.encode(), results in ValueError, related to enc:

      data = decobj.decrypt(base64.b64decode(enc))
    File "/usr/local/lib/python3.4/dist-packages/Crypto/Cipher/_mode_cbc.py", line 209, in decrypt
      ValueError: Error 3 while decrypting in CBC mode
    

    This leads to github issues:10

    Error 3 means "ERR_NOT_ENOUGH_DATA"

    According to the linked GitHub page, you have to reread the documentation, about padding data while you are encoding.


    Working example from GitHub:

    import base64
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad, unpad
    
    key = b"/AZejP0lh3McL/+Vy5yZcADdTcR65qnx5Jqinuw7raK="
    key = base64.b64decode(key)
    
    BLOCK_SIZE = 32
    encryption_suite = AES.new(key, AES.MODE_CBC, b'This is an IV...')
    cipher_text = encryption_suite.encrypt(pad(b'A really secret message...', BLOCK_SIZE))
    
    decryption_suite = AES.new(key, AES.MODE_CBC, b'This is an IV...')
    print(unpad(decryption_suite.decrypt(cipher_text), BLOCK_SIZE).decode())
    >>> A really secret message...
    

    Tested with Python: 3.4.2

    0 讨论(0)
  • 2021-01-23 17:25

    I notice this haven't been active for a while but I'm trying to do the same and can't seem to make it work.

    What I noticed is that the encoded password string stored by Laravel is a base 64 encoded json object, which wasn't taken into consideration in the original question:

    pass_obj = base64.b64decode('eyJpdiI6ImdxY0VcLzFodmpISFV4allSWmJDdEpRPT0iLCJ2YWx1ZSI6IkxXd0ZJaUd2bTUweW5pNm0wUjQwOFM2N1wvWEs5SlYrNB4xNlR7Qkh1U3FvPSIsIm1hYyI6Ijc5ZWM0YTYxYjljZGFiNzgwNjY2NDU1ZmQ5Yjc1ZmJlOGU4NzBkMjQzMzA3MmVhYzE3NzY4ZmU1MWIyMjZlOTQifQ==')
    print(pass_obj)
    >>> b'{"iv":"gqcE\\/1hvjHHUxjYRZbCtJQ==","value":"LWwFIiGvm50yni6m0R408S67\\/XK9JV+4\x1e16T{BHuSqo=","mac":"79ec4a61b9cdab780666455fd9b75fbe8e870d2433072eac17768fe51b226e94"}'
    

    From that you can get the IV and the encrypted value, both seem to be base64 encoded. But I still get a decoding error at the end like;

    UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfa in position 0: invalid start byte

    Here is my complete code;

    password = 'eyJpdiI6ImJGNDNNZjN3YWtpcDQ5VEJVXC9IazF3PT0iLCJ2YWx1ZSI6IkNVRW1VQUY1dXArYlFkU3NlY1pnZUE9PSIsIm1hYyI6ImM3ODk0NWQ0NjgxMzM4YjE0M2JhN2MzZWRmOWEwMWJiMjI2Y2FhYmUxYjFhYzAyYjY4YWZkZGE3N2EyMDYwNWYifQ=='
    key = 'some secret key that i can't share'.encode()
    p_obj = json.loads(base64.b64decode(password).decode())
    decobj = AES.new(key, AES.MODE_CBC, base64.b64decode(p_obj['iv']))
    data = decobj.decrypt(base64.b64decode(p_obj['value']))
    print(data)
    >>> b'l\xee:f\x9eZ\x90rP\x99\xca&@\x1d1\x9f'
    data.decode()
    >>> Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    UnicodeDecodeError: 'utf-8' codec can't decode byte 0xee in position 1: invalid continuation byte
    

    @Pecans did you ever figure this out?

    Thank you.


    Full working code EDIT

    I fugured it out, I had a problem with my key initially. So here's the full snippet for future reference;

    import base64
    import json
    from phpserialize import unserialize
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad, unpad
    
    key = b'my secret key'
    enc_pass = 'eyJpdiI6ImJGNDNNZjN3YWtpcDQ5VEJVXC9IazF3PT0iLCJ2YWx1ZSI6IkNVRW1VQUY1dXArYlFkU3NlY1pnZUE9PSIsIm1hYyI6ImM3ODk0NWQ0NjgxMzM4YjE0M2JhN2MzZWRmOWEwMWJiMjI2Y2FhYmUxYjFhYzAyYjY4YWZkZGE3N2EyMDYwNWYifQ=='
    
    p_obj = json.loads(base64.b64decode(password).decode())
    decobj = AES.new(key, AES.MODE_CBC, base64.b64decode(p_obj['iv']))
    data = decobj.decrypt(base64.b64decode(p_obj['value']))
    dec_pass = unserialize(unpad(data, 16)).decode()
    

    You will have the decrypted password in dec_pass.

    Note that sometimes Laravel generates the key in base64. In that case the string will be something like base64:sdfsdjfhjsdf32, then you have to decode first.

    Cheers!

    0 讨论(0)
提交回复
热议问题