0% found this document useful (0 votes)
7 views

lowleveldesign.wordpress.com-How to securely sign NETassemblies

The document discusses the process of securely signing .NET assemblies using strong name signatures and Authenticode signatures. It explains the structure of strong name signatures, the tools required for signing, and the importance of verifying signatures to ensure code integrity. Additionally, it highlights the potential vulnerabilities in signature verification and emphasizes the need for proper access controls on binaries.

Uploaded by

Mamoon Riaz
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

lowleveldesign.wordpress.com-How to securely sign NETassemblies

The document discusses the process of securely signing .NET assemblies using strong name signatures and Authenticode signatures. It explains the structure of strong name signatures, the tools required for signing, and the importance of verifying signatures to ensure code integrity. Additionally, it highlights the potential vulnerabilities in signature verification and emphasizes the need for proper access controls on binaries.

Uploaded by

Mamoon Riaz
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 5

lowleveldesign.wordpress.

com /2017/03/07/how-to-securely-sign-dotnet-assemblies/

How to securely sign .NET assemblies


Sebastian Solnica ⋮ ⋮ 07/03/2017

TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=769a8f10a7f072b4

If the above line means anything to you, you are probably a .NET developer. You also probably know that
the hex string at the end represents a public key token, which is a sign that the assembly has a strong
name signature. But do you know how this token is calculated? Or do you know the structure of the
strong name signature? In this post, I will go into details how strong naming works and what are its
shortcomings. We will also have a look at certificate-based signatures and, in the end, we will examine
the assembly verification process.

Strong name signature and Public Key token

A valid strong name signature ensures the recipients that the assembly they received has not been
tampered. It also uniquely identifies a given assembly. Though, it does not say anything about the identity
of the signer. Two parts of the assembly binary code play role in the strong name verification process.

The first part is the public key, which is part of the #Blob stream in the assembly metadata (screenshot
shows a part of the dnSpy window):

The image below lists the elements which build the Public Key block:

1/5
The Public Key Token, which is used to uniquely reference the assembly, is a hex representation of the
low 8 bytes of the SHA-1 hash of the Public Key in a reverse byte order. For my test assembly, it equals:
769a8f10a7f072b4 (SHA-1(00 24 00 00 0c 80 … c8 8a c1 b1) =
9aa4de0a96ada8d83d6d7678b472f0a7108f9a76).

The second part is the RSA signature of the hash of the assembly content. Before counting this hash, we
need to fill with zeros the following bytes of the file: the authentication signature entry (we will get to it in a
moment), the strong name blob and the PE header checksum. The signature is later stored in the PE file
text section at an offset saved in the COR20 header:

2/5
To calculate the RSA signature, we need to own the private key corresponding to the public key listed in
the previous paragraph. If you would like to see C# code implementing the validation, have a look at the
StrongNameSigner.cs file from the dnLib library.

Having examined the strong name signature structure, let’s focus on tools we may use to create it. We
start by generating the .snk file, which will store the RSA keys details (if you are interested in the details
of the .snk file format, have a look at my 010 editor template):

1sn.exe -k 2048 TestLib.snk

We should keep the generated .snk file secret. Next, depending on our scenario, we may use either the
C# compiler (csc.exe) or assembly linker (al.exe). Both accept the /keyfile parameter, in which we
provide the path to the just generated .snk file, e.g.

1csc.exe /keyfile:TestLib.snk /t:library TestLib.cs

This command will generate a signature based on the SHA-1 hash of the file content. Nowadays, SHA-1
is considered inadequate for secure hashing and it is highly recommended to use SHA-2 digest. To sign
our assembly using SHA-2 hash, we first need to extract the public key part of the .snk file:

1sn.exe -p TestLib.snk TestLibPubKey.snk sha256

Then delay the private key signing (the strong name signature block will be zeroed and the Strong Name
Signed flag will not be set). Both csc.exe and al.exe accept the /delaysign+ parameter for this purpose:

1csc.exe /keyfile:TestLibPubKey.snk /delaysign+ /t:library TestLib.cs

Finally, we need to re-sign the assembly using the private key:

