Test Key Signing


One feature that will start to show up on the latest CTP of Whidbey is test key signing — basically delay signing++.  Lets do a quick review of what delay signing is, and then see where test key signing takes over.  Recall a delay signed assembly is one which has a public key associated with it, however its strong name signature is simply a block of 0’s.  Since that block of 0’s is unlikely to be the correct signature for the assembly, delay signed assemblies are registered for skip verification with the sn -Vr command.  This command allows the assembly to load even though it does not have the correct signature bytes.


One problem with this is that it opens up a hole on the machines with these skip verification entries.  There is no restriction on who can create the delay signed assemblies — a delay signed assembly created by a developer on the CLR team looks the same as a delay signed assembly created by a malicious person on the Internet.  Both can create an assembly that has the Microsoft public key, a zeroed signature block, and both will be treated the same by anyone who has the skip verification entries setup for the Microsoft key — which turns out to be most of the team working on the CLR and framework libraries.  Since these assemblies are granted FullTrust by policy, you can see the issue.


The same issue applies basically anywhere delay signing is used.  Developers and testers have skip verification entries for trusted assemblies on their machines, and nothing prevents other parties from creating assemblies that look to those machines as if they came from the build process.  The problem is mitigated by placing strong name codegroups below a zone code group in policy, but in general still remains.


Enter test key signing.  A test key signed assembly is similar to a delay signed assembly in that it allows the assembly’s signature and public key to be mismatched.  However, a test signed assembly actually has a valid signature — one created with a separate test key pair.  When an assembly is test key signed, it still needs to use sn to register a test key for the assembly.  This registration says “when loading this assembly, verify its signature against this public key, not the one specified in the assembly identity.”  In effect, you get the same benefits of delay signing, with the added feature that only people with access to the test signing key can create assemblies that can pretend to have your public key.


In order to use test key signing, you first delay sign your assembly as normal.  For the VB and C# compilers this means something along the lines of:


csc /out:assembly.dll input files …  /keyfile:publicKey.pk /delaysign+

Now you have a standard delay signed assembly.dll.  Dumping the signature shows that it is the expected block of zeros:


Strong name signature (128 bytes):
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

The next step is to test key sign it:



D:\src>sn -TS assembly.dll testKey.snk
Microsoft (R) .NET Framework Strong Name Utility Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.

Assembly ‘assembly.dll’ successfully re-signed

At this point assembly.dll has the public key of publicKey.pk, but a signature that is computed with testKey.snk.  Note that the key size of publicKey.snk and testKey.snk must match, or the test signing operation will fail with the message “Key pair does not match public key from assembly”.  If we use the signature dumping code, then we’ll see that it is no longer all zeros:


Strong name signature (128 bytes):
31 79 de c8 9c c3 5c 19 e5 5b 4b 66 65 53 ef 6d
89 45 77 b9 ba f4 81 56 d2 5a ab 7b  9 f8 a3 14
79 9a 38 f5 cb af 7f e9 f8 78 d4 24 57 27 a4 c0
e7 42 54 e1 a7 69 51 33 2e 3b 84 56 17 b6 78 4c
82  d 45 83 a9 6a 1e  5 77 f0 88 6e 89 91 88  e
89 a1 cd 7a 54 75 26 35 69 74 75 6f b1 63 5a 63
ec 8b  2 6c  c 15 f1 9a  d 20 75 a3 89 31 82 22
1b  8 86 3c fd 20 9c b8 1d 34  a  a e2 6a f4 8f

Now we need to extract the public key from the test key, and set up a test key registration for this assembly:


D:\src>sn -p testKey.snk testPublicKey.pk

Microsoft (R) .NET Framework Strong Name Utility Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.

Public key written to testPublicKey.pk

D:\src>sn -Vr assembly.dll testPublicKey.snk

Microsoft (R) .NET Framework Strong Name Utility Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.

Verification entry added for assembly ‘assembly,B77A5C561934E089’

Assembly.dll is now fully test signed and ready to use as if it were signed with publicKey.pk.  Note that you need to register the test public key only, attempting to register the full keypair will result in the message: “The public key supplied for test signing is invalid”.  An * can also be used for the assembly name, if you supply the corresponding public key token, this works in the same way that the * wildcard works for delay signing.  Notice also that running sn -v on the assembly shows that it is valid while sn -vf indicates that it is either delay signed or test signed. 


