| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/tools/cert_verify_tool/verify_using_cert_verify_proc.h" |
| |
| #include <iostream> |
| |
| #include "base/cxx17_backports.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "crypto/sha2.h" |
| #include "net/base/net_errors.h" |
| #include "net/cert/cert_verifier.h" |
| #include "net/cert/cert_verify_proc.h" |
| #include "net/cert/cert_verify_result.h" |
| #include "net/cert/test_root_certs.h" |
| #include "net/cert/x509_certificate.h" |
| #include "net/cert/x509_util.h" |
| #include "net/log/net_log_with_source.h" |
| #include "net/tools/cert_verify_tool/cert_verify_tool_util.h" |
| |
| namespace { |
| |
| // Associates a printable name with an integer constant. Useful for providing |
| // human-readable decoding of bitmask values. |
| struct StringToConstant { |
| const char* name; |
| const int constant; |
| }; |
| |
| const StringToConstant kCertStatusFlags[] = { |
| #define CERT_STATUS_FLAG(label, value) {#label, value}, |
| #include "net/cert/cert_status_flags_list.h" |
| #undef CERT_STATUS_FLAG |
| }; |
| |
| // Writes a PEM-encoded file of |cert| and its chain. |
| bool DumpX509CertificateChain(const base::FilePath& file_path, |
| const net::X509Certificate* cert) { |
| std::vector<std::string> pem_encoded; |
| if (!cert->GetPEMEncodedChain(&pem_encoded)) { |
| std::cerr << "ERROR: X509Certificate::GetPEMEncodedChain failed.\n"; |
| return false; |
| } |
| return WriteToFile(file_path, base::StrCat(pem_encoded)); |
| } |
| |
| void PrintCertStatus(int cert_status) { |
| std::cout << base::StringPrintf("CertStatus: 0x%x\n", cert_status); |
| |
| for (const auto& flag : kCertStatusFlags) { |
| if ((cert_status & flag.constant) == flag.constant) |
| std::cout << " " << flag.name << "\n"; |
| } |
| } |
| |
| } // namespace |
| |
| void PrintCertVerifyResult(const net::CertVerifyResult& result) { |
| PrintDebugData(&result); |
| PrintCertStatus(result.cert_status); |
| if (result.has_sha1) |
| std::cout << "has_sha1\n"; |
| if (result.has_sha1_leaf) |
| std::cout << "has_sha1_leaf\n"; |
| if (result.is_issued_by_known_root) |
| std::cout << "is_issued_by_known_root\n"; |
| if (result.is_issued_by_additional_trust_anchor) |
| std::cout << "is_issued_by_additional_trust_anchor\n"; |
| |
| if (result.verified_cert) { |
| std::cout << "chain:\n " |
| << FingerPrintCryptoBuffer(result.verified_cert->cert_buffer()) |
| << " " << SubjectFromX509Certificate(result.verified_cert.get()) |
| << "\n"; |
| for (const auto& intermediate : |
| result.verified_cert->intermediate_buffers()) { |
| std::cout << " " << FingerPrintCryptoBuffer(intermediate.get()) << " " |
| << SubjectFromCryptoBuffer(intermediate.get()) << "\n"; |
| } |
| } |
| } |
| |
| bool VerifyUsingCertVerifyProc( |
| net::CertVerifyProc* cert_verify_proc, |
| const CertInput& target_der_cert, |
| const std::string& hostname, |
| const std::vector<CertInput>& intermediate_der_certs, |
| const std::vector<CertInput>& root_der_certs, |
| net::CRLSet* crl_set, |
| const base::FilePath& dump_path) { |
| std::vector<base::StringPiece> der_cert_chain; |
| der_cert_chain.push_back(target_der_cert.der_cert); |
| for (const auto& cert : intermediate_der_certs) |
| der_cert_chain.push_back(cert.der_cert); |
| |
| scoped_refptr<net::X509Certificate> x509_target_and_intermediates = |
| net::X509Certificate::CreateFromDERCertChain(der_cert_chain); |
| if (!x509_target_and_intermediates) { |
| std::cerr |
| << "ERROR: X509Certificate::CreateFromDERCertChain failed on one or " |
| "more of:\n"; |
| PrintCertError(" (target)", target_der_cert); |
| for (const auto& cert : intermediate_der_certs) |
| PrintCertError(" (intermediate)", cert); |
| return false; |
| } |
| |
| net::CertificateList x509_additional_trust_anchors; |
| for (const auto& cert : root_der_certs) { |
| scoped_refptr<net::X509Certificate> x509_root = |
| net::X509Certificate::CreateFromBytes( |
| base::as_bytes(base::make_span(cert.der_cert))); |
| |
| if (!x509_root) |
| PrintCertError("ERROR: X509Certificate::CreateFromBytes failed:", cert); |
| else |
| x509_additional_trust_anchors.push_back(x509_root); |
| } |
| |
| // TODO(mattm): add command line flags to configure VerifyFlags. |
| int flags = 0; |
| |
| // Not all platforms support providing additional trust anchors to the |
| // verifier. To workaround this, use TestRootCerts to modify the |
| // system trust store globally. |
| net::TestRootCerts* test_root_certs = net::TestRootCerts::GetInstance(); |
| CHECK(test_root_certs->IsEmpty()); |
| |
| net::ScopedTestRoot scoped_test_roots; |
| if (!x509_additional_trust_anchors.empty() && |
| !cert_verify_proc->SupportsAdditionalTrustAnchors()) { |
| std::cerr << "NOTE: Additional trust anchors not supported on this " |
| "platform. Using TestRootCerts instead.\n"; |
| |
| scoped_test_roots.Reset(x509_additional_trust_anchors); |
| x509_additional_trust_anchors.clear(); |
| } |
| |
| // TODO(crbug.com/634484): use a real netlog and print the results? |
| net::CertVerifyResult result; |
| int rv = cert_verify_proc->Verify( |
| x509_target_and_intermediates.get(), hostname, |
| /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags, |
| crl_set, x509_additional_trust_anchors, &result, net::NetLogWithSource()); |
| |
| std::cout << "CertVerifyProc result: " << net::ErrorToShortString(rv) << "\n"; |
| PrintCertVerifyResult(result); |
| if (!dump_path.empty() && result.verified_cert) { |
| if (!DumpX509CertificateChain(dump_path, result.verified_cert.get())) { |
| return false; |
| } |
| } |
| |
| return rv == net::OK; |
| } |