Add X509CertificateBytes which uses CRYPTO_BUFFER instead of macOS-native certificate types.

(Other platforms will come in later CLs.)

BUG=671420

Review-Url: https://codereview.chromium.org/2746103003
Cr-Commit-Position: refs/heads/master@{#463507}
diff --git a/chrome/browser/ui/certificate_viewer_mac.mm b/chrome/browser/ui/certificate_viewer_mac.mm
index 6415e99..370ee6d 100644
--- a/chrome/browser/ui/certificate_viewer_mac.mm
+++ b/chrome/browser/ui/certificate_viewer_mac.mm
@@ -99,7 +99,10 @@
                      forWebContents:(content::WebContents*)webContents {
   if ((self = [super init])) {
     base::ScopedCFTypeRef<CFArrayRef> certChain(
-        certificate->CreateOSCertChainForCert());
+        net::x509_util::CreateSecCertificateArrayForX509Certificate(
+            certificate));
+    if (!certChain)
+      return self;
     NSArray* certificates = base::mac::CFToNSCast(certChain.get());
     certificates_.reset([certificates retain]);
   }
diff --git a/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.mm b/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.mm
index 5135722..680d24e 100644
--- a/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.mm
+++ b/chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.mm
@@ -208,8 +208,11 @@
   identities_.reset(CFArrayCreateMutable(
       kCFAllocatorDefault, numCerts, &kCFTypeArrayCallBacks));
   for (size_t i = 0; i < numCerts; ++i) {
-    SecCertificateRef cert =
-        observer_->cert_request_info()->client_certs[i]->os_cert_handle();
+    base::ScopedCFTypeRef<SecCertificateRef> cert(
+        net::x509_util::CreateSecCertificateFromX509Certificate(
+            observer_->cert_request_info()->client_certs[i].get()));
+    if (!cert)
+      continue;
     SecIdentityRef identity;
     if (SecIdentityCreateWithCertificate(NULL, cert, &identity) == noErr) {
       CFArrayAppendValue(identities_, identity);
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 334c8e4..6320cca 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -37,6 +37,12 @@
 use_v8_in_net = !is_ios && !is_proto_quic
 enable_built_in_dns = !is_ios && !is_proto_quic
 
+# True if certificates are represented with DER byte buffers. This can be true
+# in addition to use_openssl_certs or use_nss_certs, in that case byte certs
+# are used internally but OpenSSL or NSS are used for certificate verification.
+# TODO(mattm): crbug.com/671420: Implement and enable this for all platforms.
+use_byte_certs = is_mac
+
 buildflag_header("features") {
   header = "net_features.h"
   flags = [
@@ -45,6 +51,7 @@
     "DISABLE_FTP_SUPPORT=$disable_ftp_support",
     "ENABLE_MDNS=$enable_mdns",
     "ENABLE_WEBSOCKETS=$enable_websockets",
+    "USE_BYTE_CERTS=$use_byte_certs",
   ]
 }
 
@@ -585,6 +592,7 @@
       "cert/test_root_certs_win.cc",
       "cert/x509_cert_types_mac.cc",
       "cert/x509_cert_types_win.cc",
+      "cert/x509_certificate_bytes.cc",
       "cert/x509_certificate_ios.cc",
       "cert/x509_certificate_known_roots_win.h",
       "cert/x509_certificate_mac.cc",
@@ -592,6 +600,8 @@
       "cert/x509_certificate_win.cc",
       "cert/x509_util_android.cc",
       "cert/x509_util_android.h",
+      "cert/x509_util_ios.cc",
+      "cert/x509_util_ios.h",
       "cert/x509_util_mac.cc",
       "cert/x509_util_mac.h",
       "cert/x509_util_nss.cc",
@@ -1732,6 +1742,26 @@
       ]
     }
 
+    if (use_byte_certs) {
+      if (is_ios) {
+        sources -= [ "cert/x509_certificate_ios.cc" ]
+      }
+      if (is_mac) {
+        sources -= [ "cert/x509_certificate_mac.cc" ]
+      }
+      if (use_nss_certs) {
+        sources -= [ "cert/x509_certificate_nss.cc" ]
+      }
+      if (use_openssl_certs) {
+        sources -= [ "cert/x509_certificate_openssl.cc" ]
+      }
+      if (is_win) {
+        sources -= [ "cert/x509_certificate_win.cc" ]
+      }
+    } else {
+      sources -= [ "cert/x509_certificate_bytes.cc" ]
+    }
+
     if (!use_openssl_certs) {
       sources -= [
         "cert/cert_database_openssl.cc",
diff --git a/net/cert/cert_verify_proc_mac.cc b/net/cert/cert_verify_proc_mac.cc
index 6e1f8d6..0ed65a6 100644
--- a/net/cert/cert_verify_proc_mac.cc
+++ b/net/cert/cert_verify_proc_mac.cc
@@ -202,7 +202,8 @@
   }
 
   scoped_refptr<X509Certificate> verified_cert_with_chain =
-      X509Certificate::CreateFromHandle(verified_cert, verified_chain);
+      x509_util::CreateX509CertificateFromSecCertificate(verified_cert,
+                                                         verified_chain);
   if (verified_cert_with_chain)
     verify_result->verified_cert = std::move(verified_cert_with_chain);
   else
@@ -212,7 +213,7 @@
 // Returns true if the certificate uses MD2, MD4, MD5, or SHA1, and false
 // otherwise. A return of false also includes the case where the signature
 // algorithm couldn't be conclusively labeled as weak.
-bool CertUsesWeakHash(X509Certificate::OSCertHandle cert_handle) {
+bool CertUsesWeakHash(SecCertificateRef cert_handle) {
   x509_util::CSSMCachedCertificate cached_cert;
   OSStatus status = cached_cert.Init(cert_handle);
   if (status)
@@ -616,7 +617,7 @@
       return false;
     SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>(
         const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1)));
-    SHA256HashValue hash = X509Certificate::CalculateFingerprint256(root_ref);
+    SHA256HashValue hash = x509_util::CalculateFingerprint256(root_ref);
     return known_roots_.find(hash) != known_roots_.end();
   }
 
@@ -636,7 +637,7 @@
     for (CFIndex i = 0, size = CFArrayGetCount(cert_array); i < size; ++i) {
       SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
           const_cast<void*>(CFArrayGetValueAtIndex(cert_array, i)));
-      known_roots_.insert(X509Certificate::CalculateFingerprint256(cert));
+      known_roots_.insert(x509_util::CalculateFingerprint256(cert));
     }
   }
 
@@ -782,7 +783,9 @@
     }
 
     ScopedCFTypeRef<CFMutableArrayRef> cert_array(
-        cert->CreateOSCertChainForCert());
+        x509_util::CreateSecCertificateArrayForX509Certificate(cert));
+    if (!cert_array)
+      return ERR_CERT_INVALID;
 
     // Beginning with the certificate chain as supplied by the server, attempt
     // to verify the chain. If a failure is encountered, trim a certificate
diff --git a/net/cert/internal/parse_name.cc b/net/cert/internal/parse_name.cc
index 2e7a8e7..d9eaaeed 100644
--- a/net/cert/internal/parse_name.cc
+++ b/net/cert/internal/parse_name.cc
@@ -134,6 +134,12 @@
   return der::Input(oid);
 }
 
+der::Input TypeStreetAddressOid() {
+  // street (streetAddress): 2.5.4.9 (RFC 4519)
+  static const uint8_t oid[] = {0x55, 0x04, 0x09};
+  return der::Input(oid);
+}
+
 der::Input TypeOrganizationNameOid() {
   // id-at-organizationName: 2.5.4.10 (RFC 5280)
   static const uint8_t oid[] = {0x55, 0x04, 0x0a};
@@ -176,6 +182,13 @@
   return der::Input(oid);
 }
 
