Skip to content

Instantly share code, notes, and snippets.

Created Sep 17, 2021
What would you like to do?
Decrypt crypto-js default AES encryption with OpenSSL KDF in Python 3
# I absolutely hated crypto-js for this. non-standard configurations, weird algorithms, ...
# well obviously you can encrypt it with a better configuration which people will not
# go crazy figuring out its implementation, but in this case I wasn't encrypting the data.
import base64
from Crypto.Hash import MD5
from Crypto.Util.Padding import unpad
from Crypto.Cipher import AES
# generated using: CryptoJS.AES.encrypt('test 123456 plaintext', 'some password').toString()
ciphertext_b64 = "U2FsdGVkX180iqdQznzbX/7S9Zk9fRNwmtPeogG5TjB7565wNv15JoUWQ75VSYc7"
password = "some password"
encryptedData = base64.b64decode(ciphertext_b64)
# Get the salt for KDF
salt = encryptedData[8:16]
ciphertext = encryptedData[16:]
# PBKDF2 won't work, apparently crypto-js uses some shit called EVP or something
# it uses MD5 with 1 iteration by default, making me post this snippet
# oh, also guess what? when you ask crypto-js for the key size (CryptoJS.algo.AES.keySize)
# it will tell you it's 8, and use it like that in code, but turns out their "1" is 4 bytes
derived = b""
while len(derived) < 48: # "key size" + "iv size" (8 + 4 magical units = 12 * 4 = 48)
hasher =
hasher.update(derived[-16:] + password.encode('utf-8') + salt)
derived += hasher.digest()
# "8" key size = 32 actual bytes
key = derived[0:32]
iv = derived[32:48]
# fucking finally we got the actual key and IV
cipher =, AES.MODE_CBC, iv)
decrypted = unpad(cipher.decrypt(ciphertext), 16)
print(decrypted) # output: b'test 123456 plaintext'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment