C#: Why Sign an Assembly

C#: why sign an assembly?

Why would the previous author have signed the assemblies in this way?

No idea, maybe he wanted all his assemblies to be signed with the same key.

Is signing assemblies necessary and what would be wrong with not signing it?

No, it is not necessary but it is a mechanism allowing you to ensure the authenticity of an assembly. It allows you to ensure that an assembly hasn't been tampered with and indeed it origins from this author. It is also necessary if you want to put them into the GAC.

What disadvantages are there in signing assemblies - does it cause delays?

Signed assemblies can only load other signed assemblies. Also they are tied to a specific version meaning that you need to use binding redirects or recompile the application if you wanted to use a different version. There's a little performance overhead as well due to the verification of the signature but it is so little that you shouldn't be concerned about.

C# Sign Assembly Using Code

The Mono project provide it's own sn tool which is entirely build in C# and depends on Mono.Security.dll (which works on Windows too). All of this is open source under the MIT license.

Signing C# Assembly in IDE vs with signtool.exe

However, my build process currently obfuscates my EXE and then signs it with signtool.exe and a Comodo certificate

I'll assume you are referring to Code Signing rather than Strong Naming.

Am I correct in saying that I'm already doing whatever it is that the project signing tab in VS already offers?

If you are referring to the manifest signing in the Signing tab then yes.

Both result in manifest signing (Code Signing) and in the case of obfuscation, has to be performed after the obfuscation step (because the latter removes the signing).

Tell me more

  • Brower, J, "Code Signing in Visual Studio", https://www.linkedin.com/pulse/code-signing-visual-studio-jason-brower, retrieved 2016/11/22

  • Microsoft, "Signing Page, Project Designer", https://msdn.microsoft.com/en-us/library/0k50fs3b.aspx, retrieved 2016/11/22

Why are signed assemblies slow to load?

Have a look at these links:

  • https://web.archive.org/web/20120812062059/http://blogs.technet.com/b/markrussinovich/archive/2009/05/26/3244913.aspx - The Case of the Slow Keynote Demo (Mark Russinovich’s Blog)

    Confident now that the cause of the startup delay was due to .NET seeing that Stockviewer.exe was signed and then checking to see if the signing certificate had been revoked, I entered Web searches looking for a way to make .NET to skip the check, since I knew that the keynote machines probably wouldn’t be connected to the Internet during the actual keynote. After a couple of minutes of reading through articles by others with similar experiences, I found this KB article ...

    ... checking of assembly digital signatures: create a configuration file in the executable’s directory with the same name as the executable except with “.config” appended

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
    <runtime>
    <generatePublisherEvidence enabled="false"/>
    </runtime>
    </configuration>

They might help. It could be that the config on your system means that the .NET framework is doing a lot of extra work to verify the assembly. If this is the case, then you can configure it to not be so picky.

  • https://web.archive.org/web/20130409115103/http://blogs.msdn.com/b/tess/archive/2008/05/13/asp-net-hang-authenticode-signed-assemblies.aspx - ASP.NET Hang: Authenticode signed assemblies (If broken it is, fix it you should, by Tess Ferrandez, ASP.NET Escalation Engineer)

How to fix Referenced assembly does not have a strong name error

To avoid this error you could either:

  • Load the assembly dynamically, or
  • Sign the third-party assembly.

You will find instructions on signing third-party assemblies in .NET-fu: Signing an Unsigned Assembly (Without Delay Signing).

Signing Third-Party Assemblies

The basic principle to sign a thirp-party is to

  1. Disassemble the assembly using ildasm.exe and save the intermediate language (IL):

    ildasm /all /out=thirdPartyLib.il thirdPartyLib.dll 
  2. Rebuild and sign the assembly:

    ilasm /dll /key=myKey.snk thirdPartyLib.il

Fixing Additional References

The above steps work fine unless your third-party assembly (A.dll) references another library (B.dll) which also has to be signed. You can disassemble, rebuild and sign both A.dll and B.dll using the commands above, but at runtime, loading of B.dll will fail because A.dll was originally built with a reference to the unsigned version of B.dll.

