locked
How can I choose from multiple smartcard readers and install to an existing CAPI container with IX509PrivateKey? RRS feed

  • Question

  • Hello all,

    my scenario is as follows: I am trying to generate a private key on a smart card, retrieve a certificate for the corresponding public key from a CA and write the certificate back to the smart card. Here is my code:

     

    public void InstallCert(string KeyName, string CspName)
    {
      var x509PrivateKeyClass = new CX509PrivateKeyClass();
      try
      {
        x509PrivateKeyClass.ProviderName = CspName;
        x509PrivateKeyClass.ContainerName = KeyName;
        x509PrivateKeyClass.Length = 2048;
        x509PrivateKeyClass.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
        x509PrivateKeyClass.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
        x509PrivateKeyClass.MachineContext = false;
        x509PrivateKeyClass.Create();
    
        var x509CertificateRequest = new CX509CertificateRequestPkcs10Class();
    
        x509CertificateRequest.InitializeFromPrivateKey(
          X509CertificateEnrollmentContext.ContextUser,
          x509PrivateKeyClass,
          "Copy of Smartcard Logon");
    
        var enroll = new CX509EnrollmentClass();
        enroll.InitializeFromRequest(x509CertificateRequest);
        enroll.Enroll();
      }
      catch (Exception ex)
      {
        Console.Out.WriteLine(ex.Message);
      }
    }
    

     

    It works fine as it is. I have two questions about the code though:

    1. When two smart card readers are connected to the machine, I would like to be able to choose which smart card to write to. Is there a way to do this?
    2. Apparently, it is not possible to write the key pair and certificate into a pre-existing CAPI container. Instead, the x509PrivateKeyClass.Create() call tries to create the container. If a container with the specified name (from x509PrivateKeyClass.ContainerName) already exists, Windows prompts me to insert a different smart card - presumably a smart card where the CAPI container doesn't exist yet. Is this by design? Is there a way around this, preferably without resorting to lower level APIs?

    Thanks and best regards
    Nils Loeber

     

    Thursday, March 24, 2011 11:02 AM

Answers

  • Bear in mind that smart cards are deliberately designed so that the private key is never allowed to get out of the card.  That's why when you want to perform a private key operation such as signature, you have to request the card to perform it.

    Establishment of the key pair (but not a certificate) is an internal operation performed at or after the card is (re-)initialized.  Once this has happened, all you can sensibly do is get it to generate a CSR for the CA to sign, and then import the Certificate (not the key pair) into the card.

    I believe the COM interface underlying CX509PrivateKeyClass is IX509PrivateKey, which suggests to me that it is the ReaderName Property which should be used to select the correct Smart Card Reader.

    With Smart Cards, it doesn't really make sense to try to use Import.  Instead, you should use Verify and then call Open.


    Answering policy: see profile.
    Monday, March 28, 2011 9:39 PM
  • Looking at David's response. I see there is a ReaderName property on IX509PrivateKey. That makes a lot more sense to use compared to ContainerNamePrefix.

    CAPI containers can contain 2 keys. One encryption and one signing. I don't believe you have control over this with certenroll.

    Andrew

    Thursday, March 31, 2011 2:08 AM

