1

I am trying to create a multi-platform encryption-decryption mechanism, so far I have been able to encrypt in python and decrypt in C and vice versa, now I am trying to do the same using the python script and a node js script. I am able to encrypt a string in node js and decrypt it in python but decryption in Node using the encrypted message of python is not happening Here is the python code :

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto import Random
from base64 import b64decode
from base64 import b64encode
import json
import random

#iv= get_random_bytes(16)
key=b"aaaaaaaaaaaaaaaa"
iv= b"aaaaaaaaaaaaaaaa"
value = "Hello World"
strValue= str.encode(value)
data =strValue

#Encryption
data = b64encode(data)
pad =data + b"\0" * (AES.block_size - len(data) % AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
ciphertext= cipher.encrypt(pad)
print (type(ciphertext))

print(b64encode(ciphertext).decode("utf-8"))

# Decryption
cipher = AES.new(key, AES.MODE_CBC, iv)
data = cipher.decrypt(ciphertext)
print(b64decode(data))

Here is the Nodejs code:

const crypto = require('crypto'); 

var iv = Buffer.from('aaaaaaaaaaaaaaaa')
var key =  Buffer.from('aaaaaaaaaaaaaaaa')
var cipher = crypto.createCipheriv('aes-128-cbc', key, iv);

let enc= cipher.update( "Hello World");
console.log(typeof (enc))
enc += cipher.final('base64');

console.log("enc is :",enc)



var decipher = crypto.createDecipheriv('aes-128-cbc', key,iv);
let decrypted = decipher.update(enc, 'base64');
decrypted += decipher.final('utf8'); 
console.log("plain text is :",decrypted)

I took the node portion from : AES - Encryption with Crypto (node-js) / decryption with Pycrypto (python) I am getting the error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt Any help would be much appreciated, Thank you! And if there is any better method for Node js implementation please tell.

phoenix
  • 29
  • 4

1 Answers1

1

In the Python code the plaintext is Base64 encoded before encryption, in the NodeJS code it is not. Furthermore, the Python code applies a custom Zero padding, the NodeJS code the default PKCS7 padding.

For the NodeJS code to provide the same ciphertext as the Python code, the plaintext must be Base64 encoded before encryption. Furthermore the default PKCS7 padding must be disabled and the Zero padding variant of the Python code must be applied. Since PyCryptodome does not support Zero padding, a custom implementation is required.

A possible NodeJS implementation could be:

const crypto = require('crypto') 
const buffertrim = require('buffertrim') 

function toB64padded(plaintext, blocksize){
    var bufPlaintext = Buffer.from(plaintext, 'utf8')
    var bufPlaintextB64 = Buffer.from(bufPlaintext.toString('base64'), 'utf8')      // Base64 encoding
    var bufPadding = Buffer.alloc(blocksize - bufPlaintextB64.length % blocksize)
    return Buffer.concat([bufPlaintextB64, bufPadding])                             // Zero padding
}

var iv = Buffer.from('aaaaaaaaaaaaaaaa')                                            // Static IV only for testing purposes
var key =  Buffer.from('aaaaaaaaaaaaaaaa')

// Encryption
var plaintext = "The quick brown fox jumps over the lazy dog"
var bufPlaintextB64padded = toB64padded(plaintext, 16)                              // Base64 encoding and Zero padding
var cipher = crypto.createCipheriv('aes-128-cbc', key, iv)
cipher.setAutoPadding(false)                                                        // Disable PKCS7 padding

var ciphertextB64 = cipher.update(bufPlaintextB64padded, '', 'base64')              // Encryption, Base64 encoding of ciphertext 
ciphertextB64 += cipher.final('base64')
console.log("ciphertext is:", ciphertextB64)  

// Decryption
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv)
decipher.setAutoPadding(false)                                                      // Disable PKCS7 (un-) padding

var bufPlaintextB64padded = Buffer.concat([                                         // Base64 decoding of ciphertext, decryption
    decipher.update(ciphertextB64, 'base64'), 
    decipher.final()
]);                                                                                 
var bufPlaintextB64 = buffertrim.trimEnd(bufPlaintextB64padded)                     // Unpadding (unreliable)
var bufPlaintext = Buffer.from(bufPlaintextB64.toString('utf8'), 'base64')          // Base64 decoding
console.log("plaintext is:", bufPlaintext.toString('utf8'))

As an example the plaintext The quick brown fox jumps over the lazy dog is encrypted with the Python code and the NodeJS code above. Both codes produce with the posted key and IV the same ciphertext, namely:

IETFUbiTGsKFZWLgjjP5RrKPX+GeVon1Kuy38bPdKXwqUJWUGWMJ9MOL9gEAsF+1U/N0Juwzu24Dju4UMwdZaA== 

That means it is possible to encrypt with one code and decrypt with the other.


Note that it is not necessary to Base64 encode the plaintext before encryption. Also, Zero padding is an unreliable padding that is better replaced by PKCS7 padding. PyCryptodome supports PKCS7 padding with the Crypto.Util.Padding module, so no custom implementation is needed for padding. And as already mentioned, the crypto module of NodeJS uses PKCS7 padding by default.

Furthermore for security reasons a random IV must be used for each encryption. The IV, which is not secret, is usually concatenated with the ciphertext. In the Python code the random generation of the IV is implemented (but commented out, probably for testing purposes). The concatenation part is missing, however. In the NodeJS code both are omitted for compatibility with the Python code.

Topaco
  • 18,591
  • 2
  • 12
  • 39