Convert iOS to use X509CertificateBytes.

BUG=671420

Review-Url: https://codereview.chromium.org/2864133002
Cr-Commit-Position: refs/heads/master@{#472024}
diff --git a/chrome/browser/ui/certificate_viewer_mac.mm b/chrome/browser/ui/certificate_viewer_mac.mm
index 370ee6d..1697cd3 100644
--- a/chrome/browser/ui/certificate_viewer_mac.mm
+++ b/chrome/browser/ui/certificate_viewer_mac.mm
@@ -12,6 +12,7 @@
 #import "base/mac/scoped_nsobject.h"
 #include "content/public/browser/web_contents.h"
 #include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_ios_and_mac.h"
 #include "net/cert/x509_util_mac.h"
 
 namespace {
diff --git a/ios/web/net/crw_cert_verification_controller.mm b/ios/web/net/crw_cert_verification_controller.mm
index 587d92c9..bb7717e 100644
--- a/ios/web/net/crw_cert_verification_controller.mm
+++ b/ios/web/net/crw_cert_verification_controller.mm
@@ -17,6 +17,7 @@
 #include "ios/web/public/web_thread.h"
 #import "ios/web/web_state/wk_web_view_security_util.h"
 #include "net/cert/cert_verify_proc_ios.h"
+#include "net/cert/x509_util_ios.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -224,9 +225,9 @@
 
   // Check if user has decided to proceed with this bad cert.
   scoped_refptr<net::X509Certificate> leafCert =
-      net::X509Certificate::CreateFromHandle(
+      net::x509_util::CreateX509CertificateFromSecCertificate(
           SecTrustGetCertificateAtIndex(trust, 0),
-          net::X509Certificate::OSCertHandles());
+          std::vector<SecCertificateRef>());
   if (!leafCert)
     return web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR;
 
diff --git a/ios/web/net/crw_cert_verification_controller_unittest.mm b/ios/web/net/crw_cert_verification_controller_unittest.mm
index aac4d43d..5414b54b 100644
--- a/ios/web/net/crw_cert_verification_controller_unittest.mm
+++ b/ios/web/net/crw_cert_verification_controller_unittest.mm
@@ -12,6 +12,7 @@
 #include "ios/web/public/web_thread.h"
 #import "ios/web/web_state/wk_web_view_security_util.h"
 #include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_ios_and_mac.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
 
@@ -36,20 +37,15 @@
         net::ImportCertFromFile(net::GetTestCertsDirectory(), kCertFileName);
     ASSERT_TRUE(cert_);
 
-    NSArray* chain = GetChain(cert_);
-    valid_trust_ = web::CreateServerTrustFromChain(chain, kHostName);
+    base::ScopedCFTypeRef<CFMutableArrayRef> chain(
+        net::x509_util::CreateSecCertificateArrayForX509Certificate(
+            cert_.get()));
+    ASSERT_TRUE(chain);
+    valid_trust_ = web::CreateServerTrustFromChain(
+        static_cast<NSArray*>(chain.get()), kHostName);
     web::EnsureFutureTrustEvaluationSucceeds(valid_trust_.get());
-    invalid_trust_ = web::CreateServerTrustFromChain(chain, kHostName);
-  }
-
-  // Returns NSArray of SecCertificateRef objects for the given |cert|.
-  NSArray* GetChain(const scoped_refptr<net::X509Certificate>& cert) const {
-    NSMutableArray* result = [NSMutableArray
-        arrayWithObject:static_cast<id>(cert->os_cert_handle())];
-    for (SecCertificateRef intermediate : cert->GetIntermediateCertificates()) {
-      [result addObject:static_cast<id>(intermediate)];
-    }
-    return result;
+    invalid_trust_ = web::CreateServerTrustFromChain(
+        static_cast<NSArray*>(chain.get()), kHostName);
   }
 
   // Synchronously returns result of
diff --git a/ios/web/net/crw_ssl_status_updater_unittest.mm b/ios/web/net/crw_ssl_status_updater_unittest.mm
index 96c7731..0beecd9 100644
--- a/ios/web/net/crw_ssl_status_updater_unittest.mm
+++ b/ios/web/net/crw_ssl_status_updater_unittest.mm
@@ -13,6 +13,7 @@
 #include "ios/web/public/ssl_status.h"
 #include "ios/web/public/test/web_test.h"
 #import "ios/web/web_state/wk_web_view_security_util.h"
+#include "net/cert/x509_util_ios_and_mac.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
 #include "third_party/ocmock/OCMock/OCMock.h"
@@ -92,8 +93,12 @@
     scoped_refptr<net::X509Certificate> cert =
         net::ImportCertFromFile(net::GetTestCertsDirectory(), kCertFileName);
     ASSERT_TRUE(cert);
-    NSArray* chain = @[ static_cast<id>(cert->os_cert_handle()) ];
-    trust_ = CreateServerTrustFromChain(chain, kHostName);
+    base::ScopedCFTypeRef<CFMutableArrayRef> chain(
+        net::x509_util::CreateSecCertificateArrayForX509Certificate(
+            cert.get()));
+    ASSERT_TRUE(chain);
+    trust_ = CreateServerTrustFromChain(static_cast<NSArray*>(chain.get()),
+                                        kHostName);
   }
 
   void TearDown() override {
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 3c446cd..8b17115f 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -100,6 +100,7 @@
 #import "ios/web/webui/mojo_facade.h"
 #import "net/base/mac/url_conversions.h"
 #include "net/base/net_errors.h"
+#include "net/cert/x509_util_ios.h"
 #include "net/ssl/ssl_info.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
@@ -3196,9 +3197,9 @@
     // |didFailProvisionalNavigation:| will differ (it is the server-supplied
     // chain), thus if intermediates were considered, the keys would mismatch.
     scoped_refptr<net::X509Certificate> leafCert =
-        net::X509Certificate::CreateFromHandle(
+        net::x509_util::CreateX509CertificateFromSecCertificate(
             SecTrustGetCertificateAtIndex(trust, 0),
-            net::X509Certificate::OSCertHandles());
+            std::vector<SecCertificateRef>());
     if (leafCert) {
       BOOL is_recoverable =
           policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_UNDECIDED_BY_USER;
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 2f9a6c9..69a0c60 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -38,6 +38,7 @@
 #import "ios/web/web_state/web_state_impl.h"
 #import "ios/web/web_state/wk_web_view_security_util.h"
 #import "net/base/mac/url_conversions.h"
+#include "net/cert/x509_util_ios_and_mac.h"
 #include "net/ssl/ssl_info.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
@@ -189,14 +190,18 @@
 
   scoped_refptr<net::X509Certificate> cert =
       net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
+  ASSERT_TRUE(cert);
+  base::ScopedCFTypeRef<CFMutableArrayRef> chain(
+      net::x509_util::CreateSecCertificateArrayForX509Certificate(cert.get()));
+  ASSERT_TRUE(chain);
 
-  NSArray* chain = @[ static_cast<id>(cert->os_cert_handle()) ];
   GURL url("https://chromium.test");
   NSError* error =
       [NSError errorWithDomain:NSURLErrorDomain
                           code:NSURLErrorServerCertificateHasUnknownRoot
                       userInfo:@{
-                        web::kNSErrorPeerCertificateChainKey : chain,
+                        web::kNSErrorPeerCertificateChainKey :
+                            static_cast<NSArray*>(chain.get()),
                         web::kNSErrorFailingURLKey : net::NSURLWithGURL(url),
                       }];
   base::scoped_nsobject<NSObject> navigation([[NSObject alloc] init]);
diff --git a/ios/web/web_state/wk_web_view_security_util.mm b/ios/web/web_state/wk_web_view_security_util.mm
index 5836fab..5b7e446 100644
--- a/ios/web/web_state/wk_web_view_security_util.mm
+++ b/ios/web/web_state/wk_web_view_security_util.mm
@@ -7,6 +7,7 @@
 #include "base/mac/scoped_cftyperef.h"
 #include "base/strings/sys_string_conversions.h"
 #include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_ios.h"
 #include "net/ssl/ssl_info.h"
 
 namespace web {
@@ -47,11 +48,11 @@
 scoped_refptr<net::X509Certificate> CreateCertFromChain(NSArray* certs) {
   if (certs.count == 0)
     return nullptr;
-  net::X509Certificate::OSCertHandles intermediates;
+  std::vector<SecCertificateRef> intermediates;
   for (NSUInteger i = 1; i < certs.count; i++) {
     intermediates.push_back(reinterpret_cast<SecCertificateRef>(certs[i]));
   }
-  return net::X509Certificate::CreateFromHandle(
+  return net::x509_util::CreateX509CertificateFromSecCertificate(
       reinterpret_cast<SecCertificateRef>(certs[0]), intermediates);
 }
 
@@ -65,11 +66,11 @@
     return nullptr;
   }
 
-  net::X509Certificate::OSCertHandles intermediates;
+  std::vector<SecCertificateRef> intermediates;
   for (CFIndex i = 1; i < cert_count; i++) {
     intermediates.push_back(SecTrustGetCertificateAtIndex(trust, i));
   }
-  return net::X509Certificate::CreateFromHandle(
+  return net::x509_util::CreateX509CertificateFromSecCertificate(
       SecTrustGetCertificateAtIndex(trust, 0), intermediates);
 }
 
diff --git a/ios/web/web_state/wk_web_view_security_util_unittest.mm b/ios/web/web_state/wk_web_view_security_util_unittest.mm
index d2eb9c0..d7d77d3 100644
--- a/ios/web/web_state/wk_web_view_security_util_unittest.mm
+++ b/ios/web/web_state/wk_web_view_security_util_unittest.mm
@@ -14,6 +14,7 @@
 #include "net/cert/x509_cert_types.h"
 #include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util.h"
+#include "net/cert/x509_util_ios.h"
 #include "net/ssl/ssl_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
@@ -37,8 +38,10 @@
       &der_cert);
 
   base::ScopedCFTypeRef<SecCertificateRef> cert(
-      net::X509Certificate::CreateOSCertHandleFromBytes(der_cert.data(),
-                                                        der_cert.size()));
+      net::x509_util::CreateSecCertificateFromBytes(
+          reinterpret_cast<const uint8_t*>(der_cert.data()), der_cert.size()));
+  if (!cert)
+    return nullptr;
   NSArray* result = @[ reinterpret_cast<id>(cert.get()) ];
   return result;
 }
