In this post I’ll explain why you might choose Strong Naming and/or Digital Signatures for your .NET assemblies. In my next post I describe how you can Strong Name your .NET assemblies using Team Build without sharing your private key with developers.

I’ve distilled some of the key points from these resources:

Strong Names

A strong name helps to provide a unique identity for an assembly.

Strong names are secure only when the strong name private key is kept secret.

Don’t have any revocation method:

  • If the private key is compromised, no assemblies signed with that key can be trusted anymore.
  • This means re-sign & re-deploy with a new key.

Don’t offer publisher verification:

  • There’s no way to map a particular public key to a particular publisher.
  • Just because all Microsoft.* dll’s are shipped with “PublicKeyToken=b03f5f7f11d50a3a” - There is nothing in the strong name key that indicated that it was in fact from Microsoft.
  • A malicious user could strip off the strong name signature entirely, modify the assembly, re-sign it with his own key, and then passing off the assembly as his own code.

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.

Digital Signatures (Authenticode)

Authenticode is a technology that exists independently of .NET since Windows 2000. Much like Strong Name signing, it allows you to add a digital signature to a file but it also guarantees two things:

  1. The file hasn’t been modified since it was signed
  2. The file was signed using the private keypair issued to the signing company

Authenticode assigns a definite identity to a key, and allows for that identity to be verified by chaining the certificate up to a trusted root. It also provides an expiration date for certificates, and allows for a compromised key to be revoked through a Certificate Revocation List (CRL).

To see an example of this, you can open the properties of a file like C:\Windows\System32\MSCOMCTL.OCX on your system. You will see it has an extra tab called “Digital Signatures” where you can verify the signature and the certificate chain.

Digital Signatures tab of MSCOMCTL.OCX

The important thing to note about these digital signatures, is that you can apply them to almost any files - not just assemblies. Try it out for yourself.

  • Create a non-empty MyScript.vbs text file
  • Open a Visual Studio Command Prompt
  • Make a new test certificate

MakeCert.exe -sk myNewRootKey -r -ss myNewRoot

  • Sign the text file with your new signing key

SignTool.exe sign /s myNewRoot MyScript.vbs

  • Add a timestamp to the digital signature

SignTool.exe timestamp /t http://timestamp.verisign.com/scripts/timstamp.dll MyScript.vbs

  • Verify that the digital signature is valid (which it won’t be, because your test certificate isn’t issued by a trust root certificate authority)

SignTool.exe verify myfile.vbs

  • If you open the Digital Signatures tab on the properties of the file, you should be able to see the digital signature.

Authenticode Signatures and Strong Name Signatures

These two signatures are completely independent of each other.

  • A strong name helps to provide a unique identity for an assembly.
  • Authenticode allows you to verify who the author of the assembly is.

If both types of signatures are applied to an assembly, the strong name signature is wrapped within the Authenticode signature. Meaning that I could modify the bytes of the Authenticode signature so that it is no longer valid without invalidating the strong name signature. The reverse is not true — modifying the bytes of the strong name signature would invalidate both it and the Authenticode signature.

.NET CLR Assembly Load Performance

  • When the CLR loads an assembly which has an Authenticode signature, it will always try to verify that signature. This is in contrast to the Windows loader, which will verify the signature of a file only in specific instances, such as when the file is an ActiveX control.
  • This verification can be quite time intensive, since it can require hitting the network several times to download up to date certificate revocation lists, and also to ensure that there is a full chain of valid certificates on the way to a trusted root.
  • So, when an Authenticode signature is applied to an assembly it’s not unheard of to see a several second delay while that assembly is being loaded.
  • Also note that the optimisation applied to strongly named assemblies, where the strong name signature is not verified if the assembly is being loaded from the GAC is not applied to Authenticode signatures.
  • Since Authenticode provides the ability to revoke a certificate, we cannot assume that because the assembly’s Authenticode signature was valid when it went into the GAC it will remain valid every time we load it.

Signature Verification

This turns out to be one of the more surprising areas of the way the CLR checks Authenticode signatures.

  • If the assembly fails to verify, the loader will not reject it!
  • Instead, the assembly will continue to load, but will not be granted the PublisherIdentityPermission associated with the certificate.
  • On the other hand, a strong name verification error will cause the CLR to refuse to load the assembly (unless it is delay or test signed, and registered for skip verification).

Recommendations

So when would you use one or the other, or both together? I’m afraid you’ll have to make up your own decision based on the points I’ve raised here, because “It Depends”. But the conclusion that I’ve come to is:

  • Strong Naming is a Good Idea(tm) - use it wherever possible (as long as it makes sense to)
  • If your application is deployed solely within the corporate firewall on trusted machines, you probably don’t need Authenticode.
  • If your application is deployed outside the firewall on machines that you don’t control, Authenticode probably makes sense. If their machine gets hacked, you can verify that nobody has tampered with the signed files that you shipped.

In my next post I describe how you can use delay-signing to strong name your .NET assemblies and how you can use Team Build to prevent sharing your private key with developers.