The Secrets of Strong Namingby Mike Gunderloy
If you've been working with .NET for any length of time, you've probably run
across the concept of a strong name. No, that doesn't mean that your
assemblies should have names like
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. In this article, I'll show you how strong names are constructed
and demonstrate the mechanics of working with strong names in .NET.
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.
|Figure 1. 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.
|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.
|Figure 3. 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.
|Figure 4. 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
All of this 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 (see my article "Binding Policy in .NET" for further details).
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 -k MyKeyFile.snk
If you're using the command-line compilers (
csc.exe), you can
specify the key pair to use in your command line to the assembly linker,
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
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:
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:
- 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
- Distribute the file containing only the public key to all of the developers in the company, and store the file containing both keys securely.
- 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
/delaysigncommand-line switch to indicate delay signing.
- 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
- At this point, you can use the assembly freely in testing and development.
- 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
- Finally, you can instruct the GAC to resume verification for an assembly
by issuing the following command:
sn.exe -Vu MyFile.dll
You've undoubtedly heard of Microsoft's "Trustworthy Computing" initiative, which involves intensive security reviews of all shipping Microsoft products. Microsoft is slowly but surely moving towards a world in which all of their software is secure by default. If you know what you're doing, you can relax the security of software such as Windows Server 2003, but it's designed to make it hard to make yourself vulnerable by accident.
When you assign strong names to your assemblies, you're doing your part for trustworthy computing. Shipping your code with strong names makes it much less likely to be used as a carrier for a trojan horse or other attack on someone's machine. The .NET Framework and Visual Studio .NET provide the tools to make it easy for you to protect the integrity of your shipping code. I strongly urge you to make use of these tools, and by doing so, make the computing world just that tiny bit safer.
Mike Gunderloy is the lead developer for Larkware and author of numerous books and articles on programming topics.
Return to ONDotnet.com