+der::Input TypeDomainComponentOid() {
+  // dc (domainComponent): 0.9.2342.19200300.100.1.25 (RFC 4519)
+  static const uint8_t oid[] = {0x09, 0x92, 0x26, 0x89, 0x93,
+                                0xF2, 0x2C, 0x64, 0x01, 0x19};
+  return der::Input(oid);
+}
+
 bool X509NameAttribute::ValueAsString(std::string* out) const {
   switch (value_tag) {
     case der::kTeletexString:
@@ -234,6 +247,7 @@
 bool X509NameAttribute::AsRFC2253String(std::string* out) const {
   std::string type_string;
   std::string value_string;
+  // TODO(mattm): Add streetAddress and domainComponent here?
   if (type == TypeCommonNameOid()) {
     type_string = "CN";
   } else if (type == TypeSurnameOid()) {
diff --git a/net/cert/internal/parse_name.h b/net/cert/internal/parse_name.h
index 26eff26..3a188c6 100644
--- a/net/cert/internal/parse_name.h
+++ b/net/cert/internal/parse_name.h
@@ -20,6 +20,7 @@
 NET_EXPORT der::Input TypeCountryNameOid();
 NET_EXPORT der::Input TypeLocalityNameOid();
 NET_EXPORT der::Input TypeStateOrProvinceNameOid();
+NET_EXPORT der::Input TypeStreetAddressOid();
 NET_EXPORT der::Input TypeOrganizationNameOid();
 NET_EXPORT der::Input TypeOrganizationUnitNameOid();
 NET_EXPORT der::Input TypeTitleOid();
@@ -27,6 +28,7 @@
 NET_EXPORT der::Input TypeGivenNameOid();
 NET_EXPORT der::Input TypeInitialsOid();
 NET_EXPORT der::Input TypeGenerationQualifierOid();
+NET_EXPORT der::Input TypeDomainComponentOid();
 
 // X509NameAttribute contains a representation of a DER-encoded RFC 2253
 // "AttributeTypeAndValue".
diff --git a/net/cert/internal/trust_store_mac.cc b/net/cert/internal/trust_store_mac.cc
index a088a32f..ba31ffb 100644
--- a/net/cert/internal/trust_store_mac.cc
+++ b/net/cert/internal/trust_store_mac.cc
@@ -16,8 +16,8 @@
 #include "net/cert/internal/parse_name.h"
 #include "net/cert/internal/parsed_certificate.h"
 #include "net/cert/test_keychain_search_list_mac.h"
-#include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util.h"
+#include "net/cert/x509_util_mac.h"
 
 namespace net {
 
@@ -146,7 +146,7 @@
 // |policy_oid|.
 TrustStatus IsSecCertificateTrustedForPolicy(SecCertificateRef cert_handle,
                                              const CFStringRef policy_oid) {
-  const bool is_self_signed = X509Certificate::IsSelfSigned(cert_handle);
+  const bool is_self_signed = x509_util::IsSelfSigned(cert_handle);
   // Evaluate trust domains in user, admin, system order. Admin settings can
   // override system ones, and user settings can override both admin and system.
   for (const auto& trust_domain :
@@ -320,8 +320,8 @@
   // There does not appear to be any public API to get the normalized version
   // of a Name without creating a SecCertificate.
   base::ScopedCFTypeRef<SecCertificateRef> cert_handle(
-      X509Certificate::CreateOSCertHandleFromBytes(
-          cert->der_cert().AsStringPiece().data(), cert->der_cert().Length()));
+      x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
+                                               cert->der_cert().Length()));
   if (!cert_handle) {
     LOG(ERROR) << "CreateOSCertHandleFromBytes";
     return name_data;
diff --git a/net/cert/internal/trust_store_mac_unittest.cc b/net/cert/internal/trust_store_mac_unittest.cc
index 1d1bc49..bba5995 100644
--- a/net/cert/internal/trust_store_mac_unittest.cc
+++ b/net/cert/internal/trust_store_mac_unittest.cc
@@ -18,6 +18,7 @@
 #include "net/cert/test_keychain_search_list_mac.h"
 #include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util.h"
+#include "net/cert/x509_util_mac.h"
 #include "net/test/test_data_directory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -263,9 +264,8 @@
     }
 
     base::ScopedCFTypeRef<SecCertificateRef> cert_handle(
-        X509Certificate::CreateOSCertHandleFromBytes(
-            cert->der_cert().AsStringPiece().data(),
-            cert->der_cert().Length()));
+        x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
+                                                 cert->der_cert().Length()));
     if (!cert_handle) {
       ADD_FAILURE() << "CreateOSCertHandleFromBytes " << hash_text;
       continue;
diff --git a/net/cert/test_root_certs_mac.cc b/net/cert/test_root_certs_mac.cc
index fcbca97..f3a51e1 100644
--- a/net/cert/test_root_certs_mac.cc
+++ b/net/cert/test_root_certs_mac.cc
@@ -9,14 +9,25 @@
 #include "base/logging.h"
 #include "net/cert/x509_certificate.h"
 
+#if defined(OS_IOS)
+#include "net/cert/x509_util_ios.h"
+#else
+#include "net/cert/x509_util_mac.h"
+#endif
+
 namespace net {
 
 bool TestRootCerts::Add(X509Certificate* certificate) {
+  base::ScopedCFTypeRef<SecCertificateRef> os_cert(
+      x509_util::CreateSecCertificateFromX509Certificate(certificate));
+  if (!os_cert)
+    return false;
+
   if (CFArrayContainsValue(temporary_roots_,
                            CFRangeMake(0, CFArrayGetCount(temporary_roots_)),
-                           certificate->os_cert_handle()))
+                           os_cert.get()))
     return true;
-  CFArrayAppendValue(temporary_roots_, certificate->os_cert_handle());
+  CFArrayAppendValue(temporary_roots_, os_cert.get());
   return true;
 }
 
diff --git a/net/cert/x509_certificate.cc b/net/cert/x509_certificate.cc
index 3121e1b5..cf5d2f6 100644
--- a/net/cert/x509_certificate.cc
+++ b/net/cert/x509_certificate.cc
@@ -48,7 +48,7 @@
 // The PEM block header used for PKCS#7 data
 const char kPKCS7Header[] = "PKCS7";
 
-#if !defined(USE_NSS_CERTS)
+#if !defined(USE_NSS_CERTS) && !BUILDFLAG(USE_BYTE_CERTS)
 // A thread-safe cache for OS certificate handles.
 //
 // Within each of the supported underlying crypto libraries, a certificate
@@ -189,19 +189,20 @@
     cache_.erase(pos);
   }
 }
-#endif  // !defined(USE_NSS_CERTS)
+#endif  // !defined(USE_NSS_CERTS) && !BUILDFLAG(USE_BYTE_CERTS)
 
 // See X509CertificateCache::InsertOrUpdate. NSS has a built-in cache, so there
-// is no point in wrapping another cache around it.
+// is no point in wrapping another cache around it. With USE_BYTE_CERTS, the
+// CYRPTO_BUFFERs are deduped by a CRYPTO_BUFFER_POOL.
 void InsertOrUpdateCache(X509Certificate::OSCertHandle* cert_handle) {
-#if !defined(USE_NSS_CERTS)
+#if !defined(USE_NSS_CERTS) && !BUILDFLAG(USE_BYTE_CERTS)
   g_x509_certificate_cache.Pointer()->InsertOrUpdate(cert_handle);
 #endif
 }
 
 // See X509CertificateCache::Remove.
 void RemoveFromCache(X509Certificate::OSCertHandle cert_handle) {
-#if !defined(USE_NSS_CERTS)
+#if !defined(USE_NSS_CERTS) && !BUILDFLAG(USE_BYTE_CERTS)
   g_x509_certificate_cache.Pointer()->Remove(cert_handle);
 #endif
 }
diff --git a/net/cert/x509_certificate.h b/net/cert/x509_certificate.h
index b3fff1f9..0cb93b5a 100644
--- a/net/cert/x509_certificate.h
+++ b/net/cert/x509_certificate.h
@@ -19,8 +19,11 @@
 #include "net/base/net_export.h"
 #include "net/cert/cert_type.h"
 #include "net/cert/x509_cert_types.h"
+#include "net/net_features.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(USE_BYTE_CERTS)
+#include "third_party/boringssl/src/include/openssl/base.h"
+#elif defined(OS_WIN)
 #include <windows.h>
 #include "crypto/wincrypt_shim.h"
 #elif defined(OS_MACOSX)
@@ -56,7 +59,11 @@
   // An OSCertHandle is a handle to a certificate object in the underlying
   // crypto library. We assume that OSCertHandle is a pointer type on all
   // platforms and that NULL represents an invalid OSCertHandle.
-#if defined(OS_WIN)
+#if BUILDFLAG(USE_BYTE_CERTS)
+  // TODO(mattm): Remove OSCertHandle type and clean up the interfaces once all
+  // platforms use the CRYPTO_BUFFER version.
+  typedef CRYPTO_BUFFER* OSCertHandle;
+#elif defined(OS_WIN)
   typedef PCCERT_CONTEXT OSCertHandle;
 #elif defined(OS_MACOSX)
   typedef SecCertificateRef OSCertHandle;
@@ -224,7 +231,7 @@
     return intermediate_ca_certs_;
   }
 
