CSP:使用CryptoAPI解碼X509證書內容

        微軟的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;
}
如在特殊的情況下,需要處理整個證書鏈中的所有證書,則只需要循環調用CertEnumCertificatesInStore()知道返回爲NULL爲止。

三、解碼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中逐一介紹。

相關博文:CSP:使用CryptoAPI解析X509證書基本項

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章