Skip to content

Instantly share code, notes, and snippets.

@lopes
Last active August 7, 2024 13:42
Show Gist options
  • Save lopes/168c9d74b988391e702aac5f4aa69e41 to your computer and use it in GitHub Desktop.
Save lopes/168c9d74b988391e702aac5f4aa69e41 to your computer and use it in GitHub Desktop.
Simple Python example of AES in CBC mode. #python #cryptography #aes #cbc #poc
#!/usr/bin/env python3
#
# This is a simple script to encrypt a message using AES
# with CBC mode in Python 3.
# Before running it, you must install pycryptodome:
#
# $ python -m pip install PyCryptodome
#
# Author.: José Lopes
# Date...: 2019-06-14
# License: MIT
##
from hashlib import md5
from base64 import b64decode
from base64 import b64encode
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
class AESCipher:
def __init__(self, key):
self.key = md5(key.encode('utf8')).digest()
def encrypt(self, data):
iv = get_random_bytes(AES.block_size)
self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
return b64encode(iv + self.cipher.encrypt(pad(data.encode('utf-8'),
AES.block_size)))
def decrypt(self, data):
raw = b64decode(data)
self.cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size])
return unpad(self.cipher.decrypt(raw[AES.block_size:]), AES.block_size)
if __name__ == '__main__':
print('TESTING ENCRYPTION')
msg = input('Message...: ')
pwd = input('Password..: ')
print('Ciphertext:', AESCipher(pwd).encrypt(msg).decode('utf-8'))
print('\nTESTING DECRYPTION')
cte = input('Ciphertext: ')
pwd = input('Password..: ')
print('Message...:', AESCipher(pwd).decrypt(cte).decode('utf-8'))
@inebritov
Copy link

s[:-ord(s[len(s) - 1:])] looks quite difficult, may be better to use s[:-ord(s[-1])]?

@Zajozor
Copy link

Zajozor commented Feb 13, 2019

Also, lines 37 and 39 should contain BLOCK_SIZE instead of 16 i believe.

@Passion999
Copy link

when i run this code it occurs error,it says "Object type <class 'str'> cannot be passed to C code",i use python 3.7.

@lopes
Copy link
Author

lopes commented Jun 14, 2019

Code updated. PyCrypto is dead, so I'm using PyCryptodome from now on.

@markrowsoft
Copy link

The class attribute self.cipher should just be a regular variable (cipher)

@DineshGuptaa
Copy link

Please help to use 'AES/CBC/PKCS5PADDING' in Python 3 and pycryptodome==3.7.3

 String msg = "7977099688";
 key = hex("Mu8weQyDvq1HlAzN") XOR hex("PTYfmGYkEJpFem10")

 encrypted value should be "UyMO0aehZH/WS+GhjW5Q3A=="
 decryption value should be "7977099688"

By using provide algorithm, I am getting:

  KEY = '7458791236090c0c2c18690a38180321', PLAINTEXT= '7977099688'
  Ciphertext: +s1dIvYm6x4s1Yu0UB2/e5CAZ0n3WjvEVOOPPEr8izs= 

I am creating key in below method:

import binascii
static_str = 'Mu8weQyDvq1HlAzN'
st_arr = []
dy_arr = []
for b in bytearray(static_str, "utf-8"):
    st_arr.append(b)

token_str = "iNGRmNzJiNGM2NWUxM2I1ZGJmOTk4ZTVhYzUwYzYwYmM5MyIsImp0aSI6IjRjMmFkYmY2LTUxYjEtNGE3My04YjdhLWY1MDZhMmUwNjkzYSIsImlhdCI6MTU3ODQ4ODYyM30.SAjMKd0chcAWoFwMkfxJ-Z1lWRM9-AeSXuHZiXBTYyo"
token_str = token_str[-16:]
for b in bytearray(token_str, "utf-8"):
    dy_arr.append(b)
print("Static Bytes= %s \nDynamic Bytes= %s" % (st_arr, dy_arr))
res_byts = []
for bt in bytes(a ^ b for (a, b) in zip(st_arr, dy_arr)):
    res_byts.append(bt)
# Converting bytearray to hexadecimal string
key = binascii.hexlify(bytearray(res_byts))
key = key.decode("utf-8")

Help would be appropriated to achieve result. In java it is producing right result. For python I am struggling last two days ...