-#if defined(OS_MACOSX)
+#if defined(OS_IOS)
   // Returns a new CFMutableArrayRef containing this certificate and its
   // intermediate certificates in the form expected by Security.framework
   // and Keychain Services, or NULL on failure.
diff --git a/net/cert/x509_certificate_bytes.cc b/net/cert/x509_certificate_bytes.cc
new file mode 100644
index 0000000..a351fb90
--- /dev/null
+++ b/net/cert/x509_certificate_bytes.cc
@@ -0,0 +1,481 @@
+// Copyright 2017 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/x509_certificate.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "base/pickle.h"
+#include "crypto/openssl_util.h"
+#include "net/base/ip_address.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/internal/cert_errors.h"
+#include "net/cert/internal/name_constraints.h"
+#include "net/cert/internal/parse_name.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/verify_name_match.h"
+#include "net/cert/internal/verify_signed_data.h"
+#include "net/cert/x509_util.h"
+#include "net/cert/x509_util_openssl.h"
+#include "net/der/parser.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+
+namespace net {
+
+namespace {
+
+// Converts a GeneralizedTime struct to a base::Time, returning true on success
+// or false if |generalized| was invalid or cannot be represented by
+// base::Time.
+bool GeneralizedTimeToBaseTime(const der::GeneralizedTime& generalized,
+                               base::Time* result) {
+  base::Time::Exploded exploded = {0};
+  exploded.year = generalized.year;
+  exploded.month = generalized.month;
+  exploded.day_of_month = generalized.day;
+  exploded.hour = generalized.hours;
+  exploded.minute = generalized.minutes;
+  exploded.second = generalized.seconds;
+  return base::Time::FromUTCExploded(exploded, result);
+}
+
+// Sets |value| to the Value from a DER Sequence Tag-Length-Value and return
+// true, or return false if the TLV was not a valid DER Sequence.
+WARN_UNUSED_RESULT bool GetSequenceValue(const der::Input& tlv,
+                                         der::Input* value) {
+  der::Parser parser(tlv);
+  return parser.ReadTag(der::kSequence, value) && !parser.HasMore();
+}
+
+// Normalize |cert|'s Issuer and store it in |out_normalized_issuer|, returning
+// true on success or false if there was a parsing error.
+bool GetNormalizedCertIssuer(CRYPTO_BUFFER* cert,
+                             std::string* out_normalized_issuer) {
+  der::Input tbs_certificate_tlv;
+  der::Input signature_algorithm_tlv;
+  der::BitString signature_value;
+  if (!ParseCertificate(
+          der::Input(CRYPTO_BUFFER_data(cert), CRYPTO_BUFFER_len(cert)),
+          &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value,
+          nullptr)) {
+    return false;
+  }
+  ParsedTbsCertificate tbs;
+  if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr))
+    return false;
+
+  der::Input issuer_value;
+  if (!GetSequenceValue(tbs.issuer_tlv, &issuer_value))
+    return false;
+
+  return NormalizeName(issuer_value, out_normalized_issuer);
+}
+
+// Fills |principal| from the DER encoded |name_tlv|, returning true on success
+// or false if parsing failed or some of the values could not be converted to
+// UTF-8.
+bool ParsePrincipal(const der::Input& name_tlv, CertPrincipal* principal) {
+  RDNSequence rdns;
+  if (!ParseName(name_tlv, &rdns))
+    return false;
+
+  for (const RelativeDistinguishedName& rdn : rdns) {
+    for (const X509NameAttribute& name_attribute : rdn) {
+      if (name_attribute.type == TypeCommonNameOid()) {
+        if (principal->common_name.empty() &&
+            !name_attribute.ValueAsString(&principal->common_name)) {
+          return false;
+        }
+      } else if (name_attribute.type == TypeLocalityNameOid()) {
+        if (principal->locality_name.empty() &&
+            !name_attribute.ValueAsString(&principal->locality_name)) {
+          return false;
+        }
+      } else if (name_attribute.type == TypeStateOrProvinceNameOid()) {
+        if (principal->state_or_province_name.empty() &&
+            !name_attribute.ValueAsString(&principal->state_or_province_name)) {
+          return false;
+        }
+      } else if (name_attribute.type == TypeCountryNameOid()) {
+        if (principal->country_name.empty() &&
+            !name_attribute.ValueAsString(&principal->country_name)) {
+          return false;
+        }
+      } else if (name_attribute.type == TypeStreetAddressOid()) {
+        std::string s;
+        if (!name_attribute.ValueAsString(&s))
+          return false;
+        principal->street_addresses.push_back(s);
+      } else if (name_attribute.type == TypeOrganizationNameOid()) {
+        std::string s;
+        if (!name_attribute.ValueAsString(&s))
+          return false;
+        principal->organization_names.push_back(s);
+      } else if (name_attribute.type == TypeOrganizationUnitNameOid()) {
+        std::string s;
+        if (!name_attribute.ValueAsString(&s))
+          return false;
+        principal->organization_unit_names.push_back(s);
+      } else if (name_attribute.type == TypeDomainComponentOid()) {
+        std::string s;
+        if (!name_attribute.ValueAsString(&s))
+          return false;
+        principal->domain_components.push_back(s);
+      }
+    }
+  }
+  return true;
+}
+
+// Parses certificates from a PKCS#7 SignedData structure, appending them to
+// |handles|.
+void CreateOSCertHandlesFromPKCS7Bytes(
+    const char* data,
+    size_t length,
+    X509Certificate::OSCertHandles* handles) {
+  crypto::EnsureOpenSSLInit();
+  crypto::OpenSSLErrStackTracer err_cleaner(FROM_HERE);
+
+  CBS der_data;
+  CBS_init(&der_data, reinterpret_cast<const uint8_t*>(data), length);
+  STACK_OF(X509)* certs = sk_X509_new_null();
+
+  if (PKCS7_get_certificates(certs, &der_data)) {
+    for (size_t i = 0; i < sk_X509_num(certs); ++i) {
+      base::StringPiece stringpiece;
+      x509_util::GetDER(sk_X509_value(certs, i), &stringpiece);
+      handles->push_back(x509_util::CreateCryptoBuffer(stringpiece).release());
+    }
+  }
+  sk_X509_pop_free(certs, X509_free);
+}
+
+}  // namespace
+
+bool X509Certificate::Initialize() {
+  der::Input tbs_certificate_tlv;
+  der::Input signature_algorithm_tlv;
+  der::BitString signature_value;
+
+  if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_handle_),
+                                   CRYPTO_BUFFER_len(cert_handle_)),
+                        &tbs_certificate_tlv, &signature_algorithm_tlv,
+                        &signature_value, nullptr)) {
+    return false;
+  }
+
+  ParsedTbsCertificate tbs;
+  if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr))
+    return false;
+
+  if (!ParsePrincipal(tbs.subject_tlv, &subject_) ||
+      !ParsePrincipal(tbs.issuer_tlv, &issuer_)) {
+    return false;
+  }
+
+  if (!GeneralizedTimeToBaseTime(tbs.validity_not_before, &valid_start_) ||
+      !GeneralizedTimeToBaseTime(tbs.validity_not_after, &valid_expiry_)) {
+    return false;
+  }
+  serial_number_ = tbs.serial_number.AsString();
+  return true;
+}
+
+bool X509Certificate::GetSubjectAltName(
+    std::vector<std::string>* dns_names,
+    std::vector<std::string>* ip_addrs) const {
+  if (dns_names)
+    dns_names->clear();
+  if (ip_addrs)
+    ip_addrs->clear();
+
+  der::Input tbs_certificate_tlv;
+  der::Input signature_algorithm_tlv;
+  der::BitString signature_value;
+  if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_handle_),
+                                   CRYPTO_BUFFER_len(cert_handle_)),
+                        &tbs_certificate_tlv, &signature_algorithm_tlv,
+                        &signature_value, nullptr)) {
+    return false;
+  }
+
+  ParsedTbsCertificate tbs;
+  if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr))
+    return false;
+  if (!tbs.has_extensions)
+    return false;
+
+  std::map<der::Input, ParsedExtension> extensions;
+  if (!ParseExtensions(tbs.extensions_tlv, &extensions))
+    return false;
+
+  ParsedExtension subject_alt_names_extension;
+  if (!ConsumeExtension(SubjectAltNameOid(), &extensions,
+                        &subject_alt_names_extension)) {
+    return false;
+  }
+
+  std::unique_ptr<GeneralNames> subject_alt_names =
+      GeneralNames::Create(subject_alt_names_extension.value);
+  if (!subject_alt_names)
+    return false;
+
+  if (dns_names)
+    *dns_names = subject_alt_names->dns_names;
+  if (ip_addrs) {
+    for (const IPAddress& addr : subject_alt_names->ip_addresses) {
+      ip_addrs->push_back(
+          std::string(reinterpret_cast<const char*>(addr.bytes().data()),
+                      addr.bytes().size()));
+    }
+  }
+
+  return !subject_alt_names->dns_names.empty() ||
+         !subject_alt_names->ip_addresses.empty();
+}
+
+bool X509Certificate::IsIssuedByEncoded(
+    const std::vector<std::string>& valid_issuers) {
+  std::vector<std::string> normalized_issuers;
+  for (const auto& raw_issuer : valid_issuers) {
+    der::Input issuer_value;
+    std::string normalized_issuer;
+    if (!GetSequenceValue(der::Input(&raw_issuer), &issuer_value) ||
+        !NormalizeName(issuer_value, &normalized_issuer)) {
+      continue;
+    }
+    normalized_issuers.push_back(std::move(normalized_issuer));
+  }
+
+  std::string normalized_cert_issuer;
+  if (!GetNormalizedCertIssuer(cert_handle_, &normalized_cert_issuer))
+    return false;
+  if (std::find(normalized_issuers.begin(), normalized_issuers.end(),
+                normalized_cert_issuer) != normalized_issuers.end())
+    return true;
+
+  for (CRYPTO_BUFFER* intermediate : intermediate_ca_certs_) {
+    if (!GetNormalizedCertIssuer(intermediate, &normalized_cert_issuer))
+      return false;
+    if (std::find(normalized_issuers.begin(), normalized_issuers.end(),
+                  normalized_cert_issuer) != normalized_issuers.end())
+      return true;
+  }
+  return false;
+}
+
+// static
+bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
+                                    std::string* encoded) {
+  if (!cert_handle)
+    return false;
+  encoded->assign(
+      reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert_handle)),
+      CRYPTO_BUFFER_len(cert_handle));
+  return true;
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
+                                       size_t* size_bits,
+                                       PublicKeyType* type) {
+  *type = kPublicKeyTypeUnknown;
+  *size_bits = 0;
+
+  base::StringPiece spki;
+  if (!asn1::ExtractSPKIFromDERCert(
+          base::StringPiece(
+              reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert_handle)),
+              CRYPTO_BUFFER_len(cert_handle)),
+          &spki)) {
+    return;
+  }
+
+  bssl::UniquePtr<EVP_PKEY> pkey;
+  crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+  CBS cbs;
+  CBS_init(&cbs, reinterpret_cast<const uint8_t*>(spki.data()), spki.size());
+  pkey.reset(EVP_parse_public_key(&cbs));
+  if (!pkey)
+    return;
+
+  switch (pkey->type) {
+    case EVP_PKEY_RSA:
+      *type = kPublicKeyTypeRSA;
+      break;
+    case EVP_PKEY_DSA:
+      *type = kPublicKeyTypeDSA;
+      break;
+    case EVP_PKEY_EC:
+      *type = kPublicKeyTypeECDSA;
+      break;
+    case EVP_PKEY_DH:
+      *type = kPublicKeyTypeDH;
+      break;
+  }
+  *size_bits = base::saturated_cast<size_t>(EVP_PKEY_bits(pkey.get()));
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+                                   X509Certificate::OSCertHandle b) {
+  DCHECK(a && b);
+  if (a == b)
+    return true;
+  return CRYPTO_BUFFER_len(a) == CRYPTO_BUFFER_len(b) &&
+         memcmp(CRYPTO_BUFFER_data(a), CRYPTO_BUFFER_data(b),
+                CRYPTO_BUFFER_len(a)) == 0;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+    const char* data,
+    size_t length) {
+  der::Input tbs_certificate_tlv;
+  der::Input signature_algorithm_tlv;
+  der::BitString signature_value;
+  // Do a bare minimum of DER parsing here to make sure the input is not
+  // completely crazy. (This is required for at least
+  // CreateCertificateListFromBytes with FORMAT_AUTO, if not more.)
+  if (!ParseCertificate(
+          der::Input(reinterpret_cast<const uint8_t*>(data), length),
+          &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value,
+          nullptr)) {
+    return nullptr;
+  }
+
+  return CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t*>(data), length,
+                           x509_util::GetBufferPool());
+}
+
+// static
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+    const char* data,
+    size_t length,
+    Format format) {
+  OSCertHandles results;
+
+  switch (format) {
+    case FORMAT_SINGLE_CERTIFICATE: {
+      OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
+      if (handle)
+        results.push_back(handle);
+      break;
+    }
+    case FORMAT_PKCS7: {
+      CreateOSCertHandlesFromPKCS7Bytes(data, length, &results);
+      break;
+    }
+    default: {
+      NOTREACHED() << "Certificate format " << format << " unimplemented";
+      break;
+    }
+  }
+
+  return results;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+    OSCertHandle cert_handle) {
+  CRYPTO_BUFFER_up_ref(cert_handle);
+  return cert_handle;
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+  CRYPTO_BUFFER_free(cert_handle);
+}
+
+// static
+SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) {
+  SHA256HashValue sha256;
+
+  SHA256(CRYPTO_BUFFER_data(cert), CRYPTO_BUFFER_len(cert), sha256.data);
+  return sha256;
+}
+
+// static
+SHA256HashValue X509Certificate::CalculateCAFingerprint256(
+    const OSCertHandles& intermediates) {
+  SHA256HashValue sha256;
+  memset(sha256.data, 0, sizeof(sha256.data));
+
+  SHA256_CTX sha256_ctx;
+  SHA256_Init(&sha256_ctx);
+  for (CRYPTO_BUFFER* cert : intermediates) {
+    SHA256_Update(&sha256_ctx, CRYPTO_BUFFER_data(cert),
+                  CRYPTO_BUFFER_len(cert));
+  }
+  SHA256_Final(sha256.data, &sha256_ctx);
+
+  return sha256;
+}
+
+// static
+bool X509Certificate::IsSelfSigned(OSCertHandle cert_handle) {
+  der::Input tbs_certificate_tlv;
+  der::Input signature_algorithm_tlv;
+  der::BitString signature_value;
+  if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_handle),
+                                   CRYPTO_BUFFER_len(cert_handle)),
+                        &tbs_certificate_tlv, &signature_algorithm_tlv,
+                        &signature_value, nullptr)) {
+    return false;
+  }
+  ParsedTbsCertificate tbs;
+  if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr)) {
+    return false;
+  }
+
+  der::Input subject_value;
+  std::string normalized_subject;
+  if (!GetSequenceValue(tbs.subject_tlv, &subject_value) ||
+      !NormalizeName(subject_value, &normalized_subject)) {
+    return false;
+  }
+  der::Input issuer_value;
+  std::string normalized_issuer;
+  if (!GetSequenceValue(tbs.issuer_tlv, &issuer_value) ||
+      !NormalizeName(issuer_value, &normalized_issuer)) {
+    return false;
+  }
+
+  if (normalized_subject != normalized_issuer)
+    return false;
+
+  std::unique_ptr<SignatureAlgorithm> signature_algorithm =
+      SignatureAlgorithm::Create(signature_algorithm_tlv, nullptr /* errors */);
+  if (!signature_algorithm)
+    return false;
+
+  SimpleSignaturePolicy signature_policy(1024);
+  CertErrors unused_errors;
+  return VerifySignedData(*signature_algorithm, tbs_certificate_tlv,
+                          signature_value, tbs.spki_tlv, &signature_policy,
+                          &unused_errors);
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle(
+    base::PickleIterator* pickle_iter) {
+  const char* data;
+  int length;
+  if (!pickle_iter->ReadData(&data, &length))
+    return NULL;
+
+  return CreateOSCertHandleFromBytes(data, length);
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+                                                base::Pickle* pickle) {
+  return pickle->WriteData(
+      reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert_handle)),
+      CRYPTO_BUFFER_len(cert_handle));
+}
+
+}  // namespace net
diff --git a/net/cert/x509_certificate_mac.cc b/net/cert/x509_certificate_mac.cc
index d36b36c..73dbe46 100644
--- a/net/cert/x509_certificate_mac.cc
+++ b/net/cert/x509_certificate_mac.cc
@@ -117,23 +117,6 @@
       serial_number.field()->Length);
 }
 
