I'm trying to match an AES 256 CBC encryption implemented in C# by using node JS crypto module.
This is my C# code
using System;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
Console.WriteLine(EncryptExt("Hello World"));
Console.WriteLine(DecryptExt(EncryptExt("Hello World")));
}
public static string EncryptExt(string raw)
{
using (var csp = new AesCryptoServiceProvider())
{
ICryptoTransform e = GetCryptoTransformExt(csp, true);
byte[] inputBuffer = Encoding.UTF8.GetBytes(raw);
byte[] output = e.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
string encrypted = Convert.ToBase64String(output);
return encrypted;
}
}
public static string DecryptExt(string encrypted)
{
using (var csp = new AesCryptoServiceProvider())
{
var d = GetCryptoTransformExt(csp, false);
byte[] output = Convert.FromBase64String(encrypted);
byte[] decryptedOutput = d.TransformFinalBlock(output, 0, output.Length);
string decypted = Encoding.UTF8.GetString(decryptedOutput);
return decypted;
}
}
private static ICryptoTransform GetCryptoTransformExt(AesCryptoServiceProvider csp, bool encrypting)
{
csp.Mode = CipherMode.CBC;
csp.Padding = PaddingMode.PKCS7;
var passWord = Convert.ToString("AvbSkj3BVbf4o6mdlAofDp0/SD0susEWo0pKdmqas");
var salt = Convert.ToString("ABj4PQgf3j5gblQ0iDp0/Gb07ukQWo0a");
String iv = Convert.ToString("aAB1jhPQ89o=f619");
var spec = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(passWord), Encoding.UTF8.GetBytes(salt), 65536);
byte[] key = spec.GetBytes(16);
csp.IV = Encoding.UTF8.GetBytes(iv);
csp.Key = key;
if (encrypting)
{
return csp.CreateEncryptor();
}
return csp.CreateDecryptor();
}
}
And this is my Node JS implementation
const crypto = require('crypto'),
algorithm = 'aes-128-cbc',
password = 'AvbSkj3BVbf4o6mdlAofDp0/SD0susEWo0pKdmqas',
salt = 'ABj4PQgf3j5gblQ0iDp0/Gb07ukQWo0a',
iv = 'aAB1jhPQ89o=f619',
inputEncoding = 'utf8',
outputEncoding = 'base64';
function encrypt(text) {
let cipher = crypto.createCipheriv(algorithm,createHashPassword(), iv);
let encrypted = cipher.update(text, inputEncoding, outputEncoding)
encrypted += cipher.final(outputEncoding);
return encrypted;
}
function createHashPassword(){
let nodeCrypto = crypto.pbkdf2Sync(Buffer.from(password), Buffer.from(salt), 65536, 16, 'sha1');
return nodeCrypto || nodeCrypto.toString('hex');
};
function decrypt(encrypted) {
let decipher = crypto.createDecipheriv(algorithm, Buffer.from(createHashPassword(),"hex"), iv)
let dec = decipher.update(encrypted, outputEncoding, inputEncoding)
dec += decipher.final(inputEncoding);
return dec;
}
console.log(encrypt('Hello World'));
console.log(decrypt(encrypt('Hello World')));
The encrypted data from both this options are coming different hence, not able to work this out.
So far what I have seen is,
Code can be tested in the below link: C# Implementation: https://dotnetfiddle.net/bClrpW Node JS Implementation: https://runkit.com/a-vi-nash/5c062544509d8200156f6111
It seems that you are creating a AES-128
instance in your C#
code, because you are using 16 bytes keylen.
AES-256
keylen is 32 bytes, not 16 bytes.
Bugs in code:
C#
, it uses AES-128
, not AES-256
. So you need to change node.js
to AES-128
or change generated key to 32 bytes in both sides.base64
encoded), your node.js
side uses incorrect pbkdf2Sync
parameters.IV
len for AES
algorithm in 16 bytes and you cannot use shorter ones.Since you wanted AES-256
here are your changed to both sides:
C#
side:
String iv = Convert.ToString("SOME_IV_SOME_IV_"); // 16 bytes IV
....
byte[] key = spec.GetBytes(32); // 32 bytes key
node.js
side:
iv = 'SOME_IV_SOME_IV_' // 16 bytes IV similar to C#
...
// Bugs in this function
function createHashPassword(){
// Change parameters to `base64` only if salt and password are base64. it may be true for salt, but it is can rarely be correct for password.
let nodeCrypto = crypto.pbkdf2Sync(Buffer.from(password), Buffer.from(salt), 65536, 32, 'sha1');
return nodeCrypto;
};
IMPORTANT NOTES:
IV
must be selected as a random buffer(neither fixed not text) and since it seems that you are sending it over network, you need to send IV
with it too.SALT
must be a random buffer(not text) and fixed on both sides.PBKDF2
at least.