Digital Signatures

Signing a file is an extension of hashing which additionally guarantees file authenticity. The generic signing process that we describe here is what actually happens under the hood if you get an assembly signed as it is compiled by specifying the AssemblyKeyFile attribute in a source file - but the principles apply in general, not just to .NET assemblies.

Signing a file involves two stages: a hash is embedded into the file as just described, but in addition, prior to writing the hash into the file, the hash is encrypted using some private key. Often some extra information will be added to the hash before encryption, such as the date when it was encrypted, or the name of the company that owns the private key (though that doesn't happen in the default procedure for signing .NET assemblies - they are signed by encrypting just the hash). Notice that this provides an example of the principle I discussed earlier of only using public key encryption to encrypt small amounts of data for performance reasons. Encrypting the hash is a lot quicker than encrypting the whole file would have been, but it provides virtually the same level of authentication and non-repudiation security. More importantly, when the CLR loads the assembly in order to execute it, it can decrypt the hash a lot more quickly than it would have taken to decrypt the whole file.

When some other software wants to make use of that assembly, it will first decrypt the encrypted hash using the corresponding public key. Then it will independently work out the hash value. If the two values match, that proves (well OK, not quite 100%, but it comes so close to 100% that it is a proof for all practical purposes) that the file was generated by an individual who had access to the private key, and has not been tampered with since. This, provided the organization was keeping its private keys confidential, pretty much proves the authenticity of the file.

Delay-Signing an Assembly

In Chapter 4, I explained how to use the sn tool to create a public-private key pair for an assembly. In that chapter, we presented an example called GreetMe that involved the generation of a shared assembly with satellite assemblies. However, there was a problem with that example in that it required the public-private key pair to reside in a file to which the developers have access, in order that each time the project is built it can be signed again. That's dangerous since private keys really should be kept very strictly confidential. The keys generated by sn are used strictly within and to identify assemblies and are not registered with certificate authorities, but that doesn't change the fact that if the wrong person gets their hands on the private key they can start tampering with your code without the users detecting this. So your company really should arrange things so that the .snk file generated by sn is only accessible to a few trusted people.

In this section we'll modify the GreetMe sample so that it meets those criteria, by arranging for assemblies to be delay-signed.

The new-look GreetMe sample has this file structure:

click to expand

As you can see, there are now three subfolders in the example, representing the three main roles in the development of the program. The Developer folder contains the files that the developers working on the project should be allowed to see. The TrustedPerson folder represents the folder that is accessible only to those few people who are trusted to have access to your company's private keys. In the example, this folder contains the key file, AdvDotNet.snk, as well as a couple of batch files. The Developer folder contains the same folders as previously, except that the test form program, TestForm.cs, has been moved into a new folder, Tester. This change is to represent the separation of development and testing.

So, in summary, the folders look like this:

click to expand

In order to simplify compilation of the project, the Tester and Developer folders both contain compile.bat files which handle compilation of all the files in their respective folders. The TrustedPerson folder does not have a compile.bat file since there are no source files for the trusted person to compile. Instead, this folder contains two batch files. The first batch file, SetUpKey.bat extracts the public key (but not the private key) from AdvDotNet.snk and distributes it to developers by copying it to the Developer folder. The second batch file, sign.bat, is intended to be executed when all the code is ready for shipping. It signs all the assemblies and installs them into the Global Assembly Cache (GAC).

In addition to these files, all the folders contain a cleanup.bat batch file, which you can use after trying out the project - it removes all the generated files and restores the folders to the state they were in when the example was first downloaded. And just to make compiling and cleaning up very simple, the main GreetMe folder contains compile.bat and cleanup.bat files, which simply invoke all the compile/cleanup batch files in turn.

The first stage in preparing this project is that the trusted person must give the developers a copy of the public (but not the private) key. This is done by running the batch file, SetUpKey.bat. This is what that file does:

 sn -p AdvDotNet.snk PublicKey.snk copy PublicKey.snk ..\Developer\PublicKey.snk 

The -p option tells sn to extract the public key from an .snk file and store it separately. So this code simply obtains the public key and stores it in the Developer folder so that the developers can build the files using this key (but they still never get access to the private key).

The developers now develop and test the application, exactly as before. However, we need a couple of changes to the batch file used to build it. For reference, we display the complete Compile.bat file here, and highlight the changes as compared to the version of this file we used in Chapter 4:

 rem COMPILE DEFAULT RESOURsgen Strings.txt resxgen /i:NoFlag.jpg /o:Flags.resx /n:Flag resgen Flags.resx rem COMPILE SOURCE FI /t:module FlagDlg.cs vbc /t:module /r:System.dll /r:System.drawing.dll /r:System.Windows.Forms.dll FlagDlg.vb csc /addmodule:FlagDlg.netmodule /res:Strings.resources /res:Flags.resources /t:library GreetMe.cs rem COMPILE en-US RESOURn-US resgen Strings.en-US.txt resxgen /i:USFlag.jpg /o:Flags.en-US.resx /n:Flag resgen Flags.en-US.resx al /delay+ /embed:Strings.en-US.resources /embed:Flags.en-US.resources    /c:en-US /v:1.0.1.0 /keyfile:../PublicKey.snk /out:GreetMe.resources.dll cd .. rem COMPILE en-GB RESOURn-GB resgen Strings.en-GB.txt resxgen /i:GBFlag.jpg /o:Flags.en-GB.resx /n:Flag resgen Flags.en-GB.resx al /delay+ /embed:Strings.en-GB.resources /embed:Flags.en-GB.resources    /C:en-GB /v:1.0.1.0 /keyfile:../PublicKey.snk /out:GreatMe.resources.dll cd .. rem COMPILE de RESOURCES Note that there is no de flag because de could mean Germany or Austgen Strings.de.txt al /delay+ /embed:Strings.de.resources /c:de /v:1.0.1.0    /keyfile:../PublicKey.snk /out:GreetMe.resources.dll cd .. rem INSTALL INTO GLOBAL ASSEMBLY CA------ sn /Vr GreetMe.dll gacutil /i GreetMe.dll sn /Vr en-US/GreetMe.resources.dll gacutil /i en-US/GreetMe.resources.dll sn /Vr en-GB/GreetMe.resources.dll gacutil /i en-GB/GreetMe.resources.dll sn /Vr de/GreetMe.resources.dll gacutil /i de/GreetMe.resources.dll 

The main changes are that I have added the flag /delay+ to the al commands used to create the satellite assemblies (as well as the trivial change of changing the name of the .snk file to match the one we have generated). Adding the delay sign flag means that the public key stored in the key file will be added to the assembly and form part of its identity. However, the assembly won't be signed: the hash of the assembly contents won't be encrypted - we can't encrypt it here since we don't have access to the private key to encrypt it with!

The other big change is the appearance of the sn /Vr commands immediately before we install each assembly to the GAC. sn /Vr registers an assembly for what is known as verification skipping. Don't be confused by the name - it's got nothing to do with skipping type-safety verification. Instead, it allows the assembly to be installed into the GAC even though it hasn't been digitally signed yet. Normally, gacutil.exe won't allow an assembly into the cache without a digital signature, but this step ensures that these assemblies, which have merely had space reserved for a strong name to be added later, can go in the cache, so that the example can be tested properly.

There is also one change to the C# source code for the example. The GreetMe.dll main assembly also needs to be delay-signed. Since this assembly is generated by the C# compiler, not by the al utility, the technique for specifying that it is delay-signed is different. Instead of using a command-line parameter, we use an attribute in the C# source code:

 [assembly: AssemblyVersion("1.0.1.0")] [assembly: AssemblyCulture("")] [assembly: AssemblyDelaySign(true)] [assembly: AssemblyKeyFile("PublicKey.snk")] 

The TestForm.cs test program also needs to be compiled. The batch file for this is very simple:

 csc /references:../Developer/greetme.dll testform.cs 

The /reference flag here refers to our private copy of GreetMe.dll. Recall from Chapter 4 that it's usual to reference local copies at compile time, but when the program is executed, it will still load the copies in the Global Assembly Cache.

Once the developers have finished with the project and it is ready to ship, the trusted person comes in again, and runs the final batch file, sign.bat. This is what sign.bat does:

 cd ..\Developer sn -R GreetMe.dll ../TrustedPerson/AdvDotNet.snk sn -R en-US/GreetMe.resources.dll ../TrustedPerson/AdvDotNet.snk sn -R en-GB/GreetMe.resources.dll ../TrustedPerson/AdvDotNet.snk sn -R de/GreetMe.resources.dll ../TrustedPerson/AdvDotNet.snk gacutil /i GreetMe.dll gacutil /i en-US/GreetMe.resources.dll gacutil /i en-GB/GreetMe.resources.dll gacutil /i de/GreetMe.resources.dll cd ..\TrustedPerson 

This batch file changes folder into the Developer folder, and resigns all the assemblies with the private key. The -R option on the sn command replaces any previous signature with the specified key. Then we install the signed files into the GAC (this will overwrite the unsigned copies placed there previously), and we are ready to perform final testing on the application before shipping.

The procedure we've gone through here involves delay-signing the assemblies. As an alternative, you could simply use a temporary test private key to test the assemblies with and then resign them with the real key prior to shipping. However, that approach has the minor disadvantage that the assemblies are being tested with the "wrong" identity, since the public key forms part of their identity. That isn't too serious, but it will mean that any dependent assemblies will need to be recompiled to correctly reference the signed assemblies prior to shipping. And if your application is a new version of a library and you want to test it with some other client code to which you don't have access to the source code, you won't be able to use a test key. So in general my preference would be the approach demonstrated in the example.



Advanced  .NET Programming
Advanced .NET Programming
ISBN: 1861006292
EAN: 2147483647
Year: 2002
Pages: 124

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net