AES-CBC 128, 192 and 256 encryption decryption in Python 3 using PKCS#7 padding

Nilesh Vora picture Nilesh Vora · Sep 23, 2016 · Viewed 11k times · Source

I have searched a lot on SO about complete encryption decryption example with my requirement. In fact, I've got many links and examples but None is working for me for AES-192-CBC mode and AES-256-CBC.

I have got following example which is supposed to be working with all types but it is working only with AES-128-CBC mode. I am new to Python. Can anyone help me where I am wrong?

I am using Python 3.4 on windows and I can not move to Python 2.7.

import base64
from Crypto.Cipher import AES

class AESCipher:
    class InvalidBlockSizeError(Exception):
        """Raised for invalid block sizes"""
        pass

    def __init__(self, key, block_size=16):
        if block_size < 2 or block_size > 255:
            raise AESCipher.InvalidBlockSizeError('The block size must be between 2 and 255, inclusive')
        self.block_size = block_size
        self.key = key
        self.iv = bytes(key[0:16], 'utf-8')
        print(self.key)
        print(key[0:16])

    def __pad(self, text):
        text_length = len(text)
        amount_to_pad = self.block_size - (text_length % self.block_size)
        if amount_to_pad == 0:
            amount_to_pad = self.block_size
        self.pad = chr(amount_to_pad)
        return text + self.pad * amount_to_pad

    def __unpad(self, text):
        #pad = ord(text[-1])
        #return text[:-pad]
        text = text.rstrip(self.pad)
        return text

    def encrypt( self, raw ):
        raw = self.__pad(raw)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return base64.b64encode(cipher.encrypt(raw)) 

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
        return self.__unpad(cipher.decrypt(enc).decode("utf-8"))

e = AESCipher('1234567812345678', 16)
#e = AESCipher('123456781234567812345678', 24)
#e = AESCipher('12345678123456781234567812345678', 32)
secret_data = "hi"
enc_str = e.encrypt(secret_data)
print('enc_str: ' + enc_str.decode())
dec_str = e.decrypt(enc_str)
print('dec str: ' + dec_str)

Though this code encrypts the data with 192 and 256 bit encryption and successfully decrypt that too but my other .Net and Ruby application only able to decrypt the data which was encrypted using 128 encryption.

Note .Net and Ruby application are successfully tested with each other and with online encryption tool with all encryption types.

Note that my application requires AES-CBC mode and PKCS#7 padding and must be run on Python 3.4.

Answer

Nilesh Vora picture Nilesh Vora · Sep 23, 2016

Made it working by padding of 16 bytes for any encryption types. For that I used AES.block_size which is 16 by default for AES.

import base64
from Crypto.Cipher import AES

class AESCipher:
    class InvalidBlockSizeError(Exception):
        """Raised for invalid block sizes"""
        pass

    def __init__(self, key):
        self.key = key
        self.iv = bytes(key[0:16], 'utf-8')
        print(self.key)
        print(key[0:16])

    def __pad(self, text):
        text_length = len(text)
        amount_to_pad = AES.block_size - (text_length % AES.block_size)
        if amount_to_pad == 0:
            amount_to_pad = AES.block_size
        pad = chr(amount_to_pad)
        return text + pad * amount_to_pad

    def __unpad(self, text):
        pad = ord(text[-1])
        return text[:-pad]

    def encrypt( self, raw ):
        raw = self.__pad(raw)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return base64.b64encode(cipher.encrypt(raw)) 

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
        return self.__unpad(cipher.decrypt(enc).decode("utf-8"))

e = AESCipher('1234567812345678', 16)
#e = AESCipher('123456781234567812345678', 24)
#e = AESCipher('12345678123456781234567812345678', 32)
secret_data = "hi"
enc_str = e.encrypt(secret_data)
print('enc_str: ' + enc_str.decode())
dec_str = e.decrypt(enc_str)
print('dec str: ' + dec_str)