@markrowsoft
Copy link

markrowsoft commented Jan 10, 2020

If you are using Python 3, there is really no need to base64 encode as all strings in python are either bytes or unicode(utf-8)

Example below using:

**
MacOS 10.15.2
Python 3.8
pycryptodome 3.9.4**

  • ! I did not test with windows ! -
    Problems can occur if you have the older cryptography library installed concurrently with pycryptodome.
    Uninstall both and reinstall. Hopefully this helps.
from hashlib import md5

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad


class AESCipher:
    def __init__(self, key):
        password = key.encode('utf-8')
        self.key = md5(password).digest()

    def encrypt(self, data):
        vector = get_random_bytes(AES.block_size)
        encryption_cipher = AES.new(self.key, AES.MODE_CBC, vector)
        return vector + encryption_cipher.encrypt(pad(data,  AES.block_size))

    def decrypt(self, data):
        file_vector = data[:AES.block_size]
        decryption_cipher = AES.new(self.key, AES.MODE_CBC, file_vector)
        return unpad(decryption_cipher.decrypt(data[AES.block_size:]), AES.block_size)


if __name__ == '__main__':
    print('TESTING ENCRYPTION')
    msg = "helloWorld".encode('utf-8')
    pwd = "password"
    
    encrypted = AESCipher(pwd).encrypt(msg)
    print('Ciphertext:', encrypted)
    print('\nTESTING DECRYPTION')
    decrypted = AESCipher(pwd).decrypt(encrypted).decode('utf-8')
    print("Original data: ", msg.decode('utf-8'))
    print("Decripted data:", decrypted)
    assert msg.decode('utf-8') == decrypted 

@DineshGuptaa
Copy link

Hello Mark,

Thanks for your guide !!

Key should be create as below algorithm:

Secret Key Creation is as follow:
16 Character Static String ("Mu8weQyDvq1HlAzN")
16 Character from end of Token
    token_str = "iNGRmNzJiNGM2NWUxM2I1ZGJmOTk4ZTVhYzUwYzYwYmM5MyIsImp0aSI6IjRjMmFkYmY2LTUxYjEtNGE3My04YjdhLWY1MDZhMmUwNjkzYSIsImlhdCI6MTU3ODQ4ODYyM30.SAjMKd0chcAWoFwMkfxJ-Z1lWRM9-AeSXuHZiXBTYyo"

Converting above both string to Hex Code

(hex[Static_String])XOR(hex[Last 16 characters of SSO Token]) 

Java code working fine for key creation:

public  byte[] toHexadecimal(String text) throws UnsupportedEncodingException {
	byte[] myBytes = text.getBytes("UTF-8");
	return myBytes;
}

public  byte[] xorHex(byte[] a, byte[] b) throws UnsupportedEncodingException {
	byte[] result = new byte[16];
	int i = 0;
	for (byte Ab : a) {
		result[i] = (byte) (Ab ^ b[i++]);
	}
	return result;
}

Hex function creation create key as below:

    byte[] statickeyHEX = ob.toHexadecimal("Mu8weQyDvq1HlAzN");
byte[] ssoTokenHEX = ob.toHexadecimal(sso);
byte[] secretKey = ob.xorHex(statickeyHEX, ssoTokenHEX);
System.err.println("Secret Key>>>>>>>>>>>>>>"+secretKey);

