如何通过Win32 API编程提取本地计算机证书存储中的公钥
问题描述
我已将crt文件导入本地计算机的「个人证书」存储中。
我可通过MMC的「Base64编码X509 .CER」选项提取对应公钥。
我的目标是通过Win32 API编程实现该公钥提取功能。
目前我尝试了以下代码,但base64Cert中存储的数据与公钥不匹配。请问有什么方法可以获取正确的数据?
HCERTSTORE hCertStore; PCCERT_CONTEXT pCertContext = NULL; if (!(hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, MY_ENCODING_TYPE, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"MY"))) { std::cout<<"The MY system store did not open."<< std::endl; } while (pCertContext = CertEnumCertificatesInStore( hCertStore, pCertContext)) { LPTSTR pszString; LPTSTR pszName; DWORD cbSize; CERT_BLOB blobEncodedName; PBYTE pbPKEY = NULL; DWORD iPKEYSize; bool ret = CryptDecodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData, pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData, CRYPT_ENCODE_ALLOC_FLAG, NULL, &pbPKEY, &iPKEYSize); CERT_CONTEXT certcontext; certcontext.pbCertEncoded = pbPKEY; certcontext.cbCertEncoded = iPKEYSize; DWORD dwData; if (!CryptBinaryToStringA(certcontext.pbCertEncoded, certcontext.cbCertEncoded, CRYPT_STRING_BASE64HEADER, nullptr, &dwData)) { std::cout << "error\n"; } std::string base64Cert(dwData, '\0'); CryptBinaryToStringA(certcontext.pbCertEncoded, certcontext.cbCertEncoded, CRYPT_STRING_BASE64HEADER, &base64Cert[0], &dwData); std::cout << base64Cert << "\n"; }

解决方案
你的代码存在两个核心问题:
- 错误转换公钥格式:
RSA_CSP_PUBLICKEYBLOB是微软私有格式的公钥Blob,并非MMC导出的标准X509格式公钥,直接将其当作证书上下文处理会导致格式不匹配。 - 解码对象类型错误:你需要导出的是包含完整公钥结构的X509格式数据,而非转换为RSA特定Blob。
正确实现步骤:
直接提取证书中的SubjectPublicKeyInfo字段,将其编码为标准X509 DER格式,再转换为带头部的Base64字符串,即可和MMC导出结果一致。
修改后的代码示例:
#include <windows.h> #include <wincrypt.h> #include <iostream> #include <string> #pragma comment(lib, "crypt32.lib") #define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) int main() { HCERTSTORE hCertStore; PCCERT_CONTEXT pCertContext = NULL; // 打开本地计算机的个人证书存储 if (!(hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, MY_ENCODING_TYPE, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"MY"))) { std::cout << "无法打开本地计算机的个人证书存储。" << std::endl; return 1; } // 枚举存储中的证书 while (pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext)) { PBYTE pbX509PublicKey = NULL; DWORD cbX509PublicKey = 0; // 将SubjectPublicKeyInfo编码为标准X509 DER格式 if (!CryptEncodeObjectEx( MY_ENCODING_TYPE, X509_SUBJECT_PUBLIC_KEY_INFO, &pCertContext->pCertInfo->SubjectPublicKeyInfo, CRYPT_ENCODE_ALLOC_FLAG | CRYPT_ENCODE_TO_BLOB, NULL, &pbX509PublicKey, &cbX509PublicKey)) { std::cout << "编码X509公钥失败,错误码:" << GetLastError() << std::endl; continue; } // 计算Base64字符串所需缓冲区大小 DWORD dwBase64Size = 0; if (!CryptBinaryToStringA(pbX509PublicKey, cbX509PublicKey, CRYPT_STRING_BASE64HEADER, NULL, &dwBase64Size)) { std::cout << "计算Base64缓冲区大小失败,错误码:" << GetLastError() << std::endl; LocalFree(pbX509PublicKey); continue; } // 转换为带头部的Base64字符串 std::string base64Cert(dwBase64Size, '\0'); if (!CryptBinaryToStringA(pbX509PublicKey, cbX509PublicKey, CRYPT_STRING_BASE64HEADER, &base64Cert[0], &dwBase64Size)) { std::cout << "转换为Base64失败,错误码:" << GetLastError() << std::endl; LocalFree(pbX509PublicKey); continue; } // 输出结果 std::cout << "提取的X509公钥(Base64):\n" << base64Cert << "\n" << std::endl; // 释放内存 LocalFree(pbX509PublicKey); } // 关闭证书存储 CertCloseStore(hCertStore, 0); return 0; }
关键说明:
- 使用
CryptEncodeObjectEx并指定X509_SUBJECT_PUBLIC_KEY_INFO类型,直接将证书内的公钥信息编码为标准X509 DER格式,和MMC导出的底层数据一致。 CRYPT_STRING_BASE64HEADER参数会自动添加-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----头部,完全匹配MMC的导出格式。- 注意内存释放:通过
CRYPT_ENCODE_ALLOC_FLAG分配的内存,必须用LocalFree释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Ariel
相关产品推荐
相关产品推荐

