ValueError: AES key must be either 16, 24, or 32 bytes long PyCrypto 2.7a1

14,967

The error is very clear. The key must be exactly of that size. os.urandom will return you the correct key. However this key is a bytes (binary string value). Furthermore, by using str(secret), the value of repr(secret) is written into the file instead of secret.

What is more confusing is that AES.new allows you to pass the key as Unicode! However, suppose the key was the ASCII bytes 1234123412341234. Now,

f.write(str(secret))

will write b'1234123412341234' to the text file! Instead of 16 bytes, it now contains those 16 bytes + the b, and two ' quote characters; 19 bytes in total.

Or if you take a random binary string from os.urandom,

>>> os.urandom(16)
b'\xd7\x82K^\x7fe[\x9e\x96\xcb9\xbf\xa0\xd9s\xcb'

now, instead of writing 16 bytes D7, 82,.. and so forth, it now writes that string into the file. And the error occurs because the decryption tries to use

"b'\\xd7\\x82K^\\x7fe[\\x9e\\x96\\xcb9\\xbf\\xa0\\xd9s\\xcb'"

as the decryption key, which, when encoded as UTF-8 results in

b"b'\\xd7\\x82K^\\x7fe[\\x9e\\x96\\xcb9\\xbf\\xa0\\xd9s\\xcb'"

which is a 49-bytes long bytes value.


You have 2 good choices. Either you continue to write your key to a text file, but convert it to hex, or write the key into a binary file; then the file should be exactly the key length in bytes. I am going for the latter here:

Thus for storing the key, use

    with open('aeskey.bin', 'wb') as keyfile:
        keyfile.write(secret)

and

    with open('aeskey.bin', 'rb') as keyfile:
        key = keyfile.read()

Same naturally applies to the cipher text (that is the encrypted binary), you must write and read it to and from a binary file:

    with open('ciphertext.bin', 'wb') as f:
        f.write(encoded)

and

    with open('ciphertext.bin', 'rb') as f:
        encryptedString = f.read()

If you want to base64-encode it, do note that base64.b64encode/decode are bytes-in/bytes-out.

By the way, plaintext is the original, unencrypted text; the encrypted text is called ciphertext. AES is a cipher that can encrypt plaintext to ciphertext and decrypt ciphertext to plaintext using a key.

Despite these being called "-text" neither of them is textual data per se, as understood by Python, but they're binary data, and should be represented as bytes.

Share:
14,967
Tukanoid
Author by

Tukanoid

Updated on June 04, 2022

Comments

  • Tukanoid
    Tukanoid almost 2 years

    I'm making programm for my school project and have one problem above. Here's my code:

    def aes():
        #aes
        os.system('cls')
        print('1. Encrypt')
        print('2. Decrypt')
    
        c = input('Your choice:')
    
        if int(c) == 1:
            #cipher
            os.system('cls')
            print("Let's encrypt, alright")
            print('Input a text to be encrypted')
            text = input()
    
            f = open('plaintext.txt', 'w')
            f.write(text)
            f.close()
    
            BLOCK_SIZE = 32
            PADDING = '{'
            pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
            EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
            secret = os.urandom(BLOCK_SIZE)
    
            f = open('aeskey.txt', 'w')
            f.write(str(secret))
            f.close()
    
            f = open('plaintext.txt', 'r')
            privateInfo = f.read()
            f.close()
    
            cipher = AES.new(secret)
    
            encoded = EncodeAES(cipher, privateInfo)
    
            f = open('plaintext.txt', 'w')
            f.write(str(encoded))
            f.close()
            print(str(encoded))
    
        if int(c) == 2:
            os.system('cls')
            print("Let's decrypt, alright")
    
            f = open('plaintext.txt','r')
            encryptedString = f.read()
            f.close()
    
            PADDING = '{'
            DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
            encryption = encryptedString
    
            f = open('aeskey.txt', 'r')
            key = f.read()
            f.close()
    
            cipher = AES.new(key)
            decoded = DecodeAES(cipher, encryption)
    
            f = open('plaintext.txt', 'w')
            f.write(decoded)
            f.close()
    
            print(decoded)
    

    Full error text:

    Traceback (most recent call last): File "C:/Users/vital/Desktop/Prog/Python/Enc_dec/Enc_dec.py", line 341, in aes() 
    File "C:/Users/vital/Desktop/Prog/Python/Enc_dec/Enc_dec.py", line 180, in aes cipher = AES.new(key) 
    File "C:\Users\vital\AppData\Local\Programs\Python\Python35-32\lib\site-packages\Crypto\Cipher\AES.py", line 179, in new return AESCipher(key, *args, **kwargs) 
    File "C:\Users\vital\AppData\Local\Programs\Python\Python35-32\lib\site-packages\Crypto\Cipher\AES.py", line 114, in init blockalgo.BlockAlgo.init(self, _AES, key, *args, **kwargs) 
    File "C:\Users\vital\AppData\Local\Programs\Python\Python35-32\lib\site-packages\Crypto\Cipher\blockalgo.py", line 401, in init self._cipher = factory.new(key, *args, **kwargs)
    ValueError: AES key must be either 16, 24, or 32 bytes long
    
    Process finished with exit code 1
    

    What am I doing wrong?

  • Tukanoid
    Tukanoid over 7 years
    sorry, but now i have new error: ValueError: Input strings must be a multiple of 16 in length
  • Tukanoid
    Tukanoid over 7 years
    in lines: decoded = DecodeAES(cipher, encryption) DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
  • Antti Haapala -- Слава Україні
    Antti Haapala -- Слава Україні over 7 years
    @ВиталикЛукьянов apply the same to your ciphertext! open the ciphertext file as binary, and write binary to it. It is not "plaintext" and should perhaps not written to a file named plaintext.txt.
  • Tukanoid
    Tukanoid over 7 years
    "TypeError: a bytes-like object is required, not 'str'" in lines: decoded = DecodeAES(cipher, encryption) DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
  • Antti Haapala -- Слава Україні
    Antti Haapala -- Слава Україні over 7 years
    How about b'{'. Please make sure that you thoroughly understand the bytes/str distinction. It is the very foundation on which everything here builds.