记录成功通过CSP接口获取Ukey的X509数字证书过程

通过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证书读出来了。

//枚举容器过程
	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;
		}

	}

}

补充

  1. cps ukey的providerName 一般为:eSafe Cryptographic Service Provider v1.0
  2. 可以通过注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider 查看具体的Provider.
  3. 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解析

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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值