1

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.

Community
  • 1
  • 1
War
  • 8,539
  • 4
  • 46
  • 98
  • 1
    Not all byte pairs map to a valid Unicode character. When `GetString()` encounters a byte pair that is not a valid `char`, it uses a [fallback strategy](https://msdn.microsoft.com/en-us/library/ms404377(v=vs.110).aspx#Anchor_3) to replace the pair with a valid character. Which means you lose data. Instead, use something like [`Convert.ToBase64String()`](https://msdn.microsoft.com/en-us/library/dhx0d524(v=vs.110).aspx). Though I'm not sure about base64 encoding your salt, might you lose entropy? I don't know enough about security to say for sure. – dbc Sep 02 '16 at 10:50
  • hmmm ... didn't think unicode would be the issue here ... good call though, see if I can rework it. – War Sep 02 '16 at 10:56
  • 1
    Check out [Complete Password Hashing Solution using Pbkdf2](http://stackoverflow.com/documentation/c%23/2774/hash-functions/15470/complete-password-hashing-solution-using-pbkdf2). At the bottom of the full example is a utility class `ByteConverter` you can use to convert `byte[]` to a hex string and also the inverse. This ensures a true string representation of the byte array without having to worry about losing anything in the string conversion process. If you want to do strict byte array comparison (no to string conversion) you can also use method `CompareHashes` (same example). – Igor Sep 02 '16 at 11:01

1 Answers1

1

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));
        }
    }
}
War
  • 8,539
  • 4
  • 46
  • 98
  • You get fance with a source type of `T` and then in the code assume UTF-8 encoded data. – zaph Sep 02 '16 at 11:42
  • Can you also describe what the issue is? – Artjom B. Sep 02 '16 at 17:20
  • I need a multipurpose encryption system that allows data to be sent between applications in a secure and verifyable manner. For example: The example above allows me to define a simple DTO object that represents a signature on a request then make a remote call to a Json API adding the output string from this as a custom header that the remote endpoint can confirm by decrypting the object. Using AES allows me to encrypt but adding HMAC also adds confirmation that if the data when decrypted succeeds it also hasn't been tampered with. – War Sep 04 '16 at 15:35