通过CSP接口获取X509数字证书
所谓CPS接口其实就是windowAPI ,其实就是wincrypt.h提供的接口,Visual Studio 直接包含以下内容即可
#include<wincrypt.h>
#pragma comment(lib,"Crypt32")
首先确定调用流程
开始通过百度总结调用流程如下:
调用流程
/*
1.CryptAcquireContext
2.CryptGetUserKey
3.CryptGetKeyParam
4.CertCreateCertificateContext
5.CryptDestroyKey
6.CryptReleaseContext
*/
悲催过程
- 撸撸撸,把6个过程实现完,开始测试,走到第二步CryptGetUserKey就返回失败了,通过LastError提示找不到对象。把AT_SIGNATURE : AT_KEYEXCHANGE两个类型都试了不行。看到由的文章说如果找不到那就调用CryptGenKey,去生成一个key。成功返回key了,下一个CryptGetKeyParam时候找不到了。
- 在试了几次,尼玛,直接把我Ukey里面的证书损坏了…
问题解决
首先ukey里面是存在多个容器的,一般证书都不会在这个默认容器里面,所以CryptGetUserKey就失败了。解决办法:首先进行枚举容器,把ukey里面的容器都枚举出来。在根据容器名称循环调用上面6个流程,最终把x509证书读出来了。文章来源:https://www.toymoban.com/news/detail-450002.html
//枚举容器过程
if (CryptAcquireContext(
&hCryptProv, // Handle to the CSP
NULL, // Container name
mProviderName, // Use the default provider
PROV_RSA_FULL, // Provider type
CRYPT_NEWKEYSET)) // Flag values
{
BYTE pbData[1000]; // 1000 will hold the longest
DWORD cbData = 1000;
int count = 0;
if (CryptGetProvParam(
hCryptProv,
PP_ENUMCONTAINERS,
(BYTE*)& pbData,
&cbData,
CRYPT_FIRST))
{
pbData[cbData] = '\0';
printf("%d. ContainName is %s\n", count++, pbData);
contain_vector.push_back((char*)pbData);
pbData[0] = '\0';
cbData = 1000;
while (CryptGetProvParam(
hCryptProv,
PP_ENUMCONTAINERS,
(BYTE*)& pbData,
&cbData,
CRYPT_NEXT)
)
{
if (ERROR_NO_MORE_ITEMS == GetLastError())
{
break;
}
pbData[cbData] = '\0';
printf("%d. ContainName is %s\n", count++, pbData);
contain_vector.push_back((char*)pbData);
pbData[0] = '\0';
cbData = 1000;
}
}
}
补充
- cps ukey的providerName 一般为:eSafe Cryptographic Service Provider v1.0
- 可以通过注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider 查看具体的Provider.
- CertCreateCertificateContext 出参BYTE * pbCert就是x509证书的二进制,可以直接通过其他x509证书解析库解析。也可以通过CertGetNameString解析证书相关信息。
pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbCert, dwCertLen);
调用过程代码
BOOL CryptHelper::GetPublicKey(LPCSTR containName, BYTE** certInfo, DWORD& certLength, LPTSTR& message)
{
//1.获得一个CSP句柄
HCRYPTPROV hCryptProv;
printf("csp name:%s;contain name:%s.\n", mProviderName, containName);
BOOL res = CryptAcquireContext(&hCryptProv, containName, mProviderName, PROV_RSA_FULL, 0);
if (!res) //如果没有可用CSP
{
GetErrorToString(TEXT("CryptAcquireContext"), message);
printf(message);
return FALSE;
}
//2.获取签名秘钥
HCRYPTKEY hKey;
res = CryptGetUserKey(hCryptProv, mAlgId, &hKey);
if (!res) //没有可用的签名秘钥
{
GetErrorToString(TEXT("CryptGetUserKey"), message);
printf(message);
return FALSE;
}
//3.获取证书参数
DWORD dwCertLen = 2048;
BYTE* pbCert = (BYTE*)malloc(dwCertLen);
res = CryptGetKeyParam(hKey, KP_CERTIFICATE, pbCert, &dwCertLen, 0);
if (!res)
{
GetErrorToString(TEXT("CryptGetKeyParam"), message);
printf(message);
return FALSE;
}
//4.导出证书
if (pbCert)
{
PCCERT_CONTEXT pCertContext = NULL;
pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbCert, dwCertLen);
if (pCertContext)
{
message = "success";
certLength = dwCertLen;
*certInfo = (BYTE*)malloc(certLength);
memcpy(*certInfo, pbCert, certLength);
CHAR pszBuff[256];
CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
0, NULL, pszBuff, 128);
printf("%s\n", pszBuff); // 显示名
//7.释放证书
CertFreeCertificateContext(pCertContext);
}
else
{
GetErrorToString(TEXT("CertCreateCertificateContext"), message);
printf(message);
return FALSE;
}
}
//6.释放key;
if (hKey)
{
CryptDestroyKey(hKey);
printf("The hKey could be released.\n");
}
//7.释放容器
if (hCryptProv)
{
CryptReleaseContext(hCryptProv, 0);
printf("The hCryptProv could be released.\n");
}
return TRUE;
}
LastError()解析
lastError()方法放回的是错误码,具体什么错误并不知道。具体通过FormatMessage解析文章来源地址https://www.toymoban.com/news/detail-450002.html
BOOL CryptHelper::GetErrorToString(LPTSTR funcName, LPTSTR& Message)
{
DWORD lastError = GetLastError();
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
lastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)& lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)funcName) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,LocalSize(lpDisplayBuf),TEXT("%s failure with error %d: %s"),funcName, lastError, lpMsgBuf);
Message = (LPTSTR)lpDisplayBuf;
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
return TRUE;
}
到了这里,关于记录成功通过CSP接口获取Ukey的X509数字证书过程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!