-// Test that a given |cert_handle| is actually a valid X.509 certificate, and
-// return true if it is.
-//
-// On OS X, SecCertificateCreateFromData() does not return any errors if
-// called with invalid data, as long as data is present. The actual decoding
-// of the certificate does not happen until an API that requires a CSSM
-// handle is called. While SecCertificateGetCLHandle is the most likely
-// candidate, as it performs the parsing, it does not check whether the
-// parsing was actually successful. Instead, SecCertificateGetSubject is
-// used (supported since 10.3), as a means to check that the certificate
-// parsed as a valid X.509 certificate.
-bool IsValidOSCertHandle(SecCertificateRef cert_handle) {
-  const CSSM_X509_NAME* sanity_check = NULL;
-  OSStatus status = SecCertificateGetSubject(cert_handle, &sanity_check);
-  return status == noErr && sanity_check;
-}
-
 // Parses |data| of length |length|, attempting to decode it as the specified
 // |format|. If |data| is in the specified format, any certificates contained
 // within are stored into |output|.
@@ -182,7 +165,7 @@
       // |input_format|, causing decode to succeed. On OS X 10.6, the data
       // is properly decoded as a PKCS#7, whether PEM or not, which avoids
       // the need to fallback to internal decoding.
-      if (IsValidOSCertHandle(cert)) {
+      if (x509_util::IsValidSecCertificate(cert)) {
         CFRetain(cert);
         output->push_back(cert);
       }
@@ -298,22 +281,9 @@
 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
     const char* data,
     size_t length) {
-  CSSM_DATA cert_data;
-  cert_data.Data = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(data));
-  cert_data.Length = length;
-
-  OSCertHandle cert_handle = NULL;
-  OSStatus status = SecCertificateCreateFromData(&cert_data,
-                                                 CSSM_CERT_X_509v3,
-                                                 CSSM_CERT_ENCODING_DER,
-                                                 &cert_handle);
-  if (status != noErr)
-    return NULL;
-  if (!IsValidOSCertHandle(cert_handle)) {
-    CFRelease(cert_handle);
-    return NULL;
-  }
-  return cert_handle;
+  return x509_util::CreateSecCertificateFromBytes(
+             reinterpret_cast<const uint8_t*>(data), length)
+      .release();
 }
 
 // static
