【问题标题】:X509 Certificate verification with Windows Crypto API使用 Windows Crypto API 进行 X509 证书验证
【发布时间】:2017-01-18 17:46:09
【问题描述】:

我需要为 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 链文件来验证证书签名。

我会感谢所有的建议和帮助。

【问题讨论】:

  • 不要自己编写加密代码。
  • 同意@stark,考虑使用内置CertCreateCertificateChainEngine函数:msdn.microsoft.com/en-us/library/windows/desktop/…
  • 其实我不打算自己写,问题是,我不知道如何使用 Crypt api。
  • 感谢@Crypt32,我已经开始研究如何将 CertCreateCertificateChainEngine 用于我的特定目的。
  • @Crypt32,不幸的是,在深入研究 CertCreateCertificateChainEngine 几天后,无法找到使用此功能的正确解决方案。

标签: c x509certificate cryptoapi


【解决方案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");

【讨论】:

  • 感谢@ravin.wang 分享您的解决方案。实际上我放弃了原生代码并决定在我从事那个项目时使用openssl。现在我无法在那个项目上测试你的代码,但希望有人能找到这个代码,让我们知道它是否对这个特殊需求有用。
  • 我写了一个windows OPM管理的runnable sample,在github.com/wangf1978/D3DTest中有可运行的代码,相关的实现和示例代码在CryptoUtil.cpp和OPMSessioncpp
猜你喜欢
  • 2011-09-02
  • 1970-01-01
  • 2018-07-28
  • 1970-01-01
  • 2012-09-28
  • 1970-01-01
  • 2016-01-14
  • 2018-06-30
  • 2011-04-06
相关资源
最近更新 更多