Identifying Code with Strong Names

Code must be uniquely identified to be shared. The Global Assembly Cache (GAC) does not support an assembly that does not have a strong name. You should seriously consider giving even your private assemblies a strong name that uniquely identifies your code. A strong name provides a unique identifier to a set of code that is statistically impossible to be duplicated. In other words, a strong name is a means of uniquely identifying this code as yours. A strong name is part of an AssemblyName that consists of four parts:

  • Name—Visual Studio assigns a name to an assembly that is the name of the output DLL, minus the DLL suffix. This name can be any name that you choose.

  • Publisher Key—The actual public key stored in the assembly is about 160 bits. When the full name of the assembly is referenced, you see a key token. Because the full public key takes so many bytes to represent, Microsoft hashes the public key and takes the last eight bytes of the hashed value to form what is known as a key token. This eight-byte string is unique.

  • Version—This four-part string identifies the version of the assembly. It refers to three logical parts: the assembly version, build number, and revision number. Physically, the assembly version consists of a major and a minor version number. Assuming that it is a policy for a company to build an assembly every day, the build number should increment with every build. If it is necessary for two builds to occur in a day, then the revision should be incremented. If a version attribute is specified with a wildcard such as [assembly: AssemblyVersion("1.2.*")], then Visual Studio takes care of incrementing the build and revision number for you.

  • Culture—This is a string of the form xx-yy, where xx is the language and yy is the country. Specifying only xx is known as language neutral, and not specifying anything or specifying a blank culture string is known as a neutral culture. Chapter 18, “Globalization/Localization,” goes into more detail about culture. An assembly's culture can be assigned at link time using the al.exe utility (al /c[ulture]:<xx-yy>) or with an assembly attribute such as [assembly: AssemblyCulture("xx-yy")]. Assemblies lacking MSIL code that have just resource information are known as satellite assemblies. These satellite assemblies are important in building international applications.

If the assembly is missing the public key or the public key token, then it does not have strong name. Figure 6.1 summarizes the important information that is contained in an AssemblyName.

Figure 6.1. Decoding an assembly name.


When any portion of the AssemblyName is different, the runtime considers it a different assembly. Most commonly, you see differences in the version (to reflect bug fixes, updates, and so on) and culture (to reflect support for different cultures). After an assembly is uniquely identified with a strong name, the strong name can be further used to enforce runtime version checks, enforce binding policy, provide security evidence, and prevent spoofing or tampering. These topics are covered later in this chapter.

Figure 6.2 shows the process of linking the main assembly with a referenced assembly.

Figure 6.2. Referencing a strongly named assembly.


Particularly notice that the main assembly has the version and a key token for the referenced assembly. The first benefit that this gives you is security against spoofing or tampering. When an assembly is given a strong name, the entire contents of the file are hashed with the private key. To replace a file or even slightly modify it successfully, the perpetrator would need to know the private key to successfully generate a hash for the file and the embedded public key.

Note

You can modify the file's contents to prove to yourself that adequate security is available. To complete this demo, the file was simply modified with a binary editor. You could modify the file in other ways. You could convert the file to IL, edit it, and recompile the IL. You could also use reflection to modify the string table. Another method could be to use the set of unmanaged APIs detailed in Chapter 4 to modify portions of the file containing the assembly. You could also memory map the filewith write permission and modify it that way. The way you decide to prove that a hash is indeed being generated is your choice. You could even try all of these suggestions to see if you are able to thwart the hash security.


If you try to modify only one character of the assembly, tools such as PEVerify and ILDasm work fine. After the assembly has been generated, use ILDasm to look at the edited version. Listing 6.4 illustrates the modified IL.

Listing 6.4. Using ILDasm to Illustrate Assembly Tampering
.method public hidebysig specialname rtspecialname
        instance void  .ctor() cil managed
{
  // Code size       17 (0x11)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  ldstr      "I am the neutraf version of Foo."
  IL_000b:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0010:  ret
}  // end of method Foo::.ctor

