2011-09-07 29 views
19

Tôi muốn xác minh chứng chỉ SSL trong Win32 bằng C++. Tôi nghĩ rằng tôi muốn sử dụng API Cert * để tôi có thể nhận được lợi ích của cửa hàng chứng chỉ Windows. Đây là những gì tôi đã đưa ra.Cách chính xác để xác minh chứng chỉ SSL trong Win32 là gì?

  • Có đúng không?
  • Có cách nào tốt hơn để thực hiện việc này không?
  • Tôi có làm gì sai không?
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; 
} 
+0

Bạn không đề cập đến những gì bạn đang sử dụng cho, nhưng có, nói chung bạn nên vượt qua CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT nếu bạn đang sử dụng điều này trong một kịch bản HTTPS điển hình. – EricLaw

+0

Tôi chủ yếu muốn sử dụng điều này để xác minh chứng chỉ SSL máy chủ LDAP (như bên trong hàm VERIFYSERVERCERT). Tôi cũng đang nghĩ đến việc sử dụng nó để xác minh chứng chỉ máy chủ HTTPS trong ứng dụng khách/máy chủ nơi khách hàng có thể chỉ định chứng chỉ SSL của riêng họ cho máy chủ. – briangreenery

+0

Có thường sử dụng CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT thay vì CERT_CHAIN_REVOCATION_CHECK_CHAIN ​​không? Tại sao bạn không kiểm tra chứng chỉ gốc để thu hồi? – briangreenery

Trả lời

1

Tôi nghĩ câu trả lời tốt nhất phụ thuộc vào những gì chính xác bạn đang cố gắng để làm.

Tôi sẽ cảnh báo bạn rằng SSL dựa trên giả định rằng cả hai thiết bị đầu cuối đều muốn kết nối an toàn. Nếu một trong hai điểm cuối không quan tâm đến việc duy trì bảo mật thì không có điểm cuối nào cả.

Một nỗ lực tầm thường của nó để đặt mã byte trong mã được phân phối của bạn mà chỉ trả về giá trị đúng cho hàm này. Đó là lý do tại sao các cửa sổ chuyển rất nhiều xác nhận vào nhân. Nhưng họ không lường trước được những người đang chạy các cửa sổ trên phần cứng ảo, điều này làm cho việc phá vỡ hệ điều hành chỉ là tầm thường.

Bây giờ, hãy xem xét rằng bạn mong muốn được cung cấp một chứng nhận từ một số nguồn, nhưng giả vờ rằng nguồn đó không thể được cung cấp cùng một thông tin từ một nguồn đáng tin cậy. Và sau đó đưa nó cho bạn. Vì vậy, bạn không thể dựa vào giấy chứng nhận để "chứng minh" bất cứ ai là bất cứ ai nói riêng.

Bảo vệ duy nhất thu được từ chứng chỉ là ngăn chặn người ngoài, chứ không phải điểm cuối, vi phạm tính bảo mật của thư đang được chuyển.

Bất kỳ việc sử dụng nào khác đều phải chịu số phận thất bại và cuối cùng sẽ thất bại với kết quả có khả năng thảm khốc.

Xin lỗi vì bài đăng lớn. Phần bình luận có giới hạn từ.

+2

