I want to verify an SSL certificate in Win32 using C++. I think I want to use the Cert* API so that I can get the benefit of the Windows certificate store. This is what I've come up with.
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, ¶ms, 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; }
To view certificates for the current user Select Run from the Start menu, and then enter certmgr. msc. The Certificate Manager tool for the current user appears. To view your certificates, under Certificates - Current User in the left pane, expand the directory for the type of certificate you want to view.
SSL-enabled client software always requires server authentication, or cryptographic validation by a client of the server's identity. The server sends the client a certificate to authenticate itself. The client uses the certificate to authenticate the identity the certificate claims to represent.
You should be aware of RFC3280 section 6.1 and RFC5280 section 6.1. Both describe algorithms for validating certificate paths. Even though Win32 API takes care of some things for you, it could still be valuable to know about the process in general.
Also, here’s a (in my opinion) pretty trustworthy reference: Chromium certificate verification code.
Overall, I think your code isn't incorrect. But here’s a few things I’d look into/change, if I were you:
Chromium validates certificate common name separately from the chain. Apparently they've noticed some problems with it. See the comments for their rationale:
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.
Chromium also uses the CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT flag instead of CERT_CHAIN_REVOCATION_CHECK_CHAIN. I actually started to looking into this before I found their code, and it reinforced my belief that you should use CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT.
Even though both aforementioned RFCs specify that a self-signed trust anchor is not considered part of a chain, the documentation for CertGetCertificateChain (http://msdn.microsoft.com/en-us/library/windows/desktop/aa376078(v=vs.85).aspx) says it builds a chain up to, if possible, a trusted root certificate. A trusted root certificate is defined (on the same page) as a trusted self-signed certificate.
This eliminates the possibility that *EXCLUDE_ROOT might skip revocation checking for a non-root trust anchor (Win32 actually requires trust-anchors to be self-signed, even though it is not required by any RFCs. Though this is not officially documented).
Now, since a root CA certificate can not revoke itself (the CRL could not be signed/verified), it seems to me that these two flags are identical.
I did some googling and stumbled across this forum post: http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/9f95882a-1a68-477a-80ee-0a7e3c7ae5cf/x509revocationflag-question?forum=windowssecurity. A member of .NET Product Group (supposedly) claims that the flags in practice act the same, if the root is self-signed (in theory, the ENTIRE_CHAIN flag would check the root certificate for revocation if it included a CDP extension, but that can’t happen).
He also recommends to use the *EXCLUDE_ROOT flag, because the other flag could cause an unnecessary network request, if the self-signed root CA includes the CDP extension.
Unfortunately:
To be completely sure that it’s ok to use CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, I googled a bit more and found the Chromium SSL certificate verification code I linked to at the top of my reply.
As an added bonus, the Chromium cert_verify_proc_win.cc file contains the following hints about IE verification code:
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.
Not sure how they’d know this, but at this point I’d feel comfortable using CERT_CHAIN_REVOCATION_CHECK_EXCLUDE_ROOT.
I noticed Chromium also specifies 3 certificate usages instead of 1:
szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE
From what I can gather through Google, the other usages can be required by older web browsers, otherwise they can fail to establish a secure connection.
If Chromium deems fit to include these usages, I'd follow suit.
Note that if you change your code, you should also set params.RequestedUsage.dwType to USAGE_MATCH_TYPE_OR instead of USAGE_MATCH_TYPE_AND.
—
I can’t think of any other comments at the moment. But if I were you, I’d check out Chromium source myself (and maybe Firefox too) - just to be sure I haven’t missed anything.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With