Amir Rossert
Amir Rossert

Reputation: 1053

NodeJS AES encrypt Python decrypt

I have a nodejs service that uses AES encryption on some data that needs to be decrypted in Python. No matter what I do I cannot make this to work. NodeJS code:

const algorithm = 'aes-128-ctr';

function encryptScript(data, key) {
    const cipher = crypto.createCipher(algorithm, key);

    let crypted = cipher.update(data, 'utf8', 'hex');
    crypted += cipher.final('hex');

    return crypted;
}

I have tried in Python:

counter = Counter.new(128)
cipher = AES.new(key, AES.MODE_CTR, counter=counter)
print cipher.decrypt(enc.decode("hex"))

But it is not working.

My first issue is that the Python code is not accepting keys that are longer than 32 bytes (and the Nodejs code does).

If I use NodeJS crypto module the decryption is working:

function decryptScript(data, key) {
    const decipher = crypto.createDecipher(algorithm, key);
    let dec = decipher.update(data, 'hex', 'utf8');
    dec += decipher.final('utf8');
    return dec;
}

I don't know what node is doing but it is probably related to some padding of the data.

How can I make it work?

(I will prefer a solution that will not require changes in the NodeJS code but only in the Python script).

Upvotes: 1

Views: 1281

Answers (1)

Topaco
Topaco

Reputation: 49251

  • CreateCipher uses EVP_BytesToKey to create a key and an IV from a password (what is called a key in the NodeJS-code is actually the password). Here is an implementation of EVP_BytesToKey in Python. The parameters to use are described in the documentation of CreateCipher: MD5, no salt, one iteration. In CTR-mode the IV is usually incremented with each block starting with a value to be defined. CreateCipher uses as start value the IV determined with EVP_BytesToKey. The functionality of CreateCipher can therefore be implemented in Python as follows:

    import hashlib
    from Crypto.Cipher import AES
    from Crypto.Util import Counter
    
    ...
    
    encrypted = '5e99b5190f12143c057f6bdd8625f958682e737c11e138a2f571c050313dbe1008347604c7c7e8bf506a0a'   # Example
    
    # Generate key and iv
    keySize = 16
    ivSize = 16
    digest = hashlib.md5
    salt = b''
    password = b'123456'                                                                                   # Example
    iteration = 1
    keyiv = EVP_BytesToKey(keySize, ivSize, digest, salt, password, iteration)
    key = keyiv[0]
    iv = keyiv[1]
    
    # Define counter
    nbits = 128
    initial_value = int.from_bytes(iv, byteorder = 'big');
    counter = Counter.new(nbits, initial_value = initial_value)
    
    # Decrypt
    cipher = AES.new(key, AES.MODE_CTR, counter = counter)
    decrypted = cipher.decrypt(bytes.fromhex(encrypted))
    print("Decrypted: " + decrypted.decode('utf8'))
    

    The ciphertext was generated with the NodeJS-code using the following input:

    key = '123456';
    data = 'The quick brown fox jumps over the lazy dog';
    
  • Note that CreateCipher is deprecated and should no longer be used, especially not in combination with the CTR-mode. Instead CreateCipheriv can be used. In CTR-mode, it is important that a key/IV-pair is used only once. Otherwise the security will be lost, see here. CreateCipher doesn't provide randomization, i.e. the same password always generates the same key and IV and thus always the same keystream. Therefore, security is lost if the same password is used several times. CreateCipheriv, on the other hand, expects a key and a random IV. Here a key can be used several times as long as the randomization of the IV ensures that key/value-pairs are not repeated, see here.

Upvotes: 1

Related Questions