logo

Silverlight to WCF Service Object Encryption

By Peter Bromberg
Printer Friendly Version
View My Articles
533 Views
    

Details a relatively easy way to protect your intellectual property / data going over the wire from your WCF service to your Silverlight application.


Recently in discussing a Silverlight project that uses a WCF Service to handle it's data, the issue came up that private data which represents the intellectual property of the application owner/developer would be transmitted over the wire as plaintext, enabling someone to use an HTTP sniffer such as Fiddler to capture and "steal" this information while using the application. Furthermore, it became apparent that users might be able to attempt to access the WCF service independently from outside of the Silverlight application, an action that was not desirable.  We would only want somebody to be able to use the service via the Silverlight application.

You could enable SSL under such a scenario, and provide the requisite elements in your clientaccesspolicy.xml file:

 <allow-from http-request-headers="SOAPAction">
 <domain uri="http://*"/>
 <domain uri="https://*" />
 </allow-from>

However, there is another technique that may be a lot easier, especially if the process of setting up a public certificate and all the requisite SSL settings is not feasible. You can provide an encryption class that both the Silverlight app and the WCF service can use, and simply encrypt the string properties of all DTO's that go over the wire, in either direction.  Silverlight supports AESManaged, which is essentially the Rijndael symmetric algorithm with a fixed block size and iteration count. This class functions the same way as the .NET Framework RijndaelManaged class but limits blocks to 128 bits and does not allow feedback modes.  An "Encryption Helper" class that provides this functionality will work both in Silverlight and on the server in the WCF Service.

In addition, we can create the salt or other part of the Rfc2898DeriveBytes key required for encryption and decryption from the hash codes of properties of the  Silverlight assembly itself - something that would prove that the service is truly being accessed only via our Silverlight app. By storing these values in  appSettings elements of the service's web.config file, all we would need to do is get these salt and password values in the Application_Startup event of our Silverlight app like this:

  Salt = Assembly.GetExecutingAssembly().FullName.GetHashCode().ToString();

  I originally started with the hash code of the Assembly, but an astute reader (see comments below) commented that it was changing.  The FullName  value will be the same in either debug or release builds, and at runtime, making it easy to grab and store in our web.config on the server. You, the developer, can easily set a breakpoint in your Silverlight codebehind just below the above line, get the salt value, and paste it into your WCF service web.config file in the appropriate appSettings "salt" key element. Another property of the assembly that appears not to change at runtime is:

ManifestModule.Name.GetHashCode();

  The final part of the solution involves providing "generic object encryption". Using Reflection, it is possible to create Encrypt and Decrypt methods that will accept any object, change all the string properties to their encrypted values, and return the object back to your code to send over the wire.

  The resultant encrypted  SOAP payload for a "Customer" DTO instance encrypted in this manner looks like this:


<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetCustomerResponse>
<GetCustomerResult xmlns:a="http://schemas.datacontract.org/2004/07/SLObjectEncryption" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Address>5yjSsZRiL2Au18YW/iuUWg==</a:Address>
<a:City>S/DxIstfGrzloOyhzYjHTw==</a:City>
<a:CustomerId>0691d937-8036-411b-9d83-d391df25ab31</a:CustomerId>
<a:Email>a8FK6lLtrXFFcMpFoa1i8mkiNvMB2HXQWi494rb1mto=</a:Email>
<a:FirstName>5r1HSbQEkuA99JT9AEUFmg==</a:FirstName>
<a:LastName>UMD0tfrtWy6fT6F8wdGSBA==</a:LastName>
<a:Phone>uT8bPX+Ap+vKPsk0ZS9gbA==</a:Phone>
<a:State>MPnz12L4A01l93TkqHotEQ==</a:State>
<a:ZipCode>BnmFYko5wpm1PYtZ2yhMtw==</a:ZipCode>
</GetCustomerResult></GetCustomerResponse>
</s:Body>
</s:Envelope>

As can be seen above, the names of the properties are visible, but all the values are encrypted. At the Silverlight side, this object needs only to be passed into the Decrypt method using the same key generated from strings that are created only at runtime, and we've got back our unencrypted class instance. Certainly a lot cheaper than paying $250 a year for a certificate, assuming you even had the infrastructure availability to install and maintain it.

Here is the code for the EncryptionHelper class:

using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

namespace SLObjectEncryption
{
    internal static class EncryptionHelper

