Strong Names and Signing Assemblies .NET




- April 20, 2015

Rest of the Story:

Digital signatures are used to verify the integrity of data being passed from the originator (the signer) to a recipient (the verifier). The signatures are generated and verified using public key cryptography.

The signer of a message has a pair of cryptographic keys: a public key, which everyone in the world knows, and a private key, which is kept secret by the signer.

The verifier knows only the public key, which is used to verify that the signer knew the private key and the message.
Strong Named Assemblies

  • Strong names offer a powerful mechanism for giving .NET Framework assemblies unique identities.

  • The strong name for an assembly consists of five parts:

  • a public key (RSA cryptographic public key that helps verify the assembly's authenticity)

  • a simple name (text string—usually the name of the file (without the extension) that contains the assembly)

  • a version (four-part version number, in the form of Major.Minor.Build.Revision (i.e.  1.0.0.1)

  • an optional culture (Target audience for the assembly, such as "neutral" (default audience), "en-us" (English – United States) or "fr" (France) etc.)

  • and an optional processor architecture (Defines the assembly's format, such as MSIL (intermediate language) or x86 (binary for Intel x86 processors)

  • An assembly is strong-named during the build process using the private key which corresponds to the public key in the strong name.  The strong name can be verified using the public key.

  • The .NET Framework verifies the strong name signature.  If it cannot be verified the Framework will not load the assembly.

  • The above has one exception.  Strong named assemblies that are used in the GAC (Global Assembly Cache) are not verified each time the .NET Framework loads them.  The assemblies in the GAC are verified only at the time they are installed in the GAC as the GAC already is locked down and assemblies can only be installed by an individual who has admin permissions.  Once an assembly was installed in the GAC, it's considered to be safe and being verified earlier (during gacutil.exe phase)

  • Why use strong names…Strong names prevent others from spoofing your code.  i.e. a malicious user cannot modify your code and successfully re-sign it.  Strong names are required to store shared assemblies in the global assembly cache (GAC). This is because the GAC allows multiple versions of the same assembly to reside on your system simultaneously, so that each application can find and use its own version of your assembly. This helps avoid DLL Hell, where applications that may be compiled to different versions of your assembly could potentially break because they are all forced to use the same version of your assembly.

  • Strong name signatures do not contain any reliable information about the publisher.  In order to trust keys from other organizations ensure you have a secure channel to get their public key.  Remember that through the public key the strong named assembly can be verified that it was signed with the corresponding private key.  Hence strong names are secure only when the strong name private key is kept secure.

  • Strong-name signing is a good idea for most applications, especially those that are deployed over a network or any medium not fully controlled by the deployer.

  • All assemblies referenced by a strong-named assembly must also be strong-named. If you reference an assembly written by a third party that is not strong-name signed, you cannot strong-name sign your assembly.

  • Strong-name signing makes servicing more complicated. Under current versioning policy, an assembly will always attempt to load the exact version of the assembly it was built against. If, for example, your application was built against version 1.0.0.0 of a strong-named assembly and you fix a bug in the assembly, bumping the version number to 1.0.0.1, the existing application will not find the updated assembly.

There are a few ways to deal with this situation. You can rebuild the application against the new assembly. Obviously, this is an annoying process for just picking up a bug fix and it may not be an option in some situations. Still, it works fine when your code is not widely deployed or the assembly being serviced is not widely shared across applications.
Another option is if the assembly is installed in the GAC, you can use publisher policy to redirect loads for version 1.0.0.0 to version 1.0.0.1. However, publisher policy is complicated.
Yet another option is to fix the bug and not change the assembly version number, so existing applications can still find the assembly. This is the approach the .NET Framework uses for bug fixes, although it’s not appropriate for new features or anything that breaks compatibility. If you are adding new features, you should bump the assembly version up and have applications rebuild to opt in to the new features.

  • Assemblies loaded in the GAC perform better as they are loaded from central system space and the verification process has already been completed.  The strong name ensures correct component versioning helping to prevent components with the same name from conflicting with each other.

  • To install assemblies in the GAC they must be strong named(signed).

  • Delay signing allows you to generate a partial signature during development with access only to the public key. 

  • The private key can be stored securely out of the hands of the developers and used to apply the final strong name signature just before shipping your code. 

  • Use the public key to delay sign your assemblies during development.

  • Because the assemblies are not fully signed yet, you’ll also have to configure your development machines to skip strong name signature verification for your key—otherwise, the .NET Framework will not allow you to load the delay-signed assemblies.  You must configure the .NET Framework to skip verification for the delay-signed assemblies using your public key. To do this, you use the sn.exe tool again. i.e. sn –Vr {assemblyName} 

Another options…to configure your development machine to skip all verification for delay signed assemblies signed with a particular key.  Using sn –T {assemblyName} will give you the public key token.  Then execute the following to skip strong name verification for any assembly using that public key token sn – Vr *,{public token}  Any assembly delay signed with that public key will now skip strong name signature verification and run on your development machine.  It should never be done on production computers because it opens up those machines to assembly spoofing attacks. 

Another technique to acquire the public key token is to use the public key file alone. i.e. sn – tp {publickeyfile}

  • When you are ready to ship your code, use the (well guarded) private key to apply the final strong name signature to your delay-signed assemblies.  sn –R {assemblyName} {your key file}  Now your assembly has a full signature.  It is possible to use command line “For” command to iterate over each dll saving a lot of typing

i.e. for {%variable|%%variable} in (set) do command [ CommandLineOptions] for %f in (*.dll) do sn –R %f MyKeyFile.snk

if you use this within a batch file replace %f with %ff

Additional Notes:

  • Create a strong name key (often your organization will already have one established)

i.e. sn –k MyKeyFile.snk  This creates a strong name key pair (both public and private keys in the same file)

  • Developers typically only need the public key portion.  sn –p MyKeyFile.snk MyKeyFilePublic.snk  This extracts the public key and creates a new file MyKeyFilePublic.snk.  At this point, MyKeyFile.snk should be put in a save location and available to Administrators only
  • To access the public key value and token sn – tp MyKeyFIlePublic.snk.  At this point, store the output in a text file somewhere.  We will use both the public key value and token.