@@ -68,6 +71,7 @@
 TEST_F(WKWebViewSecurityUtilTest, CreationCertFromChain) {
   scoped_refptr<net::X509Certificate> cert =
       CreateCertFromChain(MakeTestCertChain(kTestSubject));
+  ASSERT_TRUE(cert);
   EXPECT_TRUE(cert->subject().GetDisplayName() == kTestSubject);
 }
 
@@ -103,6 +107,7 @@
   base::ScopedCFTypeRef<SecTrustRef> trust =
       CreateTestTrust(MakeTestCertChain(kTestSubject));
   scoped_refptr<net::X509Certificate> cert = CreateCertFromTrust(trust);
+  ASSERT_TRUE(cert);
   EXPECT_TRUE(cert->subject().GetDisplayName() == kTestSubject);
 }
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 27952a6..e67dc82 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -41,7 +41,7 @@
 # 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 || is_android || is_nacl
+use_byte_certs = is_mac || is_android || is_nacl || is_ios
 
 buildflag_header("features") {
   header = "net_features.h"
@@ -605,6 +605,8 @@
       "cert/x509_util_android.h",
       "cert/x509_util_ios.cc",
       "cert/x509_util_ios.h",
+      "cert/x509_util_ios_and_mac.cc",
+      "cert/x509_util_ios_and_mac.h",
       "cert/x509_util_mac.cc",
       "cert/x509_util_mac.h",
       "cert/x509_util_nss.cc",
@@ -1893,6 +1895,8 @@
         "base/network_interfaces_mac.h",
         "base/platform_mime_util_mac.mm",
         "cert/test_root_certs_mac.cc",
+        "cert/x509_util_ios_and_mac.cc",
+        "cert/x509_util_ios_and_mac.h",
         "proxy/proxy_resolver_mac.cc",
         "proxy/proxy_resolver_mac.h",
         "proxy/proxy_server_mac.cc",
@@ -4319,6 +4323,7 @@
     "cert/test_root_certs_unittest.cc",
     "cert/x509_cert_types_unittest.cc",
     "cert/x509_certificate_unittest.cc",
+    "cert/x509_util_ios_and_mac_unittest.cc",
     "cert/x509_util_unittest.cc",
     "cert_net/cert_net_fetcher_impl_unittest.cc",
     "cert_net/nss_ocsp_unittest.cc",
@@ -4905,6 +4910,7 @@
     "url_request/url_request_unittest.cc",
     "url_request/view_cache_helper_unittest.cc",
   ]
