Question

I'm writing a class that signs a XML document using x509 certificates. I'm already able to get my SmartCard certificate and sign it.

However, to do this I must look for the x509 certificate in the Windows Keystore, so I must know something about my SmartCard, like the Issuer Name, or the Serial Number. That's nice.

But I want a more convenient approach to get the SmartCard certificate. I had 2 ideas:

  • Get all the certificates from the Keystore and look for those who are "coming from a SmartCard". But I dont think i'm able to get such information.
  • Interop with winscard.dll and get some information about the SmartCard connected to my computer, so I could use it to find the right certificate, but this isn't working:

    [DllImport("winscard.dll")]
    public static extern long SCardListCards(
       uint hContext, int pbAtr, 
        int rgguidInterfaces, int cguidInterfaceCount, 
        [MarshalAs(UnmanagedType.LPTStr)] ref string mszCards,
        ref int pcchCards);
    
    [DllImport("winscard.dll")]
    public static extern long SCardEstablishContext(uint dwScope, 
        int pvReserved1, 
        int pvReserved2,
        ref uint phContext); 
    

And here is a test method. The string crds holds the list of SmartCards.

[TestMethod]
public void TestInterop()
{
    uint handle = 0;
    Interop.SCardEstablishContext(0, 0, 0, ref handle);

    string crds = "";
    int pcch = 0;
    Interop.SCardListCards(handle, 0, 0, 0, ref crds, ref pcch);
    Assert.IsTrue(crds != "");
}

This test fails everytime, because crds never changes.

If I try to return crds as a byte[]:

    [DllImport("winscard.dll", EntryPoint = "SCardListCards")]
    public static extern long SCardListCardsRetBytes(
       uint hContext, int pbAtr,
        int rgguidInterfaces, int cguidInterfaceCount,
        ref byte[] mszCards,
        ref int pcchCards);

My byte[] crds becomes:

  • byte[] {171} when I initialize it with byte[] crds = {}; why 171?
  • byte[] {0} when I initialize it with byte[] crds = new byte[32];and why 0?

How can I get the SmartCard certificate to sign a XML message? And if you want to give me a bonus answer, why this Interop I did isn't working?

Was it helpful?

Solution

I discovered a way to know if a X509Certificate came from a SmartCard using only C#. That solves the problem for me, and it's been a while since I wrote the code, I deleted the interop C# and C++ code. This works for me:

if (certificate.HasPrivateKey)
{
    ICspAsymmetricAlgorithm aa = certificate.PrivateKey as ICspAsymmetricAlgorithm;

    if (aa == null || !aa.CspKeyContainerInfo.HardwareDevice) 
         DoWhateverYouWant(certificate);
}

It is not a problem for me if the card is not connected in the moment the code is run. If you want to get a list of SmartCards connected to your PC, it is feasible by using the winscard native API.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top