Search code examples
encryptionaescryptojspycrypto

encrypt using ase256 gives different output in python and nodejs


I am trying to encrypt a string "1" using key = "secret_key" and text "11869021012". Earlier I had written this in nodejs. now I want to port this to python. but here surprisingly both are giving different outputs.

var crypto = require('crypto');

function getBytes (str) {
  let bytes = [], char;
  str = encodeURI(str);
  while (str.length) {
    char = str.slice(0, 1);
    str = str.slice(1);

    if ('%' !== char) {
      bytes.push(char.charCodeAt(0));
    } else {
      char = str.slice(0, 2);
      str = str.slice(2);

      bytes.push(parseInt(char, 16));
    }
  }
  return bytes;
};


function getIV (str, bytes){
    iv = getBytes(str);
    if(!bytes) bytes = 16;
    for(let i=iv.length;i<bytes;i++) {
      iv.push(0);
    }
    return Buffer.from(iv);
};

function getKey (pwd){
    pwd = Buffer.from(getBytes(pwd), 'utf-8');
    let hash = crypto.createHash('sha256');
    pwd = hash.update(pwd).digest();
    return pwd;
};

function createCipherIV (algorithm, input_key, iv_input, text){
    let iv = getIV(iv_input);
    let key = getKey(input_key);
    let cipher = crypto.createCipheriv(algorithm, key, iv);
    let encrypted = cipher.update(text)
    encrypted += cipher.final('base64');
    return encrypted;
}

output = createCipherIV('aes256', 'secret_key', '11869021012', '1') 
console.log(output)

This produces the output: s6LMaE/YRT6y8vr2SehLKw==

python code:

# AES 256 encryption/decryption using pycrypto library
import base64
import hashlib

from Crypto.Cipher import AES
from Crypto import Random

BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]

password = "secret_key"

def encrypt(raw, password):
    private_key = hashlib.sha256(bytearray(password, "utf-8")).digest()
    raw = pad(raw)
    iv = b'11869021012\x00\x00\x00\x00\x00'
    cleartext = bytearray(raw, 'utf-8')
    cipher = AES.new(private_key, AES.MODE_CBC, iv)

    return base64.b64encode(iv + cipher.encrypt(cleartext))

# First let us encrypt secret message
encrypted = encrypt("1", password)
print(encrypted)

This produces the output: MTE4NjkwMjEwMTIAAAAAALOizGhP2EU+svL69knoSys=

I have used aes256 algorithm here for encrypting message. Clearly they are very close, but node seems to be padding the output with some extra bytes. Any ideas how I can get the two to interoperate?


Solution

  • First, in a secure crypto system, you should expect the output to be different every time you encrypt, even using the same code. That fact that yours doesn't indicates it's an insecure cipher. Typically this is done by adding a random IV.

    Your IV is "11869021012", which is horrible (because it's not random, and not even 16 bytes), but it does seem you're using it the same way in both, so that's fine.

    Your password is the SHA-256 of a string, which is a horrible way to create a key, but still, you seem to be doing it the same way in both cases, so that's fine.

    Your problem is that the Python code emits the IV followed by the cipher text. Your JS code does not emit the IV; it only emits the cipher text. So you probably meant this in the Python:

    return base64.b64encode(cipher.encrypt(cleartext))
    

    Or you need to rework the JavaScript to glue together the IV and the cipher text before Base64 encoding.