+  net_unfiltered_sources = []
 
   configs += [
     "//build/config:precompiled_headers",
@@ -5206,6 +5212,7 @@
       "socket/unix_domain_client_socket_posix_unittest.cc",
       "socket/unix_domain_server_socket_posix_unittest.cc",
     ]
+    net_unfiltered_sources += [ "cert/x509_util_ios_and_mac_unittest.cc" ]
 
     bundle_deps = [ ":net_unittests_bundle_data" ]
   }
@@ -5295,6 +5302,11 @@
   if (host_toolchain == current_toolchain) {
     deps += [ "//net/tools/transport_security_state_generator:transport_security_state_generator_test_sources" ]
   }
+
+  # Add back some sources that were otherwise filtered out.
+  set_sources_assignment_filter([])
+  sources += net_unfiltered_sources
+  set_sources_assignment_filter(sources_assignment_filter)
 }
 
 # !is_android && !is_win && !is_mac
diff --git a/net/base/net_string_util_icu_alternatives_ios.mm b/net/base/net_string_util_icu_alternatives_ios.mm
index 7ea42808..5470437 100644
--- a/net/base/net_string_util_icu_alternatives_ios.mm
+++ b/net/base/net_string_util_icu_alternatives_ios.mm
@@ -2,13 +2,34 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <CoreFoundation/CoreFoundation.h>
+
 #include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