1sn -Ra TestLib.dll TestLib.snk

If you have a strong named assembly and would like to migrate the signature, have a look at this article.

Authenticode signature

Autheticode signature, as its name suggests, is used to authenticate the owner of the assembly. It also
guards the integrity of the assembly. The size and the location of the signature is stored in the PE

3/5
optional header:

The Force Integrity flag, I also marked on the image, is used to force the loader to always check the
signature of the given assembly (Windows skips the signature verification, except for drivers and modules
loaded into the protected processes). For native code, there is a special linker option to enable it. I
haven’t found such an option in either csc.exe or al.exe and used dnSpy to set it (I needed to first delay
signed the assembly, set the flag, and re-sign it).

To create an Authenticode we need to possess a certificate containing a private key (.pfx format is
required). For test purposes a self-signed certificate will work (unless you set the Force Integrity flag), but
for release deployments you should obtain one from a trusted provider. A sample command to sign an
assembly using a certificate file might look as follows:

1signtool sign /v /ph /fd sha256 `


2/f .\fileSignature.pfx `
3/p {certificate-password} `
4/t http://timestamp.verisign.com/scripts/timstamp.dll .\TestLib.dll

Remember to create the strong name signature before creating the Authenticode.

Signature verification

.NET, starting from the version 3.5, does not perform the strong name signature validation when an
assembly is loaded into a full-trust application domain. This basically means that in full-trust app domains
we may replace a strong named assembly with an assembly containing only a public key and no one will
notice. We may change this behavior by either enabling the bypassTrustedAppStrongNames property
of the runtime in the configuration file, or by zeroing the AllowStrongNameBypass value of the
HKLM\SOFTWARE\Microsoft.NETFramework key (use Wow6432 for 32-bit apps on a 64-bit system).

Even with those settings in place, there is still a way to load a partially signed assembly into our
application app domain. This mechanism is used when working with delay signed assemblies. During
development, we often do not want to fully sign the assemblies on each build (the private key should be
kept safe in one place), but at the same time, we want the assemblies to behave as they had strong
names. This could be achieved by adding our assembly name and public key token to the registry under
the key HKLM\Software\Microsoft\StrongName\Verification, for example:

4/5
HKLM\Software\Microsoft\StrongName\Verification\TestLib,8FCE6031CC56162D, or by using sn
command with the -Vr option. Only system administrator is allowed to modify the verification list.

When it comes to the Authenticode verification, .NET performs it only if the Force Integrity flag is set in
the PE header.

Fortunately, we do not need to rely only on the automatic verification; we can perform the verification on
our own. The same tools we used to sign the assemblies, provide us with ways to validate them. To
check the strong name signature, we may use sn –vf (the -f option forces sn to check the signature even
if it is disabled in the registry). Adding the key file as an argument is optional, but recommended if we do
not use the Authenticode. Example usage:

1sn -vf TestLib.dll TestLib.snk

For Authenticode verification, we may use either signtool or sigcheck. Example calls might look as
follows:

1
2signtool verify TestLib.dll
(add /pa if you are using a self-signed root certificate)
3sigcheck -i TestLib.dll
4(-i will show the certificate chain used to sign the assembly)
5

Sigcheck can also recursively scan directories and verify all found binaries. I encourage you to check its
help to find more interesting options (like submitting the binary hash to VirusTotal).

Summary

Given that it is so easy to skip the signature verification, you may doubt the whole concept of signing
binaries. Consider though that the verification while loading is not that important. If a malicious person
gets write access to the binaries, signatures will not protect you against his/her activities. But… signing
the assembly is the only way to prove that the code in the assembly is legitimate and has not
been tampered (I encourage you to use both: strong name signature and Authenticode). We
should perform the first verification after the client receives the binaries (this is often done by the
installers) – to be sure that no tampering in transit happened. Next, it is extremely important to set valid
access rights on the folder where binaries reside – only authorized persons should be allowed to modify
the files. Finally, whenever there is a problem with our application, we should ask the client to verify the
signatures before filling the bug report – only that way we could be sure that the bug is really ours.

5/5

You might also like