Assemblies and its version policy.


  1. Introduction Assembly Types
  2. The Global Assembly Cache
  3. Configuration Files

Assembly PropertiesIntroduction

The NET Framework and  Framework Class Library are an perfect example of globally deployed assemblies as they are most widely used assemblies by multiple application and .NET software vendors. The applications are built and tested using code implemented by third party vendors and Microsoft using a particular version of the libraries. These third party libraries and NET Frameworks  are also modified and updated using service packs and hotfixes to incorporate the feature enhancements and bug fixes. Now the applications are forced to use the newer version of assemblies. The .NET framework follows certain policy which supports backward compatibility which helps the older existing application to execute.

The third party libraries also are updated and modified which are sometimes are not backward compatible which makes the existing application bit unstable. The reason of instability of existing applications is due to those apps were tuned to work with the old code which had old features and bugs.

So there must be a process to deploy new files with the hope that the applications will work properly. And if the application doesn’t work fine there has to be an easy way to restore the application to its last known good state.

The similarities and differences in privately deployed week assemblies and global deployed strong named assemblies

There are two kinds of assemblies weakly named assemblies and strongly named assemblies. Both the types of assemblies are structurally identical, i.e. they use the same portable executable (PE) file format, PE32(+)header, CLR header, metadata, manifest tables, and intermediate language. Same tools or utilities are used for generating the assemblies.

The real difference is the strongly named assemblies is that a strongly named assembly is signed with a publisher’s public/private key pair that uniquely identifies the assembly’s publisher. The key pair allows the assembly to be uniquely identified, secured and versioned, it allows the assembly to be deployed anywhere on the user’s machine or even on the internet. Because of this uniquely identifiable assembly name the CLR can implement using the safe publishing policy for deployment.

A strongly named assembly is signed with a publisher’s public/private key pair. This key pair allows the assembly to have unique ID, secured and versioned, and it allows the assembly to be deployed anywhere. An assembly can be deployed in two ways privately under application’s base directory or one of its subdirectories and secondly it is deployed globally into a well known location called GAC which CLR looks into whenever any strongly named assembly is referenced.

The table below gives a brief idea about deployment of strongly named assemblies and weakly named assemblies.

Kind of Assembly               Private deployment                                       Global deployment

Weakly named                            Yes                                                                       No

Strongly named                           Yes                                                                      Yes

There are few problems faced by developers while deploying the assemblies namely, Two companies could produce assemblies that have the same file name and if both of these files are copied to the same location where shared assemblies are kept. the most recently copied file overwrites the old file, and all the applications that were using the old assembly no longer predictable. This is similar to “DLL Hell” in COM world.

components of  strongly named assemblies

The CLR needs technology that helps assemblies to be uniquely identified. This is technique is known as strongly named assembly. The strongly named assembly consists of four attributes that uniquely identify the assembly : file name, a version number, a culture identity, and a public key. This hash value is called a public key token. The following assembly identity strings identify four completely different assembly files:

“MyAppln, Version=1.0.1123.0, Culture=neutral, PublicKeyToken=23asdfkajlkasdf”

“MyAppln, Version=1.0.1123.0, Culture=”en-US”, PublicKeyToken=23asdfkajlkasdf”

“MyAppln, Version=1.0.1234.0, Culture=neutral, PublicKeyToken=bb78343awsdfgs”

“MyAppln, Version=1.0.1123.0, Culture=neutral, PublicKeyToken=465765sdfgsdss”

The first component identifies an assembly file called “MyAppln”.

The second component informs developer is creating a version of 1.0.1123.0.

The third component identifies locale or culture is neutral.

The fourth component identified as public key token generated using public/private key pair.

Why Microsoft used cryptographic API’s for strongly named assemblies?

Microsoft has used cryptographic API and standard technology of public/private key to mark the assembly’s uniqueness, cryptography technologies help the user to verify the integrity of the assembly contents for each every machine, they can also be used for setting the privileges and permission to be granted on per user or publisher basis. Also care should be taken that not a single company shares their private key to be used for generating strongly named assembly.

The System.Reflection.AssemblyName class is a utility class that offers several public instance such as CultureInfo, FullName, KeyPair, Name and Version. The utility class offers a few public instance methods such as GetPublicKey, GetPublicKeyToken, SetPublicKey and SetPublicKeyToken.

A weakly named assembly can have assembly version and culture. The CLR always ignores the version numbers as they are always privately deployed, the CLR simply uses the name of the assembly when looking for the assembly’s file in the application base directory and its sub directories or If a different path is mentioned in the XML configuration file’s probing element’s “privatepath” XML attribute.

The strong named assembly is signed using the public/private key. The following is the steps involved in signing of assembly to make it a strongly named assembly

1. Run SN.exe to generate private/public key pair

SN –k MyCompany.snk

2. To view the public key and store it in a file called MyCompany.PublicKey

SN –p MyCompany.snk MyCompany.Publickey

3. Now execute SN.exe, passing it the –tp switch and the file contains just the public key

SN –tp MyCompany.PublicKey

When I execute this command, I get the following

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.20928.1

Copyright (c) Microsoft Corporation.  All rights reserved.

Public key is

00240003048000009404000006020000002400055253413100040000710001000f507849113404

5955a3f8fdc1bd0d29cba6357026e7caf1631831c64d71fc09051a29444d1d5b4199331d6a1c9d

883de7837dd553b26f82d9bfacad5e405a286fad65cd3e2a890925314e6d34dd3102448cd8a7c8

f16cd1b39b180b34985faa799f3d21e5c81f86467b5f02f451cc3473858d1e7bef63ee39440edf

b64ef8a8

Public key token is 74786c738e63f883

The SN.exe utility doesn’t offer any options for you to display the private key.

A public key token is a 64-bit hash of the public key, to help developers public key tokens were created to avoid using long size of public key. These public key tokens are stored in an assemblyRef table. These reduced tokens are created to conserve storage space.

The command line option or switch of C# compiler to use keyfile that holds the public key is as follows:

csc /keyfile:MyCompany.snk MyApp.cs

The compiler opens the specified file signs the assembly with the private key, and embeds the public key in the manifest. Note that the other files in assembly are not signed only manifest containing assembly is signed.

The Visual studio private/public key is created by navigating to project properties tab, clicking on the signing tab, selecting the Sign assembly check box and then clicking on the <New > option form the Choose A strong Name Key file combo box.

As each file’s name is added to the manifest the file’s contents are hashed and this hash value is stored along with the the file’s name in the FileDef table. You can override the default hash algorithm used with AL.exe’s /algid switch or apply the assembly-level System.Reflection.AssemblyAlgorithmIdAttribute custom attribute in one of the assembly’s source code files. By default, a SHA-1 algorithm is used.

After the PE file containing the manifest is built and its entire contents are hashed. The hash algorithm used here is always SHA-1 and can’t be overridden. This hash value is signed with the publisher’s private key and the resulting RSA digital signature is stored in a reserved section within the PE file. The CLR header of the PE file is updated to reflect where the digital signature is embedded within the file.

The publisher’s private key is used for signing assembly and public key is also embedded into the AssemblyDef manifest metadata table in the PE file. The combination of the file name, the assembly version, the culture and the public key gives this assembly a strong name which is guaranteed to be unique and thus duplication is avoided.

The reference assemblies that are required by your assembly need to be specified using the /reference compiler switch, this will instruct the compiler to emit an assemblyRef metadata table indicates the referenced assembly’s name, version number, culture, and public key information.

Example of AssemblyRef metadata Table and AssemblyDef metadata table

The example of AssemblyRef is shown below:

AssemblyRef #2 ——————————————————-

Token: 0x23000002

Public Key or Token: ef 41 b5 08 ea 1c fb 8b

Name: multifile

Major Version: 0x00000001

Minor Version: 0x00000002

Build Number: 0x00000003

Revision Number: 0x00000004

Locale: <null>

HashValue Blob:

Flags : [none] (00000000)

The example of AssemblyDef metadata table is shown below

// Assembly

// ——————————————————-

// Token: 0x20000001

// Name : hello

// Public Key :

// Hash Algorithm : 0x00008004

// Version: 0.0.0.0

// Major Version: 0x00000000

// Minor Version: 0x00000000

// Build Number: 0x00000000

// Revision Number: 0x00000000

// Locale: <null>

// Flags : [none] (00000000)

// CustomAttribute #1 (0c000002)

// ——————————————————-

// CustomAttribute Type: 0a00001f

// CustomAttributeName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute :: instance void .ctor(int32)

// Length: 8

// Value : 01 00 08 00 00 00 00 00 > <

// ctor args: (8)

//

// CustomAttribute #2 (0c000003)

// ——————————————————-

// CustomAttribute Type: 0a000020

// CustomAttributeName:System.Runtime.CompilerServices.RuntimeCompatibilityAttribute ::instance void .ctor()

// Length: 30

// Value : 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 > T WrapNonEx<

// : 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 >ExceptionThrows <

// ctor args: ()

//

The Global Assembly Cache

The assembly must be placed into a well known directory and the CLR must know to search in this directory automatically when a reference to the assembly is detected. This well-known location is called the global assembly cache which can usually be found in the following directory.