+#include "base/strings/sys_string_conversions.h"
 #include "net/base/net_string_util.h"
 
 namespace net {
 
+namespace {
+
+bool CharsetToCFStringEncoding(const char* charset,
+                               CFStringEncoding* encoding) {
+  if (charset == kCharsetLatin1 || strcmp(charset, kCharsetLatin1) == 0) {
+    *encoding = kCFStringEncodingISOLatin1;
+    return true;
+  }
+  // TODO(mattm): handle other charsets? See
+  // https://developer.apple.com/reference/corefoundation/cfstringbuiltinencodings?language=objc
+  // for list of standard CFStringEncodings.
+
+  return false;
+}
+
+}  // namespace
+
 // This constant cannot be defined as const char[] because it is initialized
 // by base::kCodepageLatin1 (which is const char[]) in net_string_util_icu.cc.
 const char* const kCharsetLatin1 = "ISO-8859-1";
@@ -16,8 +37,18 @@
 bool ConvertToUtf8(const std::string& text,
                    const char* charset,
                    std::string* output) {
-  DCHECK(false) << "Not implemented yet.";
-  return false;
+  CFStringEncoding encoding;
+  if (!CharsetToCFStringEncoding(charset, &encoding))
+    return false;
+
+  base::ScopedCFTypeRef<CFStringRef> cfstring(CFStringCreateWithBytes(
+      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(text.data()),
+      base::checked_cast<CFIndex>(text.length()), encoding,
+      false /* isExternalRepresentation */));
+  if (!cfstring)
+    return false;
+  *output = base::SysCFStringRefToUTF8(cfstring.get());
+  return true;
 }
 
 bool ConvertToUtf8AndNormalize(const std::string& text,
@@ -41,4 +72,4 @@
   return false;
 }
 
-}  // namespace net
\ No newline at end of file
+}  // namespace net
diff --git a/net/cert/cert_verify_proc_ios.cc b/net/cert/cert_verify_proc_ios.cc
index 527326e..ebb2960 100644
--- a/net/cert/cert_verify_proc_ios.cc
+++ b/net/cert/cert_verify_proc_ios.cc
@@ -14,6 +14,8 @@
 #include "net/cert/cert_verify_result.h"
 #include "net/cert/test_root_certs.h"
 #include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_ios.h"
+#include "net/cert/x509_util_ios_and_mac.h"
 #include "net/ssl/openssl_ssl_util.h"
 
 using base::ScopedCFTypeRef;
@@ -115,14 +117,19 @@
       verified_chain.push_back(chain_cert);
     }
 
-    std::string der_bytes;
-    if (!X509Certificate::GetDEREncoded(chain_cert, &der_bytes)) {
+    base::ScopedCFTypeRef<CFDataRef> der_data(
+        SecCertificateCopyData(chain_cert));
+    if (!der_data) {
       verify_result->cert_status |= CERT_STATUS_INVALID;
       return;
     }
 
     base::StringPiece spki_bytes;
-    if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes)) {
+    if (!asn1::ExtractSPKIFromDERCert(
+            base::StringPiece(
+                reinterpret_cast<const char*>(CFDataGetBytePtr(der_data)),
+                CFDataGetLength(der_data)),
+            &spki_bytes)) {
       verify_result->cert_status |= CERT_STATUS_INVALID;
       return;
     }
@@ -148,7 +155,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
@@ -248,7 +256,7 @@
     return NetErrorFromOSStatus(status);
 
   ScopedCFTypeRef<CFMutableArrayRef> cert_array(
-      cert->CreateOSCertChainForCert());
+      x509_util::CreateSecCertificateArrayForX509Certificate(cert));
   ScopedCFTypeRef<SecTrustRef> trust_ref;
   SecTrustResultType trust_result = kSecTrustResultDeny;
   ScopedCFTypeRef<CFArrayRef> final_chain;
diff --git a/net/cert/cert_verify_proc_ios_unittest.cc b/net/cert/cert_verify_proc_ios_unittest.cc
index b2191ce..e76b2b7 100644
--- a/net/cert/cert_verify_proc_ios_unittest.cc
+++ b/net/cert/cert_verify_proc_ios_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/mac/scoped_cftyperef.h"
 #include "base/memory/ref_counted.h"
 #include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_ios_and_mac.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -23,14 +24,22 @@
 
   scoped_refptr<net::X509Certificate> cert =
       net::ImportCertFromFile(net::GetTestCertsDirectory(), cert_file);