@@ -357,20 +327,7 @@
 
 // static
 SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) {
-  SHA256HashValue sha256;
-  memset(sha256.data, 0, sizeof(sha256.data));
-
-  CSSM_DATA cert_data;
-  OSStatus status = SecCertificateGetData(cert, &cert_data);
-  if (status)
-    return sha256;
-
-  DCHECK(cert_data.Data);
-  DCHECK_NE(cert_data.Length, 0U);
-
-  CC_SHA256(cert_data.Data, cert_data.Length, sha256.data);
-
-  return sha256;
+  return x509_util::CalculateFingerprint256(cert);
 }
 
 // static
@@ -395,20 +352,6 @@
   return sha256;
 }
 
-CFMutableArrayRef X509Certificate::CreateOSCertChainForCert() const {
-  CFMutableArrayRef cert_list =
-      CFArrayCreateMutable(kCFAllocatorDefault, 0,
-                           &kCFTypeArrayCallBacks);
-  if (!cert_list)
-    return NULL;
-
-  CFArrayAppendValue(cert_list, os_cert_handle());
-  for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i)
-    CFArrayAppendValue(cert_list, intermediate_ca_certs_[i]);
-
-  return cert_list;
-}
-
 // static
 X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle(
     base::PickleIterator* pickle_iter) {
@@ -481,39 +424,7 @@
 
 // static
 bool X509Certificate::IsSelfSigned(OSCertHandle cert_handle) {
-  x509_util::CSSMCachedCertificate cached_cert;
-  OSStatus status = cached_cert.Init(cert_handle);
-  if (status != noErr)
-    return false;
-
-  x509_util::CSSMFieldValue subject;
-  status = cached_cert.GetField(&CSSMOID_X509V1SubjectNameStd, &subject);
-  if (status != CSSM_OK || !subject.field())
-    return false;
-
-  x509_util::CSSMFieldValue issuer;
-  status = cached_cert.GetField(&CSSMOID_X509V1IssuerNameStd, &issuer);
-  if (status != CSSM_OK || !issuer.field())
-    return false;
-
-  if (subject.field()->Length != issuer.field()->Length ||
-      memcmp(subject.field()->Data, issuer.field()->Data,
-             issuer.field()->Length) != 0) {
-    return false;
-  }
-
-  CSSM_CL_HANDLE cl_handle = CSSM_INVALID_HANDLE;
-  status = SecCertificateGetCLHandle(cert_handle, &cl_handle);
-  if (status)
-    return false;
-  CSSM_DATA cert_data;
-  status = SecCertificateGetData(cert_handle, &cert_data);
-  if (status)
-    return false;
-
-  if (CSSM_CL_CertVerify(cl_handle, 0, &cert_data, &cert_data, NULL, 0))
-    return false;
-  return true;
+  return x509_util::IsSelfSigned(cert_handle);
 }
 
 #pragma clang diagnostic pop  // "-Wdeprecated-declarations"
diff --git a/net/cert/x509_certificate_unittest.cc b/net/cert/x509_certificate_unittest.cc
index da25451..a5806a5 100644
--- a/net/cert/x509_certificate_unittest.cc
+++ b/net/cert/x509_certificate_unittest.cc
@@ -282,10 +282,10 @@
   EXPECT_TRUE(memcmp(google_cert->serial_number().data(), google_serial,
                      sizeof(google_serial)) == 0);
 
-// TODO(mattm): Creating the X509Certificate fails on windows due to the null
-// in the subject. Generate a new test cert specifically for this case rather
-// than reusing paypal_null_cert.
-#if !defined(OS_WIN)
+// TODO(mattm): Creating the X509Certificate fails on some platforms due to the
+// null in the subject. Generate a new test cert specifically for this case
+// rather than reusing paypal_null_cert.
+#if !defined(OS_WIN) && !BUILDFLAG(USE_BYTE_CERTS)
   // Check a serial number where the first byte is >= 0x80, the DER returned by
   // serial() should contain the leading 0 padding byte.
   scoped_refptr<X509Certificate> paypal_null_cert(
@@ -1201,16 +1201,13 @@
   size_t expected_bits;
   X509Certificate::PublicKeyType expected_type;
 } kPublicKeyInfoTestData[] = {
-    {"768-rsa-ee-by-768-rsa-intermediate.pem",
-     768,
+    {"768-rsa-ee-by-768-rsa-intermediate.pem", 768,
      X509Certificate::kPublicKeyTypeRSA},
-    {"1024-rsa-ee-by-768-rsa-intermediate.pem",
-     1024,
+    {"1024-rsa-ee-by-768-rsa-intermediate.pem", 1024,
      X509Certificate::kPublicKeyTypeRSA},
-    {"prime256v1-ecdsa-ee-by-1024-rsa-intermediate.pem",
-     256,
+    {"prime256v1-ecdsa-ee-by-1024-rsa-intermediate.pem", 256,
      X509Certificate::kPublicKeyTypeECDSA},
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MACOSX) && !defined(OS_IOS) && !BUILDFLAG(USE_BYTE_CERTS)
     // OS X has an key length limit of 4096 bits. This should manifest as an
     // unknown key. If a future version of OS X changes this, large_key.pem may
     // need to be renegerated with a larger key. See https://crbug.com/472291.
diff --git a/net/cert/x509_util_ios.cc b/net/cert/x509_util_ios.cc
new file mode 100644
index 0000000..9bf4185
--- /dev/null
+++ b/net/cert/x509_util_ios.cc
@@ -0,0 +1,22 @@
+// Copyright 2017 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/x509_util_ios.h"
+
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace x509_util {
+
+base::ScopedCFTypeRef<SecCertificateRef>
+CreateSecCertificateFromX509Certificate(const X509Certificate* cert) {
+  return base::ScopedCFTypeRef<SecCertificateRef>(
+      reinterpret_cast<SecCertificateRef>(
+          const_cast<void*>(CFRetain(cert->os_cert_handle()))));
+}
+
+}  // namespace x509_util
+
+}  // namespace net
diff --git a/net/cert/x509_util_ios.h b/net/cert/x509_util_ios.h
new file mode 100644
index 0000000..bf3473f
--- /dev/null
+++ b/net/cert/x509_util_ios.h
@@ -0,0 +1,27 @@
+// Copyright 2017 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_X509_UTIL_IOS_H_
+#define NET_CERT_X509_UTIL_IOS_H_
+
+#include <Security/Security.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class X509Certificate;
+
+namespace x509_util {
+
+// Returns a SecCertificate representing |cert|, or NULL on failure.
+NET_EXPORT base::ScopedCFTypeRef<SecCertificateRef>
+CreateSecCertificateFromX509Certificate(const X509Certificate* cert);
+
+}  // namespace x509_util
+
+}  // namespace net
+
+#endif  // NET_CERT_X509_UTIL_IOS_H_
diff --git a/net/cert/x509_util_mac.cc b/net/cert/x509_util_mac.cc
index f2ce0f3b..0e12a64 100644
--- a/net/cert/x509_util_mac.cc
+++ b/net/cert/x509_util_mac.cc
@@ -4,11 +4,14 @@
 
 #include "net/cert/x509_util_mac.h"
 
+#include <CommonCrypto/CommonDigest.h>
+
 #include "base/logging.h"
 #include "base/mac/mac_util.h"
-#include "base/mac/scoped_cftyperef.h"
 #include "base/strings/sys_string_conversions.h"
+#include "net/cert/x509_certificate.h"
 #include "third_party/apple_apsl/cssmapplePriv.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
 
 namespace net {
 
@@ -52,6 +55,161 @@
 
 }  // namespace
 
+bool IsValidSecCertificate(SecCertificateRef cert_handle) {
+  const CSSM_X509_NAME* sanity_check = NULL;
+  OSStatus status = SecCertificateGetSubject(cert_handle, &sanity_check);
+  return status == noErr && sanity_check;
+}
+
+base::ScopedCFTypeRef<SecCertificateRef> CreateSecCertificateFromBytes(
+    const uint8_t* data,
+    size_t length) {
+  CSSM_DATA cert_data;
+  cert_data.Data = const_cast<uint8_t*>(data);
+  cert_data.Length = length;
+
+  base::ScopedCFTypeRef<SecCertificateRef> cert_handle;
+  OSStatus status = SecCertificateCreateFromData(&cert_data, CSSM_CERT_X_509v3,
+                                                 CSSM_CERT_ENCODING_DER,
+                                                 cert_handle.InitializeInto());
+  if (status != noErr)
+    return base::ScopedCFTypeRef<SecCertificateRef>();
+  if (!IsValidSecCertificate(cert_handle.get()))
+    return base::ScopedCFTypeRef<SecCertificateRef>();
+  return cert_handle;
+}
+
+base::ScopedCFTypeRef<SecCertificateRef>
+CreateSecCertificateFromX509Certificate(const X509Certificate* cert) {
+#if BUILDFLAG(USE_BYTE_CERTS)
+  return CreateSecCertificateFromBytes(
+      CRYPTO_BUFFER_data(cert->os_cert_handle()),
+      CRYPTO_BUFFER_len(cert->os_cert_handle()));
+#else
+  return base::ScopedCFTypeRef<SecCertificateRef>(
+      reinterpret_cast<SecCertificateRef>(
+          const_cast<void*>(CFRetain(cert->os_cert_handle()))));
+#endif
+}
+
+base::ScopedCFTypeRef<CFMutableArrayRef>
+CreateSecCertificateArrayForX509Certificate(X509Certificate* cert) {
+  base::ScopedCFTypeRef<CFMutableArrayRef> cert_list(
+      CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+  if (!cert_list)
+    return base::ScopedCFTypeRef<CFMutableArrayRef>();
+#if BUILDFLAG(USE_BYTE_CERTS)
+  std::string bytes;
+  base::ScopedCFTypeRef<SecCertificateRef> sec_cert(
+      CreateSecCertificateFromBytes(CRYPTO_BUFFER_data(cert->os_cert_handle()),
+                                    CRYPTO_BUFFER_len(cert->os_cert_handle())));
+  if (!sec_cert)
+    return base::ScopedCFTypeRef<CFMutableArrayRef>();
+  CFArrayAppendValue(cert_list, sec_cert);
+  for (X509Certificate::OSCertHandle intermediate :
+       cert->GetIntermediateCertificates()) {
+    base::ScopedCFTypeRef<SecCertificateRef> sec_cert(
+        CreateSecCertificateFromBytes(CRYPTO_BUFFER_data(intermediate),
+                                      CRYPTO_BUFFER_len(intermediate)));
+    CFArrayAppendValue(cert_list, sec_cert);
+  }
+#else
+  X509Certificate::OSCertHandles intermediate_ca_certs =
+      cert->GetIntermediateCertificates();
+  CFArrayAppendValue(cert_list, cert->os_cert_handle());
+  for (size_t i = 0; i < intermediate_ca_certs.size(); ++i)
+    CFArrayAppendValue(cert_list, intermediate_ca_certs[i]);
+#endif
+  return cert_list;
+}
+
+scoped_refptr<X509Certificate> CreateX509CertificateFromSecCertificate(
+    SecCertificateRef sec_cert,
+    const std::vector<SecCertificateRef>& sec_chain) {
+#if BUILDFLAG(USE_BYTE_CERTS)
+  CSSM_DATA der_data;
+  if (!sec_cert || SecCertificateGetData(sec_cert, &der_data) != noErr)
+    return nullptr;
+  bssl::UniquePtr<CRYPTO_BUFFER> cert_handle(
+      X509Certificate::CreateOSCertHandleFromBytes(
+          reinterpret_cast<const char*>(der_data.Data), der_data.Length));
+  if (!cert_handle)
+    return nullptr;
+  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
+  X509Certificate::OSCertHandles intermediates_raw;
+  for (const SecCertificateRef& sec_intermediate : sec_chain) {
+    if (!sec_intermediate ||
+        SecCertificateGetData(sec_intermediate, &der_data) != noErr) {
+      return nullptr;
+    }
+    bssl::UniquePtr<CRYPTO_BUFFER> intermediate_cert_handle(
+        X509Certificate::CreateOSCertHandleFromBytes(
+            reinterpret_cast<const char*>(der_data.Data), der_data.Length));
+    if (!intermediate_cert_handle)
+      return nullptr;
+    intermediates_raw.push_back(intermediate_cert_handle.get());
+    intermediates.push_back(std::move(intermediate_cert_handle));
+  }
+  scoped_refptr<X509Certificate> result(
+      X509Certificate::CreateFromHandle(cert_handle.get(), intermediates_raw));
+  return result;
+#else
+  return X509Certificate::CreateFromHandle(sec_cert, sec_chain);
+#endif
+}
+
+bool IsSelfSigned(SecCertificateRef cert_handle) {
+  CSSMCachedCertificate cached_cert;
+  OSStatus status = cached_cert.Init(cert_handle);
+  if (status != noErr)
+    return false;
+
+  CSSMFieldValue subject;
+  status = cached_cert.GetField(&CSSMOID_X509V1SubjectNameStd, &subject);
+  if (status != CSSM_OK || !subject.field())
+    return false;
+
+  CSSMFieldValue issuer;
+  status = cached_cert.GetField(&CSSMOID_X509V1IssuerNameStd, &issuer);
+  if (status != CSSM_OK || !issuer.field())
+    return false;
+
+  if (subject.field()->Length != issuer.field()->Length ||
+      memcmp(subject.field()->Data, issuer.field()->Data,
+             issuer.field()->Length) != 0) {
+    return false;
+  }
+
+  CSSM_CL_HANDLE cl_handle = CSSM_INVALID_HANDLE;
+  status = SecCertificateGetCLHandle(cert_handle, &cl_handle);
+  if (status)
+    return false;
+  CSSM_DATA cert_data;
+  status = SecCertificateGetData(cert_handle, &cert_data);
+  if (status)
+    return false;
+
+  if (CSSM_CL_CertVerify(cl_handle, 0, &cert_data, &cert_data, NULL, 0))
+    return false;
+  return true;
+}
+
+SHA256HashValue CalculateFingerprint256(SecCertificateRef cert) {
+  SHA256HashValue sha256;
+  memset(sha256.data, 0, sizeof(sha256.data));
+
+  CSSM_DATA cert_data;
+  OSStatus status = SecCertificateGetData(cert, &cert_data);
+  if (status)
+    return sha256;
+
+  DCHECK(cert_data.Data);
+  DCHECK_NE(cert_data.Length, 0U);
+
+  CC_SHA256(cert_data.Data, cert_data.Length, sha256.data);
+
+  return sha256;
+}
 
 OSStatus CreateSSLClientPolicy(SecPolicyRef* policy) {
   *policy = SecPolicyCreateSSL(false /* server */, nullptr);
diff --git a/net/cert/x509_util_mac.h b/net/cert/x509_util_mac.h
index 6b320a8..1700a6cc 100644
--- a/net/cert/x509_util_mac.h
+++ b/net/cert/x509_util_mac.h
@@ -10,18 +10,67 @@
 
 #include <string>
 
+#include "base/mac/scoped_cftyperef.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "net/base/hash_value.h"
 #include "net/base/net_export.h"
 
 namespace net {
 
+class X509Certificate;
+
 namespace x509_util {
 
+// Tests that a given |cert_handle| is actually a valid X.509 certificate, and
+// returns true if it is.
+//
+// On OS X, SecCertificateCreateFromData() does not return any errors if
+// called with invalid data, as long as data is present. The actual decoding
+// of the certificate does not happen until an API that requires a CSSM
+// handle is called. While SecCertificateGetCLHandle is the most likely
+// candidate, as it performs the parsing, it does not check whether the
+// parsing was actually successful. Instead, SecCertificateGetSubject is
+// used (supported since 10.3), as a means to check that the certificate
+// parsed as a valid X.509 certificate.
+NET_EXPORT bool IsValidSecCertificate(SecCertificateRef cert_handle);
+
+// Creates a SecCertificate handle from the DER-encoded representation.
+// Returns NULL on failure.
+NET_EXPORT base::ScopedCFTypeRef<SecCertificateRef>
+CreateSecCertificateFromBytes(const uint8_t* data, size_t length);
+
+// Returns a SecCertificate representing |cert|, or NULL on failure.
+NET_EXPORT base::ScopedCFTypeRef<SecCertificateRef>
+CreateSecCertificateFromX509Certificate(const X509Certificate* cert);
+
+// Returns a new CFMutableArrayRef containing this certificate and its
+// intermediate certificates in the form expected by Security.framework
+// and Keychain Services, or NULL on failure.
+// The first item in the array will be this certificate, followed by its
+// intermediates, if any.
+NET_EXPORT base::ScopedCFTypeRef<CFMutableArrayRef>
+CreateSecCertificateArrayForX509Certificate(X509Certificate* cert);
+
+// Creates an X509Certificate representing |sec_cert| with intermediates
+// |sec_chain|.
+NET_EXPORT scoped_refptr<X509Certificate>
+CreateX509CertificateFromSecCertificate(
+    SecCertificateRef sec_cert,
+    const std::vector<SecCertificateRef>& sec_chain);
+
+// Returns true if the certificate is self-signed.
+NET_EXPORT bool IsSelfSigned(SecCertificateRef cert_handle);
+
+// Calculates the SHA-256 fingerprint of the certificate.  Returns an empty
+// (all zero) fingerprint on failure.
+NET_EXPORT SHA256HashValue CalculateFingerprint256(SecCertificateRef cert);
+
 // Creates a security policy for certificates used as client certificates
 // in SSL.
 // If a policy is successfully created, it will be stored in
 // |*policy| and ownership transferred to the caller.
-OSStatus NET_EXPORT CreateSSLClientPolicy(SecPolicyRef* policy);
+NET_EXPORT OSStatus CreateSSLClientPolicy(SecPolicyRef* policy);
 
 // Create an SSL server policy. While certificate name validation will be
 // performed by SecTrustEvaluate(), it has the following limitations:
@@ -32,13 +81,13 @@
 // system trust preferences, such as those created by Safari. Preferences
 // created by Keychain Access do not share this requirement.
 // On success, stores the resultant policy in |*policy| and returns noErr.
-OSStatus NET_EXPORT CreateSSLServerPolicy(const std::string& hostname,
+NET_EXPORT OSStatus CreateSSLServerPolicy(const std::string& hostname,
                                           SecPolicyRef* policy);
 
 // Creates a security policy for basic X.509 validation. If the policy is
 // successfully created, it will be stored in |*policy| and ownership
 // transferred to the caller.
-OSStatus NET_EXPORT CreateBasicX509Policy(SecPolicyRef* policy);
+NET_EXPORT OSStatus CreateBasicX509Policy(SecPolicyRef* policy);
 
 // Creates security policies to control revocation checking (OCSP and CRL).
 // If |enable_revocation_checking| is true, revocation checking will be
@@ -47,7 +96,7 @@
 // the network or the local cache, if possible.
 // If the policies are successfully created, they will be appended to
 // |policies|.
-OSStatus NET_EXPORT CreateRevocationPolicies(bool enable_revocation_checking,
+NET_EXPORT OSStatus CreateRevocationPolicies(bool enable_revocation_checking,
                                              CFMutableArrayRef policies);
 
 // CSSM functions are deprecated as of OSX 10.7, but have no replacement.
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index b8db10f..085ef63 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -225,6 +225,13 @@
     return nullptr;
   }
 
+#if BUILDFLAG(USE_BYTE_CERTS)
+  std::vector<CRYPTO_BUFFER*> intermediate_chain;
+  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(openssl_chain); ++i)
+    intermediate_chain.push_back(sk_CRYPTO_BUFFER_value(openssl_chain, i));
+  return X509Certificate::CreateFromHandle(
+      sk_CRYPTO_BUFFER_value(openssl_chain, 0), intermediate_chain);
+#else
   // Convert the certificate chains to a platform certificate handle.
   std::vector<base::StringPiece> der_chain;
   der_chain.reserve(sk_CRYPTO_BUFFER_num(openssl_chain));
@@ -236,9 +243,10 @@
         CRYPTO_BUFFER_len(cert)));
   }
   return X509Certificate::CreateFromDERCertChain(der_chain);
+#endif
 }
 
-#if !defined(OS_IOS)
+#if !defined(OS_IOS) && !BUILDFLAG(USE_BYTE_CERTS)
 bssl::UniquePtr<CRYPTO_BUFFER> OSCertHandleToBuffer(
     X509Certificate::OSCertHandle os_handle) {
   std::string der_encoded;
@@ -1582,6 +1590,14 @@
       return -1;
     }
 
+#if BUILDFLAG(USE_BYTE_CERTS)
+    std::vector<CRYPTO_BUFFER*> chain_raw;
+    chain_raw.push_back(ssl_config_.client_cert->os_cert_handle());
+    for (X509Certificate::OSCertHandle cert :
+         ssl_config_.client_cert->GetIntermediateCertificates()) {
+      chain_raw.push_back(cert);
+    }
+#else
     std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> chain;
     std::vector<CRYPTO_BUFFER*> chain_raw;
     bssl::UniquePtr<CRYPTO_BUFFER> buf =
@@ -1605,6 +1621,7 @@
       chain_raw.push_back(buf.get());
       chain.push_back(std::move(buf));
     }
+#endif
 
     if (!SSL_set_chain_and_key(ssl_.get(), chain_raw.data(), chain_raw.size(),
                                nullptr, &SSLContext::kPrivateKeyMethod)) {
@@ -1641,7 +1658,7 @@
                                      digests.size());
 
     net_log_.AddEvent(NetLogEventType::SSL_CLIENT_CERT_PROVIDED,
-                      NetLog::IntCallback("cert_count", chain.size()));
+                      NetLog::IntCallback("cert_count", chain_raw.size()));
     return 1;
   }
 #endif  // defined(OS_IOS)
