尝试从 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。如果需要,我可以发布此代码,但这是一个不同的问题,或者我是否有配置问题导致这两者?
我已经用最终答案更新了代码。
第一次调用 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 的示例