AES + HMAC Encryption in .Net - c#

I'm trying to serialise and encrypt simple dto's as a means to securely hand them around as strings.
It seems that most people point me at Encrypt and decrypt a string when asking about this.
#jbtule took the time to provide a really detailed answer with 2 possible solutions.
I have taken a copy of the second example from his gist https://gist.github.com/jbtule/4336842#file-aesthenhmac-cs (the file named "AESThenHMAC.cs" and put that in to my project.
I then thought it might be good practice to wrap up and quickly test this solution but I can't seem to get it working.
Could someone explain what I am doing wrong here?
Here's my wrapper round #jbtule's code:
using Newtonsoft.Json;
using System.Text;
namespace Core.Data
{
public class AesCrypto<T> : ICrypto<T>
{
public string Encrypt(T source, string salt)
{
var enc = Encoding.Unicode;
var rawData = JsonConvert.SerializeObject(source);
return enc.GetString(AESThenHMAC.SimpleEncryptWithPassword(enc.GetBytes(rawData), salt));
}
public T Decrypt(string source, string salt)
{
var enc = Encoding.Unicode;
var decryptedBytes = AESThenHMAC.SimpleDecryptWithPassword(enc.GetBytes(source), salt);
return JsonConvert.DeserializeObject<T>(enc.GetString(decryptedBytes));
}
}
}
And then a simple unit test to confirm this all works:
public void TestAesCrypto()
{
var testInput = new EncryptableObject { Id = 123, Name = "Victim", When = DateTimeOffset.UtcNow };
var crypto = new AesCrypto<EncryptableObject>();
var saltBytes = new byte[32];
new Random().NextBytes(saltBytes);
var testSalt = Encoding.Unicode.GetString(saltBytes);
var magicString = crypto.Encrypt(testInput, testSalt);
var testOutput = crypto.Decrypt(magicString, testSalt);
Assert.AreEqual(testInput.Id, testOutput.Id);
Assert.AreEqual(testInput.Name, testOutput.Name);
Assert.AreEqual(testInput.When, testOutput.When);
}
For some reason the decryption method returns null because the check performed on line 261 of jbtule's gist compares the value 255 to 0.
This is a follow on from my attempts to talk to the .NET types directly (see AesEncryption doesn't appear to decrypt right?), I just need a solution that consistently works at this point.

There we go, thanks to #dbc ... how I didn't spot that I don't know!
using Newtonsoft.Json;
using System;
using System.Text;
namespace Core.Data
{
public class AesCrypto<T> : ICrypto<T>
{
public string Encrypt(T source, string salt)
{
var e = Encoding.UTF8;
var rawData = e.GetBytes(JsonConvert.SerializeObject(source));
var cipherData = AESThenHMAC.SimpleEncryptWithPassword(rawData, salt);
return Convert.ToBase64String(cipherData);
}
public T Decrypt(string source, string salt)
{
var e = Encoding.UTF8;
var decryptedBytes = AESThenHMAC.SimpleDecryptWithPassword(Convert.FromBase64String(source), salt);
return JsonConvert.DeserializeObject<T>(e.GetString(decryptedBytes));
}
}
}

Related

unable to verify webhook with timestamp from bambooHR

I'm trying to verify a webhook from bamboohr. the documentation is here https://documentation.bamboohr.com/docs/webhooks
and after unpacking the headers I see this:
X-BambooHR-Signature: 362cb0eff0193af8d3f939349f84014e5c70bba4cfc105682b45ecd305db01ff
X-BambooHR-Timestamp: 1652747163
Here is my code, from an azure function triggered by the webhook. The testOutput is not whats in the ‘X-BambooHR-Signature’ header:
string data;
using (var reader = new StreamReader(req.Body))
{
data = await reader.ReadToEndAsync();
}
string privateKey = "<gotten from bamboohr webhookconfig>";
if (req.Headers.Keys.Contains("X-BambooHR-Signature") && req.Headers.Keys.Contains("X-BambooHR-Timestamp"))
{
string timestamp = req.Headers["X-BambooHR-Timestamp"];
string signature = req.Headers["X-BambooHR-Signature"];
byte[] privateKeyBytes = Encoding.UTF8.GetBytes(privateKey);
byte[] combinedBytes = Encoding.UTF8.GetBytes(data + timestamp);
HMACSHA256 hmac = new HMACSHA256(privateKeyBytes);
byte[] testOutputBytes = hmac.ComputeHash(combinedBytes);
string testOutput = Convert.ToBase64String(testOutputBytes);
log.LogInformation("testOutput is: " + testOutput); //----NOT EQUAL TO signature.
}
Any idea what I might be doing wrong? the testUutput is something like 'llBdZd2IfVdrJBlkGFFNG2dszDxpgJlJ4vQqTATJsYU=' which as you can see isnt even close.
Instead of using ToBase64String, try converting it to hexadecimal format. (There are different ways to convert it.) I had similar issue verifying bambooHR signature key in python, and fixed it by converting expected signature to hexadecimal format (not bytes or bytes string).
c#
using System;
using System.Text;
using System.Security.Cryptography;
namespace Test
{
public class VerifySignature
{
public static void Main(string[] args)
{
string data = "request data";
string privateKey = "your private key";
string timestamp = "1652747163";
string signature = "362cb0eff0193af8d3f939349f84014e5c70bba4cfc105682b45ecd305db01ff";
byte[] privateKeyBytes = Encoding.UTF8.GetBytes(privateKey);
byte[] combinedBytes = Encoding.UTF8.GetBytes(data + timestamp);
HMACSHA256 hmac = new HMACSHA256(privateKeyBytes);
byte[] testOutputBytes = hmac.ComputeHash(combinedBytes);
// edited from here
// string testOutput = Convert.ToBase64String(testOutputBytes);
// log.LogInformation("testOutput is: " + testOutput);
string hexOutput = BitConverter.ToString(testOutputBytes).Replace("-",""); // convert to hexadecimal format
Console.WriteLine(hexOutput);
}
}
}
python
import hmac
from hashlib import sha256
def verify_signature(request):
received_signature = 'get sig from request headers'
timestamp = 'get timestamp from request headers'
private_key = 'your private key'
body = request.body
calculated_signature = hmac.new(
private_key, body + timestamp, sha256
).hexdigest()
return received_signature == calculated_signature
I hope this helps although it's a bit late!

Why does same static method behaves different in different controllers?

I have a public static method in Invoice.cs class:
public static string CalculateHash(Stream image)
{
using (var sha = SHA256.Create())
{
image.Seek(0, SeekOrigin.Begin);
var hash = sha.ComputeHash(image);
var hashStr = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
return hashStr;
}
}
I have two controllers: HookController.cs and DataController.cs. I try to check if same hash was created for same image, But, hashes which are created by these controllers are not same for same image.
I solved this way.I copied to a new memory stream.
Getting the same Hash value using the below code. Tested it on .Net Framework 4.6.1 console app.
class Program
{
static void Main(string[] args)
{
var fileStream = new FileStream(#"D:\Mukesh\Mukesh.jpg", FileMode.Open);
var result = CalculateHash(fileStream);
Console.ReadKey();
}
public static string CalculateHash(Stream image)
{
using (var sha = SHA256.Create())
{
image.Seek(0, SeekOrigin.Begin);
var hash = sha.ComputeHash(image);
var hashStr = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
return hashStr;
}
}

Decrypt passphrase protected PEM containing private key

I have the following method that creates an Encrypted Private Key using Bouncy Castle for C#:
public string GetPrivateKey(AsymmetricCipherKeyPair keyPair, string password)
{
var generator = new Pkcs8Generator(keyPair.Private, Pkcs8Generator.PbeSha1_3DES);
generator.IterationCount = 4;
generator.Password = password.ToCharArray();
var pem = generator.Generate();
TextWriter textWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(textWriter);
pemWriter.WriteObject(pem);
pemWriter.Writer.Flush();
string privateKey = textWriter.ToString();
return privateKey;
}
Which looks like this:
-----BEGIN ENCRYPTED PRIVATE KEY-----
...
-----END ENCRYPTED PRIVATE KEY-----
What I don't know is how to consume the password used to encrypt the private key in my Decrypt method. Right now, without knowing how to "decrypt" my private key using he password, I get the following Exception:
Org.BouncyCastle.OpenSsl.PemException : problem creating ENCRYPTED
private key: System.NullReferenceException: Object reference not set
to an instance of an object. at
Org.BouncyCastle.OpenSsl.PemReader.ReadPrivateKey(PemObject pemObject)
Here is the code for the Decrypt method:
public string Decrypt(string base64Input, string privateKey, string password)
{
var bytesToDecrypt = Convert.FromBase64String(base64Input);
//get a stream from the string
AsymmetricCipherKeyPair keyPair;
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(privateKey))
{
var obj = new PemReader(txtreader).ReadObject();
keyPair = (AsymmetricCipherKeyPair) obj;
decryptEngine.Init(false, keyPair.Private);
}
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
return decrypted;
}
It seems to me that you need to decrypt the private key to use it. Currently your password parameter isn't used. Unfortunately it doesn't seem to be all that easy to find out how do this.
Bouncy Castle, as many other Java API's, use a password handler to retrieve the password. The reason to do this is to allow the program to ask the user for the password only when it is required. This allows the program to leave the password in memory for the shortest amount of time.
So to allow for decryption, use the following constructor:
PemReader(TextReader reader, IPasswordFinder pFinder);
with an implementation of IPasswordFinder (Bouncy Castle for C# is mainly a Java port, otherwise a delegate would probably have been used).
For your convenience, the code. This code also reconstructs the entire key pair, not just the private key.
Import statements:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using System.IO;
the decoder:
private static AsymmetricCipherKeyPair DecodePrivateKey(string encryptedPrivateKey, string password)
{
TextReader textReader = new StringReader(encryptedPrivateKey);
PemReader pemReader = new PemReader(textReader, new PasswordFinder(password));
object privateKeyObject = pemReader.ReadObject();
RsaPrivateCrtKeyParameters rsaPrivatekey = (RsaPrivateCrtKeyParameters)privateKeyObject;
RsaKeyParameters rsaPublicKey = new RsaKeyParameters(false, rsaPrivatekey.Modulus, rsaPrivatekey.PublicExponent);
AsymmetricCipherKeyPair kp = new AsymmetricCipherKeyPair(rsaPublicKey, rsaPrivatekey);
return kp;
}
required helper class:
private class PasswordFinder : IPasswordFinder
{
private string password;
public PasswordFinder(string password)
{
this.password = password;
}
public char[] GetPassword()
{
return password.ToCharArray();
}
}
Note that normally you should only use char[] instead of string for passwords as char[] can be cleared after use, while string cannot.
Now you have the private key decryption should be easy.

Generate HMAC-SHA256 hash with BouncyCastle

I need to generate a HMAC-SHA256 hash in a PCL (developing for Xamarin Forms) which doesn't support the .NET built-in HMAC/cryptography classes, so I'm working with BouncyCastle to implement my cryptography classes.
I need to generate a HMAC-SHA256 hash, but I haven't been able to find any example on Google, nor does BouncyCastle seem to have any documentation for this. Can anyone help me out?
Thanks to the solution here I came up with this code:
public class HmacSha256
{
public byte[] Hash(string text, string key)
{
var hmac = new HMac(new Sha256Digest());
hmac.Init(new KeyParameter(Encoding.UTF8.GetBytes(key)));
byte[] result = new byte[hmac.GetMacSize()];
byte[] bytes = Encoding.UTF8.GetBytes(text);
hmac.BlockUpdate(bytes, 0, bytes.Length);
hmac.DoFinal(result, 0);
return result;
}
}
Corresponding unit test (uses FluentAssertions):
[TestClass]
public class HmacSha256Tests
{
private readonly HmacSha256 _hmac = new HmacSha256();
[TestMethod]
public void Hash_GeneratesValidHash_ForInput()
{
// Arrange
string input = "hello";
string key = "test";
string expected = "F151EA24BDA91A18E89B8BB5793EF324B2A02133CCE15A28A719ACBD2E58A986";
// Act
byte[] output = _hmac.Hash(input, key);
string outputHex = BitConverter.ToString(output).Replace("-", "").ToUpper();
// Assert
expected.Should().Be(outputHex);
}
}
Using this PCL offshoot of BouncyCastle https://www.nuget.org/packages/BouncyCastle-PCL/1.0.0.6 it's really easy, in fact identical to the windows api.
public string ComputeHMAC(string message)
{
var keyBytes = Encoding.UTF8.GetBytes(Constants.API_KEY);
var messageBytes = Encoding.UTF8.GetBytes(message);
var hmac = new HMACSHA256(keyBytes);
byte[] result = hmac.ComputeHash(messageBytes);
return Convert.ToBase64String(result);
}
And a unit test using the actual .Net version:
[Test, AutoMoqData]
public void Hash_Algorithm_Correct (
[NoAutoProperties] HashMacService sut,
string message)
{
string expected;
var key = Encoding.UTF8.GetBytes(Constants.API_KEY);
using (var hmac = new HMACSHA256(key))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
expected = Convert.ToBase64String(hash);
}
var result = sut.ComputeHMAC(message);
Assert.That(result, Is.EqualTo(expected));
}
I was using PCLCrypto but it kept crashing on Xamarin iOS, this was much cleaner and could be unit tested, wheras PCLCrypto required the platform apis so had to be deployed to a device.
private static void CreateToken(string message, string key)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[]keyByte = encoding.GetBytes(key);
HMACSHA256 hmacsha = new HMACSHA256(keyByte);
byte[]messageBytes = encoding.GetBytes(message);
byte[]hashmessage = hmacsha.ComputeHash(messageBytes);
Console.WriteLine(ByteToString(hashmessage));
}
public static string ByteToString(byte[]buff) {
string sbinary = "";
for (int i = 0; i < buff.Length; i++) {
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
Above code saved my time while working for HMAC-SHA256, I hope this may help someone and here is the reference in detail http://billatnapier.com/security01.aspx

Port DDI login password encryption to Metro

I've been working over the last few days on trying to port the simple encryption (found here) used to log into the Wizards of the Coast Character Builder API (so my app can download a user's characters directly from the servers) to something I can use in a Windows 8 Metro app, since AesManaged didn't make it into the Metro cryptography libraries. Seeing as I'm a novice programmer at best, this has proven a bit beyond my skills.
Here's the code I need to port over:
public static byte[] SimpleEncrypt(string value, string key)
{
byte[] buffer2;
ICryptoTransform transform = GetSimpleAlgorithm(key).CreateEncryptor();
using (MemoryStream stream = new MemoryStream())
{
using (CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write))
{
byte[] bytes = Encoding.UTF8.GetBytes(value);
stream2.Write(bytes, 0, bytes.Length);
stream2.Flush();
stream2.FlushFinalBlock();
stream.Position = 0L;
buffer2 = stream.ToArray();
}
}
return buffer2;
}
private static SymmetricAlgorithm GetSimpleAlgorithm(string key)
{
AesManaged aes = new AesManaged();
byte[] source = new SHA256Managed().ComputeHash(Encoding.UTF8.GetBytes(key));
return new AesManaged { Key = source, IV = source.Take<byte>((aes.BlockSize / 8)).ToArray<byte>() };
}
This is used to encrypt the password prior to passing the login:
contentClient.Login(username, SimpleEncrypt(password, username));
And in case it's needed, the web service is located at: http://ioun.wizards.com/ContentVault.svc
In the comments on that link way up there in the first link, someone suggested some code for Windows 8 back in February, but said code had a few problems I had to fix before it would even compile, and even then, when I try to log in with it, I get an exception back from the service saying "padding is invalid and cannot be removed".
Here's what I'm currently working with:
private static byte[] SimpleEncrypt(string value, string key)
{
var simpleAlgorithm = GetSimpleAlgorithm(key);
var encryptedBuffer = CryptographicEngine.Encrypt(simpleAlgorithm.Item1, CryptographicBuffer.ConvertStringToBinary(value, BinaryStringEncoding.Utf8), simpleAlgorithm.Item2);
var result = new byte[encryptedBuffer.Length];
CryptographicBuffer.CopyToByteArray(encryptedBuffer, out result);
return result;
}
private static Tuple<CryptographicKey, IBuffer> GetSimpleAlgorithm(string key)
{
var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var keyAsBinary = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
var source = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256).HashData(keyAsBinary);
var shortKey = CryptographicBuffer.CreateFromByteArray(UTF8Encoding.UTF8.GetBytes(key).Take((int)provider.BlockLength).ToArray());
return new Tuple<CryptographicKey,IBuffer>(provider.CreateSymmetricKey(source), shortKey);
}
Any help getting this working would be EXTREMELY appreciated.
Welp, spent more time looking it over, and figured out the problem.
The suggested updated version was using the "key" string to create the IV, whereas it should have been using the hash of the key, instead.
Here's the functional version, in case anyone wants it:
private static byte[] SimpleEncrypt(string value, string key)
{
var simpleAlgorithm = GetSimpleAlgorithm(key);
CryptographicKey encryptKey = simpleAlgorithm.Item1;
IBuffer IV = simpleAlgorithm.Item2;
var encryptedBuffer = CryptographicEngine.Encrypt(encryptKey, CryptographicBuffer.ConvertStringToBinary(value, BinaryStringEncoding.Utf8), IV);
var result = new byte[encryptedBuffer.Length];
CryptographicBuffer.CopyToByteArray(encryptedBuffer, out result);
return result;
}
private static Tuple<CryptographicKey, IBuffer> GetSimpleAlgorithm(string key)
{
var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var keyAsBinary = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
var source = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256).HashData(keyAsBinary);
byte[] sourceArray = new byte[source.Length];
CryptographicBuffer.CopyToByteArray(source, out sourceArray);
var shortKey = CryptographicBuffer.CreateFromByteArray(sourceArray.Take((int)provider.BlockLength).ToArray());
return new Tuple<CryptographicKey,IBuffer>(provider.CreateSymmetricKey(source), shortKey);
}

Categories

Resources