Silverlight to WCF Service Object Encryption
By Peter Bromberg
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.
Popularity (3422 Views)
 |
| 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.
|  |
|
|
Article Discussion: Silverlight to WCF Service Object Encryption
Generics versus "object"
Robbe Morris replied
to Peter Bromberg at Wednesday, October 27, 2010 3:20 AM
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 Wednesday, October 27, 2010 3:20 AM
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 Wednesday, October 27, 2010 3:20 AM
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 Wednesday, October 27, 2010 3:20 AM
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 Wednesday, October 27, 2010 3:20 AM
GetHashCode()
Karol Kolenda replied
to Peter Bromberg at Wednesday, October 27, 2010 3:20 AM
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 Wednesday, October 27, 2010 3:20 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 Wednesday, October 27, 2010 3:20 AM
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 Wednesday, October 27, 2010 3:20 AM
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!