Public and Private Assemblies


An assembly can be either private (available only to the application that uses it) or public (available to all applications resident on the machine). To this point, all of the examples in this chapter have used private assemblies: They are simply shipped with the application program and stored in the application directory or one of its subdirectories. The application program generally refers only to the file name of the assembly and ships this assembly with the application.

The concept of a public assembly is probably familiar to traditional Windows programmers in the form of the system32 directory. Programs often install or upgrade critical DLLs in this directory, leading to obvious version conflicts and inevitable problems known as "DLL hell."

To support public assemblies, .NET manages a repository of assemblies, and runtime assembly resolution facilities perform all versioning, locale, and security management. In this way, applications can specify exactly which version (and possibly culture) assembly they wish to use; .NET will ensure that this exact version will always be used, even after the installation of new versions of the assembly. To support these features, .NET imposes some additional requirements on public assemblies (that is, assemblies found in the global repository), the main one being that the assembly must have a strong name (as discussed later in this section).

Before considering these public assemblies in depth, it is worth noting that in some circumstances you should try to avoid creating public assemblies that are stored in the repository. By keeping your assemblies private to your application's directory, you can install your application very simplythat is, by copying the files to a subdirectory on another computer and executing it, assuming certain security settings are compatible. With public assemblies, you will probably need to create an installation program that correctly installs your public assembly, allows for its uninstallation, and so forth. Use public assemblies only when you need explicit sharing of assemblies.

Strong Names

A strong name, which is also known as an assembly identity, consists of the assembly name, version and culture information, and a public key. [1] It is generated using a public/private key pair, ensuring that the strong name remains cryptographically secure. The strong name is stored in the assembly manifest, which itself contains the names and hashes of all files referenced in the assembly. Thus, the entire assembly is identified and secured by the assembly strong name.

[1] An optional Authenticode signature can be added, but it is not part of the strong name.

The purpose of the strong name is to ensure that the assembly is what the application thinks it is. The original developer of the assembly uses a public and private key pair to create a digital signature, which is stored inside the assembly itself. .NET can then use the public key to confirm that the assembly is exactly as provided by the developer; using this technique, it can be certain that each bit in each file in the assembly is exactly what the developer signed the assembly with.

Although this strategy guarantees the authenticity of the assembly, it does not address any trust issues. An optional Authenticode signature allows the user to determine whether the assembly has been signed by a trusted individual, possibly to allow some kind of trusted access to the computer on which it runs. This chapter does not discuss Authenticode signatures in any detail.

In a nutshell , a strong name allows .NET to provide the exact assembly the application desires, with absolute certainty . As such, it can bring about the death of DLL hell!

Assembly Caches

In version 1 of .NET, the assembly repository consists of two assembly caches managed by the system: the global assembly cache (GAC) and the download cache. Both caches are very similar in operation and purpose; both allow applications to share assemblies with absolute safety. They differ only in the way they are populated and managed.

Global Assembly Cache

The GAC is a systemwide cache of signed assemblies. Assemblies are placed into the GAC, and their identities (i.e., strong names) are registered with the system. Any application needing such an assembly can then access it from this cache. The GAC manages different versions and locations of assemblies and, by utilizing strong names, the application can be assured that the bits of the assembly used when building the application are the exact same bits used when the user runs the application.

Assemblies are generally placed in the GAC in one of two ways: by installation programs or manually using one of the GAC management tools provided with .NET. In general, application installers will increment a reference count for the assembly, thereby allowing the system to uninstall the assembly from the GAC as the last application is uninstalled .

Only fully signed assemblies can be placed in the cache. Because assemblies without strong names do not come with any identifying information about their originator or version, no guarantees can be made to applications about the integrity of such an assembly. Furthermore, assemblies added to the GAC can reference only other strongly named assemblies in the GAC. If a strongly named assembly itself references another assembly by a simple name, then that other assembly (and therefore the original strongly named assembly) becomes vulnerable to all the DLL conflict problems that strong names are designed to avoid. Optionally, signed assemblies can include an Authenticode signature that verifies the author's identity. Authenticode signatures are not required for an assembly to be installed into the GAC.

Download Cache

