Authenticode and Assemblies


The general concepts of Authenticode signing an assembly are well understood — they mostly correlate directly to the standard Win32 concept of a signed catalog.  However, there are a few places where managed code plays differently, and sometimes these catch people off guard.


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.  The only association between identity and public key created by a strong name is via word of mouth and personal trust decisions.  For instance, since Microsoft ships a security policy which indicates that the Microsoft public key should be FullTrust, I can infer that Microsoft owns that key, and thus any assembly signed by the corresponding private key came from Microsoft.


However, notice that I needed to make that decision myself — there was nothing in the key that indicated that it was in fact from Microsoft.  Also, and more importantly, there is no way for Microsoft to revoke that key — if it happened to be cracked one day, or if it somehow leaked into the wild.


Authenticode on the other hand, 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.


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.


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 optimization 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 Errors


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.  This means that it will not match PublisherMembershipCondition code groups, and will fail demands for the associated permission (unless this is v2.0 and the assembly is FullTrust, since FullTrust means FullTrust).


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).

Comments (13)

  1. JohnB says:

    We encountered severe problems with signed assemblies – if the CRL server is not reachable (ex if you’re behind a firewall) the startup of the application is blocked for a couple of seconds. Funny thing is, the Assembly will then be loaded nevertheless, so the whole check seems pointless to me. IMHO, it should be possible to entirely trust signed assemblies in order to prevent the CLR from checking with the CRL. Completely switching off the check (possible in the advanced tab of the internet settings) is not an acceptable workaround. We will remove the digital signature from our assemblies for these reasons…

  2. Right — if a signature fails to verify we don’t block the load, rather we just don’t grant the publisher identity permission associated with the certificate.

    -Shawn

  3. JohnB says:

    If you think of it from a third party component vendor perspective it’s a pity it works like it does – as soon as someone inserts our assembly into his application, he gets the firewall popping up and once he denies the connection, he ends up with a few second delay at app startup. We had to face severe accusations of producing spyware or trojans until we found out what’s actuall happening here. You can google for several threads from component vendors with the same problem – as of now, IMO using digital signatures with .NET assemblies is not recommendable as long asw you don’t write code for specific customers to whom you can explain what’s happening.

  4. Kevin Read says:

    We have also encountered problems with signed assemblies and CRL checking.

    In our case, the problems encountered happened on our backend servers, where this assembly loading delay was completely unacceptable.

    For these servers, we decided to go down the road of turning off crl checking to return assembly loading performance to "normal". Unfortunately this is a per user setting, which meant implementing login scripts and registry changes for inbuilt accounts.

    On these servers we have actually locked down CAS Policy so that only known publishers have any permissions, anything else in any zone gets nada.

  5. Sure — you have to evaluate the costs of signing with a certificate vs the benefits it gives you.

    Costs include the startup delay as we attempt to verify the signature, which does involve getting a new CRL.

    If you’re disabling the CRL, and could make a trust decision based upon just a key, strong naming might be a better option for you.

    -Shawn

  6. Foo says:

    A simple solution to the CRL check overhead is to use authenticode certificates that don’t have a CDP embedded in them. Of course, this generally implies a self-signed cert, but we’ve found this to be an acceptable compromise.

  7. Daniel says:

    The CRL check can be disastrous if the check happens during an install and it’s a service exe (or dependency) that’s being checked.  The service only has 30 seconds to start and if the network is in a broken state, then the service never starts and the install craps out.

    I don’t understand why Microsoft tells ISVs that Authenticode signatures must be used for Vista Logo compliance.

    Especially when this failure is very easy to reproduce.

  8. Yes, services are an especially painful point for this issue.  If you need to write a managed service, you could write a thin native layer which is the signed entyr point, and delegates to managed code to do the rest of the work as one option.

    -Shawn

  9. The Evidence class supports being enumerated in three different ways: GetAssemblyEnumerator GetHostEnumerator

  10. ... says:

    Scommettevo che avete speso molto tempo lavorare al luogo. Ha valso la pena di fare;)

  11. A while back I wrote about the performance penalty of loading an assembly with an Authenticode signature