Output of Secret Key = [B@7852e922

But in Python different. Please guide me to create key

@DineshGuptaa
Copy link

DineshGuptaa commented Jan 11, 2020

I have found the solution that work perfectly for me:
Code Reference : https://yococoxc.github.io/15493867450071.html

import Crypto.Cipher.AES
import Crypto.Random
import base64
import binascii


class Cipher_AES:
	pad_default = lambda x, y: x + (y - len(x) % y) * " ".encode("utf-8")
	unpad_default = lambda x: x.rstrip()
	pad_user_defined = lambda x, y, z: x + (y - len(x) % y) * z.encode("utf-8")
	unpad_user_defined = lambda x, z: x.rstrip(z)
	pad_pkcs5 = lambda x, y: x + (y - len(x) % y) * chr(y - len(x) % y).encode("utf-8")
	unpad_pkcs5 = lambda x: x[:-ord(x[-1])]

	def __init__(self, key="abcdefgh12345678", iv=Crypto.Random.new().read(Crypto.Cipher.AES.block_size)):
		self.__key = key
		self.__iv = iv

	def set_key(self, key):
		self.__key = key

	def get_key(self):
		return self.__key

	def set_iv(self, iv):
		self.__iv = iv

	def get_iv(self):
		return self.__iv

	def Cipher_MODE_ECB(self):
		self.__x = Crypto.Cipher.AES.new(self.__key.encode("utf-8"), Crypto.Cipher.AES.MODE_ECB)

	def Cipher_MODE_CBC(self):
		self.__x = Crypto.Cipher.AES.new(self.__key.encode("utf-8"), Crypto.Cipher.AES.MODE_CBC,
										 self.__iv.encode("utf-8"))

	def encrypt(self, text, cipher_method, pad_method="", code_method=""):
		if cipher_method.upper() == "MODE_ECB":
			self.Cipher_MODE_ECB()
		elif cipher_method.upper() == "MODE_CBC":
			self.Cipher_MODE_CBC()
		cipher_text = b"".join([self.__x.encrypt(i) for i in self.text_verify(text.encode("utf-8"), pad_method)])
		if code_method.lower() == "base64":
			return base64.encodebytes(cipher_text).decode("utf-8").rstrip()
		elif code_method.lower() == "hex":
			return binascii.b2a_hex(cipher_text).decode("utf-8").rstrip()
		else:
			return cipher_text.decode("utf-8").rstrip()

	def decrypt(self, cipher_text, cipher_method, pad_method="", code_method=""):
		if cipher_method.upper() == "MODE_ECB":
			self.Cipher_MODE_ECB()
		elif cipher_method.upper() == "MODE_CBC":
			self.Cipher_MODE_CBC()
		if code_method.lower() == "base64":
			cipher_text = base64.decodebytes(cipher_text.encode("utf-8"))
		elif code_method.lower() == "hex":
			cipher_text = binascii.a2b_hex(cipher_text.encode("utf-8"))
		else:
			cipher_text = cipher_text.encode("utf-8")
		return self.unpad_method(self.__x.decrypt(cipher_text).decode("utf-8"), pad_method)

	def text_verify(self, text, method):
		while len(text) > len(self.__key):
			text_slice = text[:len(self.__key)]
			text = text[len(self.__key):]
			yield text_slice
		else:
			if len(text) == len(self.__key):
				yield text
			else:
				yield self.pad_method(text, method)

	def pad_method(self, text, method):
		if method == "":
			return Cipher_AES.pad_default(text, len(self.__key))
		elif method == "PKCS5Padding":
			return Cipher_AES.pad_pkcs5(text, len(self.__key))
		else:
			return Cipher_AES.pad_user_defined(text, len(self.__key), method)

	def unpad_method(self, text, method):
		if method == "":
			return Cipher_AES.unpad_default(text)
		elif method == "PKCS5Padding":
			return Cipher_AES.unpad_pkcs5(text)
		else:
			return Cipher_AES.unpad_user_defined(text, method)


def main2(msg, token):
	st_arr = []
	dy_arr = []
	static_str = 'Mu8weQyDvq1HlAzN'
	for b in bytearray(static_str, "utf-8"):
		st_arr.append(b)

	token_str = token[-16:]
	for b in bytearray(token_str, "utf-8"):
		dy_arr.append(b)

	res_byts = []
	for bt in bytes(a ^ b for (a, b) in zip(st_arr, dy_arr)):
		res_byts.append(bt)

	key = bytes(res_byts).decode()
	iv = key 
	text = msg  # "7977099688"
	cipher_method = "MODE_CBC"
	pad_method = "PKCS5Padding"
	code_method = "base64"
	cipher_text = Cipher_AES(key, iv).encrypt(text, cipher_method, pad_method, code_method)
	cipher_text = cipher_text.replace('\n', '')
	print('[' + cipher_text + ']')
	print(type(cipher_text))
	text = Cipher_AES(key, iv).decrypt(cipher_text, cipher_method, pad_method, code_method)
	print(text)


if __name__ == '__main__':
	main2("405862999990475",
		  "CI6MTU3ODQ4ODYyM30.SAjMKd0chcAWoFwMkfxJ-Z1lWRM9-AeSXuHZiXBTYyo")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment