John Rainey
John Rainey

Reputation: 3

wincrypt api API CryptAcquireContext also returns "Keyset does not exist"

Trying to acquire a cryptographic context from a CSP of type PROV_RSA_FULL the API function CryptAcquireContext(...) always returns an error code and last error is "Keyset does not exist". The code enumerates the CSPs and 4 of them have type PROV_RSA_FULL: Microsoft Base Cryptographic Provider v1.0 Microsoft Base Smart Card Crypto Provider Microsoft Enhanced Cryptographic Provider v1.0 Microsoft Strong Cryptographic Provider

Debug Output shows: CSP: Microsoft Base Cryptographic Provider v1.0 is a PROV_RSA_FULL type provider Keyset does not exist ... CSP: Microsoft Base Smart Card Crypto Provider is a PROV_RSA_FULL type provider Keyset does not exist ... CSP: Microsoft Enhanced Cryptographic Provider v1.0 is a PROV_RSA_FULL type provider Keyset does not exist ... CSP: Microsoft Strong Cryptographic Provider is a PROV_RSA_FULL type provider Keyset does not exist

void main()
{
    //-------------------------------------------------------------------
    // Declare and initialize variables. This includes getting a pointer 
    // to the message to be encrypted. This code creates a message
    // and gets a pointer to it. In reality, the message content 
    // usually exists somewhere and a pointer to the message is 
    // passed to the application. 

    BYTE* pbContent = (BYTE*)"Security is our business.";
    // The message
    DWORD cbContent = strlen((char*)pbContent) + 1;
    // Size of message
    HCRYPTPROV hCryptProv;                      // CSP handle
    HCERTSTORE hStoreHandle;
    PCCERT_CONTEXT pRecipientCert;
    PCCERT_CONTEXT RecipientCertArray[1];
    DWORD EncryptAlgSize;
    CRYPT_ALGORITHM_IDENTIFIER EncryptAlgorithm;
    CRYPT_ENCRYPT_MESSAGE_PARA EncryptParams;
    DWORD EncryptParamsSize;
    BYTE* pbEncryptedBlob;
    DWORD    cbEncryptedBlob;

    //-------------------------------------------------------------------
    //  Begin processing.

    printf("About to begin with the message %s.\n", pbContent);
    printf("The message length is %d bytes. \n", cbContent);


    DWORD cbName=0;
    DWORD dwType=0;
    DWORD dwIndex = 0;
    LPWSTR pszName;
    DWORD pdwProvType;
    DWORD pcbProvName;
    

    // Loop through enumerating providers.
    while(CryptEnumProvidersW(
        dwIndex,
        NULL,
        0,
        &pdwProvType,
        NULL,
        &pcbProvName
    ))
    {
        //-----------------------------------------------------------
        // cbName is the length of the name of the next provider 
        // type.

        // Allocate memory in a buffer to retrieve that name.
        if (!(pszName = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, pcbProvName)))
        {
            MyHandleError((char*)std::string::basic_string("ERROR LocalAlloc failed!\n").c_str());
        }

        //-----------------------------------------------------------
        // Get the provider type name.

        if (CryptEnumProvidersW(
            dwIndex++,
            NULL,
            0,
            &pdwProvType,
            pszName,
            &pcbProvName
        ))
        {
            std::wostringstream os;
            os << "CSP: " << pszName << "\n";
            OutputDebugString(os.str().c_str());
            printf("     %4.0d        %ls\n",dwType, pszName);
            if (pdwProvType==PROV_RSA_FULL)
            {
                std::wstring s(L"is a PROV_RSA_FULL type provider");
                OutputDebugString(s.c_str()); 
                //MS_DEF_PROV
            }
        }
        else
        {
           MyHandleError((char*) std::string::basic_string("ERROR CryptEnumProviders.\n").c_str());
        }


        //-------------------------------------------------------------------
        // Get a handle to a cryptographic provider.

        if (CryptAcquireContext(
            &hCryptProv,        // Address for handle to be returned.
            NULL,               // Use the current user's logon name.
            pszName,            // Use the default provider.
            PROV_RSA_FULL,      // Need to both encrypt and sign.
            NULL))              // No flags needed.
        {
            printf("A CSP has been acquired \n");
            break;
        }
        else
        {
/*
            DWORD e= GetLastError();
            std::wstring ermsg=getErrorMsg();
            OutputDebugString(ermsg.c_str());// ALWAYS OUTPUTS "Keyset does not exist"
    // WHY??????????????????????????????????????????????????????????????????????????????????

            printf("Cryptographic context could not be acquired.\n");
            
        }
*/

            DWORD e = GetLastError();
            std::wstring ermsg = getErrorMsg();
            OutputDebugString(ermsg.c_str());
            std::wostringstream os;
            os << "Cryptographic context could not be acquired, the                         
                   default container not found.\n";
            OutputDebugString(os.str().c_str());

             // No default container was found. Attempt to create it.
            if (CryptAcquireContext(
                &hCryptProv,
                NULL,
                NULL,
                PROV_RSA_FULL,
                CRYPT_NEWKEYSET))
            {
                std::wostringstream os;
                os << "A PROV_RSA_FULL CSP has been acquired \n";
                OutputDebugString(os.str().c_str());
                AcquiredCryptographicContext = TRUE;
                //break;
            }
            else
            {
                DWORD e = GetLastError();
                std::wstring ermsg = getErrorMsg();
                OutputDebugString(ermsg.c_str());
                MyHandleError((char*)std::string::basic_string("Could 
                  not create the default key container.\n").c_str());
            }

    if (hCryptProv != NULL)
    {
        if (!CryptReleaseContext(
            hCryptProv,
            0))
        {
            MyHandleError((char*)std::string::basic_string("FAILED To 
            RELEASE CryptoContext.\n").c_str());
        }
    }
    if(pszName!=NULL)
    {
       LocalFree(pszName);
    }
    }
}

I have a self signed certificate in my personal store. Other code can retrieve all it's properties, extended properties, and the public certificate. I was expecting to get CryptAcquireContext to succeed and return a handle. I would then use this with the self-signed cert to encrypt a message. ** I KNOW ** this is a deprecated api but I am having similar issues using NGC to get a certificate context. NCryptGetProperty for NCRYPT_CERTIFICATE_PROPERTY fails with error NTE_NOT_FOUND. I can post this code if needed but it is a different issue, or do I have a configuration problem causing both?

Upvotes: 0

Views: 72

Answers (1)

John Rainey
John Rainey

Reputation: 3

I have updated the code with the final answer.

After the first call to CryptAcquireContext fails with error "Keyset does not exist" I make a second call to CryptAcquireContext with dwFlags parameter set to CRYPT_NEWKEYSET. This succeeded, and when it is run again, the first call now succeeds. The first CSP with PROV_RSA_FULL type: Microsoft Base Cryptographic Provider v1.0 now has a default Keyset. This implies none of my CSPs with PROV_RSA_FULL type had a default Keyset.

Thanks to bartonjs for catching the pszName parameter issue. "pszName is the name of the CSP, but you're passing it to CryptAcquireContext as the name of a key container" With this fixed, the first call to CryptAcquireContext still returned the same error "Keyset does not exist".

https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/SecCrypto/example-c-program-using-cryptacquirecontext.md is an example of creating a Keyset

Upvotes: 0

Related Questions