blob: 3074cb638562828be58eb259cfc461d7460366e5 [file] [log] [blame]
// Copyright 2021 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/internal/trust_store_win.h"
#include <algorithm>
#include <memory>
#include <string_view>
#include "base/compiler_specific.h"
#include "base/containers/to_vector.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/win/wincrypt_shim.h"
#include "crypto/scoped_capi_types.h"
#include "net/base/features.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/cert/internal/test_helpers.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/cert/x509_util_win.h"
#include "net/test/cert_builder.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/boringssl/src/include/openssl/pool.h"
#include "third_party/boringssl/src/pki/cert_errors.h"
#include "third_party/boringssl/src/pki/parsed_certificate.h"
#include "third_party/boringssl/src/pki/trust_store.h"
namespace net {
namespace {
::testing::AssertionResult ParseCertFromFile(
std::string_view file_name,
std::shared_ptr<const bssl::ParsedCertificate>* out_cert) {
const scoped_refptr<X509Certificate> cert =
ImportCertFromFile(net::GetTestCertsDirectory(), file_name);
if (!cert) {
return ::testing::AssertionFailure() << "ImportCertFromFile failed";
}
bssl::CertErrors errors;
std::shared_ptr<const bssl::ParsedCertificate> parsed =
bssl::ParsedCertificate::Create(
bssl::UpRef(cert->cert_buffer()),
x509_util::DefaultParseCertificateOptions(), &errors);
if (!parsed) {
return ::testing::AssertionFailure()
<< "bssl::ParseCertificate::Create failed:\n"
<< errors.ToDebugString();
}
*out_cert = parsed;
return ::testing::AssertionSuccess();
}
class TrustStoreWinTest : public testing::Test {
public:
void SetUp() override {
ASSERT_TRUE(ParseCertFromFile("multi-root-A-by-B.pem", &a_by_b_));
ASSERT_TRUE(ParseCertFromFile("multi-root-B-by-C.pem", &b_by_c_));
ASSERT_TRUE(ParseCertFromFile("multi-root-B-by-F.pem", &b_by_f_));
ASSERT_TRUE(ParseCertFromFile("multi-root-C-by-D.pem", &c_by_d_));
ASSERT_TRUE(ParseCertFromFile("multi-root-C-by-E.pem", &c_by_e_));
ASSERT_TRUE(ParseCertFromFile("multi-root-D-by-D.pem", &d_by_d_));
ASSERT_TRUE(ParseCertFromFile("multi-root-E-by-E.pem", &e_by_e_));
ASSERT_TRUE(ParseCertFromFile("multi-root-F-by-E.pem", &f_by_e_));
}
bssl::CertificateTrust ExpectedTrustForAnchor() const {
return bssl::CertificateTrust::ForTrustAnchorOrLeaf()
.WithEnforceAnchorExpiry()
.WithEnforceAnchorConstraints()
.WithRequireLeafSelfSigned();
}
bssl::CertificateTrust ExpectedTrustForPeer() const {
return bssl::CertificateTrust::ForTrustedLeaf().WithRequireLeafSelfSigned();
}
// Returns true if |cert| successfully added to store, false otherwise.
bool AddToStore(HCERTSTORE store,
std::shared_ptr<const bssl::ParsedCertificate> cert) {
crypto::ScopedPCCERT_CONTEXT os_cert(CertCreateCertificateContext(
X509_ASN_ENCODING, CRYPTO_BUFFER_data(cert->cert_buffer()),
CRYPTO_BUFFER_len(cert->cert_buffer())));
return CertAddCertificateContextToStore(store, os_cert.get(),
CERT_STORE_ADD_ALWAYS, nullptr);
}
// Returns true if cert at file_name successfully added to store with
// restricted usage, false otherwise.
bool AddToStoreWithEKURestriction(
HCERTSTORE store,
std::shared_ptr<const bssl::ParsedCertificate> cert,
LPCSTR usage_identifier) {
crypto::ScopedPCCERT_CONTEXT os_cert(CertCreateCertificateContext(
X509_ASN_ENCODING, CRYPTO_BUFFER_data(cert->cert_buffer()),
CRYPTO_BUFFER_len(cert->cert_buffer())));
CERT_ENHKEY_USAGE usage = {};
if (!CertSetEnhancedKeyUsage(os_cert.get(), &usage)) {
return false;
}
if (usage_identifier) {
if (!CertAddEnhancedKeyUsageIdentifier(os_cert.get(), usage_identifier)) {
return false;
}
}
return !!CertAddCertificateContextToStore(store, os_cert.get(),
CERT_STORE_ADD_ALWAYS, nullptr);
}
std::unique_ptr<TrustStoreWin> CreateTrustStoreWin() {
return TrustStoreWin::CreateForTesting(std::move(stores_));
}
// The cert stores that will be used to create the trust store. These handles
// will be null after CreateTrustStoreWin() is called.
TrustStoreWin::CertStores stores_ =
TrustStoreWin::CertStores::CreateInMemoryStoresForTesting();
std::shared_ptr<const bssl::ParsedCertificate> a_by_b_, b_by_c_, b_by_f_,
c_by_d_, c_by_e_, d_by_d_, e_by_e_, f_by_e_;
};
TEST_F(TrustStoreWinTest, GetTrustInitializationError) {
// Simulate an initialization error by using null stores.
std::unique_ptr<TrustStoreWin> trust_store_win =
TrustStoreWin::CreateForTesting(
TrustStoreWin::CertStores::CreateNullStoresForTesting());
ASSERT_TRUE(trust_store_win);
bssl::CertificateTrust trust = trust_store_win->GetTrust(d_by_d_.get());
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust.ToDebugString());
}
TEST_F(TrustStoreWinTest, GetTrust) {
ASSERT_TRUE(AddToStore(stores_.roots.get(), d_by_d_));
ASSERT_TRUE(AddToStore(stores_.intermediates.get(), c_by_d_));
ASSERT_TRUE(AddToStore(stores_.trusted_people.get(), a_by_b_));
std::unique_ptr<TrustStoreWin> trust_store_win = CreateTrustStoreWin();
ASSERT_TRUE(trust_store_win);
// Explicitly trusted root should be trusted.
EXPECT_EQ(ExpectedTrustForAnchor().ToDebugString(),
trust_store_win->GetTrust(d_by_d_.get()).ToDebugString());
// Explicitly trusted peer should be trusted.
// (Although it wouldn't actually verify since it's not self-signed but has
// require_leaf_selfsigned set. That doesn't matter for the purposes of these
// tests.)
EXPECT_EQ(ExpectedTrustForPeer().ToDebugString(),
trust_store_win->GetTrust(a_by_b_.get()).ToDebugString());
// Intermediate for path building should not be trusted.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(c_by_d_.get()).ToDebugString());
// Unknown roots should not be trusted (e.g. just because they're
// self-signed doesn't make them a root)
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(e_by_e_.get()).ToDebugString());
}
// This test has a special TrustStoreWin setup with restricted EKU usages.
// Specifically, the only certs set up in the root store are set up
// as follows:
//
// - kMultiRootDByD: only has szOID_PKIX_KP_SERVER_AUTH EKU set
// - kMultiRootEByE: only has szOID_PKIX_KP_CLIENT_AUTH set
// - kMultiRootCByE: only has szOID_ANY_ENHANCED_KEY_USAGE set
// - kMultiRootCByD: no EKU usages set
TEST_F(TrustStoreWinTest, GetTrustRestrictedEKU) {
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.roots.get(), d_by_d_,
szOID_PKIX_KP_SERVER_AUTH));
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.roots.get(), e_by_e_,
szOID_PKIX_KP_CLIENT_AUTH));
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.roots.get(), c_by_e_,
szOID_ANY_ENHANCED_KEY_USAGE));
ASSERT_TRUE(
AddToStoreWithEKURestriction(stores_.roots.get(), c_by_d_, nullptr));
std::unique_ptr<TrustStoreWin> trust_store_win = CreateTrustStoreWin();
ASSERT_TRUE(trust_store_win);
// Root cert with EKU szOID_PKIX_KP_SERVER_AUTH usage set should be
// trusted.
EXPECT_EQ(ExpectedTrustForAnchor().ToDebugString(),
trust_store_win->GetTrust(d_by_d_.get()).ToDebugString());
// Root cert with EKU szOID_ANY_ENHANCED_KEY_USAGE usage set should be
// trusted.
EXPECT_EQ(ExpectedTrustForAnchor().ToDebugString(),
trust_store_win->GetTrust(c_by_e_.get()).ToDebugString());
// Root cert with EKU szOID_PKIX_KP_CLIENT_AUTH does not allow usage of
// cert for server auth, return UNSPECIFIED.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(e_by_e_.get()).ToDebugString());
// Root cert with no EKU usages, return UNSPECIFIED.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(c_by_d_.get()).ToDebugString());
// Unknown cert has unspecified trust.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(f_by_e_.get()).ToDebugString());
}
// Same as GetTrustRestrictedEKU but for the Trusted People store.
TEST_F(TrustStoreWinTest, GetTrustTrustedPeopleRestrictedEKU) {
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.trusted_people.get(),
d_by_d_, szOID_PKIX_KP_SERVER_AUTH));
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.trusted_people.get(),
e_by_e_, szOID_PKIX_KP_CLIENT_AUTH));
ASSERT_TRUE(AddToStoreWithEKURestriction(
stores_.trusted_people.get(), c_by_e_, szOID_ANY_ENHANCED_KEY_USAGE));
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.trusted_people.get(),
c_by_d_, nullptr));
std::unique_ptr<TrustStoreWin> trust_store_win = CreateTrustStoreWin();
ASSERT_TRUE(trust_store_win);
// TrustedPeople cert with EKU szOID_PKIX_KP_SERVER_AUTH usage set should be
// trusted.
EXPECT_EQ(ExpectedTrustForPeer().ToDebugString(),
trust_store_win->GetTrust(d_by_d_.get()).ToDebugString());
// TrustedPeople cert with EKU szOID_ANY_ENHANCED_KEY_USAGE usage set should
// be trusted.
EXPECT_EQ(ExpectedTrustForPeer().ToDebugString(),
trust_store_win->GetTrust(c_by_e_.get()).ToDebugString());
// TrustedPeople cert with EKU szOID_PKIX_KP_CLIENT_AUTH does not allow usage
// of cert for server auth, return UNSPECIFIED.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(e_by_e_.get()).ToDebugString());
// TrustedPeople cert with no EKU usages, return UNSPECIFIED.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(c_by_d_.get()).ToDebugString());
// Unknown cert has unspecified trust.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(f_by_e_.get()).ToDebugString());
}
// If duplicate certs are added to the root store with different EKU usages,
// the cert should be trusted if any one of the usages is valid.
// Root store set up as follows:
//
// - kMultiRootDByD: only has szOID_PKIX_KP_CLIENT_AUTH EKU set
// - kMultiRootDByD (dupe): only has szOID_PKIX_KP_SERVER_AUTH set
// - kMultiRootDByD (dupe 2): no EKU usages set
TEST_F(TrustStoreWinTest, GetTrustRestrictedEKUDuplicateCerts) {
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.roots.get(), d_by_d_,
szOID_PKIX_KP_CLIENT_AUTH));
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.roots.get(), d_by_d_,
szOID_PKIX_KP_SERVER_AUTH));
ASSERT_TRUE(
AddToStoreWithEKURestriction(stores_.roots.get(), d_by_d_, nullptr));
std::unique_ptr<TrustStoreWin> trust_store_win = CreateTrustStoreWin();
ASSERT_TRUE(trust_store_win);
// One copy of the Root cert is trusted for TLS Server Auth.
EXPECT_EQ(ExpectedTrustForAnchor().ToDebugString(),
trust_store_win->GetTrust(d_by_d_.get()).ToDebugString());
}
// Test that disallowed certs will be distrusted regardless of EKU settings.
TEST_F(TrustStoreWinTest, GetTrustDisallowedCerts) {
ASSERT_TRUE(AddToStore(stores_.roots.get(), d_by_d_));
ASSERT_TRUE(AddToStore(stores_.roots.get(), e_by_e_));
ASSERT_TRUE(AddToStore(stores_.trusted_people.get(), f_by_e_));
ASSERT_TRUE(AddToStoreWithEKURestriction(stores_.disallowed.get(), d_by_d_,
szOID_PKIX_KP_CLIENT_AUTH));
ASSERT_TRUE(AddToStore(stores_.disallowed.get(), e_by_e_));
ASSERT_TRUE(AddToStore(stores_.disallowed.get(), f_by_e_));
std::unique_ptr<TrustStoreWin> trust_store_win = CreateTrustStoreWin();
ASSERT_TRUE(trust_store_win);
// E-by-E is in both root and distrusted store. Distrust takes precedence.
EXPECT_EQ(bssl::CertificateTrust::ForDistrusted().ToDebugString(),
trust_store_win->GetTrust(e_by_e_.get()).ToDebugString());
// F-by-E is in both trusted people and distrusted store. Distrust takes
// precedence.
EXPECT_EQ(bssl::CertificateTrust::ForDistrusted().ToDebugString(),
trust_store_win->GetTrust(f_by_e_.get()).ToDebugString());
// D-by-D is in root and in distrusted but without szOID_PKIX_KP_SERVER_AUTH
// set. It should still be distrusted since the EKU settings aren't checked
// on distrust.
EXPECT_EQ(bssl::CertificateTrust::ForDistrusted().ToDebugString(),
trust_store_win->GetTrust(d_by_d_.get()).ToDebugString());
}
MATCHER_P(ParsedCertEq, expected_cert, "") {
return arg && expected_cert &&
std::ranges::equal(arg->der_cert(), expected_cert->der_cert());
}
TEST_F(TrustStoreWinTest, GetIssuersInitializationError) {
// Simulate an initialization error by using null stores.
std::unique_ptr<TrustStoreWin> trust_store_win =
TrustStoreWin::CreateForTesting(
TrustStoreWin::CertStores::CreateNullStoresForTesting());
ASSERT_TRUE(trust_store_win);
bssl::ParsedCertificateList issuers;
trust_store_win->SyncGetIssuersOf(b_by_f_.get(), &issuers);
ASSERT_EQ(0U, issuers.size());
}
TEST_F(TrustStoreWinTest, GetIssuers) {
ASSERT_TRUE(AddToStore(stores_.roots.get(), d_by_d_));
ASSERT_TRUE(AddToStore(stores_.intermediates.get(), c_by_d_));
ASSERT_TRUE(AddToStore(stores_.intermediates.get(), c_by_e_));
ASSERT_TRUE(AddToStore(stores_.intermediates.get(), f_by_e_));
ASSERT_TRUE(AddToStore(stores_.trusted_people.get(), b_by_c_));
ASSERT_TRUE(AddToStore(stores_.disallowed.get(), b_by_f_));
std::unique_ptr<TrustStoreWin> trust_store_win = CreateTrustStoreWin();
// No matching issuer (Trusted People and Disallowed are not consulted).
{
bssl::ParsedCertificateList issuers;
trust_store_win->SyncGetIssuersOf(a_by_b_.get(), &issuers);
ASSERT_EQ(0U, issuers.size());
}
// Single matching issuer found in intermediates.
{
bssl::ParsedCertificateList issuers;
trust_store_win->SyncGetIssuersOf(b_by_f_.get(), &issuers);
ASSERT_EQ(1U, issuers.size());
EXPECT_THAT(issuers, testing::UnorderedElementsAre(ParsedCertEq(f_by_e_)));
}
// Single matching issuer found in roots.
{
bssl::ParsedCertificateList issuers;
trust_store_win->SyncGetIssuersOf(d_by_d_.get(), &issuers);
ASSERT_EQ(1U, issuers.size());
EXPECT_THAT(issuers, testing::UnorderedElementsAre(ParsedCertEq(d_by_d_)));
}
// Multiple issuers found.
{
bssl::ParsedCertificateList issuers;
trust_store_win->SyncGetIssuersOf(b_by_c_.get(), &issuers);
ASSERT_EQ(2U, issuers.size());
EXPECT_THAT(issuers, testing::UnorderedElementsAre(ParsedCertEq(c_by_d_),
ParsedCertEq(c_by_e_)));
}
}
MATCHER_P(CertWithTrustEq, expected_cert_with_trust, "") {
return arg.cert_bytes == expected_cert_with_trust.cert_bytes &&
arg.trust.ToDebugString() ==
expected_cert_with_trust.trust.ToDebugString();
}
TEST_F(TrustStoreWinTest, GetAllUserAddedCerts) {
ASSERT_TRUE(AddToStore(stores_.roots.get(), d_by_d_));
ASSERT_TRUE(
AddToStoreWithEKURestriction(stores_.roots.get(), c_by_d_, nullptr));
ASSERT_TRUE(AddToStore(stores_.intermediates.get(), c_by_e_));
ASSERT_TRUE(AddToStore(stores_.intermediates.get(), f_by_e_));
ASSERT_TRUE(AddToStore(stores_.trusted_people.get(), b_by_c_));
ASSERT_TRUE(AddToStore(stores_.disallowed.get(), b_by_f_));
std::unique_ptr<TrustStoreWin> trust_store_win = CreateTrustStoreWin();
std::vector<net::PlatformTrustStore::CertWithTrust> certs =
trust_store_win->GetAllUserAddedCerts();
ASSERT_EQ(5U, certs.size());
EXPECT_THAT(certs, testing::UnorderedElementsAre(
CertWithTrustEq(net::PlatformTrustStore::CertWithTrust(
base::ToVector(d_by_d_->der_cert()),
bssl::CertificateTrust::ForTrustAnchorOrLeaf()
.WithEnforceAnchorExpiry()
.WithEnforceAnchorConstraints()
.WithRequireLeafSelfSigned())),
CertWithTrustEq(net::PlatformTrustStore::CertWithTrust(
base::ToVector(c_by_e_->der_cert()),
bssl::CertificateTrust::ForUnspecified())),
CertWithTrustEq(net::PlatformTrustStore::CertWithTrust(
base::ToVector(f_by_e_->der_cert()),
bssl::CertificateTrust::ForUnspecified())),
CertWithTrustEq(net::PlatformTrustStore::CertWithTrust(
base::ToVector(b_by_c_->der_cert()),
bssl::CertificateTrust::ForTrustedLeaf()
.WithRequireLeafSelfSigned())),
CertWithTrustEq(net::PlatformTrustStore::CertWithTrust(
base::ToVector(b_by_f_->der_cert()),
bssl::CertificateTrust::ForDistrusted()))));
}
// Verifies that TrustStoreWin automatically synchronizes with the Windows
// certificate stores when certificates are added or removed at the system
// level. This ensures dynamic updates without restarting the process.
//
// WARNING: This test modifies the OS trust store. If it fails to clean up
// after itself, your system may be left in an insecure state. To verify it
// cleaned up after itself, make sure certificates that begin with
// "Chromium Test Cert" are not trusted by Windows.
//
// This is a manual test and is intentionally excluded from automated
// builders and normal local runs. The test name is prefixed with "MANUAL_"
// so that it is skipped by default in net/test/net_test_suite.cc. It only
// runs when the --run-manual flag is provided.
//
// Example:
// net_unittests --gtest_filter=TrustStoreWinTest.MANUAL_* --run-manual
//
// Run this after substantial changes to the Windows trust store integration
// to validate end-to-end synchronization behavior.
TEST_F(TrustStoreWinTest, MANUAL_AutoSyncCertStores) {
// Create a test certificate using CertBuilder with a random private key
// that gets discarded after the test, making it safer if cleanup fails.
auto cert_builder = std::make_unique<net::CertBuilder>(nullptr, nullptr);
cert_builder->SetSubjectCommonName(base::StrCat(
{"Chromium Test Cert - ", net::CertBuilder::MakeRandomHexString(12)}));
cert_builder->SetBasicConstraints(/*is_ca=*/true, /*path_len=*/-1);
// Set validity period for the certificate
base::Time not_before = base::Time::Now() - base::Days(1);
base::Time not_after = base::Time::Now() + base::Days(30);
cert_builder->SetValidity(not_before, not_after);
// Parse the generated certificate for use with TrustStoreWin
bssl::CertErrors errors;
std::shared_ptr<const bssl::ParsedCertificate> test_cert =
bssl::ParsedCertificate::Create(
cert_builder->DupCertBuffer(),
x509_util::DefaultParseCertificateOptions(), &errors);
ASSERT_TRUE(test_cert) << "Failed to parse generated test certificate: "
<< errors.ToDebugString();
// Create a real TrustStoreWin that connects to actual Windows system stores
auto trust_store_win = std::make_unique<TrustStoreWin>();
trust_store_win->InitializeStores();
// Open the same system ROOT store that TrustStoreWin uses internally.
// We need a separate handle with write access to add/remove test
// certificates, while TrustStoreWin uses read-only collection stores that
// aggregate multiple Windows system store locations.
DWORD flags = CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG;
crypto::ScopedHCERTSTORE system_root_store(CertOpenStore(
CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, NULL, flags, L"ROOT"));
if (!system_root_store.get()) {
EXPECT_NE(nullptr, system_root_store.get());
}
// Load the certificate.
crypto::ScopedPCCERT_CONTEXT test_cert_context(CertCreateCertificateContext(
X509_ASN_ENCODING, CRYPTO_BUFFER_data(test_cert->cert_buffer()),
CRYPTO_BUFFER_len(test_cert->cert_buffer())));
if (!test_cert_context.get()) {
EXPECT_NE(nullptr, test_cert_context.get());
}
// Verify certificate is NOT trusted initially.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(test_cert.get()).ToDebugString())
<< "Certificate should not be trusted initially";
// Add certificate to the actual Windows system ROOT store.
BOOL add_result = CertAddCertificateContextToStore(
system_root_store.get(), test_cert_context.get(), CERT_STORE_ADD_NEW,
nullptr);
if (!add_result) {
GTEST_SKIP() << "Could not add certificate to system store, error: "
<< GetLastError();
}
// Test auto-sync: Certificate should be immediately trusted without restart.
EXPECT_EQ(ExpectedTrustForAnchor().ToDebugString(),
trust_store_win->GetTrust(test_cert.get()).ToDebugString())
<< "Auto-sync should allow immediate detection of newly added "
"certificate";
// Cleanup: Remove the test certificate.
crypto::ScopedPCCERT_CONTEXT cert_to_delete(CertFindCertificateInStore(
system_root_store.get(), X509_ASN_ENCODING, 0, CERT_FIND_EXISTING,
test_cert_context.get(), nullptr));
if (cert_to_delete.get()) {
CertDeleteCertificateFromStore(cert_to_delete.release());
}
// Verify cleanup: Certificate should no longer be trusted.
EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
trust_store_win->GetTrust(test_cert.get()).ToDebugString())
<< "Certificate should not be trusted after removal";
}
} // namespace
} // namespace net