Signing in .NET

All about signatures in .NET

Here I will describe the different kinds of signing available for your .NET applications and assemblies as well as when and how to use them.

Different Kinds of Signatures

Briefly, there are two different ways you can sign your .NET assemblies in Windows. It's not immediately obvious from the properties tab, so I'm writing an article encompassing a lot of the details.

Strong Name Signing
Allows the assembly to be uniquely identified.
Can be performed on executables, assemblies, and manifests.
Code Signing
Authenticates the publisher and ensures code integrity.

Strong-Naming

Strong-named assemblies

A strong name is the combination of name, version number, culture information (optional), processor architecture (optional), public key, and digital signature.
These characteristics uniquely identify the assembly, providing many benefits.

When to use a strong-name

  • You want to reference it from another strongly-named assembly

    Except for certain full-trust scenarios, strong-named assemblies can only reference other strongly named assemblies.
    If the property wasn't transitive, integrity couldn't be guaranteed.

  • You want to reference multiple versions of the same assembly from within the same application

    To differentiate between versions, the strong name can be used since it uniquely identifies the assembly.

  • You want to load it as domain-neutral

    This is done when you want multiple applications to share the JIT-compiled code and reduces the memory footprint of the assembly. There are performance trade-offs associated and you shouldn't do this without a reason.

  • You want to add it to the GAC

    The GAC is a machine-wide cache where shared assemblies reside. Assemblies are generally located in the application directory but there are some cases where you might prefer placing them in the GAC.

    • To provide a shared location for multiple applications - regardless of domain-neutrality.
    • To prevent users from tampering with the file, as the containing directory is often protected.
    • To provide a backup location for runtime binding to find the proper assembly.
    • To make use of publisher policy, which allows you to specify redirects to newer versions of assemblies when you update your application.

How to create a strong-name

How to sign an assembly with a strong name
  • Using the Signing tab in the properties screen of your project in Visual Studio.

    The checkbox in the bottom half of the screen will enable a drop-down where you can select your .pfk file.

  • Using the Assembly Linker

    I don't think I'll ever use this. It seems to be for pre- visual studio 2008 projects.

  • Using attributes in AssemblyInfo.cs

    With AssemblyKeyFileAttribute you can specify a .snk file containing the public/private key-pair.

    With AssemblyKeyNameAttribute you can specify a key container by using the name of a Cryptographic Service Provider.

  • Using compiler options

    With the /keyfile option, you can specify your key file. Use a colon between the two: csc ... /keyfile:key.snk

Downsides of Strong-Naming

A strongly-named assembly cannot reference non-strongly-signed assemblies.

Best Practices for strong-naming

  • Rather than strongly-naming an assembly or executable, sign the manifest. This avoids the force transitivity of strong-naming.
  • If you must sign assemblies, avoid those that are not shared, as signing makes dependency management more cumbersome.
  • Because strong names do not expire, extra care must be taken to avoid compromising private keys.
  • Do not use the same pfx file to sign strong-names and to sign code. Because strong-name signatures do not establish trust, you can use a private key that is not password protected and avoid some encryption overhead.

Code Signing

Introduction to code signing

Code signing embeds a digital signature into the binary itself. It authenticates the publisher of the code, while also guaranteeing the integrity of the file by verifying it has not been modified since it was signed. Sometimes called authenticode.

Definitions

Code Signing Best Practices
Digital Signature
To create a digital signature, the cryptographic hash of a file is signed with the publisher's private key. This binds the publisher's identity to the file and provides a way to detect modifications to the data.
Digital Certificate
A digital certificate is a credential binding an entity to a public/private key pair.
Certificate Authority What are CAs?
A CA is a broker of trust who can issue certificates to verified third parties. Their certificate can be self-signed or even signed by another CA.
Authenticode
Embeds a digital signature in the non-executable portion of a file.
Can be applied to a variety of file formats, including
  • Portable executables: .exe, .dll, .sys, .ocx
  • Cabinet: .cab
  • Windows Installer: .msi, .msp
Timestamps Best Practices for Timestamping
Certificates usually have an expiration period. To allow software to continue to be used for longer periods of time, you can add a timestamp to your digital signature. If a digital signature's signed hash contains a timestamp issued from a TSA before the certificate's expiration date, Windows will consider the signature to be valid even if the certificate has since expired.
Chain of trust
A chain of trust is a series of validation steps that allow trust to be established without needing to maintain a list of all trusted publishers from the start. By maintaining one or more trusted entities at the root level, then allowing these to delegate trust, you obtain a chain effect which allows independent publishers to demonstrate their trustworthiness.
In this example, the provider of your operating system software will be the trust anchor, Microsoft. Microsoft signs a certificate for a software publishing company, say ACME. I verify my identity with ACME and they provide me with a signed certificate. When my software runs on your PC, it will validate that my certificate is signed by ACME, whose certificate is signed by Microsoft, which is trusted at the root level. Therefore, the chain has been verified and my software will be allowed to run.
Self-signed certificate
Developers can create their own sel-signed certificate which is not issued by a CA and is signed by it's own private key. This allows the developer to operate independently from any infrastructure components in the development environment and requires no overhead.
Commercially issued digital certificate
A certificate issued by a Certificate Authority comes with several advantages. The PKI is already set-up and clients will most likely already include the issuer in their chain of trust.
For test-signing, a certificate issued in-house can be a good middle-ground.

When to use code signing

Code signing allows for client computers to better determine trust relationships. A signed application with a trusted certificate chain is understood to be safer than an application without it. Certain types of drivers must be signed to be installed. Computer security policies may mandate or enforce that applications be signed before allowing them to run.

