Implement versioning.
Plan, configure and deploy side-by-side deployments and applications.
A shared assembly is shared among multiple applications on a machine. It is reasonable to store a shared assembly in a common place that is well known to all the applications that use the assembly. However if shared assemblies are identified by just their name similar to the private assemblies, there is a problem. There might be a case in which two software publishers might use the same name for an assembly, thereby overwriting files and causing applications using those assemblies to behave abnormally.
To resolve this problem, the .NET Framework requires all shared assemblies to have a strong name. A strong name uses four attributes to identify an assembly:
Simple name
Version number
Culture identity (optional)
Public key token
A regular Windows folder can just differentiate files based on their simple name and not the strong name. Therefore, to store strongly named assemblies in a well-known shared location, you need a special type of storage. The .NET Framework provides this storage in the form of the Global Assembly Cache (GAC). In addition to providing a shared location, the GAC also provides the following benefits for shared assemblies:
Integrity Check When assemblies are installed in the GAC, the GAC applies a strong integrity check on the assembly. This check guarantees that the contents of the assembly have not been changed since it was built.
Security The GAC only allows users with administrator privileges to modify its contents.
Side-by-side Versioning Multiple assemblies with the same name but different version information can be maintained in the global assembly cache.
In this section, you'll learn the following about shared assemblies:
How to assign a strong name to an assembly
How to add an assembly to the GAC
How to reference an assembly from the GAC
The binding policy for shared assemblies
How the CLR binds to a shared assembly
How to delay sign a shared assembly
To create a strong name, you need an assembly's simple name, version number, an optional culture identity, and a key pair. The key pair consists of two related pieces of binary data: a public key and a private key.
The public key represents the identity of a software publisher. When you create a strongly named assembly, the public key is stored in the assembly manifestalong with other identification information, such as name, version number, and culture. This scheme does not look foolproof because the public key is easily available from the assembly manifest and one can easily fake an assembly identity with some other company's public key. To verify that only the legitimate owner of the public key has created the assembly, an assembly is signed using the publisher's private key . The private key is assumed to be known only to the publisher of the assembly. The combination of a public key and a private key is known as a key pair . Key pairs have the property that data encrypted with the private key can only be decrypted using the public key. But it doesn't work the other way. That is, knowing the public key doesn't let you produce the encrypted data. It is also computationally infeasible to determine the private key if you know only the public key.
The purpose of this public-key cryptography is not to encrypt things so that they can't be decrypted. Rather, it is to encrypt them so that their origin is known. If you keep your private key private (as you should), you can use it to produce messages that can only come from you.
The process of signing an assembly and verifying its signature works like this:
Signing an assembly When you sign an assembly, a cryptographic hash of the assembly's contents is computed. The hash is then encoded with the private key and is stored within the assembly. The public key is stored in the assembly manifest.
Verifying the signature When the CLR verifies an assembly's identity, it reads the public key from the assembly manifest and uses it to decrypt the cryptographic hash that is stored in the assembly. It then recalculates the hash for the current contents of the assembly. If the two hashes match, this ensures two things: The contents of the assembly were not tampered with after the assembly was signed, and only the party that has a private key associated with the public key stored in the assembly has signed the assembly.
NOTE
Signing a Multifile Assembly If an assembly consists of multiple files, only the file that contains the assembly manifest needs to be signed. This is because the assembly manifest already contains file hashes for all the files that constitute the assembly implementation. The CLR can easily determine whether a file has been tampered with by matching its actual hash with what is stored in the assembly manifest.
You can easily generate public/private key pairs by using the Strong Name tool ( sn.exe ), which is available in the .NET Framework SDK.
Step by Step 10.5 shows you how to create a public/private key pair by using the Strong Name tool ( sn.exe ).
STEP BY STEP10.5 Creating a Public/Private Key Pair by Using the Strong Name Tool ( sn.exe )
|
NOTE
Create a Single Key File Because the key file is used to establish your identity, not the identity of your code, you should only create a single key file for an entire development organization.
Step by Step 10.6 shows you how to create a strongly named assembly. You'll use the key file generated in Step by Step 10.5 to assign a strong name to an assembly.
STEP BY STEP10.6 Assigning a Strong Name to an Assembly
|
In Step by Step 10.6, you change the AssemblyVersion attribute of the assembly from 1.0.* to 1.0 . The assembly's version consists of up to four parts :
<major>.<minor>.<build>.<revision>
If you want to use a fixed version value, you can hard-code it. The default value of the version uses an asterisk in place of build and revision numbers ; this changes the build and revision each time you compile the project. The build is calculated as the number of days since January 1, 2000, and the revision is calculated as the number of seconds since midnight divided by 2.
At runtime, the CLR uses version information to load a shared assembly. In the next few examples, you may change and compile your projects several times and as a result, you'll change the version of the assembly if you use the default version property. This will undesirably lead to having multiple versions of the same assembly installed in the GAC when you only wanted the most recent one.
To have the control of version numbers, in the examples of this chapter, I'll hard-code the version number. Shortly, you will also learn that if you willingly want to install multiple versions of an assembly in the GAC, you can still manage their side-by-side execution.
In Step by Step 10.6, you used Visual Studio .NET to attach a strong name to an assembly. If you want to do this from the command line, you can use the Assembly Linker tool ( al.exe ) with the keyfile option.
After you have associated a strong name with an assembly, you can place the assembly in the GAC. There are several ways you can add an assembly to the GAC. Using the Windows Installer is the recommended approach, but there are some quick alternatives, too. However, you should use these quick approaches only for development purposes; they are not recommended for installing assemblies on the end user 's computer.
Using the Microsoft Windows Installer is the preferred way of adding assemblies to the GAC. The Windows Installer maintains a reference count for assemblies in the GAC and provides un-installation support. You will learn how to add assemblies using Windows Installer technology through the setup and deployment projects of Visual Studio .NET a little later in this chapter.
The Assembly Cache Viewer Shell Extension ( shfusion.dll ) is installed as a part of the .NET Framework. This extension allows you to view the complex structure of the GAC using Windows Explorer and allows administrators to install and uninstall assemblies using drag-and-drop and menu operations.
STEP BY STEP10.7 Adding an Assembly to the GAC by Using Windows Explorer
|
If you want to remove a file from the GAC, you just delete it from Windows Explorer by selecting File, Delete or by selecting Delete from the assembly's shortcut menu.
NOTE
The Assembly Cache Folder The assembly cache folder actually contains two caches: the GAC and the native image cache. When you view the assembly cache folder by using Windows Explorer, the Assembly Cache Viewer Shell Extension shows you a combined list of both caches. You can determine whether an assembly is from the GAC or from the native image cache by looking at the Type field in the list. When you add an assembly to the assembly cache folder by using Windows Explorer, it is added to the GAC. You will learn about the native image cache and will learn how to add assemblies to it later in this chapter.
You can also use the .NET Framework Configuration tool ( mscorcfg .msc ) to manage an assembly in the GAC. Step by Step 10.8 guides you through the process of adding an assembly to the GAC by using .NET Framework Configuration tool.
STEP BY STEP10.8 Adding an Assembly to the GAC by Using the .NET Framework Configuration Tool
|
To uninstall an assembly by using the .NET Framework Configuration tool, you just select Action, Delete or select Delete from the assembly's shortcut menu.
In addition to helping you add or remove assemblies, the .NET Framework Configuration tool helps you configure assemblies and manage their runtime security policies.
GacUtil.exe is a command-line tool that is especially useful for adding and removing assemblies from the GAC via a program script or a batch file. Step by Step 10.9 demonstrates the use of this tool.
STEP BY STEP10.9 Adding an Assembly to the GAC by Using the Global Assembly Cache Tool
|
You can list all the assemblies in the GAC by using the gacutil.exe tool with the /l option. You can use the /u option with the name of the assembly (without the file extension) to uninstall the assembly from the GAC:
gacutil /u RandomNumberGenerator
You can also choose to uninstall from the GAC an assembly of a specific version and specific culture by specifying its version, culture, and public key, along with the name of the assembly:
[View full width]gacutil /u RandomNumberGenerator,Version=1.0.0.0, Culture=neutral, PublicKeyToken=f26af4dbb33881b1
Normally, when you refer an assembly in a Visual Studio .NET project, you can invoke the Add Reference dialog box and browse to the desired assembly. But after you have added an assembly to the GAC, this approach does not work because the GAC has a complex structure that cannot be directly enumerated by the Add Reference dialog box.
When you view the GAC by using the tools mentioned in the preceding section, you see an abstraction of its structure. If you instead switch to the command prompt and change the directory to the GAC folder, you see that the GAC is actually made up of various subdirectoriesone for each assembly. Each of these directories has subdirectories, whose names depend on the assemblies' versions and public keys. Each subdirectory stores the actual assembly file, along with some additional assembly information. Figure 10.9 shows how the GAC entry is made for the RandomNumberGenerator component on my computer.
A good practice is to keep a copy of the assemblies installed in the GAC somewhere outside the GAC, where they are easily accessible via a pathname. You can then easily reference these assemblies through the Add Reference dialog box by browsing to the correct path . .NET adds the reference, but instead of using the copy that you browsed to, it uses the copy in the GAC. In fact, the .NET Framework uses the same techniques for all of its own assemblies stored in the GAC. The .NET Framework also stores copies of those assemblies in the folder where the .NET Framework is installed.
Although you can add a reference to an assembly by browsing to the folder where it is stored, the convenient way is to have its name directly displayed in the Add Reference dialog box so that you can just check to select it.
Step by Step 10.10 shows how to instruct Visual Studio .NET to add assemblies that are stored in a custom folder to the Add Reference dialog box.
STEP BY STEP10.10 Displaying an Assembly in the Add Reference Dialog Box
|
In Step by Step 10.11, you create a small Windows application that, when executed, loads the components installed in the GAC. Before you begin Step by Step 10.11, make sure that you have already installed the RandomNumberGenerator.dll file in the GAC.
STEP BY STEP10.11 Creating a Windows Application That Uses the RandomNumberGenerator Component
|
In Step by Step 10.11, you added a reference to the assembly stored in the bin folder of the RandomNumberGenerator project. However, the application loads the assembly from the GAC instead of its copy in the bin folder. Even when you move the executable file, the referenced assembly continues to load from the GAC. To understand this, you need to understand how the CLR locates assemblies; this is discussed in the following section.
For shared assemblies, the binding policy specifies a set of rules that instructs
Which directories the CLR should search for an assembly
Which version of an assembly the CLR should attempt to locate
When the CLR searches for a shared assembly, it goes through three stages of binding policy resolution, as shown in Figure 10.12.
At each of these stages, you can specify binding rules in XML-based configuration files. The three stages, in order, are
Application Policy Resolution At this stage, the CLR will look for an application configuration file for the binding rules. This configuration file can be used to specify additional search paths for an assembly. In addition, you can also use this file to redirect the CLR to a specific version of an assembly. The application-specific binding rules are usually set either by the application developer or by the administrator.
Publisher Policy Resolution The next stage after the application policy resolution is the publisher policy resolution. Publisher policy is set by the publisher of a shared assembly to distribute service-packlike updates to the customers. For example, take a scenario in which a customer installs version 1.0.0.0 of an assembly from a vendor. Later, the vendor realizes that there are certain errors with this version and releases a service pack to fix those errors. In this case, the publisher distributes a new version 1.1 of the assembly along with a publisher policy that redirects all the already installed applications to use the new version 1.1 of the assembly instead of the old version 1.0.
Publisher policy is specified using an XML file just like the application configuration file. But unlike the application configuration file, the publisher policy is complied into an assembly. In addition, the publisher policy only contains information about redirecting the CLR to a different version of the assembly.
By default, the binding rules specified in the publisher policy overrides the application policy. If however, you want an application to override the updates and continue using the existing versions, you can bypass publisher policy file by specifying the <publisherPolicy apply="no"/> XML element in the application configuration file.
Administrator Policy Resolution This is the final stage for applying the binding rules. The binding rules at this stage are specified by the administrator in the machine-wide configuration file named machine.config . This file is stored in the config subdirectory under the .NET Framework installation directory on a machine.
The settings specified in the machine configuration file override any settings specified in the application policy and the publisher policy.
The CLR uses the following steps to locate a shared assembly:
Determines the correct version of the assembly by examining the application configuration file, publisher policy file, and machine configuration file in the order mentioned in the previous section.
Checks whether the assembly is already loaded. If the requested assembly has been loaded in one of the previous calls, the CLR binds to the already loaded assembly and stops searching any further.
Checks the GAC. If the assembly is in the GAC, the CLR loads the assembly from there, binds to it, and stops searching any further.
If a < codebase > element is specified in the configuration files, the CLR locates the assembly by using the paths specified in the <codebase> element. If the CLR finds the assembly, the binding is successful; otherwise , the binding request fails and the CLR stops searching any further.
Follows the steps needed to probe for a private assembly as explained in the section "How the CLR Binds to a Privately Deployed Assembly" to locate the assembly.
Multiple versions of an assembly can exist in the GAC. Two common scenarios in which you might end up having multiple versions of an assembly are
A feature upgrade of an assembly is released.
A bug-fix service pack update of an assembly is released.
An application only binds to an assembly with the same identity with which the application was originally compiled. So if you release a new version of the assembly and remove the old one, existing applications will break because they will still be looking for an older version of the assembly.
Fortunately, the application, machine, and the publisher configuration files provide a mechanism to redirect the binding requests to a different version of the assembly without any need to recompile already deployed applications.
Consider a scenario in which you have an application named RandomNumberApplication that uses version 1.0 of a shared component named RandomNumberGenerator . Now, a new version 1.1 of the RandomNumberGenerator component is released. You install that new version in the GAC. Some of the applications on your server still use version 1.0 of the component. You want the already deployed RandomNumberApplication to start using version 1.1 of the component. You do not want to recompile the RandomNumberApplication and also do not want to affect the installation of other applications on the server.
The best policy in the preceding scenario is to modify the application configuration file of RandomNumberApplication to redirect any request from version 1.0 of RandomNumberGenerator to version 1.1 of that assembly. Modifying configuration files does not require the application to be recompiled, and if you modify the application configuration file, it only affects the application to which it belongs.
To demonstrate the scenario, Step By Step 10.12 installs version 1.1 of RandomNumberGenerator assembly in the GAC.
STEP BY STEP10.12 Installing Multiple Versions of an Assembly in the GAC
|
From Step By Step 10.12, you can see that the RandomNumberApp is still using the older version of the component because that's what is stored in its assembly manifest.
Step By Step 10.13 shows how you can modify the application configuration file for RandomNumberApp.exe to use version 1.1 of the RandomNumberGenerator .
STEP BY STEP10.13 Configuring Application-level Binding Policy for an Assembly Using the .NET Framework Configuration Tool
|
WARNING
No Validation The .NET Framework Configuration tool performs no validation to determine whether the specified new version of the assembly even exists, let alone whether it has the proper interface for the application.
In Step by Step 10.13, you used the .NET Framework configuration tool to create an application configuration file. You can also create the application configuration file manually, although in that case you need to know that you have to use the <assemblyBinding> element and its sub-elements to redirect the version numbers.
Note that, in the <bindingRedirect> element, the values for the attribute oldVersion and newVersion need not be an old version and a new version, respectively. You can, in fact, redirect an application using a newer version of an assembly to an older version also (provided, of course, that the changes are non-breaking).
When you use the application configuration file, you can only configure a single application. What if an administrator needs to configure all the applications on a machine that uses the old version of RandomNumberGenerator to use its new version? In this case, it would be tedious to configure an assembly for each application. Further, you would also need to take care that the new applications installed also bind to the new version of the component. To ease this, you can add the <assemblyBinding> element to the machine.config file. However, you need administrator privileges to edit the machine.config file. Step by Step 10.14 shows you how to configure an assembly for a machine using the .NET Framework Configuration tool.
STEP BY STEP10.14 Configuring Machine-level Binding Policy for an Assembly Using the .NET Framework Configuration Tool
|
The configuration performed in Step by Step 10.14 affects not only the RandomNumberApp application, but also, in fact, any application that uses the RandomNumberGenerator component on the given machine.
Consider another scenario in which you might end up having multiple versions of an assembly in the GAC. In this scenario, you are the company that publishes the RandomNumberGenerator component. You release version 1.0 of the component. Your customers develop applications such as RandomNumberApp that use the component. Then customers report that a bug is in the component that needs to be fixed at high priority. You plan to release a service pack of the component. The new version of the component is version 1.1. You want to deploy the component on the customer's computers in such a way that all existing applications that refer to version 1.0 of the RandomNumberGenerator component should now be redirected to version 1.1. You do not want extra configuration steps on the part of administrators on the customer's side.
The .NET Framework provides publisher policy for such a scenario. A publisher policy can be configured using the publisher policy configuration file, which uses a format similar to that of the application configuration files. But unlike the application configuration file, a publisher policy file needs to be compiled into an assembly and placed in the GAC.
The steps involved in creating a publisher policy are as follows:
Create a publisher policy file.
Use the assembly generation tool ( al.exe ) to convert the publisher policy file into a strongly named assembly. The name of the assembly should be of the format policy. majorNumber.minorNumber.AssemblyName.dll . Here, majorNumber and minorNumber are the version numbers of the existing assembly for which you want to redirect the binding requests. AssemblyName is the name of the assembly.
Install the publisher policy assembly to the GAC.
Step by Step 10.15 shows you how to create a publisher policy by using the .NET Framework Configuration tool.
STEP BY STEP10.15 Configuring a Publisher Policy for an Assembly
|
Binding redirections specified in machine.config override those specified in the publisher policy. The publisher policy overrides the setting specified in the application configuration file. But an application can choose to ignore the publisher policy altogether by specifying the following XML element in the application configuration file:
<publisherPolicy apply="no"/>
In Step by Step 10.6, when you signed an assembly, you used a key file that contains both the public and private keys for a company. As discussed earlier in the chapter, the private key ensures that the assembly is signed only by its advertised publisher. Thus, in most companies, the private key is stored securely, and only a few people have access to it.
If the key is highly protected, it might be difficult to frequently access the key when multiple developers of a company are building assemblies several times a day. To solve this problem, the .NET Framework uses the delay signing technique for assemblies.
When you use delay signing, you use only the public key to build an assembly. Associating public keys with an assembly allows you to place the assembly in the GAC and complete most of the development and testing tasks with the assembly. Later, when you are ready to package the assembly, an authorized person signs the assembly with the private key. Signing with the private key ensures that the CLR will provide tamper protection for the assembly. The following list summarizes the various steps involved with delay signing:
Extract a 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 use the Strong Name tool as follows:
sn.exe p 70310.snk 70310PublicKey.snk
At this stage, the 70310PublicKey.snk file can be freely distributed to the development team, and the 70310.snk file that contains both the private and public keys can be stored securely, possibly on a hardware device such as smart card.
Delay signing an assembly using Visual Studio .NET To use delay signing in a Visual Studio .NET project, you need to modify the following two attributes of the project's AssemblyInfo.vb file and build the assembly:
<Assembly: AssemblyDelaySign(true)> <Assembly: AssemblyKeyFile("70310PublicKey.snk")>
Turn off verification for an 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 RandomNumberGenerator.dll
If you execute this command, the GAC always skips the verification for this assembly in the future.
Sign a delay-signed assembly with the private key When you are ready to deploy a delay-signed assembly, you need to sign it with the company's private key:
sn.exe R RandomNumberGenerator.dll 70310.snk
Turn on verification for an assembly in the GAC Finally, you can instruct the GAC to turn on verification for an assembly by issuing the following command:
sn.exe Vu RandomNumberGenerator.dll
The Assembly Linker tool ( al.exe ) generates an assembly with an assembly manifest from the given modules or resource files. Remember that a module is a Microsoft Intermediate Language (MSIL) file without an assembly manifest.
While generating an assembly, you can also instruct the Assembly Linker tool to sign or delay sign an assembly with the given public/private key file. When you use al.exe for delay signing, you also use the arguments listed in Table 10.1.
Argument | Description |
---|---|
< sourcefiles > | You replace < sourcefiles > with the names of one or more complied modules that will be the parts of the resulting assembly. |
/delay[sign][+-] | You can use either the delay argument or the delay[sign] argument for delay signing. The option + is used to delay sign the assembly by storing just the public key manifest in the assembly manifest. The option is used to fully sign an assembly by using both public and private keys. If you do not use either + or , the default value of is assumed. |
/keyf[ile]:< filename > | You can use either keyf or keyfile to specify the key file. You replace < filename > with the name of the file that stores the key(s). |
/out:< filename > | You replace < filename > with the desired name of the output assembly file. |
Assume that you want to create an assembly by linking two modules, Sample1.netmodule and Sample2.netmodule . The public key file is SamplePublicKey.snk , and the desired output assembly is SignedSample.exe . You would use the al.exe command as follows:
[View full width]al.exe Sample1.netmodule,Sample2.netmodule/delaysign+ /keyfile:SamplePublicKey.snk/out: SignedSample.exe
REVIEW BREAK
|
Top |