diff --git a/net/ssl/client_cert_store_mac.cc b/net/ssl/client_cert_store_mac.cc
index d94ab18..a3ef589 100644
--- a/net/ssl/client_cert_store_mac.cc
+++ b/net/ssl/client_cert_store_mac.cc
@@ -91,9 +91,12 @@
   DCHECK(cert);
   DCHECK(cert->get());
 
-  X509Certificate::OSCertHandle cert_handle = (*cert)->os_cert_handle();
+  base::ScopedCFTypeRef<SecCertificateRef> os_cert(
+      x509_util::CreateSecCertificateFromX509Certificate(cert->get()));
+  if (!os_cert)
+    return false;
   CFArrayRef cert_chain = NULL;
-  OSStatus result = CopyCertChain(cert_handle, &cert_chain);
+  OSStatus result = CopyCertChain(os_cert.get(), &cert_chain);
   if (result) {
     OSSTATUS_LOG(ERROR, result) << "CopyCertChain error";
     return false;
@@ -102,7 +105,7 @@
   if (!cert_chain)
     return false;
 
-  X509Certificate::OSCertHandles intermediates;
+  std::vector<SecCertificateRef> intermediates;
   for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain);
        i < chain_count; ++i) {
     SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
@@ -110,8 +113,9 @@
     intermediates.push_back(cert);
   }
 
-  scoped_refptr<X509Certificate> new_cert(X509Certificate::CreateFromHandle(
-      cert_handle, intermediates));
+  scoped_refptr<X509Certificate> new_cert(
+      x509_util::CreateX509CertificateFromSecCertificate(os_cert.get(),
+                                                         intermediates));
   CFRelease(cert_chain);  // Also frees |intermediates|.
 
   if (!new_cert || !new_cert->IsIssuedByEncoded(valid_issuers))
@@ -193,7 +197,7 @@
   selected_certs->clear();
   for (size_t i = 0; i < preliminary_list.size(); ++i) {
     scoped_refptr<X509Certificate>& cert = preliminary_list[i];
-    if (cert->HasExpired() || !SupportsSSLClientAuth(cert->os_cert_handle()))
+    if (cert->HasExpired())
       continue;
 
     // Skip duplicates (a cert may be in multiple keychains).
@@ -287,9 +291,12 @@
       continue;
     ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle);
 
+    if (!SupportsSSLClientAuth(cert_handle))
+      continue;
+
     scoped_refptr<X509Certificate> cert(
-        X509Certificate::CreateFromHandle(cert_handle,
-                                          X509Certificate::OSCertHandles()));
+        x509_util::CreateX509CertificateFromSecCertificate(
+            cert_handle, std::vector<SecCertificateRef>()));
     if (!cert)
       continue;
 