Notice the misspelling of neutral. Nothing so drastic has been done to make the file unreadable. However, the assembly fails to load with an exception:

Unhandled Exception: System.IO.FileLoadException: Strong name validation failed for
 assembly 'FooLib'.

Now when a hash is computed, it is different because of the single character change that was made to the file. If you edit this DLL and change the letter back, the assembly loads without error. Giving an assembly a strong name has more benefits than just being able to share the assembly. It is recommended that all assemblies be given a strong name. Specifically, the following presents some additional considerations for strongly naming an assembly:

  • After an assembly is signed, it can be shared and redistributed.

  • Runtime version checking is possible after an assembly has a strong name.

  • Runtime policy checking is possible with strong-named assemblies.

  • If the strong-named assembly is shared, you can take full advantage of side-by-side deployment.

You have learned the benefits of giving an assembly a strong name. You will now learn a step-by-step approach to developing and deploying an assembly with a strong name:

1.
Generate a public/private key pair using the sn (Strong Name) utility. To do so, enter the following at a command prompt:

sn –k secret.snk

This generates a public/private key pair and exports them to a file called secret.snk. This file should be guarded and not exposed to anyone but a select group of lead developers. After the public/private key pair is generated, then a public key needs to be generated that can be distributed to all developers with the following command:

sn –p secret.snk public.snk

This command generates a file called public.snk that contains the public key to be used for a project or projects. This file can be freely distributed, and its use in signing files is encouraged.

2.
Mark each assembly with an attribute that identifies the assembly key file for signing the assembly. Actually, for the assembly to compile successfully, an assembly needs two attributes, as follows:

[assembly: AssemblyDelaySign(true)]
[assembly: AssemblyKeyFile(@"....pubfoo.snk")]

The AssemblyDelaySignAttribute indicates that the assembly is in development and does not need to be signed now. If the AssemblyDelaySignAttribute is not present and is not set to true, then you get a compile error:

Cryptographic failure while signing assembly 'FooLib.dll' -- 'Key file '....pubfoo.snk'
 is missing the private key needed for signing'

The AssemblyKeyFileAttribute identifies the public key file that was generated in the first step. An assembly that is specified by these two attributes is known as partially signed because the private key was not available to fully sign the assembly.

3.
Now when you try to use this assembly, you get the following exception:

Unhandled Exception: System.IO.FileLoadException: Strong name
validation failed for assembly 'FooLib'.

To prevent this error, you need to again use the sn utility to register for strong name verification to be turned off. Registering an assembly for verification skip requires administrative privileges.

sn –Vr foolib.dll

Registering an assembly using sn adds the following key to the registry:

HKEY_LOCAL_MACHINESOFTWAREMicrosoftStrongNameVerificationFooLib,BA049F56C6309B78

When the CLR verifies an assembly, it first checks this registry to see if an appropriate entry exists. If it does, then the verification is skipped. When the development phase is finished, the developer can unregister the assembly as follows:

sn –Vu foolib.dll

Alternatively, you can unregister the verification skips like this:

sn –Vx

It is possible to register all assemblies of a given public key:

sn –Vr *,BA049F56C6309B78

or register all assemblies to be skipped:

sn –Vr *

It is also possible to skip verification for a comma-separated list of users:

sn –Vr foolib.dll machineme,machineyou

Caution

All options for the Strong Name Utility are case sensitive.

4.
After the strong-named assembly is registered, the developer can develop software as before. The assembly can be removed and rebuilt. The registry entry is causing the verification skip.

5.
When development is completed for that particular assembly, its entry can be removed from the registry, either with the -Vx or the -Vu <assembly> flag.

6.
As part of the build process for shipping a product, each assembly needs to be signed. This is done with the sn utility again as follows:

sn –R foolib.dll secret.snk

To fully sign the assembly, the private key is required, and that is contained in the secret.snk file. Again, access to the secret.snk file should be tightly controlled. After signing an assembly with the private key, it is fully signed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.119.17