簡體   English   中英

在Win32中驗證SSL證書的正確方法是什么?

[英]What's the correct way to verify an SSL certificate in Win32?

我想使用C ++在Win32中驗證SSL證書。 我想我想使用Cert * API,以便我可以獲得Windows證書存儲的好處。 這就是我想出來的。

  • 這是對的嗎?
  • 有一個更好的方法嗎?
  • 我做錯了嗎?
bool IsValidSSLCertificate( PCCERT_CONTEXT certificate, LPWSTR serverName )
{
    LPTSTR usages[] = { szOID_PKIX_KP_SERVER_AUTH };

    CERT_CHAIN_PARA params                           = { sizeof( params ) };
    params.RequestedUsage.dwType                     = USAGE_MATCH_TYPE_AND;
    params.RequestedUsage.Usage.cUsageIdentifier     = _countof( usages );
    params.RequestedUsage.Usage.rgpszUsageIdentifier = usages;

    PCCERT_CHAIN_CONTEXT chainContext = 0;

    if ( !CertGetCertificateChain( NULL,
                                   certificate,
                                   NULL,
                                   NULL,
                                   &params,
                                   CERT_CHAIN_REVOCATION_CHECK_CHAIN,
                                   NULL,
                                   &chainContext ) )
    {
        return false;
    }

    SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslPolicy = { sizeof( sslPolicy ) };
    sslPolicy.dwAuthType                       = AUTHTYPE_SERVER;
    sslPolicy.pwszServerName                   = serverName;

    CERT_CHAIN_POLICY_PARA policy = { sizeof( policy ) };
    policy.pvExtraPolicyPara      = &sslPolicy;

    CERT_CHAIN_POLICY_STATUS status = { sizeof( status ) };

    BOOL verified = CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
                                                      chainContext,
                                                      &policy,
                                                      &status );

    CertFreeCertificateChain( chainContext );
    return verified && status.dwError == 0;
}

您應該了解RFC3280第6.1節RFC5280第6.1節 兩者都描述了驗證證書路徑的算法。 盡管Win32 API會為您處理一些事情,但一般來說了解該過程仍然很有價值。

此外,這是(在我看來)非常值得信賴的參考: Chromium證書驗證碼

總的來說,我認為你的代碼並不正確。 但是,如果我是你,我會調查/改變一些事情:

1.單獨的通用名稱驗證

Chromium將證書公用名與鏈分開驗證。 顯然他們已經注意到了一些問題。 請參閱評論的理由:

cert_verify_proc.win.cc:731 // Certificate name validation happens separately, later, using an internal
cert_verify_proc.win.cc:732 // routine that has better support for RFC 6125 name matching.

2.使用CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT

Chromium還使用CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT標志而不是CERT_CHAIN_REVOCATION_CHECK_CHAIN。 在我找到他們的代碼之前,我實際上開始研究這個問題,並且它強化了我的信念,即你應該使用CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT。