diff --git a/net/ssl/ssl_platform_key_mac.cc b/net/ssl/ssl_platform_key_mac.cc
index 373f432..1f7e3ef 100644
--- a/net/ssl/ssl_platform_key_mac.cc
+++ b/net/ssl/ssl_platform_key_mac.cc
@@ -30,6 +30,7 @@
 #include "crypto/openssl_util.h"
 #include "net/base/net_errors.h"
 #include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_mac.h"
 #include "net/ssl/ssl_platform_key.h"
 #include "net/ssl/ssl_platform_key_util.h"
 #include "net/ssl/ssl_private_key.h"
@@ -84,9 +85,13 @@
   OSStatus status;
   base::ScopedCFTypeRef<SecIdentityRef> identity;
   {
+    base::ScopedCFTypeRef<SecCertificateRef> os_cert(
+        x509_util::CreateSecCertificateFromX509Certificate(certificate));
+    if (!os_cert)
+      return nullptr;
     base::AutoLock lock(crypto::GetMacSecurityServicesLock());
-    status = SecIdentityCreateWithCertificate(
-        keychain, certificate->os_cert_handle(), identity.InitializeInto());
+    status = SecIdentityCreateWithCertificate(keychain, os_cert.get(),
+                                              identity.InitializeInto());
   }
   if (status != noErr) {
     OSSTATUS_LOG(WARNING, status);
diff --git a/net/ssl/ssl_platform_key_mac_unittest.cc b/net/ssl/ssl_platform_key_mac_unittest.cc
index 9207ded..e35af63 100644
--- a/net/ssl/ssl_platform_key_mac_unittest.cc
+++ b/net/ssl/ssl_platform_key_mac_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/memory/ref_counted.h"
+#include "net/cert/x509_util_mac.h"
 #include "net/ssl/ssl_private_key.h"
 #include "net/ssl/ssl_private_key_test_util.h"
 #include "net/test/cert_test_util.h"
@@ -76,8 +77,10 @@
                               nullptr, keychain.InitializeInto()));
 
   // Insert the certificate into the keychain.
-  ASSERT_EQ(noErr,
-            SecCertificateAddToKeychain(cert->os_cert_handle(), keychain));
+  base::ScopedCFTypeRef<SecCertificateRef> sec_cert(
+      x509_util::CreateSecCertificateFromX509Certificate(cert.get()));
+  ASSERT_TRUE(sec_cert);
+  ASSERT_EQ(noErr, SecCertificateAddToKeychain(sec_cert, keychain));
 
   // Import the key into the keychain. Apple doesn't accept unencrypted PKCS#8,
   // but it accepts the low-level RSAPrivateKey and ECPrivateKey types as