Signing an Assembly in C#

Introduction

This article has been excerpted from book "The Complete Visual C# Programmer's Guide" from the Authors of C# Corner.

You can ensure an assembly's identity by signing it. This signing operation employs public-key infrastructure algorithms. Assembly manifests are hashed and signed with a private key. When any assembly is deployed, the hashed value is calculated and compared with the hashed value stored in the assembly. If the runtime hash value matches the hard-coded hash, the installation is allowed to continue.

Note. That the digital signature is found elsewhere in the metadata, in a section that is not included in the manifest. The digital signature of an assembly is just a secure hash of the manifest, which ensures that no one can modify the assembly contents after shipment. The CLR also provides support for embedding digital certificates, Authenticode, into assemblies.

You must use a private key to sign the assembly to create a strong-named assembly. (The strong name is a cryptographically strong identifier provided by the author or publisher of the assembly). The public key is by design incorporated into the assembly when it is signed, so your clients can verify the strong name. A .NET Framework SDK utility, the Strong Name tool (Sn.exe), is used to build strong-name key files.

Strong-name (SN) signatures and Authenticode signatures do not override each other; they coexist. You must be sure that the strong-name signing operation takes place before the Authenticode signing.

You can identify managed code by using Authenticode signatures with strong-name signing. You can consider using both if you already have a security trust structure that depends on Authenticode signatures. The Authenticode signature is stored in a special section of the assembly if an assembly is also signed by using Authenticode. Only the file containing the manifest should be signed when you use Authenticode for multifile assemblies.

Together, SN and Authenticode signatures are nearly impenetrable by hackers. Authenticode signatures warrant that a particular publisher signed an assembly. Authenticode signatures also guarantee that the publisher has not yet revoked the certificate that signed the assembly. However, strong names do not warrant that a signature has not been revoked. Strong names allow you to ensure that the contents of a given assembly have not been tampered with. Therefore, you can make sure that the assembly loaded at runtime is from the same publisher who coded and compiled your program.

For several reasons, the cost of verifying assemblies signed by Authenticode is significantly greater than the cost of verifying a strong-named assembly. Strong-name signing does not involve certificates, but Authenticode does. Similarly, strong-name signing does not depend on a certification hierarchy, but Authenticode does. Remember, an Authenticode signature will make your assembly invalid if you assign a strong-name signature after an Authenticode signature, but the converse is not true.

To summarize, digital signatures and public-key encryption are used for code signing and strong names. After the publisher or author assigns a strong name to an assembly, the strong-name information, which includes a unique public key to the assembly, is stored in the manifest. Each strong-named assembly is digitally signed with a private key corresponding to its public key. This digital signature can be verified by using the public key that is stored in the manifest. You can also further sign an assembly with Authenticode on demand.

Understanding Strong-Named Assemblies and Digital Signatures in .NET

  • You want to build a strong-named assembly and sign it. Each file in the assembly is hashed, and the hash values are stored in the manifest. Your public key is stored in the manifest. The manifest file is digitally signed with your private key, and this signature is stored in a nonhashed segment of the assembly.
  • You develop some code that references some other class of another strong-named assembly. The strong-named assembly's public key is read from the manifest and hashed to create a unique public-key token. This public-key token is a unique hash of the public key. This public-key token is stored, along with the version, simple name, and culture of the target assembly in the assembly references section (one entry for each external assembly referenced by the current module). The .NET Framework uses the assembly references section information at runtime to identify and locate the target assemblies.
  • You want to load or install a strong-named assembly. The framework verifies the strongname signature when you install the assembly in the GAC. If your assembly is not strongnamed, then the framework verifies the signature whenever the assembly is loaded. If the system cannot verify the signature, the assembly cannot be installed in the GAC or you cannot load it. At runtime, whenever the system loads a module of an assembly, first its contents are hashed, and then the system compares that resultant value with the hash value stored in the manifest. If both hashes do not match, the system will not load the module of the assembly.

