Suresh Pattu
Suresh Pattu

Reputation: 6219

AES/CBC/PKCS5Padding in NodeJs

I am trying to convert my java code to NodeJs code.
Facing some issues on the encryptions.
Here is my java code: compiled code

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.Base64;

public class AESCBCPKCS5Encryption {
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";

    public static String encrypt(String message, String key) throws GeneralSecurityException, UnsupportedEncodingException {
        if (message == null || key == null) {
            throw new IllegalArgumentException("text to be encrypted and key should not be null");
        }
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        byte[] messageArr = message.getBytes();
        SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(key), "AES");
        byte[] ivParams = new byte[16];
        byte[] encoded = new byte[messageArr.length + 16];
        System.arraycopy(ivParams, 0, encoded, 0, 16);
        System.arraycopy(messageArr, 0, encoded, 16, messageArr.length);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivParams));
        byte[] encryptedBytes = cipher.doFinal(encoded);
        encryptedBytes = Base64.getEncoder().encode(encryptedBytes);
        return new String(encryptedBytes);
    }

    public static String decrypt(String encryptedStr, String key) throws GeneralSecurityException, UnsupportedEncodingException {
        if (encryptedStr == null || key == null) {
            throw new IllegalArgumentException("text to be decrypted and key should not be null");
        }
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        SecretKeySpec keySpec = new
                SecretKeySpec(Base64.getDecoder().decode(key), "AES");
        byte[] encoded = encryptedStr.getBytes();
        encoded = Base64.getDecoder().decode(encoded);
        byte[] decodedEncrypted = new byte[encoded.length - 16];
        System.arraycopy(encoded, 16, decodedEncrypted, 0, encoded.length - 16);
        byte[] ivParams = new byte[16];
        System.arraycopy(encoded, 0, ivParams, 0, ivParams.length);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivParams));
        byte[] decryptedBytes = cipher.doFinal(decodedEncrypted);
        return new String(decryptedBytes);
    }

    public static void main(String[] args) throws GeneralSecurityException, UnsupportedEncodingException {
        String str = "<Request xmlns=\"http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd\" ><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>";
        String key = "e3a74e3c7599f3ab4601d587bd2cc768";
        String enc = encrypt(str, key);
        System.out.println(enc);
        String dec = decrypt(enc, key);
        System.out.println(dec);
    }
}

Here is my javascript code:

var crypto = require('crypto');

function getAlgorithm(keyBase64) {

    var key = Buffer.from(keyBase64, 'base64');
    switch (key.length) {
        case 16:
            return 'aes-128-cbc';
        case 32:
            return 'aes-256-cbc';

    }

    throw new Error('Invalid key length: ' + key.length);
}


function encrypt(plainText, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv  = Buffer.from(ivBase64, 'base64');

    const cipher  = crypto.createCipheriv(getAlgorithm(keyBase64), key, iv);
    let encrypted = cipher.update(plainText, 'utf8', 'base64');
    encrypted += cipher.final('base64');
    return encrypted;
}

function decrypt(messagebase64, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv  = Buffer.from(ivBase64, 'base64');

    const decipher = crypto.createDecipheriv(getAlgorithm(keyBase64), key, iv);
    let decrypted  = decipher.update(messagebase64, 'base64');
    decrypted += decipher.final();
    return decrypted;
}


var keyBase64 = "DWIzFkO22qfVMgx2fIsxOXnwz10pRuZfFJBvf4RS3eY=";
var ivBase64  = 'e3a74e3c7599f3ab4601d587bd2cc768';
var plainText = '<Request xmlns="http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd"><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>';

var cipherText          = encrypt(plainText, keyBase64, ivBase64);
var decryptedCipherText = decrypt(cipherText, keyBase64, ivBase64);

console.log('Algorithm: ' + getAlgorithm(keyBase64));
console.log('Plaintext: ' + plainText);
console.log('Ciphertext: ' + cipherText);
console.log('Decoded Ciphertext: ' + decryptedCipherText);

Encryptions throwing errors,
What I am doing wrong here?

Upvotes: 3

Views: 15521

Answers (3)

Arjun V
Arjun V

Reputation: 1

This is the solution for the above issue. I was getting the decryption error whenever I encrypt using the above code. I made a small update in the code by adding 16 characters at the start of plain text while encrypting.

var crypto = require('crypto');
function getAlgorithm(keyBase64) {

    var key = Buffer.from(keyBase64, 'base64');

    switch (key.length) {
        case 16:
            return 'aes-128-cbc';
        case 32:
            return 'aes-256-cbc';

    }
    throw new Error('Invalid key length: ' + key.length);
}