C:\Windows\Assembly

The GAC directory is structured: it contains many subdirectories and an algorithm is used to generate the names of these subdirectories. You should never manually copy assembly files into GAC instead you need to install the assemblies into GAC using the tools which knows the internal structure and how to generate the proper subdirectory names. The most common tool for installing a strongly name assembly into the GAC is GACUtil.exe

Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Usage: GACUtil <command> [ <options> ]
Commands:
/i <assembly_path> [ /r <…> ] [ /f ]
Installs an assembly to the global assembly cache.

/il <assembly_path_list_file> [ /r <…> ] [ /f ]
Installs one or more assemblies to the global assembly cache.

/u <assembly_display_name> [ /r <…> ]
Uninstalls an assembly from the global assembly cache.

/ul <assembly_display_name_list_file> [ /r <…> ]
Uninstalls one or more assemblies from the global assembly cache.

/l [ <assembly_name> ]
List the global assembly cache filtered by <assembly_name>

/lr [ <assembly_name> ]
List the global assembly cache with all traced references.

/cdl
Deletes the contents of the download cache

/ldl
Lists the contents of the download cache

/?
Displays a detailed help screen

Options:
/r <reference_scheme> <reference_id> <description>
Specifies a traced reference to install (/i, /il) or uninstall (/u, /ul).

/f
Forces reinstall of an assembly.

/nologo
Suppresses display of the logo banner

/silent
Suppresses display of all output

The GACUtil used for installing assembly to GAC we use /I switch but for properly deployment one should use /r switch in addition to specifying the /I or /u switch to install or uninstall the assembly. The /r switch integrates the assembly with the Windows install and uninstall engine. GACUtil instructs which are the application are using /sharing the assembly and then ties the application and the assembly together.

The GACUtil tool is not shipped with .NET Redistributable package. If your application includes some assemblies that you want to deployed into GAC, you should use the Windows Installer (MSI), because MSI is the only tool that is guaranteed to be on the end-user machines and capable of installing assemblies into the GAC.

Whenever an assembly is created it has to refer the other assemblies for success compilation, the reference switch provides the name of the referred assemblies. If the file name is a full path, CSC.exe loads the specified assemblies and uses its metadata information to build the assembly. if you specify a file name without a path, CSC.exe attempts to find the assembly by looking in the following directories

1. Working directory

2. The directory that contains the CSC.exe file itself. This directory also contains the CLR DLLs.

3. Any directories specified using the /lib compiler switch.

4. Any directories specified using the LIB environment variable.

Even though GAC is the directory where the assembly is found at a compile time, this isn’t the directory where the assembly will be loaded from at runtime, when you install the .NET framework, two copies of Microsoft’s assembly files are actually installed. One set is installed into the compiler/CLR directory and another set is installed into a GAC subdirectory. The files in the compiler/CLR directory exist so that you can easily build your assembly, whereas the copies in the GAC exist so that they can be loaded at runtime for execution.

The reason that CSC.exe doesn’t look in the GAC for referenced assemblies is that you did have to know the path to the assembly file and the structure of the GAC which is undocumented

When assembly is hashed i.e. signed using the private key, the system would have hashed the contents of the file containing the manifest and compares the hash value with the RSA digital signature value embedded within the PE file. If the values are identical, the file contents haven’t been tampered with, and also the public key that corresponds to the publisher’s private key. The system hashes the contents of the assembly’s other files and compares the hash value don’t match at least one of the assembly’s files has been tampered with, and the assembly will fail to install into the GAC.

The CLR loads the referenced global assembly from the GAC using the strong name properties. If the referenced assembly is available in the GAC, CLR will return its containing subdirectory and  the file holding the manifest is loaded. Finding the assembly this way assures the caller that the assembly loaded at runtime came from the same publisher that built the assembly the code was compiled against. Now comparison of public key token in the referencing assembly’s assemblyRef table and public key token in the referenced assembly’s AssemblyDef table. If the referenced assembly isn’t in the GAC, the CLR looks in the application’s base directory and then in the private paths identified in the application’s configuration file; if the application containing the assembly is installed using the MSI, then CLR invokes MSI to load the required assembly. IF the assembly is not found in any of these location, an exception is thrown and finally the binding of assembly fails.

Assembly Hashing

A hashing of the file is performed every time an application executes and loads the assembly. This performance hit is a tradeoff for being certain that the assembly file’s content hasn’t been tampered with. When the CLR detects mismatched hash values at runtime, it throws System.IO.FileLoadException.

