从磁盘上加载证书文件
2024-03-17 17:33:41

方式一

将证书视为存储库,通过 CertOpenStore 打开,枚举出第一个证书即可。
一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
template <typename T>
T decodeObject(DWORD dwEncodingType, const PCERT_EXTENSION pExt)
{
T pInfo = nullptr;
DWORD cbInfo = 0;
if (!CryptDecodeObjectEx(dwEncodingType, pExt->pszObjId, pExt->Value.pbData, pExt->Value.cbData, CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, nullptr, &pInfo, &cbInfo))
return nullptr;
return pInfo;
}

// 证书既是存储库
HCERTSTORE hCertStore = CertOpenStore(CERT_STORE_PROV_FILENAME_A, dwEncodingType, NULL, CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, "ca.cer");
if (hCertStore) {
// 取出第一个证书
PCCERT_CONTEXT pCertContext = CertEnumCertificatesInStore(hCertStore, nullptr);
if (pCertContext) {
// 遍历扩展信息
for (DWORD i = 0; i < pCertContext->pCertInfo->cExtension; ++i) {
const PCERT_EXTENSION pExt = pCertContext->pCertInfo->rgExtension + i;
if (strcmp(pExt->pszObjId, szOID_KEY_USAGE) == 0) {
// 密钥用法
const auto pInfo = decodeObject<PCRYPT_BIT_BLOB>(dwEncodingType, pExt);
LocalFree(pInfo);
} else if(strcmp(pExt->pszObjId, szOID_BASIC_CONSTRAINTS2) == 0) {
// 基本约束
const auto pInfo = decodeObject<PCERT_BASIC_CONSTRAINTS2_INFO>(dwEncodingType, pExt);
LocalFree(pInfo);
} else if (strcmp(pExt->pszObjId, szOID_ENHANCED_KEY_USAGE) == 0) {
// 增强型密钥用法
auto pInfo = decodeObject<PCTL_USAGE>(dwEncodingType, pExt);
LocalFree(pInfo);
} else if (strcmp(pExt->pszObjId, szOID_SUBJECT_ALT_NAME2) == 0) {
// 使用者可选名称
const auto pInfo = decodeObject<PCERT_ALT_NAME_INFO>(dwEncodingType, pExt);
LocalFree(pInfo);
}
}
}
CertCloseStore(hCertStore, 0);
}

方式二

CryptoAPI 中,一个证书文件用 CERT_CONTEXT 结构描述,所以只要将证书文件转换为 CERT_CONTEXT 结构即可。
CertCreateCertificateContext 函数可以做到,它从内存中读取证书数据并转换为 CERT_CONTEXT 结构。
但是要注意,这个函数只接受DER编码格式的证书,而我们常用的一般是PEM格式(以BEGIN开头,END结尾,中间是BASE64编码),所以要进行转换。
转换可以用 CryptStringToBinaryA 函数搞定。
一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const DWORD dwEncodingType = X509_ASN_ENCODING;

// 打开证书
HANDLE hFile = CreateFileW(L"ca.cer", GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
LARGE_INTEGER fileSize;
GetFileSizeEx(hFile, &fileSize);

// 读取证书内容
BYTE* buffer = new BYTE[fileSize.QuadPart];
DWORD dwWritten;
ReadFile(hFile, buffer, fileSize.QuadPart, &dwWritten, nullptr);

// PEM转DER
BYTE* bin = new BYTE[dwWritten];
DWORD flags;
BOOL ok = CryptStringToBinaryA((LPCSTR)buffer, dwWritten, CRYPT_STRING_BASE64HEADER, bin, &dwWritten, NULL, &flags);

// 获得证书上下文
PCCERT_CONTEXT pCert = CertCreateCertificateContext(dwEncodingType, bin, dwWritten);

参考

Load a Certificate from a File

CertCreateCertificateContext returns CRYPT_E_ASN1_BADTAG / 8009310b