Skip to content Skip to sidebar Skip to footer

How To Decrypt Password From Javascript Cryptojs.aes.encrypt(password, Passphrase) In Python

I have a password which is encrypt from JavaScript via var password = 'sample' var passphrase ='sample_passphrase' CryptoJS.AES.encrypt(password, passphrase) Then I tried to

Solution 1:

You will have to implement OpenSSL's EVP_BytesToKey, because that is what CryptoJS uses to derive the key and IV from the provided password, but pyCrypto only supports the key+IV type encryption. CryptoJS also generates a random salt which also must be send to the server. If the ciphertext object is converted to a string, then it uses automatically an OpenSSL-compatible format which includes the random salt.

var data = "Some semi-long text for testing";
var password = "some password";
var ctObj = CryptoJS.AES.encrypt(data, password);
var ctStr = ctObj.toString();

out.innerHTML = ctStr;
<scriptsrc="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script><divid="out"></div>

Possible output:

U2FsdGVkX1+ATH716DgsfPGjzmvhr+7+pzYfUzR+25u0D7Z5Lw04IJ+LmvPXJMpz

CryptoJS defaults to 256 bit key size for AES, PKCS#7 padding and CBC mode. AES has a 128 bit block size which is also the IV size. This means that we have to request 32+16 = 48 byte from EVP_BytesToKey. I've found a semi-functional implementation here and extended it further.

Here is the full Python (tested with 2.7 and 3.4) code, which is compatible with CryptoJS:

from Cryptodome import Random
from Cryptodome.Cipher import AES
import base64
from hashlib import md5

BLOCK_SIZE = 16defpad(data):
    length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
    return data + (chr(length)*length).encode()

defunpad(data):
    return data[:-(data[-1] iftype(data[-1]) == intelseord(data[-1]))]

defbytes_to_key(data, salt, output=48):
    # extended from https://gist.github.com/gsakkis/4546068assertlen(salt) == 8, len(salt)
    data += salt
    key = md5(data).digest()
    final_key = key
    whilelen(final_key) < output:
        key = md5(key + data).digest()
        final_key += key
    return final_key[:output]

defencrypt(message, passphrase):
    salt = Random.new().read(8)
    key_iv = bytes_to_key(passphrase, salt, 32+16)
    key = key_iv[:32]
    iv = key_iv[32:]
    aes = AES.new(key, AES.MODE_CBC, iv)
    return base64.b64encode(b"Salted__" + salt + aes.encrypt(pad(message)))

defdecrypt(encrypted, passphrase):
    encrypted = base64.b64decode(encrypted)
    assert encrypted[0:8] == b"Salted__"
    salt = encrypted[8:16]
    key_iv = bytes_to_key(passphrase, salt, 32+16)
    key = key_iv[:32]
    iv = key_iv[32:]
    aes = AES.new(key, AES.MODE_CBC, iv)
    return unpad(aes.decrypt(encrypted[16:]))


password = "some password".encode()
ct_b64 = "U2FsdGVkX1+ATH716DgsfPGjzmvhr+7+pzYfUzR+25u0D7Z5Lw04IJ+LmvPXJMpz"

pt = decrypt(ct_b64, password)
print("pt", pt)

print("pt", decrypt(encrypt(pt, password), password))

Similar code can be found in my answers for Java and PHP.

JavaScript AES encryption in the browser without HTTPS is simple obfuscation and does not provide any real security, because the key must be transmitted alongside the ciphertext.

[UPDATE]:

You should use pycryptodome instead of pycrypto because pycrypto(latest pypi version is 2.6.1) no longer maintained and it has vulnerabilities CVE-2013-7459 and CVE-2018-6594 (CVE warning reported by github). I choose pycryptodomex package here(Cryptodome replace Crypto in code) instead of pycryptodome package to avoid conflict name with Crypto from pycrypto package.

Post a Comment for "How To Decrypt Password From Javascript Cryptojs.aes.encrypt(password, Passphrase) In Python"