Cảm ơn câu trả lời này, trong khi thông tin chứa có giá trị, tôi hy vọng rằng ai đó có thể xác minh rằng mã được mô tả ở trên là chính xác trong ngữ cảnh xác minh chứng chỉ để sử dụng SSL bằng API mã hóa Windows. Tôi cũng nhận thức được những hạn chế của SSL trong những khía cạnh này, nhưng ở đây tôi chỉ thực sự quan tâm đến việc làm cho điểm cuối của tôi hoàn toàn chính xác. Đối với ngữ cảnh, tôi đã làm việc trên [miếng vá này] (https://github.com/php/php-src/pull/601) cho lõi PHP - vì vậy chúng tôi không lo lắng về một ứng dụng cụ thể, chỉ có các công cụ chúng tôi cung cấp là chính xác. – DaveRandom

14

Bạn nên biết về RFC3280 section 6.1RFC5280 section 6.1. Cả hai đều mô tả các thuật toán để xác thực đường dẫn chứng chỉ. Mặc dù Win32 API chăm sóc một số thứ cho bạn, nhưng vẫn có thể có giá trị để biết về quy trình nói chung.

Ngoài ra, đây là (theo ý kiến ​​của tôi) tham khảo khá đáng tin cậy: Chromium certificate verification code.

Nói chung, tôi nghĩ mã của bạn không chính xác. Nhưng đây là một vài điều tôi muốn nhìn vào/thay đổi, nếu tôi là bạn:

1. Tách Common Name Validation

Chromium xác nhận giấy chứng nhận tên gọi chung tách biệt khỏi chuỗi. Rõ ràng họ đã nhận thấy một số vấn đề với nó. Xem các ý kiến ​​cho lý do của họ:

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. Sử dụng CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT

Chromium cũng sử dụng lá cờ CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT thay vì CERT_CHAIN_REVOCATION_CHECK_CHAIN. Tôi thực sự bắt đầu xem xét điều này trước khi tôi tìm thấy mã của họ và nó củng cố niềm tin của tôi rằng bạn nên sử dụng CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT.

Mặc dù cả hai RFC nói trên chỉ ra rằng một neo tin cậy tự ký không được coi là một phần của chuỗi, tài liệu cho CertGetCertificateChain (http://msdn.microsoft.com/en-us/library/windows/desktop/aa376078(v=vs.85).aspx) nói rằng nó xây dựng một chuỗi cho tới khi có thể. Chứng chỉ gốc đáng tin cậy được xác định (trên cùng một trang) dưới dạng chứng chỉ tự ký đáng tin cậy.

Điều này loại bỏ khả năng * EXCLUDE_ROOT có thể bỏ qua việc kiểm tra thu hồi đối với một neo tin cậy không phải gốc (Win32 thực sự yêu cầu các trust-anchor phải tự ký, mặc dù nó không được yêu cầu bởi bất kỳ RFC nào. tài liệu).

Bây giờ, vì chứng chỉ CA gốc không thể tự thu hồi (CRL không thể được ký/xác minh), có vẻ như với tôi rằng hai cờ này giống hệt nhau.

Tôi đã làm một số googling và vấp trên bài đăng trên diễn đàn này: http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/9f95882a-1a68-477a-80ee-0a7e3c7ae5cf/x509revocationflag-question?forum=windowssecurity. Một thành viên của nhóm sản phẩm .NET (được cho là) ​​tuyên bố rằng cờ trong thực tế cũng giống nhau, nếu thư mục gốc tự ký (theo lý thuyết, cờ ENTIRE_CHAIN ​​sẽ kiểm tra chứng chỉ gốc để thu hồi nếu nó bao gồm phần mở rộng CDP, nhưng không thể xảy ra).

Ông cũng khuyên bạn nên sử dụng cờ * EXCLUDE_ROOT vì cờ khác có thể gây ra yêu cầu mạng không cần thiết, nếu CA gốc tự ký bao gồm phần mở rộng CDP.

Thật không may:

  • tôi không thể tìm thấy bất kỳ lời giải thích chính thức ghi nhận về sự khác biệt giữa hai lá cờ.
  • Mặc dù có khả năng là các cuộc thảo luận được liên kết áp dụng cho cùng một cờ Win32 API dưới mui xe của .NET, nó không được đảm bảo.

Để được hoàn toàn chắc chắn rằng đó là ok để sử dụng CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, tôi googled hơn một chút và tìm thấy mã xác minh giấy chứng nhận SSL Chromium tôi liên kết với ở đầu trả lời của tôi.

Là một tiền thưởng thêm, các tập tin cert_verify_proc_win.cc Chromium chứa các gợi ý sau đây về mã xác minh 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. 

Không chắc cách họ muốn biết điều này, nhưng tại thời điểm này, tôi muốn cảm thấy thoải mái sử dụng CERT_CHAIN_REVOCATION_CHECK_EXCLUDE_ROOT.

3. Giấy chứng nhận được chấp nhận khác nhau Công dụng

tôi nhận thấy Chromium cũng quy định cụ thể 3 tập quán giấy chứng nhận thay cho 1:

szOID_PKIX_KP_SERVER_AUTH, 
szOID_SERVER_GATED_CRYPTO, 
szOID_SGC_NETSCAPE 

Từ những gì tôi có thể thu thập thông qua Google, các tập quán khác có thể được yêu cầu của web cũ hơn trình duyệt, nếu không chúng có thể không thiết lập kết nối an toàn.

Nếu Chromium thích hợp để bao gồm các tập quán này, tôi sẽ làm theo.

Lưu ý rằng nếu bạn thay đổi mã, bạn cũng nên đặt params.RequestedUsage.dwType thành USAGE_MATCH_TYPE_OR thay vì USAGE_MATCH_TYPE_AND.

-

Tôi không thể nghĩ ra bất kỳ nhận xét nào khác vào lúc này.Nhưng nếu tôi là bạn, tôi sẽ tự mình kiểm tra nguồn Chromium (và có thể cả Firefox nữa) - chỉ để chắc chắn tôi đã không bỏ lỡ bất cứ điều gì.

-1

Các chức năng CertGetCertificateChainCertVerifyCertificatePolicy đi cùng nhau. Phần này là chính xác.

Đối CertGetCertificateChain cờ có thể được thiết lập để một trong ba sau đây nếu bạn muốn kiểm tra thu hồi:

  • CERT_CHAIN_REVOCATION_CHECK_END_CERT
  • CERT_CHAIN_REVOCATION_CHECK_CHAIN ​​
  • CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT.

Chỉ một trong số chúng có thể được sử dụng, ba tùy chọn này không thể là ORed. Bên cạnh một trong các cờ này, bạn có thể xem xét cách tạo chuỗi; sử dụng local cache hoặc chỉ CRL hoặc OCSP. Đối với những cân nhắc này read this link.

Lỗi khi thực hiện hàm hoặc đơn giản hơn nếu giá trị trả về là 0, điều đó không có nghĩa chứng chỉ không hợp lệ, thay vào đó bạn không thể thực hiện thao tác. Để biết thông tin lỗi, hãy sử dụng GetLastError(). Vì vậy, logic của bạn trả về false là sai, nó là nhiều hơn của một trường hợp ném lỗi và để cho mã khách hàng quyết định có nên thử lại hoặc tiếp tục làm công cụ khác.

Trong this link có phần có tên "phân loại lỗi", vui lòng đọc phần đó. Về cơ bản bạn nên kiểm tra tham chiếu certChainContext->TrustStatus.dwErrorStatus. Here a list of error statuses will be ORed. Please check CERT_TRUST_STATUS msdn. Vì vậy, ở đây bạn có thể có logic kinh doanh của bạn. Ví dụ: nếu bạn thấy trạng thái lỗi của giá trị (CERT_TRUST_REVOCATION_STATUS_UNKNOWN | CERT_TRUST_IS_OFFLINE_REVOCATION) không thể thực hiện kiểm tra thu hồi chứng chỉ, bạn có tùy chọn quyết định những gì bạn muốn (để chứng chỉ đi hoặc vẫn đánh dấu là không hợp lệ).

Vì vậy, trước khi gọi CertVerifyCertificatePolicy bạn có tùy chọn hủy hoặc đã gắn cờ lỗi xác thực.

Nếu bạn chọn truy cập CertVerifyCertificatePolicy, mã crôm là tham chiếu tuyệt vời về cách ánh xạ policy_status.dwError với lớp lỗi/enum của bạn.

Các vấn đề liên quan