即使前面提到的RFC都指定自簽名信任錨不被視為鏈的一部分,但CertGetCertificateChain的文檔( http://msdn.microsoft.com/en-us/library/windows/desktop/aa376078(v= vs.85).aspx )表示如果可能的話,它會構建一個鏈,直到可信的根證書。 受信任的根證書(在同一頁面上)定義為受信任的自簽名證書。

這消除了* EXCLUDE_ROOT可能跳過非root信任錨的撤銷檢查的可能性(Win32實際上需要信任錨自簽名,即使任何RFC都不需要它。雖然這沒有正式記錄)。

現在,由於根CA證書無法撤銷(CRL無法簽名/驗證),因此在我看來這兩個標志是相同的。

我做了一些谷歌搜索,偶然發現了這個論壇帖子: http ://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/9f95882a-1a68-477a-80ee-0a7e3c7ae5cf/x509revocationflag-question?forum= windowssecurity .NET產品組的成員(據稱)聲稱,如果根是自簽名的,實際中的標志行為相同(理論上,如果它包含CDP擴展名,ENTIRE_CHAIN標志將檢查根證書是否撤銷,但是不可能發生)。

他還建議使用* EXCLUDE_ROOT標志,因為如果自簽名根CA包含CDP擴展,則另一個標志可能會導致不必要的網絡請求。

不幸:

  • 關於兩面旗幟之間的差異,我找不到任何正式記錄的解釋。
  • 盡管鏈接的討論可能適用於.NET引擎下的相同Win32 API標志,但不能保證。

為了完全確定可以使用CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,我搜索了一下,找到了我在回復頂部鏈接到的Chromium SSL證書驗證碼。

作為額外的好處,Chromium cert_verify_proc_win.cc文件包含有關IE驗證代碼的以下提示:

618: // IE passes a non-NULL pTime argument that specifies the current system
619: // time.  IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the
620: // chain_flags argument.

不知道他們怎么知道這個,但是在這一點上我覺得使用CERT_CHAIN_REVOCATION_CHECK_EXCLUDE_ROOT感覺很舒服。

3.不同的接受證書用法

我注意到Chromium還指定了3個證書用法而不是1個:

szOID_PKIX_KP_SERVER_AUTH,
szOID_SERVER_GATED_CRYPTO,
szOID_SGC_NETSCAPE

從我可以通過谷歌收集的內容來看,舊的網絡瀏覽器可能需要其他用法,否則他們可能無法建立安全連接。

如果Chromium認為適合包括這些用法,我會效仿。

請注意,如果更改代碼,還應將params.RequestedUsage.dwType設置為USAGE_MATCH_TYPE_OR而不是USAGE_MATCH_TYPE_AND。

-

我現在想不出任何其他評論。 但如果我是你,我會自己檢查Chromium來源(也可能是Firefox) - 只是為了確保我沒有錯過任何東西。

我認為最好的答案取決於你究竟想要做什么。

我會提醒您,SSL基於兩個端點都需要安全連接的假設。 如果任一端點對維護安全性不感興趣,那么就沒有。

將字節代碼放入分布式代碼中,只需對此函數返回true即可輕松完成。 這就是為什么Windows將大量驗證移入內核的原因。 但他們沒有預料到人們會在虛擬硬件上運行Windows,這使得繞過操作系統變得微不足道。

現在考慮您希望從某個來源提供證書,但假裝無法從可靠的來源提供相同的信息。 然后交給你。 所以你不能依靠證書“證明”任何人都是特別的人。

從證書中獲得的唯一保護是防止外部人員而不是端點違反正在傳輸的消息的機密性。

任何其他用途注定要失敗,並且最終會因潛在的災難性后果而失敗。

對不起,這個大帖子。 評論部分有一個字數限制。

CertGetCertificateChainCertVerifyCertificatePolicy功能一起使用。 這部分是正確的。

對於CertGetCertificateChain ,如果要檢查吊銷,則可以將標志設置為以下三種中的任何一種:

  • CERT_CHAIN_REVOCATION_CHECK_END_CERT
  • CERT_CHAIN_REVOCATION_CHECK_CHAIN
  • CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT。

只能使用其中一個,這三個選項不能進行ORed 除了其中一個標志,你可以考慮如何創建鏈; 使用local cache或僅使用CRLOCSP 有關這些注意事項請閱讀此鏈接

如果返回值為0 ,則執行函數時出錯或更簡單,這並不意味着證書無效,而是您無法執行操作。 有關錯誤信息,請使用GetLastError() 因此,返回false的邏輯是錯誤的,更多的是拋出錯誤並讓客戶端代碼決定是再次嘗試還是繼續做其他事情。

這個鏈接中有一個名為“分類錯誤”的部分,請閱讀。 基本上你應該檢查certChainContext->TrustStatus.dwErrorStatus. Here a list of error statuses will be ORed. Please check CERT_TRUST_STATUS certChainContext->TrustStatus.dwErrorStatus. Here a list of error statuses will be ORed. Please check CERT_TRUST_STATUS certChainContext->TrustStatus.dwErrorStatus. Here a list of error statuses will be ORed. Please check CERT_TRUST_STATUS msdn參考。 所以在這里你可以擁有自己的業務邏輯。 例如,如果您發現無法執行證書吊銷檢查的值( CERT_TRUST_REVOCATION_STATUS_UNKNOWN | CERT_TRUST_IS_OFFLINE_REVOCATION )的錯誤狀態,您可以選擇決定您想要的內容(讓證書繼續或仍將其標記為無效)。

因此,在調用CertVerifyCertificatePolicy之前,您可以選擇丟棄或已標記驗證錯誤。

如果您選擇訪問CertVerifyCertificatePolicy ,則CertVerifyCertificatePolicy代碼是關於如何將policy_status.dwError映射到錯誤類/枚舉的精彩參考。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM