Reputation: 1792
I'm writing a program to do encryption things, using certificates stored on hardware devices (smart cards).
Preamble
As far as I understood, when a smart card, containing one or more certificates, is connected to the computer, the certificates appears on the Windows certificates store (CurrentUser/My).
This is managed by the smartcard software, that must be installed.
Since my software must work with any hardware device, the easiest solution is to popup a window, and making the user to choose the certificate to use.
First problem
I don't want to show all the certificates that are on CurrentUser/My, but only the ones that have an associated hardware connected to the computer.
This is, more or less, the code that I'm using:
X509Store storeObject = new X509Store(StoreName.My, StoreLocation.CurrentUser);
storeObject.Open(OpenFlags.ReadOnly);
foreach (var certificate in storeObject.Certificates)
{
bool isValid = false;
if (certificate.HasPrivateKey)
{
try
{
var rsaKey = certificate.PrivateKey as RSACryptoServiceProvider;
if (rsaKey.CspKeyContainerInfo.HardwareDevice)
{
isValid = true;
}
}
catch (Exception ex)
{
// just log
}
}
}
Despite this solution is very ugly, it's the best I came up.
The certificate belongs to a smart card if:
The problem is on the PrivateKey cast: it seems that lot of things are performed by the CryptoApi system during this cast (that includes calls - of course - to the smart card software).
This is very slow, and if the smartcard software has some kind of failures, uncontrollable error messages are shown, ...
Question
Is there any other way to determine if the certificate belongs to an hardware device? And to determine if the hardware is currently connected?
Second problem
The certificate may require a PIN.
I must (it's a prerequisite) avoid the Windows input box asking for the PIN.
It's not a problem for me to collect the PIN, and to use it during my crypto operations.
My problem is to understand if the PIN is required or not.
ANY example I found online assume that if rsaKey.CspKeyContainerInfo.HardwareDevice == true then a PIN is required.
But I'm not sure about that.
Question
Is there any way I can determine if a certificate requires a PIN code?
Conclusion
I also like external libraries, as long as they are free (preferably open source) and/or come from Microsoft.
Thank you very much!
Upvotes: 2
Views: 1718
Reputation: 13924
In comments I offered Win32 interop solution which may be faster than delay OP experiences. Proposed solution will extended certificate properties to read provider name from there (I believe, .NET does the same somewhere internally?). Then open specified provider (CSP or KSP) and query hardware capabilites from provider. This solution doesn't touch private key, nor prompt for PIN.
Call sequence:
CERT_KEY_PROV_INFO_PROP_ID
property.pwszProvName
field that stores provider name.NCRYPT_IMPL_TYPE_PROPERTY
property.pbOutput
parameter will store 4 bytes integer value) has NCRYPT_IMPL_HARDWARE_FLAG
flag enabled.Release all unmanaged resources when calls are done. Although this looks a bit complicated, but from my experience this is the fastest way to determine where key is stored for a given certificate.
steps 4 and 5 may not work for all smart card providers (given that you use legacy CSP), though, works for mine. In this case, replace steps 4 and 5 as follows:
PP_IMPTYPE
property. pbData
parameter will store a 4-byte long byte array that represents a bitwise combination of implementation types. Check if this value has CRYPT_IMPL_HARDWARE1
flag enabled.Upvotes: 1