Over the weekend, I began to think about how to better protect proprietary algorithms and other sensitive code in a Silverlight application, keeping the assembly away from prying, snooping eyes. I decided the best way would be to keep the code in memory and never have it committed to the hard drive. A little research and a little coding and badda bing (er, badda google?).
The solution turns out to be rather simple. You need four projects: the Silverlight app, the web app, the contract (interface) and the implementation Silverlight class libraries. The Silverlight app references the contract library which pulls it into the XAP. The implementation library references the contract library to implement the interface, of course. And the web app does its thing, supplying the XAP file to the browser and most importantly supplying the protected bits via a stream that presumably is protected by SSL, authentication and authorization mechanisms, items I've conveniently left out of this post and the sample code for brevity.
Start with the AppManifest.xaml (in Dinorythm.xap)
Note that the manifest contains only the Silverlight app and the contract class library along with the other assemblies required for the Silverlight Navigation Application (find what you need at Scott Guthrie's informative Silverlight 4 Released blog post).
<Deployment
xmlns="http://schemas.microsoft.com/client/2007/deployment"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
EntryPointAssembly="Dinorythm"
EntryPointType="Dinorythm.App"
RuntimeVersion="4.0.50401.0">
<Deployment.Parts>
<AssemblyPart x:Name="Dinorythm" Source="Dinorythm.dll" />
<AssemblyPart x:Name="DinoContracts" Source="DinoContracts.dll" />
<AssemblyPart x:Name="System.ComponentModel.DataAnnotations" Source="System.ComponentModel.DataAnnotations.dll" />
<AssemblyPart x:Name="System.ServiceModel.DomainServices.Client" Source="System.ServiceModel.DomainServices.Client.dll" />
<AssemblyPart x:Name="System.ServiceModel.DomainServices.Client.Web" Source="System.ServiceModel.DomainServices.Client.Web.dll" />
<AssemblyPart x:Name="System.ServiceModel.Web.Extensions" Source="System.ServiceModel.Web.Extensions.dll" />
<AssemblyPart x:Name="System.Windows.Controls" Source="System.Windows.Controls.dll" />
<AssemblyPart x:Name="System.Windows.Controls.Navigation" Source="System.Windows.Controls.Navigation.dll" />
</Deployment.Parts>
</Deployment>
DinoContracts a Silverlight 4 Class Library (in Dinorythm.xap).
To any nosy disassembler looking for the secret sauce code, all they will get is the interface and perhaps a few domain classes if you need them.
namespace DinoContracts
{
public interface IMySecretCode
{
string DoSecretWork(string input);
}
}
SecretAlgorithms a Silverlight 4 Class Library (NOT in Dinorythm.xap)
This library has a project reference to DinoContracts and copies it's output to the Dynorythm.Web/App_Data folder.
namespace SecretAlgorithms
{
public class MySecretCode : IMySecretCode
{
public string DoSecretWork(string input)
{
return "results of my secret code";
}
}
}
Dinorythm.Web an ASP.NET MVC 2 project
Obviously this code is not production ready. You need some security here, but what you see here will get you the result you seek. Securing this action method might be a good topic for another blog post.
namespace Dinorythm.Web.Controllers
{
[HandleError]
public class HomeController : Controller
{
//...other code removed for brevity
public FileContentResult Secret()
{
string ctype = "application/octet-stream";
string fileName = "SecretAlgorithms.dll";
byte[] dll = GetFile(fileName);
return File(dll, ctype, fileName);
}
byte[] GetFile(string fileName)
{
string path = HostingEnvironment.MapPath(@"~/App_Data/" + fileName);
byte[] bytes = System.IO.File.ReadAllBytes(path);
return bytes;
}
}
}
Dinorythm a Silverlight 4 Navigation Application
This app has a project reference to DinoContracts but knows nothing about the SecretAlgorithms project. The secret code in this demo won't win any awards but it might help you conceive (and me to remember) of how to get the job done with your real intellectual property.
namespace Dinorythm
{
public partial class About : Page
{
public About()
{
InitializeComponent();
}
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
WebClient down = new WebClient();
down.OpenReadCompleted += new OpenReadCompletedEventHandler(down_OpenReadCompleted);
Uri location = new Uri(System.Windows.Application.Current.Host.Source, @"../Home/Secret");
down.OpenReadAsync(location);
}
void down_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
AssemblyPart part = new AssemblyPart();
Assembly asm = part.Load(e.Result);
IMySecretCode secret = (IMySecretCode)asm.CreateInstance("SecretAlgorithms.MySecretCode");
if (secret != null)
this.ContentText.Text = secret.DoSecretWork("help me");
else
this.ContentText.Text = "was null";
}
}
}
Help credits go to Tim Heuer for some comparison with cached assemblies and to the practical help from a Silverlight Tip of the Day.
A Note About Security: I am not a self-proclaimed security expert. I'm sure there are ways to defeat this approach, but I suspect that doing so would be more trouble than it would be worth. But certainly this would be more efficient than simple or even complex obfuscation. Then again one could obfuscate and then dynamically download and instantiate in memory. That ought to really throw a would be intellectual property thief for a real loop. (Also note, I've never tried obfuscation in a Silverlight class library, so perhaps it's not even possible. Hmm... Another research and blog topic.)
If you find this code useful, I'd love to hear from you. Download Dinorythm.zip (313.34 KB) here.