[转贴]使用CryptoAPI解析X509证书和P12证书

原文在 http://bbs.pediy.com/archive/index.php?t-97663.html,但是觉得这篇文章非常好,我抄下来作我笔记用

一、解析X509证书

1.从磁盘上的证书文件中读取证书数据
unsigned char* pbX509Data; // 证书数据
unsigned long ulX509DataLen; // 证书数据长度

2.获取CertContext
PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, pbX509Data, ulX509DataLen);

3.获取证书信息
pCertContext->pCertInfo->dwVersion; // 证书版本号
CRYPT_INTEGER_BLOB snBlob = pCertContext->pCertInfo->SerialNumber; // 证书SN
CERT_NAME_BLOB issuerBlob = pCertContext->pCertInfo->Issuer; // 证书颁发者
CERT_NAME_BLOB subjectBlob = pCertContext->pCertInfo->Subject; // 证书主题
// 证书有效起始日期
SYSTEMTIME sysTime;
memset(&sysTime, 0, sizeof(sysTime));
FileTimeToSystemTime(&pCertContext->pCertInfo->NotBefore, &sysTime);
char szTime[128] = {0};
sprintf_s(szTime, 128, "%d年%d月%d日 %d:%d:%d", sysTime.wYear,
sysTime.wMonth, sysTime.wDay, sysTime.wHour, sysTime.wMinute,
sysTime.wSecond);
// 证书有效终止日期
memset(&sysTime, 0, sizeof(sysTime));
FileTimeToSystemTime(&pCertContext->pCertInfo->NotAfter, &sysTime);
memset(szTime, 0, sizeof(szTime));
sprintf_s(szTime, 128, "%d年%d月%d日 %d:%d:%d", sysTime.wYear,
sysTime.wMonth, sysTime.wDay, sysTime.wHour, sysTime.wMinute,
sysTime.wSecond);

4.创建临时密钥容器
HCRYPTPROV hTmpProv = NULL;
if (RCRYPT_FAILED(CryptAcquireContext(&hTmpProv,
"My_Temporary_Container", NULL, PROV_RSA_AES, CRYPT_NEWKEYSET))) //
NULL表示使用系统默认CSP
{
if (RCRYPT_FAILED(CryptAcquireContext(&hTmpProv,
"My_Temporary_Container", NULL, PROV_RSA_AES, 0))) // NULL表示使用系统默认CSP
{
CertFreeCertificateContext(pCertContext);
return NTE_FAIL;
}
}

5.向容器中导入公钥,获取公钥句柄
HCRYPTKEY hKey = NULL;
CERT_PUBLIC_KEY_INFO certPubKeyInfo = pCertContext->pCertInfo->SubjectPublicKeyInfo;
CryptImportPublicKeyInfo(hTmpProv, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, &certPubKeyInfo, &hKey);

6.导出公钥(最好采用二次调用方式)
unsigned char* pBuf = NULL;
unsigned long ulBufLen = 0;
CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, pBuf, &ulBufLen);
pBuf = new unsigned char[ulBufLen];
memset(pBuf, 0, ulBufLen);
CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, pBuf, &ulBufLen);

7.获取公钥信息
unsigned char* p = pBuf + sizeof(PUBLICKEYSTRUC);
(*(RSAPUBKEY*)p).bitlen; // 公钥模长(以bit为单位)
(*(RSAPUBKEY*)p).pubexp; // 公钥的e(注意字节顺序)
p += sizeof(RSAPUBKEY); // 公钥的n(注意字节顺序)

8.清理工作
delete[] pBuf;
pBuf = NULL;
CryptDestroyKey(hKey);
CryptReleaseContext(hTmpProv, 0);
CertFreeCertificateContext(pCertContext);

二、解析P12证书

1.从磁盘上的证书文件中读取证书数据
unsigned char* pbP12Data; // 证书数据
unsigned long ulP12DataLen; // 证书数据长度

2.让用户输入证书密码
char* szPwd; // 证书密码

3.将证书密码转换成UNICODE格式(最好采用二次调用方式)
LPWSTR pWideChar = NULL;
int nWideChar = 0;
nWideChar = MultiByteToWideChar(CP_ACP, 0, szPwd, -1, pWideChar, nWideChar);
pWideChar = new WCHAR[nWideChar];
memset(pWideChar, 0, sizeof(WCHAR)*nWideChar);
MultiByteToWideChar(CP_ACP, 0, szPwd, -1, pWideChar, nWideChar);

4.将证书数据导入临时store
CRYPT_DATA_BLOB blob;
memset(&blob, 0, sizeof(blob));
blob.pbData = pbP12Data;
blob.cbData = ulP12DataLen;
HCERTSTORE hCertStore = NULL;
hCertStore = PFXImportCertStore(&blob, pWideChar, CRYPT_EXPORTABLE);

5.在store中查找证书,获取CertContext
PCCERT_CONTEXT pCertContext = CertFindCertificateInStore(hCertStore,
X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL);

6.获取证书信息
CRYPT_INTEGER_BLOB snBlob = pCertContext->pCertInfo->SerialNumber; // 证书SN
pCertContext->pbCertEncoded; // X509格式证书数据
pCertContext->cbCertEncoded; // X509格式证书数据长度

7.获取CSP句柄
HCRYPTPROV hProv = NULL;
DWORD dwKeySpec = 0;
BOOL bCallerFreeProv = FALSE;
CryptAcquireCertificatePrivateKey(pCertContext, 0, NULL, &hProv, &dwKeySpec, &bCallerFreeProv);

8.获取密钥句柄
HCRYPTKEY hKey = NULL;
CryptGetUserKey(hProv, dwKeySpec, &hKey);

9.导出私钥(最好采用二次调用方式)
BYTE* pbData = NULL;
DWORD dwDataLen = 0;
CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, pbData, &dwDataLen);
pbData = new BYTE[dwDataLen];
memset(pbData, 0, dwDataLen);
CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, pbData, &dwDataLen);

10.获取公私钥信息
BYTE *p = pbData+ sizeof(PUBLICKEYSTRUC);
(*(RSAPUBKEY*)p).bitlen; // 公私钥模长(以bit为单位)
(*(RSAPUBKEY*)p).pubexp; // 公钥的e(注意字节顺序)
p += sizeof(RSAPUBKEY); // 公私钥的n(注意字节顺序)
p += ((*(RSAPUBKEY*)p).bitlen)/8; // 私钥的p(注意字节顺序)
p += ((*(RSAPUBKEY*)p).bitlen)/16; // 私钥的q(注意字节顺序)
p += ((*(RSAPUBKEY*)p).bitlen)/16; // 私钥的dp(注意字节顺序)
p += ((*(RSAPUBKEY*)p).bitlen)/16; // 私钥的dq(注意字节顺序)
p += ((*(RSAPUBKEY*)p).bitlen)/16; // 私钥的qu(注意字节顺序)
p += ((*(RSAPUBKEY*)p).bitlen)/16; // 私钥的d(注意字节顺序)

11.清理工作
delete[] pbData;
pbData = NULL;
CryptDestroyKey(hKey);
CryptReleaseContext(hProv, 0);
CertFreeCertificateContext(pCertContext);
CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
delete[] pWideChar;
pWideChar = NULL;

注:为了缩短篇幅,上述代码没有包含错误和异常处理。

时间: 2024-10-10 16:00:21

[转贴]使用CryptoAPI解析X509证书和P12证书的相关文章

使用CryptoAPI解析X509证书和P12证书

读入一个CER文件,CertCreateCertificateContext生成CERT_CONTEXT.CERT_CONTEXT这个数据结构中有一项CERT_INFO,CERT_INFO里面有一项SubjectPublicKeyInfo,  SubjectPublicKeyInfo是一段PUBLICKEY_INFO类型的数据段,里面保存的是算法的ID和DER编码的公钥.有了这段DER编码,我们就可以用其他工具(比如openssl)来解析公钥的各项参数.遗憾的是,CryptoAPI定义的接口却不

CSP:使用CryptoAPI解析X509证书基本项

在之前的文章"CSP:使用CryptoAPI解码X509证书内容"里,讲述了如何使用CryptoAPI将证书文件解码,得到证书上下文句柄PCCERT_CONTEXT的方法.下面我们接着讲述如何通过证书上下文句柄,获得想要的证书项.本文先讲述如何获取证书的基本项,后面还有文章介绍如何获取证书的扩展项. 下面的代码,都是假定已经通过解码证书文件.得到了证书上下文句柄m_pCertContext.至于如何解码证书文件.得到证书上下文句柄m_pCertContext,请阅读之前的文章. 首先,

CSP:使用CryptoAPI获取X509证书的HASH(指纹)值

请关注之前的系列文章 使用CryptoAPI解码X509证书内容 使用CryptoAPI解析X509证书基本项 通过之前的文章,我们可以使用CryptoAPI解码X509证书文件,并且解析证书的基本项,这次我们尝试通过CryptoAPI获取证书的HASH值.证书的HASH值,也称指纹值,是检查证书的完整性.正确性的属性.如果使用父证书来验证证书的签名时,就会使用到HASH值. 通过Windows查看证书的方式看到的证书HASH(指纹)值如下: 有了前面系列文章的基础,我们假设已经通过Crypto

使用windows crypt API解析X509证书

一.版本号 结构体CERT_INFO中的字段dwVersion即为证书版本,可以直接通过下面的代码获得: DWORD dwCertVer = m_pCertContext->pCertInfo->dwVersion; 版本值的定义如下: #define CERT_V1     0 #define CERT_V2     1 #define CERT_V3     2 也就是说,V1的值为0:V3的值为2,目前绝大多是证书都是V3版本. 二.序列号 序列号对应结构体CERT_INFO中的字段Se

Openssl解析私pfx/p12证书(1)

PKCS#12标准描述了个人标识信息的语法,一种交换数字证书的加密标准,包括用户公钥.私钥.证书等.Openssl提供了API供我们解析pfx/p12文件,提取我们需要的信息. 首先我们需要了解几个数据结构,由于Openssl文档里面有些介绍的不是很详细,在这里列举一下: 1.X509 struct typedef struct x509_st X509; struct x509_st { X509_CINF *cert_info;//证书主体信息 X509_ALGOR *sig_alg;//签

java 解析国密SM2算法证书

首先说明用Java自带的解析x509证书类,是不能解析sm2算法的证书,运行会抛出异常. 用开源库bouncycastle可以解析.具体代码 private byte[] getCSPK(byte[] csCert) { InputStream inStream = new ByteArrayInputStream(csCert); ASN1Sequence seq = null; ASN1InputStream aIn; try { aIn = new ASN1InputStream(inSt

【openssl】利用openssl完成X509证书和PFX证书之间的互转

利用openssl完成X509证书和PFX证书之间的互转 # OpenSSL的下载与安装: 1.下载地址: 官方网址—— https://www.openssl.org/source/ OpenSSL for Windows —— http://gnuwin32.sourceforge.net/packages/openssl.htm 2.安装:此处已OpenSSL for Windows为例 * 点击openssl-0.9.8h-1-setup.exe 进行安装: * 添加环境变量:path

SSL/TLS深度解析--OpenSSL 生成自签证书

密钥算法 OpenSSL 支持 RSA.DSA 和 ECDSA 密钥,但是在实际场景中只是用 RSA 和 ECDSA 密钥.例如 Web 服务器的密钥,都使用RSA或ECDSA,因为DSA效率问题会限制在1024位(相对旧版本浏览器不支持更长的DSA密钥),ECDSA还没有全面范围的普及.比如SSH,一般都是使用DSA和RSA,而不是所有的客户端(不只是浏览器)都支持ECDSA算法. 密钥长度 默认的密钥长度一般都不够安全,老版本的 OpenSSL 默认 RSA 私钥是1024位,所以我们需要指

IOS push对应的p12证书制作方法

1.用钥匙串生成一个CertificateSigningRequest.certSigningRequest文件 2.新建appid 然后下载对应证书 3.双击ssl对应的证书 如:aps_development.cer 4.再钥匙串中展开aps_development.cer安装后的证书,导出其中的专用密钥(导出格式为p12) 5.将下载下来的ssl证书aps_development.cer转成pem格式,命令: openssl x509 -in aps_production.cer -inf