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


    通过CSP接口获取X509数字证书

    所谓CPS接口其实就是windowAPI ,其实就是wincrypt.h提供的接口,Visual Studio 直接包含以下内容即可

    #include<wincrypt.h>
    #pragma comment(lib,"Crypt32")
    
    • 1
    • 2

    首先确定调用流程

    开始通过百度总结调用流程如下:

    调用流程
    /*
    1.CryptAcquireContext
    2.CryptGetUserKey
    3.CryptGetKeyParam
    4.CertCreateCertificateContext
    5.CryptDestroyKey
    6.CryptReleaseContext
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    悲催过程

    • 撸撸撸,把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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    补充

    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);
    
    • 1

    调用过程代码

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    南美委内瑞拉市场最全分析开发攻略,收藏一篇就够了
    CleanMyMac X2022苹果电脑专业清理Mac加速器软件
    【LeetCode】图解算法数据结构+java代码实现(数据结构篇)
    数据结构——二叉树线索化遍历(前中后序遍历)
    HTML如何设置引文与特色元素
    Java 中的八大基本数据类型、类型转换
    Lua专栏目录
    【五一创作】使用Scala二次开发Spark3.3.0实现对MySQL的upsert操作
    python科研绘图:绘制X-bar图
    Java8-Java16部分重要新特性汇总
  • 原文地址:https://blog.csdn.net/esiangchioa/article/details/125508292