When you are ready to package your strongly named assembly you’ll have to use the secure private key to sign it. However, while developing and testing the assembly, gaining access to the secure private key can be a huge problem. Due to this .NET provides a technique known as delayed signing a.k.a partial signing. Delayed signing allows the user to build an assembly by using the user’s public key, the private key isn’t required.

The delayed signing is set on the C# compiler using /delaysign compiler switch. In Visual studio open the project properties of your project, navigate to Signing tab, and then select the Delay Sign Only check box. If you are using AL.exe you can specify the /delay[sign] command-line switch.

For avoiding or preventing of verification of integrity of the assembly’s files. you have to set the –Vr command-line switch of SN.exe utility, Executing the SN.exe with this switch is tells the CLR to skip verifying the hash values for any of the assembly files loaded at runtime. SN’s –Vr switch adds the assembly’s strong name in registry under the follow subkey: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification.

The –r switch of SN utility is used along with the name of the file that contains the actual private key to hash it, sign it file contents of the assembly and then embed the RSA digital signature in the file where the space for it had been previously reserved. After this step you can deploy the fully signed assembly.

The Cryptographic service providers offer containers that abstract the location of these keys. Microsoft uses a CSP that has a container that, when accessed, obtains the private key from a hardware device. If public and private key pair is in a CSP we have to specify different switches to the CSC.exe, AL.exe, and SN.exe programs: When compiling specify the /keycontainer switch; when linking using AL.exe specify /keyname and when using the strong Name SN tool specify –Rc to add a private key to delayed signed assembly. SN offers many more switches for performing operations with CSP.

Delayed signing is useful whenever you want to perform some other operation to an assembly before you package it. For e.g. you may want to obfuscate your assembly, you cannot obfuscate after you have fully signed because the hash value will be incorrect. So, if you want to obfuscate an assembly file or perform any other type of post build operations, you should use delayed signing, performing the post-build operations, and then run SN.exe with –R or –Rc switch to complete the signing process of the assembly with all of its hashing.

Deploying privately preserves the simple copy install deployment story and better isolates the application and its assemblies. Also GAC isn’t intended to be new dumping ground for assemblies. The reason is because new versions of assemblies don’t overwrite each other, they are installed side by side eating up disk space.

Another alternative way of deploying the assemblies is to use XML configuration files which have the shared assembly’s codeBase element indicate the path of the shared assembly. Now at runtime, the CLR will know to look in the strongly named assembly’s directory for the shared assemblies.This technique is rarely used since any one of application sharing the assembly is uninstalled then there is chance that these shared assemblies might be uninstalled.

When the source code is compiled to create an executable, this executable is executed the CLR loads the assemblies and initialization takes place.i.e. CLR reads the assembly’s CLR header, looking for the MethodDefToken. that identifies the application’s entry point method(Main). From the MethodDef metadata table, the offset within the file for the method’s IL Code is located and JIT-compiled into native code, which includes having the code verified for type safety. The native code then starts executing.

When JIT-compiling this code, the CLR detects all references to types and  members and loads their defining assemblies. At this point, the CLR knows which assembly it needs. Now the CLR must locate the assembly in order to load it. When resolving a referenced type, the CLR can find the type in one of three places;

1. Same file: Access to a type that is in the same file is determined at compile time. The type is loaded out of the file directly and execution starts.

2. Type is in Different file but in same assembly.

3. Type is in Different file and in different assembly

If any errors occur while resolving a type reference –file can’t found, file can’t be loaded, hash mismatch, version mismatch and so on – an appropriate exception is thrown. The CLR then creates its internal data structure to represent the type, and the JIT compiler successful completes the compilation of the main method. finally application starts executing.

Flow chart of Type binding by CLR during compilation.

Type Binding

The GAC identifies assemblies using name, version, culture, public key, and CPU architecture. When searching the GAC for an assembly, the CLR figures out what type of process the application is currently running in 32-bit x86 on top of WOW64 technology, 64-bit x64, 64-bit IA 64. Then when searching the GAC for an assembly, the CLR first searches for a CPU  architecture-specific version of the assembly. If it does not find a matching assembly, it then searches version for a CPU-agnostic version of the assembly.

Configuration Files

