Decryption AES decrypt() not yielding results - python

I am running into the issue of my decrypt_file function unable to decrypt text which was encrypted, No errors are provided just unexpected returns.
The encryption function uses salt and a password to encrypt a text file, then saves it as a .enc file, Then after attempting to run my decrypt_file function, it identifies the salt, iv, password, iterations, ect but does not decrypt() properly. Here is the code. And a dry run. Any guidance or help would be appreciated.
Encryption/Decryption:
from Crypto import Random
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
from os import urandom
import hashlib
def key_generation(password, salt, iterations):
assert iterations > 0
print "Salt: " + salt, '\n', "Password: " + password
key = password + salt #Combines [password] and [salt] to create a [key]
for i in range(iterations): #Hashes the [key]
key = hashlib.sha256(key).digest() #Using Sha256 it hashes the [key] based on amount of [iterations]
print '\nKey: ' + key #Debug Print
return key
def pad(s):
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def encryption(message, password, salt, iterations, key_size=256):
key = key_generation(password, salt, iterations)
message = pad(message)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
print "Random IV: " + iv
enc = salt + iv + cipher.encrypt(message)
print "Length: " + str(len(enc))
enc = pad(enc)
print "Length: " + str(len(enc))
return enc
def decryption(ciphertext, password, iterations):
salt = ciphertext[0:16]
iv = ciphertext[:AES.block_size]
print len(str(iv))
print len(str(salt))
key = key_generation(password, salt, iterations)
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[AES.block_size:])
print "Plaintext: " + plaintext
return plaintext.rstrip(b"\0")
def encrypt_file(file_name, password, salt, iterations):
with open(file_name, 'rb') as fo:
plaintext = fo.read()
print "Text: " + plaintext
enc = encryption(plaintext, password, salt, iterations)
print "Encrypted Contents: " + enc
with open(file_name + ".enc", 'wb') as fo:
fo.write(enc)
def decrypt_file(file_name, password, iterations):
with open(file_name, 'rb') as fo:
ciphertext = fo.read()
print "Cipher'd Text: " + ciphertext
dec = decryption(ciphertext, password, iterations)
with open(file_name[:-4], 'wb') as fo:
fo.write(dec)
encrypt_file('HelloWorld.txt', 'password', 'randomsalt', 64000)
decrypt_file('HelloWorld.txt.enc', 'password', 64000)
Dry Run:
Encrypt:
Text: }¦—Z“Íd¥çgZw?øÈH™«Nœfra¥)ÊãjnÞª»^}K^Ì„¦ý
×¾Šv“_3w€mG9‚ä¤Å¥žUƯ0£Óy®0²
nrfÖÖ «–‰¯ò
Salt: randomsalt
Password: password
Key: /Îbdヘ5è!ニヒAᆰv=L*øK/ò)Ü
Random IV: eミý1ËÈUÓbIワᄡムl
Length: 138
Length: 144
Encrypted Contents: randomsalteミý1ËÈUÓbIワᄡムl$֭>oリ)<L゙y\I!%wÙßÞlモÊJt$ワ
è Ì-ᄈMᄀ8ヘ!ᄚܩᄃÆ4ÒO9AÃðO.ä3}ヘål{ヌヒ#ÿËzᄋgDᆰ\éUʼaà8タLᅠMupaÔAミマX0ンᄉi3ヨ˧cᄃ;ᆱÛo
Decrypt:
Cipher'd Text: randomsalteミý1ËÈUÓbIワᄡムl$֭>oリ)<L゙y\I!%wÙßÞlモÊJt$ワ
è Ì-ᄈMᄀ8ヘ!ᄚܩᄃÆ4ÒO9AÃðO.ä3}ヘål{ヌヒ#ÿËzᄋgDᆰ\éUʼaà8タLᅠMupaÔAミマX0ンᄉi3ヨ˧cᄃ;ᆱÛo
16
16
Salt: randomsalteミý1Ë
Password: password
Key: 1ÜA !TzxGÑ`wß~|º‹|¡(—ª-%òÇŒÖ
Plaintext: Rネ*SᄊÕñÛ.
t-Îテýト͛'úᄎSタ&2ᆴæマéヌᄏýýᄑtçØÉe?×ûìrモᄈÞcᄎᄇ×_Kメ
ᄎÀ~ä'ᄅ,ᄉ-Èᄀt&gSð:WÕ|
メ^リøᄃ]ノヤÏYvísgl/ᆵレホ*`\ᄚåᄌŴÇlヒÓ!Â`゚

So please print things out in hexadecimals. You can then clearly see that indeed during decrypt that the salt and IV were overlapping. In your code you were also assuming a different length salt than that was given.
from Crypto import Random
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
from os import urandom
import hashlib
import binascii
def key_generation(password, salt, iterations):
assert iterations > 0
print "Salt: " + tohex(salt), '\n', "Password: " + password
key = password + salt #Combines [password] and [salt] to create a [key]
for i in range(iterations): #Hashes the [key]
key = hashlib.sha256(key).digest() #Using Sha256 it hashes the [key] based on amount of [iterations]
return key
def pad(s):
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def encryption(message, password, salt, iterations, key_size=256):
print " === ENCRYPTION === "
key = key_generation(password, salt, iterations)
print "Key: " + tohex(key)
message = pad(message)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
print "Random IV: " + tohex(iv)
enc = salt + iv + cipher.encrypt(message)
return enc
def decryption(ciphertext, password, iterations):
print " === DECRYPTION === "
salt = ciphertext[0:10]
iv = ciphertext[10:10+AES.block_size]
print "Random IV: " + tohex(iv)
key = key_generation(password, salt, iterations)
print "Key: " + tohex(key)
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[10+AES.block_size:])
print "Plaintext: " + plaintext
return plaintext.rstrip(b"\0")
def encrypt_file(file_name, password, salt, iterations):
with open(file_name, 'rb') as fo:
plaintext = fo.read()
print "Text: " + plaintext
enc = encryption(plaintext, password, salt, iterations)
print "Encrypted Contents: " + enc
with open(file_name + ".enc", 'wb') as fo:
fo.write(enc)
def decrypt_file(file_name, password, iterations):
with open(file_name, 'rb') as fo:
ciphertext = fo.read()
print "Cipher'd Text: " + ciphertext
dec = decryption(ciphertext, password, iterations)
with open(file_name + ".dec", 'wb') as fo:
fo.write(dec)
def tohex(data):
return binascii.hexlify(data)
encrypt_file('HelloWorld.txt', 'password', 'randomsalt', 64000)
decrypt_file('HelloWorld.txt.enc', 'password', 64000)

Related

AES encryption/decryption implementation problem

encryption.py
# AES 256 encryption/decryption using pycrypto library
import base64
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
password = input("Enter encryption password: ")
def encrypt(raw, password):
private_key = hashlib.sha256(password.encode("utf-8")).digest()
raw = pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(private_key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(enc, password):
private_key = hashlib.sha256(password.encode("utf-8")).digest()
enc = base64.b64decode(enc)
iv = enc[:16]
cipher = AES.new(private_key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc[16:]))
# First let us encrypt secret message
encrypted = encrypt("This is a secret message", password)
print(encrypted)
# Let us decrypt using our original password
decrypted = decrypt(encrypted, password)
print(bytes.decode(decrypted))
decryption.py
import base64
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Protocol.KDF import PBKDF2
BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
password = input("Enter encryption password: ")
def get_private_key(password):
salt = b"this is a salt"
kdf = PBKDF2(password, salt, 64, 1000)
key = kdf[:32]
return key
def encrypt(raw, password):
private_key = get_private_key(password)
raw = pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(private_key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(enc, password):
private_key = get_private_key(password)
enc = base64.b64decode(enc)
iv = enc[:16]
cipher = AES.new(private_key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc[16:]))
# First let us encrypt secret message
encrypted = encrypt("This is a secret message", password)
print(encrypted)
# Let us decrypt using our original password
decrypted = decrypt(encrypted, password)
print(bytes.decode(decrypted))
i was learning computer security and learned AES encryption. i wanted to make a simple program that implements this encryption i found this program but we i run it this happens
Enter encryption password: test
Traceback (most recent call last):
File "c:\AES testing\encryptionAES.py", line 31, in <module>
encrypted = encrypt("This is a secret message", password)
File "c:\AES testing\encryptionAES.py", line 19, in encrypt
return base64.b64encode(iv + cipher.encrypt(raw))
File "C:\Users\xxxx\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\Crypto
\Cipher\_mode_cbc.py", line 178, in encrypt
c_uint8_ptr(plaintext),
File "C:\Users\xxxx\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\Crypto
\Util\_raw_api.py", line 242, in c_uint8_ptr
raise TypeError("Object type %s cannot be passed to C code" % type(data))
TypeError: Object type <class 'str'> cannot be passed to C code
so when running the code the above error happens the result should have been the secret password but am not getting that what am i doing wrong

Probles with AES encrypting. cant decrypt with a right key

I'm trying to decrypt encrypdet data. Data was encrypted with AES CBC mode using pycryptodome lib.
Have a such error - "ValueError: Incorrect AES key length (256 bytes)
import os
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher:
def __init__(self, key):
pass
def pad(self, s):
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def encrypt(self, message, key, key_size=256):
message = self.pad(message)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(message)
def decrypt(self, ciphertext, key):
iv = ciphertext[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[AES.block_size:])
return plaintext.rstrip(b"\0")
def send_data(data)
key = os.urandom(16)
cipher = AESCipher(key)
ciphertext = cipher.encrypt(data, key)
return key, ciphertext
def receive_data(key, data):
cipher = AESCipher(key)
decrypted = cipher.decrypt(data, key)
return decrypted
data = b'12 43 42 46 af'
key, ciphertext = send_data(data)
decrypted = receive_data(key, data)
I think that what you want to decrypt is the ciphered text, not the original data (non-encrypted):
decrypted = receive_data(key, ciphertext)

Setting Variables in Module from different program

I am struggling with converting this piece of code into a module in where I can use an outside program to set the variables in this piece of code. How would I go about setting variables in one program and set them in another(to be used as a module) and get the results from said program into the first program.
Here is the code, any advice/help would be greatly appreciated.
import Crypto.Random
from Crypto.Cipher import AES
import hashlib
SALT_SIZE = 16
iterations = 64000
salt = 'h5eE0b814M'
password = 'fortytwo'
text = 'What do you mean'
padded_text = ''
ciphertext = ''
key = ''
ciphertext_with_salt = ''
def key_generation(password, salt, iterations):
global key
assert iterations > 0
key = password + salt
for i in range(iterations):
key = hashlib.sha256(key).digest()
print '\nKey: ' + key #Debug Print
return key
def pad_text(text, SALT_SIZE):
print '\nUnpadded Text: ' + text #Debug Print
global padded_text
extra_bytes = len(text) % SALT_SIZE
pad_size = SALT_SIZE - extra_bytes
pad = chr(pad_size) * pad_size
padded_text = text + pad
print '\nPadded Text: ' + padded_text #Debug Print
return padded_text
def encryption(text, password):
global ciphertext
global key
salt1 = Crypto.Random.get_random_bytes(SALT_SIZE)
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = pad_text(text, SALT_SIZE)
ciphertext = cipher.encrypt(padded_text)
ciphertext_with_salt = salt + ciphertext
#debug script
print '\nSalt: ' + salt #Debug Print
print '\nEncrypted Text: ' + ciphertext #Debug Print
print '\nEncrypted Text with Salt: ' + ciphertext_with_salt #Debug Print
return ciphertext_with_salt
def decryption(ciphertext, password):
salt = ciphertext[0:SALT_SIZE]
ciphertext_sans_salt = ciphertext[SALT_SIZE:]
key = key_generation(password, salt, iterations)
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = cipher.decrypt(ciphertext_sans_salt)
print '\nUnencrypted Text: ' + text #Debug Print
return text
key_generation(password, salt, iterations)
encryption(text, password)
decryption(ciphertext, password)
Simply put
if __name__ == '__main__':
Before your last 3 lines. Then you can import it like any other module and call functions with e.g. yourmodulename.encryption(text, password).

Python Encryption - Unexpected Variable Return

I am currently having an issue of not being able to decrypt the text provided after encryption. It returns:
Key: ンƚ!*!゙ᆱø}Qd`Dᆱd!Þxͦ}ᄚミᄀ>'U
Unpadded Text: Hello World
Padded Text: Hello World
Salt: h5eE0b814M
Encrypted Text: WxCž~¼!Ò]Cú´=P+
Encrypted Text with Salt: h5eE0b814MWxCž~¼!Ò]Cú´=P+
Key: ンƚ!*!゙ᆱø}Qd`Dᆱd!Þxͦ}ᄚミᄀ>'U
Unencrypted Text:
Where
Unencrypted Text:
Should be "Unencypted Text: Hello World"
Two programs are used in this, one a module and a master. You must run the master to run the module. Any adivce or help would be greatly appricated as I have been stuck for a while. Thank you for your time.
Here is the code:
Master.py
import Encryption as encrypt
#Place Holder Variables
SALT_SIZE = 16
padded_text = ''
ciphertext = ''
key = ''
ciphertext_with_salt = ''
#Adjustable Variables
text = "Hello World"
iterations = 62705
salt = 'h5eE0b814M'
password = 'pause232'
encrypt.key_generation(password, salt, iterations)
encrypt.encryption(text, password, SALT_SIZE, salt, iterations)
encrypt.decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations)
Encryption.py
import Crypto.Random
from Crypto.Cipher import AES
import hashlib
#Key Generation(Used in encyption to create cipher)
def key_generation(password, salt, iterations):
global key
assert iterations > 0
key = password + salt #Combines [password] and [salt] to create a [key]
for i in range(iterations): #Hashes the [key]
key = hashlib.sha256(key).digest() #Using Sha256 it hashes the [key] based on amount of [iterations]
print '\nKey: ' + key #Debug Print
return key
#Text padding function to set text to a incerment of SALT_SIZE
def pad_text(text, SALT_SIZE):
print '\nUnpadded Text: ' + text #Debug Print
global padded_text
extra_bytes = len(text) % SALT_SIZE #Using the length of [text] it counts how many more characters is required to make an incerment of [SALT_SIZE]
pad_size = SALT_SIZE - extra_bytes #Subtracts the needed bytes from the [SALT_SIZE] and sets [pad_size] as the length of pading needed.
pad = chr(pad_size) * pad_size #Creates padding for the [text]
padded_text = text + pad #Adds the [pad] to the [text]
print '\nPadded Text: ' + padded_text #Debug Print
#Primary Encryption Function(using text and password)
def encryption(text, password, SALT_SIZE, salt, iterations):
global padded_text
global ciphertext
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = pad_text(text, SALT_SIZE)
ciphertext = cipher.encrypt(padded_text)
ciphertext_with_salt = salt + ciphertext
#debug script
print '\nSalt: ' + salt #Debug Print
print '\nEncrypted Text: ' + ciphertext #Debug Print
print '\nEncrypted Text with Salt: ' + ciphertext_with_salt #Debug Print
return ciphertext_with_salt
#Primary Decryption Function(using the encrypted text and password)
def decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations):
ciphertext_with_salt = ciphertext[SALT_SIZE:]
key = key_generation(password, salt, iterations)
cipher = AES.new(key, AES.MODE_ECB)
unencrypted_text = cipher.decrypt(ciphertext_with_salt)
print '\nUnencrypted Text: ' + unencrypted_text #Debug Print
return unencrypted_text
#Code to allow to use as outside module
if __name__ == '__main__':
key_generation(password, salt, iterations)
encryption(text, password, SALT_SIZE, salt, iterations)
decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations)
You are transferring your ciphertext and key as strings. You should make sure that the bytes that make up the ciphertext and keys stay intact. If you want to transport them using character strings, please use either hexadecimal encoding or base 64. Hexadecimals are easier to read and check for length, base 64 is more efficient.
Note that the ciphertext will contain bytes with any value, including ones that are ASCII control characters (< 0x20) which also contain termination characters such as 0x00.

Encrypting and Decrypting with python and nodejs

I'm trying to encrypt some content in Python and decrypt it in a nodejs application.
I'm struggling to get the two AES implementations to work together though. Here is where I am at.
In node:
var crypto = require('crypto');
var password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var input = 'hello world';
var encrypt = function (input, password, callback) {
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
// add padding
while (input.length % 16 !== 0) {
input += ' ';
}
var data = new Buffer(input, 'utf8').toString('binary');
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
var encrypted = cipher.update(data, 'binary') + cipher.final('binary');
var encoded = new Buffer(encrypted, 'binary').toString('base64');
callback(encoded);
};
var decrypt = function (input, password, callback) {
// Convert urlsafe base64 to normal base64
var input = input.replace('-', '+').replace('/', '_');
// Convert from base64 to binary string
var edata = new Buffer(input, 'base64').toString('binary')
// Create key from password
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
// Create iv from password and key
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
// Decipher encrypted data
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));
var decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
var plaintext = new Buffer(decrypted, 'binary').toString('utf8');
callback(plaintext);
};
encrypt(input, password, function (encoded) {
console.log(encoded);
decrypt(encoded, password, function (output) {
console.log(output);
});
});
This produces the output:
BXSGjDAYKeXlaRXVVJGuREKTPiiXeam8W9e96Nknt3E=
hello world
In python
from Crypto.Cipher import AES
from hashlib import md5
import base64
password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
input = 'hello world'
def _encrypt(data, nonce, password):
m = md5()
m.update(password)
key = m.hexdigest()
m = md5()
m.update(password + key)
iv = m.hexdigest()
# pad to 16 bytes
data = data + " " * (16 - len(data) % 16)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
encrypted = aes.encrypt(data)
return base64.urlsafe_b64encode(encrypted)
def _decrypt(edata, nonce, password):
edata = base64.urlsafe_b64decode(edata)
m = md5()
m.update(password)
key = m.hexdigest()
m = md5()
m.update(password + key)
iv = m.hexdigest()
aes = AES.new(key, AES.MODE_CBC, iv[:16])
return aes.decrypt(edata)
output = _encrypt(input, "", password)
print(output)
plaintext = _decrypt(output, "", password)
print(plaintext)
This produces the output
BXSGjDAYKeXlaRXVVJGuRA==
hello world
Clearly they are very close, but node seems to be padding the output with something. Any ideas how I can get the two to interoperate?
OK, I've figured it out, node uses OpenSSL which uses PKCS5 to do padding. PyCrypto doesn't handle the padding so I was doing it myself just add ' ' in both.
If I add PKCS5 padding in the python code and remove the padding in the node code, it works.
So updated working code.
Node:
var crypto = require('crypto');
var password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var input = 'hello world';
var encrypt = function (input, password, callback) {
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
var data = new Buffer(input, 'utf8').toString('binary');
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
// UPDATE: crypto changed in v0.10
// https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var encrypted;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
encrypted = cipher.update(data, 'binary') + cipher.final('binary');
} else {
encrypted = cipher.update(data, 'utf8', 'binary') + cipher.final('binary');
}
var encoded = new Buffer(encrypted, 'binary').toString('base64');
callback(encoded);
};
var decrypt = function (input, password, callback) {
// Convert urlsafe base64 to normal base64
var input = input.replace(/\-/g, '+').replace(/_/g, '/');
// Convert from base64 to binary string
var edata = new Buffer(input, 'base64').toString('binary')
// Create key from password
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
// Create iv from password and key
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
// Decipher encrypted data
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));
// UPDATE: crypto changed in v0.10
// https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var decrypted, plaintext;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
plaintext = new Buffer(decrypted, 'binary').toString('utf8');
} else {
plaintext = (decipher.update(edata, 'binary', 'utf8') + decipher.final('utf8'));
}
callback(plaintext);
};
encrypt(input, password, function (encoded) {
console.log(encoded);
decrypt(encoded, password, function (output) {
console.log(output);
});
});
Python:
from Crypto.Cipher import AES
from hashlib import md5
import base64
password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
input = 'hello world'
BLOCK_SIZE = 16
def pad (data):
pad = BLOCK_SIZE - len(data) % BLOCK_SIZE
return data + pad * chr(pad)
def unpad (padded):
pad = ord(chr(padded[-1]))
return padded[:-pad]
def get_key_iv (password):
m = md5()
m.update(password.encode('utf-8'))
key = m.hexdigest()
m = md5()
m.update((password + key).encode('utf-8'))
iv = m.hexdigest()
return [key,iv]
def _encrypt(data, password):
key,iv = get_key_iv(password)
data = pad(data)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
encrypted = aes.encrypt(data)
return base64.urlsafe_b64encode(encrypted)
def _decrypt(edata, password):
edata = base64.urlsafe_b64decode(edata)
key,iv = get_key_iv(password)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
return unpad(aes.decrypt(edata))
output = _encrypt(input, password)
print(output)
plaintext = _decrypt(output, password)
print(plaintext)
while trying to run the Python script using Python 3.8 I encountered the following error:
m.update(password)
TypeError: Unicode-objects must be encoded before hashing
the password should be :
password = b'abcd'
I also got the following error :
m.update(password + key)
TypeError: can't concat str to bytes
I was able to fix it by adding the following line after key:
key = bytes.fromhex(key_)
The python script should work this way :
from Crypto.Cipher import AES
from hashlib import md5
import base64
password = b'abcd'
input = 'hello world'
BLOCK_SIZE = 16
def pad (data):
pad = BLOCK_SIZE - len(data) % BLOCK_SIZE
return data + pad * chr(pad)
def unpad (padded):
pad = ord(chr(padded[-1]))
return padded[:-pad]
def _encrypt(data, nonce, password):
m = md5()
m.update(password)
key_ = m.hexdigest()
key = bytes.fromhex(key_)
m = md5()
m.update(password + key)
iv = m.hexdigest()
iv = bytes.fromhex(iv)
data = pad(data)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
encrypted = aes.encrypt(data.encode('utf-8'))
return base64.urlsafe_b64encode(encrypted)
def _decrypt(edata, nonce, password):
edata = base64.urlsafe_b64decode(edata)
m = md5()
m.update(password)
key = m.hexdigest()
key = bytes.fromhex(key)
m = md5()
m.update(password + key)
iv = m.hexdigest()
iv = bytes.fromhex(iv)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
return unpad(aes.decrypt(edata))
output = _encrypt(input, "", password)
print(output)
plaintext = _decrypt(output, "", password)
print(plaintext)
Just for any one that is similar to me, who was finding a simple way to do the encryption and decryption for AES in python that is doing the same thing in node.js. The class here supports different bits of AES and both hex and base64 encoding that produces same result in node.js.
Also noted that if you are missing the package Crypto, you can simply install it by
pip install pycrypto
The code for python is as follows:
import base64
import hashlib
from Crypto.Cipher import AES
class AESCrypto(object):
def __init__(self, algorithm, password):
self.algorithm = filter(lambda x: not x.isdigit(), algorithm).lower()
self.bits = int(filter(str.isdigit, algorithm))
self.bs = 16
if not self.algorithm == 'aes':
raise Exception('Only AES crypto is supported')
if not self.bits % 8 == 0:
raise Exception('Bits of crypto must be a multiply of 8.')
self.bytes = self.bits / 8
self.password = password
self.generateKeyAndIv()
def generateKeyAndIv(self):
last = ''
allBytes = ''
maxBytes = self.bytes + self.bs
while len(allBytes) < maxBytes:
last = hashlib.md5(last + self.password).digest()
allBytes += last
self.key = allBytes[:self.bytes]
self.iv = allBytes[self.bytes:maxBytes]
def encrypt(self, raw, outputEncoding):
outputEncoding = outputEncoding.lower()
raw = self._pad(raw)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
encrypted = cipher.encrypt(raw)
if outputEncoding == 'hex':
return encrypted.encode('hex')
elif outputEncoding == 'base64':
return base64.b64encode(encrypted)
else:
raise Exception('Encoding is not supported.')
def decrypt(self, data, inputEncoding):
inputEncoding = inputEncoding.lower()
if inputEncoding == 'hex':
data = ''.join(map(chr, bytearray.fromhex(data)))
elif inputEncoding == 'base64':
data = base64.b64decode(data)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return self._unpad(cipher.decrypt(data))
def _pad(self, data):
padding = self.bs - len(data) % self.bs
return data + padding * chr(padding)
#staticmethod
def _unpad(data):
return data[0:-ord(data[-1])]
The following are examples to use the class:
Encryption Example:
password = 'some_random_password'
content = 'content_to_be_encrypted'
cipher = AESCrypto('aes192', password)
encrypted = cipher.encrypt(content, 'hex')
Decryption Example:
password = 'some_random_password'
content = 'encrypted_content'
cipher = AESCrypto('aes192', password)
decrypted = cipher.decrypt(content, 'hex')
Because I spent way too much time on this with Python 3.10.7 and Node.js v18.6.0.
Here is a working code totally compatible between two languages with examples.
Only the secret is needed for getting same values as expected :)
Note pycryptodome is needed for Python. Code should be tweaked for supporting different algorithms.
const crypto = require('crypto')
function get_crypto(secret, encode) {
// Create hashed key from password/key
let m = crypto.createHash('md5').update(secret)
const key = m.digest('hex')
m = crypto.createHash('md5').update(secret + key)
const iv = m.digest('hex').slice(0, 16) // only in aes-256
return encode
? crypto.createCipheriv('aes-256-cbc', key, iv)
: crypto.createDecipheriv('aes-256-cbc', key, iv)
}
const secret = 'f8abb29f13cb932704badb0de414ab08ca9f6c63' // crypto.randomBytes(20).toString('hex')
const value = 'hello world'
const data = Buffer.from(value, 'utf8').toString('binary')
const cipher = get_crypto(secret, true)
const encrypted = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]).toString('binary')
const encoded = Buffer.from(encrypted, 'binary').toString('base64')
console.log('encoded:', encoded)
const edata = Buffer.from(encoded, 'base64').toString('binary')
const decipher = get_crypto(secret, false)
const decoded = Buffer.concat([decipher.update(edata, 'binary'), decipher.final()]).toString('utf-8')
console.log('decoded:', decoded)
# This script needs pycryptodome dependency
# pip install pycryptodome
from Crypto.Cipher import AES
from hashlib import md5
import base64
BLOCK_SIZE = AES.block_size
def get_aes(s):
m = md5()
m.update(s.encode('utf-8'))
key = m.hexdigest()
m = md5()
m.update((s + key).encode('utf-8'))
iv = m.hexdigest()
return AES.new(key.encode("utf8"), AES.MODE_CBC, iv.encode("utf8")[:BLOCK_SIZE])
# pkcs5 padding
def pad(byte_array):
pad_len = BLOCK_SIZE - len(byte_array) % BLOCK_SIZE
return byte_array + (bytes([pad_len]) * pad_len)
# pkcs5 - unpadding
def unpad(byte_array):
return byte_array[:-ord(byte_array[-1:])]
def _encrypt(s, data):
data = pad(data.encode("UTF-8"))
aes = get_aes(s)
encrypted = aes.encrypt(data)
return base64.urlsafe_b64encode(encrypted).decode('utf-8')
def _decrypt(s, edata):
edata = base64.urlsafe_b64decode(edata)
aes = get_aes(s)
return unpad(aes.decrypt(edata)).decode('utf-8')
if __name__ == '__main__':
secret = 'f8abb29f13cb932704badb0de414ab08ca9f6c63'
value = 'hello world'
encoded = _encrypt(secret, value)
print('encoded:', encoded)
decoded = _decrypt(secret, encoded)
print('decoded:', decoded)
Help from:
Implementing AES/ECB/PKCS5 padding in Python
Node.js - Set padding in crypto module
Python Encrypting with PyCrypto AES

Categories