summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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',