Strong Naming Assemblies

Microsoft's Strong Naming facilities let you, at build time, or shortly after, sign assemblies. This has several consequences:

Generating Keys

.NET ships with a utility called SN for dealing with strong naming. It knows how to generate keys, delay sign assemblies and all sorts of other stuff. For basic use to just generate a key pair we do this:

c:\> sn -k mykeyfile

The generated key file contains both the public and private key components and MUST NOT BE DISTRIBUTED.

Basic Strong Naming

After generating a key file using sn you just need to tell your assemblies about it and it'll all just work. This does require, however, that developers have access to the private key which may not always be a good idea (see delay signing below)

[assembly: AssemblyDelaySign(false)] 
[assembly: AssemblyKeyFile("c:\\mykey.sn")]

When you reference an assembly from another one a copy of the referenced assemblies public key is embedded in the calling assembly. This can be used to verify that the library does come from the correct vendor in addition to things like a version number.

Tamper Proofing

When an assembly is strongly named the process is something like this:

Now when loading a strongly named assembly the .NET runtime checks the signature on the assembly using the public key that is embedded in the assembly. Assuming the signature and public key are left intact, any tampering to the assembly will become evident. However, one can simple tamper with the assembly and resign using a new key and attach that in place of the existing.

As far as I can tell you can't prevent against this. For libraries though assembly using the library would bail because the assembly is no longer signed by the correct key. For executables I don't believe there is anything stopping this unless the EXE itself tries to verify what key it was signed with and that could be disabled too. Oh well

Delay Signing

Delay signing allows you to deploy using strong naming but without requiring the developers have access to the private key.

First strip out the public key from your key file and distribute this to the developers. The private key file would only exist on the build machine and a backup CD or two somewhere locked away.

c:\> sn -p mykey.sn mypubkey.sn

Now in the assemblies turn on delay signing and point them to the public key file:

[assembly: AssemblyDelaySign(true)] 
[assembly: AssemblyKeyFile("c:\\mypubkey.sn")]

Now if we do a build it won't run because the applications are not signed so for each assembly we are delay signing you need to turn off verification of it.

c:\> sn -Vr myassembly.dll

The build should run happily now on the developer machines. When you go to make a final build take each of the assemblies and manually sign them:

c:\> sn -R myassembly.dll mykey.sn

And if you want to test this out on the current machine we need to re-enable strong name verification for all our assemblies again:

c:\> sn -Vu myassembly.dll

It also looks like this does the right thing with checks like StrongNameIdentityPermission during developer time. Since this works I'm wondering if this check just looks at the public key attached to the calling assembly and assumes that .NET has already verified that that key matches the signing key and signatures instead of actually rechecking the signature on the calling assembly to see if it matches. I suppose that makes sense.

Securing Libraries

Securing libraries is fairly simple. The idea being to only allow libraries to be called by assemblies signed by a specific key. This allows vendors to distribute libraries that can only be used by their applications.

The process involves adding a security check to every class in the libraries assembly (StrongNameIdentityPermission). You provide the public key of the signing key and an action of Demand or LinkDemand. Demans means that all callers, all the way up the call stack, are required to be signed by the indicated key. LinkDemand only requires the immediate caller to be signed.

Here is my example library:

using System;
using System.Reflection;
using System.Security.Permissions;

[assembly: AssemblyKeyFile("c:\\test.sn")]

namespace ClassLibrary1
{

    // This class requires immediate callers to be signed by the following key.
    // To get the Public Key just to a sn -Tp library.dll after the first build and cut 
    // and paste the result into here.  This code is required on each an EVERY class you
    // want to limit access to.  LinkDemand means that only the immediate caller is 
    // checked while Demand means all callers are checked.
    [StrongNameIdentityPermission( SecurityAction.LinkDemand,
         PublicKey="002400000480000094000000060200000024000052534131000400000100010071" +
         "6dc81f83e56947cc88aa1b66ad0391cea82bea8351c83ba60181c0a0555ccd8794" +
         "bdc55eeaf100f0a6ea7656737ee54181eda5dc7e3b854b7fc3a8982c8bf3fba60b" +
         "d1e1d36b10e57935fa0456c9bbf2da268d37cbc5743663ab969dbc118d2798c042" +
         "90012e8b193e7a2ca61f0f1f750387036b9d3876c3154a1dd7b311cc")]

    public class Class1
    {
        public static void hi()
        {
            System.Console.WriteLine("Hello from NEW");
        }
    }

}

The public key string is obtained by running "sn -Tp library.dll" after the first build. This string is then embedded in all your security checks. I would think this is something that the build process should handle.

The application could look like this. If it is signed then the call will succeed. If it is not a security exception will be raised.

using System;
using System.Reflection;

[assembly: AssemblyKeyFile("c:\\test.sn")]

namespace ConsoleApplication4
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            ClassLibrary1.Class1.hi();
        }
    }
}

Thoughts on Security

Strong Naming raises the bar for tampering with .NET applications. Apps can verify that the libraries they are using are the libraries they were built against. The .NET runtime will verify that the application itself hasn't been tampered with and libraries can assert who can use them. However, since disassmbling .NET code is really easy there is very little stopping someone from disassmbling an app to code, removing the security checks / strong naming, and rebuilding.

Also for verifying applications / assemblies. Since the signature and public key are stored on the end of the executable just simple tampering with the EXE is difficult but there is nothing stopping one from just resigning the app with a new key and attaching the new signature and other public key to the app. Unles the app actually validates which key it was signed with and even that can be removed.

I seem to recall reading that strongly named assemblies are more a strong versioning tool that a security tool. I believe that you can external to the application, turn off strong name checking. This should definately be checked into.