How to sign code

  1. Acquire a code-signing certificate

    You can purchase a certificate from a CA and use that for production releases, but for development you should create a self-signed certificate.

    First, create a root signing certificate. With this we will sign the code-signing certificate. This lets us have a long-term certificate and create short-lived ones as time goes on. In an enterprise setting, infrastructure may have created their own root signing certificate and assign development code-signing certificates to developers.

    #create your new root certificate
    $rootCert = New-SelfSignedCertificate -CertStoreLocation cert:\CurrentUser\My -DnsName "RootCA" -TextExtension  @("2.5.29.37={text}1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2") -KeyUsage CertSign, DigitalSignature
    
    #enter a password
    [System.Security.SecureString]$rootcertPassword = ConvertTo-SecureString -String "password" -Force -AsPlainText
    #browse to the certificate
    [String]$rootCertPath = Join-Path -Path "cert:\CurrentUser\My\" -ChildPath "$($rootcert.Thumbprint)"
    #export the private key
    Export-PfxCertificate -Cert $rootCertPath -FilePath "D:\RootCA.pfx" -Password $rootcertPassword
    #export the certificate
    Export-Certificate -Cert $rootCertPath -FilePath "D:\RootCA.crt"
    
    #Install the certificate to the Trusted Root Certification Authorities Store (requires running as admin)
    Import-Certificate -FilePath "D:\RootCA.crt" -CertStoreLocation "Cert:\LocalMachine\Root" -Verbose

    Once that is complete, you can create your own signing certificates

    #create your signing certificate
    $testCert = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My -DnsName "SignedByRootCA" -KeyExportPolicy Exportable -KeyLength 2048 -KeyUsage DigitalSignature, KeyEncipherment -Signer $rootCert -KeySpec Signature
    
    #export as before
    [String]$testCertPath = Join-Path -Path "cert:\LocalMachine\My\" -ChildPath "$($testCert.Thumbprint)"
    Export-PfxCertificate -Cert $testCertPath -FilePath "D:\testcert.pfx" -Password $rootcertPassword -ChainOption EndEntityCertOnly
    Export-Certificate -Cert $testCertPath -FilePath "D:\testcert.crt"
  2. Sign assembly code

    Now we can sign the assembly with the powershell command Set‑AuthenticodeSignature. Note that the last 2 parameters are optional. ‑IncludeChain All will include all of the signatures in the trust chain. ‑TimestampServer "..." adds a timestamp as described in the definition section.

    $cert = Get-PfxCertificate -FilePath "D:\testcert.pfx"
    Set-AuthenticodeSignature -FilePath D:\library1.dll -Certificate $cert -IncludeChain All -TimestampServer "http://timestamp.digicert.com"

Best Practices for code signing

  • Test-sign during development

    Software should be tested to ensure it installs and functions properly with a signature before it is sent for final signing and packaging.

    During testing, an application should be digitally signed with a certificate trusted only within the test environment. This will:

    • Ensure that accidentally leaked pre-release software is not signed with the enterprise certificate.
    • Decrease risks of the release certificate being compromised by decreasing the proliferation of the private key.
    • Minimize the chance of accidentally signing code that should not be signed.
    • Allow for developers to have access to a certificate with which they can sign code with no delay. This avoids the turnover time of having an infrastructure department sign code for the developers after every change.
    • In the event of a virus or compromised certificate, there is no need for a revocation of a public facing certificate.

    During development and testing, skip the timestamp step when digitally signing.

  • Use a code-signing service for production

    To protect the private key of a release certificate, set up a process that reduces the need to distribute it.

    Implement a submission and approval process to prevent the signing of unapproved or malicious code. By separating responsibilities between submitters, approvers, and signers, you reduce the risk of a rogue employee causing damage to your organization. Logging all such activity for auditing purposes allows examination and tracking of individuals and accountability if incidents do occur. Microsoft recommends that software be virus scanned before being signed as part of the release process.

  • Use a CA issued certificate for release signing

    This way you can make use of existing PKI and have your software trusted by users without the need to mess with certificate stores.


Common Issues

  • Invalid provider type specified

    The tools used by Visual Studio support the CSP library, but do not yet support the CNG library. By default, Export‑PfxCertificate will use a legacy CSP. You must include the parameter ‑KeySpec with the argument: Signature.

    Note that this is a requirement only for the private key being used to sign your code. It doesn't matter what parameters were used to create root certificates.

  • Cannot import the following key file:

    The key file may be password protected. To correct this, try to import the certificate again or manually install the certificate to the Strong Name CSP with the following key container name:

    The solution to this issue is the same as the previous one. You might see this when strong-naming with a certificate that otherwise works with code-signing. In that case, the certificate was likely created with ‑KeySpec KeyExchange

  • Cannot find the certificate and private key for decryption

    To be used by Visual Studio to sign code, a certificate cannot include certificate chaining information. To ensure this is the case, when running Export‑PfxCertificate include the parameter ‑ChainOption with the argument: EndEntityCertOnly.

    As in the previous issue, this is a requirement only for the private key being used to sign your code. It doesn't matter what parameters were used to create root certificates.

  • Friendly assembly reference is invalid. Strong-name signed assemblies must specify a public key in their InternalsVisibleTo declarations.

    Because strongly-named assemblies are identified by their strong-name, all of that information must be referenced together. You will need something like:

    
         [assembly: InternalsVisibleTo("ProjectwiseApi.Tests, PublicKey=...")]

    The hex value for PublicKey can be obtained with Sn.exe.

    Sn -Tp "name of assembly file"

Comments

Popular posts from this blog

Creating a custom configuration section in .NET