    {
        public static Object EncryptObject(object obj)
        {

            byte[] key = EncryptionHelper.GetHashKey(App.Password, App.Salt);
            Type t = obj.GetType();
           
            PropertyInfo[] fi = t.GetProperties();

            object o = null;
            foreach (PropertyInfo f in fi)
            {

                if (f.PropertyType == typeof(string))
                {
                    PropertyInfo pi = t.GetProperty(f.Name, BindingFlags.Public | BindingFlags.Instance);

                    try
                    {
                        o = pi.GetValue(obj, null);
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(ex.ToString());
                    }

                    string s = (string)o;
                    f.SetValue(obj, EncryptionHelper.Encrypt(key, s), null);
                }

            }
            return obj;
        }

 public static Object DecryptObject(object obj)
 {
     byte[] key = EncryptionHelper.GetHashKey(App.Pasword, App.Salt);
     Type t = obj.GetType();
     PropertyInfo[] fi = t.GetProperties();
     object o = null;
     foreach (PropertyInfo f in fi)
     {
         if (f.PropertyType == typeof(string))
         {
             PropertyInfo pi = t.GetProperty(f.Name, BindingFlags.Public | BindingFlags.Instance);

             try
             {
                 o = pi.GetValue(obj, null);
             }
             catch (Exception ex)
             {
                 System.Diagnostics.Debug.WriteLine(ex.ToString());
             }

             string s = (string)o;
             f.SetValue(obj, EncryptionHelper.Decrypt(key, s), null);
         }
     }
     return obj;
 }
        
        internal static byte[] GetHashKey(string hashKey, string salt)
        {
            UTF8Encoding encoder = new UTF8Encoding();
            byte[] saltBytes = encoder.GetBytes(salt);
           
            Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(hashKey, saltBytes);

            // Return the key
            return rfc.GetBytes(16);
        }

