I've been trying to encrypt and decrypt strings in by AES and C# in an interoperable way. My client application is a Node server that communicates with vendor's API is in dot NET.
The vendor uses these methods for encrypting and decrypting the strings:
public static string Encrypt(string data, string key)
{
string IV = key.Substring(0, 16);
byte[] iv = Encoding.UTF8.GetBytes(IV);
byte[] array;
using(Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
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(data);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
public static string Decrypt(string data, string key)
{
string IV = key.Substring(0, 16);
byte[] iv = Encoding.UTF8.GetBytes(IV);
byte[] buffer = Convert.FromBase64String(data);
using(Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using(MemoryStream memoryStream = new MemoryStream(buffer))
{
using(CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using(StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
I tried for example crypto-js
for decrypting the strings, but I cannot make it work:
const encryptedText = CryptoJS.enc.Base64.parse(base64Value)
const encrypted2 = encryptedText.toString(CryptoJS.enc.Base64);
const decrypt2 = CryptoJS.AES.decrypt(encrypted2, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log(decrypt2.toString(CryptoJS.enc.Utf8)) // (also tried various other encodings, Utf16, Utf16LE and others)
The following Node.js code should work correctly with your .NET code.
We're using the algorithm aes-256-cbc
to match the mode used in the C# example.
const crypto = require("crypto");
const Algorithm = "aes-256-cbc";
function encrypt(plainText, key, iv, outputEncoding = "base64") {
const cipher = crypto.createCipheriv(Algorithm, key, iv);
const output = Buffer.concat([cipher.update(plainText), cipher.final()]).toString(outputEncoding);
return output.replace('+', '-').replace('/', '_').replace('=', '');
}
function decrypt(cipherText, key, iv, outputEncoding = "utf8") {
cipherText = Buffer.from(cipherText, "base64");
const cipher = crypto.createDecipheriv(Algorithm, key, iv);
return Buffer.concat([cipher.update(cipherText), cipher.final()]).toString(outputEncoding);
}
const KEY = 'KFmnMAPzP!g@6Dy5HD?JSgYC9obE&m@m';
const IV = KEY.slice(0,16);
// Taking the output from our C# sample...
const encrypted = 'SORoNS48u0KniiANU3Y9Mw==';
console.log("Encrypted (base64):", encrypted);
const decrypted = decrypt(encrypted, KEY, IV)
console.log("Decrypted:", decrypted);
The equivalent C# code is below:
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
public class Program
{
public static void Main()
{
var str = Encrypt("test", "KFmnMAPzP!g@6Dy5HD?JSgYC9obE&m@m");
Console.WriteLine("Encrypted: " + str);
Console.WriteLine("Decrypted: " + Decrypt(str, "KFmnMAPzP!g@6Dy5HD?JSgYC9obE&m@m"));
}
public static string Encrypt(string data, string key)
{
string IV = key.Substring(0, 16);
byte[] iv = Encoding.UTF8.GetBytes(IV);
byte[] array;
using(Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
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(data);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
public static string Decrypt(string data, string key)
{
string IV = key.Substring(0, 16);
byte[] iv = Encoding.UTF8.GetBytes(IV);
byte[] buffer = Convert.FromBase64String(data);
using(Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using(MemoryStream memoryStream = new MemoryStream(buffer))
{
using(CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using(StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
}
The output for the C# code is:
Encrypted: SORoNS48u0KniiANU3Y9Mw== Decrypted: test
The Node.js code then decrypts this (using the same key and IV):
Encrypted (base64): SORoNS48u0KniiANU3Y9Mw= Decrypted: test