none
Give assembly strong name using certificate store certificate RRS feed

  • Question

  • My company has provided me with a code signing certificate on a smart card whose private key is protected with a pin and is not exportable.  I can use "signtool" to sign the assembly with the smart card cert and I can "Sign the ClickOnce manifests" with the smart card cert as well.  In both cases, there is a straightforward option to choose a store certificate (the smart card cert is loaded into my personal store upon card insertion).  I am prompted for my pin when performing either action and the signing completes successfully.

    Creating a strong name using the smart card cert seems to be a different story.  I can use

         sn -c [my smart card CSP]

    which I know is effective because the key container name (blank in my case...is that a problem?) and the unique key container (a GUID) can be used as such

         sn -pc "[GUID or blank]" mytest.pub

    and the error is "Failed to extract public key from key pair -- Key does not exist."

    I said "effective" above because if I switch to my smart card CSP and then pass "sn -pc" something besides empty quotes or the correct GUID, I get the error "Failed to extract public key from key pair -- Keyset does not exist."  Notice the difference is "Key does not exist" vs. "Keyset does not exist."

    I've tried appending the subject name of the certificate on the smart card in every way I can imagine such as

         sn -pc "[GUID]\[cert name]" mytest.pub
         sn -pc "[GUID]/[cert name]" mytest.pub
         sn -pc "[GUID]:[cert name]" mytest.pub
         sn -pc "[GUID],[cert name]" mytest.pub

    but with no luck.  I've also tried specifying the key container name, subject name, and combinations in the assembly attribute AssemblyKeyName but I get the error "The key container name '[whatever I try]' does not exist."

    This link (http://www.dotnetthis.com/Articles/SNandSmartCards.htm) seemed to be promising but if I can't use "sn -pc" to extract the "public portion of the key pair" (or whatever it's supposed to be doing), I'm obviously stuck.  I can export the "public key" from my smart card cert using the MMC certificate console but using that in the process described in the link above (specifying it in the assembly attribute AssemblyKeyFile) gives me a build error "Cryptographic failure while signing assembly '...' -- 'Bad Version of provider.'  What's the difference between the output of 'sn -pc' and my certificates public key?

    • Edited by IkeLikesToBike Thursday, December 4, 2008 2:05 AM Change 4
    Thursday, December 4, 2008 1:59 AM

All replies

  • I think you're confusing certificates with strong names, they are unrelated.  The article you linked talked about smart cards as a way to protect the private key.  A strong name allows the .NET runtime to verify that the assembly wasn't tampered with.  It doesn't require a certificate.  A certificate allows your customer to verify that the binary she got from you was indeed produced by your company.  That requires a certificate published by an authority like Verisign.  You give assemblies a strong name with sn.exe, a certificate with signtool.exe
    Hans Passant.
    Thursday, December 4, 2008 11:38 AM
    Moderator
  • I am not confusing them at all.  I left out one point which may help clear up why I think should be able to do what I describe above. 

    I have had absolutely no problem creating a strong name using a standard X509 PKI certificate whose public and private keys I hold within a password protected PFX file.  I generated this PFX PKI cert myself based on our enterprise's certificate authority (it has absolutely nothing to do with sn or an SNK key pair file) and am able to use it in all three scenarios I describe in my original post (signtool, sign ClickOnce manifest, and create strong name).  In fact, as most people are probably aware of, Visual Studio provides the option of using a PFX's PKI certificate to create a strong name. 

    I tried to post technical details, errors, etc. above in case someone has actually attempted to get this to work and has run into the same issues I have but now I'll try to simplify what I am asking.

    QUESTION:  Since I can create an assembly strong name from a standard PKI software signing certificate stored in a PFX file (not an SNK generated with sn!) using Visual Studio, how can I create a strong name using a standard PKI software signing certificate stored in a certificate store? 

    Hans, If you aren't familiar with creating a strong name using a standard PKI software signing cert in a PFX, I can provide you with detailed instructions.  Also, keep in mind that in every other case I've tested (signtool, sign ClickOnce manifest, etc.), it doesn't matter that the CSP protects the private key with a smart card and PIN, as long as the certificate has been pushed to a certificate store, it is available to signtool, etc. and the CSP takes over (prompts for PIN) when signtool, etc. tries to access the private key.

    Incidentally, the one thing someone could clear up for me that I admit I am confused about (but shouldn't affect the issue I am describing) is:  What is the difference between the key pair (not the entire file) but the key pair stored in a PKI certificate and the key pair stored in an SNK file?

    Any help is greatly appreciated!
    • Edited by IkeLikesToBike Thursday, December 4, 2008 6:05 PM Change 1
    Thursday, December 4, 2008 12:19 PM
  • I have now been able to successfully create an snk-compatible public key file by getting an X509Certificate2 reference to my smart card certificate, invoking

        ((RSACryptoServiceProvider)[cert].PublicKey.Key).ExportCspBlob(false);

    and appending the 12 byte header that "sn [-e | -p | -pc]" public key files contain.  I am now able to select that file in Visual Studio and delay sign my assembly.  "sn -Tp" returns the proper public key and a public key token.  However, when I use "sn -c" to switch to my smart card CSP and then try to resign using "sn -Rc", I get the same error I got when trying to initially extract the public key:  "Failed to extract public key from pair -- Key does not exist."

    I think the problem boils down to sn not being able to find a cert in key container.  Again, I have verified that sn is set to the correct CSP.  I am now verifying that I have the correct key container name.
    Thursday, December 4, 2008 6:17 PM
  • Any updates on this? I'm in the same boat.
    Friday, October 1, 2010 6:03 PM
  • I realize I'm replying to a post from 2008, but I ran into almost the exact same issue this last month trying to strong name sign an assembly using a key from my Yubikey.

    This set of posts got me most of the way to the solution (which I've documented here).

    > What is the difference between the key pair (not the entire file) but the key pair stored in a PKI certificate and the key pair stored in an SNK file?

    I'm only partly sure I understand what you're asking here, but I'm guessing it has to do with the typical .snk file vs an .snk file used with a key container, so that's what I'll answer (if I've got it wrong, let me know and I'll try to answer correctly).

    When you're using a key container, specifically one where the private key is not exportable (whether enforced by the OSes crypto provider in your certificate store or a hardware device like a TPM Virtual Smart Card or a Yubikey/typical Smart Card), is that the .snk file does not contain the keyset, it only contains the public key.  To get 'csc.exe' or any of the tools that can be used to strong name sign an assembly to sign the assembly (fully), you need to point them at the key container.

    If you're using 'sn.exe' to sign your files, you'd do this by telling Visual Studio to 'Delay Sign' with the .snk, which only contains the public part of the keyset.  You'd then run sn.exe -Rc 'container name' to "resign" the library/executable with the private key from the container.

    Simple, right?  No.  To get sn.exe to do this, you need to get the public key into a .snk file.  This requires you to run sn.exe -pc "container" key.snk.  The tool, however, will not find the keyset unless you've set it up to use the CSP that the device which stores your key is using (usually this is the Microsoft Base Smart Card Crypto Provider, but I included instructions on the above link on how to look that up).  You also need to make sure that the sn.exe -c "Microsoft Base Smart Card Crypto Provider" is run before you run your build because for whatever reason, delay signing with that .snk doesn't work without access to the key it came from (I'm not sure what *that's* all about).  It's a good idea to do this *anyway* if you use the method I documented in my followup post, which negates the need to delay sign/resign in favor of just using the AssemblyKeyNameAttribute to strong name sign the assembly at build.

    To make it even more confusing, when you've switched to the Smart Card crypto provider, several functions of sn.exe stop working, such as generating a key with "sn.exe -k".  I assume this is because a smart card is incapable of exporting a key set, since the private key cannot ever be exported from a smart card (at least through the OS/software -- that's a major point of a smart card).  So there are times that you have to reset the provider while you're working and you have to remember to reset it back (and, if you review the second link, it appears the specific version you use to switch it back is also important). And the best part is that the error messages that sn.exe gives don't appear to be in any way related to what has actually gone wrong.

    There's also no way that I could find to get any of the tools (sn.exe, ilasm.exe, csc.exe, Visual Studio, etc) to tell you what CSP it's currently configured to use.  And I, similarly, can't find a way to specify the CSP in a similar manner to how you can specify the key container at the point at which you are specifying a key container. It appears to always require this extra sn.exe -c "CSP" command to be run beforehand.  Oh, and that has to be run as an elevated user, however if you just throw your hands up in the air and run Visual Studio as an elevated user, the sn.exe -c "CSP" command will work, but it will be unable to find the keyset because the elevated user can't see your regular account's personal key store (insert profanity here).

    I'd hazard a guess that a lot of what you've run into has to do with the wonky operation of this tool.  That's certainly what sent me down a few rabbit holes trying various solutions that had nothing to do with the problem because the problem was incomprehensible on its own.

    The worst part of all of this is that Strong Naming serves a security purpose to prevent .dll hijacking, which is a really common method that hostile actors use to attack software.  By far, the least hassle way of using it is to just store the public/private keypair alongside your code in a file, which is what almost everyone I've talked to does (almost always with no password protection).  This ensures a high probability that you'll forget to add it to your .gitignore (or the less security knowledgeable among us won't even realize it's necessary) and you'll push that key to a git repo -- and in the case of open source software, it'll be a public git repo.  Using a key container makes that impossible if the private key is set to non-exportable and makes it require finding a vulnerability in the OS to get at that key.  Using a Smart Card in combination with a key container mitigates even that OS vulnerability situation since the card is protecting that private key as a function of software independent of the OS.

    Honestly, if I weren't so hell-bent on using my code signing key, which I created with a CSR generated via a Linux Live CD disconnected from the internet -- i.e. the private key has *never* existed outside of a hardware protected medium -- I would have probably thrown my hands up and just gone back to using a generated key and "being careful".  There's no value to using a code signing key for strong naming, other than it gives me a single key that I'm using for every cryptographic operation that it's allowed to be used for ... and because I fully understand the value of the private key to an attacker, I would never handle that key in a manner that isn't ridiculously secure, so it causes that behavior to trickle down to everything it touches.

    Sunday, September 17, 2017 10:12 PM
  • I realized after I wrote the last post, that I failed to answer the first, bold, question:

    Since I can create an assembly strong name from a standard PKI software signing certificate stored in a PFX file (not an SNK generated with sn!) using Visual Studio, how can I create a strong name using a standard PKI software signing certificate stored in a certificate store? 

    Here's how that's done (this is documented more thoroughly, again, at the link on my blog)

    First, you have to determine what CSP the key is using.  This is pretty easy to get.  Hop into PowerShell:

    cd cert:\Path\To\Your\Key
    $cert=Get-Item .\(ThumbprintOfYourKey)
    $cert.PrivateKey.CspKeyContainerInfo | fl *

    You're going to see output like this:

    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

    The "ProviderName" is the key provider, so now you have to tell sn.exe to use that provider.  This has to be done in an elevated command prompt (I've included the PowerShell equivalent -- remove the "." if you're in a Visual Studio Developer Command Prompt".  The "KeyContainerName" is the container name

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

    Now, you have choices.  You can export the public portion of the key, set Visual Studio to use that key, and check the box for "Delay Signing".  That is done as follows:

    . sn.exe -pc "$($cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)" key.snk sha256

    And then going into Visual Studio, Project Properties, Signing, check the box for "Sign this Assembly" along with "Delay Sign" and point it at the key.snk created with the above command.  The lousy part is that you can't launch/debug this assembly until it is properly signed.

    To then properly sign the assembly, you'll run:

    sn.exe -Rc MyLibrary.dll "$($cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)"

    You can do this all via a set of pre/post build steps.

    Alternatively

    You can use the AssemblyKeyNameAttribute, which only requires that KeyContainerName and running the "sn.exe -c 'CSP of that Key'"I've documented that, including a script that elevates during pre-build to execute the sn.exe -c 'CSP of that key' command since it requires elevation, but you can't just use an elevated Visual Studio because the elevated context cannot access your user's personal key store.  Strictly speaking, this needn't be run every build, setting the CSP via sn.exe -c "CSP of that key" once (though I don't think it persists across boots) is enough.

    More thorough instructions on how to use the AssemblyKeyNameAttribute are in the follow-up post on my blog.






    • Edited by mdip Sunday, September 17, 2017 10:28 PM Neglected to leave the second link about AssemblyKeyNameAttribute
    Sunday, September 17, 2017 10:26 PM