All replies

  • 1. When two smart card readers are connected to the machine, I would like to be able to choose which smart card to write to. Is there a way to do this?

    I can't answer this, but my guess is that it is possible.

    2. Apparently, it is not possible to write the key pair and certificate into a pre-existing CAPI container. Instead, the x509PrivateKeyClass.Create() call tries to create the container. If a container with the specified name (from x509PrivateKeyClass.ContainerName) already exists, Windows prompts me to insert a different smart card - presumably a smart card where the CAPI container doesn't exist yet. Is this by design? Is there a way around this, preferably without resorting to lower level APIs?

    Your mistake may be trying to import the key pair instead of just the certificate.  The private key is already present on the smart card.


    Answering policy: see profile.
    Sunday, March 27, 2011 2:55 PM
  • You can force a particular reader by using \\.\<Reader Name>\ as a prefix to the container name. You can find some information about this here. You can get the reader name by using the SCard* functions such as SCardListReaders. Then using the IX509PrivateKey interface, I suspect you can the ContainerNamePrefix property before creating the key [I think that will work, not 100% sure].

    If you want to open an existing key container then use x509PrivateKeyClass.Open(). However, I agree with David that you may be using the api incorrectly. The private key is created when you create the certificate request. You probably don't want to "Create" another private key.

    Andrew

    Monday, March 28, 2011 12:02 AM
  • Hello David, thanks for your helpful input. I don't quite understand the following paragraph though:
    Your mistake may be trying to import the key pair instead of just the certificate.  The private key is already present on the smart card.

    As I see it, I first create the key (x509PrivateKeyClasss.Create()), then I parametrize the request from said key (x509CertificateRequest.InitializeFromPrivateKey) and then send the request and install the reply. In which line do I import a key pair?

    Thanks again and best regards
    Nils Loeber

    Monday, March 28, 2011 8:02 AM
  • Hi Andrew, thank you for pointing me to this most helpful page! I have a similar question though than the one I already posed David: Why would the private key be created during certificate request creation? After all, I do use the InitializeFromPrivateKey(...) method - shouldn't this lead to my pre-existing key being used for the cert request?

    Also, I don't want to open an existing key container but rather a CAPI container - the kind of entity created by CryptAcquireContextW(..., CRYPT.CRYPT_NEWKEYSET); In my mind, there is a m..n relationship between CAPI container and key container.

    Am I confused here? Are key containers and CAPI containers the same thing after all? If that's the case, I was operating under the wrong assumptions all along and my question ceases to make sense. So if you could help me clarify this question, I'd obviously be most grateful :-)

    Thanks again and best regards
    Nils Loeber


    Monday, March 28, 2011 8:14 AM
  • Nils,

      Your are correct. By initializing the request from the private key you are saying that the request should be created with that private key. A new key will not be created. Before you call "Create" on private key, I think you should set the container prefix name to the reader name as discussed earlier.

     So I think I may not have answered your question #2 earlier because I misunderstood it. Is the problem that you create a request using a smartcard and the private key is on the card and then you use the same card again to create a request and you get a prompt to insert a new card? I think this is probably by design. x509PrivateKeyClass.Create() is calling CryptAcquireContext with CRYPT_NEWKEYSET flag which is trying to create a new container with the given name. Since it already exists and doesn't allow overwrite then it asks for a new card.

    You can get around this problem by calling x509PrivateKeyClass.Open() and if it succeeds then calling x509PrivateKeyClass.Delete().

    I hope this helps. Does it answer your question?

    Monday, March 28, 2011 7:25 PM
  • Bear in mind that smart cards are deliberately designed so that the private key is never allowed to get out of the card.  That's why when you want to perform a private key operation such as signature, you have to request the card to perform it.

    Establishment of the key pair (but not a certificate) is an internal operation performed at or after the card is (re-)initialized.  Once this has happened, all you can sensibly do is get it to generate a CSR for the CA to sign, and then import the Certificate (not the key pair) into the card.

    I believe the COM interface underlying CX509PrivateKeyClass is IX509PrivateKey, which suggests to me that it is the ReaderName Property which should be used to select the correct Smart Card Reader.

    With Smart Cards, it doesn't really make sense to try to use Import.  Instead, you should use Verify and then call Open.


    Answering policy: see profile.
    Monday, March 28, 2011 9:39 PM
  • I hope this helps. Does it answer your question?

    Hi Andrew,

    while you definitely gave me some interesting additional info there, I'm afraid your comment does not answer my root question which I now understand to be: Is it possible to store multiple key pairs inside one CAPI container? And if so: Is it possible to do that using Certenrolllib?

    Thank you for your time and best regards
    Nils Loeber


    Tuesday, March 29, 2011 8:57 AM
  • Looking at David's response. I see there is a ReaderName property on IX509PrivateKey. That makes a lot more sense to use compared to ContainerNamePrefix.

    CAPI containers can contain 2 keys. One encryption and one signing. I don't believe you have control over this with certenroll.

    Andrew

    Thursday, March 31, 2011 2:08 AM
  • Hi Andrew,

    sorry for taking so long to reply and thank you very much for your answer; this is exactly the information I was looking for.

    Best regards
    Nils Loeber


    Wednesday, April 6, 2011 10:33 AM