Use CRYPTO_BUFFER APIs instead of X509 when building with BoringSSL.

Using CRYPTO_BUFFERs instead of legacy X509 objects offers memory and
security gains, and will provide binary size improvements as well once
the default list of built-in certificates can be removed; the code
dealing with them still depends on the X509 API.

Implemented by splitting openssl_identity and openssl_certificate
into BoringSSL and vanilla OpenSSL implementations.

Bug: webrtc:11410
Change-Id: Idc043462faac5e4ab1b75bedab2057197f80aba6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/174120
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: David Benjamin <davidben@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Taylor <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32811}
diff --git a/pc/test/fake_rtc_certificate_generator.h b/pc/test/fake_rtc_certificate_generator.h
index b726a4c..b591c4c 100644
--- a/pc/test/fake_rtc_certificate_generator.h
+++ b/pc/test/fake_rtc_certificate_generator.h
@@ -83,7 +83,7 @@
 
 // ECDSA with EC_NIST_P256.
 // These PEM strings were created by generating an identity with
-// |SSLIdentity::Generate| and invoking |identity->PrivateKeyToPEMString()|,
+// |SSLIdentity::Create| and invoking |identity->PrivateKeyToPEMString()|,
 // |identity->PublicKeyToPEMString()| and
 // |identity->certificate().ToPEMString()|.
 static const rtc::RTCCertificatePEM kEcdsaPems[] = {
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 8b92090..8762bfb 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -911,12 +911,10 @@
     "openssl.h",
     "openssl_adapter.cc",
     "openssl_adapter.h",
-    "openssl_certificate.cc",
-    "openssl_certificate.h",
     "openssl_digest.cc",
     "openssl_digest.h",
-    "openssl_identity.cc",
-    "openssl_identity.h",
+    "openssl_key_pair.cc",
+    "openssl_key_pair.h",
     "openssl_session_cache.cc",
     "openssl_session_cache.h",
     "openssl_stream_adapter.cc",
@@ -962,6 +960,22 @@
     "unique_id_generator.h",
   ]
 