+  if (!cert) {
+    ADD_FAILURE();
+    return scoped_result;
+  }
   base::ScopedCFTypeRef<CFMutableArrayRef> certs(
-      CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks));
-  CFArrayAppendValue(certs, cert->os_cert_handle());
+      net::x509_util::CreateSecCertificateArrayForX509Certificate(cert.get()));
+  if (!certs) {
+    ADD_FAILURE();
+    return scoped_result;
+  }
 
   base::ScopedCFTypeRef<SecPolicyRef> policy(
       SecPolicyCreateSSL(TRUE, CFSTR("chromium.org")));
   SecTrustRef result = nullptr;
-  if (SecTrustCreateWithCertificates(certs, policy, &result) == errSecSuccess) {
+  if (SecTrustCreateWithCertificates(certs.get(), policy, &result) ==
+      errSecSuccess) {
     scoped_result.reset(result);
   }
   return scoped_result;
diff --git a/net/cert/cert_verify_proc_mac.cc b/net/cert/cert_verify_proc_mac.cc
index 4184294b..701587d 100644
--- a/net/cert/cert_verify_proc_mac.cc
+++ b/net/cert/cert_verify_proc_mac.cc
@@ -35,6 +35,7 @@
 #include "net/cert/test_root_certs.h"
 #include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util.h"
+#include "net/cert/x509_util_ios_and_mac.h"
 #include "net/cert/x509_util_mac.h"
 
 // CSSM functions are deprecated as of OSX 10.7, but have no replacement.
diff --git a/net/cert/x509_certificate.h b/net/cert/x509_certificate.h
index 0cb93b5a..4fb0b3e 100644
--- a/net/cert/x509_certificate.h
+++ b/net/cert/x509_certificate.h
@@ -231,15 +231,6 @@
     return intermediate_ca_certs_;
   }
 
-#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.
-  // The first item in the array will be this certificate, followed by its
-  // intermediates, if any.
-  CFMutableArrayRef CreateOSCertChainForCert() const;
-#endif
-
   // Do any of the given issuer names appear in this cert's chain of trust?
   // |valid_issuers| is a list of DER-encoded X.509 DistinguishedNames.
   bool IsIssuedByEncoded(const std::vector<std::string>& valid_issuers);
diff --git a/net/cert/x509_certificate_ios.cc b/net/cert/x509_certificate_ios.cc
index df8482f..475de8f9 100644
--- a/net/cert/x509_certificate_ios.cc
+++ b/net/cert/x509_certificate_ios.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_util.h"
 #include "crypto/openssl_util.h"
 #include "net/base/ip_address.h"
+#include "net/cert/x509_util_ios.h"
 #include "net/cert/x509_util_openssl.h"
 #include "net/ssl/openssl_ssl_util.h"
 #include "third_party/boringssl/src/include/openssl/x509.h"
@@ -24,21 +25,6 @@
 
 namespace {
 
-// Returns true if a given |cert_handle| is actually a valid X.509 certificate
-// handle.
-//
-// SecCertificateCreateFromData() does not always force the immediate parsing of
-// the certificate, and as such, may return a SecCertificateRef for an
-// invalid/unparsable certificate. Force parsing to occur to ensure that the
-// SecCertificateRef is correct. On later versions where
-// SecCertificateCreateFromData() immediately parses, rather than lazily, this
-// call is cheap, as the subject is cached.
-bool IsValidOSCertHandle(SecCertificateRef cert_handle) {
-  ScopedCFTypeRef<CFStringRef> sanity_check(
-      SecCertificateCopySubjectSummary(cert_handle));
-  return sanity_check != nullptr;
-}
-
 bssl::UniquePtr<X509> OSCertHandleToOpenSSL(
     X509Certificate::OSCertHandle os_handle) {
   std::string der_encoded;
@@ -245,19 +231,8 @@
 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
     const char* data,
     size_t length) {
-  ScopedCFTypeRef<CFDataRef> cert_data(CFDataCreateWithBytesNoCopy(
-      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data),
-      base::checked_cast<CFIndex>(length), kCFAllocatorNull));
-  if (!cert_data)
-    return nullptr;
-  OSCertHandle cert_handle = SecCertificateCreateWithData(nullptr, cert_data);
-  if (!cert_handle)
-    return nullptr;
-  if (!IsValidOSCertHandle(cert_handle)) {
-    CFRelease(cert_handle);
-    return nullptr;
-  }
-  return cert_handle;
+  return x509_util::CreateSecCertificateFromBytes(
+      reinterpret_cast<const uint8_t*>(data), length);
 }
 
 // static
