Strong Naming and Assembly Signing in C# Explained

Strong naming in C# is a mechanism that gives a .NET assembly a globally unique identity and verifiable integrity using cryptographic key pairs. This article covers what strong naming and assembly signing entail, the technical components that make up a strong name, the distinction between identity and security, and modern best practices for implementing signing in .NET applications and libraries.

What is a Strong Name?

A strong name consists of four core components that uniquely identify an assembly:

  1. Simple text name: The basic file or assembly name (e.g., MyLibrary).
  2. Version number: The major, minor, build, and revision numbers (e.g., 1.0.0.0).
  3. Culture info: The specified language/locale, if applicable (e.g., neutral or en-US).
  4. Public key and digital signature: A cryptographic signature generated using the developer's private key, accompanied by the corresponding public key token.

When an assembly is signed, the runtime uses these elements to ensure that references resolve to the exact, expected binary.

How Assembly Signing Works

Assembly signing utilizes public-key cryptography (typically RSA):

  1. Key Generation: A key pair containing a public key and a private key is generated (typically stored in a .snk file).
  2. Hashing: During compilation, the compiler generates a cryptographic hash of the assembly's contents.
  3. Signing: The compiler encrypts this hash with the private key to create a digital signature.
  4. Embedding: The signature and the public key are embedded directly into the compiled assembly's manifest (metadata).
  5. Verification: When another assembly loads the signed binary, the .NET runtime decrypts the signature using the embedded public key and compares the resulting hash against a newly generated hash of the file contents. If they match, the assembly's identity is verified.

Primary Uses of Strong Naming

  • Guaranteed Unique Identity: Two assemblies with the same filename cannot conflict if they have distinct strong names.
  • Strict Dependency Resolution: An assembly with a strong name can only reference other strong-named assemblies. This prevents a signed binary from depending on an untrusted or easily swappable unsigned assembly.
  • Global Assembly Cache (GAC): In legacy .NET Framework applications, an assembly had to be strong-named to be installed into the GAC for system-wide sharing.

Strong Naming vs. Authenticode Signing

A common misconception is that strong naming provides security or authenticates the publisher. It does not:

  • Strong Naming only ensures identity and integrity. It confirms that the assembly has not been modified since it was built and that it came from the owner of the private key. It does not prove who that owner is.
  • Authenticode (Code Signing) uses certificates issued by a trusted Certificate Authority (CA) to prove publisher identity and establish legal trust.

For actual security and publisher verification, Authenticode signing must be used instead of, or in addition to, strong naming.

How to Sign an Assembly in Modern .NET

In modern .NET projects, signing can be configured directly inside the .csproj file.

First, generate a key file using the .NET CLI or the Developer Command Prompt:

sn -k MyKey.snk

Then, add the following configuration to your project file:

<PropertyGroup>
  <SignAssembly>true</SignAssembly>
  <AssemblyOriginatorKeyFile>MyKey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

Alternatively, open-source projects often use Public Signing (or OSS signing), which embeds the public key at build time without requiring access to the private key:

<PropertyGroup>
  <SignAssembly>true</SignAssembly>
  <AssemblyOriginatorKeyFile>MyPublicKey.snk</AssemblyOriginatorKeyFile>
  <PublicSign>true</PublicSign>
</PropertyGroup>

Current Best Practices

  • For NuGet Library Authors: The .NET team recommends strong-naming publicly distributed NuGet packages. Because strong-named assemblies can only reference other strong-named assemblies, failing to sign a public library prevents developers who build strong-named applications from consuming it.
  • For Private Applications: If you are building standalone applications, microservices, or internal tools, strong naming is generally unnecessary unless specific enterprise architectural requirements dictate its use.