转载自: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为二进制数据):
-
ULONG CCSPCertificate::_DecodeX509Cert(LPBYTE lpCertData, ULONG ulDataLen)
-
{
-
if (!lpCertData || ulDataLen == 0)
-
{
-
return CERT_ERR_INVALIDPARAM;
-
}
-
-
m_pCertContext = CertCreateCertificateContext(GLOBAL_ENCODING_TYPE, lpCertData, ulDataLen);
-
if (!m_pCertContext)
-
{
-
return GetLastError();
-
}
-
-
return CERT_ERR_OK;
-
}
二、解码P7B证书文件
由于P7B是个证书链文件,理论上可以包含多个X509证书。但是实际应用中,往往只包含一个文件,所以我们只处理第一个证书。示例代码如下:
-
ULONG CCSPCertificate::_DecodeP7bCert(LPBYTE lpCertData, ULONG ulDataLen)
-
{
-
ULONG ulRes = CERT_ERR_OK;
-
ULONG ulFlag = CRYPT_FIRST;
-
ULONG ulContainerNameLen = 512;
-
CHAR csContainerName[ 512] = { 0};
-
BOOL bFoundContainer = FALSE;
-
-
if (!lpCertData || ulDataLen == 0)
-
{
-
return CERT_ERR_INVALIDPARAM;
-
}
-
-
// 由证书链创建一个证书库
-
HCERTSTORE hCertStore = NULL;
-
CRYPT_DATA_BLOB dataBlob = {ulDataLen, lpCertData};
-
hCertStore = CertOpenStore(CERT_STORE_PROV_PKCS7, GLOBAL_ENCODING_TYPE, NULL, 0, &dataBlob);
-
if ( NULL == hCertStore)
-
{
-
ulRes = GetLastError();
-
return ulRes;
-
}
-
-
// 释放之前的证书内容
-
if (m_pCertContext)
-
{
-
CertFreeCertificateContext(m_pCertContext);
-
m_pCertContext = NULL;
-
}
-
-
// 得到第一个证书内容
-
m_pCertContext = CertEnumCertificatesInStore(hCertStore, m_pCertContext);
-
if ( NULL == m_pCertContext)
-
{
-
ulRes = GetLastError();
-
goto CLOSE_STORE;
-
}
-
-
// 关闭证书库
-
CLOSE_STORE:
-
if (hCertStore)
-
{
-
CertCloseStore(hCertStore, 0);
-
hCertStore = NULL;
-
}
-
-
return ulRes;
-
}
三、解码PFX证书文件
解码PFX证书时,和处理P7B很相似,只是多了密码检验。示例代码如下:
-
ULONG CCSPCertificate::_DecodePfxCert(LPBYTE lpCertData, ULONG ulDataLen, LPSTR lpscPassword)
-
{
-
ULONG ulRes = 0;
-
HCERTSTORE hCertStore = NULL;
-
PCCERT_CONTEXT pCertContext = NULL;
-
-
USES_CONVERSION;
-
-
if (!lpCertData || ulDataLen == 0)
-
{
-
return CERT_ERR_INVALIDPARAM;
-
}
-
-
// 创建证书库
-
CRYPT_DATA_BLOB dataBlob = {ulDataLen, lpCertData};
-
hCertStore = PFXImportCertStore(&dataBlob, lpscPassword ? A2W(lpscPassword) : NULL, CRYPT_EXPORTABLE);
-
if ( NULL == hCertStore)
-
{
-
hCertStore = PFXImportCertStore(&dataBlob, L"", CRYPT_EXPORTABLE);
-
}
-
if ( NULL == hCertStore)
-
{
-
ulRes = GetLastError();
-
return ulRes;
-
}
-
-
// 枚举证书,只处理第一个证书
-
while(pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext))
-
{
-
if (pCertContext->pbCertEncoded && pCertContext->cbCertEncoded > 0)
-
{
-
m_pCertContext = CertDuplicateCertificateContext(pCertContext);
-
break;
-
}
-
}
-
-
// 关闭证书库
-
CertCloseStore(hCertStore, 0);
-
hCertStore = NULL;
-
-
return ulRes;
-
}
至此,三种常见证书文件的解码以完成,通过解码得到的证书上下文结构体指针m_pCertContext 就可以解析证书的项和扩展属性了。具体的解析方法,将在后续的Blog中逐一介绍。