CSP:使用CryptoAPI解码X509证书内容

转载自:https://blog.csdn.net/yyfzy/article/details/46681679

微软的CryptoAPI提供了一套解码X509证书的函数,一个X509证书解码之后,得到一个PCCERT_CONTEXT类型的结构体指针。通过该结构体,我们就可以获取想要的证书项和属性等。

        X509证书文件,根据封装的不同,主要有以下三种类型:

*.cer:单个X509证书文件,不私钥,可以是二进制和Base64格式。该类型的证书最常见;

*.p7b:PKCS#7格式的证书链文件,包含一个或多个X509证书,不含私钥。通常从CA中心申请RSA证书时,返回的签名证书就是p7b格式的证书文件;

*.pfx:PKCS#12格式的证书文件,可以包含一个或者多个X509证书,含有私钥,一般有密码保护。通常从CA中心申请RSA证书时,加密证书和RSA加密私钥就是一个pfx格式的文件返回。

        下面,针对这三种类型的证书文件,使用CryptoAPI进行解码,得到对应的PCCERT_CONTEXT结构体指针。需要注意的是,示例代码中的证书文件内容都是指二进制数据,如果证书文件本身使用的Base64格式,从文件读取之后,需要将Base64格式的内容转化为二进制数据,才能使用下面的解码函数。

一、解码CER证书文件

CER格式的文件最简单,只需要调用API CertCreateCertificateContext()即可。示例代码如下(lpCertData为二进制数据):

  1. ULONG CCSPCertificate::_DecodeX509Cert(LPBYTE lpCertData, ULONG ulDataLen)
  2. {
  3. if (!lpCertData || ulDataLen == 0)
  4. {
  5. return CERT_ERR_INVALIDPARAM;
  6. }
  7. m_pCertContext = CertCreateCertificateContext(GLOBAL_ENCODING_TYPE, lpCertData, ulDataLen);
  8. if (!m_pCertContext)
  9. {
  10. return GetLastError();
  11. }
  12. return CERT_ERR_OK;
  13. }

二、解码P7B证书文件

由于P7B是个证书链文件,理论上可以包含多个X509证书。但是实际应用中,往往只包含一个文件,所以我们只处理第一个证书。示例代码如下:

  1. ULONG CCSPCertificate::_DecodeP7bCert(LPBYTE lpCertData, ULONG ulDataLen)
  2. {
  3. ULONG ulRes = CERT_ERR_OK;
  4. ULONG ulFlag = CRYPT_FIRST;
  5. ULONG ulContainerNameLen = 512;
  6. CHAR csContainerName[ 512] = { 0};
  7. BOOL bFoundContainer = FALSE;
  8. if (!lpCertData || ulDataLen == 0)
  9. {
  10. return CERT_ERR_INVALIDPARAM;
  11. }
  12. // 由证书链创建一个证书库
  13. HCERTSTORE hCertStore = NULL;
  14. CRYPT_DATA_BLOB dataBlob = {ulDataLen, lpCertData};
  15. hCertStore = CertOpenStore(CERT_STORE_PROV_PKCS7, GLOBAL_ENCODING_TYPE, NULL, 0, &dataBlob);
  16. if ( NULL == hCertStore)
  17. {
  18. ulRes = GetLastError();
  19. return ulRes;
  20. }
  21. // 释放之前的证书内容
  22. if (m_pCertContext)
  23. {
  24. CertFreeCertificateContext(m_pCertContext);
  25. m_pCertContext = NULL;
  26. }
  27. // 得到第一个证书内容
  28. m_pCertContext = CertEnumCertificatesInStore(hCertStore, m_pCertContext);
  29. if ( NULL == m_pCertContext)
  30. {
  31. ulRes = GetLastError();
  32. goto CLOSE_STORE;
  33. }
  34. // 关闭证书库
  35. CLOSE_STORE:
  36. if (hCertStore)
  37. {
  38. CertCloseStore(hCertStore, 0);
  39. hCertStore = NULL;
  40. }
  41. return ulRes;
  42. }
如在特殊的情况下,需要处理整个证书链中的所有证书,则只需要循环调用CertEnumCertificatesInStore()知道返回为NULL为止。

三、解码PFX证书文件

解码PFX证书时,和处理P7B很相似,只是多了密码检验。示例代码如下:

  1. ULONG CCSPCertificate::_DecodePfxCert(LPBYTE lpCertData, ULONG ulDataLen, LPSTR lpscPassword)
  2. {
  3. ULONG ulRes = 0;
  4. HCERTSTORE hCertStore = NULL;
  5. PCCERT_CONTEXT pCertContext = NULL;
  6. USES_CONVERSION;
  7. if (!lpCertData || ulDataLen == 0)
  8. {
  9. return CERT_ERR_INVALIDPARAM;
  10. }
  11. // 创建证书库
  12. CRYPT_DATA_BLOB dataBlob = {ulDataLen, lpCertData};
  13. hCertStore = PFXImportCertStore(&dataBlob, lpscPassword ? A2W(lpscPassword) : NULL, CRYPT_EXPORTABLE);
  14. if ( NULL == hCertStore)
  15. {
  16. hCertStore = PFXImportCertStore(&dataBlob, L"", CRYPT_EXPORTABLE);
  17. }
  18. if ( NULL == hCertStore)
  19. {
  20. ulRes = GetLastError();
  21. return ulRes;
  22. }
  23. // 枚举证书,只处理第一个证书
  24. while(pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext))
  25. {
  26. if (pCertContext->pbCertEncoded && pCertContext->cbCertEncoded > 0)
  27. {
  28. m_pCertContext = CertDuplicateCertificateContext(pCertContext);
  29. break;
  30. }
  31. }
  32. // 关闭证书库
  33. CertCloseStore(hCertStore, 0);
  34. hCertStore = NULL;
  35. return ulRes;
  36. }

至此,三种常见证书文件的解码以完成,通过解码得到的证书上下文结构体指针m_pCertContext 就可以解析证书的项和扩展属性了。具体的解析方法,将在后续的Blog中逐一介绍。


猜你喜欢

转载自blog.csdn.net/ayang1986/article/details/81025657