The download cache is intended specifically to hold "slow" contentthat is, any content hosted on a slow resource. For example, if an assembly is referenced from a Web page, a floppy disk, network, or CD resource, it will be stored in the download cache and be used for the application referencing it. Unlike with the GAC, content in the download cache always refers to the original location and may need to be refetched at any time. In contrast, once an assembly is installed in the GAC, the cache never implicitly drops it; consequently, the cache does not need to remember the original location from which the assembly was installed.

The download cache also stores each assembly on behalf of each individual application that references it. This strategy prevents conflicts arising from any other applications' attempts to use this same assembly.

Managing the Caches

The GAC can be managed either via installation programs, a GUI interface, or command-line tools. Of these techniques, only using installation programs is officially supported by .NET; these programs will always use reference counting and ensure that the lifetime of these assemblies is managed correctly. Using command-line tools or a GUI interface means that the assemblies will be installed without reference counting and can, therefore, be removed only explicitly (presumably by the same GUI interface or command-line tool). See the documentation for your installer tool for information on how assemblies are stored in the GAC.

During application development, however, the GUI and command-line tools are often your friends . On the GUI side of the world, Windows Explorer has an extension that allows you to use standard GUI operations on the cache. Opening the %windir%\assemblies directory using Windows Explorer, you will see a list containing the relevant strong name information, such as the assembly name and version (see Figure 5.8).

Figure 5.8. Global assembly cache as shown by Windows Explorer

graphics/05fig08.gif

As you can see in Figure 5.8, Windows Explorer shows much of the information used by the assemblies' strong names; the versions and public keys are shown (but none of the visible assemblies have a culture identifier specified). In addition, Figure 5.8 shows how multiple assemblies with the same name can be contained in the GACfor example, multiple versions of Microsoft's CalcR reside on this machine. There is also an example of a precompiled assembly in Figure 5.8, called CustomMarshalers . Later sections in this chapter dig a little deeper into this directory structure and look at the actual files found inside the cache.

As you would expect with a Windows Explorer extension, you can delete files from the cache or add them by using drag-and-drop or the standard cut-copy-paste Clipboard operations. A Microsoft Management Console (MMC) snap-in allows you to manage the GAC along with a host of other .NET options, which you can start by executing mscorcfg .msc .

For developing and building applications, the .NET Framework SDK provides a command-line tool that provides management capabilities for the GAC. This tool, called gacutil , allows you to install files into the GAC, dump the contents of the GAC, and add references to assemblies. It is particularly useful for developers who may need to install their built assemblies directly into the GAC for testing; indeed, the rest of the examples in this chapter will use this tool to install public assemblies into the GAC.

Example: Creating and Using Public Assemblies

Let's extend the progressive example to work as a public assembly. This step involves creating a strong name for the assembly and installing it into the GAC.

To create a strong name for the assembly, you must create a public and private key pair in a file, and then pass the name of this key file to the Assembly Linker. The Assembly Linker will then create a strong name for the assembly, allowing you to use gacutil to install the assembly in the GAC.

This modification requires changes only to the makefile . No changes to the C# sources are needed, but these items can be found in the AboutBoxGAC directory containing this chapter's examples. The significant changes to the makefile from the previous versions are as follows :

  • The strong name utility ( sn.exe ) is used to generate the key pair into ..\AboutBox.key . This key pair file is generated only once, and is used by later code in this chapter.

  • The key pair file is passed to the Assembly Linker via the / keyfile : parameter. In addition, the version portion of the strong name is filled with 1.0.0.0; later, you will see how side-by-side versioning works when this value is incremented.

  • The gacutil utility is used to install the assembly into the GAC.

  • When building the "clean" target, the key pair file is removed and the assembly is uninstalled from the GAC.

Listing 5.3 shows these changes.

Listing 5.3 Revised AboutBox makefile
 all: AboutBox.dll UseAboutBox.exe ..\AboutBox.key:     sn -k ..\AboutBox.key AboutBox.dll: AboutBox.cs message.txt ..\AboutBox.key     csc /t:module AboutBox.cs ..\AboutBoxBase.cs     al /v:1.0.0.0 /keyfile:..\AboutBox.key \        /link:message.txt /out:AboutBox.dll \        AboutBox.netmodule     gacutil /i AboutBox.dll UseAboutBox.exe: AboutBox.dll ..\UseAboutBox.cs     csc /t:winexe /r:AboutBox.dll /out:UseAboutBox.exe \         ..\UseAboutBox.cs clean:     -gacutil /u AboutBox,Version=1.0.0.0     -del *.exe     -del *.netmodule     -del *.dll     -del ..\AboutBox.key 

