How can I get the name of a Key Container that's in a smart card required for initialization of CspParameters?

CspParameters^ cspa = gcnew CspParameters(ProviderType, ProviderName, keyContainerName, cryptoSecurityKey, securityString);

RSACryptoServiceProvider^ csp = gcnew RSACryptoServiceProvider(cspa);

Since certificates from smart cards are automatically installed in a personal certificate store, I have tried looking for keyContainerName in X509Certificate2 and I couldn't find it there.

I have managed to find a container name of one of the keys outside of C++ using certutils.exe, and that way, I just entered that name and I managed to generate a digital signature using the aforementioned RSACryptoServiceProvider. It works beautifully. What can do I do with other certificates that the software might be used with? I can't ask the users to enter container name manually.

But, from what I understand, if I don't provide the container name (leave it empty), it doesn't work. So I need a way to find/get a key container name for any certificate in any smart card within the program code.

Unfortunately, most examples I've seen on the internet provide a constant predefined container name (like "example" or "test" - like here https://secpal.codeplex.com/discussions/13106 ), which is useless in real life.

bumped to the homepage by Community 7 hours ago

This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.

migrated from crypto.stackexchange.com Feb 26 '15 at 12:43

This question came from our site for software developers, mathematicians and others interested in cryptography.

Is this what you're looking for? (sorry for messy code, I'm a Java guy thrown into VC++ code)

    HCRYPTPROV hProv = NULL;
    DWORD status;

    if (FALSE == CryptAcquireContext(
        &hProv, 
        NULL, 
        MS_SCARD_PROV, 
        PROV_RSA_FULL, 
        CRYPT_MACHINE_KEYSET | CRYPT_SILENT)
    ){
        status = GetLastError();
    } else {
        PBYTE pbData = (PBYTE)Alloc(256);  // formally, you should check the max size
        DWORD tmp = 256;
        PDWORD pdwDataLen = &tmp;
        status = 0;
        bool is_first = TRUE;
        // PP_ENUMCONTAINERS does an enumeration over all containers available to the MS_SCARD_PROV crypto provider 
        // (which should be just all containers on the smartcard in case of single smartcard and single reader)
        while (status == 0) {
            if (FALSE == CryptGetProvParam(hProv, PP_ENUMCONTAINERS, pbData, pdwDataLen, (is_first ? CRYPT_FIRST : CRYPT_NEXT)))
            {
                status = GetLastError();
                //__leave;
            }

            OutputDebugStringA((LPSTR)pbData); // or std::cout in a console application
            OutputDebugStringA("\n");
            is_first = FALSE;
        }
    }

You'll have to know something about the certificate in question... subject name or issuer for example, get a handle on the X509Certificate2 object. From there, you can obtain the unique container name, generally located in C:\ProgramData\Microsoft\crypto\RSA\MachineKeys.

This is for c#, but can be adapted for C++ easily enough.

            try
            {
                RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;

                if (rsa != null)
                {
                    string keyfilepath = FindKeyLocation(rsa.CspKeyContainerInfo.UniqueKeyContainerName);

                    string fullPrivateKeyPath = (keyfilepath + "\\" + rsa.CspKeyContainerInfo.UniqueKeyContainerName);

                    //Inform the user of the specific privatekey path.
                    Console.WriteLine(fullPrivateKeyPath);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception occurred while trying to decrypt rsa key and find locaiton in file system:" + ex);
            }

To get a handle, i generally search by subject name (if a known name):

X509Certificate2Collection CACerts = castore.Certificates.Find(X509FindType.FindBySubjectName, "Test-Certificate-Authority", true);
                        if (CACerts.Count <= 0)
                        {
                            MessageBox.Show("No existing Certificate Authority was found on this machine. Please either import one or select 'no' to generate a new one.");
                            return;
                        }
                        ca2 = CACerts[0];

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.