1.
Weakly Named Assembly vs Strong Named Assembly
Weakly named assemblies and strongly named assemblies are
structurally identical—that is, they use the same portable executable (PE) file
format, PE32(+) header, CLR header, metadata,
manifest
tables, and Intermediate Language (IL) .
The real
difference between weakly named and strongly
named assemblies is that a strongly named assembly is
signed with a publisher’s public/private key pair that uniquely identifies
the assembly’s publisher.
A strongly named assembly consists of
four attributes that uniquely identify the
assembly:
a file
name (without an extension),
a
version number,
a
culture identity, and
a
public key.
e.g
"MyTypes, Version=1.0.8123.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
"MyTypes, Version=1.0.8123.0, Culture="en-US",
PublicKeyToken=b77a5c561934e089"
"MyTypes, Version=2.0.1234.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
"MyTypes, Version=1.0.8123.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
The first string identifies an assembly file called MyTypes.exe or
MyTypes.dll (you can’t actually determine the file extension from an
assembly identity string). The company producing the
assembly
is creating version 1.0.8123.0 of this assembly, and nothing in the assembly is
sensitive to any one culture because Culture is set to neutral. Of course,
any company could produce a MyTypes.dll (or MyTypes.exe) assembly file that
is marked with a version number of 1.0.8123.0 and a neutral culture.
Microsoft chose to use
standard public/private key cryptographic
technologies instead of any other
unique identification technique such as GUIDs, URLs, or URNs.
2.
SN.exe (Strong Name)
SN –k
MyCompany.snk
This
line tells SN.exe to create a file called MyCompany.snk. This file will contain
the public and private key
numbers persisted in a binary format.
Public
key numbers are very big. If you want to, after creating the file that contains
the public and private key, you can use the SN.exe utility again to see the
actual public key. To do this,
you
must execute the SN.exe utility twice. First, you invoke SN.exe with the –p
switch to create a file that contains only the public key
(MyCompany.PublicKey):
SN
–p MyCompany.snk MyCompany.PublicKey
Then,
you invoke SN.exe, passing it the –tp switch and the file that contains just the
public key:
SN
–tp MyCompany.PublicKey
When
I execute this line, I get the following output:
Microsoft (R) .NET Framework Strong Name Utility Version
4.0.20928.1
Copyright (c) Microsoft Corporation. All rights reserved.
Public key is
00240000048000009400000006020000002400005253413100040000010001003f9d621b702111
850be453b92bd6a58c020eb7b804f75d67ab302047fc786ffa3797b669215afb4d814a6f294010
b233bac0b8c8098ba809855da256d964c0d07f16463d918d651a4846a62317328cac893626a550
69f21a125bc03193261176dd629eace6c90d36858de3fcb781bfc8b817936a567cad608ae672b6
1fb80eb0
Public key token is 3db32f38c8b42c9a
The
SN.exe utility doesn’t offer any way for you to display the private key.
The
size of public keys makes them difficult to work with. To make things easier for
the developer (and for end users too), public key tokens were created. A
public key token is a 64-bit
hash
of the public key. SN.exe’s –tp switch shows the public key token that
corresponds to the complete public key at the end of its output.
Now
that you know how to create a public/private key pair, creating a strongly
named assembly is simple. When you compile your assembly, you use
the /keyfile:<file> compiler
switch:
csc
/keyfile:MyCompany.snk Program.cs
When
the C# compiler sees this switch, the
compiler opens the specified file
(MyCompany.snk), signs the assembly with the private key, and
embeds the public key in the manifest. Note that you sign only the
assembly file that contains the manifest; the assembly’s other files can’t
be signed explicitly.
3.
Sign Assembly
Here’s what it means to sign a file: When you build a strongly
named assembly, the assembly’s FileDef manifest metadata table includes the
list of all the files that make up the assembly.
As
each file’s name is added to the manifest, the file’s
contents are hashed, and this hash value is stored along with the file’s
name in the FileDef table. You can override the
default hash algorithm used with
AL.exe’s /algid switch or apply the
assembly-level System.Reflection.AssemblyAlgorithmIdAttribute custom
attribute in one of the assembly’s source code files. By default, a SHA-1
algorithm is used, and this should be sufficient for almost all
applications.
After
the PE file containing the manifest is built, the PE file’s entire contents
(except for any Authenticode Signature, the assembly’s strong name data,
and the PE header checksum) are
hashed,
as shown in Figure below. The hash algorithm used here
is always SHA-1 and can’t be overridden. This hash value is signed with the
publisher’s private key, and the resulting RSA digital signature is stored
in a reserved section (not included in the hash) within the PE file. The
CLR header of the PE file is updated to reflect where the digital signature is
embedded within the file.
Note: Because public keys are such large
numbers, and a single assembly might reference many assemblies, a large
percentage of the resulting file’s total size would be occupied with
public key information. To conserve storage space, Microsoft hashes the
public key and takes the last 8 bytes of the hashed value. These reduced
public key values—known as public key
tokens—are what are actually stored
in an AssemblyRef table.
In general, developers and end users will see public key token values much
more frequently than full public key values.
Note,
however, that the CLR never uses public key tokens when making security or trust
decisions because it is possible that several public keys could hash to a
single public key token.
e.g
Assembly
-------------------------------------------------------
Token: 0x20000001
Name : JeffTypes
Public Key :
Hash Algorithm : 0x00008004
Version: 3.0.0.0
Major Version: 0x00000003
Minor Version: 0x00000000
Build Number: 0x00000000
Revision Number: 0x00000000
Locale: <null>
Flags : [none] (00000000)
"JeffTypes, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=null"
AssemblyRef #1 (23000001)
-------------------------------------------------------
Token: 0x23000001
Public Key or Token: b7 7a 5c 56 19 34 e0 89
Name: mscorlib
Version: 4.0.0.0
Major Version: 0x00000004
Minor Version: 0x00000000
Build Number: 0x00000000
Revision Number: 0x00000000
Locale: <null>
HashValue Blob:
Flags: [none] (00000000)
"MSCorLib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
4.
The Global Assembly Cache - GAC
If an assembly is to be accessed by multiple applications, the
assembly must be placed into a well-known directory, and the CLR must know
to look in this directory automatically when a
reference
to the assembly is detected. This well-known location is called
the global assembly cache (GAC), which
can usually be found in the following directory (assuming that Windows is
installed in the C:\Windows directory):
C:\Windows\Assembly
The GAC directory is structured: It contains many
subdirectories, and an algorithm is used to generate the names of these
subdirectories. You should never manually copy
assembly files into the GAC; instead, you should use tools to accomplish
this task. These tools know the GAC’s internal structure and how to
generate the proper subdirectory names.
While developing and testing, the most common tool
for installing a strongly named
assembly into the GAC
is GACUtil.exe.
5.
GACUtil.exe
switches
/i <assembly_path> [ /r
<...> ] [ /f ]
Installs an assembly to the global assembly cache.
/il <assembly_path_list_file> [
/r <...> ] [ /f ]
Installs one or more assemblies to the global assembly cache.
/u <assembly_display_name> [ /r
<...> ]
Uninstalls an assembly from the global assembly cache.
/ul <assembly_display_name_list_file>
[ /r <...> ]
Uninstalls one or more assemblies from the global assembly cache.
/l [ <assembly_name> ]
List the global assembly cache filtered by <assembly_name>
/lr [ <assembly_name> ]
List the global assembly cache with all traced references.
/cdl
Deletes the contents of the download cache
/ldl
Lists the contents of the download cache
/r <reference_scheme>
<reference_id> <description>
Specifies a traced reference to install (/i, /il) or uninstall (/u,
/ul).
Note: By default, the GAC can be manipulated only by a
user belonging to the Windows Administrators group. GACUtil.exe
will fail to install or uninstall an assembly if the user invoking the
execution of the utility isn’t a member of this group.
Using GACUtil.exe’s /i
switch is very
convenient for developer testing. However, if
you use GACUtil.exe to deploy an assembly in a
production environment, it’s recommended that you use
GACUtil.exe’s /r switch in addition to specifying the /i or /u switch to install
or uninstall the assembly. The /r switch
integrates the assembly with the Windows install and uninstall engine.
Uasically, it tells the system which application requires the assembly and
then ties the application and the assembly together.
Important - Globally deploying assembly files
into the GAC is a form of registering the assembly, although the actual
Windows registry isn’t affected in any
way. Installing assemblies into the GAC breaks
the goal of simple application installation, backup, restore, moving, and
uninstall. So it is recommended that you avoid global deployment and use
private deployment whenever possible.
6. Strongly
Named Assemblies Are Tamper-Resistant (防篡改)
When a strongly named assembly is installed in the GAC, the system
ensures that the file containing the manifest hasn’t been tampered with.
This check occurs only once, at installationtime. In addition, to improve
performance, the CLR does not check if a strongly named assembly has been
tampered with if the assembly is fully trusted and is being loaded into a fully
trusted AppDomain. On the other hand, when a strongly named assembly is
loaded from a directory other than the GAC, the CLR verifies the assembly’s
manifest file to ensure that the file’s contents have not been tampered
with, causing an additional performance hit every time this file
is loaded.
7.
Delay Signing
For the C# compiler, you do this
by specifying the /delaysign compiler
switch. In Visual Studio, you display the
properties for your project, click the Signing tab, and then select
the Delay Sign Only check box. If you’re
using AL.exe, you can specify
the /delay[sign] command-line
switch.
8. Privately
Deploying Strongly Named Assemblies
In addition to deploying a strongly named assembly in the GAC or
privately, a strongly named assembly can be
deployed to some arbitrary directory that a small set of applications know
about.
When you install each application into its directory, also
install an XML configuration file, and
have the shared
assembly’s codeBase element
indicate the path of the shared assembly. Now
at runtime, the CLR will know to look in the strongly named assembly’s
directory for the shared assembly. For the record, this technique is rarely
used and is somewhat discouraged because no single application controls
when the assembly’s files should be uninstalled.
The configuration
file’s codeBase element
actually identifies a URL. This URL can refer to any directory on the
user’s machine or to a Web address. In the case of a Web address, the CLR
will
automatically download the file and store it in the user’s download cache (a
subdirectory under C:\Users\UserName\Local Settings\Application
Data\Assembly, where UserName is the name of the Windows user account
currently signed on). When referenced in the future, the CLR will compare
the timestamp of the downloaded file with the timestamp of the file at the
specified URL. If the timestamp of the file at the URL is newer, the CLR
will download the new version of the file and load it. If the previously
downloaded file is newer, the CLR will load this file and will not download
the file again (improving performance). An example of a configuration file
containing a codeBase element is shown later in this chapter.
9. How
the Runtime Resolves Type References