How to Secure Your Assemblies

Introduction

What’s stopping people from using any assemblies you’ve written?

This is one of the first questions that I asked myself about .NET. In a well-organised piece of software, anything useful is designed to be shared—not just in one application, but in all applications written by a particular company. The way of doing this is normally by the use of shared code. Prior to .NET, this generally occurred using either Win32 DLLs or COM DLLs. In .NET, the same philosophy of shared code comes in the form of assemblies, which are again DLLs.

Why use this philosophy of shared DLLs as opposed to copying classes around and including them in projects? The one advantage is that the DLL (whether it be Win32, COM, or .NET assembly) can be tested and proved to be correct. Once proven to be correct, any company then can use that DLL with confidence; this speeds up development and also leads to more stable solutions. Another advantage is that if a bug is found in the DLL, a fix can be made and the fix will automatically filter down through all the affected applications.

.NET assemblies can be accessed by any application, leading to the possibility of other people utilising your company’s development effort without cost. This accessabiilty also cause can a potential breach of security. Someone, for instance, could write an assembly with the same interface (in other words, the same classes and so forth), and replace the original assembly. Such a method would give the new assembly access to all data passed to it from the application. If this new assembly then calls the original assembly, the application would be unaware of this breach of security.

It’s no surprise that it is the System.Security namespace within the .NET Framework that provides some of the answers to the above problems.

Using Strong Names

The primary way to protect your assemblies from attack is to attach a strong name. Strong names are pairs of keys (strings of numbers)—one private and one public. The private key is held inside the assembly and is inaccessible. The public key is available to all. Generally speaking, a public/private key pair is used in encryption: A public key is used to encrypt a data stream, but you can only decrypt this data if you have the private key.

You generate a public/private key pair by use of the sn.exe utility, which generates a key pair and stores it into a file. This utility is a part of the .NET Framework SDK, so you most likely already have it if you are doing .NET development.

You use the strong naming tool as shown below:

sn -k <keyfilename>.snk

To attach a strong named key file to an assembly (either a DLL or an executable), go to the AssemblyInfo file and replace the AssemblyKeyFile attribute with your .snk file. For example,

[assembly: AssemblyKeyFile("mykeyfile.snk")]

If you do this for both your executable and any assemblies, using the same key file, these assemblies cannot be replaced after compilation without linking to the same public/private key pair in the SNK file. This, then, prevents access to your applications data by replacing the appropriate assembly with one that traps the data passing through it.

Code Access Attributes

There are a number of attributes that you can attach to your classes inside of assemblies to prevent applications from using them. All are derived from the System.Security.Permissions.CodeAccessSecurityAttribute. Here, I will give an example of one of them: the StrongNameIdentityPermissionAttribute. To use this attribute, you first need to be able to extract the public key out of the .snk file. You do this by the use of the Secutil.exe tool.

If you run the following on your .dll that has a .snk file attatched:

secutil -hex -c -s <yourassembly>.dll

You will see output similar to the following:

Microsoft (R) .NET Framework SecUtil 1.1.4322.573
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

Public Key =
0x002400000480000094000000060200000024000052534131000400
0001000100B7E269CFBC5A020745C65DD01E39A01FC4819125A52265
186DB13D82B947B5745AC14CC11EAD50A6DE94B1BA7E2973867298C6
BB031A86DB0570BF8E12D08A98DF6D2609258449D3A10AE89165222E
F04A9B15BF7E896BB317B383137F12B3A93D928EA43720739BB62A22
0508BF4FD2E20F1CFE0FF63888FC25B6A1E0EABABD
Name =
Assemblies.File
Version =
1.1.0.0
Success

You will need access to this public key, so I recommend you pipe the output into a file you then can open in NotePad, for example:

secutil -hex -c -s <yourassembly>.dll > key.txt

If you have a class in an assembly that you don’t want anyone else to be able to access, you can use the StrongNameIdentityPermissionAttribute to prevent access. Any calling code that isn’t signed with your .snk file in the following way—using the public key from above as an example—won’t have access:

[StrongNameIdentityPermissionAttribute(SecurityAction.Demand,
   PublicKey = "002400000480000094000000060200000024000052534131000400" +
               "0001000100B7E269CFBC5A020745C65DD01E39A01FC4819125A522" +
               "65186DB13D82B947B5745AC14CC11EAD50A6DE94B1BA7E29738672" +
               "98C6BB031A86DB0570BF8E12D08A98DF6D2609258449D3A10AE891" +
               "65222EF04A9B15BF7E896BB317B383137F12B3A93D928EA4372073" +
               "9BB62A220508BF4FD2E20F1CFE0FF63888FC25B6A1E0EABABD")]
public class MyClass
{
   public MyClass()
   {
   }
}

This attribute can be added at either the class level or the method level. In addition, you can choose when you want your attribute to be tested: MSDN lists quite a few alternatives here. You choose by passing in the appropriate SecurityAction enumerated value. I should let you know that I’ve only been able to get two to work, SecurityAction.Demand and securityAction.LinkDemand.

SecurityAction.Demand—This tests the condition every time either the class is instansiated or every time the method is called, depending on whether a method or a class has the attribute attached.

SecurityAction.LinkDemand—This tests the condition when the code to which the attribute is attached is JIT-ted. This only happens once and so is potentially more efficient.

Conclusion

I have shown how to protect our code both from a hacker attempting to replace an assembly and from someone trying to use one of our assemblies. In fact, there are many more code access attributes that you could use; I’ve only dealt with one here. In addition, you could write your own code access attributes to test just about anything, but that’s a subject for a later date.

The demonstration code included with this article is intended as a testbed to try out various combinations of attributes and see what happens.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read