I'm trying to encrypt something in NodeJs with Rjndael-js and I'm not getting the same result that I'm getting with Java and C#. I think I must be doing something wrong with how I'm encoding strings going into the JS cipher, but I can't figure it out. The java code and the c# below both output the same result which is not the same as the JS. Please help. Thnx
Here's the JS:
const toHexString = (bytes) => {
return Array.from(bytes, (byte) => {
return ('0' + (byte & 0xff).toString(16)).slice(-2);
}).join('');
};
const hexToBytes = (hex) => {
var bytes = [];
for (var c = 0; c < hex.length; c += 2) {
bytes.push(parseInt(hex.substr(c, 2), 16));
}
return bytes;
};
const stringToEncrypt = 'test';
const keyAsHex = '12341234123412341234123412341234';
const ivAsHex = '43214321432143214321432143214321';
const keyAsBytes = hexToBytes(keyAsHex);
const ivAsBytes = hexToBytes(ivAsHex);
const Rijndael = require('rijndael-js');
const cipher = new Rijndael(keyAsBytes, 'cbc');
const cipherAsBytes = cipher.encrypt(stringToEncrypt, 128, ivAsBytes);
console.log("cipherAsHex",toHexString(cipherAsBytes)); //outputs: 172e3a9bec36e96f8f7f1da65e6e876c
Here's the Java:
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class cryptoTesting {
public static void main(String[] args) {
try{
String keyAsHex = "12341234123412341234123412341234";
String ivAsHex = "43214321432143214321432143214321";
String message = "test";
byte[] messageAsBytes = message.getBytes();
byte[] keyAsBytes = HexFormat.of().parseHex(keyAsHex);
byte[] ivAsBytes = HexFormat.of().parseHex(ivAsHex);
System.out.println(Arrays.toString(messageAsBytes));
System.out.println(Arrays.toString(keyAsBytes));
System.out.println(Arrays.toString(ivAsBytes));
String plaintext = "test";
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//You can use ENCRYPT_MODE or DECRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyAsBytes, "AES"), new IvParameterSpec(ivAsBytes));
byte[] cipherAsBytes = cipher.doFinal(messageAsBytes);
String cipherAsHex = HexFormat.of().formatHex(cipherAsBytes);
System.out.println (cipherAsHex); //outputs: 647768a8c969dc195e34c7968514494f
} catch (Exception exc){
exc.printStackTrace(System.out);
//System.out.write(exc);
}
}
}
And here's the C#
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace RijndaelManaged_Example
{
class RijndaelExample
{
public static void Main()
{
string original = "test";
// Create a new instance of the RijndaelManaged
// class. This generates a new key and initialization
// vector (IV).
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
myRijndael.Key = Convert.FromHexString("12341234123412341234123412341234");
myRijndael.IV = Convert.FromHexString("43214321432143214321432143214321");
myRijndael.BlockSize = 128;
// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes(original, myRijndael.Key, myRijndael.IV);
PrintByteArray(encrypted);
Console.WriteLine(Convert.ToHexString(encrypted)); //647768A8C969DC195E34C7968514494F
}
}
public static void PrintByteArray(byte[] bytes)
{
var sb = new StringBuilder("new byte[] { ");
foreach (var b in bytes)
{
sb.Append(b + ", ");
}
sb.Append("}");
Console.WriteLine(sb.ToString());
}
static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
rijAlg.BlockSize = 128;
Console.WriteLine(rijAlg.IV);
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
}
encrypted = msEncrypt.ToArray();
}
}
PrintByteArray(Key);
PrintByteArray(IV);
// Return the encrypted bytes from the memory stream.
return encrypted;
}
}
}
rijndael.js applies hard-coded zero-padding, s. here, while in the Java and C# code PKCS#7 padding is used. Zero-padding is unreliable in contrast to PKCS#7 padding. You should consider switching to a different NodeJS library.
Because of the block and key size you are using (both 128 bits), you don't need a Rijndael library, but an AES library will suffice. NodeJS has its own crypto module (that supports AES, CBC and PKCS#7, among others), which is a more secure and convenient alternative.
If you absolutely want to stick to rijndael.js, you can implement PKCS#7 padding yourself. Since the zero-padding variant used here does not pad if the plaintext length already equals a multiple of the block size, a plaintext padded with PKCS#7 is not padded again with zero-padding (i.e. the PKCS#7 padding automatically disables zero-padding).
The functions for padding and unpadding are:
function pad (text, bs) {
var padLen = bs - text.length % bs;
return text.padEnd(text.length + padLen, String.fromCharCode(padLen))
}
function unpad(text){
var padLen = text.charCodeAt(text.length - 1);
return text.substring(0, text.length - padLen);
}
Example of use:
const Rijndael = require('rijndael-js');
const stringToEncrypt = 'test';
const keyAsHex = '12341234123412341234123412341234';
const ivAsHex = '43214321432143214321432143214321';
// Encryption
const keyAsBytes = hexToBytes(keyAsHex);
const ivAsBytes = hexToBytes(ivAsHex);
const cipher = new Rijndael(keyAsBytes, 'cbc');
const cipherAsBytes = cipher.encrypt(pad(stringToEncrypt, 16), 128, ivAsBytes); // Fix: pad with PKCS#7
console.log("cipherAsHex", toHexString(cipherAsBytes)); //outputs: cipherAsHex 647768a8c969dc195e34c7968514494f
// Decryption
const decryptedAsBytes = cipher.decrypt(cipherAsBytes, 128, ivAsBytes);
const decryptedPadded = Buffer.from(cipher.decrypt(cipherAsBytes, 128, ivAsBytes)).toString();
const decrypted = unpad(decryptedPadded); // Fix: unpad with PKCS#7
console.log("decryptedPadded", decryptedPadded, "decrypted", decrypted); //outputs: decryptedPadded test decrypted test