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