Configuration files are XML files that can be changed as needed. Configuration Files are standard XML files. The .NET Framework defines a set of elements that implement configuration settings. Developers can use configuration files to change settings without recompiling applications. Administrators can use configuration files to set policies that affect how applications run on their computers.

  • <configuration> Element
    Describes the <configuration> element, which is the top-level element for all configuration files.
  • <assemblyBinding> Element for <configuration>
    Specifies assembly binding policy at the configuration level.
  • <linkedConfiguration> Element
    Specifies a configuration file to include.
  • Startup Settings Schema
    Describes the elements that specify which version of the common language runtime to use.
  • Runtime Settings Schema
    Describes the elements that configure assembly binding and runtime behavior.
  • Network Settings Schema
    Describes the elements that specify how the .NET Framework connects to the Internet.
  • Cryptography Settings Schema
    Describes elements that map friendly algorithm names to classes that implement cryptography algorithms.
  • Configuration Sections Schema
    Describes the elements used to create and use configuration sections for custom settings.
  • Trace and Debug Settings Schema
    Describes the elements that specify trace switches and listeners.
  • Compiler and Language Provider Settings Schema
    Describes the elements that specify compiler configuration for available language providers.
  • Application Settings Schema
    Describes the elements that enable a Windows Forms or ASP.NET application to store and retrieve application-scoped and user-scoped settings.
  • Web Settings Schema
    All elements in the Web settings schema, which includes elements for configuring how ASP.NET works with a host application such as IIS. Used in aspnet.config files.
  • Example of Publisher’s policy

    The schema for publisher policy  is as follows

    <configSections>
    <clear>
    <remove>
    <section>
    <sectionGroup>
    <section>
    <appSettings>
    <Custom element for configuration section>
    <Custom element for configuration section>
    <add>
    <remove>
    <clear>

    Example of application Configuration file is shown below

    <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

    <dependentAssembly> <assemblyIdentity name="myAssembly" publicKeyToken="32ab4ba45e0a69a1" culture="en-us" />

    <!– Assembly version can be redirected in application, publisher policy or m/c configuration files –>

    <bindingRedirect oldVersion="3.0.0.0" newVersion="3.0.1.1" />

    </dependentAssembly>

    <dependentAssembly>

    <assemblyIdentity name="mySecondAssembly" publicKeyToken="1f2e54s865swqcds" culture="en-us" />

    <!– Publisher policy can be set only in the application configuration file. –> <publisherPolicy apply="no" />

    </dependentAssembly>

    </assemblyBinding>

    </runtime>

    </configuration>

    When JIT-compilation process CLR looks up the assembly version in the application configuration file and applies any version number redirections; the CLR is now looking for this assembly/version.

    For e.g.

    <assemblyBinding xmlns="…" appliesTo="v1.0.3705">

    <!—.NET Framework version 1.0 redirects here –>

    </assemblyBinding>

    <assemblyBinding xmlns="…" appliesTo="v1.1.5000">

    <!—.NET Framework version 1.1 redirects here –>

    </assemblyBinding>

    If publisher’s policy elements apply attribute is set to yes, the CLR examines the GAC for the new assembly/version  and applies any version number redirections in the machine.config file and applies any version number redirections there. At this point CLR knows the version and attempts to load the assembly from the GAC, if the assembly isn’t in the GAC and if there is no codeBase element, the CLR checks for assembly in the app base directory. If the codebase element is there the CLR attempts to load the assembly from the codeBase element’s specified URL.

    When you package your new assembly to send out to all of your users, a XML configuration file is created. So that publisher can set policies only for the assemblies that they themselves create. In addition, the elements shown here are the only elements that can be specified in a publisher policy configuration file. Now publisher can create an assembly that contains this publisher policy configuration file.

    AL.exe /out: Policy.1.0.MyAppln.dll

    / version: 1.0.0.0

    / keyfile: MyCompany.snk

    / linkresource: Myapps.config

    /platform:x86

    In this command:

  • The Myapps.config argument is the name of the publisher policy file.
  • The Policy.1.0.MyAppln.dll argument is the name of the publisher policy assembly that results from this command. The assembly file name must follow the format:policy. majorNumber . minorNumber . mainAssemblyName .dll
  • The MyCompany.snk argument is the name of the file containing the key pair. You must sign the assembly and publisher policy assembly with the same key pair.
  • The x86 argument identifies the platform targeted by a processor-specific assembly. It can be amd64, ia64, msil, or x86.
  • Once the publisher policy assembly is built and distributed. It has to be deployed into the GAC.

    The following command adds policy.1.0.myAssembly.dll to the global assembly cache.

    gacutil /i publisherPolicyAssemblyFile

    for e.g. gacutil /I Policy.1.0.MyAppln.dll

    Finally to have the runtime do this, the administrator can edit the application configuration file and add the following publisher policy element

    <publisherPolicy apply =”no”>

    This element can be placed as a child element of <assemblybinding> XML tag/element in the application config file so that it applies to all the assemblies or if you need to apply it a specific assembly you need to specify it as a child of <dependentAssembly>

    Digg This
    Advertisements

    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s