Thursday, January 17, 2008

Strong Naming

The Secrets of Strong Naming

The strength of a strong name lies in the protection that it offers your assemblies. The .NET Framework uses strong names to identify assemblies and to protect them from tampering.

Hashes and Signing

To grasp the way that strong names work, you first need to understand a pair of cryptographic concepts: hashing and digital signatures.

Hashing is used to create a unique, compact value for a plaintext message. "Message" is a very broad term here; in terms of assemblies, the message is the assembly itself. The message is used as an input to the hash algorithm (in the case of strong naming, the SHA1 algorithm is used). Figure 1 diagrams the process.

How hashing works

Hashing is a one-way street: you can't decrypt the hash value once it has been computed. However, hashing is very useful for comparing values. If two assemblies produce the same hash value, you can assume that the assemblies are the same. Conversely, if hashing an assembly produces a value that doesn't match a previously-calculated hash, you know that something in the assembly has been changed.

Knowing the hash value for an assembly lets you check that no one has tampered with the assembly. But how do you prevent someone from tampering with the hash value? That's where digital signing comes in. While the mathematics of digital signatures are complex, the concept is fairly simple. A digital signature depends on a pair of related numbers, the public key and the private key. When data is encrypted with the public key, it can only be decrypted with the private key (and vice versa), as shown in Figure 2.

Using key pairs

Strong Naming for Assembly Identity

The combination of hashing and digital signing allows .NET to protect your assemblies from tampering. Here's how it works. First, a hash value is created from the assembly. Then, the hash value is encrypted with your private key and placed, along with your public key, in the assembly itself. Figure 3 shows this process schematically.

Placing a strong name in an assembly

The CLR validates assemblies at runtime by comparing two sets of hash values. First, the public key is used to decrypt the encoded version of the hash. Second, a new hash is computed from the current contents of the assembly. If the two hashes match, all is well. Figure 4 shows this process.

Checking the strong name in an assembly

What happens if an assembly has been tampered with after it was signed? In this case, the new hash value calculated at runtime won't match the stored hash value that was encrypted with your private key. Under those circumstances, the CLR will refuse to load the assembly.

Note that the strong name guarantees the integrity of the assembly, not necessarily its safety! There's nothing to prevent someone from creating a malicious assembly and signing it with a strong name.
You can use a strong name to verify that an assembly came from a particular source, and that it wasn't tampered with after it was signed. It's up to you to decide, based on whatever information you choose, whether to trust code from that source.

What's in a (Strong) Name?

In addition to a hash derived from the assembly's contents, the strong name includes three other pieces of information:

  • The simple text name of the assembly
  • The version number of the assembly
  • The culture code (if any) of the assembly

This entire information works together to supply a unique identity for each assembly. The CLR uses this information when deciding whether a particular assembly is the one called for by a reference from another assembly. When you set a reference from one assembly to another, the calling assembly stores a representation of the called assembly's public key. At runtime, the CLR can use this to check that the referenced assembly comes from the correct vendor. In addition, the other information in the strong name is used to determine whether a particular assembly fills the binding policy requirements for the reference.

The Mechanics of Strong Naming

Both the .NET Framework SDK and Visual Studio .NET provide tools for assigning strong names. That makes sense, because using Visual Studio .NET to create assemblies is completely optional. Before you can sign anything, you need to create a key pair (consisting of a public key and a private key). Typically, you'll store your key pair in a file with the extension .snk. To do this, you use the strong name tool, sn.exe:

sn -k MyKeyFile.snk

If you're using the command-line compilers (vbc.exe or csc.exe), you can specify the key pair to use in your command line to the assembly linker, al.exe. For example, you might sign MyFile.dll with the key pair in MyKeyFile.snk with this command line:

al /out:MyFile.dll MyFile.netmodule /keyfile:MyKeyFile.snk

If you're using Visual Studio .NET, you'll still generate your key pair at the command line. After generating it, you can include it in your assembly by specifying the AssemblyKeyFile attribute in the assembly information file (AssemblyInfo.vb or AssemblyInfo.cs). In a C# project, for example, you can include a key file and produce a signed assembly with this attribute:

[assembly: AssemblyKeyFile ("..\\..\\MyKeyFile.snk")]

The filename in the AssemblyKeyFile attribute should include the full relative path from the compiled assembly to the key file.

Keeping Secrets with Delay Signing

Protecting your private key is obviously very important; if a nefarious person gets your private key, they can produce assemblies that appear to have been signed by you. Because of this, you may wish to keep your private key a closely-guarded secret, known only to a few people in the company. But then, how can you handle assembly signing? It would be tedious if you were the only person who knew the private key, and you had to sign every build of every assembly produced by every developer in your company.

Fortunately, .NET provides a way around this problem: delay signing. With delay signing, you can build and test an assembly knowing only the public key. The private key can be applied later if the assembly is actually shipped to customers. Here is a summary of the delay signing process:

  1. Extract the public key from the public/private key pair. To extract the public key from a file that is storing the public/private key pair, you can use the strong name tool with a slightly different command line:

    sn.exe -p MyKeyFile.snk MyPublicKeyFile.snk

  2. Distribute the file containing only the public key to all of the developers in the company, and store the file containing both keys securely.

  3. Include the public key file in your assembly information file, and specify delay signing:

    [assembly: AssemblyDelaySign(true)]

    [assembly: AssemblyKeyFile("..\\..\\MyPublicKeyFile.snk")]

If you're using the assembly linker tool rather than Visual Studio .NET, use the /delaysign command-line switch to indicate delay signing.






  1. Turn off verification for the assembly if you're storing the assembly in the GAC. By default, the GAC verifies the strong name of each assembly. If the assembly is not signed by using the private key, this verification fails. So, for development and testing purposes, you can relax this verification for an assembly by issuing the following command:

    sn.exe -Vr MyFile.dll

  2. At this point, you can use the assembly freely in testing and development.

  3. When you're ready to deploy a delay-signed assembly, you need to sign it with your private key:

    sn.exe -R MyFile.dll MyKeyFile.snk

  4. Finally, you can instruct the GAC to resume verification for an assembly by issuing the following command:

    sn.exe -Vu MyFile.dll

0 Comments: