首页
社区
课程
招聘
[原创]使用CryptoAPI解析X509证书和P12证书
2009-9-12 16:17 28238

[原创]使用CryptoAPI解析X509证书和P12证书

2009-9-12 16:17
28238
一、解析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;

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

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞7
打赏
分享
最新回复 (21)
雪    币: 291
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
leking 2009-9-12 20:56
2
0
文章思路很清晰,对大家有帮助
雪    币: 84
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
liudanking 2009-9-12 22:54
3
0
不错呀。很清楚,排版风格也不错。
雪    币: 283
活跃值: (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
nudtsong 1 2009-9-14 10:30
4
0
能否申请加精
雪    币: 240
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
liuhuwei 2009-9-14 11:15
5
0
正在学习这个啊  支持了
雪    币: 1450
活跃值: (35)
能力值: (RANK:680 )
在线值:
发帖
回帖
粉丝
jackozoo 14 2009-9-14 12:40
6
0
把你的完整可编译代码放上来吧, 这样有利于论坛更多的朋友了解相关原理 .
雪    币: 283
活跃值: (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
nudtsong 1 2009-9-14 15:41
7
0
好的,我把代码整理一下再传上来。感谢版主和大家的支持鼓励!
雪    币: 1450
活跃值: (35)
能力值: (RANK:680 )
在线值:
发帖
回帖
粉丝
jackozoo 14 2009-9-14 17:09
8
0
只要是原创的, 确实花了功夫的, 必要的鼓励是应该给与的.
我对此贴有几个建议:
1. 多增加文字描述, 酌情增加相关介绍, 而不是仅仅贴代码 . 这样可以帮到更多的人.
2. 部分代码可以简短带过, 比如delete [], 释放内存之类.
3. 完整可编译代码+bin . 能有演示程序更好.

    这样此贴会更有价值.
雪    币: 104
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zjw 2009-9-15 22:19
9
0
好东东,有价值!
雪    币: 283
活跃值: (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
nudtsong 1 2009-9-17 10:10
10
1
上传源代码及exe文件,欢迎大家指正
上传的附件:
雪    币: 283
活跃值: (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
nudtsong 1 2009-9-17 10:19
11
0
原帖在解析X509证书创建临时密钥容器时漏了一条语句,现已改正,给大家带来不便,特此致歉。
雪    币: 32403
活跃值: (18850)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2009-9-17 10:21
12
0
谢谢分享源码
雪    币: 16
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiaojiuhk 2009-9-17 10:41
13
0
谢谢分享源码.
雪    币: 21
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
一鸿 2009-9-18 21:52
14
0
謝謝樓主提供該詳細資料。
程序都能夠正確運行。
我想請問加載完畢證書之後,能不能實現加解密請求呢?如何實現?
雪    币: 21
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
一鸿 2009-9-27 10:38
15
0
没有安装CryptoAPI,因此运行不了。
雪    币: 144
活跃值: (25)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
wsmfgf 2009-10-21 21:22
16
0
怎么下载不了源码啊?
雪    币: 806
活跃值: (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
zyr零零发 1 2010-2-1 17:02
17
0
Thank you very much.I'm learning cryptoAPI.GOOD DATA!
雪    币: 88
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jameswu 2010-4-22 09:26
18
0
学习了,最近正在看着方面的东西~~
雪    币: 190
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mmkclub 2010-5-11 09:49
19
0
谢谢楼主,好贴一定要顶
雪    币: 287
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wdbg 2010-5-11 10:38
20
0
楼主的编程习惯相当好
雪    币: 288
活跃值: (70)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
hejiwen 2 2010-5-11 12:41
21
0
mark一下
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
spwx 2011-10-26 20:50
22
0
楼主的程序很值得学习,对我最近研究的CSP加解密和生成证书和签名帮助很大,谢谢。。
游客
登录 | 注册 方可回帖
返回