The fix to this issue is to patch the IL file generated in step 1 above. You will need to add the public key token of B.dll to the reference. You get this token by calling

sn -Tp B.dll 

which will give you the following output:

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.33440
Copyright (c) Microsoft Corporation. All rights reserved.

Public key (hash algorithm: sha1):
002400000480000094000000060200000024000052534131000400000100010093d86f6656eed3
b62780466e6ba30fd15d69a3918e4bbd75d3e9ca8baa5641955c86251ce1e5a83857c7f49288eb
4a0093b20aa9c7faae5184770108d9515905ddd82222514921fa81fff2ea565ae0e98cf66d3758
cb8b22c8efd729821518a76427b7ca1c979caa2d78404da3d44592badc194d05bfdd29b9b8120c
78effe92

Public key token is a8a7ed7203d87bc9

The last line contains the public key token. You then have to search the IL of A.dll for the reference to B.dll and add the token as follows:

.assembly extern /*23000003*/ MyAssemblyName
{
.publickeytoken = (A8 A7 ED 72 03 D8 7B C9 )
.ver 10:0:0:0
}

What exactly is an Assembly in C# or .NET?

An assembly is the compiled output of your code, typically a DLL, but your EXE is also an assembly. It's the smallest unit of deployment for any .NET project.

The assembly typically contains .NET code in MSIL (Microsoft Intermediate language) that will be compiled to native code ("JITted" - compiled by the Just-In-Time compiler) the first time it is executed on a given machine. That compiled code will also be stored in the assembly and reused on subsequent calls.

The assembly can also contain resources like icons, bitmaps, string tables and so on. Furthermore, the assembly also contains metadata in the assembly manifest - information like version number, strong name, culture, referenced assemblies and so forth.

In 99% of your cases, one assembly equals a physical file on disk - the case of a multi-file assembly (one assembly, distributed across more than a single file) appears to be a rather odd-ball edge case which I've never encountered so far in my 5+ years of .NET development.

In a multifile assembly there would still be only one assembly manifest in a DLL or EXE and the MSIL code in multiple netmodule files.

Signing a DLL assembly with public key token

As Luaan said above, what I wanted to do is not possible. The private key is just that - private. The public key is designed for verification purposes, to ensure that the assembly has not been modified.

Removing the strong name was an option, as Luaan said in one of his comments. This did not work for me, however, as there were many dependencies, making the method impractical.

As a temporary solution, I've left the assembly file delay signed, and disabled strong name validation in the registry for the DLL concerned.

To disable strong name validation, add this key to the registry:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification\<filename without extension>,<public key token>

For 64-bit systems, you need to add this key as well:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\<filename without extension>,<public key token>

Note that disabling strong name validation is recommended for testing purposes only, as this could represent a security issue.

Remove signing from an assembly

In this case, the problem is a project "common properties" reference.

Inside of the project .csproj file is this innocuous little line:

<Import Project="..\build\CommonProperties.targets" />

Unfortunately, the file (CommonProperties.targets) instructs VS to re-write the properties, but it does not provide any clear indication in the user interface that this is taking place.

The solution is to go into the CommonProperties.targets file and delete the following lines:

<!-- delay sign the assembly if the PrivateKeyPath property is not specified -->
<PropertyGroup Condition=" '$(PrivateKeyPath)' == '' And '$(PrivateKeyName)' == ''">
<AssemblyOriginatorKeyFile>..\public_key.snk</AssemblyOriginatorKeyFile>
<DelaySign>true</DelaySign>
</PropertyGroup>

<!-- sign the assembly using the specified key file containing both the private and public keys -->
<PropertyGroup Condition=" '$(PrivateKeyPath)' != '' ">
<AssemblyOriginatorKeyFile>$(PrivateKeyPath)</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
</PropertyGroup>

<!-- sign the assembly using the specified key container containing both the private and public keys -->
<PropertyGroup Condition=" '$(PrivateKeyName)' != '' ">
<AssemblyOriginatorKeyFile></AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
</PropertyGroup>

Replace those lines with the following:

  <PropertyGroup>
<DelaySign>false</DelaySign>
<SignAssembly>false</SignAssembly>
</PropertyGroup>


Related Topics



Leave a reply



Submit