        internal static string Encrypt(byte[] key, string dataToEncrypt)
        {
            AesManaged encryptor = new AesManaged();

            // Set key and IV
            encryptor.Key = key;
            encryptor.IV = key;

            // create memory stream
            using (MemoryStream encryptionStream = new MemoryStream())
            {
                // Create crypto stream
                using (CryptoStream encrypt = new CryptoStream(encryptionStream, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    // Encrypt
                    byte[] data = UTF8Encoding.UTF8.GetBytes(dataToEncrypt);
                    encrypt.Write(data, 0, data.Length);
                    encrypt.FlushFinalBlock();
                    encrypt.Close();

                    // Return encrypted data as base64 string
                    return Convert.ToBase64String(encryptionStream.ToArray());
                }
            }
        }

        internal static string Decrypt(byte[] key, string encryptedString)
        {
            AesManaged decryptor = new AesManaged();

            // convert base64 string to byte array
            byte[] encryptedData = Convert.FromBase64String(encryptedString);

            // Set key and IV
            decryptor.Key = key;
            decryptor.IV = key;

            // create  memory stream
            using (MemoryStream decryptionStream = new MemoryStream())
            {
                // Create crypto stream
                using (CryptoStream decrypt = new CryptoStream(decryptionStream, decryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    // Encrypt
                    decrypt.Write(encryptedData, 0, encryptedData.Length);
                    decrypt.Flush();
                    decrypt.Close();

                    // Return unencrypted data
                    byte[] decryptedData = decryptionStream.ToArray();
                    return UTF8Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);
                }
            }
        }
    }
}
And here is what the Silverlight UI looks like, after it's been "exercised":



In closing, let me just address a couple of concerns: First, nowhere here do I say that this is "better" than SSL. I provide it only as an alternative.  Second, the fact that the password or other component of the generated cryptographic key may be shown in plaintext in the code is not useful to a hacker, as both the password and the hash (both of which can be generated only at runtime) are required for a valid key. The download reflects the change to using the FullName property of the assembly to generate the salt value, and the ManifestModule.Name.GetHashCode() for the password, as discussed earlier.

You can download the full Visual Studio 2008 Solution here.

Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites. Pete Tweets at peterbromberg


Didn't Find The Answer You Were Looking For?

EggHeadCafe has experts online right now that may know the answer to your question.  We pay them a bonus for answering as many questions as they can.  So, why not help them and yourself by becoming a member (free) and ask them your question right now?
Ask Question In Live Forum

If you have an OpenID and do not want to become a member of the EggHeadCafe forum, you can also sign on to Chat Chaos and post your question to our real time Silverlight chat application.
Ask Question In Chat Chaos

Article Discussion: Silverlight to WCF Service Object Encryption
Peter Bromberg posted at Tuesday, February 10, 2009 1:27 PM
Original Article
 

Generics versus "object"
Robbe Morris replied to Peter Bromberg at Tuesday, February 10, 2009 1:56 PM

This is pretty good Pete.

At first glance, someone may want to take your universal approach and bind it more closely to a specific app.  You could create a base class that all other data classes inherit from.  Then, use a generic where clause on your methods asking for a class that inherits your base class versus casting back and forth from object.

I could be wrong, but I suspect the performance would be better.  How much is anyone who has time to test its guess.  Caching the results of .GetProperties() "somewhere" will also help with performance.

For you newbie's, make sure you obfuscated and string encrypt your assemblies to make it much harder to get the hardcoded keys in this sample.

 

Good ideas.
Peter Bromberg replied to Robbe Morris at Tuesday, February 10, 2009 4:50 PM

In debug mode (release would be faster) it takes about 20 ms to encrypt a "Customer". On the Silverlight side, about 100 ms to decrypt. I'm sure that and other methods can be used to improve the performance. The PropertyInfo array can certainly be cached in a keyed Dictionary with the key being the type name. That means the only big hit would be the first time around.

Regarding the hardcoded keys, all three components (password, hash and salt) are required to get a valid key, so the fact that two are hardcoded in the code is not helpful to a hacker. You could certainly use other features such as the hashcode of the assemblyFullName and other items to have all three generated at runtime and not visible anywhere in the code.

 

Good ideas but...
Karol Kolenda replied to Peter Bromberg at Thursday, February 12, 2009 5:58 PM
Hi Peter
First of all thank you for your article, it's been a great pleasure to read it.

I've got only one question re: security. What is stopping a hacker from creating a new Silverlight project, loading your legitimate assembly dynamically into hacker's app domain and executing GetHashCode() on your freshly loaded assembly (instead of Assembly.GetExecutingAssembly().GetHashCode().ToString();?

Thanks,
Karol
 

Good question!
Peter Bromberg replied to Karol Kolenda at Thursday, February 12, 2009 7:26 PM
Why don't you try it, and let us know what you find out?
 

I want my wsHTTPBinding
Mr. Nadig replied to Peter Bromberg at Thursday, February 12, 2009 7:55 PM

Good stuff.

 

 

GetHashCode()
Karol Kolenda replied to Peter Bromberg at Thursday, February 12, 2009 9:53 PM
Hi Peter,
It's worse than I thought. It looks like Assembly.GetExecutingAssembly().GetHashCode() is changing every time your reload your app.
If I'm wrong I'll take it back, but according to .Net reference GetHashCode() returns an instance hash code not your assembly hash code, so you cannot really use it. It just happens to be the same value in your VS env because it creates a brand new instance of browse+Silverlight plugin every time you hit test your app - hit F5 (reload the page) again and the magic is gone.

It's really a shame because I've been looking for non-SSL security for SL applications for quite a long time. All this sweet encrypting classes in SL and we still cannot use it to create a secure SL app without SSL. Shame.

Regards,
Karol
 

You might be right
Peter Bromberg replied to Karol Kolenda at Friday, February 13, 2009 7:14 AM
I haven't had a chance to fully test this, but if that's the case you would simply have to use hard-coded strings for all the key parameters and accept a slightly lesser degree of security. The main target here is to ensure that what's coming and going over the wire is encrypted.
 

You are right!
Peter Bromberg replied to Karol Kolenda at Saturday, February 14, 2009 7:51 PM
Reloading the app in the browser is giving a new salt value each time (outside the IDE). So for now, I have revised the article and the downloadable source code to create unique strings for Hash and Password that do not change at runtime. At any rate, the important thing was to encrypt the values that are going over the wire, and that works fine.
 

  Help!
Sergio Davila replied to Peter Bromberg at Saturday, December 19, 2009 11:19 PM
I'm trying to implement your solution to thwart man-in-the-middle/fiddler attacks on my Silverlight project.  However, I can't firgure out where to hook into Linq2SqlDomainService/Context to encrypt/decrypt de RIA entities as they go back and forth between my client & server?

I would sincerely appreciate your help.

Thank you!
 






  $1000 Contest    [)ia6l0 iii - $231  |  Jonathan VH - $153  |  Huggy Bear - $133  |  egg egg - $100  |  F Cali - $93  |  more Advertise  |  Privacy  |   (c) 2010