function encrypt(plainText, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv = Buffer.from(ivBase64, 'base64');

    const cipher = crypto.createCipheriv(getAlgorithm(keyBase64), key, iv);
    let encrypted = cipher.update(iv.subarray(0, 16) + plainText, 'utf8', 'base64')
    encrypted += cipher.final('base64');
    return encrypted;
};

function decrypt(messagebase64, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv = Buffer.from(ivBase64, 'base64');
    const decipher = crypto.createDecipheriv(getAlgorithm(keyBase64), key, iv);
    let decrypted = decipher.update(messagebase64, 'base64');
    decrypted += decipher.final();
    return decrypted;
}

var keyBase64 = "DWIzFkO22qfVMgx2fIsxOXnwz10pRuZfFJBvf4RS3eY=";
var ivBase64 = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).toString('base64');;
var plainText = '<Request xmlns="http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd"><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>';



var cipherText = encrypt(plainText, keyBase64, ivBase64);
var decryptedCipherText = decrypt(cipherText, keyBase64, ivBase64);

console.log('Algorithm: ' + getAlgorithm(keyBase64));
console.log('Plaintext: ' + plainText);
console.log('Ciphertext: ' + cipherText);
console.log('Decoded Ciphertext: ' + decryptedCipherText);

Upvotes: 0

Ranjeet Jha
Ranjeet Jha

Reputation: 53

in java key converted in 16 byte so 'AES/CBC/PKCS5Padding' algo applied while in nodejs this is convered into 24 byte so we need to apply 'aes-192-cbc'.

var crypto = require('crypto');

const encrypt = (plainText, keyBase64) =>{

    const textBuffer = Buffer.from(plainText);
    const key = Buffer.from(keyBase64, 'base64');
    var iv = Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
    var encoded = Buffer.concat([iv,textBuffer]);
    const cipher = crypto.createCipheriv('aes-192-cbc', key, iv);
    let encrypted = cipher.update(encoded, 'binary', 'base64')
    encrypted += cipher.final('base64');
    return encrypted;
};

const decrypt =  (messagebase64, keyBase64) =>{

    const key = Buffer.from(keyBase64, 'base64');
    const encoded = Buffer.from(messagebase64, 'base64');
    var iv = encoded.slice(0, 16);
    var decoded = encoded.slice(16,encoded.length);
    const decipher = crypto.createDecipheriv('aes-192-cbc', key, iv);
    let decrypted = decipher.update(decoded, 'binary','utf-8');
    decrypted += decipher.final();
    return decrypted;
}



 String str = "<Request xmlns=\"http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd\" ><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>";

String key = "e3a74e3c7599f3ab4601d587bd2cc768";

console.log("input:   "+str);
const hash = encrypt(str,key);
console.log("encrypted:::   " + hash);
const text = decrypt(hash,key);
console.log("output:   "+text); 

Upvotes: 3

Gaurav Dhiman
Gaurav Dhiman

Reputation: 1173

Initialization vector length of AES in CBC mode is 16 Bytes, code in JAVA takes first 16 bytes of IV however for node.js code it uses all 24 bytes of IV, hence an error. try this code

var crypto = require('crypto');

function getAlgorithm(keyBase64) {

    var key = Buffer.from(keyBase64, 'base64');
    switch (key.length) {
        case 16:
            return 'aes-128-cbc';
        case 32:
            return 'aes-256-cbc';

    }

    throw new Error('Invalid key length: ' + key.length);
}


function encrypt(plainText, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv  = Buffer.from(ivBase64, 'base64');

    const cipher  = crypto.createCipheriv(getAlgorithm(keyBase64), key, iv.slice(0, 16));
    let encrypted = cipher.update(plainText, 'utf8', 'base64');
    encrypted += cipher.final('base64');
    return encrypted;
}

function decrypt(messagebase64, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv  = Buffer.from(ivBase64, 'base64');

    const decipher = crypto.createDecipheriv(getAlgorithm(keyBase64), key, iv.slice(0, 16));
    let decrypted  = decipher.update(messagebase64, 'base64');
    decrypted += decipher.final();
    return decrypted;
}


var keyBase64 = "DWIzFkO22qfVMgx2fIsxOXnwz10pRuZfFJBvf4RS3eY=";
var ivBase64  = 'e3a74e3c7599f3ab4601d587bd2cc768';
var plainText = '<Request xmlns="http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd"><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>';

var cipherText          = encrypt(plainText, keyBase64, ivBase64);
var decryptedCipherText = decrypt(cipherText, keyBase64, ivBase64);

console.log('Algorithm: ' + getAlgorithm(keyBase64));
console.log('Plaintext: ' + plainText);
console.log('Ciphertext: ' + cipherText);
console.log('Decoded Ciphertext: ' + decryptedCipherText);

Upvotes: 6

Related Questions