I am decrypting the encoded data fetched from API using CryptoJS data is coming from node js crypto-js in a string format.
Here is what I have tried
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
make = make.json()['data']
key = "SecretPassphrase"
encrypted_make = self.decrypt(make,key)
def decrypt(self,encrypted, passphrase):
encrypted = base64.b64decode(encrypted)
IV = encrypted[:16]
aes = AES.new(passphrase.encode('utf-8'), AES.MODE_CFB, IV, segment_size=128)
return unpad(aes.decrypt(encrypted[16:]),aes.block_size)
I am getting the issue {ValueError}Padding is incorrect.
Can I know what I am trying wrong?
As per the discussion from the chat, we found out that the issue was not related to padding. Actually it is related to the decryption key.
The CryptoJS API considers the provided key as a pass phrase, not as a key. Thus, it processes it to create a key from it. When using that passphrase as the decryption key on the Python side, it results in inconsistent decoded data because the decryption key does not match the encryption key generated from the given pass phrase on the CryptoJS side.
According to the CryptoJS documentation, we have three points:
b"Salted__"
before the 8bytes IV.In order to convert the CryptoJS pass phrase to a key, the following piece of code helps (taken from Github):
def bytes_to_key(data, salt="12345678"):
# Simplified version of M2Crypto.m2.bytes_to_key(). Based on:
# https://github.com/ajmirsky/M2Crypto/blob/master/M2Crypto/EVP.py#L105
# http://stackoverflow.com/questions/8008253/c-sharp-version-of-openssl-evp-bytestokey-method
assert len(salt) == 8, len(salt)
data += salt
key = md5(data).digest()
key += md5(key + data).digest()
return key
Then the decryption logic mainly remains the same and goes like:
def bytes_to_key(data, salt, output=48):
assert len(salt) == 8, len(salt)
data += salt
key = md5(data).digest()
final_key = key
while len(final_key) < output:
key = md5(key + data).digest()
final_key += key
return final_key[:output]
def decrypt(encrypted, passphrase):
assert encrypted[0:8] == b"Salted__"
salt = encrypted[8:16]
key_iv = bytes_to_key(passphrase, salt, 32 + 16)
iv = key_iv[32:]
key = key_iv[:32]
aes = AES.new(key, AES.MODE_CBC, iv)
return unpad(aes.decrypt(encrypted[16:]), 16)