When an application calls a method that is found in another assembly, the runtime tries to locate the assembly by examining binding policy information. The binding policy information is created at compile time and is stored in the application's metadata. Binding is the operation of mapping references to physical assemblies (typically dll's). The policy rules are used to determine how binding is done. An application will be bound to an assembly it was built and tested with, even if a newer version of the assembly is available. Default binding policy is always used unless it is explicitly overridden. Through the use of policy configuration files, binding policy can be altered without having to rebuild the assembly or the application. There are three policy resolution stages that the runtime may goes through before choosing which assembly to load. These stages, in order, are.

Application Policy for Assembly loading

  • Modifies binding rules for a single application.
  • Modified by Administrator.
  • Can be provided with the application.
  • Captured in XML configuration file in application directory in the format of assembly_file_name.file_extension.config.

Publisher Policy for Assembly loading

  • Compiled into an assembly.
  • Modifies binding rules for all applications using a specific assembly.
  • Provided by publisher as a statement about compatibility.
  • Captured in XML file wrapped as assembly that is signed by the publisher.
  • Deployable with msi (Microsoft Installation) packages.
  • Policy can be versioned.
  • Stored in the global assembly cache.
  • Found by naming convention.
  • Forces applications to use the latest version of an assembly.
  • Overrides application policy unless individual app refuses publisher policy.

Administrator Policy for Assembly loading

  • Modifies binding rules for all applications on the machine.
  • Modified by administrator.
  • Overrides all other policy on machine.
  • Mostly used security fixes to existing assemblies.
  • Captured in machine.config file in CONFIG subdirectory of the runtime install path, C:\Windows\Microsoft.net\Frameworks\<Version>\config.

Because the CLR cache is a vital part of the framework, we should delve a bit into the topic. The CLR cache consists of two parts.

  • Download cache: can be managed with al.exe (an assembly generation tool), Windows Installer, and Windows Explorer. Downloaded code is placed in a special download cache and is not globally available on the machine. Downloaded code does not affect existing assemblies because of the versioning mechanism.
  • Global assembly cache: can be managed with gacutil.exe (a global assembly cache management utility) and mscorcfg.msc (the .NET Framework Configuration tool). It is a local cache of the shared assemblies maintained by the .NET Framework for the locally installed applications.

From the command prompt in the same directory where MyComponent.dll resides, run gacutil.exe of .NET Framework SDK as shown below.

gacutil /i MyComponent.dll Command

With this command you have placed the assembly in the GAC; and that assembly is said to be a public, shared assembly. It can now be used from other assemblies on the server, regardless of their location on the computer.

The .NET Framework requires developers to specifically identify as partially trusted any strongnamed assembly and components that will be called by mobile code downloaded from the Internet or intranet and any code to which their security policy grants less than full trust.

Without the [assembly:AllowPartiallyTrustedCallers] attribute declaration (a custom attribute of the System.Security.AllowPartiallyTrustedCallersAttribute class) in the assembly configuration file, only fully trusted callers will be able to use the assembly. This attribute makes the assembly callable from any other partially or fully trusted assembly.

The AllowPartiallyTrustedCallers attribute removes the implicit LinkDemand (the permission check conducted on the caller) for the FullTrust permission set that is otherwise automatically placed on each assembly. Following are just some of the .NET Framework assemblies that have the AllowPartiallyTrustedCallers attribute declared.

  • System.dll
  • mscorlib.dll
  • System.XML.dll
  • System.Web.Services.dll
  • System.Data.dll
  • System.Windows.Forms.dll
  • System.Drawing.dll

Refer to the Microsoft .NET Web site for updates to the .NET security framework.

Also note that even with the AllowPartiallyTrustedCallers attribute enabled, assemblies cannot be called by partially trusted code if one of the following security attributes is declared in the code.

  • [PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
  • [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
  • [PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")]
  • [FileIOPermissionAttribute(SecurityAction.RequestMinimum, Unrestricted=true)]

Conclusion

Hope this article would have helped you in understanding Signing an Assembly in C#. See other articles on the website on .NET and C#.


Similar Articles