wincrypt api API CryptAcquireContext 也返回“Keyset 不存在”

问题描述 投票:0回答:1

尝试从 API 函数 PROV_RSA_FULL 类型的 CSP 获取加密上下文 CryptAcquireContext(...) 总是返回一个错误代码,最后一个错误是“Keyset 不存在”。 该代码枚举了 CSP,其中 4 个具有 PROV_RSA_FULL 类型: Microsoft 基础加密提供程序 v1.0 Microsoft 基础智能卡加密提供商 Microsoft 增强型加密提供程序 v1.0 Microsoft 强大的加密提供商

调试输出显示: CSP:Microsoft 基础加密提供程序 v1.0 是一个 PROV_RSA_FULL 类型提供者 键集不存在 ... CSP:Microsoft 基础智能卡加密提供商 是一个 PROV_RSA_FULL 类型提供者 键集不存在 ... CSP:Microsoft 增强型加密提供程序 v1.0 是一个 PROV_RSA_FULL 类型提供者 键集不存在 ... CSP:Microsoft 强大的加密提供商 是一个 PROV_RSA_FULL 类型提供者 按键集不存在

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);
    }
    }
}

我的个人商店中有一个自签名证书。 其他代码可以检索它的所有属性、扩展属性和公共证书。 我期望 CryptAcquireContext 成功并返回一个句柄。 然后,我将其与自签名证书一起使用来加密消息。 ** 我知道 ** 这是一个已弃用的 api,但我在使用 NGC 获取证书上下文时遇到了类似的问题。 NCRYPT_CERTIFICATE_PROPERTY 的 NCryptGetProperty 失败并出现错误 NTE_NOT_FOUND。如果需要,我可以发布此代码,但这是一个不同的问题,或者我是否有配置问题导致这两者?

windows security visual-c++ certificate wincrypt
1个回答
0
投票

我已经用最终答案更新了代码。

第一次调用 CryptAcquireContext 之后 失败并出现错误“密钥集不存在” 我第二次调用 CryptAcquireContext dwFlags 参数设置为 CRYPT_NEWKEYSET。 这成功了,当再次运行时,第一个调用现在成功了。 第一个具有 PROV_RSA_FULL 类型的 CSP:Microsoft Base Cryptographic Provider v1.0 现在有一个默认的键集。 这意味着我的 PROV_RSA_FULL 类型的 CSP 都没有默认密钥集。

感谢 bartonjs 捕获 pszName 参数问题。 “pszName 是 CSP 的名称,但您将其作为密钥容器的名称传递给 CryptAcquireContext” 修复此问题后,第一次调用 CryptAcquireContext 仍然返回相同的错误“Keyset 不存在”。

https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/SecCrypto/example-c-program-using-cryptacquirecontext.md 是创建 Keyset 的示例

© www.soinside.com 2019 - 2024. All rights reserved.