summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrsleevi <rsleevi@chromium.org>2014-09-23 20:12:55 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-24 03:13:10 +0000
commit25e2bc0a95354727342757ba71582bf4de638fe8 (patch)
tree769e510b3a3566a92e098baecd86bc272fef3b67
parent6fb1f2e798c05bc01f3feeb8ae17dbb34f482f86 (diff)
downloadchromium_src-25e2bc0a95354727342757ba71582bf4de638fe8.zip
chromium_src-25e2bc0a95354727342757ba71582bf4de638fe8.tar.gz
chromium_src-25e2bc0a95354727342757ba71582bf4de638fe8.tar.bz2
Use sidestep to detour CertVerifyCertificateSignatureEx on Windows versions earlier than Vista (excluding XP SP3), adding in SHA-256 support by deferring to NSS.
The canonical path to supporting SHA-256 is to install XP SP3 or the appropriate hotfixes for XP x64 / Windows Server 2003. However, as not all users may do so, and there's enough of a usability hurdle, provide an interception hook until we fully drop support for these systems. XP is already outside of MSFT EOL (April 2014). Windows Server 2003 is EOL'd July 2015. BUG=401365 Review URL: https://codereview.chromium.org/561613002 Cr-Commit-Position: refs/heads/master@{#296335}
-rw-r--r--content/browser/browser_main_runner.cc87
-rw-r--r--net/cert/sha256_legacy_support_win.cc195
-rw-r--r--net/cert/sha256_legacy_support_win.h48
-rw-r--r--net/cert/sha256_legacy_support_win_unittest.cc52
-rw-r--r--net/data/ssl/certificates/README5
-rw-r--r--net/data/ssl/certificates/sha256.pem70
-rw-r--r--net/net.gypi3
7 files changed, 460 insertions, 0 deletions
diff --git a/content/browser/browser_main_runner.cc b/content/browser/browser_main_runner.cc
index a06f18a..7acd62d 100644
--- a/content/browser/browser_main_runner.cc
+++ b/content/browser/browser_main_runner.cc
@@ -20,7 +20,10 @@
#include "ui/base/ime/input_method_initializer.h"
#if defined(OS_WIN)
+#include "base/win/win_util.h"
#include "base/win/windows_version.h"
+#include "net/cert/sha256_legacy_support_win.h"
+#include "sandbox/win/src/sidestep/preamble_patcher.h"
#include "ui/base/win/scoped_ole_initializer.h"
#endif
@@ -28,6 +31,89 @@ bool g_exited_main_message_loop = false;
namespace content {
+#if defined(OS_WIN)
+namespace {
+
+// Pointer to the original CryptVerifyCertificateSignatureEx function.
+net::sha256_interception::CryptVerifyCertificateSignatureExFunc
+ g_real_crypt_verify_signature_stub = NULL;
+
+// Stub function that is called whenever the Crypt32 function
+// CryptVerifyCertificateSignatureEx is called. It just defers to net to perform
+// the actual verification.
+BOOL WINAPI CryptVerifyCertificateSignatureExStub(
+ HCRYPTPROV_LEGACY provider,
+ DWORD encoding_type,
+ DWORD subject_type,
+ void* subject_data,
+ DWORD issuer_type,
+ void* issuer_data,
+ DWORD flags,
+ void* extra) {
+ return net::sha256_interception::CryptVerifyCertificateSignatureExHook(
+ g_real_crypt_verify_signature_stub, provider, encoding_type, subject_type,
+ subject_data, issuer_type, issuer_data, flags, extra);
+}
+
+// If necessary, install an interception
+void InstallSha256LegacyHooks() {
+#if defined(_WIN64)
+ // Interception on x64 is not supported.
+ return;
+#else
+ if (base::win::MaybeHasSHA256Support())
+ return;
+
+ net::sha256_interception::CryptVerifyCertificateSignatureExFunc
+ cert_verify_signature_ptr = reinterpret_cast<
+ net::sha256_interception::CryptVerifyCertificateSignatureExFunc>(
+ ::GetProcAddress(::GetModuleHandle(L"crypt32.dll"),
+ "CryptVerifyCertificateSignatureEx"));
+ CHECK(cert_verify_signature_ptr);
+
+ DWORD old_protect = 0;
+ if (!::VirtualProtect(cert_verify_signature_ptr, 5, PAGE_EXECUTE_READWRITE,
+ &old_protect)) {
+ return;
+ }
+
+ g_real_crypt_verify_signature_stub =
+ reinterpret_cast<
+ net::sha256_interception::CryptVerifyCertificateSignatureExFunc>(
+ VirtualAllocEx(::GetCurrentProcess(), NULL,
+ sidestep::kMaxPreambleStubSize, MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE));
+ if (g_real_crypt_verify_signature_stub == NULL) {
+ CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect,
+ &old_protect));
+ return;
+ }
+
+ sidestep::SideStepError patch_result =
+ sidestep::PreamblePatcher::Patch(
+ cert_verify_signature_ptr, CryptVerifyCertificateSignatureExStub,
+ g_real_crypt_verify_signature_stub, sidestep::kMaxPreambleStubSize);
+ if (patch_result != sidestep::SIDESTEP_SUCCESS) {
+ CHECK(::VirtualFreeEx(::GetCurrentProcess(),
+ g_real_crypt_verify_signature_stub, 0,
+ MEM_RELEASE));
+ CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect,
+ &old_protect));
+ return;
+ }
+
+ DWORD dummy = 0;
+ CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect, &dummy));
+ CHECK(::VirtualProtect(g_real_crypt_verify_signature_stub,
+ sidestep::kMaxPreambleStubSize, old_protect,
+ &old_protect));
+#endif // _WIN64
+}
+
+} // namespace
+
+#endif // OS_WIN
+
class BrowserMainRunnerImpl : public BrowserMainRunner {
public:
BrowserMainRunnerImpl()
@@ -64,6 +150,7 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
// Win32 API here directly.
ImmDisableTextFrameService(static_cast<DWORD>(-1));
}
+ InstallSha256LegacyHooks();
#endif // OS_WIN
base::StatisticsRecorder::Initialize();
diff --git a/net/cert/sha256_legacy_support_win.cc b/net/cert/sha256_legacy_support_win.cc
new file mode 100644
index 0000000..656edd73
--- /dev/null
+++ b/net/cert/sha256_legacy_support_win.cc
@@ -0,0 +1,195 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/sha256_legacy_support_win.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <cert.h>
+#include <keyhi.h>
+#include <secoid.h>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/win/windows_version.h"
+#include "crypto/scoped_nss_types.h"
+
+namespace net {
+
+namespace sha256_interception {
+
+namespace {
+
+bool IsSupportedSubjectType(DWORD subject_type) {
+ switch (subject_type) {
+ case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB:
+ case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT:
+ case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL:
+ return true;
+ }
+ return false;
+}
+
+bool IsSupportedIssuerType(DWORD issuer_type) {
+ switch (issuer_type) {
+ case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY:
+ case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT:
+ case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN:
+ return true;
+ }
+ return false;
+}
+
+base::StringPiece GetSubjectSignature(DWORD subject_type,
+ void* subject_data) {
+ switch (subject_type) {
+ case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB: {
+ CRYPT_DATA_BLOB* data_blob =
+ reinterpret_cast<CRYPT_DATA_BLOB*>(subject_data);
+ return base::StringPiece(reinterpret_cast<char*>(data_blob->pbData),
+ data_blob->cbData);
+ }
+ case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT: {
+ PCCERT_CONTEXT subject_cert =
+ reinterpret_cast<PCCERT_CONTEXT>(subject_data);
+ return base::StringPiece(
+ reinterpret_cast<char*>(subject_cert->pbCertEncoded),
+ subject_cert->cbCertEncoded);
+ }
+ case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL: {
+ PCCRL_CONTEXT subject_crl =
+ reinterpret_cast<PCCRL_CONTEXT>(subject_data);
+ return base::StringPiece(
+ reinterpret_cast<char*>(subject_crl->pbCrlEncoded),
+ subject_crl->cbCrlEncoded);
+ }
+ }
+ return base::StringPiece();
+}
+
+PCERT_PUBLIC_KEY_INFO GetIssuerPublicKey(DWORD issuer_type,
+ void* issuer_data) {
+ switch (issuer_type) {
+ case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY:
+ return reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(issuer_data);
+ case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT: {
+ PCCERT_CONTEXT cert = reinterpret_cast<PCCERT_CONTEXT>(issuer_data);
+ return &cert->pCertInfo->SubjectPublicKeyInfo;
+ }
+ case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN: {
+ PCCERT_CHAIN_CONTEXT chain =
+ reinterpret_cast<PCCERT_CHAIN_CONTEXT>(issuer_data);
+ PCCERT_CONTEXT cert = chain->rgpChain[0]->rgpElement[0]->pCertContext;
+ return &cert->pCertInfo->SubjectPublicKeyInfo;
+ }
+ }
+ return NULL;
+}
+
+} // namespace
+
+BOOL CryptVerifyCertificateSignatureExHook(
+ CryptVerifyCertificateSignatureExFunc original_func,
+ HCRYPTPROV_LEGACY provider,
+ DWORD encoding_type,
+ DWORD subject_type,
+ void* subject_data,
+ DWORD issuer_type,
+ void* issuer_data,
+ DWORD flags,
+ void* extra) {
+ CHECK(original_func);
+
+ // Only intercept if the arguments are supported.
+ if (provider != NULL || (encoding_type != X509_ASN_ENCODING) ||
+ !IsSupportedSubjectType(subject_type) || subject_data == NULL ||
+ !IsSupportedIssuerType(issuer_type) || issuer_data == NULL) {
+ return original_func(provider, encoding_type, subject_type, subject_data,
+ issuer_type, issuer_data, flags, extra);
+ }
+
+ base::StringPiece subject_signature =
+ GetSubjectSignature(subject_type, subject_data);
+ bool should_intercept = false;
+
+ crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ CERTSignedData signed_data;
+ memset(&signed_data, 0, sizeof(signed_data));
+
+ // Attempt to decode the subject using the generic "Signed Data" template,
+ // which all of the supported subject types match. If the signature
+ // algorithm is RSA with one of the SHA-2 algorithms supported by NSS
+ // (excluding SHA-224, which is pointless), then defer to the NSS
+ // implementation. Otherwise, fall back and let the OS handle it (e.g.
+ // in case there are any algorithm policies in effect).
+ if (!subject_signature.empty()) {
+ SECItem subject_sig_item;
+ subject_sig_item.data = const_cast<unsigned char*>(
+ reinterpret_cast<const unsigned char*>(subject_signature.data()));
+ subject_sig_item.len = subject_signature.size();
+ SECStatus rv = SEC_QuickDERDecodeItem(
+ arena.get(), &signed_data, SEC_ASN1_GET(CERT_SignedDataTemplate),
+ &subject_sig_item);
+ if (rv == SECSuccess) {
+ SECOidTag signature_alg =
+ SECOID_GetAlgorithmTag(&signed_data.signatureAlgorithm);
+ if (signature_alg == SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION ||
+ signature_alg == SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION ||
+ signature_alg == SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION) {
+ should_intercept = true;
+ }
+ }
+ }
+
+ if (!should_intercept) {
+ return original_func(provider, encoding_type, subject_type, subject_data,
+ issuer_type, issuer_data, flags, extra);
+ }
+
+ // Rather than attempting to synthesize a CERTSubjectPublicKeyInfo by hand,
+ // just force the OS to do an ASN.1 encoding and then decode it back into
+ // NSS. This is silly for performance, but safest for consistency.
+ PCERT_PUBLIC_KEY_INFO issuer_public_key =
+ GetIssuerPublicKey(issuer_type, issuer_data);
+ if (!issuer_public_key) {
+ SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
+ return FALSE;
+ }
+
+ unsigned char* issuer_spki_data = NULL;
+ DWORD issuer_spki_len = 0;
+ if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
+ issuer_public_key, CRYPT_ENCODE_ALLOC_FLAG, NULL,
+ &issuer_spki_data, &issuer_spki_len)) {
+ return FALSE;
+ }
+
+ SECItem nss_issuer_spki;
+ nss_issuer_spki.data = issuer_spki_data;
+ nss_issuer_spki.len = issuer_spki_len;
+ CERTSubjectPublicKeyInfo* spki =
+ SECKEY_DecodeDERSubjectPublicKeyInfo(&nss_issuer_spki);
+ ::LocalFree(issuer_spki_data);
+ if (!spki) {
+ SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
+ return FALSE;
+ }
+
+ // Attempt to actually verify the signed data. If it fails, synthesize the
+ // failure as a generic "bad signature" and let CryptoAPI handle the rest.
+ SECStatus rv = CERT_VerifySignedDataWithPublicKeyInfo(
+ &signed_data, spki, NULL);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+ if (rv != SECSuccess) {
+ SetLastError(static_cast<DWORD>(NTE_BAD_SIGNATURE));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+} // namespace sha256_interception
+
+} // namespace net \ No newline at end of file
diff --git a/net/cert/sha256_legacy_support_win.h b/net/cert/sha256_legacy_support_win.h
new file mode 100644
index 0000000..c98414c
--- /dev/null
+++ b/net/cert/sha256_legacy_support_win.h
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_SHA256_LEGACY_SUPPORT_WIN_H_
+#define NET_CERT_SHA256_LEGACY_SUPPORT_WIN_H_
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+namespace sha256_interception {
+
+typedef BOOL (WINAPI* CryptVerifyCertificateSignatureExFunc)(
+ HCRYPTPROV_LEGACY provider,
+ DWORD encoding_type,
+ DWORD subject_type,
+ void* subject_data,
+ DWORD issuer_type,
+ void* issuer_data,
+ DWORD flags,
+ void* extra);
+
+// Interception function meant to be called whenever
+// CryptVerifyCertificateSignatureEx is called. Note that the calling
+// conventions do not match, as the caller is expected to ensure that their
+// interposed function handles the calling conventions and provides a pointer
+// to the original CryptVerifyCertificateSignatureEx (e.g. to handle parameters
+// and keys that are not supported).
+NET_EXPORT BOOL CryptVerifyCertificateSignatureExHook(
+ CryptVerifyCertificateSignatureExFunc original_func,
+ HCRYPTPROV_LEGACY provider,
+ DWORD encoding_type,
+ DWORD subject_type,
+ void* subject_data,
+ DWORD issuer_type,
+ void* issuer_data,
+ DWORD flags,
+ void* extra);
+
+} // namespace sha256_interception
+
+} // namespace net
+
+#endif // NET_CERT_SHA256_LEGACY_SUPPORT_WIN_H_ \ No newline at end of file
diff --git a/net/cert/sha256_legacy_support_win_unittest.cc b/net/cert/sha256_legacy_support_win_unittest.cc
new file mode 100644
index 0000000..0496ca3
--- /dev/null
+++ b/net/cert/sha256_legacy_support_win_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/sha256_legacy_support_win.h"
+
+#include "base/memory/ref_counted.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace sha256_interception {
+
+namespace {
+
+// Verifies that SHA-256 signatures can be validated through the interception.
+// Although this is only needed on legacy platforms, the test is run on all
+// Windows platforms to make sure that the CryptoAPI<->NSS integration does not
+// regress.
+TEST(Sha256Interception, HandlesSHA2) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> server_cert =
+ ImportCertFromFile(certs_dir, "sha256.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert.get());
+
+ CryptVerifyCertificateSignatureExFunc cert_verify_signature_ptr =
+ reinterpret_cast<CryptVerifyCertificateSignatureExFunc>(
+ ::GetProcAddress(::GetModuleHandle(L"crypt32.dll"),
+ "CryptVerifyCertificateSignatureEx"));
+ ASSERT_TRUE(cert_verify_signature_ptr);
+
+ BOOL rv = CryptVerifyCertificateSignatureExHook(
+ cert_verify_signature_ptr, NULL, X509_ASN_ENCODING,
+ CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
+ const_cast<void*>(reinterpret_cast<const void*>(
+ server_cert->os_cert_handle())),
+ CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
+ const_cast<void*>(reinterpret_cast<const void*>(
+ server_cert->os_cert_handle())),
+ 0, NULL);
+ EXPECT_EQ(TRUE, rv);
+}
+
+} // namespace
+
+} // namespace sha256_interception
+
+} // namespace net \ No newline at end of file
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README
index 17247f3..a3a94c8 100644
--- a/net/data/ssl/certificates/README
+++ b/net/data/ssl/certificates/README
@@ -112,6 +112,11 @@ unit tests.
- ocsp-test-root.pem : A root certificate for the code in
net/tools/testserver/minica.py
+- sha256.pem: Used to test the handling of SHA-256 certs on Windows.
+ Generated by using the command:
+ "openssl req -x509 -days 3650 -sha256 -newkey rsa:2048 -text \
+ -config ../scripts/ee.cnf -out sha256.pem"
+
- spdy_pooling.pem : Used to test the handling of spdy IP connection pooling
Generated by using the command
"openssl req -x509 -days 3650 -sha1 -extensions req_spdy_pooling \
diff --git a/net/data/ssl/certificates/sha256.pem b/net/data/ssl/certificates/sha256.pem
new file mode 100644
index 0000000..507ff04
--- /dev/null
+++ b/net/data/ssl/certificates/sha256.pem
@@ -0,0 +1,70 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number:
+ 90:23:b2:54:1d:1a:a1:b4
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
+ Validity
+ Not Before: Sep 11 19:19:22 2014 GMT
+ Not After : Sep 8 19:19:22 2024 GMT
+ Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (2048 bit)
+ Modulus (2048 bit):
+ 00:d2:41:24:4c:b5:a5:83:ad:b9:64:5b:90:e2:d1:
+ e5:17:14:fe:ee:d0:6a:d8:d0:74:3c:98:35:d1:6b:
+ 2a:81:32:9e:e4:a2:77:5b:f2:81:2c:ae:c1:cc:ca:
+ f0:45:dd:8e:21:d3:4c:a6:7a:69:1b:08:3e:83:31:
+ 0e:cc:5a:f8:a4:b6:05:10:68:ec:a0:a2:44:b6:95:
+ e9:c5:e3:7c:a2:fa:a0:72:de:b3:49:e7:ac:cc:dc:
+ ce:f3:7c:af:9f:05:46:e4:c2:31:54:a4:2b:cc:bf:
+ ab:c3:e2:a6:9d:30:24:f4:2b:4d:44:eb:63:5f:35:
+ 0f:c5:ec:e9:77:40:0d:3d:35:6c:88:e7:18:c3:01:
+ 95:4c:c1:d4:14:96:88:66:98:fd:8e:0f:0e:a1:50:
+ d3:f9:13:e1:b6:5d:5a:8a:1c:8d:55:e1:43:3d:ca:
+ c6:31:95:9c:0a:00:68:38:1a:b7:24:ce:cd:34:d6:
+ 8d:f5:11:79:64:7b:01:b3:0f:76:fd:cf:ca:73:33:
+ 96:03:e4:1a:2e:98:8b:54:4d:0b:b9:62:27:8f:0d:
+ 9a:2c:1f:5d:b5:6a:e4:3a:27:33:02:eb:7f:0d:30:
+ 6a:6f:ea:61:c9:78:fe:7a:9f:08:7b:61:cd:b8:b2:
+ 04:98:24:37:d5:f7:ce:ec:34:29:b3:bc:26:ca:6f:
+ 54:53
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha256WithRSAEncryption
+ 73:89:72:15:fa:cf:c9:6a:4d:a5:1a:66:0d:da:03:13:2e:31:
+ 6b:6f:df:f1:4f:dd:54:8d:bd:87:1e:e9:b1:b2:84:4a:da:8e:
+ fa:76:b8:66:f0:2d:d6:f4:72:2d:55:ef:99:d4:e1:99:7c:79:
+ 98:b7:b9:bc:9c:f8:69:47:6d:10:03:4b:41:59:ea:84:7c:04:
+ 97:67:37:d4:a8:75:53:4a:f3:f9:9a:cb:bb:09:fb:dd:d7:b0:
+ d5:35:48:04:f3:58:78:bc:fb:26:d7:dc:e2:bf:cb:64:22:3d:
+ f7:bb:a4:5e:a3:98:5f:aa:29:46:aa:c6:a9:7c:8c:04:ac:21:
+ 21:97:b1:94:da:eb:ee:49:03:9d:46:e0:a0:22:e4:dd:e4:2b:
+ 3d:0d:0e:af:1b:59:39:41:e8:47:43:b8:58:36:82:86:c6:0e:
+ d6:d3:c0:92:da:2b:a4:5a:5a:f8:4f:20:93:32:a7:74:21:13:
+ 9d:b7:7b:68:20:71:81:4a:1a:32:b7:5e:e7:45:de:f7:e4:04:
+ dd:c1:7b:89:3e:a2:cb:47:20:fa:36:a6:bc:17:5a:19:56:97:
+ 1e:3c:fa:a7:fc:1e:d9:ad:dc:62:6b:ef:52:88:c8:d3:09:6c:
+ 6f:ff:d4:28:4b:7d:1e:9d:bb:0c:08:12:f3:f8:fd:24:35:57:
+ a2:3a:26:e2
+-----BEGIN CERTIFICATE-----
+MIIDPDCCAiQCCQCQI7JUHRqhtDANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJV
+UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQ
+MA4GA1UECgwHVGVzdCBDQTESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTE0MDkxMTE5
+MTkyMloXDTI0MDkwODE5MTkyMlowYDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
+bGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3Qg
+Q0ExEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBANJBJEy1pYOtuWRbkOLR5RcU/u7QatjQdDyYNdFrKoEynuSid1vygSyu
+wczK8EXdjiHTTKZ6aRsIPoMxDsxa+KS2BRBo7KCiRLaV6cXjfKL6oHLes0nnrMzc
+zvN8r58FRuTCMVSkK8y/q8Pipp0wJPQrTUTrY181D8Xs6XdADT01bIjnGMMBlUzB
+1BSWiGaY/Y4PDqFQ0/kT4bZdWoocjVXhQz3KxjGVnAoAaDgatyTOzTTWjfUReWR7
+AbMPdv3PynMzlgPkGi6Yi1RNC7liJ48NmiwfXbVq5DonMwLrfw0wam/qYcl4/nqf
+CHthzbiyBJgkN9X3zuw0KbO8JspvVFMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+c4lyFfrPyWpNpRpmDdoDEy4xa2/f8U/dVI29hx7psbKEStqO+na4ZvAt1vRyLVXv
+mdThmXx5mLe5vJz4aUdtEANLQVnqhHwEl2c31Kh1U0rz+ZrLuwn73dew1TVIBPNY
+eLz7Jtfc4r/LZCI997ukXqOYX6opRqrGqXyMBKwhIZexlNrr7kkDnUbgoCLk3eQr
+PQ0OrxtZOUHoR0O4WDaChsYO1tPAktorpFpa+E8gkzKndCETnbd7aCBxgUoaMrde
+50Xe9+QE3cF7iT6iy0cg+jamvBdaGVaXHjz6p/we2a3cYmvvUojI0wlsb//UKEt9
+Hp27DAgS8/j9JDVXojom4g==
+-----END CERTIFICATE-----
diff --git a/net/net.gypi b/net/net.gypi
index fe7cff3..35ec25e 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -77,6 +77,8 @@
'cert/ct_verify_result.h',
'cert/pem_tokenizer.cc',
'cert/pem_tokenizer.h',
+ 'cert/sha256_legacy_support_win.cc',
+ 'cert/sha256_legacy_support_win.h',
'cert/signed_certificate_timestamp.cc',
'cert/signed_certificate_timestamp.h',
'cert/single_request_cert_verifier.cc',
@@ -1299,6 +1301,7 @@
'cert/nss_cert_database_unittest.cc',
'cert/nss_profile_filter_chromeos_unittest.cc',
'cert/pem_tokenizer_unittest.cc',
+ 'cert/sha256_legacy_support_win_unittest.cc',
'cert/signed_certificate_timestamp_unittest.cc',
'cert/test_root_certs_unittest.cc',
'cert/x509_cert_types_unittest.cc',