Search code examples
.net.net-2.0code-signingstrongnamesn.exe

Can sn.exe utilize the Windows certificate store?


For signing an assembly with sn.exe in .NET, is it possible to specify a public key for which the private key is contained only within the Windows CryptoAPI keystore?

I see the option for specifying the CSP name, and the container name.

Are there values to use to access the Windows certificates? (i.e. those accessible from Windows EFS, Outlook, Internet Explorer, etc.)

Thank you.

Comment: Specifically, I am asking this because there are a few keys which do not have the private key marked as exportable, so I cannot export a .pfx and follow that route.


Solution

  • I had the same question at the end of this week.

    YES, it can be configured to use the Windows Certificate store and if you're being truly security conscious you should absolutely do this. It makes it very hard to accidentally leak the private key (and if you use a Smart Card like a Yubikey, it makes it impossible to leak the private key -- the OS never sees it).

    I documented how to do this in two different, but related, manners in posts on my blog.

    If you're just using a certificate in your personal certificate store, but not using a Smart Card, it's relatively easy.

    In PowerShell, you need to get the details of your certificate:

    Set-Location "cert:\Path\To\Your\Certificate"
    # Usually "cert:\CurrentUser\My" is what you want
    $cert=Get-Item ".\(your-certificate-thumbprint)"
    

    You need to determine the Key Container Name and CSP that's used to access that key container (if it's not a smart card, the default CSP works)

    $cert=Get-Item .\(ThumbprintOfYourKey)
    $cert.PrivateKey.CspKeyContainerInfo | fl *
    

    This will produce something similar to the following:

    MachineKeyStore        : False
    ProviderName           : Microsoft Base Smart Card Crypto Provider
    ProviderType           : 1
    KeyContainerName       : c0f031c2-0b5e-171b-d552-fab7345fc10a
    UniqueKeyContainerName : c0f031c2-0b5e-171b-d552-fab7345fc10a
    KeyNumber              : Signature
    Exportable             : False
    HardwareDevice         : True
    Removable              : True
    Accessible             : True
    Protected              : True
    CryptoKeySecurity      : System.Security.AccessControl.CryptoKeySecurity
    RandomlyGenerated      : False
    

    In my case, I'm using a Yubikey, so the CSP is "Microsoft Base Smart Card Crypto Provider". This means in order to strong name sign my code, I need to run:

    sn.exe -c "Microsoft Base Smart Card Crypto Provider"
    

    At some point before I build (only once, it needn't be run every build, however I have linked to some scripts to help with that in the second post on this subject).

    There are two options from here: you tell sn.exe to create a key that contains only the public key and delay sign with that key (check the box at the bottom of the "Signing" tab in the project properties), then post-build, sign using sn.exe -Rc "your-container-name" "key.snk" or you can use the easy way: AssemblyKeyNameAttribute in the AssemblyInfo.cs file as follows:

    [assembly: AssemblyKeyNameAttribute("Your Key Container Name")]
    

    The compiler will handle everything else for you. Just bear in mind that you need to make sure your CSP is set using sn.exe -c before you try to build or you will get a Keyset not found error on build (along with the name of your key container).