+  if (rtc_openssl_is_boringssl) {
+    sources += [
+      "boringssl_certificate.cc",
+      "boringssl_certificate.h",
+      "boringssl_identity.cc",
+      "boringssl_identity.h",
+    ]
+  } else {
+    sources += [
+      "openssl_certificate.cc",
+      "openssl_certificate.h",
+      "openssl_identity.cc",
+      "openssl_identity.h",
+    ]
+  }
+
   if (build_with_chromium) {
     include_dirs = [ "../../boringssl/src/include" ]
     public_configs += [ ":rtc_base_chromium_config" ]
diff --git a/rtc_base/boringssl_certificate.cc b/rtc_base/boringssl_certificate.cc
new file mode 100644
index 0000000..c9713c2
--- /dev/null
+++ b/rtc_base/boringssl_certificate.cc
@@ -0,0 +1,410 @@
+/*
+ *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/boringssl_certificate.h"
+
+#if defined(WEBRTC_WIN)
+// Must be included first before openssl headers.
+#include "rtc_base/win32.h"  // NOLINT
+#endif                       // WEBRTC_WIN
+
+#include <openssl/asn1.h>
+#include <openssl/bytestring.h>
+#include <openssl/digest.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/pool.h>
+#include <openssl/rand.h>
+#include <time.h>
+
+#include <cstring>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/message_digest.h"
+#include "rtc_base/openssl_digest.h"
+#include "rtc_base/openssl_identity.h"
+#include "rtc_base/openssl_utility.h"
+
+namespace rtc {
+namespace {
+
+// List of OIDs of signature algorithms accepted by WebRTC.
+// Taken from openssl/nid.h.
+static const uint8_t kMD5WithRSA[] = {0x2b, 0x0e, 0x03, 0x02, 0x03};
+static const uint8_t kMD5WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                0x0d, 0x01, 0x01, 0x04};
+static const uint8_t kECDSAWithSHA1[] = {0x2a, 0x86, 0x48, 0xce,
+                                         0x3d, 0x04, 0x01};
+static const uint8_t kDSAWithSHA1[] = {0x2a, 0x86, 0x48, 0xce,
+                                       0x38, 0x04, 0x03};
+static const uint8_t kDSAWithSHA1_2[] = {0x2b, 0x0e, 0x03, 0x02, 0x1b};
+static const uint8_t kSHA1WithRSA[] = {0x2b, 0x0e, 0x03, 0x02, 0x1d};
+static const uint8_t kSHA1WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                 0x0d, 0x01, 0x01, 0x05};
+static const uint8_t kECDSAWithSHA224[] = {0x2a, 0x86, 0x48, 0xce,
+                                           0x3d, 0x04, 0x03, 0x01};
+static const uint8_t kSHA224WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                   0x0d, 0x01, 0x01, 0x0e};
+static const uint8_t kDSAWithSHA224[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+                                         0x03, 0x04, 0x03, 0x01};
+static const uint8_t kECDSAWithSHA256[] = {0x2a, 0x86, 0x48, 0xce,
+                                           0x3d, 0x04, 0x03, 0x02};
+static const uint8_t kSHA256WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                   0x0d, 0x01, 0x01, 0x0b};
+static const uint8_t kDSAWithSHA256[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+                                         0x03, 0x04, 0x03, 0x028};
+static const uint8_t kECDSAWithSHA384[] = {0x2a, 0x86, 0x48, 0xce,
+                                           0x3d, 0x04, 0x03, 0x03};
+static const uint8_t kSHA384WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                   0x0d, 0x01, 0x01, 0x0c};
+static const uint8_t kECDSAWithSHA512[] = {0x2a, 0x86, 0x48, 0xce,
+                                           0x3d, 0x04, 0x03, 0x04};
+static const uint8_t kSHA512WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                   0x0d, 0x01, 0x01, 0x0d};
+
+#if !defined(NDEBUG)
+// Print a certificate to the log, for debugging.
+static void PrintCert(BoringSSLCertificate* cert) {
+  // Since we're using CRYPTO_BUFFER, we can't use X509_print_ex, so we'll just
+  // print the PEM string.
+  RTC_DLOG(LS_VERBOSE) << "PEM representation of certificate:\n"
+                       << cert->ToPEMString();
+}
+#endif
+
+bool AddSHA256SignatureAlgorithm(CBB* cbb, KeyType key_type) {
+  // An AlgorithmIdentifier is described in RFC 5280, 4.1.1.2.
+  CBB sequence, oid, params;
+  if (!CBB_add_asn1(cbb, &sequence, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&sequence, &oid, CBS_ASN1_OBJECT)) {
+    return false;
+  }
+
+  switch (key_type) {
+    case KT_RSA:
+      if (!CBB_add_bytes(&oid, kSHA256WithRSAEncryption,
+                         sizeof(kSHA256WithRSAEncryption)) ||
+          !CBB_add_asn1(&sequence, &params, CBS_ASN1_NULL)) {
+        return false;
+      }
+      break;
+    case KT_ECDSA:
+      if (!CBB_add_bytes(&oid, kECDSAWithSHA256, sizeof(kECDSAWithSHA256))) {
+        return false;
+      }
+      break;
+    default:
+      RTC_NOTREACHED();
+      return false;
+  }
+  if (!CBB_flush(cbb)) {
+    return false;
+  }
+  return true;
+}
+
+// Adds an X.509 Common Name to |cbb|.
+bool AddCommonName(CBB* cbb, const std::string& common_name) {
+  // See RFC 4519.
+  static const uint8_t kCommonName[] = {0x55, 0x04, 0x03};
+
+  if (common_name.empty()) {
+    RTC_LOG(LS_ERROR) << "Common name cannot be empty.";
+    return false;
+  }
+
+  // See RFC 5280, section 4.1.2.4.
+  CBB rdns;
+  if (!CBB_add_asn1(cbb, &rdns, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+
+  CBB rdn, attr, type, value;
+  if (!CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET) ||
+      !CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT) ||
+      !CBB_add_bytes(&type, kCommonName, sizeof(kCommonName)) ||
+      !CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING) ||
+      !CBB_add_bytes(&value,
+                     reinterpret_cast<const uint8_t*>(common_name.c_str()),
+                     common_name.size()) ||
+      !CBB_flush(cbb)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool AddTime(CBB* cbb, time_t time) {
+  bssl::UniquePtr<ASN1_TIME> asn1_time(ASN1_TIME_new());
+  if (!asn1_time) {
+    return false;
+  }
+
+  if (!ASN1_TIME_set(asn1_time.get(), time)) {
+    return false;
+  }
+
+  unsigned tag;
+  switch (asn1_time->type) {
+    case V_ASN1_UTCTIME:
+      tag = CBS_ASN1_UTCTIME;
+      break;
+    case V_ASN1_GENERALIZEDTIME:
+      tag = CBS_ASN1_GENERALIZEDTIME;
+      break;
+    default:
+      return false;
+  }
+
+  CBB child;
+  if (!CBB_add_asn1(cbb, &child, tag) ||
+      !CBB_add_bytes(&child, asn1_time->data, asn1_time->length) ||
+      !CBB_flush(cbb)) {
+    return false;
+  }
+
+  return true;
+}
+
+// Generate a self-signed certificate, with the public key from the
+// given key pair. Caller is responsible for freeing the returned object.
+static bssl::UniquePtr<CRYPTO_BUFFER> MakeCertificate(
+    EVP_PKEY* pkey,
+    const SSLIdentityParams& params) {
+  RTC_LOG(LS_INFO) << "Making certificate for " << params.common_name;
+
+  // See RFC 5280, section 4.1. First, construct the TBSCertificate.
+  bssl::ScopedCBB cbb;
+  CBB tbs_cert, version, validity;
+  uint8_t* tbs_cert_bytes;
+  size_t tbs_cert_len;
+  uint64_t serial_number;
+  if (!CBB_init(cbb.get(), 64) ||
+      !CBB_add_asn1(cbb.get(), &tbs_cert, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&tbs_cert, &version,
+                    CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+      !CBB_add_asn1_uint64(&version, 2) ||
+      !RAND_bytes(reinterpret_cast<uint8_t*>(&serial_number),
+                  sizeof(serial_number)) ||
+      !CBB_add_asn1_uint64(&tbs_cert, serial_number) ||
+      !AddSHA256SignatureAlgorithm(&tbs_cert, params.key_params.type()) ||
+      !AddCommonName(&tbs_cert, params.common_name) ||  // issuer
+      !CBB_add_asn1(&tbs_cert, &validity, CBS_ASN1_SEQUENCE) ||
+      !AddTime(&validity, params.not_before) ||
+      !AddTime(&validity, params.not_after) ||
+      !AddCommonName(&tbs_cert, params.common_name) ||  // subject
+      !EVP_marshal_public_key(&tbs_cert, pkey) ||       // subjectPublicKeyInfo
+      !CBB_finish(cbb.get(), &tbs_cert_bytes, &tbs_cert_len)) {
+    return nullptr;
+  }
+
+  bssl::UniquePtr<uint8_t> delete_tbs_cert_bytes(tbs_cert_bytes);
+
+  // Sign the TBSCertificate and write the entire certificate.
+  CBB cert, signature;
+  bssl::ScopedEVP_MD_CTX ctx;
+  uint8_t* sig_out;
+  size_t sig_len;
+  uint8_t* cert_bytes;
+  size_t cert_len;
+  if (!CBB_init(cbb.get(), tbs_cert_len) ||
+      !CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_bytes(&cert, tbs_cert_bytes, tbs_cert_len) ||
+      !AddSHA256SignatureAlgorithm(&cert, params.key_params.type()) ||
+      !CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING) ||
+      !CBB_add_u8(&signature, 0 /* no unused bits */) ||
+      !EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, pkey) ||
+      // Compute the maximum signature length.
+      !EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes,
+                      tbs_cert_len) ||
+      !CBB_reserve(&signature, &sig_out, sig_len) ||
+      // Actually sign the TBSCertificate.
+      !EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_cert_bytes,
+                      tbs_cert_len) ||
+      !CBB_did_write(&signature, sig_len) ||
+      !CBB_finish(cbb.get(), &cert_bytes, &cert_len)) {
+    return nullptr;
+  }
+  bssl::UniquePtr<uint8_t> delete_cert_bytes(cert_bytes);
+
+  RTC_LOG(LS_INFO) << "Returning certificate";
+  return bssl::UniquePtr<CRYPTO_BUFFER>(
+      CRYPTO_BUFFER_new(cert_bytes, cert_len, openssl::GetBufferPool()));
+}
+
+}  // namespace
+
+BoringSSLCertificate::BoringSSLCertificate(
+    bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer)
+    : cert_buffer_(std::move(cert_buffer)) {
+  RTC_DCHECK(cert_buffer_ != nullptr);
+}
+
+std::unique_ptr<BoringSSLCertificate> BoringSSLCertificate::Generate(
+    OpenSSLKeyPair* key_pair,
+    const SSLIdentityParams& params) {
+  SSLIdentityParams actual_params(params);
+  if (actual_params.common_name.empty()) {
+    // Use a random string, arbitrarily 8 chars long.
+    actual_params.common_name = CreateRandomString(8);
+  }
+  bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer =
+      MakeCertificate(key_pair->pkey(), actual_params);
+  if (!cert_buffer) {
+    openssl::LogSSLErrors("Generating certificate");
+    return nullptr;
+  }
+  auto ret = std::make_unique<BoringSSLCertificate>(std::move(cert_buffer));
+#if !defined(NDEBUG)
+  PrintCert(ret.get());
+#endif
+  return ret;
+}
+
+std::unique_ptr<BoringSSLCertificate> BoringSSLCertificate::FromPEMString(
+    const std::string& pem_string) {
+  std::string der;
+  if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der)) {
+    return nullptr;
+  }
+  bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer(
+      CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t*>(der.c_str()),
+                        der.length(), openssl::GetBufferPool()));
+  if (!cert_buffer) {
+    return nullptr;
+  }
+  return std::make_unique<BoringSSLCertificate>(std::move(cert_buffer));
+}
+
+#define OID_MATCHES(oid, oid_other)      \
+  (CBS_len(&oid) == sizeof(oid_other) && \
+  0 == memcmp(CBS_data(&oid), oid_other, sizeof(oid_other)))
+
+bool BoringSSLCertificate::GetSignatureDigestAlgorithm(
+    std::string* algorithm) const {
+  CBS oid;
+  if (!openssl::ParseCertificate(cert_buffer_.get(), &oid, nullptr)) {
+    RTC_LOG(LS_ERROR) << "Failed to parse certificate.";
+    return false;
+  }
+  if (OID_MATCHES(oid, kMD5WithRSA) ||
+      OID_MATCHES(oid, kMD5WithRSAEncryption)) {
+    *algorithm = DIGEST_MD5;
+    return true;
+  }
+  if (OID_MATCHES(oid, kECDSAWithSHA1) || OID_MATCHES(oid, kDSAWithSHA1) ||
+      OID_MATCHES(oid, kDSAWithSHA1_2) || OID_MATCHES(oid, kSHA1WithRSA) ||
+      OID_MATCHES(oid, kSHA1WithRSAEncryption)) {
+    *algorithm = DIGEST_SHA_1;
+    return true;
+  }
+  if (OID_MATCHES(oid, kECDSAWithSHA224) ||
+      OID_MATCHES(oid, kSHA224WithRSAEncryption) ||
+      OID_MATCHES(oid, kDSAWithSHA224)) {
+    *algorithm = DIGEST_SHA_224;
+    return true;
+  }
+  if (OID_MATCHES(oid, kECDSAWithSHA256) ||
+      OID_MATCHES(oid, kSHA256WithRSAEncryption) ||
+      OID_MATCHES(oid, kDSAWithSHA256)) {
+    *algorithm = DIGEST_SHA_256;
+    return true;
+  }
+  if (OID_MATCHES(oid, kECDSAWithSHA384) ||
+      OID_MATCHES(oid, kSHA384WithRSAEncryption)) {
+    *algorithm = DIGEST_SHA_384;
+    return true;
+  }
+  if (OID_MATCHES(oid, kECDSAWithSHA512) ||
+      OID_MATCHES(oid, kSHA512WithRSAEncryption)) {
+    *algorithm = DIGEST_SHA_512;
+    return true;
+  }
+  // Unknown algorithm.  There are several unhandled options that are less
+  // common and more complex.
+  RTC_LOG(LS_ERROR) << "Unknown signature algorithm.";
+  algorithm->clear();
+  return false;
+}
+
+bool BoringSSLCertificate::ComputeDigest(const std::string& algorithm,
+                                         unsigned char* digest,
+                                         size_t size,
+                                         size_t* length) const {
+  return ComputeDigest(cert_buffer_.get(), algorithm, digest, size, length);
+}
+
+bool BoringSSLCertificate::ComputeDigest(const CRYPTO_BUFFER* cert_buffer,
+                                         const std::string& algorithm,
+                                         unsigned char* digest,
+                                         size_t size,
+                                         size_t* length) {
+  const EVP_MD* md = nullptr;
+  unsigned int n = 0;
+  if (!OpenSSLDigest::GetDigestEVP(algorithm, &md)) {
+    return false;
+  }
+  if (size < static_cast<size_t>(EVP_MD_size(md))) {
+    return false;
+  }
+  if (!EVP_Digest(CRYPTO_BUFFER_data(cert_buffer),
+                  CRYPTO_BUFFER_len(cert_buffer), digest, &n, md, nullptr)) {
+    return false;
+  }
+  *length = n;
+  return true;
+}
+
+BoringSSLCertificate::~BoringSSLCertificate() {}
+
+std::unique_ptr<SSLCertificate> BoringSSLCertificate::Clone() const {
+  return std::make_unique<BoringSSLCertificate>(
+      bssl::UpRef(cert_buffer_.get()));
+}
+
+std::string BoringSSLCertificate::ToPEMString() const {
+  return OpenSSLIdentity::DerToPem(kPemTypeCertificate,
+                                   CRYPTO_BUFFER_data(cert_buffer_.get()),
+                                   CRYPTO_BUFFER_len(cert_buffer_.get()));
+}
+
+void BoringSSLCertificate::ToDER(Buffer* der_buffer) const {
+  der_buffer->SetData(CRYPTO_BUFFER_data(cert_buffer_.get()),
+                      CRYPTO_BUFFER_len(cert_buffer_.get()));
+}
+
+bool BoringSSLCertificate::operator==(const BoringSSLCertificate& other) const {
+  return CRYPTO_BUFFER_len(cert_buffer_.get()) ==
+             CRYPTO_BUFFER_len(other.cert_buffer_.get()) &&
+         0 == memcmp(CRYPTO_BUFFER_data(cert_buffer_.get()),
+                     CRYPTO_BUFFER_data(other.cert_buffer_.get()),
+                     CRYPTO_BUFFER_len(cert_buffer_.get()));
+}
+
+bool BoringSSLCertificate::operator!=(const BoringSSLCertificate& other) const {
+  return !(*this == other);
+}
+
+int64_t BoringSSLCertificate::CertificateExpirationTime() const {
+  int64_t ret;
+  if (!openssl::ParseCertificate(cert_buffer_.get(), nullptr, &ret)) {
+    RTC_LOG(LS_ERROR) << "Failed to parse certificate.";
+    return -1;
+  }
+  return ret;
+}
+
+}  // namespace rtc
diff --git a/rtc_base/boringssl_certificate.h b/rtc_base/boringssl_certificate.h
new file mode 100644
index 0000000..740763d
--- /dev/null
+++ b/rtc_base/boringssl_certificate.h
@@ -0,0 +1,80 @@
+/*
+ *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_BORINGSSL_CERTIFICATE_H_
+#define RTC_BASE_BORINGSSL_CERTIFICATE_H_
+
+#include <openssl/ossl_typ.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "rtc_base/buffer.h"
+#include "rtc_base/constructor_magic.h"
+#include "rtc_base/ssl_certificate.h"
+#include "rtc_base/ssl_identity.h"
+
+namespace rtc {
+
+class OpenSSLKeyPair;
+
+// BoringSSLCertificate encapsulates a BoringSSL CRYPTO_BUFFER object holding a
+// certificate, which is also reference counted inside the BoringSSL library.
+// This offers binary size and memory improvements over the OpenSSL X509
+// object.
+class BoringSSLCertificate final : public SSLCertificate {
+ public:
+  explicit BoringSSLCertificate(bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer);
+
+  static std::unique_ptr<BoringSSLCertificate> Generate(
+      OpenSSLKeyPair* key_pair,
+      const SSLIdentityParams& params);
+  static std::unique_ptr<BoringSSLCertificate> FromPEMString(
+      const std::string& pem_string);
+
+  ~BoringSSLCertificate() override;
+
+  std::unique_ptr<SSLCertificate> Clone() const override;
+
+  CRYPTO_BUFFER* cert_buffer() const { return cert_buffer_.get(); }
+
+  std::string ToPEMString() const override;
+  void ToDER(Buffer* der_buffer) const override;
+  bool operator==(const BoringSSLCertificate& other) const;
+  bool operator!=(const BoringSSLCertificate& other) const;
+
+  // Compute the digest of the certificate given |algorithm|.
+  bool ComputeDigest(const std::string& algorithm,
+                     unsigned char* digest,
+                     size_t size,
+                     size_t* length) const override;
+
+  // Compute the digest of a certificate as a CRYPTO_BUFFER.
+  static bool ComputeDigest(const CRYPTO_BUFFER* cert_buffer,
+                            const std::string& algorithm,
+                            unsigned char* digest,
+                            size_t size,
+                            size_t* length);
+
+  bool GetSignatureDigestAlgorithm(std::string* algorithm) const override;
+
+  int64_t CertificateExpirationTime() const override;
+
+ private:
+  // A handle to the DER encoded certificate data.
+  bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(BoringSSLCertificate);
+};
+
+}  // namespace rtc
+
+#endif  // RTC_BASE_BORINGSSL_CERTIFICATE_H_
diff --git a/rtc_base/boringssl_identity.cc b/rtc_base/boringssl_identity.cc
new file mode 100644
index 0000000..d22c8ce
--- /dev/null
+++ b/rtc_base/boringssl_identity.cc
@@ -0,0 +1,215 @@
+/*
+ *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/boringssl_identity.h"
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/pool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "absl/memory/memory.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/openssl.h"
+#include "rtc_base/openssl_utility.h"
+
+namespace rtc {
+
+BoringSSLIdentity::BoringSSLIdentity(
+    std::unique_ptr<OpenSSLKeyPair> key_pair,
+    std::unique_ptr<BoringSSLCertificate> certificate)
+    : key_pair_(std::move(key_pair)) {
+  RTC_DCHECK(key_pair_ != nullptr);
+  RTC_DCHECK(certificate != nullptr);
+  std::vector<std::unique_ptr<SSLCertificate>> certs;
+  certs.push_back(std::move(certificate));
+  cert_chain_.reset(new SSLCertChain(std::move(certs)));
+}
+
+BoringSSLIdentity::BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
+                                     std::unique_ptr<SSLCertChain> cert_chain)
+    : key_pair_(std::move(key_pair)), cert_chain_(std::move(cert_chain)) {
+  RTC_DCHECK(key_pair_ != nullptr);
+  RTC_DCHECK(cert_chain_ != nullptr);
+}
+
+BoringSSLIdentity::~BoringSSLIdentity() = default;
+
+std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateInternal(
+    const SSLIdentityParams& params) {
+  auto key_pair = OpenSSLKeyPair::Generate(params.key_params);
+  if (key_pair) {
+    std::unique_ptr<BoringSSLCertificate> certificate(
+        BoringSSLCertificate::Generate(key_pair.get(), params));
+    if (certificate != nullptr) {
+      return absl::WrapUnique(
+          new BoringSSLIdentity(std::move(key_pair), std::move(certificate)));
+    }
+  }
+  RTC_LOG(LS_ERROR) << "Identity generation failed.";
+  return nullptr;
+}
+
+// static
+std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateWithExpiration(
+    const std::string& common_name,
+    const KeyParams& key_params,
+    time_t certificate_lifetime) {
+  SSLIdentityParams params;
+  params.key_params = key_params;
+  params.common_name = common_name;
+  time_t now = time(nullptr);
+  params.not_before = now + kCertificateWindowInSeconds;
+  params.not_after = now + certificate_lifetime;
+  if (params.not_before > params.not_after)
+    return nullptr;
+  return CreateInternal(params);
+}
+
+std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateForTest(
+    const SSLIdentityParams& params) {
+  return CreateInternal(params);
+}
+
+std::unique_ptr<SSLIdentity> BoringSSLIdentity::CreateFromPEMStrings(
+    const std::string& private_key,
+    const std::string& certificate) {
+  std::unique_ptr<BoringSSLCertificate> cert(
+      BoringSSLCertificate::FromPEMString(certificate));
+  if (!cert) {
+    RTC_LOG(LS_ERROR)
+        << "Failed to create BoringSSLCertificate from PEM string.";
+    return nullptr;
+  }
+
+  auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
+  if (!key_pair) {
+    RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
+    return nullptr;
+  }
+
+  return absl::WrapUnique(
+      new BoringSSLIdentity(std::move(key_pair), std::move(cert)));
+}
+
+std::unique_ptr<SSLIdentity> BoringSSLIdentity::CreateFromPEMChainStrings(
+    const std::string& private_key,
+    const std::string& certificate_chain) {
+  bssl::UniquePtr<BIO> bio(
+      BIO_new_mem_buf(certificate_chain.data(),
+                      rtc::dchecked_cast<int>(certificate_chain.size())));
+  if (!bio) {
+    return nullptr;
+  }
+  BIO_set_mem_eof_return(bio.get(), 0);
+  std::vector<std::unique_ptr<SSLCertificate>> certs;
+  while (true) {
+    char* name;
+    char* header;
+    unsigned char* data;
+    long len;  // NOLINT
+    int ret = PEM_read_bio(bio.get(), &name, &header, &data, &len);
+    if (ret == 0) {
+      uint32_t err = ERR_peek_error();
+      if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
+          ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
+        break;
+      }
+      RTC_LOG(LS_ERROR) << "Failed to parse certificate from PEM string.";
+      return nullptr;
+    }
+    bssl::UniquePtr<char> owned_name(name);
+    bssl::UniquePtr<char> owned_header(header);
+    bssl::UniquePtr<unsigned char> owned_data(data);
+    if (strcmp(owned_name.get(), PEM_STRING_X509) != 0) {
+      RTC_LOG(LS_ERROR)
+          << "Non-certificate found while parsing certificate chain: "
+          << owned_name.get();
+      return nullptr;
+    }
+    bssl::UniquePtr<CRYPTO_BUFFER> crypto_buffer(
+        CRYPTO_BUFFER_new(data, len, openssl::GetBufferPool()));
+    if (!crypto_buffer) {
+      return nullptr;
+    }
+    certs.emplace_back(new BoringSSLCertificate(std::move(crypto_buffer)));
+  }
+  if (certs.empty()) {
+    RTC_LOG(LS_ERROR) << "Found no certificates in PEM string.";
+    return nullptr;
+  }
+
+  auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
+  if (!key_pair) {
+    RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
+    return nullptr;
+  }
+
+  return absl::WrapUnique(new BoringSSLIdentity(
+      std::move(key_pair), std::make_unique<SSLCertChain>(std::move(certs))));
+}
+
+const BoringSSLCertificate& BoringSSLIdentity::certificate() const {
+  return *static_cast<const BoringSSLCertificate*>(&cert_chain_->Get(0));
+}
+
+const SSLCertChain& BoringSSLIdentity::cert_chain() const {
+  return *cert_chain_.get();
+}
+
+std::unique_ptr<SSLIdentity> BoringSSLIdentity::CloneInternal() const {
+  // We cannot use std::make_unique here because the referenced
+  // BoringSSLIdentity constructor is private.
+  return absl::WrapUnique(
+      new BoringSSLIdentity(key_pair_->Clone(), cert_chain_->Clone()));
+}
+
+bool BoringSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
+  std::vector<CRYPTO_BUFFER*> cert_buffers;
+  for (size_t i = 0; i < cert_chain_->GetSize(); ++i) {
+    cert_buffers.push_back(
+        static_cast<const BoringSSLCertificate*>(&cert_chain_->Get(i))
+            ->cert_buffer());
+  }
+  // 1 is the documented success return code.
+  if (1 != SSL_CTX_set_chain_and_key(ctx, &cert_buffers[0], cert_buffers.size(),
+                                     key_pair_->pkey(), nullptr)) {
+    openssl::LogSSLErrors("Configuring key and certificate");
+    return false;
+  }
+  return true;
+}
+
+std::string BoringSSLIdentity::PrivateKeyToPEMString() const {
+  return key_pair_->PrivateKeyToPEMString();
+}
+
+std::string BoringSSLIdentity::PublicKeyToPEMString() const {
+  return key_pair_->PublicKeyToPEMString();
+}
+
+bool BoringSSLIdentity::operator==(const BoringSSLIdentity& other) const {
+  return *this->key_pair_ == *other.key_pair_ &&
+         this->certificate() == other.certificate();
+}
+
+bool BoringSSLIdentity::operator!=(const BoringSSLIdentity& other) const {
+  return !(*this == other);
+}
+
+}  // namespace rtc
diff --git a/rtc_base/boringssl_identity.h b/rtc_base/boringssl_identity.h
new file mode 100644
index 0000000..71b29b4
--- /dev/null
+++ b/rtc_base/boringssl_identity.h
@@ -0,0 +1,76 @@
+/*
+ *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_BORINGSSL_IDENTITY_H_
+#define RTC_BASE_BORINGSSL_IDENTITY_H_
+
+#include <openssl/ossl_typ.h>
+
+#include <ctime>
+#include <memory>
+#include <string>
+
+#include "rtc_base/boringssl_certificate.h"
+#include "rtc_base/constructor_magic.h"
+#include "rtc_base/openssl_key_pair.h"
+#include "rtc_base/ssl_certificate.h"
+#include "rtc_base/ssl_identity.h"
+
+namespace rtc {
+
+// Holds a keypair and certificate together, and a method to generate them
+// consistently. Uses CRYPTO_BUFFER instead of X509, which offers binary size
+// and memory improvements.
+class BoringSSLIdentity final : public SSLIdentity {
+ public:
+  static std::unique_ptr<BoringSSLIdentity> CreateWithExpiration(
+      const std::string& common_name,
+      const KeyParams& key_params,
+      time_t certificate_lifetime);
+  static std::unique_ptr<BoringSSLIdentity> CreateForTest(
+      const SSLIdentityParams& params);
+  static std::unique_ptr<SSLIdentity> CreateFromPEMStrings(
+      const std::string& private_key,
+      const std::string& certificate);
+  static std::unique_ptr<SSLIdentity> CreateFromPEMChainStrings(
+      const std::string& private_key,
+      const std::string& certificate_chain);
+  ~BoringSSLIdentity() override;
+
+  const BoringSSLCertificate& certificate() const override;
+  const SSLCertChain& cert_chain() const override;
+
+  // Configure an SSL context object to use our key and certificate.
+  bool ConfigureIdentity(SSL_CTX* ctx);
+
+  std::string PrivateKeyToPEMString() const override;
+  std::string PublicKeyToPEMString() const override;
+  bool operator==(const BoringSSLIdentity& other) const;
+  bool operator!=(const BoringSSLIdentity& other) const;
+
+ private:
+  BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
+                    std::unique_ptr<BoringSSLCertificate> certificate);
+  BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
+                    std::unique_ptr<SSLCertChain> cert_chain);
+  std::unique_ptr<SSLIdentity> CloneInternal() const override;
+
+  static std::unique_ptr<BoringSSLIdentity> CreateInternal(
+      const SSLIdentityParams& params);
+
+  std::unique_ptr<OpenSSLKeyPair> key_pair_;
+  std::unique_ptr<SSLCertChain> cert_chain_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(BoringSSLIdentity);
+};
+
+}  // namespace rtc
+
+#endif  // RTC_BASE_BORINGSSL_IDENTITY_H_
diff --git a/rtc_base/openssl_adapter.cc b/rtc_base/openssl_adapter.cc
index 8fd882c..e5c2c42 100644
--- a/rtc_base/openssl_adapter.cc
+++ b/rtc_base/openssl_adapter.cc
@@ -13,6 +13,9 @@
 #include <errno.h>
 #include <openssl/bio.h>
 #include <openssl/err.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/pool.h>
+#endif
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 #include <string.h>
@@ -20,13 +23,24 @@
 
 #include <memory>
 
+// Use CRYPTO_BUFFER APIs if available and we have no dependency on X509
+// objects.
+#if defined(OPENSSL_IS_BORINGSSL) && \
+    defined(WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS)
+#define WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
+#endif
+
 #include "absl/memory/memory.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/location.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/numerics/safe_conversions.h"
 #include "rtc_base/openssl.h"
-#include "rtc_base/openssl_certificate.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
+#include "rtc_base/openssl_identity.h"
+#endif
 #include "rtc_base/openssl_utility.h"
 #include "rtc_base/string_encode.h"
 #include "rtc_base/thread.h"
@@ -223,8 +237,13 @@
 
 void OpenSSLAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
   RTC_DCHECK(!identity_);
+#ifdef OPENSSL_IS_BORINGSSL
+  identity_ =
+      absl::WrapUnique(static_cast<BoringSSLIdentity*>(identity.release()));
+#else
   identity_ =
       absl::WrapUnique(static_cast<OpenSSLIdentity*>(identity.release()));
+#endif
 }
 
 void OpenSSLAdapter::SetRole(SSLRole role) {
@@ -797,7 +816,70 @@
 
 #endif
 
+#ifdef WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
+// static
+enum ssl_verify_result_t OpenSSLAdapter::SSLVerifyCallback(SSL* ssl,
+                                                           uint8_t* out_alert) {
+  // Get our stream pointer from the SSL context.
+  OpenSSLAdapter* stream =
+      reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
+
+  ssl_verify_result_t ret = stream->SSLVerifyInternal(ssl, out_alert);
+
+  // Should only be used for debugging and development.
+  if (ret != ssl_verify_ok && stream->ignore_bad_cert_) {
+    RTC_DLOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
+    return ssl_verify_ok;
+  }
+
+  return ret;
+}
+
+enum ssl_verify_result_t OpenSSLAdapter::SSLVerifyInternal(SSL* ssl,
+                                                           uint8_t* out_alert) {
+  if (ssl_cert_verifier_ == nullptr) {
+    RTC_LOG(LS_WARNING) << "Built-in trusted root certificates disabled but no "
+                           "SSL verify callback provided.";
+    return ssl_verify_invalid;
+  }
+
+  RTC_LOG(LS_INFO) << "Invoking SSL Verify Callback.";
+  const STACK_OF(CRYPTO_BUFFER)* chain = SSL_get0_peer_certificates(ssl);
+  if (sk_CRYPTO_BUFFER_num(chain) == 0) {
+    RTC_LOG(LS_ERROR) << "Peer certificate chain empty?";
+    return ssl_verify_invalid;
+  }
+
+  BoringSSLCertificate cert(bssl::UpRef(sk_CRYPTO_BUFFER_value(chain, 0)));
+  if (!ssl_cert_verifier_->Verify(cert)) {
+    RTC_LOG(LS_WARNING) << "Failed to verify certificate using custom callback";
+    return ssl_verify_invalid;
+  }
+
+  custom_cert_verifier_status_ = true;
+  RTC_LOG(LS_INFO) << "Validated certificate using custom callback";
+  return ssl_verify_ok;
+}
+#else  // WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
 int OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
+  // Get our stream pointer from the store
+  SSL* ssl = reinterpret_cast<SSL*>(
+      X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()));
+
+  OpenSSLAdapter* stream =
+      reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
+  ok = stream->SSLVerifyInternal(ok, ssl, store);
+
+  // Should only be used for debugging and development.
+  if (!ok && stream->ignore_bad_cert_) {
+    RTC_DLOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
+    return 1;
+  }
+
+  return ok;
+}
+
+int OpenSSLAdapter::SSLVerifyInternal(int ok, SSL* ssl, X509_STORE_CTX* store) {
 #if !defined(NDEBUG)
   if (!ok) {
     char data[256];
@@ -814,33 +896,40 @@
                       << X509_verify_cert_error_string(err);
   }
 #endif
-  // Get our stream pointer from the store
-  SSL* ssl = reinterpret_cast<SSL*>(
-      X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()));
-
-  OpenSSLAdapter* stream =
-      reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
-
-  if (!ok && stream->ssl_cert_verifier_ != nullptr) {
-    RTC_LOG(LS_INFO) << "Invoking SSL Verify Callback.";
-    const OpenSSLCertificate cert(X509_STORE_CTX_get_current_cert(store));
-    if (stream->ssl_cert_verifier_->Verify(cert)) {
-      stream->custom_cert_verifier_status_ = true;
-      RTC_LOG(LS_INFO) << "Validated certificate using custom callback";
-      ok = true;
-    } else {
-      RTC_LOG(LS_INFO) << "Failed to verify certificate using custom callback";
-    }
+  if (ssl_cert_verifier_ == nullptr) {
+    return ok;
   }
 
-  // Should only be used for debugging and development.
-  if (!ok && stream->ignore_bad_cert_) {
-    RTC_DLOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
-    ok = 1;
+  RTC_LOG(LS_INFO) << "Invoking SSL Verify Callback.";
+#ifdef OPENSSL_IS_BORINGSSL
+  // Convert X509 to CRYPTO_BUFFER.
+  uint8_t* data = nullptr;
+  int length = i2d_X509(X509_STORE_CTX_get_current_cert(store), &data);
+  if (length < 0) {
+    RTC_LOG(LS_ERROR) << "Failed to encode X509.";
+    return ok;
+  }
+  bssl::UniquePtr<uint8_t> owned_data(data);
+  bssl::UniquePtr<CRYPTO_BUFFER> crypto_buffer(
+      CRYPTO_BUFFER_new(data, length, openssl::GetBufferPool()));
+  if (!crypto_buffer) {
+    RTC_LOG(LS_ERROR) << "Failed to allocate CRYPTO_BUFFER.";
+    return ok;
+  }
+  const BoringSSLCertificate cert(std::move(crypto_buffer));
+#else
+  const OpenSSLCertificate cert(X509_STORE_CTX_get_current_cert(store));
+#endif
+  if (!ssl_cert_verifier_->Verify(cert)) {
+    RTC_LOG(LS_INFO) << "Failed to verify certificate using custom callback";
+    return ok;
   }
 
-  return ok;
+  custom_cert_verifier_status_ = true;
+  RTC_LOG(LS_INFO) << "Validated certificate using custom callback";
+  return 1;
 }
+#endif  // !defined(WEBRTC_USE_CRYPTO_BUFFER_CALLBACK)
 
 int OpenSSLAdapter::NewSSLSessionCallback(SSL* ssl, SSL_SESSION* session) {
   OpenSSLAdapter* stream =
@@ -852,8 +941,15 @@
 }
 
 SSL_CTX* OpenSSLAdapter::CreateContext(SSLMode mode, bool enable_cache) {
+#ifdef WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
+  // If X509 objects aren't used, we can use these methods to avoid
+  // linking the sizable crypto/x509 code.
+  SSL_CTX* ctx = SSL_CTX_new(mode == SSL_MODE_DTLS ? DTLS_with_buffers_method()
+                                                   : TLS_with_buffers_method());
+#else
   SSL_CTX* ctx =
       SSL_CTX_new(mode == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
+#endif
   if (ctx == nullptr) {
     unsigned long error = ERR_get_error();  // NOLINT: type used by OpenSSL.
     RTC_LOG(LS_WARNING) << "SSL_CTX creation failed: " << '"'
@@ -877,8 +973,16 @@
   SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
 #endif
 
+#ifdef OPENSSL_IS_BORINGSSL
+  SSL_CTX_set0_buffer_pool(ctx, openssl::GetBufferPool());
+#endif
+
+#ifdef WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
+  SSL_CTX_set_custom_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
+#else
   SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
   SSL_CTX_set_verify_depth(ctx, 4);
+#endif
   // Use defaults, but disable HMAC-SHA256 and HMAC-SHA384 ciphers
   // (note that SHA256 and SHA384 only select legacy CBC ciphers).
   // Additionally disable HMAC-SHA1 ciphers in ECDSA. These are the remaining
diff --git a/rtc_base/openssl_adapter.h b/rtc_base/openssl_adapter.h
index 6f1f7dc..76b003a 100644
--- a/rtc_base/openssl_adapter.h
+++ b/rtc_base/openssl_adapter.h
@@ -11,6 +11,7 @@
 #ifndef RTC_BASE_OPENSSL_ADAPTER_H_
 #define RTC_BASE_OPENSSL_ADAPTER_H_
 
+#include <openssl/ossl_typ.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -21,7 +22,11 @@
 #include "rtc_base/async_socket.h"
 #include "rtc_base/buffer.h"
 #include "rtc_base/message_handler.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
 #include "rtc_base/openssl_identity.h"
+#endif
 #include "rtc_base/openssl_session_cache.h"
 #include "rtc_base/socket.h"
 #include "rtc_base/socket_address.h"
@@ -109,7 +114,16 @@
   // In debug builds, logs info about the state of the SSL connection.
   static void SSLInfoCallback(const SSL* ssl, int where, int ret);
 #endif
+
+#if defined(OPENSSL_IS_BORINGSSL) && \
+    defined(WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS)
+  static enum ssl_verify_result_t SSLVerifyCallback(SSL* ssl,
+                                                    uint8_t* out_alert);
+  enum ssl_verify_result_t SSLVerifyInternal(SSL* ssl, uint8_t* out_alert);
+#else
   static int SSLVerifyCallback(int ok, X509_STORE_CTX* store);
+  int SSLVerifyInternal(int ok, SSL* ssl, X509_STORE_CTX* store);
+#endif
   friend class OpenSSLStreamAdapter;  // for custom_verify_callback_;
 
   // If the SSL_CTX was created with |enable_cache| set to true, this callback
@@ -123,7 +137,12 @@
   SSLCertificateVerifier* ssl_cert_verifier_ = nullptr;
   // The current connection state of the (d)TLS connection.
   SSLState state_;
+
+#ifdef OPENSSL_IS_BORINGSSL
+  std::unique_ptr<BoringSSLIdentity> identity_;
+#else
   std::unique_ptr<OpenSSLIdentity> identity_;
+#endif
   // Indicates whethere this is a client or a server.
   SSLRole role_;
   bool ssl_read_needs_write_;
diff --git a/rtc_base/openssl_identity.cc b/rtc_base/openssl_identity.cc
index c94df40..3794d98 100644
--- a/rtc_base/openssl_identity.cc
+++ b/rtc_base/openssl_identity.cc
@@ -20,10 +20,8 @@
 #endif                       // WEBRTC_WIN
 
 #include <openssl/bio.h>
-#include <openssl/bn.h>
 #include <openssl/err.h>
 #include <openssl/pem.h>
-#include <openssl/rsa.h>
 #include <stdint.h>
 
 #include "absl/memory/memory.h"
@@ -35,160 +33,6 @@
 
 namespace rtc {
 
-// We could have exposed a myriad of parameters for the crypto stuff,
-// but keeping it simple seems best.
-
-// Generate a key pair. Caller is responsible for freeing the returned object.
-static EVP_PKEY* MakeKey(const KeyParams& key_params) {
-  RTC_LOG(LS_INFO) << "Making key pair";
-  EVP_PKEY* pkey = EVP_PKEY_new();
-  if (key_params.type() == KT_RSA) {
-    int key_length = key_params.rsa_params().mod_size;
-    BIGNUM* exponent = BN_new();
-    RSA* rsa = RSA_new();
-    if (!pkey || !exponent || !rsa ||
-        !BN_set_word(exponent, key_params.rsa_params().pub_exp) ||
-        !RSA_generate_key_ex(rsa, key_length, exponent, nullptr) ||
-        !EVP_PKEY_assign_RSA(pkey, rsa)) {
-      EVP_PKEY_free(pkey);
-      BN_free(exponent);
-      RSA_free(rsa);
-      RTC_LOG(LS_ERROR) << "Failed to make RSA key pair";
-      return nullptr;
-    }
-    // ownership of rsa struct was assigned, don't free it.
-    BN_free(exponent);
-  } else if (key_params.type() == KT_ECDSA) {
-    if (key_params.ec_curve() == EC_NIST_P256) {
-      EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
-
-      // Ensure curve name is included when EC key is serialized.
-      // Without this call, OpenSSL versions before 1.1.0 will create
-      // certificates that don't work for TLS.
-      // This is a no-op for BoringSSL and OpenSSL 1.1.0+
-      EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);
-
-      if (!pkey || !ec_key || !EC_KEY_generate_key(ec_key) ||
-          !EVP_PKEY_assign_EC_KEY(pkey, ec_key)) {
-        EVP_PKEY_free(pkey);
-        EC_KEY_free(ec_key);
-        RTC_LOG(LS_ERROR) << "Failed to make EC key pair";
-        return nullptr;
-      }
-      // ownership of ec_key struct was assigned, don't free it.
-    } else {
-      // Add generation of any other curves here.
-      EVP_PKEY_free(pkey);
-      RTC_LOG(LS_ERROR) << "ECDSA key requested for unknown curve";
-      return nullptr;
-    }
-  } else {
-    EVP_PKEY_free(pkey);
-    RTC_LOG(LS_ERROR) << "Key type requested not understood";
-    return nullptr;
-  }
-
-  RTC_LOG(LS_INFO) << "Returning key pair";
-  return pkey;
-}
-
-OpenSSLKeyPair* OpenSSLKeyPair::Generate(const KeyParams& key_params) {
-  EVP_PKEY* pkey = MakeKey(key_params);
-  if (!pkey) {
-    openssl::LogSSLErrors("Generating key pair");
-    return nullptr;
-  }
-  return new OpenSSLKeyPair(pkey);
-}
-
-OpenSSLKeyPair* OpenSSLKeyPair::FromPrivateKeyPEMString(
-    const std::string& pem_string) {
-  BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
-  if (!bio) {
-    RTC_LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
-    return nullptr;
-  }
-  BIO_set_mem_eof_return(bio, 0);
-  EVP_PKEY* pkey =
-      PEM_read_bio_PrivateKey(bio, nullptr, nullptr, const_cast<char*>("\0"));
-  BIO_free(bio);  // Frees the BIO, but not the pointed-to string.
-  if (!pkey) {
-    RTC_LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
-    return nullptr;
-  }
-  if (EVP_PKEY_missing_parameters(pkey) != 0) {
-    RTC_LOG(LS_ERROR)
-        << "The resulting key pair is missing public key parameters.";
-    EVP_PKEY_free(pkey);
-    return nullptr;
-  }
-  return new OpenSSLKeyPair(pkey);
-}
-
-OpenSSLKeyPair::~OpenSSLKeyPair() {
-  EVP_PKEY_free(pkey_);
-}
-
-OpenSSLKeyPair* OpenSSLKeyPair::GetReference() {
-  AddReference();
-  return new OpenSSLKeyPair(pkey_);
-}
-
-void OpenSSLKeyPair::AddReference() {
-  EVP_PKEY_up_ref(pkey_);
-}
-
-std::string OpenSSLKeyPair::PrivateKeyToPEMString() const {
-  BIO* temp_memory_bio = BIO_new(BIO_s_mem());
-  if (!temp_memory_bio) {
-    RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
-    RTC_NOTREACHED();
-    return "";
-  }
-  if (!PEM_write_bio_PrivateKey(temp_memory_bio, pkey_, nullptr, nullptr, 0,
-                                nullptr, nullptr)) {
-    RTC_LOG_F(LS_ERROR) << "Failed to write private key";
-    BIO_free(temp_memory_bio);
-    RTC_NOTREACHED();
-    return "";
-  }
-  BIO_write(temp_memory_bio, "\0", 1);
-  char* buffer;
-  BIO_get_mem_data(temp_memory_bio, &buffer);
-  std::string priv_key_str = buffer;
-  BIO_free(temp_memory_bio);
-  return priv_key_str;
-}
-
-std::string OpenSSLKeyPair::PublicKeyToPEMString() const {
-  BIO* temp_memory_bio = BIO_new(BIO_s_mem());
-  if (!temp_memory_bio) {
-    RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
-    RTC_NOTREACHED();
-    return "";
-  }
-  if (!PEM_write_bio_PUBKEY(temp_memory_bio, pkey_)) {
-    RTC_LOG_F(LS_ERROR) << "Failed to write public key";
-    BIO_free(temp_memory_bio);
-    RTC_NOTREACHED();
-    return "";
-  }
-  BIO_write(temp_memory_bio, "\0", 1);
-  char* buffer;
-  BIO_get_mem_data(temp_memory_bio, &buffer);
-  std::string pub_key_str = buffer;
-  BIO_free(temp_memory_bio);
-  return pub_key_str;
-}
-
-bool OpenSSLKeyPair::operator==(const OpenSSLKeyPair& other) const {
-  return EVP_PKEY_cmp(this->pkey_, other.pkey_) == 1;
-}
-
-bool OpenSSLKeyPair::operator!=(const OpenSSLKeyPair& other) const {
-  return !(*this == other);
-}
-
 OpenSSLIdentity::OpenSSLIdentity(
     std::unique_ptr<OpenSSLKeyPair> key_pair,
     std::unique_ptr<OpenSSLCertificate> certificate)
@@ -211,8 +55,7 @@
 
 std::unique_ptr<OpenSSLIdentity> OpenSSLIdentity::CreateInternal(
     const SSLIdentityParams& params) {
-  std::unique_ptr<OpenSSLKeyPair> key_pair(
-      OpenSSLKeyPair::Generate(params.key_params));
+  auto key_pair = OpenSSLKeyPair::Generate(params.key_params);
   if (key_pair) {
     std::unique_ptr<OpenSSLCertificate> certificate(
         OpenSSLCertificate::Generate(key_pair.get(), params));
@@ -221,7 +64,7 @@
           new OpenSSLIdentity(std::move(key_pair), std::move(certificate)));
     }
   }
-  RTC_LOG(LS_INFO) << "Identity generation failed";
+  RTC_LOG(LS_ERROR) << "Identity generation failed";
   return nullptr;
 }
 
@@ -256,8 +99,7 @@
     return nullptr;
   }
 
-  std::unique_ptr<OpenSSLKeyPair> key_pair(
-      OpenSSLKeyPair::FromPrivateKeyPEMString(private_key));
+  auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
   if (!key_pair) {
     RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
     return nullptr;
@@ -298,8 +140,7 @@
     return nullptr;
   }
 
-  std::unique_ptr<OpenSSLKeyPair> key_pair(
-      OpenSSLKeyPair::FromPrivateKeyPEMString(private_key));
+  auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
   if (!key_pair) {
     RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
     return nullptr;
@@ -320,8 +161,8 @@
 std::unique_ptr<SSLIdentity> OpenSSLIdentity::CloneInternal() const {
   // We cannot use std::make_unique here because the referenced OpenSSLIdentity
   // constructor is private.
-  return absl::WrapUnique(new OpenSSLIdentity(
-      absl::WrapUnique(key_pair_->GetReference()), cert_chain_->Clone()));
+  return absl::WrapUnique(
+      new OpenSSLIdentity(key_pair_->Clone(), cert_chain_->Clone()));
 }
 
 bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
diff --git a/rtc_base/openssl_identity.h b/rtc_base/openssl_identity.h
index a2ac87c..00d6c74 100644
--- a/rtc_base/openssl_identity.h
+++ b/rtc_base/openssl_identity.h
@@ -17,45 +17,14 @@
 #include <memory>
 #include <string>
 
-#include "rtc_base/checks.h"
 #include "rtc_base/constructor_magic.h"
 #include "rtc_base/openssl_certificate.h"
+#include "rtc_base/openssl_key_pair.h"
 #include "rtc_base/ssl_certificate.h"
 #include "rtc_base/ssl_identity.h"
 
 namespace rtc {
 
-// OpenSSLKeyPair encapsulates an OpenSSL EVP_PKEY* keypair object,
-// which is reference counted inside the OpenSSL library.
-class OpenSSLKeyPair final {
- public:
-  explicit OpenSSLKeyPair(EVP_PKEY* pkey) : pkey_(pkey) {
-    RTC_DCHECK(pkey_ != nullptr);
-  }
-
-  static OpenSSLKeyPair* Generate(const KeyParams& key_params);
-  // Constructs a key pair from the private key PEM string. This must not result
-  // in missing public key parameters. Returns null on error.
-  static OpenSSLKeyPair* FromPrivateKeyPEMString(const std::string& pem_string);
-
-  virtual ~OpenSSLKeyPair();
-
-  virtual OpenSSLKeyPair* GetReference();
-
-  EVP_PKEY* pkey() const { return pkey_; }
-  std::string PrivateKeyToPEMString() const;
-  std::string PublicKeyToPEMString() const;
-  bool operator==(const OpenSSLKeyPair& other) const;
-  bool operator!=(const OpenSSLKeyPair& other) const;
-
- private:
-  void AddReference();
-
-  EVP_PKEY* pkey_;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLKeyPair);
-};
-
 // Holds a keypair and certificate together, and a method to generate
 // them consistently.
 class OpenSSLIdentity final : public SSLIdentity {
diff --git a/rtc_base/openssl_key_pair.cc b/rtc_base/openssl_key_pair.cc
new file mode 100644
index 0000000..911a751
--- /dev/null
+++ b/rtc_base/openssl_key_pair.cc
@@ -0,0 +1,192 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/openssl_key_pair.h"
+
+#include <memory>
+#include <utility>
+
+#if defined(WEBRTC_WIN)
+// Must be included first before openssl headers.
+#include "rtc_base/win32.h"  // NOLINT
+#endif                       // WEBRTC_WIN
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/openssl.h"
+#include "rtc_base/openssl_utility.h"
+
+namespace rtc {
+
+// We could have exposed a myriad of parameters for the crypto stuff,
+// but keeping it simple seems best.
+
+// Generate a key pair. Caller is responsible for freeing the returned object.
+static EVP_PKEY* MakeKey(const KeyParams& key_params) {
+  RTC_LOG(LS_INFO) << "Making key pair";
+  EVP_PKEY* pkey = EVP_PKEY_new();
+  if (key_params.type() == KT_RSA) {
+    int key_length = key_params.rsa_params().mod_size;
+    BIGNUM* exponent = BN_new();
+    RSA* rsa = RSA_new();
+    if (!pkey || !exponent || !rsa ||
+        !BN_set_word(exponent, key_params.rsa_params().pub_exp) ||
+        !RSA_generate_key_ex(rsa, key_length, exponent, nullptr) ||
+        !EVP_PKEY_assign_RSA(pkey, rsa)) {
+      EVP_PKEY_free(pkey);
+      BN_free(exponent);
+      RSA_free(rsa);
+      RTC_LOG(LS_ERROR) << "Failed to make RSA key pair";
+      return nullptr;
+    }
+    // ownership of rsa struct was assigned, don't free it.
+    BN_free(exponent);
+  } else if (key_params.type() == KT_ECDSA) {
+    if (key_params.ec_curve() == EC_NIST_P256) {
+      EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+      if (!ec_key) {
+        EVP_PKEY_free(pkey);
+        RTC_LOG(LS_ERROR) << "Failed to allocate EC key";
+        return nullptr;
+      }
+
+      // Ensure curve name is included when EC key is serialized.
+      // Without this call, OpenSSL versions before 1.1.0 will create
+      // certificates that don't work for TLS.
+      // This is a no-op for BoringSSL and OpenSSL 1.1.0+
+      EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);
+
+      if (!pkey || !ec_key || !EC_KEY_generate_key(ec_key) ||
+          !EVP_PKEY_assign_EC_KEY(pkey, ec_key)) {
+        EVP_PKEY_free(pkey);
+        EC_KEY_free(ec_key);
+        RTC_LOG(LS_ERROR) << "Failed to make EC key pair";
+        return nullptr;
+      }
+      // ownership of ec_key struct was assigned, don't free it.
+    } else {
+      // Add generation of any other curves here.
+      EVP_PKEY_free(pkey);
+      RTC_LOG(LS_ERROR) << "ECDSA key requested for unknown curve";
+      return nullptr;
+    }
+  } else {
+    EVP_PKEY_free(pkey);
+    RTC_LOG(LS_ERROR) << "Key type requested not understood";
+    return nullptr;
+  }
+
+  RTC_LOG(LS_INFO) << "Returning key pair";
+  return pkey;
+}
+
+std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::Generate(
+    const KeyParams& key_params) {
+  EVP_PKEY* pkey = MakeKey(key_params);
+  if (!pkey) {
+    openssl::LogSSLErrors("Generating key pair");
+    return nullptr;
+  }
+  return std::make_unique<OpenSSLKeyPair>(pkey);
+}
+
+std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::FromPrivateKeyPEMString(
+    const std::string& pem_string) {
+  BIO* bio =
+      BIO_new_mem_buf(const_cast<char*>(pem_string.data()), pem_string.size());
+  if (!bio) {
+    RTC_LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
+    return nullptr;
+  }
+  BIO_set_mem_eof_return(bio, 0);
+  EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
+  BIO_free(bio);  // Frees the BIO, but not the pointed-to string.
+  if (!pkey) {
+    RTC_LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
+    return nullptr;
+  }
+  if (EVP_PKEY_missing_parameters(pkey) != 0) {
+    RTC_LOG(LS_ERROR)
+        << "The resulting key pair is missing public key parameters.";
+    EVP_PKEY_free(pkey);
+    return nullptr;
+  }
+  return std::make_unique<OpenSSLKeyPair>(pkey);
+}
+
+OpenSSLKeyPair::~OpenSSLKeyPair() {
+  EVP_PKEY_free(pkey_);
+}
+
+std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::Clone() {
+  AddReference();
+  return std::make_unique<OpenSSLKeyPair>(pkey_);
+}
+
+void OpenSSLKeyPair::AddReference() {
+  EVP_PKEY_up_ref(pkey_);
+}
+
+std::string OpenSSLKeyPair::PrivateKeyToPEMString() const {
+  BIO* temp_memory_bio = BIO_new(BIO_s_mem());
+  if (!temp_memory_bio) {
+    RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
+    RTC_NOTREACHED();
+    return "";
+  }
+  if (!PEM_write_bio_PrivateKey(temp_memory_bio, pkey_, nullptr, nullptr, 0,
+                                nullptr, nullptr)) {
+    RTC_LOG_F(LS_ERROR) << "Failed to write private key";
+    BIO_free(temp_memory_bio);
+    RTC_NOTREACHED();
+    return "";
+  }
+  char* buffer;
+  size_t len = BIO_get_mem_data(temp_memory_bio, &buffer);
+  std::string priv_key_str(buffer, len);
+  BIO_free(temp_memory_bio);
+  return priv_key_str;
+}
+
+std::string OpenSSLKeyPair::PublicKeyToPEMString() const {
+  BIO* temp_memory_bio = BIO_new(BIO_s_mem());
+  if (!temp_memory_bio) {
+    RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
+    RTC_NOTREACHED();
+    return "";
+  }
+  if (!PEM_write_bio_PUBKEY(temp_memory_bio, pkey_)) {
+    RTC_LOG_F(LS_ERROR) << "Failed to write public key";
+    BIO_free(temp_memory_bio);
+    RTC_NOTREACHED();
+    return "";
+  }
+  BIO_write(temp_memory_bio, "\0", 1);
+  char* buffer;
+  BIO_get_mem_data(temp_memory_bio, &buffer);
+  std::string pub_key_str = buffer;
+  BIO_free(temp_memory_bio);
+  return pub_key_str;
+}
+
+bool OpenSSLKeyPair::operator==(const OpenSSLKeyPair& other) const {
+  return EVP_PKEY_cmp(this->pkey_, other.pkey_) == 1;
+}
+
+bool OpenSSLKeyPair::operator!=(const OpenSSLKeyPair& other) const {
+  return !(*this == other);
+}
+
+}  // namespace rtc
diff --git a/rtc_base/openssl_key_pair.h b/rtc_base/openssl_key_pair.h
new file mode 100644
index 0000000..a84c43b
--- /dev/null
+++ b/rtc_base/openssl_key_pair.h
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_OPENSSL_KEY_PAIR_H_
+#define RTC_BASE_OPENSSL_KEY_PAIR_H_
+
+#include <openssl/ossl_typ.h>
+
+#include <memory>
+#include <string>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/constructor_magic.h"
+#include "rtc_base/ssl_identity.h"
+
+namespace rtc {
+
+// OpenSSLKeyPair encapsulates an OpenSSL EVP_PKEY* keypair object,
+// which is reference counted inside the OpenSSL library.
+class OpenSSLKeyPair final {
+ public:
+  // Takes ownership of the key.
+  explicit OpenSSLKeyPair(EVP_PKEY* pkey) : pkey_(pkey) {
+    RTC_DCHECK(pkey_ != nullptr);
+  }
+
+  static std::unique_ptr<OpenSSLKeyPair> Generate(const KeyParams& key_params);
+  // Constructs a key pair from the private key PEM string. This must not result
+  // in missing public key parameters. Returns null on error.
+  static std::unique_ptr<OpenSSLKeyPair> FromPrivateKeyPEMString(
+      const std::string& pem_string);
+
+  ~OpenSSLKeyPair();
+
+  std::unique_ptr<OpenSSLKeyPair> Clone();
+
+  EVP_PKEY* pkey() const { return pkey_; }
+  std::string PrivateKeyToPEMString() const;
+  std::string PublicKeyToPEMString() const;
+  bool operator==(const OpenSSLKeyPair& other) const;
+  bool operator!=(const OpenSSLKeyPair& other) const;
+
+ private:
+  void AddReference();
+
+  EVP_PKEY* pkey_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLKeyPair);
+};
+
+}  // namespace rtc
+
+#endif  // RTC_BASE_OPENSSL_KEY_PAIR_H_
diff --git a/rtc_base/openssl_session_cache_unittest.cc b/rtc_base/openssl_session_cache_unittest.cc
index 1d3084b..0441d5c 100644
--- a/rtc_base/openssl_session_cache_unittest.cc
+++ b/rtc_base/openssl_session_cache_unittest.cc
@@ -19,10 +19,28 @@
 #include "rtc_base/gunit.h"
 #include "rtc_base/openssl.h"
 
+namespace {
+// Use methods that avoid X509 objects if possible.
+SSL_CTX* NewDtlsContext() {
+#ifdef OPENSSL_IS_BORINGSSL
+  return SSL_CTX_new(DTLS_with_buffers_method());
+#else
+  return SSL_CTX_new(DTLS_method());
+#endif
+}
+SSL_CTX* NewTlsContext() {
+#ifdef OPENSSL_IS_BORINGSSL
+  return SSL_CTX_new(TLS_with_buffers_method());
+#else
+  return SSL_CTX_new(TLS_method());
+#endif
+}
+}  // namespace
+
 namespace rtc {
 
 TEST(OpenSSLSessionCache, DTLSModeSetCorrectly) {
-  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL_CTX* ssl_ctx = NewDtlsContext();
 
   OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
   EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_DTLS);
@@ -31,7 +49,7 @@
 }
 
 TEST(OpenSSLSessionCache, TLSModeSetCorrectly) {
-  SSL_CTX* ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
+  SSL_CTX* ssl_ctx = NewTlsContext();
 
   OpenSSLSessionCache session_cache(SSL_MODE_TLS, ssl_ctx);
   EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_TLS);
@@ -40,7 +58,7 @@
 }
 
 TEST(OpenSSLSessionCache, SSLContextSetCorrectly) {
-  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL_CTX* ssl_ctx = NewDtlsContext();
 
   OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
   EXPECT_EQ(session_cache.GetSSLContext(), ssl_ctx);
@@ -49,7 +67,7 @@
 }
 
 TEST(OpenSSLSessionCache, InvalidLookupReturnsNullptr) {
-  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL_CTX* ssl_ctx = NewDtlsContext();
 
   OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
   EXPECT_EQ(session_cache.LookupSession("Invalid"), nullptr);
@@ -60,7 +78,7 @@
 }
 
 TEST(OpenSSLSessionCache, SimpleValidSessionLookup) {
-  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL_CTX* ssl_ctx = NewDtlsContext();
   SSL_SESSION* ssl_session = SSL_SESSION_new(ssl_ctx);
 
   OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
@@ -71,7 +89,7 @@
 }
 
 TEST(OpenSSLSessionCache, AddToExistingReplacesPrevious) {
-  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL_CTX* ssl_ctx = NewDtlsContext();
   SSL_SESSION* ssl_session_1 = SSL_SESSION_new(ssl_ctx);
   SSL_SESSION* ssl_session_2 = SSL_SESSION_new(ssl_ctx);
 
diff --git a/rtc_base/openssl_stream_adapter.cc b/rtc_base/openssl_stream_adapter.cc
index f59b4ed..63b8069 100644
--- a/rtc_base/openssl_stream_adapter.cc
+++ b/rtc_base/openssl_stream_adapter.cc
@@ -32,7 +32,12 @@
 #include "rtc_base/openssl.h"
 #include "rtc_base/openssl_adapter.h"
 #include "rtc_base/openssl_digest.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
 #include "rtc_base/openssl_identity.h"
+#endif
+#include "rtc_base/openssl_utility.h"
 #include "rtc_base/ssl_certificate.h"
 #include "rtc_base/stream.h"
 #include "rtc_base/task_utils/to_queued_task.h"
@@ -304,10 +309,14 @@
 
 void OpenSSLStreamAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
   RTC_DCHECK(!identity_);
+#ifdef OPENSSL_IS_BORINGSSL
+  identity_.reset(static_cast<BoringSSLIdentity*>(identity.release()));
+#else
   identity_.reset(static_cast<OpenSSLIdentity*>(identity.release()));
+#endif
 }
 
-OpenSSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const {
+SSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const {
   return identity_.get();
 }
 
@@ -994,8 +1003,16 @@
 }
 
 SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
+#ifdef OPENSSL_IS_BORINGSSL
+  // If X509 objects aren't used, we can use these methods to avoid
+  // linking the sizable crypto/x509 code, using CRYPTO_BUFFER instead.
+  SSL_CTX* ctx =
+      SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_with_buffers_method()
+                                             : TLS_with_buffers_method());
+#else
   SSL_CTX* ctx =
       SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
+#endif
   if (ctx == nullptr) {
     return nullptr;
   }
@@ -1033,6 +1050,7 @@
   if (g_use_time_callback_for_testing) {
     SSL_CTX_set_current_time_cb(ctx, &TimeCallbackForTesting);
   }
+  SSL_CTX_set0_buffer_pool(ctx, openssl::GetBufferPool());
 #endif
 
   if (identity_ && !identity_->ConfigureIdentity(ctx)) {
@@ -1053,11 +1071,16 @@
   }
 
   // Configure a custom certificate verification callback to check the peer
-  // certificate digest. Note the second argument to SSL_CTX_set_verify is to
-  // override individual errors in the default verification logic, which is not
-  // what we want here.
+  // certificate digest.
+#ifdef OPENSSL_IS_BORINGSSL
+  // Use CRYPTO_BUFFER version of the callback if building with BoringSSL.
+  SSL_CTX_set_custom_verify(ctx, mode, SSLVerifyCallback);
+#else
+  // Note the second argument to SSL_CTX_set_verify is to override individual
+  // errors in the default verification logic, which is not what we want here.
   SSL_CTX_set_verify(ctx, mode, nullptr);
   SSL_CTX_set_cert_verify_callback(ctx, SSLVerifyCallback, nullptr);
+#endif
 
   // Select list of available ciphers. Note that !SHA256 and !SHA384 only
   // remove HMAC-SHA256 and HMAC-SHA384 cipher suites, not GCM cipher suites
@@ -1082,14 +1105,12 @@
     RTC_LOG(LS_WARNING) << "Missing digest or peer certificate.";
     return false;
   }
-  const OpenSSLCertificate* leaf_cert =
-      static_cast<const OpenSSLCertificate*>(&peer_cert_chain_->Get(0));
 
   unsigned char digest[EVP_MAX_MD_SIZE];
   size_t digest_length;
-  if (!OpenSSLCertificate::ComputeDigest(
-          leaf_cert->x509(), peer_certificate_digest_algorithm_, digest,
-          sizeof(digest), &digest_length)) {
+  if (!peer_cert_chain_->Get(0).ComputeDigest(
+          peer_certificate_digest_algorithm_, digest, sizeof(digest),
+          &digest_length)) {
     RTC_LOG(LS_WARNING) << "Failed to compute peer cert digest.";
     return false;
   }
@@ -1113,6 +1134,36 @@
   return peer_cert_chain_ ? peer_cert_chain_->Clone() : nullptr;
 }
 
+#ifdef OPENSSL_IS_BORINGSSL
+enum ssl_verify_result_t OpenSSLStreamAdapter::SSLVerifyCallback(
+    SSL* ssl,
+    uint8_t* out_alert) {
+  // Get our OpenSSLStreamAdapter from the context.
+  OpenSSLStreamAdapter* stream =
+      reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
+  const STACK_OF(CRYPTO_BUFFER)* chain = SSL_get0_peer_certificates(ssl);
+  // Creates certificate chain.
+  std::vector<std::unique_ptr<SSLCertificate>> cert_chain;
+  for (CRYPTO_BUFFER* cert : chain) {
+    cert_chain.emplace_back(new BoringSSLCertificate(bssl::UpRef(cert)));
+  }
+  stream->peer_cert_chain_.reset(new SSLCertChain(std::move(cert_chain)));
+
+  // If the peer certificate digest isn't known yet, we'll wait to verify
+  // until it's known, and for now just return a success status.
+  if (stream->peer_certificate_digest_algorithm_.empty()) {
+    RTC_LOG(LS_INFO) << "Waiting to verify certificate until digest is known.";
+    // TODO(deadbeef): Use ssl_verify_retry?
+    return ssl_verify_ok;
+  }
+
+  if (!stream->VerifyPeerCertificate()) {
+    return ssl_verify_invalid;
+  }
+
+  return ssl_verify_ok;
+}
+#else   // OPENSSL_IS_BORINGSSL
 int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) {
   // Get our SSL structure and OpenSSLStreamAdapter from the store.
   SSL* ssl = reinterpret_cast<SSL*>(
@@ -1120,20 +1171,10 @@
   OpenSSLStreamAdapter* stream =
       reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
 
-#if defined(OPENSSL_IS_BORINGSSL)
-  STACK_OF(X509)* chain = SSL_get_peer_full_cert_chain(ssl);
-  // Creates certificate chain.
-  std::vector<std::unique_ptr<SSLCertificate>> cert_chain;
-  for (X509* cert : chain) {
-    cert_chain.emplace_back(new OpenSSLCertificate(cert));
-  }
-  stream->peer_cert_chain_.reset(new SSLCertChain(std::move(cert_chain)));
-#else
   // Record the peer's certificate.
   X509* cert = X509_STORE_CTX_get0_cert(store);
   stream->peer_cert_chain_.reset(
       new SSLCertChain(std::make_unique<OpenSSLCertificate>(cert)));
-#endif
 
   // If the peer certificate digest isn't known yet, we'll wait to verify
   // until it's known, and for now just return a success status.
@@ -1149,6 +1190,7 @@
 
   return 1;
 }
+#endif  // !OPENSSL_IS_BORINGSSL
 
 bool OpenSSLStreamAdapter::IsBoringSsl() {
 #ifdef OPENSSL_IS_BORINGSSL
diff --git a/rtc_base/openssl_stream_adapter.h b/rtc_base/openssl_stream_adapter.h
index fbfccd684..a09737c 100644
--- a/rtc_base/openssl_stream_adapter.h
+++ b/rtc_base/openssl_stream_adapter.h
@@ -21,7 +21,11 @@
 
 #include "absl/types/optional.h"
 #include "rtc_base/buffer.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
 #include "rtc_base/openssl_identity.h"
+#endif
 #include "rtc_base/ssl_identity.h"
 #include "rtc_base/ssl_stream_adapter.h"
 #include "rtc_base/stream.h"
@@ -71,7 +75,7 @@
   ~OpenSSLStreamAdapter() override;
 
   void SetIdentity(std::unique_ptr<SSLIdentity> identity) override;
-  OpenSSLIdentity* GetIdentityForTesting() const override;
+  SSLIdentity* GetIdentityForTesting() const override;
 
   // Default argument is for compatibility
   void SetServerRole(SSLRole role = SSL_SERVER) override;
@@ -179,9 +183,16 @@
   SSL_CTX* SetupSSLContext();
   // Verify the peer certificate matches the signaled digest.
   bool VerifyPeerCertificate();
+
+#ifdef OPENSSL_IS_BORINGSSL
+  // SSL certificate verification callback. See SSL_CTX_set_custom_verify.
+  static enum ssl_verify_result_t SSLVerifyCallback(SSL* ssl,
+                                                    uint8_t* out_alert);
+#else
   // SSL certificate verification callback. See
   // SSL_CTX_set_cert_verify_callback.
   static int SSLVerifyCallback(X509_STORE_CTX* store, void* arg);
+#endif
 
   bool WaitingToVerifyPeerCertificate() const {
     return GetClientAuthEnabled() && !peer_certificate_verified_;
@@ -208,7 +219,11 @@
   SSL_CTX* ssl_ctx_;
 
   // Our key and certificate.
+#ifdef OPENSSL_IS_BORINGSSL
+  std::unique_ptr<BoringSSLIdentity> identity_;
+#else
   std::unique_ptr<OpenSSLIdentity> identity_;
+#endif
   // The certificate chain that the peer presented. Initially null, until the
   // connection is established.
   std::unique_ptr<SSLCertChain> peer_cert_chain_;
diff --git a/rtc_base/openssl_utility.cc b/rtc_base/openssl_utility.cc
index 1984eb0..f91e8d9 100644
--- a/rtc_base/openssl_utility.cc
+++ b/rtc_base/openssl_utility.cc
@@ -14,6 +14,9 @@
 #include "rtc_base/win32.h"  // NOLINT
 #endif                       // WEBRTC_WIN
 
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/pool.h>
+#endif
 #include <openssl/err.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
@@ -33,6 +36,10 @@
 
 // Holds various helper methods.
 namespace {
+
+// TODO(crbug.com/webrtc/11710): When OS certificate verification is available,
+// and we don't need VerifyPeerCertMatchesHost, don't compile this in order to
+// avoid a dependency on OpenSSL X509 objects (see crbug.com/webrtc/11410).
 void LogCertificates(SSL* ssl, X509* certificate) {
 // Logging certificates is extremely verbose. So it is disabled by default.
 #ifdef LOG_CERTIFICATES
@@ -65,6 +72,118 @@
 }
 }  // namespace
 
+#ifdef OPENSSL_IS_BORINGSSL
+bool ParseCertificate(CRYPTO_BUFFER* cert_buffer,
+                      CBS* signature_algorithm_oid,
+                      int64_t* expiration_time) {
+  CBS cbs;
+  CRYPTO_BUFFER_init_CBS(cert_buffer, &cbs);
+
+  //   Certificate  ::=  SEQUENCE  {
+  CBS certificate;
+  if (!CBS_get_asn1(&cbs, &certificate, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+  //        tbsCertificate       TBSCertificate,
+  CBS tbs_certificate;
+  if (!CBS_get_asn1(&certificate, &tbs_certificate, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+  //        signatureAlgorithm   AlgorithmIdentifier,
+  CBS signature_algorithm;
+  if (!CBS_get_asn1(&certificate, &signature_algorithm, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+  if (!CBS_get_asn1(&signature_algorithm, signature_algorithm_oid,
+                    CBS_ASN1_OBJECT)) {
+    return false;
+  }
+  //        signatureValue       BIT STRING  }
+  if (!CBS_get_asn1(&certificate, nullptr, CBS_ASN1_BITSTRING)) {
+    return false;
+  }
+  if (CBS_len(&certificate)) {
+    return false;
+  }
+
+  // Now parse the inner TBSCertificate.
+  //        version         [0]  EXPLICIT Version DEFAULT v1,
+  if (!CBS_get_optional_asn1(
+          &tbs_certificate, nullptr, nullptr,
+          CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC)) {
+    return false;
+  }
+  //        serialNumber         CertificateSerialNumber,
+  if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_INTEGER)) {
+    return false;
+  }
+  //        signature            AlgorithmIdentifier
+  if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+  //        issuer               Name,
+  if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+  //        validity             Validity,
+  CBS validity;
+  if (!CBS_get_asn1(&tbs_certificate, &validity, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+  // Skip over notBefore.
+  if (!CBS_get_any_asn1_element(&validity, nullptr, nullptr, nullptr)) {
+    return false;
+  }
+  // Parse notAfter.
+  CBS not_after;
+  unsigned not_after_tag;
+  if (!CBS_get_any_asn1(&validity, &not_after, &not_after_tag)) {
+    return false;
+  }
+  bool long_format;
+  if (not_after_tag == CBS_ASN1_UTCTIME) {
+    long_format = false;
+  } else if (not_after_tag == CBS_ASN1_GENERALIZEDTIME) {
+    long_format = true;
+  } else {
+    return false;
+  }
+  if (expiration_time) {
+    *expiration_time =
+        ASN1TimeToSec(CBS_data(&not_after), CBS_len(&not_after), long_format);
+  }
+  //        subject              Name,
+  if (!CBS_get_asn1_element(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+  //        subjectPublicKeyInfo SubjectPublicKeyInfo,
+  if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) {
+    return false;
+  }
+  //        issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL
+  if (!CBS_get_optional_asn1(&tbs_certificate, nullptr, nullptr,
+                             0x01 | CBS_ASN1_CONTEXT_SPECIFIC)) {
+    return false;
+  }
+  //        subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL
+  if (!CBS_get_optional_asn1(&tbs_certificate, nullptr, nullptr,
+                             0x02 | CBS_ASN1_CONTEXT_SPECIFIC)) {
+    return false;
+  }
+  //        extensions      [3]  EXPLICIT Extensions OPTIONAL
+  if (!CBS_get_optional_asn1(
+          &tbs_certificate, nullptr, nullptr,
+          0x03 | CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC)) {
+    return false;
+  }
+  if (CBS_len(&tbs_certificate)) {
+    return false;
+  }
+
+  return true;
+}
+#endif  // OPENSSL_IS_BORINGSSL
+
 bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) {
   if (host.empty()) {
     RTC_DLOG(LS_ERROR) << "Hostname is empty. Cannot verify peer certificate.";
@@ -76,9 +195,28 @@
     return false;
   }
 
+#ifdef OPENSSL_IS_BORINGSSL
+  // We can't grab a X509 object directly, as the SSL context may have been
+  // initialized with TLS_with_buffers_method.
+  const STACK_OF(CRYPTO_BUFFER)* chain = SSL_get0_peer_certificates(ssl);
+  if (chain == nullptr || sk_CRYPTO_BUFFER_num(chain) == 0) {
+    RTC_LOG(LS_ERROR)
+        << "SSL_get0_peer_certificates failed. This should never happen.";
+    return false;
+  }
+  CRYPTO_BUFFER* leaf = sk_CRYPTO_BUFFER_value(chain, 0);
+  bssl::UniquePtr<X509> x509(X509_parse_from_buffer(leaf));
+  if (!x509) {
+    RTC_LOG(LS_ERROR) << "Failed to parse certificate to X509 object.";
+    return false;
+  }
+  LogCertificates(ssl, x509.get());
+  return X509_check_host(x509.get(), host.c_str(), host.size(), 0, nullptr) ==
+         1;
+#else   // OPENSSL_IS_BORINGSSL
   X509* certificate = SSL_get_peer_certificate(ssl);
   if (certificate == nullptr) {
-    RTC_DLOG(LS_ERROR)
+    RTC_LOG(LS_ERROR)
         << "SSL_get_peer_certificate failed. This should never happen.";
     return false;
   }
@@ -89,6 +227,7 @@
       X509_check_host(certificate, host.c_str(), host.size(), 0, nullptr) == 1;
   X509_free(certificate);
   return is_valid_cert_name;
+#endif  // !defined(OPENSSL_IS_BORINGSSL)
 }
 
 void LogSSLErrors(const std::string& prefix) {
@@ -123,5 +262,12 @@
 }
 #endif  // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
 
+#ifdef OPENSSL_IS_BORINGSSL
+CRYPTO_BUFFER_POOL* GetBufferPool() {
+  static CRYPTO_BUFFER_POOL* instance = CRYPTO_BUFFER_POOL_new();
+  return instance;
+}
+#endif
+
 }  // namespace openssl
 }  // namespace rtc
diff --git a/rtc_base/openssl_utility.h b/rtc_base/openssl_utility.h
index 022294d..ee29ccd 100644
--- a/rtc_base/openssl_utility.h
+++ b/rtc_base/openssl_utility.h
@@ -20,8 +20,21 @@
 // to OpenSSL that are commonly used and don't require global state should be
 // placed here.
 namespace openssl {
+
+#ifdef OPENSSL_IS_BORINGSSL
+// Does minimal parsing of a certificate (only verifying the presence of major
+// fields), primarily for the purpose of extracting the relevant out
+// parameters. Any that the caller is uninterested in can be null.
+bool ParseCertificate(CRYPTO_BUFFER* cert_buffer,
+                      CBS* signature_algorithm_oid,
+                      int64_t* expiration_time);
+#endif
+
 // Verifies that the hostname provided matches that in the peer certificate
 // attached to this SSL state.
+// TODO(crbug.com/webrtc/11710): When OS certificate verification is available,
+// skip compiling this as it adds a dependency on OpenSSL X509 objects, which we
+// are trying to avoid in favor of CRYPTO_BUFFERs (see crbug.com/webrtc/11410).
 bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host);
 
 // Logs all the errors in the OpenSSL errror queue from the current thread. A
@@ -35,6 +48,10 @@
 bool LoadBuiltinSSLRootCertificates(SSL_CTX* ssl_ctx);
 #endif  // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
 
+#ifdef OPENSSL_IS_BORINGSSL
+CRYPTO_BUFFER_POOL* GetBufferPool();
+#endif
+
 }  // namespace openssl
 }  // namespace rtc
 
diff --git a/rtc_base/openssl_utility_unittest.cc b/rtc_base/openssl_utility_unittest.cc
index 9c9b971..d090524 100644
--- a/rtc_base/openssl_utility_unittest.cc
+++ b/rtc_base/openssl_utility_unittest.cc
@@ -24,8 +24,12 @@
 #include <openssl/crypto.h>
 #include <openssl/evp.h>
 #include <openssl/ssl.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/pool.h>
+#else
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
+#endif
 
 #include "rtc_base/arraysize.h"
 #include "rtc_base/checks.h"
@@ -169,14 +173,17 @@
     0x84, 0x0b, 0xc7, 0x15, 0x86, 0xc3, 0xfc, 0x48, 0x55, 0xb5, 0x81, 0x94,
     0x73, 0xbd, 0x18, 0xcd, 0x9d, 0x92, 0x47, 0xaa, 0xfd, 0x18};
 
+#ifdef OPENSSL_IS_BORINGSSL
+enum ssl_verify_result_t DummyVerifyCallback(SSL* ssl, uint8_t* out_alert) {
+  return ssl_verify_ok;
+}
+#endif
+
 // Creates a client SSL that has completed handshaking with a server that uses
 // the specified certificate (which must have private key kFakeSSLPrivateKey).
 // The server is deallocated. This client will have a peer certificate available
 // and is thus suitable for testing VerifyPeerCertMatchesHost.
 SSL* CreateSSLWithPeerCertificate(const unsigned char* cert, size_t cert_len) {
-  X509* x509 =
-      d2i_X509(nullptr, &cert, checked_cast<long>(cert_len));  // NOLINT
-  RTC_CHECK(x509);
 
   const unsigned char* key_ptr = kFakeSSLPrivateKey;
   EVP_PKEY* key = d2i_PrivateKey(
@@ -184,14 +191,33 @@
       checked_cast<long>(arraysize(kFakeSSLPrivateKey)));  // NOLINT
   RTC_CHECK(key);
 
-  SSL_CTX* ctx = SSL_CTX_new(SSLv23_method());
+#ifdef OPENSSL_IS_BORINGSSL
+  SSL_CTX* ctx = SSL_CTX_new(TLS_with_buffers_method());
+#else
+  SSL_CTX* ctx = SSL_CTX_new(TLS_method());
+#endif
   SSL* client = SSL_new(ctx);
   SSL* server = SSL_new(ctx);
   SSL_set_connect_state(client);
   SSL_set_accept_state(server);
 
+#ifdef OPENSSL_IS_BORINGSSL
+  bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer(CRYPTO_BUFFER_new(
+      static_cast<const uint8_t*>(cert), cert_len, openssl::GetBufferPool()));
+  RTC_CHECK(cert_buffer);
+  std::vector<CRYPTO_BUFFER*> cert_buffers;
+  cert_buffers.push_back(cert_buffer.get());
+  RTC_CHECK(1 == SSL_set_chain_and_key(server, cert_buffers.data(),
+                                       cert_buffers.size(), key, nullptr));
+  // When using crypto buffers we don't get any built-in verification.
+  SSL_set_custom_verify(client, SSL_VERIFY_PEER, DummyVerifyCallback);
+#else
+  X509* x509 =
+      d2i_X509(nullptr, &cert, checked_cast<long>(cert_len));  // NOLINT
+  RTC_CHECK(x509);
   RTC_CHECK(SSL_use_certificate(server, x509));
   RTC_CHECK(SSL_use_PrivateKey(server, key));
+#endif
 
   BIO* bio1;
   BIO* bio2;
@@ -221,13 +247,19 @@
   SSL_free(server);
   SSL_CTX_free(ctx);
   EVP_PKEY_free(key);
+#ifndef OPENSSL_IS_BORINGSSL
   X509_free(x509);
+#endif
   return client;
 }
 }  // namespace
 
 TEST(OpenSSLUtilityTest, VerifyPeerCertMatchesHostFailsOnNoPeerCertificate) {
-  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+#ifdef OPENSSL_IS_BORINGSSL
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLS_with_buffers_method());
+#else
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLS_method());
+#endif
   SSL* ssl = SSL_new(ssl_ctx);
 
   EXPECT_FALSE(openssl::VerifyPeerCertMatchesHost(ssl, "webrtc.org"));
diff --git a/rtc_base/rtc_certificate_generator.cc b/rtc_base/rtc_certificate_generator.cc
index d95b645..5e1fdca 100644
--- a/rtc_base/rtc_certificate_generator.cc
+++ b/rtc_base/rtc_certificate_generator.cc
@@ -51,7 +51,7 @@
     expires_s = std::min(expires_s, kYearInSeconds);
     // TODO(torbjorng): Stop using |time_t|, its type is unspecified. It it safe
     // to assume it can hold up to a year's worth of seconds (and more), but
-    // |SSLIdentity::Generate| should stop relying on |time_t|.
+    // |SSLIdentity::Create| should stop relying on |time_t|.
     // See bugs.webrtc.org/5720.
     time_t cert_lifetime_s = static_cast<time_t>(expires_s);
     identity = SSLIdentity::Create(kIdentityName, key_params, cert_lifetime_s);
diff --git a/rtc_base/ssl_certificate.cc b/rtc_base/ssl_certificate.cc
index db9097b..3f7013e 100644
--- a/rtc_base/ssl_certificate.cc
+++ b/rtc_base/ssl_certificate.cc
@@ -16,7 +16,12 @@
 
 #include "absl/algorithm/container.h"
 #include "rtc_base/checks.h"
-#include "rtc_base/openssl_certificate.h"
+#include "rtc_base/openssl.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
+#include "rtc_base/openssl_identity.h"
+#endif
 #include "rtc_base/ssl_fingerprint.h"
 #include "rtc_base/third_party/base64/base64.h"
 
@@ -117,7 +122,11 @@
 // static
 std::unique_ptr<SSLCertificate> SSLCertificate::FromPEMString(
     const std::string& pem_string) {
+#ifdef OPENSSL_IS_BORINGSSL
+  return BoringSSLCertificate::FromPEMString(pem_string);
+#else
   return OpenSSLCertificate::FromPEMString(pem_string);
+#endif
 }
 
 }  // namespace rtc
diff --git a/rtc_base/ssl_identity.cc b/rtc_base/ssl_identity.cc
index 09d25d2..8d93ecf 100644
--- a/rtc_base/ssl_identity.cc
+++ b/rtc_base/ssl_identity.cc
@@ -11,12 +11,16 @@
 // Handling of certificates and keypairs for SSLStreamAdapter's peer mode.
 #include "rtc_base/ssl_identity.h"
 
+#include <openssl/ossl_typ.h>
 #include <string.h>
 #include <time.h>
-#include <string>
 
 #include "rtc_base/checks.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
 #include "rtc_base/openssl_identity.h"
+#endif
 #include "rtc_base/ssl_certificate.h"
 #include "rtc_base/strings/string_builder.h"
 #include "rtc_base/third_party/base64/base64.h"
@@ -213,28 +217,36 @@
 std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
                                                  const KeyParams& key_param,
                                                  time_t certificate_lifetime) {
+#ifdef OPENSSL_IS_BORINGSSL
+  return BoringSSLIdentity::CreateWithExpiration(common_name, key_param,
+                                                 certificate_lifetime);
+#else
   return OpenSSLIdentity::CreateWithExpiration(common_name, key_param,
                                                certificate_lifetime);
+#endif
 }
 
 // static
 std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
                                                  const KeyParams& key_param) {
-  return OpenSSLIdentity::CreateWithExpiration(
-      common_name, key_param, kDefaultCertificateLifetimeInSeconds);
+  return Create(common_name, key_param, kDefaultCertificateLifetimeInSeconds);
 }
 
 // static
 std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
                                                  KeyType key_type) {
-  return OpenSSLIdentity::CreateWithExpiration(
-      common_name, KeyParams(key_type), kDefaultCertificateLifetimeInSeconds);
+  return Create(common_name, KeyParams(key_type),
+                kDefaultCertificateLifetimeInSeconds);
 }
 
 //  static
 std::unique_ptr<SSLIdentity> SSLIdentity::CreateForTest(
     const SSLIdentityParams& params) {
+#ifdef OPENSSL_IS_BORINGSSL
+  return BoringSSLIdentity::CreateForTest(params);
+#else
   return OpenSSLIdentity::CreateForTest(params);
+#endif
 }
 
 // Construct an identity from a private key and a certificate.
@@ -242,7 +254,11 @@
 std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMStrings(
     const std::string& private_key,
     const std::string& certificate) {
+#ifdef OPENSSL_IS_BORINGSSL
+  return BoringSSLIdentity::CreateFromPEMStrings(private_key, certificate);
+#else
   return OpenSSLIdentity::CreateFromPEMStrings(private_key, certificate);
+#endif
 }
 
 // Construct an identity from a private key and a certificate chain.
@@ -250,13 +266,23 @@
 std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMChainStrings(
     const std::string& private_key,
     const std::string& certificate_chain) {
+#ifdef OPENSSL_IS_BORINGSSL
+  return BoringSSLIdentity::CreateFromPEMChainStrings(private_key,
+                                                      certificate_chain);
+#else
   return OpenSSLIdentity::CreateFromPEMChainStrings(private_key,
                                                     certificate_chain);
+#endif
 }
 
 bool operator==(const SSLIdentity& a, const SSLIdentity& b) {
+#ifdef OPENSSL_IS_BORINGSSL
+  return static_cast<const BoringSSLIdentity&>(a) ==
+         static_cast<const BoringSSLIdentity&>(b);
+#else
   return static_cast<const OpenSSLIdentity&>(a) ==
          static_cast<const OpenSSLIdentity&>(b);
+#endif
 }
 bool operator!=(const SSLIdentity& a, const SSLIdentity& b) {
   return !(a == b);
diff --git a/rtc_base/ssl_identity_unittest.cc b/rtc_base/ssl_identity_unittest.cc
index 0d9d0fd..a907bfc 100644
--- a/rtc_base/ssl_identity_unittest.cc
+++ b/rtc_base/ssl_identity_unittest.cc
@@ -65,7 +65,7 @@
     0x35, 0xce, 0x26, 0x58, 0x4a, 0x33, 0x6d, 0xbc, 0xb6};
 
 // These PEM strings were created by generating an identity with
-// |SSLIdentity::Generate| and invoking |identity->PrivateKeyToPEMString()|,
+// |SSLIdentity::Create| and invoking |identity->PrivateKeyToPEMString()|,
 // |identity->PublicKeyToPEMString()| and
 // |identity->certificate().ToPEMString()|. If the crypto library is updated,
 // and the update changes the string form of the keys, these will have to be
@@ -406,6 +406,21 @@
   EXPECT_EQ(kECDSA_CERT_PEM, identity->certificate().ToPEMString());
 }
 
+TEST_F(SSLIdentityTest, FromPEMChainStrings) {
+  // This doesn't form a valid certificate chain, but that doesn't matter for
+  // the purposes of the test
+  std::string chain(kRSA_CERT_PEM);
+  chain.append(kTestCertificate);
+  std::unique_ptr<SSLIdentity> identity(
+      SSLIdentity::CreateFromPEMChainStrings(kRSA_PRIVATE_KEY_PEM, chain));
+  EXPECT_TRUE(identity);
+  EXPECT_EQ(kRSA_PRIVATE_KEY_PEM, identity->PrivateKeyToPEMString());
+  EXPECT_EQ(kRSA_PUBLIC_KEY_PEM, identity->PublicKeyToPEMString());
+  ASSERT_EQ(2u, identity->cert_chain().GetSize());
+  EXPECT_EQ(kRSA_CERT_PEM, identity->cert_chain().Get(0).ToPEMString());
+  EXPECT_EQ(kTestCertificate, identity->cert_chain().Get(1).ToPEMString());
+}
+
 TEST_F(SSLIdentityTest, CloneIdentityRSA) {
   TestCloningIdentity(*identity_rsa1_);
   TestCloningIdentity(*identity_rsa2_);
diff --git a/rtc_base/ssl_stream_adapter_unittest.cc b/rtc_base/ssl_stream_adapter_unittest.cc
index 379acac..c580d83 100644
--- a/rtc_base/ssl_stream_adapter_unittest.cc
+++ b/rtc_base/ssl_stream_adapter_unittest.cc
@@ -508,8 +508,9 @@
     }
   }
 
-  // This tests that the handshake can complete before the identity is
-  // verified, and the identity will be verified after the fact.
+  // This tests that the handshake can complete before the identity is verified,
+  // and the identity will be verified after the fact. It also verifies that
+  // packets can't be read or written before the identity has been verified.
   void TestHandshakeWithDelayedIdentity(bool valid_identity) {
     server_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS);
     client_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS);
@@ -524,14 +525,9 @@
     }
 
     // Start the handshake
-    int rv;
-
     server_ssl_->SetServerRole();
-    rv = server_ssl_->StartSSL();
-    ASSERT_EQ(0, rv);
-
-    rv = client_ssl_->StartSSL();
-    ASSERT_EQ(0, rv);
+    ASSERT_EQ(0, server_ssl_->StartSSL());
+    ASSERT_EQ(0, client_ssl_->StartSSL());
 
     // Now run the handshake.
     EXPECT_TRUE_WAIT(
@@ -547,16 +543,57 @@
     EXPECT_EQ(rtc::SR_BLOCK, client_ssl_->Write(&packet, 1, &sent, 0));
     EXPECT_EQ(rtc::SR_BLOCK, server_ssl_->Write(&packet, 1, &sent, 0));
 
-    // If we set an invalid identity at this point, SetPeerCertificateDigest
-    // should return false.
-    SetPeerIdentitiesByDigest(valid_identity, valid_identity);
+    // Collect both of the certificate digests; needs to be done before calling
+    // SetPeerCertificateDigest as that may reset the identity.
+    unsigned char server_digest[20];
+    size_t server_digest_len;
+    unsigned char client_digest[20];
+    size_t client_digest_len;
+    bool rv;
+
+    rv = server_identity()->certificate().ComputeDigest(
+        rtc::DIGEST_SHA_1, server_digest, 20, &server_digest_len);
+    ASSERT_TRUE(rv);
+    rv = client_identity()->certificate().ComputeDigest(
+        rtc::DIGEST_SHA_1, client_digest, 20, &client_digest_len);
+    ASSERT_TRUE(rv);
+
+    if (!valid_identity) {
+      RTC_LOG(LS_INFO) << "Setting bogus digest for client/server certs";
+      client_digest[0]++;
+      server_digest[0]++;
+    }
+
+    // Set the peer certificate digest for the client.
+    rtc::SSLPeerCertificateDigestError err;
+    rtc::SSLPeerCertificateDigestError expected_err =
+        valid_identity
+            ? rtc::SSLPeerCertificateDigestError::NONE
+            : rtc::SSLPeerCertificateDigestError::VERIFICATION_FAILED;
+    rv = client_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, server_digest,
+                                               server_digest_len, &err);
+    EXPECT_EQ(expected_err, err);
+    EXPECT_EQ(valid_identity, rv);
     // State should then transition to SS_OPEN or SS_CLOSED based on validation
     // of the identity.
     if (valid_identity) {
       EXPECT_EQ(rtc::SS_OPEN, client_ssl_->GetState());
-      EXPECT_EQ(rtc::SS_OPEN, server_ssl_->GetState());
+      // If the client sends a packet while the server still hasn't verified the
+      // client identity, the server should continue to return SR_BLOCK.
+      EXPECT_EQ(rtc::SR_SUCCESS, client_ssl_->Write(&packet, 1, &sent, 0));
+      EXPECT_EQ(rtc::SR_BLOCK, server_ssl_->Read(&packet, 1, 0, 0));
     } else {
       EXPECT_EQ(rtc::SS_CLOSED, client_ssl_->GetState());
+    }
+
+    // Set the peer certificate digest for the server.
+    rv = server_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, client_digest,
+                                               client_digest_len, &err);
+    EXPECT_EQ(expected_err, err);
+    EXPECT_EQ(valid_identity, rv);
+    if (valid_identity) {
+      EXPECT_EQ(rtc::SS_OPEN, server_ssl_->GetState());
+    } else {
       EXPECT_EQ(rtc::SS_CLOSED, server_ssl_->GetState());
     }
   }
diff --git a/webrtc.gni b/webrtc.gni
index 05a230c..2f6e77d 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -227,6 +227,10 @@
   rtc_libvpx_build_vp9 = !build_with_mozilla
   rtc_build_opus = !build_with_mozilla
   rtc_build_ssl = !build_with_mozilla
+
+  # Can be set to true if rtc_build_ssl is false, but externally provided
+  # openssl library is boringssl, to enable the use of boringssl-specific code.
+  rtc_openssl_is_boringssl = !build_with_mozilla
   rtc_build_usrsctp = !build_with_mozilla
 
   # Enable libevent task queues on platforms that support it.