// Copyright 2017 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/cert/x509_util_nss.h" #include #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/time/time.h" #include "net/cert/scoped_nss_types.h" #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" #include "net/test/cert_test_util.h" #include "net/test/test_certificate_data.h" #include "net/test/test_data_directory.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace { std::string BytesForNSSCert(CERTCertificate* cert) { std::string der_encoded; if (!x509_util::GetDEREncoded(cert, &der_encoded)) ADD_FAILURE(); return der_encoded; } } // namespace TEST(X509UtilNSSTest, IsSameCertificate) { ScopedCERTCertificate google_nss_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(google_nss_cert); ScopedCERTCertificate google_nss_cert2( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(google_nss_cert2); ScopedCERTCertificate webkit_nss_cert( x509_util::CreateCERTCertificateFromBytes(webkit_der)); ASSERT_TRUE(webkit_nss_cert); scoped_refptr google_x509_cert( X509Certificate::CreateFromBytes(google_der)); ASSERT_TRUE(google_x509_cert); scoped_refptr webkit_x509_cert( X509Certificate::CreateFromBytes(webkit_der)); ASSERT_TRUE(webkit_x509_cert); EXPECT_TRUE(x509_util::IsSameCertificate(google_nss_cert.get(), google_nss_cert.get())); EXPECT_TRUE(x509_util::IsSameCertificate(google_nss_cert.get(), google_nss_cert2.get())); EXPECT_TRUE(x509_util::IsSameCertificate(google_nss_cert.get(), google_x509_cert.get())); EXPECT_TRUE(x509_util::IsSameCertificate(google_x509_cert.get(), google_nss_cert.get())); EXPECT_TRUE(x509_util::IsSameCertificate(webkit_nss_cert.get(), webkit_nss_cert.get())); EXPECT_TRUE(x509_util::IsSameCertificate(webkit_nss_cert.get(), webkit_x509_cert.get())); EXPECT_TRUE(x509_util::IsSameCertificate(webkit_x509_cert.get(), webkit_nss_cert.get())); EXPECT_FALSE(x509_util::IsSameCertificate(google_nss_cert.get(), webkit_nss_cert.get())); EXPECT_FALSE(x509_util::IsSameCertificate(google_nss_cert.get(), webkit_x509_cert.get())); EXPECT_FALSE(x509_util::IsSameCertificate(google_x509_cert.get(), webkit_nss_cert.get())); } TEST(X509UtilNSSTest, CreateCERTCertificateFromBytes) { ScopedCERTCertificate google_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(google_cert); EXPECT_STREQ( "CN=www.google.com,O=Google Inc,L=Mountain View,ST=California,C=US", google_cert->subjectName); } TEST(X509UtilNSSTest, CreateCERTCertificateFromBytesGarbage) { EXPECT_EQ(nullptr, x509_util::CreateCERTCertificateFromBytes( base::span())); static const uint8_t garbage_data[] = "garbage"; EXPECT_EQ(nullptr, x509_util::CreateCERTCertificateFromBytes(garbage_data)); } TEST(X509UtilNSSTest, CreateCERTCertificateFromX509Certificate) { scoped_refptr x509_cert = ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"); ASSERT_TRUE(x509_cert); ScopedCERTCertificate nss_cert = x509_util::CreateCERTCertificateFromX509Certificate(x509_cert.get()); ASSERT_TRUE(nss_cert); EXPECT_STREQ("CN=127.0.0.1,O=Test CA,L=Mountain View,ST=California,C=US", nss_cert->subjectName); } TEST(X509UtilNSSTest, CreateCERTCertificateListFromX509Certificate) { scoped_refptr x509_cert = CreateCertificateChainFromFile( GetTestCertsDirectory(), "multi-root-chain1.pem", X509Certificate::FORMAT_PEM_CERT_SEQUENCE); ASSERT_TRUE(x509_cert); EXPECT_EQ(3U, x509_cert->intermediate_buffers().size()); ScopedCERTCertificateList nss_certs = x509_util::CreateCERTCertificateListFromX509Certificate(x509_cert.get()); ASSERT_EQ(4U, nss_certs.size()); for (int i = 0; i < 4; ++i) ASSERT_TRUE(nss_certs[i]); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(x509_cert->cert_buffer()), BytesForNSSCert(nss_certs[0].get())); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece( x509_cert->intermediate_buffers()[0].get()), BytesForNSSCert(nss_certs[1].get())); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece( x509_cert->intermediate_buffers()[1].get()), BytesForNSSCert(nss_certs[2].get())); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece( x509_cert->intermediate_buffers()[2].get()), BytesForNSSCert(nss_certs[3].get())); } TEST(X509UtilTest, CreateCERTCertificateListFromX509CertificateErrors) { scoped_refptr ok_cert( ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); ASSERT_TRUE(ok_cert); bssl::UniquePtr bad_cert = x509_util::CreateCryptoBuffer(std::string_view("invalid")); ASSERT_TRUE(bad_cert); scoped_refptr ok_cert2( ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem")); ASSERT_TRUE(ok_cert); std::vector> intermediates; intermediates.push_back(std::move(bad_cert)); intermediates.push_back(bssl::UpRef(ok_cert2->cert_buffer())); scoped_refptr cert_with_intermediates( X509Certificate::CreateFromBuffer(bssl::UpRef(ok_cert->cert_buffer()), std::move(intermediates))); ASSERT_TRUE(cert_with_intermediates); EXPECT_EQ(2U, cert_with_intermediates->intermediate_buffers().size()); // Normal CreateCERTCertificateListFromX509Certificate fails with invalid // certs in chain. ScopedCERTCertificateList nss_certs = x509_util::CreateCERTCertificateListFromX509Certificate( cert_with_intermediates.get()); EXPECT_TRUE(nss_certs.empty()); // With InvalidIntermediateBehavior::kIgnore, invalid intermediate certs // should be silently dropped. nss_certs = x509_util::CreateCERTCertificateListFromX509Certificate( cert_with_intermediates.get(), x509_util::InvalidIntermediateBehavior::kIgnore); ASSERT_EQ(2U, nss_certs.size()); for (const auto& nss_cert : nss_certs) ASSERT_TRUE(nss_cert.get()); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert->cert_buffer()), BytesForNSSCert(nss_certs[0].get())); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert2->cert_buffer()), BytesForNSSCert(nss_certs[1].get())); } TEST(X509UtilNSSTest, CreateCERTCertificateListFromBytes) { base::FilePath cert_path = GetTestCertsDirectory().AppendASCII("multi-root-chain1.pem"); std::string cert_data; ASSERT_TRUE(base::ReadFileToString(cert_path, &cert_data)); ScopedCERTCertificateList certs = x509_util::CreateCERTCertificateListFromBytes( cert_data.data(), cert_data.size(), X509Certificate::FORMAT_AUTO); ASSERT_EQ(4U, certs.size()); EXPECT_STREQ("CN=127.0.0.1,O=Test CA,L=Mountain View,ST=California,C=US", certs[0]->subjectName); EXPECT_STREQ("CN=B CA - Multi-root", certs[1]->subjectName); EXPECT_STREQ("CN=C CA - Multi-root", certs[2]->subjectName); EXPECT_STREQ("CN=D Root CA - Multi-root", certs[3]->subjectName); } TEST(X509UtilNSSTest, DupCERTCertificate) { ScopedCERTCertificate cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(cert); ScopedCERTCertificate cert2 = x509_util::DupCERTCertificate(cert.get()); // Both handles should hold a reference to the same CERTCertificate object. ASSERT_EQ(cert.get(), cert2.get()); // Release the initial handle. cert.reset(); // The duped handle should still be safe to access. EXPECT_STREQ( "CN=www.google.com,O=Google Inc,L=Mountain View,ST=California,C=US", cert2->subjectName); } TEST(X509UtilNSSTest, DupCERTCertificateList) { ScopedCERTCertificate cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(cert); ScopedCERTCertificate cert2( x509_util::CreateCERTCertificateFromBytes(webkit_der)); ASSERT_TRUE(cert2); ScopedCERTCertificateList certs; certs.push_back(std::move(cert)); certs.push_back(std::move(cert2)); ScopedCERTCertificateList certs_dup = x509_util::DupCERTCertificateList(certs); ASSERT_EQ(2U, certs_dup.size()); ASSERT_EQ(certs[0].get(), certs_dup[0].get()); ASSERT_EQ(certs[1].get(), certs_dup[1].get()); // Release the initial handles. certs.clear(); // The duped handles should still be safe to access. EXPECT_STREQ( "CN=www.google.com,O=Google Inc,L=Mountain View,ST=California,C=US", certs_dup[0]->subjectName); EXPECT_STREQ( "CN=*.webkit.org,OU=Mac OS Forge,O=Apple " "Inc.,L=Cupertino,ST=California,C=US", certs_dup[1]->subjectName); } TEST(X509UtilNSSTest, DupCERTCertificateList_EmptyList) { EXPECT_EQ(0U, x509_util::DupCERTCertificateList({}).size()); } TEST(X509UtilNSSTest, CreateX509CertificateFromCERTCertificate_NoChain) { ScopedCERTCertificate nss_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(nss_cert); scoped_refptr x509_cert = x509_util::CreateX509CertificateFromCERTCertificate(nss_cert.get()); EXPECT_EQ(BytesForNSSCert(nss_cert.get()), x509_util::CryptoBufferAsStringPiece(x509_cert->cert_buffer())); EXPECT_TRUE(x509_cert->intermediate_buffers().empty()); } TEST(X509UtilNSSTest, CreateX509CertificateFromCERTCertificate_EmptyChain) { ScopedCERTCertificate nss_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(nss_cert); scoped_refptr x509_cert = x509_util::CreateX509CertificateFromCERTCertificate( nss_cert.get(), std::vector()); EXPECT_EQ(BytesForNSSCert(nss_cert.get()), x509_util::CryptoBufferAsStringPiece(x509_cert->cert_buffer())); EXPECT_TRUE(x509_cert->intermediate_buffers().empty()); } TEST(X509UtilNSSTest, CreateX509CertificateFromCERTCertificate_WithChain) { ScopedCERTCertificate nss_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(nss_cert); ScopedCERTCertificate nss_cert2( x509_util::CreateCERTCertificateFromBytes(webkit_der)); ASSERT_TRUE(nss_cert2); std::vector chain; chain.push_back(nss_cert2.get()); scoped_refptr x509_cert = x509_util::CreateX509CertificateFromCERTCertificate(nss_cert.get(), chain); EXPECT_EQ(BytesForNSSCert(nss_cert.get()), x509_util::CryptoBufferAsStringPiece(x509_cert->cert_buffer())); ASSERT_EQ(1U, x509_cert->intermediate_buffers().size()); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece( x509_cert->intermediate_buffers()[0].get()), BytesForNSSCert(nss_cert2.get())); } TEST(X509UtilNSSTest, CreateX509CertificateListFromCERTCertificates) { ScopedCERTCertificate nss_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(nss_cert); ScopedCERTCertificate nss_cert2( x509_util::CreateCERTCertificateFromBytes(webkit_der)); ASSERT_TRUE(nss_cert2); ScopedCERTCertificateList nss_certs; nss_certs.push_back(std::move(nss_cert)); nss_certs.push_back(std::move(nss_cert2)); CertificateList x509_certs = x509_util::CreateX509CertificateListFromCERTCertificates(nss_certs); ASSERT_EQ(2U, x509_certs.size()); EXPECT_EQ(BytesForNSSCert(nss_certs[0].get()), x509_util::CryptoBufferAsStringPiece(x509_certs[0]->cert_buffer())); EXPECT_EQ(BytesForNSSCert(nss_certs[1].get()), x509_util::CryptoBufferAsStringPiece(x509_certs[1]->cert_buffer())); } TEST(X509UtilNSSTest, CreateX509CertificateListFromCERTCertificates_EmptyList) { ScopedCERTCertificateList nss_certs; CertificateList x509_certs = x509_util::CreateX509CertificateListFromCERTCertificates(nss_certs); ASSERT_EQ(0U, x509_certs.size()); } TEST(X509UtilNSSTest, GetDEREncoded) { ScopedCERTCertificate google_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(google_cert); std::string der_encoded; ASSERT_TRUE(x509_util::GetDEREncoded(google_cert.get(), &der_encoded)); EXPECT_EQ(std::string(reinterpret_cast(google_der), std::size(google_der)), der_encoded); } TEST(X509UtilNSSTest, GetDefaultNickname) { base::FilePath certs_dir = GetTestCertsDirectory(); ScopedCERTCertificate test_cert = ImportCERTCertificateFromFile( certs_dir, "no_subject_common_name_cert.pem"); ASSERT_TRUE(test_cert); std::string nickname = x509_util::GetDefaultUniqueNickname( test_cert.get(), USER_CERT, nullptr /*slot*/); EXPECT_EQ( "wtc@google.com's COMODO Client Authentication and " "Secure Email CA ID", nickname); } TEST(X509UtilNSSTest, GetCERTNameDisplayName_CN) { base::FilePath certs_dir = GetTestCertsDirectory(); ScopedCERTCertificate test_cert = ImportCERTCertificateFromFile(certs_dir, "ok_cert.pem"); ASSERT_TRUE(test_cert); scoped_refptr x509_test_cert = ImportCertFromFile(certs_dir, "ok_cert.pem"); ASSERT_TRUE(x509_test_cert); std::string name = x509_util::GetCERTNameDisplayName(&test_cert->subject); EXPECT_EQ("127.0.0.1", name); EXPECT_EQ(x509_test_cert->subject().GetDisplayName(), name); } TEST(X509UtilNSSTest, GetCERTNameDisplayName_O) { base::FilePath certs_dir = GetTestNetDataDirectory().AppendASCII("parse_certificate_unittest"); ScopedCERTCertificate test_cert = ImportCERTCertificateFromFile(certs_dir, "subject_t61string.pem"); ASSERT_TRUE(test_cert); scoped_refptr x509_test_cert = ImportCertFromFile(certs_dir, "subject_t61string.pem"); ASSERT_TRUE(x509_test_cert); std::string name = x509_util::GetCERTNameDisplayName(&test_cert->subject); EXPECT_EQ( " !\"#$%&'()*+,-./" "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" "abcdefghijklmnopqrstuvwxyz{|}~" " ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæç" "èéêëìíîïðñòóôõö÷øùúûüýþÿ", name); EXPECT_EQ(x509_test_cert->subject().GetDisplayName(), name); } TEST(X509UtilNSSTest, ParseClientSubjectAltNames) { base::FilePath certs_dir = GetTestCertsDirectory(); // This cert contains one rfc822Name field, and one Microsoft UPN // otherName field. ScopedCERTCertificate san_cert = ImportCERTCertificateFromFile(certs_dir, "client_3.pem"); ASSERT_TRUE(san_cert); std::vector rfc822_names; x509_util::GetRFC822SubjectAltNames(san_cert.get(), &rfc822_names); ASSERT_EQ(1U, rfc822_names.size()); EXPECT_EQ("santest@example.com", rfc822_names[0]); std::vector upn_names; x509_util::GetUPNSubjectAltNames(san_cert.get(), &upn_names); ASSERT_EQ(1U, upn_names.size()); EXPECT_EQ("santest@ad.corp.example.com", upn_names[0]); } TEST(X509UtilNSSTest, GetValidityTimes) { ScopedCERTCertificate google_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(google_cert); base::Time not_before, not_after; EXPECT_TRUE( x509_util::GetValidityTimes(google_cert.get(), ¬_before, ¬_after)); // Constants copied from x509_certificate_unittest.cc. EXPECT_EQ(1238192407, // Mar 27 22:20:07 2009 GMT not_before.InSecondsFSinceUnixEpoch()); EXPECT_EQ(1269728407, // Mar 27 22:20:07 2010 GMT not_after.InSecondsFSinceUnixEpoch()); } TEST(X509UtilNSSTest, GetValidityTimesOptionalArgs) { ScopedCERTCertificate google_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(google_cert); base::Time not_before; EXPECT_TRUE( x509_util::GetValidityTimes(google_cert.get(), ¬_before, nullptr)); // Constants copied from x509_certificate_unittest.cc. EXPECT_EQ(1238192407, // Mar 27 22:20:07 2009 GMT not_before.InSecondsFSinceUnixEpoch()); base::Time not_after; EXPECT_TRUE( x509_util::GetValidityTimes(google_cert.get(), nullptr, ¬_after)); EXPECT_EQ(1269728407, // Mar 27 22:20:07 2010 GMT not_after.InSecondsFSinceUnixEpoch()); } TEST(X509UtilNSSTest, CalculateFingerprint256) { static const SHA256HashValue google_fingerprint = { {0x21, 0xaf, 0x58, 0x74, 0xea, 0x6b, 0xad, 0xbd, 0xe4, 0xb3, 0xb1, 0xaa, 0x53, 0x32, 0x80, 0x8f, 0xbf, 0x8a, 0x24, 0x7d, 0x98, 0xec, 0x7f, 0x77, 0x49, 0x38, 0x42, 0x81, 0x26, 0x7f, 0xed, 0x38}}; ScopedCERTCertificate google_cert( x509_util::CreateCERTCertificateFromBytes(google_der)); ASSERT_TRUE(google_cert); EXPECT_EQ(google_fingerprint, x509_util::CalculateFingerprint256(google_cert.get())); } } // namespace net