I am using AES/GCM/NoPadding 128 bit. I want to encrypt in Java & decrypt it in Javascript.
When I encrypt in JS & try to decrypt in Java I get the error as Tag mismatch!null
When I encrypt in Java & try to decrypt in JS I get the error as
internal/crypto/cipher.js:164
const ret = this._handle.final();
^
Error: Unsupported state or unable to authenticate data
at Decipheriv.final (internal/crypto/cipher.js:164:28)
at decrypt (/tmp/HoErdq6TQ2.js:51:58)
What am I missing in my JS
Please suggest fixes in JS code, Java code can't be changed as it is in use on production
Java code:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import java.util.logging.Logger;
import java.util.Base64;
public class HelloWorld {
private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
public static void main(String []args) {
String masterKey = "2f12cb0f1d2e3d12345f1af2b123dce4";
String encrypted = aesEncryptStringV2("Hello, World!", masterKey);
System.out.println(encrypted);
String decrypted = aesDecryptStringV2(encrypted, masterKey);
System.out.println(decrypted);
}
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int GCM_IV_LENGTH = 12;
private static final int GCM_TAG_LENGTH = 16;
private static SecretKeySpec setKeyV2(final String myKey) {
try {
byte[] newKey = myKey.getBytes(StandardCharsets.UTF_8);
MessageDigest sha = MessageDigest.getInstance("SHA-512");
newKey = sha.digest(newKey);
newKey = Arrays.copyOf(newKey, 16);
return new SecretKeySpec(newKey, "AES");
} catch (Exception e) {
System.out.println("Error in setKeyV2: ");
System.out.println(e.getMessage());
}
return null;
}
public static synchronized String aesEncryptStringV2(
final String strToEncrypt, final String secret) {
try {
SecretKeySpec newSecretKey = setKeyV2(secret);
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec gcmParameterSpec = new
GCMParameterSpec(GCM_TAG_LENGTH * 8,
new byte[GCM_IV_LENGTH]);
cipher.init(Cipher.ENCRYPT_MODE, newSecretKey, gcmParameterSpec);
return Base64.getEncoder()
.encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8
)));
} catch (Exception e) {
System.out.println("Error in aesEncryptStringV2: ");
System.out.println(e.getMessage());
}
return null;
}
public static synchronized String aesDecryptStringV2(
final String strToDecrypt, final String secret) {
try {
SecretKeySpec newSecretKey = setKeyV2(secret);
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec gcmParameterSpec = new
GCMParameterSpec(GCM_TAG_LENGTH * 8,
new byte[GCM_IV_LENGTH]);
cipher.init(Cipher.DECRYPT_MODE, newSecretKey, gcmParameterSpec);
return new
String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error in aesDecryptStringV2: ");
System.out.println(e.getMessage());
}
return null;
}
}
Javascript Code:
const crypto = require('crypto');
const cryptoConfig = {
cipherAlgorithm: 'aes-128-gcm',
masterKey: '2f12cb0f1d2e3d12345f1af2b123dce4',
ivLength: 12,
tagLength: 16,
digest: 'sha512'
}
const generateKey = () => {
var h = crypto.createHash(cryptoConfig.digest);
h.update(cryptoConfig.masterKey, 'utf8');
var k = h.digest().slice(0, 16);
return k;
};
function encrypt(content) {
const iv = crypto.randomBytes(cryptoConfig.ivLength);
const key = generateKey();
const cipher = crypto.createCipheriv(cryptoConfig.cipherAlgorithm, key, iv, {authTagLength: cryptoConfig.tagLength});
const encrypted = Buffer.concat([cipher.update(content, 'utf8'), cipher.final()]);
const tag = cipher.getAuthTag();
return Buffer.concat([iv, encrypted, tag]).toString('base64');
}
const decrypt = (encdata, masterkey) => {
const bData = Buffer.from(encdata, 'base64');
const iv = bData.slice(0, 12);
const tag = bData.slice(bData.length - 16, bData.length);
const text = bData.slice(12, bData.length - 16);
const key = generateKey(masterkey);
const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
decipher.setAuthTag(tag);
const decrypted =
decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');
return decrypted;
};
const encryptedData = encrypt('hello world');
console.log('encrypt data -> ', encryptedData);
const decryptedData = decrypt(encryptedData);
console.log('decryptedData -> ', decryptedData);
So the Java code doesn't attach IV/nonce to the output. It uses a buffer of 0's of specified length.
Updated JS decryption code:
const decrypt = (encdata, masterkey) => {
const bData = Buffer.from(encdata, 'base64');
const iv = Buffer.alloc(12);
const tag = bData.slice(bData.length - 16, bData.length);
const text = bData.slice(0, bData.length - 16);
const key = generateKey(masterkey);
const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
decipher.setAuthTag(tag);
const decrypted =
decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');
return decrypted;
};
Serious security warning: Do NOT use the above code in production as it is UNSECURE. You should consider to change your production code as the encryption in AES GCM mode gets total insecure when using a nonce more than one time with the same key (the Java code is using a static IV/nonce)
Credits - Michael Fehr's comment
I am using the getReport operation to fetch the documentId, which later I use to download the report document itself which is encrypted and compressed.
The code looks like this:
const documentData = await this.sellingPartner.callAPI({
operation: "getReportDocument",
endpoint: "reports",
path: { reportDocumentId: reportData.reportDocumentId }
})
const request = https.get(documentData.url, function(res) {
const data = [];
res.on("data", chunk => data.push(chunk));
res.on("end", () => {
const key = new Buffer.from(documentData.encryptionDetails.key, 'base64')
const initializationVector = new Buffer.from(documentData.encryptionDetails.initializationVector, 'base64')
const input = Buffer.concat(data)
let result;
try {
result = aes.decryptText(
aes.CIPHERS.AES_256,
key,
initializationVector,
input
)
} catch (e) {
console.log(e)
}
console.log(">>>>")
console.log(result)
zlib.gunzip(result, (err, unzipped) => {
debugger
});
});
}
The current error I am getting is from zlib:
Error: incorrect header check
at Zlib.zlibOnError [as onerror] (node:zlib:189:17)
I am getting the same even if I pass the unencrypted value directly to zlib.
There is a Sample Java code example in the docs, but I cannot understand very well where they do the decryption: before unzipping or after?
In any case, what is the right way to solve this: unzip and decrypt or decrypt and unzip? The former does not work at all, the latter almost works but fails at the unzipping part.
How can I solve the unzip problem?
Short Answer:
Decrypt first and then decompress if compression type is specified.
Longer answer:
After a little research I stumbled upon this example (sadly written in python not in javascript) but it covers the steps to do in more detail.
https://github.com/amzn/selling-partner-api-docs/issues/186#issuecomment-756108550
It contains both compressed or not compressed cases.
Specifically for compressed it looks like this:
import gzip
import requests
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def ase_cbc_decryptor(key, iv, encryption):
cipher = Cipher(algorithms.AES(base64.b64decode(key)), modes.CBC(base64.b64decode(iv)))
decryptor = cipher.decryptor()
decrypted_text = decryptor.update(encryption)
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
unpaded_text = unpadder.update(decrypted_text)
return unpaded_text + unpadder.finalize()
def get_report_document_content(self, key, iv, url, compression_type=None):
resp = requests.get(url=url)
resp_content = resp.content
decrypted_content = ase_cbc_decryptor(key=key, iv=iv, encryption=resp_content)
if compression_type == 'GZIP':
decrypted_content = gzip.decompress(decrypted_content)
code = 'utf-8'
if 'cp1252' in resp.headers.get('Content-Type', '').lower():
code = 'Cp1252'
return decrypted_content.decode(code)
P.S. keep in mind you need to use AES in CBC mode
Short example here:
https://gist.github.com/manuks/5cef1e536ef791e97b39
var keyhex = "8479768f48481eeb9c8304ce0a58481eeb9c8304ce0a5e3cb5e3cb58479768f4"; //length 32
var blockSize = 16;
function encryptAES(input) {
try {
var iv = require('crypto').randomBytes(16);
//console.info('iv',iv);
var data = new Buffer(input).toString('binary');
//console.info('data',data);
key = new Buffer(keyhex, "hex");
//console.info(key);
var cipher = require('crypto').createCipheriv('aes-256-cbc', key, iv);
// UPDATE: crypto changed in v0.10
// https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var encrypted;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
encrypted = cipher.update(data, 'binary') + cipher.final('binary');
} else {
encrypted = cipher.update(data, 'utf8', 'binary') + cipher.final('binary');
}
var encoded = new Buffer(iv, 'binary').toString('hex') + new Buffer(encrypted, 'binary').toString('hex');
return encoded;
} catch (ex) {
// handle error
// most likely, entropy sources are drained
console.error(ex);
}
}
function decryptAES(encoded) {
var combined = new Buffer(encoded, 'hex');
key = new Buffer(keyhex, "hex");
// Create iv
var iv = new Buffer(16);
combined.copy(iv, 0, 0, 16);
edata = combined.slice(16).toString('binary');
// Decipher encrypted data
var decipher = require('crypto').createDecipheriv('aes-256-cbc', key, iv);
// UPDATE: crypto changed in v0.10
// https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var decrypted, plaintext;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
plaintext = new Buffer(decrypted, 'binary').toString('utf8');
} else {
plaintext = (decipher.update(edata, 'binary', 'utf8') + decipher.final('utf8'));
}
return plaintext;
}
var input="testing";
var encrypted = encryptAES(input);
console.info('encrypted:', encrypted);
var decryped = decryptAES(encrypted);
console.info('decryped:',decryped);
I want to decode a BASE64 to String with ciphertext, key, and iv using Crypto-JS. I get the error Malformed UTF-8 data.
My code:
const CryptoJS = require('crypto-js')
const fs = require('fs');
// BASE64
const str = fs.readFileSync('file.txt','utf8')
const genRanHex = function(_0x8d0344) {
for (var _0x15f7b3 = [], _0x2491b5 = '0123456789abcdef', _0x2b0a63 = 0x0; _0x2b0a63 < _0x8d0344; _0x2b0a63++) _0x15f7b3['push'](_0x2491b5['charAt'](Math['floor'](Math['random']() * _0x2491b5['length'])));
return _0x15f7b3['join']('');
}
var KEYS = genRanHex(0x10) + '3c07f4b0efef700a',
ciphertext = str['substring'](0x18),
key = CryptoJS['enc']['Hex']['parse'](KEYS),
iv = CryptoJS.enc.Base64.parse(str.substring(0x0, 0x18))
try {
const decrypted = CryptoJS.AES.decrypt(ciphertext, key, {iv }).toString(CryptoJS.enc.Utf8);
var originalText = JSON.parse(decrypted)
console.log(originalText)
} catch (err) {
console.error(err)
}
My clean data is : Test12345678910
My secret key is : 12345678901234561234567890123456
When i encrypt the data i get this output : ldJbAK2rYjDnS6kWz2O+Aw==
when i decrypt it i get error
internal/crypto/cipher.js:164
const ret = this._handle.final();
^
Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
my code is ;
const crypto = require('crypto');
const encrypted = "ldJbAK2rYjDnS6kWz2O+Aw==";
const key = "12345678901234561234567890123456";
let decipher = crypto.createDecipher('aes-256-cbc', key);
let decrypted = decipher.update(encrypted);
decrypted = [decrypted, decipher.final("utf-8")];
console.log(decrypted.toString());
Encrypting from C#
public static string EncryptToken(string text)
{
string confkey = "12345678901234561234567890123456";
byte[] array;
try
{
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(confkey);
aes.IV = new byte[16];
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
{
streamWriter.Write(text);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
catch
{
return string.Empty;
}
}
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?
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);
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);
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);