我需要为Windows编写一个C程序,它从网络接收证书(采用PEM格式),并使用证书链文件(已在应用程序的文件夹中显示)验证其签名。
使用openssl库编写这样的应用程序非常容易并且前进很好但是使用Windows Crypto API似乎有些复杂。
这是我到目前为止所尝试的内容:
首先我想我可以使用证书链文件创建一个HCERTSTORE:
HCERTSTORE hFileStoreHandle = CertOpenStore(
CERT_STORE_PROV_FILENAME,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL,
(CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG),
L"certificate-chain.pem.cert");
然后我想我可以遍历商店并获得证书的PCCERT_CONTEXT结构:
PCCERT_CONTEXT CAfileContext = NULL;
while(CAfileContext = CertEnumCertificatesInStore(
hCertStore,
CAfileContext)) {
//start verification here
}
我不知道我是否走在正确的轨道上,但我在这里遇到两个主要问题。
首先,我不知道如何从缓冲区获取收到的证书,并将其转换为正确的结构,以便使用证书链文件验证其签名。
其次是我不知道如何使用CA链文件验证证书签名。
我会感谢所有的建议和帮助。
答案 0 :(得分:1)
这是为您提供的示例代码,希望它可以为您提供帮助。
HRESULT hr = E_FAIL;
DWORD dwError = 0;
PCCERT_CONTEXT pCert = NULL;
HCERTSTORE hCertStore = CertOpenStore(
CERT_STORE_PROV_FILENAME,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
NULL,
(CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG),
L"certificate-chain.pem.cert");
do
{
if ((pCert = CertEnumCertificatesInStore(hCertStore, pCert)) == NULL)
break;
PCCERT_CONTEXT pcIssuer = NULL;
PCCERT_CONTEXT pcSubject = CertDuplicateCertificateContext(pCert);
for (; NULL != pcSubject;)
{
DWORD dwFlags = 0;
BOOL bret = TRUE;
hr = S_OK;
pcIssuer = CertGetIssuerCertificateFromStore(
hCertStore, pcSubject, NULL, &dwFlags);
if (pcIssuer == NULL)
{
dwError = GetLastError();
if (CRYPT_E_SELF_SIGNED != dwError)
{
hr = E_FAIL;
break;
}
else
{
if ((bret = CertCompareCertificateName(
X509_ASN_ENCODING,
&pcSubject->pCertInfo->Subject,
&pcSubject->pCertInfo->Issuer)) == FALSE)
{
hr = E_FAIL;
break;
}
}
}
HCRYPTPROV hprov = NULL;
bret = CryptAcquireContext(
&hprov, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET);
if (FALSE == bret)
{
dwError = GetLastError();
if (NTE_EXISTS == dwError)
bret = CryptAcquireContext(
&hprov, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_VERIFYCONTEXT);
if (FALSE == bret)
{
hr = E_FAIL;
break;
}
}
if ((bret = CryptVerifyCertificateSignatureEx(
hprov, X509_ASN_ENCODING,
CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
(void*)pcSubject,
CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
(void*)(pcIssuer == nullptr ? pcSubject : pcIssuer),
0, nullptr)) == FALSE)
{
hr = E_FAIL;
break;
}
if (nullptr == pcIssuer)
{
CERT_PUBLIC_KEY_INFO msCert;
msCert.Algorithm =
pcSubject->pCertInfo->SubjectPublicKeyInfo.Algorithm;
msCert.PublicKey.cbData =
sizeof(PETRUSTED_ROOTCERT_PUBKEY);
msCert.PublicKey.pbData =
const_cast<LPBYTE>(PETRUSTED_ROOTCERT_PUBKEY);
msCert.PublicKey.cUnusedBits =
pcSubject->pCertInfo->SubjectPublicKeyInfo.PublicKey.cUnusedBits;
if (FALSE == CertComparePublicKeyInfo(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&pcSubject->pCertInfo->SubjectPublicKeyInfo, &msCert))
{
hr = E_FAIL;
break;
}
}
bret = CryptReleaseContext(hprov, 0);
hprov = NULL;
CertFreeCertificateContext(pcSubject);
pcSubject = pcIssuer;
pcIssuer = NULL;
}
if (pcIssuer != NULL)
CertFreeCertificateContext(pcIssuer);
if (pcSubject != NULL)
CertFreeCertificateContext(pcSubject);
if (FAILED(hr))
{
CertFreeCertificateContext(pCert);
break;
}
} while (pCert != NULL);
CertCloseStore(hCertStore, 0);
if (FAILED(hr))
wprintf(L"Failed to verify X509 certification.\n");
else
wprintf(L"Successfully verify X509 certification.\n");