Even though this assembly has been built and installed into the GAC, it must still exist locally so that you can build the application that relies on the assembly. You must pass the name of the assembly on the command line of the application so that all references to the assembly can be correctly resolved.

To demonstrate that the correct assembly is used, the message.txt file is altered to contain only a single line:

 Public Assembly 

Now you can again build and test the new program. In the About Box dialog, the text matches the content of the new message.txt file.

To prove that this assembly is indeed public, you can run the executable in a directory without the built assembly. If you copy the resulting UseAboutBox.exe to another directory and attempt to execute it, you will find that it works correctly, as expected. If you had tried this step with any of the earlier programs, their use of private assemblies would have caused them to fail.

Strong Names and Security

Using this public assembly, let's check out some of the security offered by strong names. To demonstrate how the .NET use of strong names prevents a modified assembly from being used in place of the original assembly, a file will be modified and an attempt made to install it in the GAC. To try this, change the message.txt file and then attempt to reinsert the assembly in the GAC using the following command:

 gacutil /i AboutBox.dll 

The gacutil utility responds with the following message:

 Microsoft (R) .NET Global Assembly Cache Utility.  Version 1.0.3617.0  Copyright (C) Microsoft Corporation 1998-2001. All rights reserved. Failure adding assembly to the cache: One or more modules were streamed in which did not match those specified by the manifest.  Invalid file hash in the manifest? 

Thus, once an assembly has been signed, it cannot be modified in any way. Applications can therefore rest assured that the version they have is exactly what the original developer provided.

Note that modifying message.txt prevented reinstallation in this example, as the text file is linked (rather than embedded) in the assembly; thus message.txt is part of the assembly. If you had embedded the file in the assembly, you could freely modify the text file, as its contents would have already been embedded in the main assembly DLL.

If you are so inclined, you can get your hands very dirty and experiment directly with the GAC to see what happens if you modify the message.txt file after installing the assembly.

WARNING

The following exercise is not supported and would be severely frowned upon by Microsoft. It is not guaranteed to work in future versions, and it may harm dolphins or cause your house or office to spontaneously explode. Use at your own risk.


Ensure that you have administrator privileges for the machine you are using, and perform the following steps:

  1. Switch to the GAC directory.

  2. Recursively search for any files called message.txt in the tree.

  3. Edit and save the file.

  4. Rerun this example's application.

Following is a command-prompt session that performs these steps:

 C:\WINDOWS\Assembly\GAC> dir message.txt /s  ... Directory of C:\WINDOWS\Assembly\GAC\AboutBox.0.0.0__a0df1539bc79861c 24/01/2002  02:58p                  19 message.txt               1 File(s)             19 bytes ... C:\WINDOWS\Assembly\GAC> notepad AboutBox.0.0.0__a0df1539bc79861c\message.txt 

If you now execute the application, you do indeed see the new text in the dialog. By default, .NET does not attempt to verify the contents of an assembly each time it is loaded from the GAC, but only at GAC installation time. It stores the GAC under the Windows directory for exactly that reason; once a file appears in the GAC, .NET relies on Windows' built-in security to keep it safe (hence the requirement for administrator privileges). This approach allows .NET to avoid the time-consuming strong name validation for each assembly load.

Delayed Signing of Assemblies

In the interests of securing private keys, .NET supports the concept of delayed signing of assemblies. With delayed signing, the assembly creation step sets up the output file with all the structures needed to be a signed assembly, but the actual signing of the assembly takes place in a later, separate step.

The key advantage of this concept is that the private key used to sign your assemblies need not be distributed to all developers or installed on your build machines. Rather, the actual signing step can occur on a separate secure machine that need not have all the development tools required to build the application.

The Assembly Linker ( al.exe ) tool supports a /delaysign option that is used when the assembly is first created. The strong name tool ( sn.exe ) is then used to finalize the signed assembly.



Programming in the .NET Environment
Programming in the .NET Environment
ISBN: 0201770180
EAN: 2147483647
Year: 2002
Pages: 146

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net