A week ago I ran into Alan Smith at the Stockholm Cental Station on the way to the Scandinavian Developer Conference. We were both doing talks about Windows Azure, and we started talking about different Windows Azure features and thoughts. At some point, Alan mentioned that he had heard a few people say that they would like to have their BrokeredMessages encrypted. For some reason this stuck with me, and I decided to give it a try…
My first thought was to enherit the BrokeredMessage class, and introduce encryption like that. Basically pass in an encryption startegy in the constructor, and handle all encryption and decryption inside this subclass. However, about 2 seconds in to my attempt, I realized that the BrokeredMessage class was sealed. An annoying, but somewhat understandable decision made by Microsoft. Ok, so I couldn’t inherit the class, what can you do then? Well, there is no way to stop me from creating a couple of extension methods…
Ok, so extension methods would have to be my plan of attack, and as such, the goal would be to create 2 methods that look something like this
public static void Encrypt(this BrokeredMessage msg, IEncryptor encryptor)
public static void Decrypt(this BrokeredMessage msg, IDecryptor decryptor)
where IEncryptor and IDecryptor would be a couple of really simple interfaces like this
public interface IEncryptor
{
byte[] Encrypt(byte[] data);
}
public interface IDecryptor
{
byte[] Decrypt(byte[] data);
}
So the plan would be that the user created a new BrokeredMessage and set the body and the properties needed, and then he would call Encrypt() on it, and all the information would get encrypted. And when it was received, he would call Decrypt, and the body and the properties would be decrypted and ready for use. And with the interfaces abstracting the actual encryption and decryption, any type of algorithm would be possible to use.
I decided to start by encrypting the body, that is the object passed into the constructor, which is then serialized and passed as the body of the message. So I started looking at ways to access the serialized data…hmm…that seemed harder than expected. The only way to access the body is through the generic GetBody<T>() method. Unfortunately, that requires me to know the type of the body to get hold of it, and on top of that, I would get an object, not the serialized form…hmm…
I also figured out that once you have set the message’s body using the constructor, there is no real way of changing it…
I found 2 ways to get around this. Either I would have to modify the extension methods, or I would have to do some funky stuff. I will start by showing how to do it by changing my extension methods slightly, and then later on, I will show how to do it using reflection.
So to go forward, I had to change my extension methods a little… They ended up looking like this instead
public static BrokeredMessage Encrypt<T>(this BrokeredMessage msg, IEncryptor encryptor)
public static BrokeredMessage Decrypt<T>(this BrokeredMessage msg, IDecryptor decryptor)
As you can see, I am now forced to define the type of the body, as well as return a new BroekeredMessage instead of updating the original…but it will still work…
I will ignore the implementation of the IEncryptor and IDecryptor interfaces for now. The whole idea is that the implementation of this should not matter. They should take a byte array, and encrypt or decrypt it for me….that’s it…
The first order of business is to implement the Encrypt<T>() method. I need to have an encrypted message to be able to try my decryption, so I found that to be a good place to start…
First thing to do is to get hold of the body object, and serialize it. I decided to serialize it using JSON as it has nice and compact syntax…
var body = msg.GetBody<T>();
var json = JsonConvert.SerializeObject(body);
Now that I have the serialized object in string form, I can easily turn that into a byte array using the UTF8 encoding, and pass that to the encryptor. Once that is done, I can convert the byte array into a Base64 encoded string using the Convert class.
var bytes = encryptor.Encrypt(Encoding.UTF8.GetBytes(json));
var encryptedObject = Convert.ToBase64String(bytes, 0, bytes.Length);
Once the encryption of the serialized object is done, and converted to a Base64 encoded string, I can just creat a new BrokeredMessage, using that string as the actual body.
var encryptedMessage = new BrokeredMessage(encryptedObject);
All that is left now is to encrypt the properties, set them on the new BrokeredMessage, and return it…
Unfortunately, the BrokeredMessage’s properties are of type object. Not that they can take actual objects, but because that they can take any simple type. So to handle this, I decided to wrap the property value in a class that would contain the property value as a string, as well as a definition of what type it was. The wrapper looks like this
public class BrokeredMessagePropertyWrapper
{
public string Type { get; set; }
public string Value { get; set; }
public object GetValue()
{
switch (Type)
{
case "Int32":
return int.Parse(Value);
case "Double":
return double.Parse(Value);
case "Decimal":
return decimal.Parse(Value);
default:
return Value;
}
}
}
As you can see, I also include a simple method for deserializing the value.
Now that I have a way of wrapping the property values, all I need to do is to go through the properties and create new wrappers. Then I need to serialize the wrapper into JSON, encrypt it, turn into a Base64 encoded string, and set the property on the encrypted message.
foreach (var property in sourceMessage.Properties.ToArray())
{
var wrapper = new BrokeredMessagePropertyWrapper
{
Type = property.Value.GetType().Name,
Value = property.Value.ToString()
};
var bytes = encryptor.Encrypt(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(wrapper)));
targetMessage.Properties[property.Key] = Convert.ToBase64String(bytes, 0, bytes.Length);
}
In the above code, the sourceMessage points towards the original message, and the targetMessage points to the newly created, and encrypted message.
That is actually it… An encrypted message ready to be sent to the Service Bus…
The decryption is pretty much the same thing in reverse for obvious reasons…
Get the body as a string, which is really is. Get a byte array from the Base64 encoded string. Decrypt it. Convert it to a string. Deserialize the JSON and create a new BrokeredMessage with the deserialized object as the body. Like this
var encryptedObject = msg.GetBody<string>();
var json = Encoding.UTF8.GetString(decryptor.Decrypt(Convert.FromBase64String(encryptedObject)));
var body = JsonConvert.DeserializeObject<T>(json);
var decryptedMessage = new BrokeredMessage(body);
And the properties are the same thing. Pull the Base64 encoded string from the message’s properties, get the bytes, decrypt them, turn it into a string, deserialize to the wrapper type, and then set the decrypted message’s property using the GetValue() method of the wrapper.
foreach (var property in sourceMessage.Properties.ToArray())
{
var decryptedBytes = decryptor.Decrypt(Convert.FromBase64String(property.Value.ToString()));
var serializedWrapper = Encoding.UTF8.GetString(decryptedBytes);
var wrapper = JsonConvert.DeserializeObject<BrokeredMessagePropertyWrapper>(serializedWrapper);
targetMessage.Properties[property.Key] = wrapper.GetValue();
}
And voila! A very simple way to encrypt and decrypt BrokeredMessages. Using the extension methods when sending a message looks something like this
var client = GetQueueClient();
var obj = new MessageObject { Id = 1, Name = "Chris" };
var msg = new BrokeredMessage(obj);
msg.Properties.Add("MyProp", "MyValue");
msg.Properties.Add("MyProp2", 1);
msg = msg.Encrypt<MessageObject>(GetEncryption());
client.Send(msg);
And the receiving end like this
var client = GetQueueClient();
var message = client.Receive(TimeSpan.FromSeconds(5));
message = message.Decrypt<MessageObject>(GetEncryption());
var body = message.GetBody<MessageObject>();
That’s it! About a simple as can be. But I do say “about” as simple as can be. I still do not like the generic syntax being used. And I don’t like a new message being created. I want to change the existing message… And that can be done using reflection…
I know that there are a lot of people out there that doesn’t like reflection, and I am not convinced it is a great way to solve things. But I still want to show the difference, and how cool it can be…
Using reflection, I can go back and use the original extension method signatures
public static void Encrypt(this BrokeredMessage msg, IEncryptor encryptor)
public static void Decrypt(this BrokeredMessage msg, IDecryptor decryptor)
They will mofidy the object instead of actually creating a new one.
They way it works is by pulling out the private property called BodyStream on the BrokeredMessage instance. This contains a Stream that contains the serialized version of the body object.
The reflection part looks like this
var member = typeof(BrokeredMessage).GetProperty("BodyStream", BindingFlags.NonPublic | BindingFlags.Instance);
var data = (Stream)member.GetValue(msg);
where msg is the BrokeredMessage…
Encypting the data in the stream requires me to read the content of the stream as a byte array, send it to the IEncryptor instance, and then create a new Stream with the encrypted data. This newly created stream is then used to set the BodyStream property.
var bytes = new byte[data.Length];
data.Read(bytes, 0, (int)data.Length);
var handledBytes = encryptor.Encrypt(bytes);
var encryptedStream = new MemoryStream(handledBytes);
member.SetValue(msg, encryptedStream);
Once that is done, the encryption of the properties is exactly the same. It is only the body that is the issue…
Decrypting the body is once again pretty much the same… Get the Stream from the BodyStream. Decrypt the bytes. Turn them into a Stream, and set the BodyStream.
var member = typeof(BrokeredMessage).GetProperty("BodyStream", BindingFlags.NonPublic | BindingFlags.Instance);
var data = (Stream)member.GetValue(msg);
var bytes = new byte[data .Length];
data .Read(bytes, 0, (int)data .Length);
var handledBytes = decryptor.Decrypt(bytes);
var decryptedStream = new MemoryStream(handledBytes);
member.SetValue(msg, decryptedStream);
Ok, so the reflection part is not that great. It requires the BrokeredMessage’s implementation to not change, or at least not change by removing the BodyStream property. And some people say it might be slow, but hey, I am not running this a million times in a row… However, it does remove the Json.Net dependency, and relieves us of having to serialize and deserailize the object… So there is both good and bad parts involved in going down this route instead of the first one.
And jst for the sake of completeness, I want to show an implementation of the IEncryptor and IDecryptor. In this case using TripleDES (the download contains both TripleDes and Aes, as well as a base class that will make it VERY easy to implement new ones as long as you use a symmetric algorithm).
Actually, the code below is a previous implementation before the base class was introduced, but it will show the idea…
I have implemented both of the interfaces using the same class, called TripleDesEncryption. It takes 2 parameters in the constructor, the key and the initialization vector (2 strings).
It starts out by making sure that the key and IV is the correct length. The TripleDESCryptoServiceProvider requires the key to be 16 bytes (128 bits), and the IV to be 8 bytes (64 bits). It actually might support more key lengths, but the important thing is that they are fixed. So the constructor makes sure that they are the correct length before storing them.
public class TripleDesEncryption : EncryptionBase
{
private readonly string _key;
private readonly string _iv;
public TripleDesEncryption(string key, string iv) : base(16, key, 8, iv)
{
if (key.Length > 16)
key = key.Substring(0, 16);
else if (key.Length < 16)
key = key.PadRight(16, '*');
_key = key;
if (iv.Length > 8)
iv = iv.Substring(0, 8);
else if (iv.Length < 8)
iv = iv.PadRight(8, '*');
_iv = iv;
}
...
}
As you can see, it either cuts the string if it is too long, or pads it if it is too short… Not the fanciest way of doing things, but it will work…
The Encrypt() method is implemented like this
public byte[] Encrypt(byte[] data)
{
byte[] encryptedBytes;
using (var algorithm = GetProvider())
{
algorithm.Key = Encoding.UTF8.GetBytes(_key);
algorithm.IV = Encoding.UTF8.GetBytes(_iv);
using (var encryptor = algorithm.CreateEncryptor())
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
and the Decrypt() method like this
public byte[] Decrypt(byte[] data)
{
byte[] decryptedBytes;
using (var algorithm = GetProvider())
{
algorithm.Key = Encoding.UTF8.GetBytes(_key);
algorithm.IV = Encoding.UTF8.GetBytes(_iv);
using (var decryptor = algorithm.CreateDecryptor())
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
decryptedBytes = ms.ToArray();
algorithm.Clear();
}
}
return decryptedBytes;
}
and for the curious person, the GetProvider method is actually just a one-liner
protected override SymmetricAlgorithm GetProvider()
{
return new TripleDESCryptoServiceProvider();
}
That’s all there is to it! That will encrypt and decrypt the BrokeredMessages using TripleDES…making sending messages through the Service Bus even safer.
There is obviously downlaodable code for this. It is a little hefty at 2,7 Mb…sorry about that… NuGetting (new word?) Json.Net adds a bit to the download…
It will require you to have the Azure SDK installed to work, as well as a Service Bus service set up in Windows Azure. Other than that, you have to configure it by setting the appSetting values in the App.config file. They should be pretty self-explanatory though…
Code available here: DarksideCookie.Azure.Sb.Encryption.zip (2.72 mb)
I hope that this helps out! If there are any problems or comments, just add a comment, or even better, drop me an e-mail at chris(a)59north.com. I have a tendency to miss comments on the blog, even if I do try to monitor them.