Cannot match Rijdnael encryption between C# and Javascript / Node

107 views Asked by At

I need to convert an Rijndael encryption function from C# to Node. But I cannot match the result, even with the same Key, IV, Mode and Block Size. What am I doing wrong?

C# MRE:

using System.Security.Cryptography;
byte[] encrypted;

using (Rijndael rijAlg = Rijndael.Create())
{
    rijAlg.Key = new byte[] {  63, 115,  38, 206,  45, 102, 229,  13,
    161, 196, 250, 133, 149,  70, 153,  33,
    218,  32, 135, 149, 150,  21, 143,  11,
    210, 115, 185, 163,  24,  70, 145, 141 };

    rijAlg.IV = new byte[] { 151, 121, 168, 83, 221, 99, 206, 230, 74, 190, 106, 212, 232, 117, 192, 37 };

    ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

    using (MemoryStream msEncrypt = new MemoryStream())
    {
        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
        {
            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
            {
                swEncrypt.Write("1234");
            }
            encrypted = msEncrypt.ToArray();
        }
    }

    Console.WriteLine(rijAlg.BlockSize); //128
    Console.WriteLine(rijAlg.Mode); //CBC
    Console.WriteLine(rijAlg.Padding); //PKCS7
    Console.WriteLine(Convert.ToBase64String(encrypted)); //6d9SB6t9dktDJ+siSlFtOQ==
}

Node / Javascript MRE:

import Rijndael from "rijndael-js";

const key = Buffer.from([63, 115, 38, 206, 45, 102, 229, 13, 161, 196, 250, 133, 149, 70, 153, 33, 218, 32, 135, 149, 150, 21, 143, 11, 210, 115, 185, 163, 24, 70, 145, 141]);
const IV = Buffer.from([151, 121, 168, 83, 221, 99, 206, 230, 74, 190, 106, 212, 232, 117, 192, 37]);
const cipher = new Rijndael(key, "cbc");

const buffer = cipher.encrypt("1234", "128", IV);
console.log(Buffer.from(buffer).toString("base64")); //6ewzPyaxUgFX8IXW9iLJfw==

The result of encrypting "1234" in C# is "6d9SB6t9dktDJ+siSlFtOQ==" while in Node is "6ewzPyaxUgFX8IXW9iLJfw=="

2

There are 2 answers

0
Neftali Ramos On

This can be a bit tricky due to differences in how each environment handles encryption, especially with things like padding and block sizes.

In your C# code, you're using Rijndael with a block size of 128 bits (as indicated by rijAlg.BlockSize output), and the padding mode is PKCS7. This is pretty standard for Rijndael/AES encryption.

However, in your Node.js code, when you use rijndael-js and call cipher.encrypt, you're specifying "256" as the block size. This might be causing the mismatch. Rijndael supports various block sizes, but AES, which is a subset of Rijndael, is fixed at a block size of 128 bits.

So, a couple of things you might want to check:

  • Block Size: Make sure you're using a consistent block size across both your C# and Node implementations. If you're aiming for AES compatibility, this should be 128 bits.
  • Padding: Ensure that the padding scheme matches in both implementations. Mismatches in padding can lead to different results.
  • Encoding: Check that you're handling text and byte encoding consistently in both environments. Sometimes, issues can arise due to differences in default character encoding.

Try aligning these parameters and see if that resolves the discrepancy. Encryption can be a bit finicky with these details, so it's all about making sure everything lines up perfectly.

Hope this helps and best of luck with your project!

0
Topaco On

Both codes use different paddings: PKCS#7 padding in the C# code and Zero padding in the NodeJS code. This is the reason for the different ciphertexts.
In order for both codes to generate the same ciphertext, the same padding must be applied. Since PKCS#7 padding is more reliable than Zero padding, PKCS#7 padding should be used in both codes.

rijndael-js does not seem to support PKCS#7 padding, only Zero padding, s. here. Although PKCS#7 padding can easily be implemented by yourself, an alternative to the rijndael-js library is the built-in crypto module of NodeJS, which supports PKCS#7 padding by default.

Also note that rijndael-js implements the Rijndael algorithm, while in the C# code AES is used. This works because AES is a subset of Rijndael, s. here. On the other hand, AES and not Rijndael is the standard, and since the C# side uses AES, it is obvious to use a pure AES implementation on the NodeJS side as well, which applies to the crypto module of NodeJS.

A possible implementation with the crypto module of NodeJS is:

import crypto from 'crypto';
const key = Buffer.from([63, 115, 38, 206, 45, 102, 229, 13, 161, 196, 250, 133, 149, 70, 153, 33, 218, 32, 135, 149, 150, 21, 143, 11, 210, 115, 185, 163, 24, 70, 145, 141]);
const IV = Buffer.from([151, 121, 168, 83, 221, 99, 206, 230, 74, 190, 106, 212, 232, 117, 192, 37]);
const cipher = crypto.createCipheriv('aes-256-cbc', key, IV);
const ciphertext = Buffer.concat([cipher.update('1234', 'utf8'), cipher.final()]).toString('base64');
console.log(ciphertext); // 6d9SB6t9dktDJ+siSlFtOQ==

This implementation gives the same ciphertext as the C# code.