Created
July 29, 2022 17:57
-
-
Save scottrudy/3bc255b8f601b0e15f6f76a533fe1253 to your computer and use it in GitHub Desktop.
Example of AES-256-CBC encryption and decryption functions, which use a shared key and a SHA-256 HMAC, that works across .NET (tested on 6) and Node (tested on 16.16).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Security.Cryptography; | |
using System.Text; | |
internal class Program | |
{ | |
private static async Task Main(string[] args) | |
{ | |
var passKey = "0102030405060708091011121314151617181920212223242526272829303132"; // some 64 character hex string (32 bytes) | |
var testString = "Hello, World! How are you today? I am doing fine and thought I would drop you a line and say hello, because you're pretty cool."; | |
var encryptedString = await EncryptAsync(testString, passKey).ConfigureAwait(false); | |
Console.WriteLine(encryptedString); | |
var decryptedString = await DecryptAsync(encryptedString, passKey).ConfigureAwait(false); | |
Console.WriteLine(decryptedString); | |
} | |
private static async Task<string> EncryptAsync(string plainText, string hexKey) { | |
var key = Convert.FromHexString(hexKey); | |
using Aes aes = Aes.Create(); | |
aes.Key = key; | |
aes.GenerateIV(); | |
aes.Mode = CipherMode.CBC; | |
aes.Padding = PaddingMode.Zeros; | |
ICryptoTransform cipher = aes.CreateEncryptor(); | |
using var ms = new MemoryStream(); | |
using var cs = new CryptoStream(ms, cipher, CryptoStreamMode.Write); | |
await cs.WriteAsync(Encoding.UTF8.GetBytes(plainText)).ConfigureAwait(false); | |
await cs.FlushFinalBlockAsync().ConfigureAwait(false); | |
var encryptedStringHex = Convert.ToHexString(ms.ToArray()); | |
var ivStr = Convert.ToHexString(aes.IV); | |
using var hmacsha256 = new HMACSHA256(key); | |
var hmac = Convert.ToHexString(hmacsha256.ComputeHash(Encoding.ASCII.GetBytes(ivStr+encryptedStringHex))); | |
return ivStr+encryptedStringHex+hmac; | |
} | |
private static async Task<string> DecryptAsync(string encrypted, string hexKey) { | |
var key = Convert.FromHexString(hexKey); | |
var ivStr = encrypted.Substring(0, 32); | |
var encryptedStringHex = encrypted.Substring(32, encrypted.Length - 64 - 32); | |
var encryptedString = Convert.FromHexString(encryptedStringHex); | |
var hashHex = encrypted.Substring(encrypted.Length - 64, 64); | |
using var hmacsha256 = new HMACSHA256(key); | |
var hmac = Convert.ToHexString(hmacsha256.ComputeHash(Encoding.ASCII.GetBytes(ivStr+encryptedStringHex))); | |
if (!hmac.Equals(hashHex, StringComparison.OrdinalIgnoreCase)) { | |
Console.WriteLine("Error: HMAC did not match."); | |
} | |
using Aes aes = Aes.Create(); | |
aes.Key = key; | |
aes.IV = Convert.FromHexString(ivStr); | |
aes.Mode = CipherMode.CBC; | |
aes.Padding = PaddingMode.None; | |
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV); | |
using var ms = new MemoryStream(encryptedString); | |
using var cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read); | |
using var sr = new StreamReader(cs); | |
var plainText = await sr.ReadToEndAsync().ConfigureAwait(false); | |
return plainText; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let crypto = require("crypto"); | |
function encrypt(plainText, hexKey) { | |
let key = Buffer.from(hexKey,'hex'); | |
let iv = crypto.randomBytes(16); | |
// create cipher | |
let cipher = crypto.createCipheriv('aes-256-cbc', key, iv).setAutoPadding(false); | |
let padLength = Buffer.byteLength(plainText, 'utf8') % 16; | |
padLength = padLength === 0 ? 0 : 16 - padLength; | |
plainText = plainText.padEnd(plainText.length + padLength); | |
let cipherText = cipher.update(plainText, 'utf8', 'hex'); | |
cipherText += cipher.final('hex'); | |
cipherText = cipherText.toUpperCase(); | |
const ivStr = iv.toString('hex').toUpperCase(); | |
let concat = ivStr + cipherText; | |
let hmac = crypto.createHmac('sha256', key).update(ivStr + cipherText).digest('hex').toUpperCase(); | |
console.log(ivStr); | |
console.log(cipherText); | |
console.log(hmac); | |
return (ivStr + cipherText + hmac); | |
} | |
function decrypt(encrypted, hexKey) { | |
let key = Buffer.from(hexKey, 'hex'); | |
let ivStr = encrypted.substr(0, 32); | |
let encryptedStringHex = encrypted.substr(32, encrypted.length - 64 - 32); | |
let encryptedString = Buffer.from(encryptedStringHex, 'hex'); | |
let hashHex = encrypted.substr(encrypted.length - 64, 64); | |
let hmac = crypto.createHmac('sha256', key).update(ivStr + encryptedStringHex).digest('hex').toUpperCase(); | |
if (hmac !== hashHex) { | |
console.log("Error: HMAC did not match."); | |
} | |
let iv = Buffer.from(ivStr, 'hex'); | |
let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); | |
decipher.setAutoPadding(false); | |
let plainText = ''; | |
decipher.on('readable', () => { | |
let data = decipher.read(); | |
if (data) { | |
plainText += data.toString('utf8'); | |
} | |
}); | |
decipher.write(encryptedString, 'hex'); | |
decipher.end(); | |
return plainText.trim(); | |
} | |
let passKey = "0102030405060708091011121314151617181920212223242526272829303132"; // some 64 character hex string (32 bytes) | |
let testString = "How are you today? I am doing fine and thought I would drop you a line and say hello, because you're pretty cool."; | |
let encryptedString = encrypt(testString, passKey) | |
console.log(encryptedString); | |
let decryptedString = decrypt(encryptedString, passKey); | |
console.log(decryptedString); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment