Combining Strong Names with Authenticode

If you want to use both a strong name and Authenticode signature on your assembly (for instance if you need a strong name for strong assembly identity, and your company has a rule requiring Authenticode signatures on all shipped products), then you need to make sure to do these in a specific order.

  1. Strong name the assembly
  2. Authenticode sign the assembly

The reason for this is that since Authenticode signatures were invented before strong naming was a twinkle in someone's eye, they don't know anything about strong name signatures.  However, the CLR does know about both signature types, and can therefore make sure that signatures are applied in a manner that allows both to validate.

Why does the CLR have to do anything at all to allow both signatures to validate?  Imagine for a minute that no extra work is done in the dual-signature case, and that we just hash and sign the assembly.  When you strong name sign the hash is of the assembly with no signatures; however the Authenticode signature will be with the hash of the assembly containing the strong name signature.  During verification, the problem appears -- when verifying the strong name signature the hash of the assembly is different than the one calculated during signing, because the Authenticode signature is now part of the assembly.  If we attempt to fix this by resigning the assembly after Authenticode signature is applied the reverse is now true -- the hash of the assembly with the original strong name signature (which is what the Authenticode signature contains) is now different from the hash with the new strong name signature.

In order to prevent this cycle, when the hash of an assembly is calculated the CLR actually zeros out the directory entry in the optional headers of the PE file for the Authenticode signature, and will skip over the section of the file containing the Authenticode signature itself.  This means that the hash used for the strong name signature does not include any Authenticode information at all.  Since you should always strong name sign first, this step actually does nothing during the signing stage because there will not be an Authenticode signature to skip.  However, during the verification process there will be an Authenticode signature present .  By zeroing out the directory entry and skipping the section of the PE image containing the Authenticode signature the hash will be the same as it was at signing time -- therefore allowing the strong name signature to verify.

Since Authenticode doesn't know anything about strong names, it does not treat the strong name signature in any sort of special way.  That's why it needs to be applied after the assembly is strong name signed, since it needs to see the exact same bits during signing and verification.  Note that this means that if you're delay or test key signing, you need to apply the Authenticode signature after the final signature is calculated, not just after the assembly is built.

Incidentally, the Authenticode signature is not the only section of the PE file that is not covered by the strong name signature.  We also skip the checksum field of the PE header and the strong name signature blob.