In addition to the commands shown here, a few other sn commands got updated to support test key signing.  There is a new -TSc option to test sign using a key container instead of the key file that -TS requires.  sn -Vl will now show test signed assemblies and the public key they are signed with.  sn -Vu and -Vx will also remove test key signatures for the given assembly.  Finally, sn -R will also resign a test key signed assembly.


Test keys can be managed in a few ways.  One option is to create a test signing key and check it into the source control system with the rest of the code, next to the official public key.  You can then periodically change this key in the source control system.  This has the advantage that developers can generally just create a new enlistment in your source control and everything is ready to build right away.  The downside is the usual problems with checking in a key pair.  (this is mitigated somewhat by the fact that you are changing this key pair periodically and that it is not the actual official pair).


Another option would be to have developers simply create a key on their own machines.  The downside here is that there is a step that everybody must do before they can build the code, and that there is no central way to force individual developers to update their key every now and again.


[Updated 11/4/2005: fixed the keyfile to be registered]

Comments (10)

  1. Kevin Westhead says:

    "Note that you need to register the test public key only, attempting to register the full keypair will result in the message: ‘The public key supplied for test signing is invalid.’"

    Doesn’t this mean that the line

    D:src>sn -Vr assembly.dll testKey.snk

    should actually be

    D:src>sn -Vr assembly.dll testPublicKey.snk

    You also mentioned a couple of problems with managing test keys. Is this feature currently in use on the CLR and BCL teams? If so, how are they managing their test keys?

  2. shawnfa says:

    Hi Kevin — you’re write, I meant to register the public key, and I’ve updated it.

    This feature is not currently used by the CLR and BCL teams (we used delay signing for Whidbey), but we could move towards it in the future.

    -Shawn

  3. hevean says:

    Hi all,

    what’s the different between ‘sn -TS….’ and ‘sn -Ra’. and what’s the different between the re-signed .dll file via ‘sn -TS….’ and via ‘sn -TS….’.

    Thanks 🙂

  4. shawnfa says:

    SN -TS test signs an assembly as I described in this post.  That means the assembly will not load unless the machine has registerd the test key pair for that assembly.

    SN -Ra re-signs an assembly, leaving it with a valid signature.  Since the signature is valid, it does not require any special configuration to verify on the user’s machine.

    -Shawn

  5. Steve says:

    Am I trying to understand the world of digital signing, so I’m probably missing something … but if an internal signing authority can resign a delay signed or test signed assembly, what prevents a malicious person from re-signing an assembly that I have deployed?  Does this imply that there needs to be some link between the test and official keys?  I assume there is some way to re-sign a delay signed assembly with a different public key if the original key is lost or compromised?

  6. shawnfa says:

    Hi Steve,

    The intent is that you never deploy a test signed assembly.  Once you are ready to ship your final bits, then you’ll apply the real signature.  This signature can only be calculated if you have both the private and public key for the assembly.

    A third party could of course resign your assembly with their own key.  This would have the effect of changing the public key of the assembly however, which gives the assembly an entirely different identity.

    -Shawn

    (The link between the test key and the public key that you mention is kept in the registry in the skip verification entries by the way.)

  7. Steve says:

    Hello again,

    Thanks for your reply.  I have a situation where I inherited a project that was delay signed but I only have the public key – the original key pair is not available.  The assemblies have not been deployed, so I am trying to find out what my options are when deployment time comes.  I tried generating another key pair and delay signed a test assembly that was previous signed with the original public key with the new public key.  When I tried to use this test assembly it would load, saying it had been tampered with.  Do you know how I can resolve this issue?  Thanks again,

    Steve

  8. shawnfa says:

    If you’ve lost the private key for the assembly’s public key, your only option is to generate a new key pair to sign the assemblies with.  This will of course end up changing the identity of the assemblies — however if we allowed anyone to sign assemblies who did not have the corresponding private key, this would totally invalidate the purpose of the signatures 🙂

    -Shawn