|< Day Day Up >|
15.2. Strongly Named Assemblies
The software development process relies increasingly on integrating code with components from multiple vendors. The vendor may be well known and trusted, or the component may be Internet freeware from an unknown developer. In both cases, security concerns demand a way to identify and authenticate the software. The most reliable solution is to sign the software with some unique digital signature that guarantees its origin and trustworthiness. The certificates that identify the publisher of downloadable software on the Internet are an example of this.
One of the integral parts of .NET security is the use of signing to create a uniquely identifiable assembly. .NET refers to such an assembly as strongly named. A strongly named assembly uses four attributes to uniquely identify itself: its file or simple name, a version number, a culture identity, and a public key token (discussed later). Together, these four are referred to as the "name" of the assembly. In addition, the assembly has the digital signature created by signing it. All of this is stored in the assembly's manifest. 
Although an assembly does need to be strongly named, doing so offers several advantages:
Creating a Strongly Named Assembly
The first step in creating a strong name is to create a pair of public and private encryption keys. During compilation, the private key is used to encrypt the hashed contents of the files contained in the assembly. The encrypted string that is produced is the digital signature that "signs" the assembly. It is stored in the manifest, along with the public key. Here are the steps to create and use a strongly named assembly:
Figure 15-3 summarizes the overall process. Note how decryption works. The decrypted signature yields a hash that should match the output when a new hash is performed on the assembly. If the two match, you can be sure that the private key associated with the public key was used for the original signing, and that the assembly has not been tampered with梒hanging even one bit will result in a different hash.
Figure 15-3. Using private and public keys to sign and verify a strong assembly
It is imperative that the private key generated using Sn.exe (or some other process) not be compromised, because it is how an organization uniquely signs its software. If another party has access to the key, consumers cannot trust the ownership of the assembly.
One measure to secure the key is to limit its availability to developers, or withhold it altogether. During the development stage, developers are given only the public key. Use of the private key is delayed until it is necessary to sign the final software version. Delayed signing requires different steps than are used for creating a strongly named assembly:
Because an assembly references another strongly named assembly using its public key, there is no need to rebuild assemblies dependent on this one.
Global Assembly Cache (GAC)
The Global Assembly Cache is a special directory set aside where strongly named assemblies can be stored and located by the CLR. As part of resolving references at load time, the CLR automatically looks in the GAC for the requested assembly. The obvious advantage of storing assemblies in the GAC is that they are located in a central, well known location where they can be located and shared by multiple applications. A less obvious advantage is that the strong name signatures for assemblies in the GAC are verified only when installed in the GAC. This improves the performance of applications referencing these assemblies, because no verification is required when loading the assembly.
Physically, the GAC is a Microsoft Windows directory located on the following path:
You can view its contents using a shell extension (ShFusion.dll) that is added to Windows Explorer as part of the .NET Framework installation. Each entry displays an assembly's name, type, version number, and public key token. By clicking an assembly entry, you can bring up a context menu that permits you to display the assembly's properties or delete it.
The easiest way to install a strongly named assembly into the GAC (or uninstall one) is to use GACUtil.exe, a command-line utility that ships with .NET SDK. Here is the syntax for performing selected operations:
There are a couple of drawbacks to storing an assembly in the GAC. First, it is difficult to reference during compilation due to the verbose GAC subdirectory naming conventions. An alternative is to compile referencing a local copy of the assembly. Then, remove the local assembly after compilation is completed.
Another possible drawback stems from the fact that an assembly cannot be copied into the GAC. If your application requires an assembly in the GAC, it eliminates deploying an application by simply copying files to a client's machine. Deployment issues are discussed in the last section of this chapter.
A major benefit of using strongly named assemblies is that the CLR uses the assembly's version information to bind assemblies that are dependent on each other. When such an assembly is loaded, the CLR checks the version number of referenced assemblies to ensure they have the same version numbers as recorded in the calling assembly's manifest. If the version fails to match (usually because a new version has been created), an exception is thrown.
Assigning a Version Number to an Assembly
Every assembly has a version number. A default value is used if one is not explicitly defined. The version can be assigned using the Assembly Linker (Al.exe) tool, but is usually declared within the code using an AssemblyVersion attribute:
The version number has four parts:
<major version>.<minor version>.<build number>.<revision>
You can specify all the values or you can accept the default build number, revision number, or both by using an asterisk (*). For example:
[assembly:AssemblyVersion("2.3.")] yields 188.8.131.52 [assembly:AssemblyVersion("2.3.*")] yields 2.3.1830,4000
When an asterisk (*) is specified for the build number, a default build number is calculated by taking the number of days since January 1, 2000. The default revision number is the number of seconds past midnight divided by two.
You can use reflection to view an assembly's version along with the other parts of its identity. To illustrate, add the following attributes to the code in Listing 15-1 to create a custom version number and strong name:
[assembly: AssemblyKeyFile("Keylb.snk")] [assembly: AssemblyVersion("1.0.*")]
Compile the code and use the Assembly.GetName method to display the assembly's identification.
This method returns an instance of the AssemblyName class that contains the assembly's simple name, culture, public key or public key token, and version.
fxtest, Version=1.0.1839.24546, Culture=neutral, PublicKeyToken=1f081c4ba0eeb6db
|< Day Day Up >|