@@ -375,19 +350,6 @@
   *size_bits = EVP_PKEY_bits(key);
 }
 
-CFMutableArrayRef X509Certificate::CreateOSCertChainForCert() const {
-  CFMutableArrayRef cert_list =
-      CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
-  if (!cert_list)
-    return nullptr;
-
-  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;
-}
-
 bool X509Certificate::IsIssuedByEncoded(
     const std::vector<std::string>& valid_issuers) {
   if (valid_issuers.empty())
diff --git a/net/cert/x509_util_ios.cc b/net/cert/x509_util_ios.cc
index 9bf4185..18d33151 100644
--- a/net/cert/x509_util_ios.cc
+++ b/net/cert/x509_util_ios.cc
@@ -5,16 +5,101 @@
 #include "net/cert/x509_util_ios.h"
 
 #include "net/cert/x509_certificate.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
 
 namespace net {
 
 namespace x509_util {
 
+namespace {
+
+// Returns true if a given |cert_handle| is actually a valid X.509 certificate
+// handle.
+//
+// SecCertificateCreateFromData() does not always force the immediate parsing of
+// the certificate, and as such, may return a SecCertificateRef for an
+// invalid/unparsable certificate. Force parsing to occur to ensure that the
+// SecCertificateRef is correct. On later versions where
+// SecCertificateCreateFromData() immediately parses, rather than lazily, this
+// call is cheap, as the subject is cached.
+bool IsValidSecCertificate(SecCertificateRef cert_handle) {
+  base::ScopedCFTypeRef<CFStringRef> sanity_check(
+      SecCertificateCopySubjectSummary(cert_handle));
+  return sanity_check != nullptr;
+}
+
+}  // namespace
+
+base::ScopedCFTypeRef<SecCertificateRef> CreateSecCertificateFromBytes(
+    const uint8_t* data,
+    size_t length) {
+  base::ScopedCFTypeRef<CFDataRef> cert_data(CFDataCreateWithBytesNoCopy(
+      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data),
+      base::checked_cast<CFIndex>(length), kCFAllocatorNull));
+  if (!cert_data)
+    return base::ScopedCFTypeRef<SecCertificateRef>();
+
+  base::ScopedCFTypeRef<SecCertificateRef> cert_handle(
+      SecCertificateCreateWithData(nullptr, cert_data));
+  if (!cert_handle)
+    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
+}
+
+scoped_refptr<X509Certificate> CreateX509CertificateFromSecCertificate(
+    SecCertificateRef sec_cert,
+    const std::vector<SecCertificateRef>& sec_chain) {
+#if BUILDFLAG(USE_BYTE_CERTS)
+  if (!sec_cert)
+    return nullptr;
+  base::ScopedCFTypeRef<CFDataRef> der_data(SecCertificateCopyData(sec_cert));
+  if (!der_data)
+    return nullptr;
+  bssl::UniquePtr<CRYPTO_BUFFER> cert_handle(
+      X509Certificate::CreateOSCertHandleFromBytes(
+          reinterpret_cast<const char*>(CFDataGetBytePtr(der_data)),
+          CFDataGetLength(der_data)));
+  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)
+      return nullptr;
+    der_data.reset(SecCertificateCopyData(sec_intermediate));
+    if (!der_data)
+      return nullptr;
+    bssl::UniquePtr<CRYPTO_BUFFER> intermediate_cert_handle(
+        X509Certificate::CreateOSCertHandleFromBytes(
+            reinterpret_cast<const char*>(CFDataGetBytePtr(der_data)),
+            CFDataGetLength(der_data)));
+    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
 }
 
 }  // namespace x509_util
diff --git a/net/cert/x509_util_ios.h b/net/cert/x509_util_ios.h
index bf3473f..cfc9827 100644
--- a/net/cert/x509_util_ios.h
+++ b/net/cert/x509_util_ios.h
@@ -7,7 +7,10 @@
 
 #include <Security/Security.h>
 
+#include <vector>
+
 #include "base/mac/scoped_cftyperef.h"
+#include "base/memory/ref_counted.h"
 #include "net/base/net_export.h"
 
 namespace net {
@@ -16,10 +19,22 @@
 
 namespace x509_util {
 
+// 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);
 
+// 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);
+
 }  // namespace x509_util
 
 }  // namespace net
