This article has been
excerpted from book "The Complete Visual C# Programmer's Guide" from the Authors
of C# Corner.
You can ensure an assembly's identity by signing it. This signing operation
employs public-key infrastructure algorithms. Assembly manifests are hashed and
signed with a private key. When any assembly is deployed, the hashed value is
calculated and compared with the hashed value stored in the assembly. If the
runtime hash value matches the hard-coded hash, the installation is allowed to
continue.
Note that the digital signature is found elsewhere in the metadata, in a section
that is not included in the manifest. The digital signature of an assembly is
just a secure hash of the manifest, which ensures that no one can modify the
assembly contents after shipment. The CLR also provides support for embedding
digital certificates, Authenticode, into assemblies.
You must use a private key to sign the assembly to create a strong-named
assembly. (The strong name is a cryptographically strong identifier provided by
the author or publisher of the assembly). The public key is by design
incorporated into the assembly when it is signed, so your clients can verify the
strong name. A .NET Framework SDK utility, the Strong Name tool (Sn.exe), is
used to build strong-name key files.
Strong-name (SN) signatures and Authenticode signatures do not override each
other; they coexist. You must be sure that the strong-name signing operation
takes place before the Authenticode signing.
You can identify managed code by using Authenticode signatures with strong-name
signing. You can consider using both if you already have a security trust
structure that depends on Authenticode signatures. The Authenticode signature is
stored in a special section of the assembly if an assembly is also signed by
using Authenticode. Only the file containing the manifest should be signed when
you use Authenticode for multifile assemblies.
Together, SN and Authenticode signatures are nearly impenetrable by hackers.
Authenticode signatures warrant that a particular publisher signed an assembly.
Authenticode signatures also guarantee that the publisher has not yet revoked
the certificate that signed the assembly. However, strong names do not warrant
that a signature has not been revoked. Strong names allow you to ensure that the
contents of a given assembly have not been tampered with. Therefore, you can
make sure that the assembly loaded at runtime is from the same publisher who
coded and compiled your program.
For several reasons, the cost of verifying assemblies signed by Authenticode is
significantly greater than the cost of verifying a strong-named assembly.
Strong-name signing does not involve certificates, but Authenticode does.
Similarly, strong-name signing does not depend on a certification hierarchy, but
Authenticode does. Remember, an Authenticode signature will make your assembly
invalid if you assign a strong-name signature after an Authenticode signature,
but the converse is not true.
To summarize, digital signatures and public-key encryption are used for code
signing and strong names. After the publisher or author assigns a strong name to
an assembly, the strong-name information, which includes a unique public key to
the assembly, is stored in the manifest. Each strong-named assembly is digitally
signed with a private key corresponding to its public key. This digital
signature can be verified by using the public key that is stored in the
manifest. You can also further sign an assembly with Authenticode on demand.
So much for theory-now let's examine a few realistic scenarios for signing
assemblies.
- You want to build a strong-named assembly
and sign it. Each file in the assembly is hashed, and the hash values are
stored in the manifest. Your public key is stored in the manifest. The
manifest file is digitally signed with your private key, and this signature
is stored in a nonhashed segment of the assembly.
- You develop some code that references some
other class of another strong-named assembly. The strong-named assembly's
public key is read from the manifest and hashed to create a unique
public-key token. This public-key token is a unique hash of the public key.
This public-key token is stored, along with the version, simple name, and
culture of the target assembly in the assembly references section (one entry
for each external assembly referenced by the current module). The .NET
Framework uses the assembly references section information at runtime to
identify and locate the target assemblies.
- You want to load or install a strong-named
assembly. The framework verifies the strongname signature when you install
the assembly in the GAC. If your assembly is not strongnamed, then the
framework verifies the signature whenever the assembly is loaded. If the
system cannot verify the signature, the assembly cannot be installed in the
GAC or you cannot load it. At runtime, whenever the system loads a module of
an assembly, first its contents are hashed, and then the system compares
that resultant value with the hash value stored in the manifest. If both
hashes do not match, the system will not load the module of the assembly.
When an application calls a method that is
found in another assembly, the runtime tries to locate the assembly by examining
binding policy information. The binding policy information is created at compile
time and is stored in the application's metadata. Binding is the operation of
mapping references to physical assemblies (typically dll's). The policy rules
are used to determine how binding is done. An application will be bound to an
assembly it was built and tested with, even if a newer version of the assembly
is available. Default binding policy is always used unless it is explicitly
overridden. Through the use of policy configuration files, binding policy can be
altered without having to rebuild the assembly or the application. There are
three policy resolution stages that the runtime may goes through before choosing
which assembly to load. These stages, in order, are:
Application Policy:
- Modifies binding rules for a single
application.
- Modified by Administrator.
- Can be provided with the application.
- Captured in XML configuration file in
application directory in the format of
assembly_file_name.file_extension.config.
Publisher Policy:
- Compiled into an assembly.
- Modifies binding rules for all
applications using a specific assembly.
- Provided by publisher as a statement about
compatibility.
- Captured in XML file wrapped as assembly
that is signed by the publisher.
- Deployable with msi (Microsoft
Installation) packages.
- Policy can be versioned.
- Stored in the global assembly cache.
- Found by naming convention.
- Forces applications to use the latest
version of an assembly.
- Overrides application policy unless
individual app refuses publisher policy.
Administrator Policy:
- Modifies binding rules for all
applications on the machine.
- Modified by administrator.
- Overrides all other policy on machine.
- Mostly used security fixes to existing
assemblies.
- Captured in machine.config file in CONFIG
subdirectory of the runtime install path,
C:\Windows\Microsoft.net\Frameworks\<Version>\config.
Because the CLR cache is a vital part of the
framework, we should delve a bit into the topic. The CLR cache consists of two
parts:
- Download cache can be managed with
al.exe (an assembly generation tool), Windows Installer, and Windows
Explorer. Downloaded code is placed in a special download cache and is not
globally available on the machine. Downloaded code does not affect existing
assemblies because of the versioning mechanism.
- Global assembly cache can be
managed with gacutil.exe (a global assembly cache management utility) and
mscorcfg.msc (the .NET Framework Configuration tool). It is a local cache of
the shared assemblies maintained by the .NET Framework for the locally
installed applications.
From the command prompt in the same directory
where MyComponent.dll resides, run gacutil.exe of .NET Framework SDK as shown
below.
gacutil /i MyComponent.dll
With this command you have placed the assembly in the GAC; and that assembly is
said to be a public, shared assembly. It can now be used from other assemblies
on the server, regardless of their location on the computer.
The .NET Framework requires developers to specifically identify as partially
trusted any strongnamed assembly and components that will be called by mobile
code downloaded from the Internet or intranet and any code to which their
security policy grants less than full trust.
Without the [assembly:AllowPartiallyTrustedCallers] attribute declaration (a
custom attribute of the System.Security.AllowPartiallyTrustedCallersAttribute
class) in the assembly configuration file, only fully trusted callers will be
able to use the assembly. This attribute makes the assembly callable from any
other partially or fully trusted assembly.
The AllowPartiallyTrustedCallers attribute removes the implicit LinkDemand (the
permission check conducted on the caller) for the FullTrust permission set that
is otherwise automatically placed on each assembly. Following are just some of
the .NET Framework assemblies that have the AllowPartiallyTrustedCallers
attribute declared:
- System.dll
- mscorlib.dll
- System.XML.dll
- System.Web.Services.dll
- System.Data.dll
- System.Windows.Forms.dll
- System.Drawing.dll
Refer to the Microsoft .NET Web site for
updates to the .NET security framework.
Also note that even with the AllowPartiallyTrustedCallers attribute enabled,
assemblies cannot be called by partially trusted code if one of the following
security attributes is declared in the code:
- [PermissionSet(SecurityAction.LinkDemand,
Name="FullTrust")]
- [SecurityPermission(SecurityAction.LinkDemand,
UnmanagedCode=true)]
-
[PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")]
- [FileIOPermissionAttribute(SecurityAction.RequestMinimum,
Unrestricted=true)]
Conclusion
Hope this article
would have helped you in understanding Signing an Assembly in C#. See other articles on the website on .NET and C#.
|
The Complete Visual
C# Programmer's Guide covers most of the major components that make
up C# and the .net environment. The book is geared toward the
intermediate programmer, but contains enough material to satisfy the
advanced developer. |