5

How can I ensure to I am accesing the Certificates from my SmartCard and not form my personal certificate store in c#? and How can I make my RSACryptoProvider to utilize my smart card certificate private key?

thanks

Wally

2 Answers2

3

You will need to go through your Cryptographic Service Provider (CSP) for your smartcard. On Windows (2000, XP, and Vista) any time you insert your smartcard into a smartcard reader all the certificates on it are propogated to your personal certificate store. Your private key stays on your smart card. What that means is if you use your certificate (for example to digitally sign an e-mail) then you are prompted to insert your smart card. If your smart card requires a PIN you will be asked to input it. The reason for this is that there is one location for applications to look for user certificates, your personal certificate store, so applications don't have to be rewritten just to handle certificates on smartcards.

Craig Delthony
  • 193
  • 2
  • 10
  • 1
    Java apps can use the smartcard/certificate without install certificate on personal store. If it's not installed before, .NET apps failed to find the necessary certificate. – Nime Cloud May 27 '14 at 08:34
3

Sometimes, especially if you are not using default key container name on the smart card (recommended by Microsoft), certificates are not copied to local certificate store. The solution is to use crypto api to access the key with KP_CERTIFICATE, construct certificate from the retrieved data, and assign it a new RSACryptoServiceProvider constructed using your own key container name.

The pseudo C# code follows:

int reti = CryptoApi.CryptGetUserKey(_hprovider, keytype, ref userKey);

if (reti)
{
    reti =CryptoApi.CryptGetKeyParam(_userKey, KP_CERTIFICATE, ref  pbdata, ref pwddatalen, 0);
}

if (reti || pwddatalen>0)
{
    byte[] data = new byte[pwddatalen];
    ret  = CryptoApi.CryptGetKeyParam(_userKey, KP_CERTIFICATE, data, ref pwddatalen, 0);
    if (ret) 
    {
        X509Certificate2 c = new X509Certificate2(data);
        X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly);
        X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindByThumbprint, c.Thumbprint, validonly);
        store.Close();

        if (col.Count != 1) 
        {
            //not found in store - CSP didn't copy it
            c.PrivateKey = PrivateKey(keytype);
            return c;
        }
        else
        {
            return col[0];
        }
    }
}


private RSACryptoServiceProvider PrivateKey (KeyType keytype)
{
    CspParameters csparms = new CspParameters();
    csparms.KeyContainerName = _containerName;
    csparms.ProviderName = _provider;
    csparms.ProviderType = 1;
    csparms.Flags = CspProviderFlags.UseMachineKeyStore | CspProviderFlags.UseExistingKey;
    csparms.KeyNumber = (int)keytype;

    return new RSACryptoServiceProvider(csparms);
}
Ant Swift
  • 20,089
  • 10
  • 38
  • 55
Željko Tanović
  • 602
  • 5
  • 11
  • I'm a bit confused as to when such code would be needed? If say you know the smart card subject line, then you store that for each of your users on registration, and then store the certificate in the DB, then each time someone tries to log in, compare certificate in DB with the certificate given in Request.ClientCertificate, right? – Dexter Apr 16 '12 at 15:10
  • You can only store public part of the certificate somewhere - and comparing public parts of the certificate is not enough to prove possesion of the private key. Therefore you need private key access to sign some data and prove that. Crypto provider will sometimes make available private key to certificate store automatically and sometimes not. In case it doesn't this is the way to get it. – Željko Tanović Apr 17 '12 at 08:55
  • Is there an article where you found this type of code? Your code is a bit incomplete with undeclared variables. Where is there a C# article that describes this process of checking the validity of private keys. I always assumed a simple checking of valid certification using X509Certificate to check if the hash is a valid hash in Client Certificate might be enough, but yeah I suppose it can be faked with a self-signed certificate. Any information would be helpful. – Dexter Apr 17 '12 at 20:01
  • I still don't get why I would need to check private keys. The API of C# should not return a ClientCertificate the same as the original certificate used when registering, if in fact, the ClientCertificate variable was faked--the decryption should cause an invalid certificate to appear in Context.ClientCertificate. – Dexter Apr 17 '12 at 20:52
  • Hi, you might want to read up a bit on public-private key cryptography... Comparing hash of the public key to some other hash is not enough to prove identity as public key is PUBLIC ( sorry for caps lock, can't make it bold here ), and therefore available to anyone for interception. To prove identity you need to sign piece of data with private key (effectively encrypting hash with your private key). This signature can be verified using your public key (using it to decrypt the hash and compare it with data). That's the reason why you need access to private key. Or I misunderstood your question? – Željko Tanović Apr 18 '12 at 10:45
  • Agree - the code is incomplete, and as I said above it is more a pseudo code... I didn't have time to refactor the class ( which is part of commercial project ) so I could post the complete code. No article either - this is my code I had to write to get around a misbehaving smart card crypto provider which didn't provide access to private key through windows certificate store.... – Željko Tanović Apr 18 '12 at 10:47