diff --git a/net/cert/x509_util_ios_and_mac.cc b/net/cert/x509_util_ios_and_mac.cc
new file mode 100644
index 0000000..ff8faaf
--- /dev/null
+++ b/net/cert/x509_util_ios_and_mac.cc
@@ -0,0 +1,54 @@
+// 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_and_mac.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
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+namespace net {
+
+namespace x509_util {
+
+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)));
+    if (!sec_cert)
+      return base::ScopedCFTypeRef<CFMutableArrayRef>();
+    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;
+}
+
+}  // namespace x509_util
+
+}  // namespace net
diff --git a/net/cert/x509_util_ios_and_mac.h b/net/cert/x509_util_ios_and_mac.h
new file mode 100644
index 0000000..4cc9183
--- /dev/null
+++ b/net/cert/x509_util_ios_and_mac.h
@@ -0,0 +1,32 @@
+// 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_AND_MAC_H_
+#define NET_CERT_X509_UTIL_IOS_AND_MAC_H_
+
+#include <CoreFoundation/CFArray.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 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);
+
+}  // namespace x509_util
+
+}  // namespace net
+
+#endif  // NET_CERT_X509_UTIL_IOS_AND_MAC_H_
diff --git a/net/cert/x509_util_ios_and_mac_unittest.cc b/net/cert/x509_util_ios_and_mac_unittest.cc
new file mode 100644
index 0000000..bab2923
--- /dev/null
+++ b/net/cert/x509_util_ios_and_mac_unittest.cc
@@ -0,0 +1,152 @@
+// 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_and_mac.h"
+
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_data_directory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_IOS)
+#include "net/cert/x509_util_ios.h"
+#else
+#include "net/cert/x509_util_mac.h"
+#endif
+
+namespace net {
+
+namespace x509_util {
+
+namespace {
+
+std::string BytesForSecCert(SecCertificateRef sec_cert) {
+  std::string result;
+  base::ScopedCFTypeRef<CFDataRef> der_data(SecCertificateCopyData(sec_cert));
+  if (!der_data) {
+    ADD_FAILURE();
+    return result;
+  }
+  result.assign(reinterpret_cast<const char*>(CFDataGetBytePtr(der_data)),
+                CFDataGetLength(der_data));
+  return result;
+}
+
+std::string BytesForSecCert(const void* sec_cert) {
+  return BytesForSecCert(
+      reinterpret_cast<SecCertificateRef>(const_cast<void*>(sec_cert)));
+}
+
+std::string BytesForX509CertHandle(X509Certificate::OSCertHandle handle) {
+  std::string result;
+  if (!X509Certificate::GetDEREncoded(handle, &result))
+    ADD_FAILURE();
+  return result;
+}
+
+std::string BytesForX509Cert(X509Certificate* cert) {
+  return BytesForX509CertHandle(cert->os_cert_handle());
+}
+
+}  // namespace
+
+TEST(X509UtilTest, CreateSecCertificateArrayForX509Certificate) {
+  scoped_refptr<X509Certificate> cert = CreateCertificateChainFromFile(
+      GetTestCertsDirectory(), "multi-root-chain1.pem",
+      X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+  ASSERT_TRUE(cert);
+  EXPECT_EQ(3U, cert->GetIntermediateCertificates().size());
+
+  base::ScopedCFTypeRef<CFMutableArrayRef> sec_certs(
+      CreateSecCertificateArrayForX509Certificate(cert.get()));
+  ASSERT_TRUE(sec_certs);
+  ASSERT_EQ(4, CFArrayGetCount(sec_certs.get()));
+  for (int i = 0; i < 4; ++i)
+    ASSERT_TRUE(CFArrayGetValueAtIndex(sec_certs.get(), i));
+
+  EXPECT_EQ(BytesForX509Cert(cert.get()),
+            BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 0)));
+  EXPECT_EQ(BytesForX509CertHandle(cert->GetIntermediateCertificates()[0]),
+            BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 1)));
+  EXPECT_EQ(BytesForX509CertHandle(cert->GetIntermediateCertificates()[1]),
+            BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 2)));
+  EXPECT_EQ(BytesForX509CertHandle(cert->GetIntermediateCertificates()[2]),
+            BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 3)));
+}
+
+TEST(X509UtilTest,
+     CreateSecCertificateFromBytesAndCreateX509CertificateFromSecCertificate) {
+  CertificateList certs = CreateCertificateListFromFile(
+      GetTestCertsDirectory(), "multi-root-chain1.pem",
+      X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+  ASSERT_EQ(4u, certs.size());
+
+  std::string bytes_cert0 = BytesForX509CertHandle(certs[0]->os_cert_handle());
+  std::string bytes_cert1 = BytesForX509CertHandle(certs[1]->os_cert_handle());
+  std::string bytes_cert2 = BytesForX509CertHandle(certs[2]->os_cert_handle());
+  std::string bytes_cert3 = BytesForX509CertHandle(certs[3]->os_cert_handle());
+
+  base::ScopedCFTypeRef<SecCertificateRef> sec_cert0(
+      CreateSecCertificateFromBytes(
+          reinterpret_cast<const uint8_t*>(bytes_cert0.data()),
+          bytes_cert0.length()));
+  ASSERT_TRUE(sec_cert0);
+  EXPECT_EQ(bytes_cert0, BytesForSecCert(sec_cert0));
+
+  base::ScopedCFTypeRef<SecCertificateRef> sec_cert1(
+      CreateSecCertificateFromBytes(
+          reinterpret_cast<const uint8_t*>(bytes_cert1.data()),
+          bytes_cert1.length()));
+  ASSERT_TRUE(sec_cert1);
+  EXPECT_EQ(bytes_cert1, BytesForSecCert(sec_cert1));
+
+  base::ScopedCFTypeRef<SecCertificateRef> sec_cert2(
+      CreateSecCertificateFromX509Certificate(certs[2].get()));
+  ASSERT_TRUE(sec_cert2);
+  EXPECT_EQ(bytes_cert2, BytesForSecCert(sec_cert2));
+
+  base::ScopedCFTypeRef<SecCertificateRef> sec_cert3(
+      CreateSecCertificateFromX509Certificate(certs[3].get()));
+  ASSERT_TRUE(sec_cert3);
+  EXPECT_EQ(bytes_cert3, BytesForSecCert(sec_cert3));
+
+  scoped_refptr<X509Certificate> x509_cert_no_intermediates =
+      CreateX509CertificateFromSecCertificate(sec_cert0.get(), {});
+  ASSERT_TRUE(x509_cert_no_intermediates);
+  EXPECT_EQ(0U,
+            x509_cert_no_intermediates->GetIntermediateCertificates().size());
+  EXPECT_EQ(bytes_cert0, BytesForX509CertHandle(
+                             x509_cert_no_intermediates->os_cert_handle()));
+
+  scoped_refptr<X509Certificate> x509_cert_one_intermediate =
+      CreateX509CertificateFromSecCertificate(sec_cert0.get(),
+                                              {sec_cert1.get()});
+  ASSERT_TRUE(x509_cert_one_intermediate);
+  EXPECT_EQ(bytes_cert0, BytesForX509CertHandle(
+                             x509_cert_one_intermediate->os_cert_handle()));
+  ASSERT_EQ(1U,
+            x509_cert_one_intermediate->GetIntermediateCertificates().size());
+  EXPECT_EQ(bytes_cert1,
+            BytesForX509CertHandle(
+                x509_cert_one_intermediate->GetIntermediateCertificates()[0]));
+
+  scoped_refptr<X509Certificate> x509_cert_two_intermediates =
+      CreateX509CertificateFromSecCertificate(
+          sec_cert0.get(), {sec_cert1.get(), sec_cert2.get()});
+  ASSERT_TRUE(x509_cert_two_intermediates);
+  EXPECT_EQ(bytes_cert0, BytesForX509CertHandle(
+                             x509_cert_two_intermediates->os_cert_handle()));
+  ASSERT_EQ(2U,
+            x509_cert_two_intermediates->GetIntermediateCertificates().size());
+  EXPECT_EQ(bytes_cert1,
+            BytesForX509CertHandle(
+                x509_cert_two_intermediates->GetIntermediateCertificates()[0]));
+  EXPECT_EQ(bytes_cert2,
+            BytesForX509CertHandle(
+                x509_cert_two_intermediates->GetIntermediateCertificates()[1]));
+}
+
+}  // namespace x509_util
+
+}  // namespace net
diff --git a/net/cert/x509_util_mac.cc b/net/cert/x509_util_mac.cc
index c8f6a2f..d533588 100644
--- a/net/cert/x509_util_mac.cc
+++ b/net/cert/x509_util_mac.cc
@@ -92,39 +92,6 @@
 #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)));
-    if (!sec_cert)
-      return base::ScopedCFTypeRef<CFMutableArrayRef>();
-    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) {
diff --git a/net/cert/x509_util_mac.h b/net/cert/x509_util_mac.h
index 1700a6cc..4368613 100644
--- a/net/cert/x509_util_mac.h
+++ b/net/cert/x509_util_mac.h
@@ -44,14 +44,6 @@
 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>
diff --git a/net/ssl/client_cert_store_mac.cc b/net/ssl/client_cert_store_mac.cc
index 8003524..7fadb33 100644
--- a/net/ssl/client_cert_store_mac.cc
+++ b/net/ssl/client_cert_store_mac.cc
@@ -22,6 +22,7 @@
 #include "crypto/mac_security_services_lock.h"
 #include "net/base/host_port_pair.h"
 #include "net/cert/x509_util.h"
+#include "net/cert/x509_util_ios_and_mac.h"
 #include "net/cert/x509_util_mac.h"
 
 using base::ScopedCFTypeRef;