Move certificateProvider out of //ash

This change is a necessary part of smart cards migration to Lacros.

Bug: 1291887
Change-Id: I418d617da74b4eda7a8d660f509c53926536cf4d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3645419
Reviewed-by: Hidehiko Abe <[email protected]>
Reviewed-by: Reilly Grant <[email protected]>
Commit-Queue: Oleksandr Kulkov <[email protected]>
Reviewed-by: Maksim Ivanov <[email protected]>
Reviewed-by: Vasilii Sukhanov <[email protected]>
Reviewed-by: Peter Kasting <[email protected]>
Reviewed-by: Denis Kuznetsov <[email protected]>
Reviewed-by: Ted Choc <[email protected]>
Reviewed-by: Alexander Alekseev <[email protected]>
Reviewed-by: David Benjamin <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1010040}
diff --git a/chrome/browser/certificate_provider/DIR_METADATA b/chrome/browser/certificate_provider/DIR_METADATA
new file mode 100644
index 0000000..6bbb1c66
--- /dev/null
+++ b/chrome/browser/certificate_provider/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail: {
+  component: "OS>Software>Enterprise>Smartcard"
+}
diff --git a/chrome/browser/certificate_provider/OWNERS b/chrome/browser/certificate_provider/OWNERS
new file mode 100644
index 0000000..325f7c3
--- /dev/null
+++ b/chrome/browser/certificate_provider/OWNERS
@@ -0,0 +1 @@
[email protected]
diff --git a/chrome/browser/certificate_provider/certificate_info.cc b/chrome/browser/certificate_provider/certificate_info.cc
new file mode 100644
index 0000000..19e1235
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_info.cc
@@ -0,0 +1,27 @@
+// Copyright 2015 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 "chrome/browser/certificate_provider/certificate_info.h"
+
+#include "net/cert/x509_certificate.h"
+
+namespace chromeos {
+namespace certificate_provider {
+
+CertificateInfo::CertificateInfo() {}
+
+CertificateInfo::CertificateInfo(const CertificateInfo& other) = default;
+
+CertificateInfo::~CertificateInfo() {}
+
+bool CertificateInfo::operator==(const CertificateInfo& other) const {
+  return net::X509Certificate::CalculateFingerprint256(
+             this->certificate->cert_buffer()) ==
+             net::X509Certificate::CalculateFingerprint256(
+                 other.certificate->cert_buffer()) &&
+         this->supported_algorithms == other.supported_algorithms;
+}
+
+}  // namespace certificate_provider
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/certificate_info.h b/chrome/browser/certificate_provider/certificate_info.h
new file mode 100644
index 0000000..e6ffe8d
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_info.h
@@ -0,0 +1,38 @@
+// Copyright 2015 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_INFO_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_INFO_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "net/cert/x509_certificate.h"
+#include "net/ssl/ssl_private_key.h"
+
+namespace chromeos {
+namespace certificate_provider {
+
+// Holds all information of a certificate that must be synchronously available
+// to implement net::SSLPrivateKey.
+struct CertificateInfo {
+  CertificateInfo();
+  CertificateInfo(const CertificateInfo& other);
+  ~CertificateInfo();
+
+  bool operator==(const CertificateInfo& other) const;
+
+  scoped_refptr<net::X509Certificate> certificate;
+  // Contains the list of supported signature algorithms, using TLS 1.3's
+  // SignatureScheme values. See net::SSLPrivateKey documentation for details.
+  std::vector<uint16_t> supported_algorithms;
+};
+using CertificateInfoList = std::vector<CertificateInfo>;
+
+}  // namespace certificate_provider
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_INFO_H_
diff --git a/chrome/browser/certificate_provider/certificate_provider.h b/chrome/browser/certificate_provider/certificate_provider.h
new file mode 100644
index 0000000..1d44c33
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_provider.h
@@ -0,0 +1,25 @@
+// Copyright 2015 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_H_
+
+#include "net/ssl/client_cert_identity.h"
+
+namespace chromeos {
+
+class CertificateProvider {
+ public:
+  CertificateProvider() {}
+  CertificateProvider(const CertificateProvider&) = delete;
+  CertificateProvider& operator=(const CertificateProvider&) = delete;
+  virtual ~CertificateProvider() {}
+
+  virtual void GetCertificates(
+      base::OnceCallback<void(net::ClientCertIdentityList)> callback) = 0;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_H_
diff --git a/chrome/browser/certificate_provider/certificate_provider_service.cc b/chrome/browser/certificate_provider/certificate_provider_service.cc
new file mode 100644
index 0000000..b0b5661
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_provider_service.cc
@@ -0,0 +1,492 @@
+// Copyright 2015 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 "chrome/browser/certificate_provider/certificate_provider_service.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_piece.h"
+#include "base/task/task_runner.h"
+#include "base/task/task_runner_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "chrome/browser/certificate_provider/certificate_provider.h"
+#include "net/base/net_errors.h"
+
+namespace chromeos {
+
+namespace {
+
+void PostSignResult(net::SSLPrivateKey::SignCallback callback,
+                    net::Error error,
+                    const std::vector<uint8_t>& signature) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), error, signature));
+}
+
+void PostIdentities(
+    base::OnceCallback<void(net::ClientCertIdentityList)> callback,
+    net::ClientCertIdentityList certs) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), std::move(certs)));
+}
+
+}  // namespace
+
+class CertificateProviderService::CertificateProviderImpl
+    : public CertificateProvider {
+ public:
+  // This provider must be used on the same thread as the
+  // CertificateProviderService.
+  explicit CertificateProviderImpl(
+      const base::WeakPtr<CertificateProviderService>& service);
+
+  CertificateProviderImpl(const CertificateProviderImpl&) = delete;
+  CertificateProviderImpl& operator=(const CertificateProviderImpl&) = delete;
+
+  ~CertificateProviderImpl() override;
+
+  void GetCertificates(
+      base::OnceCallback<void(net::ClientCertIdentityList)> callback) override;
+
+ private:
+  const base::WeakPtr<CertificateProviderService> service_;
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+// Implements an SSLPrivateKey backed by the signing function exposed by an
+// extension through the certificateProvider API.
+// Objects of this class must be used the CertificateProviderService's sequence.
+class CertificateProviderService::SSLPrivateKey : public net::SSLPrivateKey {
+ public:
+  SSLPrivateKey(const std::string& extension_id,
+                const CertificateInfo& cert_info,
+                const base::WeakPtr<CertificateProviderService>& service);
+
+  SSLPrivateKey(const SSLPrivateKey&) = delete;
+  SSLPrivateKey& operator=(const SSLPrivateKey&) = delete;
+
+  // net::SSLPrivateKey:
+  std::string GetProviderName() override;
+  std::vector<uint16_t> GetAlgorithmPreferences() override;
+  void Sign(uint16_t algorithm,
+            base::span<const uint8_t> input,
+            SignCallback callback) override;
+
+ private:
+  ~SSLPrivateKey() override;
+
+  const std::string extension_id_;
+  const CertificateInfo cert_info_;
+  const base::WeakPtr<CertificateProviderService> service_;
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+class CertificateProviderService::ClientCertIdentity
+    : public net::ClientCertIdentity {
+ public:
+  ClientCertIdentity(scoped_refptr<net::X509Certificate> cert,
+                     base::WeakPtr<CertificateProviderService> service)
+      : net::ClientCertIdentity(std::move(cert)), service_(service) {}
+
+  ClientCertIdentity(const ClientCertIdentity&) = delete;
+  ClientCertIdentity& operator=(const ClientCertIdentity&) = delete;
+
+  ~ClientCertIdentity() override;
+
+  void AcquirePrivateKey(
+      base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
+          private_key_callback) override;
+
+ private:
+  SEQUENCE_CHECKER(sequence_checker_);
+  const base::WeakPtr<CertificateProviderService> service_;
+};
+
+CertificateProviderService::ClientCertIdentity::~ClientCertIdentity() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void CertificateProviderService::ClientCertIdentity::AcquirePrivateKey(
+    base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
+        private_key_callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!service_) {
+    std::move(private_key_callback).Run(nullptr);
+    return;
+  }
+
+  bool is_currently_provided = false;
+  CertificateInfo info;
+  std::string extension_id;
+  // TODO(mattm): can the ClientCertIdentity store a handle directly to the
+  // extension instead of having to go through service_->certificate_map_ ?
+  service_->certificate_map_.LookUpCertificate(
+      *certificate(), &is_currently_provided, &info, &extension_id);
+  if (!is_currently_provided) {
+    std::move(private_key_callback).Run(nullptr);
+    return;
+  }
+
+  std::move(private_key_callback)
+      .Run(base::MakeRefCounted<SSLPrivateKey>(extension_id, info, service_));
+}
+
+CertificateProviderService::CertificateProviderImpl::CertificateProviderImpl(
+    const base::WeakPtr<CertificateProviderService>& service)
+    : service_(service) {}
+
+CertificateProviderService::CertificateProviderImpl::
+    ~CertificateProviderImpl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void CertificateProviderService::CertificateProviderImpl::GetCertificates(
+    base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Avoid running the callback reentrantly.
+  callback = base::BindOnce(&PostIdentities, std::move(callback));
+
+  if (!service_) {
+    std::move(callback).Run(net::ClientCertIdentityList());
+    return;
+  }
+  service_->GetCertificatesFromExtensions(std::move(callback));
+}
+
+CertificateProviderService::SSLPrivateKey::SSLPrivateKey(
+    const std::string& extension_id,
+    const CertificateInfo& cert_info,
+    const base::WeakPtr<CertificateProviderService>& service)
+    : extension_id_(extension_id), cert_info_(cert_info), service_(service) {}
+
+std::string CertificateProviderService::SSLPrivateKey::GetProviderName() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return "extension \"" + extension_id_ + "\"";
+}
+
+std::vector<uint16_t>
+CertificateProviderService::SSLPrivateKey::GetAlgorithmPreferences() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return cert_info_.supported_algorithms;
+}
+
+void CertificateProviderService::SSLPrivateKey::Sign(
+    uint16_t algorithm,
+    base::span<const uint8_t> input,
+    SignCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // The Sign() method should not call its callback reentrantly, so wrap it in
+  // PostSignResult().
+  callback = base::BindOnce(&PostSignResult, std::move(callback));
+
+  if (!service_) {
+    std::move(callback).Run(net::ERR_FAILED, /*signature=*/{});
+    return;
+  }
+
+  service_->RequestSignatureFromExtension(
+      extension_id_, cert_info_.certificate, algorithm, input,
+      /*authenticating_user_account_id=*/{}, std::move(callback));
+}
+
+CertificateProviderService::SSLPrivateKey::~SSLPrivateKey() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+CertificateProviderService::CertificateProviderService() {}
+
+CertificateProviderService::~CertificateProviderService() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void CertificateProviderService::SetDelegate(
+    std::unique_ptr<Delegate> delegate) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!delegate_);
+  DCHECK(delegate);
+
+  delegate_ = std::move(delegate);
+}
+
+void CertificateProviderService::AddObserver(Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  observers_.AddObserver(observer);
+}
+
+void CertificateProviderService::RemoveObserver(Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  observers_.RemoveObserver(observer);
+}
+
+void CertificateProviderService::SetCertificatesProvidedByExtension(
+    const std::string& extension_id,
+    const CertificateInfoList& certificate_infos) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  certificate_map_.UpdateCertificatesForExtension(extension_id,
+                                                  certificate_infos);
+  for (auto& observer : observers_)
+    observer.OnCertificatesUpdated(extension_id, certificate_infos);
+}
+
+bool CertificateProviderService::SetExtensionCertificateReplyReceived(
+    const std::string& extension_id,
+    int cert_request_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  bool completed = false;
+  if (!certificate_requests_.SetExtensionReplyReceived(
+          extension_id, cert_request_id, &completed)) {
+    DLOG(WARNING) << "Unexpected reply of extension " << extension_id
+                  << " to request " << cert_request_id;
+    return false;
+  }
+  if (completed) {
+    base::OnceCallback<void(net::ClientCertIdentityList)> callback;
+    certificate_requests_.RemoveRequest(cert_request_id, &callback);
+    CollectCertificatesAndRun(std::move(callback));
+  }
+  return true;
+}
+
+bool CertificateProviderService::ReplyToSignRequest(
+    const std::string& extension_id,
+    int sign_request_id,
+    const std::vector<uint8_t>& signature) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
+  LOG(WARNING) << "Extension " << extension_id
+               << " replied to signature request " << sign_request_id
+               << ", size " << signature.size();
+
+  scoped_refptr<net::X509Certificate> certificate;
+  net::SSLPrivateKey::SignCallback callback;
+  if (!sign_requests_.RemoveRequest(extension_id, sign_request_id, &certificate,
+                                    &callback)) {
+    return false;
+  }
+  pin_dialog_manager_.RemoveSignRequest(extension_id, sign_request_id);
+
+  const net::Error error_code = signature.empty() ? net::ERR_FAILED : net::OK;
+  std::move(callback).Run(error_code, signature);
+
+  if (!signature.empty()) {
+    for (auto& observer : observers_)
+      observer.OnSignCompleted(certificate, extension_id);
+  }
+  return true;
+}
+
+bool CertificateProviderService::LookUpCertificate(
+    const net::X509Certificate& cert,
+    bool* has_extension,
+    std::string* extension_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  CertificateInfo unused_info;
+  return certificate_map_.LookUpCertificate(cert, has_extension, &unused_info,
+                                            extension_id);
+}
+
+std::unique_ptr<CertificateProvider>
+CertificateProviderService::CreateCertificateProvider() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return std::make_unique<CertificateProviderImpl>(weak_factory_.GetWeakPtr());
+}
+
+void CertificateProviderService::OnExtensionUnregistered(
+    const std::string& extension_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  certificate_map_.RemoveExtension(extension_id);
+}
+
+void CertificateProviderService::OnExtensionUnloaded(
+    const std::string& extension_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  certificate_map_.RemoveExtension(extension_id);
+
+  for (const int completed_cert_request_id :
+       certificate_requests_.DropExtension(extension_id)) {
+    base::OnceCallback<void(net::ClientCertIdentityList)> callback;
+    certificate_requests_.RemoveRequest(completed_cert_request_id, &callback);
+    CollectCertificatesAndRun(std::move(callback));
+  }
+
+  for (auto& callback : sign_requests_.RemoveAllRequests(extension_id))
+    std::move(callback).Run(net::ERR_FAILED, std::vector<uint8_t>());
+
+  pin_dialog_manager_.ExtensionUnloaded(extension_id);
+}
+
+void CertificateProviderService::RequestSignatureBySpki(
+    const std::string& subject_public_key_info,
+    uint16_t algorithm,
+    base::span<const uint8_t> input,
+    const absl::optional<AccountId>& authenticating_user_account_id,
+    net::SSLPrivateKey::SignCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  bool is_currently_provided = false;
+  CertificateInfo info;
+  std::string extension_id;
+  certificate_map_.LookUpCertificateBySpki(
+      subject_public_key_info, &is_currently_provided, &info, &extension_id);
+  if (!is_currently_provided) {
+    LOG(ERROR) << "no certificate with the specified spki was found";
+    std::move(callback).Run(net::ERR_FAILED, std::vector<uint8_t>());
+    return;
+  }
+
+  RequestSignatureFromExtension(extension_id, info.certificate, algorithm,
+                                input, authenticating_user_account_id,
+                                std::move(callback));
+}
+
+bool CertificateProviderService::LookUpSpki(
+    const std::string& subject_public_key_info,
+    std::vector<uint16_t>* supported_algorithms,
+    std::string* extension_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  bool is_currently_provided = false;
+  CertificateInfo info;
+  certificate_map_.LookUpCertificateBySpki(
+      subject_public_key_info, &is_currently_provided, &info, extension_id);
+  if (!is_currently_provided) {
+    LOG(ERROR) << "no certificate with the specified spki was found";
+    return false;
+  }
+  *supported_algorithms = info.supported_algorithms;
+  return true;
+}
+
+void CertificateProviderService::AbortSignatureRequestsForAuthenticatingUser(
+    const AccountId& authenticating_user_account_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  using ExtensionNameRequestIdPair =
+      certificate_provider::SignRequests::ExtensionNameRequestIdPair;
+
+  const std::vector<ExtensionNameRequestIdPair> sign_requests_to_abort =
+      sign_requests_.FindRequestsForAuthenticatingUser(
+          authenticating_user_account_id);
+
+  for (const ExtensionNameRequestIdPair& sign_request :
+       sign_requests_to_abort) {
+    const std::string& extension_id = sign_request.first;
+    const int sign_request_id = sign_request.second;
+
+    // TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
+    LOG(WARNING) << "Aborting user login signature request from extension "
+                 << extension_id << " id " << sign_request_id;
+
+    pin_dialog_manager_.RemoveSignRequest(extension_id, sign_request_id);
+
+    scoped_refptr<net::X509Certificate> certificate;
+    net::SSLPrivateKey::SignCallback sign_callback;
+    if (sign_requests_.RemoveRequest(extension_id, sign_request_id,
+                                     &certificate, &sign_callback)) {
+      std::move(sign_callback).Run(net::ERR_FAILED, std::vector<uint8_t>());
+    }
+  }
+}
+
+void CertificateProviderService::GetCertificatesFromExtensions(
+    base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const std::vector<std::string> provider_extensions(
+      delegate_->CertificateProviderExtensions());
+
+  if (provider_extensions.empty()) {
+    DVLOG(2) << "No provider extensions left.";
+    // Note that there could still be unfinished requests to extensions that
+    // were previously registered.
+    CollectCertificatesAndRun(std::move(callback));
+    return;
+  }
+
+  const int cert_request_id = certificate_requests_.AddRequest(
+      provider_extensions, std::move(callback),
+      base::BindOnce(&CertificateProviderService::TerminateCertificateRequest,
+                     base::Unretained(this)));
+
+  DVLOG(2) << "Start certificate request " << cert_request_id;
+  delegate_->BroadcastCertificateRequest(cert_request_id);
+}
+
+void CertificateProviderService::CollectCertificatesAndRun(
+    base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  net::ClientCertIdentityList client_cert_identity_list;
+  std::vector<scoped_refptr<net::X509Certificate>> certificates =
+      certificate_map_.GetCertificates();
+  for (const scoped_refptr<net::X509Certificate>& certificate : certificates) {
+    client_cert_identity_list.push_back(std::make_unique<ClientCertIdentity>(
+        certificate, weak_factory_.GetWeakPtr()));
+  }
+
+  std::move(callback).Run(std::move(client_cert_identity_list));
+}
+
+void CertificateProviderService::TerminateCertificateRequest(
+    int cert_request_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::OnceCallback<void(net::ClientCertIdentityList)> callback;
+  if (!certificate_requests_.RemoveRequest(cert_request_id, &callback)) {
+    DLOG(WARNING) << "Request id " << cert_request_id << " unknown.";
+    return;
+  }
+
+  DVLOG(1) << "Time out certificate request " << cert_request_id;
+  CollectCertificatesAndRun(std::move(callback));
+}
+
+void CertificateProviderService::RequestSignatureFromExtension(
+    const std::string& extension_id,
+    const scoped_refptr<net::X509Certificate>& certificate,
+    uint16_t algorithm,
+    base::span<const uint8_t> input,
+    const absl::optional<AccountId>& authenticating_user_account_id,
+    net::SSLPrivateKey::SignCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const int sign_request_id = sign_requests_.AddRequest(
+      extension_id, certificate, authenticating_user_account_id,
+      std::move(callback));
+
+  // TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
+  LOG(WARNING) << "Starting signature request to extension " << extension_id
+               << " id " << sign_request_id;
+
+  pin_dialog_manager_.AddSignRequestId(extension_id, sign_request_id,
+                                       authenticating_user_account_id);
+  if (!delegate_->DispatchSignRequestToExtension(
+          extension_id, sign_request_id, algorithm, certificate, input)) {
+    // TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
+    LOG(WARNING) << "Failed to dispatch signature request to extension "
+                 << extension_id << " id " << sign_request_id;
+    scoped_refptr<net::X509Certificate> local_certificate;
+    sign_requests_.RemoveRequest(extension_id, sign_request_id,
+                                 &local_certificate, &callback);
+    pin_dialog_manager_.RemoveSignRequest(extension_id, sign_request_id);
+    std::move(callback).Run(net::ERR_FAILED, std::vector<uint8_t>());
+  }
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/certificate_provider_service.h b/chrome/browser/certificate_provider/certificate_provider_service.h
new file mode 100644
index 0000000..f4c0b535
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_provider_service.h
@@ -0,0 +1,280 @@
+// Copyright 2015 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_SERVICE_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_SERVICE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/containers/span.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/sequence_checker.h"
+#include "chrome/browser/certificate_provider/certificate_info.h"
+#include "chrome/browser/certificate_provider/certificate_requests.h"
+#include "chrome/browser/certificate_provider/pin_dialog_manager.h"
+#include "chrome/browser/certificate_provider/sign_requests.h"
+#include "chrome/browser/certificate_provider/thread_safe_certificate_map.h"
+#include "components/account_id/account_id.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "net/cert/x509_certificate.h"
+#include "net/ssl/client_cert_identity.h"
+#include "net/ssl/ssl_private_key.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace chromeos {
+
+class CertificateProvider;
+
+// A keyed service that manages registrations of extensions as certificate
+// providers. It exposes all certificates that are provided by extensions
+// through a |CertificateProvider| object that can be created using
+// |CreateCertificateProvider()|. Private key handles are exposed through
+// net::ClientKeyStore. Sign operations are routed to the extension that exposed
+// the certificate.
+//
+// The typical order of execution is as follows:
+//  1. HTTPS server requests client certs or
+//     chrome.platformKeys.selectClientCertificates is called.
+//  2. This starts the certificate request with ID x.
+//  3. All extensions registered for the event onClientCertificatesRequested are
+//     notified, the exposed callback is bound to request ID x.
+//  4. Wait for all extensions to reply to request with ID x
+//     or time out.
+//  5. Filter all certificates from extensions that replied to request with ID x
+//     and from the platform.
+//  6. Show the selection dialog, user will select one.
+//  7. Create private key handle. As this call is not associated with a specific
+//     certificate request it looks at the certificate list obtained by the most
+//     recent certificate request (execution of 3-5), which may or may not have
+//     had ID x.
+//  8. Sign() function of the key handle is called.
+//  9. Forward the sign request to the extension that registered the
+//     certificate. This request has a new sign request ID y.
+// 10. Wait until the extension replies with the signature or fails the sign
+//     request with ID y.
+// 11. Forward the signature or failure as result of the key handle's Sign()
+//     function.
+class CertificateProviderService : public KeyedService {
+ public:
+  using CertificateInfo = certificate_provider::CertificateInfo;
+  using CertificateInfoList = certificate_provider::CertificateInfoList;
+
+  class Delegate {
+   public:
+    Delegate() {}
+
+    Delegate(const Delegate&) = delete;
+    Delegate& operator=(const Delegate&) = delete;
+
+    virtual ~Delegate() {}
+
+    // Returns the ids of the extensions that want to provide certificates and
+    // therefore want to be notified about certificate requests. This is called
+    // once per client certificate request by the net layer.
+    virtual std::vector<std::string> CertificateProviderExtensions() = 0;
+
+    // Broadcasts a certificate request with |cert_request_id| to all
+    // certificate provider extensions.
+    virtual void BroadcastCertificateRequest(int cert_request_id) = 0;
+
+    // Dispatches a sign request with the given arguments to the extension with
+    // id |extension_id|. |algorithm| is a TLS 1.3 SignatureScheme value. See
+    // net::SSLPrivateKey for details. Returns whether that extension is
+    // actually a listener for that event.
+    virtual bool DispatchSignRequestToExtension(
+        const std::string& extension_id,
+        int sign_request_id,
+        uint16_t algorithm,
+        const scoped_refptr<net::X509Certificate>& certificate,
+        base::span<const uint8_t> input) = 0;
+  };
+
+  class Observer : public base::CheckedObserver {
+   public:
+    // Called when an extension updates the certificates it provides.
+    virtual void OnCertificatesUpdated(
+        const std::string& extension_id,
+        const CertificateInfoList& certificate_infos) {}
+
+    // Called when a sign request gets successfully completed.
+    virtual void OnSignCompleted(
+        const scoped_refptr<net::X509Certificate>& certificate,
+        const std::string& extension_id) {}
+  };
+
+  // |SetDelegate| must be called exactly once directly after construction.
+  CertificateProviderService();
+  CertificateProviderService(const CertificateProviderService&) = delete;
+  CertificateProviderService& operator=(const CertificateProviderService&) =
+      delete;
+  ~CertificateProviderService() override;
+
+  // Must be called exactly once after construction and before other methods are
+  // called. The delegate will be destroyed in the destructor of the service and
+  // not before, which allows to unregister observers (e.g. for
+  // OnExtensionUnloaded) in the delegate's destructor on behalf of the service.
+  void SetDelegate(std::unique_ptr<Delegate> delegate);
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // Updates the certificates provided by the extension with |extension_id| to
+  // be |certificates_infos|.
+  void SetCertificatesProvidedByExtension(
+      const std::string& extension_id,
+      const CertificateInfoList& certificate_infos);
+
+  // Must be called when an extension replied to a previous certificate
+  // request, after the new certificates were registered with
+  // SetCertificatesProvidedByExtension(). For each request, it is expected that
+  // every registered extension replies exactly once. |cert_request_id| must
+  // refer to a previously broadcast certificate request. Returns false if the
+  // request id is unknown or it was called before with the same combination of
+  // request id and extension id. E.g. the request could have timed out before
+  // an extension replies.
+  bool SetExtensionCertificateReplyReceived(const std::string& extension_id,
+                                            int cert_request_id);
+
+  // Must be called with the reply of an extension to a previous sign request.
+  // |sign_request_id| is provided in the reply of the extension and must refer
+  // to a previous sign request. The extension id must be provided, because
+  // not the sign request id alone but only the pair (extension id, sign request
+  // id) is unambiguous.
+  // If the signature could be calculated by the extension, |signature| is
+  // provided in the reply and should be the signature of the data sent in the
+  // sign request. Otherwise, in case of a failure, |signature| must be empty.
+  // Returns false if |sign_request_id| is not referring to a pending request.
+  bool ReplyToSignRequest(const std::string& extension_id,
+                          int sign_request_id,
+                          const std::vector<uint8_t>& signature);
+
+  // Returns whether this certificate was provided by any extension during the
+  // lifetime of this service. If this certificate is currently provided by an
+  // extension, sets |is_currently_provided| to true and |extension_id| to that
+  // extension's id. If this certificate was provided before but not anymore,
+  // |is_currently_provided| will be set to false and |extension_id| will not be
+  // modified.
+  bool LookUpCertificate(const net::X509Certificate& cert,
+                         bool* is_currently_provided,
+                         std::string* extension_id);
+
+  // Returns a CertificateProvider that always returns the latest list of
+  // certificates that are provided by all registered extensions. Therefore, it
+  // is sufficient to create the CertificateProvider once and then repeatedly
+  // call its |GetCertificates()|. The returned provider is valid even after the
+  // destruction of this service.
+  std::unique_ptr<CertificateProvider> CreateCertificateProvider();
+
+  // Called whenever the extension with id |extension_id| unregisters from
+  // receiving future certificate requests. This will clear certificates
+  // currently provided by the extension.
+  void OnExtensionUnregistered(const std::string& extension_id);
+
+  // Must be called if extension with id |extension_id| is unloaded and cannot
+  // serve certificates anymore. This should be called everytime the
+  // corresponding notification of the ExtensionRegistry is triggered.
+  void OnExtensionUnloaded(const std::string& extension_id);
+
+  // Requests the extension which provided the certificate identified by
+  // |subject_public_key_info| to sign the unhashed |input| with the
+  // corresponding private key. |algorithm| is a TLS 1.3 SignatureScheme value.
+  // See net::SSLPrivateKey for details. |callback| will be run with the reply
+  // of the extension or an error.
+  void RequestSignatureBySpki(
+      const std::string& subject_public_key_info,
+      uint16_t algorithm,
+      base::span<const uint8_t> input,
+      const absl::optional<AccountId>& authenticating_user_account_id,
+      net::SSLPrivateKey::SignCallback callback);
+
+  // Looks up the certificate identified by |subject_public_key_info|. If any
+  // extension is currently providing such a certificate, fills |extension_id|,
+  // fills *|supported_algorithms| with the algorithms supported for that
+  // certificate, and returns true. Values used for |supported_algorithms| are
+  // TLS 1.3 SignatureSchemes. See net::SSLPrivateKey for details. If no
+  // extension is currently providing such a certificate, returns false.
+  bool LookUpSpki(const std::string& subject_public_key_info,
+                  std::vector<uint16_t>* supported_algorithms,
+                  std::string* extension_id);
+
+  // Aborts all signature requests and related PIN dialogs that are associated
+  // with the authentication of the given user.
+  void AbortSignatureRequestsForAuthenticatingUser(
+      const AccountId& authenticating_user_account_id);
+
+  PinDialogManager* pin_dialog_manager() { return &pin_dialog_manager_; }
+
+ private:
+  class ClientCertIdentity;
+  class CertificateProviderImpl;
+  class SSLPrivateKey;
+
+  // Requests the current list of certificates from every registered extension.
+  // Once all extensions replied or a timeout was reached, the internal
+  // |extension_to_certificates_| is updated and |callback| is run with the
+  // retrieved list of certificates.
+  void GetCertificatesFromExtensions(
+      base::OnceCallback<void(net::ClientCertIdentityList)> callback);
+
+  // Collects all currently available certificates and passes them to
+  // |callback|.
+  void CollectCertificatesAndRun(
+      base::OnceCallback<void(net::ClientCertIdentityList)> callback);
+
+  // Terminates the certificate request with id |cert_request_id| by ignoring
+  // pending replies from extensions. Certificates that were already reported
+  // are processed.
+  void TerminateCertificateRequest(int cert_request_id);
+
+  // Requests extension with |extension_id| to sign the unhashed |input| with
+  // the private key certified by |certificate|. |algorithm| is a TLS 1.3
+  // SignatureScheme value. See net::SSLPrivateKey for details. |callback| will
+  // be run with the reply of the extension or an error.
+  void RequestSignatureFromExtension(
+      const std::string& extension_id,
+      const scoped_refptr<net::X509Certificate>& certificate,
+      uint16_t algorithm,
+      base::span<const uint8_t> input,
+      const absl::optional<AccountId>& authenticating_user_account_id,
+      net::SSLPrivateKey::SignCallback callback);
+
+  std::unique_ptr<Delegate> delegate_;
+
+  base::ObserverList<Observer> observers_;
+
+  // The object to manage the dialog displayed when requestPin is called by the
+  // extension.
+  PinDialogManager pin_dialog_manager_;
+
+  // State about all pending sign requests.
+  certificate_provider::SignRequests sign_requests_;
+
+  // Contains all pending certificate requests.
+  certificate_provider::CertificateRequests certificate_requests_;
+
+  // Contains all certificates that the extensions returned during the lifetime
+  // of this service. Each certificate is associated with the extension that
+  // reported the certificate in response to the most recent certificate
+  // request. If a certificate was reported previously but in the most recent
+  // responses, it is still cached but not loses it's association with any
+  // extension. This ensures that a certificate can't magically appear as
+  // platform certificate (e.g. in the client certificate selection dialog)
+  // after an extension doesn't report it anymore.
+  certificate_provider::ThreadSafeCertificateMap certificate_map_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<CertificateProviderService> weak_factory_{this};
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_SERVICE_H_
diff --git a/chrome/browser/certificate_provider/certificate_provider_service_factory.cc b/chrome/browser/certificate_provider/certificate_provider_service_factory.cc
new file mode 100644
index 0000000..cce0016
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_provider_service_factory.cc
@@ -0,0 +1,352 @@
+// Copyright 2015 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 "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "base/containers/span.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/values.h"
+#include "chrome/browser/certificate_provider/certificate_provider_service.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/common/extensions/api/certificate_provider.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "extensions/browser/event_listener_map.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/event_router_factory.h"
+#include "extensions/browser/extension_event_histogram_value.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_factory.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/common/extension.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/ssl/ssl_private_key.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+
+namespace chromeos {
+
+namespace {
+
+namespace api_cp = extensions::api::certificate_provider;
+
+class DefaultDelegate : public CertificateProviderService::Delegate,
+                        public extensions::EventRouter::Observer,
+                        public extensions::ExtensionRegistryObserver {
+ public:
+  // |event_router| may be null in tests.
+  DefaultDelegate(CertificateProviderService* service,
+                  extensions::ExtensionRegistry* registry,
+                  extensions::EventRouter* event_router);
+  DefaultDelegate(const DefaultDelegate&) = delete;
+  DefaultDelegate& operator=(const DefaultDelegate&) = delete;
+  ~DefaultDelegate() override;
+
+  // CertificateProviderService::Delegate:
+  std::vector<std::string> CertificateProviderExtensions() override;
+  void BroadcastCertificateRequest(int request_id) override;
+  bool DispatchSignRequestToExtension(
+      const std::string& extension_id,
+      int request_id,
+      uint16_t algorithm,
+      const scoped_refptr<net::X509Certificate>& certificate,
+      base::span<const uint8_t> input) override;
+
+  // extensions::EventRouter::Observer:
+  void OnListenerAdded(const extensions::EventListenerInfo& details) override {}
+  void OnListenerRemoved(const extensions::EventListenerInfo& details) override;
+
+  // extensions::ExtensionRegistryObserver:
+  void OnExtensionUnloaded(content::BrowserContext* browser_context,
+                           const extensions::Extension* extension,
+                           extensions::UnloadedExtensionReason reason) override;
+
+ private:
+  // Returns extension IDs that currently have event listeners for the given
+  // event,
+  base::flat_set<std::string> GetSubscribedExtensions(
+      const std::string& event_name);
+
+  CertificateProviderService* const service_;
+  extensions::ExtensionRegistry* const registry_;
+  extensions::EventRouter* const event_router_;
+};
+
+// Constructs the "onCertificatesUpdateRequested" event.
+std::unique_ptr<extensions::Event> BuildOnCertificatesUpdateRequestedEvent(
+    int request_id) {
+  api_cp::CertificatesUpdateRequest certificates_update_request;
+  certificates_update_request.certificates_request_id = request_id;
+  std::vector<base::Value> event_args;
+  event_args.push_back(
+      base::Value::FromUniquePtrValue(certificates_update_request.ToValue()));
+  return std::make_unique<extensions::Event>(
+      extensions::events::CERTIFICATEPROVIDER_ON_CERTIFICATES_UPDATE_REQUESTED,
+      api_cp::OnCertificatesUpdateRequested::kEventName, std::move(event_args));
+}
+
+// Constructs the legacy "onCertificatesRequested" event.
+std::unique_ptr<extensions::Event> BuildOnCertificatesRequestedEvent(
+    int request_id) {
+  std::vector<base::Value> event_args;
+  event_args.push_back(base::Value(request_id));
+  return std::make_unique<extensions::Event>(
+      extensions::events::CERTIFICATEPROVIDER_ON_CERTIFICATES_REQUESTED,
+      api_cp::OnCertificatesRequested::kEventName, std::move(event_args));
+}
+
+// Constructs the "onSignatureRequested" event.
+std::unique_ptr<extensions::Event> BuildOnSignatureRequestedEvent(
+    int request_id,
+    uint16_t algorithm,
+    const net::X509Certificate& certificate,
+    base::span<const uint8_t> input) {
+  api_cp::SignatureRequest request;
+  request.sign_request_id = request_id;
+  switch (algorithm) {
+    case SSL_SIGN_RSA_PKCS1_MD5_SHA1:
+      request.algorithm = api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_MD5_SHA1;
+      break;
+    case SSL_SIGN_RSA_PKCS1_SHA1:
+      request.algorithm = api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_SHA1;
+      break;
+    case SSL_SIGN_RSA_PKCS1_SHA256:
+      request.algorithm = api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_SHA256;
+      break;
+    case SSL_SIGN_RSA_PKCS1_SHA384:
+      request.algorithm = api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_SHA384;
+      break;
+    case SSL_SIGN_RSA_PKCS1_SHA512:
+      request.algorithm = api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_SHA512;
+      break;
+    case SSL_SIGN_RSA_PSS_RSAE_SHA256:
+      request.algorithm = api_cp::ALGORITHM_RSASSA_PSS_SHA256;
+      break;
+    case SSL_SIGN_RSA_PSS_RSAE_SHA384:
+      request.algorithm = api_cp::ALGORITHM_RSASSA_PSS_SHA384;
+      break;
+    case SSL_SIGN_RSA_PSS_RSAE_SHA512:
+      request.algorithm = api_cp::ALGORITHM_RSASSA_PSS_SHA512;
+      break;
+    default:
+      LOG(ERROR) << "Unknown signature algorithm";
+      return nullptr;
+  }
+  request.input.assign(input.begin(), input.end());
+  base::StringPiece cert_der =
+      net::x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer());
+  request.certificate.assign(cert_der.begin(), cert_der.end());
+
+  std::vector<base::Value> event_args;
+  event_args.push_back(base::Value::FromUniquePtrValue(request.ToValue()));
+
+  return std::make_unique<extensions::Event>(
+      extensions::events::CERTIFICATEPROVIDER_ON_SIGNATURE_REQUESTED,
+      api_cp::OnSignatureRequested::kEventName, std::move(event_args));
+}
+
+// Constructs the legacy "onSignDigestRequested" event.
+std::unique_ptr<extensions::Event> BuildOnSignDigestRequestedEvent(
+    int request_id,
+    uint16_t algorithm,
+    const net::X509Certificate& certificate,
+    base::span<const uint8_t> input) {
+  api_cp::SignRequest request;
+
+  request.sign_request_id = request_id;
+  switch (algorithm) {
+    case SSL_SIGN_RSA_PKCS1_MD5_SHA1:
+      request.hash = api_cp::HASH_MD5_SHA1;
+      break;
+    case SSL_SIGN_RSA_PKCS1_SHA1:
+      request.hash = api_cp::HASH_SHA1;
+      break;
+    case SSL_SIGN_RSA_PKCS1_SHA256:
+      request.hash = api_cp::HASH_SHA256;
+      break;
+    case SSL_SIGN_RSA_PKCS1_SHA384:
+      request.hash = api_cp::HASH_SHA384;
+      break;
+    case SSL_SIGN_RSA_PKCS1_SHA512:
+      request.hash = api_cp::HASH_SHA512;
+      break;
+    default:
+      LOG(ERROR) << "Unknown signature algorithm";
+      return nullptr;
+  }
+  base::StringPiece cert_der =
+      net::x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer());
+  request.certificate.assign(cert_der.begin(), cert_der.end());
+
+  // The extension expects the input to be hashed ahead of time.
+  request.digest.resize(EVP_MAX_MD_SIZE);
+  const EVP_MD* md = SSL_get_signature_algorithm_digest(algorithm);
+  unsigned digest_len;
+  if (!md || !EVP_Digest(input.data(), input.size(), request.digest.data(),
+                         &digest_len, md, /*ENGINE *impl=*/nullptr)) {
+    return nullptr;
+  }
+  request.digest.resize(digest_len);
+
+  std::vector<base::Value> event_args;
+  event_args.push_back(base::Value(request_id));
+  event_args.push_back(base::Value::FromUniquePtrValue(request.ToValue()));
+
+  return std::make_unique<extensions::Event>(
+      extensions::events::CERTIFICATEPROVIDER_ON_SIGN_DIGEST_REQUESTED,
+      api_cp::OnSignDigestRequested::kEventName, std::move(event_args));
+}
+
+DefaultDelegate::DefaultDelegate(CertificateProviderService* service,
+                                 extensions::ExtensionRegistry* registry,
+                                 extensions::EventRouter* event_router)
+    : service_(service), registry_(registry), event_router_(event_router) {
+  DCHECK(service_);
+  registry_->AddObserver(this);
+  event_router_->RegisterObserver(
+      this, api_cp::OnCertificatesUpdateRequested::kEventName);
+  event_router_->RegisterObserver(this,
+                                  api_cp::OnCertificatesRequested::kEventName);
+}
+
+DefaultDelegate::~DefaultDelegate() {
+  event_router_->UnregisterObserver(this);
+  registry_->RemoveObserver(this);
+}
+
+std::vector<std::string> DefaultDelegate::CertificateProviderExtensions() {
+  base::flat_set<std::string> ids = GetSubscribedExtensions(
+      api_cp::OnCertificatesUpdateRequested::kEventName);
+  const base::flat_set<std::string> legacy_ids =
+      GetSubscribedExtensions(api_cp::OnCertificatesRequested::kEventName);
+  ids.insert(legacy_ids.begin(), legacy_ids.end());
+  return std::vector<std::string>(ids.begin(), ids.end());
+}
+
+void DefaultDelegate::BroadcastCertificateRequest(int request_id) {
+  // First, broadcast the event to the extensions that use the up-to-date
+  // version of the API.
+  const auto up_to_date_api_extension_ids = GetSubscribedExtensions(
+      api_cp::OnCertificatesUpdateRequested::kEventName);
+  for (const std::string& extension_id : up_to_date_api_extension_ids) {
+    event_router_->DispatchEventToExtension(
+        extension_id, BuildOnCertificatesUpdateRequestedEvent(request_id));
+  }
+  // Second, broadcast the event to the extensions that only listen for the
+  // legacy event.
+  for (const std::string& extension_id :
+       GetSubscribedExtensions(api_cp::OnCertificatesRequested::kEventName)) {
+    if (up_to_date_api_extension_ids.contains(extension_id))
+      continue;
+    event_router_->DispatchEventToExtension(
+        extension_id, BuildOnCertificatesRequestedEvent(request_id));
+  }
+}
+
+bool DefaultDelegate::DispatchSignRequestToExtension(
+    const std::string& extension_id,
+    int request_id,
+    uint16_t algorithm,
+    const scoped_refptr<net::X509Certificate>& certificate,
+    base::span<const uint8_t> input) {
+  DCHECK(certificate);
+  std::unique_ptr<extensions::Event> event;
+  // Send the up-to-date version of the event, and fall back to the legacy event
+  // if the extension is only listening for that one.
+  if (event_router_->ExtensionHasEventListener(
+          extension_id, api_cp::OnSignatureRequested::kEventName)) {
+    event = BuildOnSignatureRequestedEvent(request_id, algorithm, *certificate,
+                                           input);
+  } else if (event_router_->ExtensionHasEventListener(
+                 extension_id, api_cp::OnSignDigestRequested::kEventName)) {
+    event = BuildOnSignDigestRequestedEvent(request_id, algorithm, *certificate,
+                                            input);
+  }
+  if (!event)
+    return false;
+  event_router_->DispatchEventToExtension(extension_id, std::move(event));
+  return true;
+}
+
+void DefaultDelegate::OnListenerRemoved(
+    const extensions::EventListenerInfo& details) {
+  if (!event_router_->ExtensionHasEventListener(
+          details.extension_id,
+          api_cp::OnCertificatesUpdateRequested::kEventName) &&
+      !event_router_->ExtensionHasEventListener(
+          details.extension_id, api_cp::OnCertificatesRequested::kEventName)) {
+    service_->OnExtensionUnregistered(details.extension_id);
+  }
+}
+
+void DefaultDelegate::OnExtensionUnloaded(
+    content::BrowserContext* browser_context,
+    const extensions::Extension* extension,
+    extensions::UnloadedExtensionReason reason) {
+  service_->OnExtensionUnloaded(extension->id());
+}
+
+base::flat_set<std::string> DefaultDelegate::GetSubscribedExtensions(
+    const std::string& event_name) {
+  std::vector<std::string> ids;
+  for (const std::unique_ptr<extensions::EventListener>& listener :
+       event_router_->listeners().GetEventListenersByName(event_name)) {
+    ids.push_back(listener->extension_id());
+  }
+  return base::flat_set<std::string>(ids.begin(), ids.end());
+}
+
+}  // namespace
+
+// static
+CertificateProviderService*
+CertificateProviderServiceFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<CertificateProviderService*>(
+      GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+// static
+CertificateProviderServiceFactory*
+CertificateProviderServiceFactory::GetInstance() {
+  return base::Singleton<CertificateProviderServiceFactory>::get();
+}
+
+CertificateProviderServiceFactory::CertificateProviderServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "CertificateProviderService",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(extensions::EventRouterFactory::GetInstance());
+  DependsOn(extensions::ExtensionRegistryFactory::GetInstance());
+}
+
+content::BrowserContext*
+CertificateProviderServiceFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextRedirectedInIncognito(context);
+}
+
+bool CertificateProviderServiceFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
+KeyedService* CertificateProviderServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  CertificateProviderService* const service = new CertificateProviderService();
+  service->SetDelegate(std::make_unique<DefaultDelegate>(
+      service,
+      extensions::ExtensionRegistryFactory::GetForBrowserContext(context),
+      extensions::EventRouterFactory::GetForBrowserContext(context)));
+  return service;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/certificate_provider_service_factory.h b/chrome/browser/certificate_provider/certificate_provider_service_factory.h
new file mode 100644
index 0000000..afdce85
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_provider_service_factory.h
@@ -0,0 +1,52 @@
+// Copyright 2015 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_SERVICE_FACTORY_H_
+
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace content {
+class BrowserContext;
+}
+
+namespace chromeos {
+
+class CertificateProviderService;
+
+// Factory to create CertificateProviderService.
+class CertificateProviderServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static CertificateProviderService* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  static CertificateProviderServiceFactory* GetInstance();
+
+  CertificateProviderServiceFactory(const CertificateProviderServiceFactory&) =
+      delete;
+  CertificateProviderServiceFactory& operator=(
+      const CertificateProviderServiceFactory&) = delete;
+
+ private:
+  friend struct base::DefaultSingletonTraits<CertificateProviderServiceFactory>;
+
+  CertificateProviderServiceFactory();
+
+  // BrowserContextKeyedServiceFactory:
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+  bool ServiceIsNULLWhileTesting() const override;
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_PROVIDER_SERVICE_FACTORY_H_
diff --git a/chrome/browser/certificate_provider/certificate_provider_service_unittest.cc b/chrome/browser/certificate_provider/certificate_provider_service_unittest.cc
new file mode 100644
index 0000000..a5582187
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_provider_service_unittest.cc
@@ -0,0 +1,617 @@
+// Copyright 2015 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 "chrome/browser/certificate_provider/certificate_provider_service.h"
+
+#include <stdint.h>
+#include <set>
+#include <utility>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/containers/span.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_piece.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/certificate_provider/certificate_provider.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_data_directory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+
+namespace chromeos {
+
+namespace {
+
+const char kExtension1[] = "extension1";
+const char kExtension2[] = "extension2";
+
+void ExpectEmptySignatureAndStoreError(net::Error* out_error,
+                                       net::Error error,
+                                       const std::vector<uint8_t>& signature) {
+  EXPECT_TRUE(signature.empty());
+  *out_error = error;
+}
+
+void ExpectOKAndStoreSignature(std::vector<uint8_t>* out_signature,
+                               net::Error error,
+                               const std::vector<uint8_t>& signature) {
+  EXPECT_EQ(net::OK, error);
+  *out_signature = signature;
+}
+
+void StoreCertificates(net::ClientCertIdentityList* out_certs,
+                       net::ClientCertIdentityList certs) {
+  if (out_certs)
+    *out_certs = std::move(certs);
+}
+
+void StorePrivateKey(scoped_refptr<net::SSLPrivateKey>* out_key,
+                     scoped_refptr<net::SSLPrivateKey> in_key) {
+  *out_key = std::move(in_key);
+}
+
+certificate_provider::CertificateInfo CreateCertInfo(
+    const std::string& cert_filename) {
+  certificate_provider::CertificateInfo cert_info;
+  cert_info.certificate =
+      net::ImportCertFromFile(net::GetTestCertsDirectory(), cert_filename);
+  EXPECT_TRUE(cert_info.certificate) << "Could not load " << cert_filename;
+  cert_info.supported_algorithms.push_back(SSL_SIGN_RSA_PKCS1_SHA256);
+
+  return cert_info;
+}
+
+bool IsKeyEqualToCertInfo(const certificate_provider::CertificateInfo& info,
+                          net::SSLPrivateKey* key) {
+  return info.supported_algorithms == key->GetAlgorithmPreferences();
+}
+
+bool ClientCertIdentityAlphabeticSorter(
+    const std::unique_ptr<net::ClientCertIdentity>& a_identity,
+    const std::unique_ptr<net::ClientCertIdentity>& b_identity) {
+  return a_identity->certificate()->subject().GetDisplayName() <
+         b_identity->certificate()->subject().GetDisplayName();
+}
+
+class TestDelegate : public CertificateProviderService::Delegate {
+ public:
+  enum class RequestType { NONE, SIGN, GET_CERTIFICATES };
+
+  TestDelegate() {}
+  TestDelegate(const TestDelegate&) = delete;
+  TestDelegate& operator=(const TestDelegate&) = delete;
+
+  std::vector<std::string> CertificateProviderExtensions() override {
+    return std::vector<std::string>(provider_extensions_.begin(),
+                                    provider_extensions_.end());
+  }
+
+  void BroadcastCertificateRequest(int cert_request_id) override {
+    EXPECT_EQ(expected_request_type_, RequestType::GET_CERTIFICATES);
+    last_cert_request_id_ = cert_request_id;
+    expected_request_type_ = RequestType::NONE;
+  }
+
+  bool DispatchSignRequestToExtension(
+      const std::string& extension_id,
+      int sign_request_id,
+      uint16_t algorithm,
+      const scoped_refptr<net::X509Certificate>& certificate,
+      base::span<const uint8_t> input) override {
+    EXPECT_EQ(expected_request_type_, RequestType::SIGN);
+    last_sign_request_id_ = sign_request_id;
+    last_extension_id_ = extension_id;
+    last_certificate_ = certificate;
+    expected_request_type_ = RequestType::NONE;
+    return true;
+  }
+
+  // Prepares this delegate for the dispatch of a request of type
+  // |expected_request_type|. The first request of the right type will cause
+  // |expected_request_type_| to be reset to NONE. The request's arguments will
+  // be stored in |last_*_request_id_| and |last_extension_id_|. Any additional
+  // request and any request of the wrong type will fail the test.
+  void ClearAndExpectRequest(RequestType expected_request_type) {
+    last_extension_id_.clear();
+    last_sign_request_id_ = -1;
+    last_cert_request_id_ = -1;
+    last_certificate_ = nullptr;
+    expected_request_type_ = expected_request_type;
+  }
+
+  int last_sign_request_id_ = -1;
+  int last_cert_request_id_ = -1;
+  scoped_refptr<net::X509Certificate> last_certificate_;
+  std::string last_extension_id_;
+  std::set<std::string> provider_extensions_;
+  RequestType expected_request_type_ = RequestType::NONE;
+};
+
+class MockObserver : public CertificateProviderService::Observer {
+ public:
+  MOCK_METHOD2(
+      OnCertificatesUpdated,
+      void(const std::string& extension_id,
+           const certificate_provider::CertificateInfoList& certificate_infos));
+  MOCK_METHOD2(OnSignCompleted,
+               void(const scoped_refptr<net::X509Certificate>& certificate,
+                    const std::string& extension_id));
+};
+
+}  // namespace
+
+class CertificateProviderServiceTest : public testing::Test {
+ public:
+  CertificateProviderServiceTest()
+      : task_runner_(new base::TestMockTimeTaskRunner()),
+        task_runner_handle_(task_runner_),
+        service_(new CertificateProviderService()),
+        cert_info1_(CreateCertInfo("client_1.pem")),
+        cert_info2_(CreateCertInfo("client_2.pem")) {
+    std::unique_ptr<TestDelegate> test_delegate(new TestDelegate);
+    test_delegate_ = test_delegate.get();
+    service_->SetDelegate(std::move(test_delegate));
+
+    service_->AddObserver(&observer_);
+
+    certificate_provider_ = service_->CreateCertificateProvider();
+    EXPECT_TRUE(certificate_provider_);
+
+    test_delegate_->provider_extensions_.insert(kExtension1);
+  }
+
+  CertificateProviderServiceTest(const CertificateProviderServiceTest&) =
+      delete;
+  CertificateProviderServiceTest& operator=(
+      const CertificateProviderServiceTest&) = delete;
+
+  // Triggers a GetCertificates request and returns the request id. Assumes that
+  // at least one extension is registered as a certificate provider.
+  int RequestCertificatesFromExtensions(net::ClientCertIdentityList* certs) {
+    test_delegate_->ClearAndExpectRequest(
+        TestDelegate::RequestType::GET_CERTIFICATES);
+
+    certificate_provider_->GetCertificates(
+        base::BindOnce(&StoreCertificates, certs));
+
+    task_runner_->RunUntilIdle();
+    EXPECT_EQ(TestDelegate::RequestType::NONE,
+              test_delegate_->expected_request_type_);
+    return test_delegate_->last_cert_request_id_;
+  }
+
+  scoped_refptr<net::SSLPrivateKey> FetchIdentityPrivateKey(
+      net::ClientCertIdentity* identity) {
+    scoped_refptr<net::SSLPrivateKey> ssl_private_key;
+    identity->AcquirePrivateKey(
+        base::BindOnce(StorePrivateKey, &ssl_private_key));
+    task_runner_->RunUntilIdle();
+    return ssl_private_key;
+  }
+
+  // Provides |cert_info1_| through kExtension1.
+  std::unique_ptr<net::ClientCertIdentity> ProvideDefaultCert() {
+    net::ClientCertIdentityList certs;
+    const int cert_request_id = RequestCertificatesFromExtensions(&certs);
+    SetCertificateProvidedByExtension(kExtension1, cert_request_id,
+                                      cert_info1_);
+    task_runner_->RunUntilIdle();
+    if (certs.empty())
+      return nullptr;
+    return std::move(certs[0]);
+  }
+
+  // Like service_->SetCertificatesProvidedByExtension but taking a single
+  // CertificateInfo instead of a list.
+  void SetCertificateProvidedByExtension(
+      const std::string& extension_id,
+      int cert_request_id,
+      const certificate_provider::CertificateInfo& cert_info) {
+    certificate_provider::CertificateInfoList infos;
+    infos.push_back(cert_info);
+    EXPECT_CALL(observer_, OnCertificatesUpdated(extension_id, infos));
+    service_->SetCertificatesProvidedByExtension(extension_id, infos);
+    service_->SetExtensionCertificateReplyReceived(extension_id,
+                                                   cert_request_id);
+  }
+
+  bool CheckLookUpCertificate(
+      const certificate_provider::CertificateInfo& cert_info,
+      bool expected_is_certificate_known,
+      bool expected_is_currently_provided,
+      const std::string& expected_extension_id) {
+    bool is_currently_provided = !expected_is_currently_provided;
+    std::string extension_id;
+    if (expected_is_certificate_known !=
+        service_->LookUpCertificate(*cert_info.certificate,
+                                    &is_currently_provided, &extension_id)) {
+      LOG(ERROR) << "Wrong return value.";
+      return false;
+    }
+    if (expected_is_currently_provided != is_currently_provided) {
+      LOG(ERROR) << "Wrong |is_currently_provided|.";
+      return false;
+    }
+    if (expected_extension_id != extension_id) {
+      LOG(ERROR) << "Wrong extension id. Got " << extension_id;
+      return false;
+    }
+    return true;
+  }
+
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+  TestDelegate* test_delegate_ = nullptr;
+  testing::StrictMock<MockObserver> observer_;
+  std::unique_ptr<CertificateProvider> certificate_provider_;
+  std::unique_ptr<CertificateProviderService> service_;
+  const certificate_provider::CertificateInfo cert_info1_;
+  const certificate_provider::CertificateInfo cert_info2_;
+};
+
+TEST_F(CertificateProviderServiceTest, GetCertificates) {
+  test_delegate_->provider_extensions_.insert(kExtension2);
+
+  net::ClientCertIdentityList certs;
+  const int cert_request_id = RequestCertificatesFromExtensions(&certs);
+
+  task_runner_->RunUntilIdle();
+  // No certificates set until all registered extensions replied.
+  EXPECT_TRUE(certs.empty());
+
+  SetCertificateProvidedByExtension(kExtension1, cert_request_id, cert_info1_);
+
+  task_runner_->RunUntilIdle();
+  // No certificates set until all registered extensions replied.
+  EXPECT_TRUE(certs.empty());
+
+  SetCertificateProvidedByExtension(kExtension2, cert_request_id, cert_info2_);
+
+  task_runner_->RunUntilIdle();
+  ASSERT_EQ(2u, certs.size());
+
+  // Verify that the ClientCertIdentity returns key handles for the provided
+  // certs.
+  EXPECT_TRUE(FetchIdentityPrivateKey(certs[0].get()));
+  EXPECT_TRUE(FetchIdentityPrivateKey(certs[1].get()));
+
+  // Deregister the extensions as certificate providers. The next
+  // GetCertificates call must report an empty list of certs.
+  test_delegate_->provider_extensions_.clear();
+  service_->OnExtensionUnloaded(kExtension1);
+  service_->OnExtensionUnloaded(kExtension2);
+
+  // No request expected.
+  test_delegate_->ClearAndExpectRequest(TestDelegate::RequestType::NONE);
+
+  certificate_provider_->GetCertificates(
+      base::BindOnce(&StoreCertificates, &certs));
+
+  task_runner_->RunUntilIdle();
+  // As |certs| was not empty before, this ensures that StoreCertificates() was
+  // called.
+  EXPECT_TRUE(certs.empty());
+}
+
+TEST_F(CertificateProviderServiceTest, LookUpCertificate) {
+  // Provide only |cert_info1_|.
+  {
+    const int cert_request_id = RequestCertificatesFromExtensions(nullptr);
+    SetCertificateProvidedByExtension(kExtension1, cert_request_id,
+                                      cert_info1_);
+    task_runner_->RunUntilIdle();
+  }
+
+  EXPECT_TRUE(CheckLookUpCertificate(cert_info1_, true /* is known */,
+                                     true /* is currently provided */,
+                                     kExtension1));
+
+  EXPECT_TRUE(CheckLookUpCertificate(cert_info2_, false /* is not known */,
+                                     false /* is currently not provided */,
+                                     std::string()));
+
+  // Provide only |cert_info2_| from |kExtension2|.
+  test_delegate_->provider_extensions_.insert(kExtension2);
+  {
+    const int cert_request_id = RequestCertificatesFromExtensions(nullptr);
+    EXPECT_CALL(observer_,
+                OnCertificatesUpdated(
+                    kExtension1, certificate_provider::CertificateInfoList()));
+    service_->SetCertificatesProvidedByExtension(
+        kExtension1, certificate_provider::CertificateInfoList());
+    service_->SetExtensionCertificateReplyReceived(kExtension1,
+                                                   cert_request_id);
+    SetCertificateProvidedByExtension(kExtension2, cert_request_id,
+                                      cert_info2_);
+    task_runner_->RunUntilIdle();
+  }
+
+  EXPECT_TRUE(CheckLookUpCertificate(cert_info1_, true /* is known */,
+                                     false /* is currently not provided */,
+                                     std::string()));
+
+  EXPECT_TRUE(CheckLookUpCertificate(cert_info2_, true /* is known */,
+                                     true /* is currently provided */,
+                                     kExtension2));
+
+  // Deregister |kExtension2| as certificate provider and provide |cert_info1_|
+  // from |kExtension1|.
+  test_delegate_->provider_extensions_.erase(kExtension2);
+  service_->OnExtensionUnloaded(kExtension2);
+
+  {
+    const int cert_request_id = RequestCertificatesFromExtensions(nullptr);
+    SetCertificateProvidedByExtension(kExtension1, cert_request_id,
+                                      cert_info1_);
+    task_runner_->RunUntilIdle();
+  }
+
+  EXPECT_TRUE(CheckLookUpCertificate(cert_info1_, true /* is known */,
+                                     true /* is currently provided */,
+                                     kExtension1));
+
+  EXPECT_TRUE(CheckLookUpCertificate(cert_info2_, true /* is known */,
+                                     false /* is currently not provided */,
+                                     std::string()));
+
+  // Provide |cert_info2_| from |kExtension1|.
+  {
+    const int cert_request_id = RequestCertificatesFromExtensions(nullptr);
+    SetCertificateProvidedByExtension(kExtension1, cert_request_id,
+                                      cert_info2_);
+    task_runner_->RunUntilIdle();
+  }
+
+  {
+    bool is_currently_provided = true;
+    std::string extension_id;
+    // |cert_info1_.certificate| was provided before, so this must return true.
+    EXPECT_TRUE(service_->LookUpCertificate(
+        *cert_info1_.certificate, &is_currently_provided, &extension_id));
+    EXPECT_FALSE(is_currently_provided);
+    EXPECT_TRUE(extension_id.empty());
+  }
+
+  {
+    bool is_currently_provided = false;
+    std::string extension_id;
+    EXPECT_TRUE(service_->LookUpCertificate(
+        *cert_info2_.certificate, &is_currently_provided, &extension_id));
+    EXPECT_TRUE(is_currently_provided);
+    EXPECT_EQ(kExtension1, extension_id);
+  }
+
+  EXPECT_TRUE(CheckLookUpCertificate(cert_info1_, true /* is known */,
+                                     false /* is currently not provided */,
+                                     std::string()));
+
+  EXPECT_TRUE(CheckLookUpCertificate(cert_info2_, true /* is known */,
+                                     true /* is currently provided */,
+                                     kExtension1));
+}
+
+TEST_F(CertificateProviderServiceTest, GetCertificatesTimeout) {
+  test_delegate_->provider_extensions_.insert(kExtension2);
+
+  net::ClientCertIdentityList certs;
+  const int cert_request_id = RequestCertificatesFromExtensions(&certs);
+
+  certificate_provider::CertificateInfoList infos;
+  SetCertificateProvidedByExtension(kExtension1, cert_request_id, cert_info1_);
+
+  task_runner_->RunUntilIdle();
+  // No certificates set until all registered extensions replied or a timeout
+  // occurred.
+  EXPECT_TRUE(certs.empty());
+
+  task_runner_->FastForwardUntilNoTasksRemain();
+  // After the timeout, only extension1_'s certificates are returned.
+  // This verifies that the timeout delay is > 0 but not how long the delay is.
+  ASSERT_EQ(1u, certs.size());
+
+  EXPECT_TRUE(FetchIdentityPrivateKey(certs[0].get()));
+}
+
+TEST_F(CertificateProviderServiceTest, UnloadExtensionAfterGetCertificates) {
+  test_delegate_->provider_extensions_.insert(kExtension2);
+
+  net::ClientCertIdentityList certs;
+  const int cert_request_id = RequestCertificatesFromExtensions(&certs);
+
+  SetCertificateProvidedByExtension(kExtension1, cert_request_id, cert_info1_);
+  SetCertificateProvidedByExtension(kExtension2, cert_request_id, cert_info2_);
+  task_runner_->RunUntilIdle();
+
+  ASSERT_EQ(2u, certs.size());
+
+  // Sort the returned certs to ensure that the test results are stable.
+  std::sort(certs.begin(), certs.end(), ClientCertIdentityAlphabeticSorter);
+
+  // Private key handles for both certificates must be available now.
+  EXPECT_TRUE(FetchIdentityPrivateKey(certs[0].get()));
+  EXPECT_TRUE(FetchIdentityPrivateKey(certs[1].get()));
+
+  // Unload one of the extensions.
+  service_->OnExtensionUnloaded(kExtension2);
+
+  // extension1 isn't affected by the uninstall.
+  EXPECT_TRUE(FetchIdentityPrivateKey(certs[0].get()));
+  // No key handles that were backed by the uninstalled extension must be
+  // returned.
+  EXPECT_FALSE(FetchIdentityPrivateKey(certs[1].get()));
+}
+
+TEST_F(CertificateProviderServiceTest, DestroyServiceAfterGetCertificates) {
+  test_delegate_->provider_extensions_.insert(kExtension2);
+
+  net::ClientCertIdentityList certs;
+  const int cert_request_id = RequestCertificatesFromExtensions(&certs);
+
+  SetCertificateProvidedByExtension(kExtension1, cert_request_id, cert_info1_);
+  SetCertificateProvidedByExtension(kExtension2, cert_request_id, cert_info2_);
+  task_runner_->RunUntilIdle();
+
+  ASSERT_EQ(2u, certs.size());
+
+  // Destroy the service.
+  service_.reset();
+
+  // Private key handles for both certificates should return nullptr now.
+  EXPECT_FALSE(FetchIdentityPrivateKey(certs[0].get()));
+  EXPECT_FALSE(FetchIdentityPrivateKey(certs[1].get()));
+}
+
+TEST_F(CertificateProviderServiceTest, UnloadExtensionDuringGetCertificates) {
+  test_delegate_->provider_extensions_.insert(kExtension2);
+
+  net::ClientCertIdentityList certs;
+  const int cert_request_id = RequestCertificatesFromExtensions(&certs);
+
+  SetCertificateProvidedByExtension(kExtension1, cert_request_id, cert_info1_);
+
+  // The pending certificate request is only waiting for kExtension2. Unloading
+  // that extension must cause the request to be finished.
+  service_->OnExtensionUnloaded(kExtension2);
+
+  task_runner_->RunUntilIdle();
+  EXPECT_EQ(1u, certs.size());
+}
+
+// Trying to sign data using the exposed SSLPrivateKey must cause a sign
+// request. The reply must be correctly routed back to the private key.
+TEST_F(CertificateProviderServiceTest, SignRequest) {
+  std::unique_ptr<net::ClientCertIdentity> cert(ProvideDefaultCert());
+  ASSERT_TRUE(cert);
+
+  scoped_refptr<net::SSLPrivateKey> private_key(
+      FetchIdentityPrivateKey(cert.get()));
+
+  ASSERT_TRUE(private_key);
+  EXPECT_TRUE(IsKeyEqualToCertInfo(cert_info1_, private_key.get()));
+  EXPECT_NE(std::string::npos,
+            private_key->GetProviderName().find(kExtension1));
+
+  test_delegate_->ClearAndExpectRequest(TestDelegate::RequestType::SIGN);
+
+  std::string input = "any input data";
+  std::vector<uint8_t> received_signature;
+  private_key->Sign(
+      SSL_SIGN_RSA_PKCS1_SHA256,
+      std::vector<uint8_t>(input.begin(), input.end()),
+      base::BindOnce(&ExpectOKAndStoreSignature, &received_signature));
+
+  task_runner_->RunUntilIdle();
+
+  const int sign_request_id = test_delegate_->last_sign_request_id_;
+  EXPECT_EQ(TestDelegate::RequestType::NONE,
+            test_delegate_->expected_request_type_);
+  EXPECT_TRUE(cert_info1_.certificate->EqualsExcludingChain(
+      test_delegate_->last_certificate_.get()));
+
+  // No signature received until the extension replied to the service.
+  EXPECT_TRUE(received_signature.empty());
+
+  EXPECT_CALL(observer_, OnSignCompleted(cert_info1_.certificate, kExtension1));
+
+  std::vector<uint8_t> signature_reply;
+  signature_reply.push_back(5);
+  signature_reply.push_back(7);
+  signature_reply.push_back(8);
+  service_->ReplyToSignRequest(kExtension1, sign_request_id, signature_reply);
+
+  task_runner_->RunUntilIdle();
+  EXPECT_EQ(signature_reply, received_signature);
+}
+
+TEST_F(CertificateProviderServiceTest, UnloadExtensionDuringSign) {
+  std::unique_ptr<net::ClientCertIdentity> cert(ProvideDefaultCert());
+  ASSERT_TRUE(cert);
+
+  scoped_refptr<net::SSLPrivateKey> private_key(
+      FetchIdentityPrivateKey(cert.get()));
+  ASSERT_TRUE(private_key);
+
+  test_delegate_->ClearAndExpectRequest(TestDelegate::RequestType::SIGN);
+
+  std::string input = "any input data";
+  net::Error error = net::OK;
+  private_key->Sign(SSL_SIGN_RSA_PKCS1_SHA256,
+                    std::vector<uint8_t>(input.begin(), input.end()),
+                    base::BindOnce(&ExpectEmptySignatureAndStoreError, &error));
+
+  task_runner_->RunUntilIdle();
+
+  // No signature received until the extension replied to the service or is
+  // unloaded.
+  EXPECT_EQ(net::OK, error);
+
+  // Unload the extension.
+  service_->OnExtensionUnloaded(kExtension1);
+
+  task_runner_->RunUntilIdle();
+  EXPECT_EQ(net::ERR_FAILED, error);
+}
+
+// Try to sign data using key; using the Subject Public Key Info (SPKI) to
+// identify the key.
+TEST_F(CertificateProviderServiceTest, SignUsingSpkiAsIdentification) {
+  base::StringPiece client1_spki_piece;
+  ASSERT_TRUE(net::asn1::ExtractSPKIFromDERCert(
+      net::x509_util::CryptoBufferAsStringPiece(
+          cert_info1_.certificate->cert_buffer()),
+      &client1_spki_piece));
+  std::string client1_spki(client1_spki_piece);
+
+  std::unique_ptr<net::ClientCertIdentity> cert(ProvideDefaultCert());
+  ASSERT_TRUE(cert);
+
+  std::vector<uint16_t> supported_algorithms;
+  std::string extension_id;
+  // If this fails, try to regenerate kClient1SpkiBase64 using the command shown
+  // above.
+  EXPECT_TRUE(
+      service_->LookUpSpki(client1_spki, &supported_algorithms, &extension_id));
+  EXPECT_EQ(extension_id, kExtension1);
+  EXPECT_THAT(supported_algorithms,
+              testing::UnorderedElementsAre(SSL_SIGN_RSA_PKCS1_SHA256));
+
+  test_delegate_->ClearAndExpectRequest(TestDelegate::RequestType::SIGN);
+  std::vector<uint8_t> input{'d', 'a', 't', 'a'};
+  std::vector<uint8_t> received_signature;
+  service_->RequestSignatureBySpki(
+      client1_spki, SSL_SIGN_RSA_PKCS1_SHA256, input,
+      /*authenticating_user_account_id=*/{},
+      base::BindOnce(&ExpectOKAndStoreSignature, &received_signature));
+
+  task_runner_->RunUntilIdle();
+
+  const int sign_request_id = test_delegate_->last_sign_request_id_;
+  EXPECT_EQ(TestDelegate::RequestType::NONE,
+            test_delegate_->expected_request_type_);
+  EXPECT_TRUE(cert_info1_.certificate->EqualsExcludingChain(
+      test_delegate_->last_certificate_.get()));
+
+  // No signature received until the extension replied to the service.
+  EXPECT_TRUE(received_signature.empty());
+
+  EXPECT_CALL(observer_, OnSignCompleted(cert_info1_.certificate, kExtension1));
+
+  std::vector<uint8_t> signature_reply;
+  signature_reply.push_back(5);
+  signature_reply.push_back(7);
+  signature_reply.push_back(8);
+  service_->ReplyToSignRequest(kExtension1, sign_request_id, signature_reply);
+
+  task_runner_->RunUntilIdle();
+  EXPECT_EQ(signature_reply, received_signature);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/certificate_requests.cc b/chrome/browser/certificate_provider/certificate_requests.cc
new file mode 100644
index 0000000..dad3ab9
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_requests.cc
@@ -0,0 +1,111 @@
+// Copyright 2015 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 "chrome/browser/certificate_provider/certificate_requests.h"
+
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace chromeos {
+namespace certificate_provider {
+
+namespace {
+const int kGetCertificatesTimeoutInMinutes = 5;
+}  // namespace
+
+// Holds state for a single certificate request.
+struct CertificateRequests::CertificateRequestState {
+  CertificateRequestState() {}
+
+  ~CertificateRequestState() {}
+
+  // Extensions that are too slow are eventually dropped from a request.
+  base::OneShotTimer timeout;
+
+  // Extensions that this request is still waiting for.
+  std::set<std::string> pending_extensions;
+
+  // The callback that must be run with the final list of certificates.
+  base::OnceCallback<void(net::ClientCertIdentityList)> callback;
+};
+
+CertificateRequests::CertificateRequests() {}
+
+CertificateRequests::~CertificateRequests() {}
+
+int CertificateRequests::AddRequest(
+    const std::vector<std::string>& extension_ids,
+    base::OnceCallback<void(net::ClientCertIdentityList)> callback,
+    base::OnceCallback<void(int)> timeout_callback) {
+  auto state = std::make_unique<CertificateRequestState>();
+  state->callback = std::move(callback);
+  state->pending_extensions.insert(extension_ids.begin(), extension_ids.end());
+
+  const int request_id = next_free_request_id_++;
+  state->timeout.Start(FROM_HERE,
+                       base::Minutes(kGetCertificatesTimeoutInMinutes),
+                       base::BindOnce(std::move(timeout_callback), request_id));
+
+  const auto insert_result =
+      requests_.insert(std::make_pair(request_id, std::move(state)));
+  DCHECK(insert_result.second) << "request id already in use.";
+  return request_id;
+}
+
+bool CertificateRequests::SetExtensionReplyReceived(
+    const std::string& extension_id,
+    int request_id,
+    bool* completed) {
+  *completed = false;
+  const auto it = requests_.find(request_id);
+  if (it == requests_.end())
+    return false;
+
+  CertificateRequestState& state = *it->second;
+  if (state.pending_extensions.erase(extension_id) == 0)
+    return false;
+
+  *completed = state.pending_extensions.empty();
+  return true;
+}
+
+bool CertificateRequests::RemoveRequest(
+    int request_id,
+    base::OnceCallback<void(net::ClientCertIdentityList)>* callback) {
+  const auto it = requests_.find(request_id);
+  if (it == requests_.end())
+    return false;
+
+  CertificateRequestState& state = *it->second;
+  *callback = std::move(state.callback);
+  requests_.erase(it);
+  DVLOG(2) << "Completed certificate request " << request_id;
+  return true;
+}
+
+std::vector<int> CertificateRequests::DropExtension(
+    const std::string& extension_id) {
+  std::vector<int> completed_requests;
+  for (const auto& entry : requests_) {
+    DVLOG(2) << "Remove extension " << extension_id
+             << " from certificate request " << entry.first;
+
+    CertificateRequestState& state = *entry.second.get();
+    state.pending_extensions.erase(extension_id);
+    if (state.pending_extensions.empty())
+      completed_requests.push_back(entry.first);
+  }
+  return completed_requests;
+}
+
+}  // namespace certificate_provider
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/certificate_requests.h b/chrome/browser/certificate_provider/certificate_requests.h
new file mode 100644
index 0000000..86229423
--- /dev/null
+++ b/chrome/browser/certificate_provider/certificate_requests.h
@@ -0,0 +1,63 @@
+// Copyright 2015 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_REQUESTS_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_REQUESTS_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "net/ssl/client_cert_identity.h"
+
+namespace chromeos {
+namespace certificate_provider {
+
+class CertificateRequests {
+ public:
+  CertificateRequests();
+  CertificateRequests(const CertificateRequests&) = delete;
+  CertificateRequests& operator=(const CertificateRequests&) = delete;
+  ~CertificateRequests();
+
+  // Returns the id of the new request. |callback| will be stored with this
+  // request and be returned by RemoveRequest(). |timeout_callback| will be
+  // called with the request id if this request times out before
+  // SetExtensionReplyReceived() was called for all extensions in
+  // |extension_ids|.
+  int AddRequest(const std::vector<std::string>& extension_ids,
+                 base::OnceCallback<void(net::ClientCertIdentityList)> callback,
+                 base::OnceCallback<void(int)> timeout_callback);
+
+  // Returns whether this reply was expected, i.e. the request with |request_id|
+  // was waiting for a reply from this extension. If it was expected,
+  // |completed| is set to whether this request has no more pending replies.
+  // Otherwise |completed| will be set to false.
+  bool SetExtensionReplyReceived(const std::string& extension_id,
+                                 int request_id,
+                                 bool* completed);
+
+  // If this request is pending, sets |callback|, drops the request, and returns
+  // true. Otherwise returns false.
+  bool RemoveRequest(
+      int request_id,
+      base::OnceCallback<void(net::ClientCertIdentityList)>* callback);
+
+  // Removes this extension from all pending requests and returns the ids of
+  // all completed requests.
+  std::vector<int> DropExtension(const std::string& extension_id);
+
+ private:
+  struct CertificateRequestState;
+
+  std::map<int, std::unique_ptr<CertificateRequestState>> requests_;
+  int next_free_request_id_ = 0;
+};
+
+}  // namespace certificate_provider
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_CERTIFICATE_REQUESTS_H_
diff --git a/chrome/browser/certificate_provider/pin_dialog_manager.cc b/chrome/browser/certificate_provider/pin_dialog_manager.cc
new file mode 100644
index 0000000..0502fa2
--- /dev/null
+++ b/chrome/browser/certificate_provider/pin_dialog_manager.cc
@@ -0,0 +1,264 @@
+// Copyright 2016 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 "chrome/browser/certificate_provider/pin_dialog_manager.h"
+
+#include "base/bind.h"
+#include "base/containers/contains.h"
+#include "base/containers/cxx20_erase.h"
+#include "base/logging.h"
+
+namespace chromeos {
+
+// Define timeout for issued sign_request_id.
+constexpr base::TimeDelta kSignRequestIdTimeout = base::Minutes(10);
+
+PinDialogManager::PinDialogManager() = default;
+
+PinDialogManager::~PinDialogManager() = default;
+
+void PinDialogManager::AddSignRequestId(
+    const std::string& extension_id,
+    int sign_request_id,
+    const absl::optional<AccountId>& authenticating_user_account_id) {
+  ExtensionNameRequestIdPair key(extension_id, sign_request_id);
+  sign_requests_.insert(
+      std::make_pair(key, SignRequestState(/*begin_time=*/base::Time::Now(),
+                                           authenticating_user_account_id)));
+}
+
+void PinDialogManager::RemoveSignRequest(const std::string& extension_id,
+                                         int sign_request_id) {
+  if (active_dialog_state_ &&
+      active_dialog_state_->extension_id == extension_id &&
+      active_dialog_state_->sign_request_id == sign_request_id) {
+    CloseActiveDialog();
+  }
+
+  ExtensionNameRequestIdPair key(extension_id, sign_request_id);
+  sign_requests_.erase(key);
+}
+
+int PinDialogManager::StoredSignRequestsForTesting() const {
+  return sign_requests_.size();
+}
+
+PinDialogManager::RequestPinResult PinDialogManager::RequestPin(
+    const std::string& extension_id,
+    const std::string& extension_name,
+    int sign_request_id,
+    security_token_pin::CodeType code_type,
+    security_token_pin::ErrorLabel error_label,
+    int attempts_left,
+    RequestPinCallback callback) {
+  DCHECK_GE(attempts_left, -1);
+  const bool accept_input = (attempts_left != 0);
+
+  // Check the validity of sign_request_id.
+  const SignRequestState* const sign_request_state =
+      FindSignRequestState(extension_id, sign_request_id);
+  if (!sign_request_state)
+    return RequestPinResult::kInvalidId;
+
+  // Start from sanity checks, as the extension might have issued this call
+  // incorrectly.
+  if (active_dialog_state_) {
+    // The active dialog exists already, so we need to make sure it belongs to
+    // the same extension and the user submitted some input.
+    if (extension_id != active_dialog_state_->extension_id)
+      return RequestPinResult::kOtherFlowInProgress;
+    if (active_dialog_state_->request_pin_callback ||
+        active_dialog_state_->stop_pin_request_callback) {
+      // Extension requests a PIN without having received any input from its
+      // previous request. Reject the new request.
+      return RequestPinResult::kDialogDisplayedAlready;
+    }
+  } else {
+    // Check that the sign request hasn't timed out yet.
+    const base::Time current_time = base::Time::Now();
+    if (current_time - sign_request_state->begin_time > kSignRequestIdTimeout)
+      return RequestPinResult::kInvalidId;
+
+    // A new dialog will be opened, so initialize the related internal state.
+    active_dialog_state_.emplace(GetHostForNewDialog(), extension_id,
+                                 extension_name, sign_request_id, code_type);
+  }
+
+  active_dialog_state_->request_pin_callback = std::move(callback);
+  active_dialog_state_->host->ShowSecurityTokenPinDialog(
+      extension_name, code_type, accept_input, error_label, attempts_left,
+      sign_request_state->authenticating_user_account_id,
+      base::BindOnce(&PinDialogManager::OnPinEntered,
+                     weak_factory_.GetWeakPtr()),
+      base::BindOnce(&PinDialogManager::OnPinDialogClosed,
+                     weak_factory_.GetWeakPtr()));
+
+  return RequestPinResult::kSuccess;
+}
+
+PinDialogManager::StopPinRequestResult
+PinDialogManager::StopPinRequestWithError(
+    const std::string& extension_id,
+    security_token_pin::ErrorLabel error_label,
+    StopPinRequestCallback callback) {
+  DCHECK_NE(error_label, security_token_pin::ErrorLabel::kNone);
+
+  // Perform sanity checks, as the extension might have issued this call
+  // incorrectly.
+  if (!active_dialog_state_ ||
+      active_dialog_state_->extension_id != extension_id) {
+    return StopPinRequestResult::kNoActiveDialog;
+  }
+  if (active_dialog_state_->request_pin_callback ||
+      active_dialog_state_->stop_pin_request_callback) {
+    return StopPinRequestResult::kNoUserInput;
+  }
+
+  const SignRequestState* const sign_request_state =
+      FindSignRequestState(extension_id, active_dialog_state_->sign_request_id);
+  if (!sign_request_state)
+    return StopPinRequestResult::kNoActiveDialog;
+
+  active_dialog_state_->stop_pin_request_callback = std::move(callback);
+  active_dialog_state_->host->ShowSecurityTokenPinDialog(
+      active_dialog_state_->extension_name, active_dialog_state_->code_type,
+      /*enable_user_input=*/false, error_label,
+      /*attempts_left=*/-1, sign_request_state->authenticating_user_account_id,
+      base::BindOnce(&PinDialogManager::OnPinEntered,
+                     weak_factory_.GetWeakPtr()),
+      base::BindOnce(&PinDialogManager::OnPinDialogClosed,
+                     weak_factory_.GetWeakPtr()));
+
+  return StopPinRequestResult::kSuccess;
+}
+
+bool PinDialogManager::LastPinDialogClosed(
+    const std::string& extension_id) const {
+  auto iter = last_response_closed_.find(extension_id);
+  return iter != last_response_closed_.end() && iter->second;
+}
+
+bool PinDialogManager::CloseDialog(const std::string& extension_id) {
+  // Perform sanity checks, as the extension might have issued this call
+  // incorrectly.
+  if (!active_dialog_state_ ||
+      extension_id != active_dialog_state_->extension_id) {
+    LOG(ERROR) << "StopPinRequest called by unexpected extension: "
+               << extension_id;
+    return false;
+  }
+
+  CloseActiveDialog();
+  return true;
+}
+
+void PinDialogManager::ExtensionUnloaded(const std::string& extension_id) {
+  if (active_dialog_state_ &&
+      active_dialog_state_->extension_id == extension_id) {
+    CloseActiveDialog();
+  }
+
+  last_response_closed_[extension_id] = false;
+
+  for (auto it = sign_requests_.cbegin(); it != sign_requests_.cend();) {
+    if (it->first.first == extension_id)
+      sign_requests_.erase(it++);
+    else
+      ++it;
+  }
+}
+
+void PinDialogManager::AddPinDialogHost(
+    SecurityTokenPinDialogHost* pin_dialog_host) {
+  DCHECK(!base::Contains(added_dialog_hosts_, pin_dialog_host));
+  added_dialog_hosts_.push_back(pin_dialog_host);
+}
+
+void PinDialogManager::RemovePinDialogHost(
+    SecurityTokenPinDialogHost* pin_dialog_host) {
+  if (active_dialog_state_ && active_dialog_state_->host == pin_dialog_host)
+    CloseActiveDialog();
+  DCHECK(base::Contains(added_dialog_hosts_, pin_dialog_host));
+  base::Erase(added_dialog_hosts_, pin_dialog_host);
+}
+
+PinDialogManager::SignRequestState::SignRequestState(
+    base::Time begin_time,
+    const absl::optional<AccountId>& authenticating_user_account_id)
+    : begin_time(begin_time),
+      authenticating_user_account_id(authenticating_user_account_id) {}
+
+PinDialogManager::SignRequestState::SignRequestState(const SignRequestState&) =
+    default;
+PinDialogManager::SignRequestState&
+PinDialogManager::SignRequestState::operator=(const SignRequestState&) =
+    default;
+
+PinDialogManager::SignRequestState::~SignRequestState() = default;
+
+PinDialogManager::ActiveDialogState::ActiveDialogState(
+    SecurityTokenPinDialogHost* host,
+    const std::string& extension_id,
+    const std::string& extension_name,
+    int sign_request_id,
+    security_token_pin::CodeType code_type)
+    : host(host),
+      extension_id(extension_id),
+      extension_name(extension_name),
+      sign_request_id(sign_request_id),
+      code_type(code_type) {}
+
+PinDialogManager::ActiveDialogState::~ActiveDialogState() = default;
+
+PinDialogManager::SignRequestState* PinDialogManager::FindSignRequestState(
+    const std::string& extension_id,
+    int sign_request_id) {
+  const ExtensionNameRequestIdPair key(extension_id, sign_request_id);
+  const auto sign_request_iter = sign_requests_.find(key);
+  if (sign_request_iter == sign_requests_.end())
+    return nullptr;
+  return &sign_request_iter->second;
+}
+
+void PinDialogManager::OnPinEntered(const std::string& user_input) {
+  DCHECK(!active_dialog_state_->stop_pin_request_callback);
+  last_response_closed_[active_dialog_state_->extension_id] = false;
+  if (active_dialog_state_->request_pin_callback)
+    std::move(active_dialog_state_->request_pin_callback).Run(user_input);
+}
+
+void PinDialogManager::OnPinDialogClosed() {
+  DCHECK(!active_dialog_state_->request_pin_callback ||
+         !active_dialog_state_->stop_pin_request_callback);
+
+  last_response_closed_[active_dialog_state_->extension_id] = true;
+  if (active_dialog_state_->request_pin_callback) {
+    std::move(active_dialog_state_->request_pin_callback)
+        .Run(/*user_input=*/std::string());
+  }
+  if (active_dialog_state_->stop_pin_request_callback)
+    std::move(active_dialog_state_->stop_pin_request_callback).Run();
+  active_dialog_state_.reset();
+}
+
+SecurityTokenPinDialogHost* PinDialogManager::GetHostForNewDialog() {
+  if (added_dialog_hosts_.empty())
+    return &default_dialog_host_;
+  return added_dialog_hosts_.back();
+}
+
+void PinDialogManager::CloseActiveDialog() {
+  if (!active_dialog_state_)
+    return;
+
+  // Ignore any further callbacks from the host. Instead of relying on the host
+  // to call the closing callback, run OnPinDialogClosed() below explicitly.
+  weak_factory_.InvalidateWeakPtrs();
+
+  active_dialog_state_->host->CloseSecurityTokenPinDialog();
+  OnPinDialogClosed();
+  DCHECK(!active_dialog_state_);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/pin_dialog_manager.h b/chrome/browser/certificate_provider/pin_dialog_manager.h
new file mode 100644
index 0000000..3956092
--- /dev/null
+++ b/chrome/browser/certificate_provider/pin_dialog_manager.h
@@ -0,0 +1,205 @@
+// Copyright 2016 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_PIN_DIALOG_MANAGER_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_PIN_DIALOG_MANAGER_H_
+
+#include <map>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/certificate_provider/security_token_pin_dialog_host.h"
+#include "chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.h"
+#include "chromeos/components/security_token_pin/constants.h"
+#include "components/account_id/account_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace chromeos {
+
+// Manages the state of the dialog that requests the PIN from user. Used by the
+// extensions that need to request the PIN. Implemented as requirement for
+// crbug.com/612886
+class PinDialogManager final {
+ public:
+  enum class RequestPinResult {
+    kSuccess,
+    kInvalidId,
+    kOtherFlowInProgress,
+    kDialogDisplayedAlready,
+  };
+
+  enum class StopPinRequestResult {
+    kSuccess,
+    kNoActiveDialog,
+    kNoUserInput,
+  };
+
+  using RequestPinCallback =
+      base::OnceCallback<void(const std::string& user_input)>;
+  using StopPinRequestCallback = base::OnceClosure;
+
+  PinDialogManager();
+  PinDialogManager(const PinDialogManager&) = delete;
+  PinDialogManager& operator=(const PinDialogManager&) = delete;
+  ~PinDialogManager();
+
+  // Stores internally the |signRequestId| along with current timestamp.
+  void AddSignRequestId(
+      const std::string& extension_id,
+      int sign_request_id,
+      const absl::optional<AccountId>& authenticating_user_account_id);
+
+  // Removes the specified sign request, aborting both the current and the
+  // future PIN dialogs related to it.
+  void RemoveSignRequest(const std::string& extension_id, int sign_request_id);
+
+  // Returns the number of pending sign requests stored in sign_requests_
+  int StoredSignRequestsForTesting() const;
+
+  // Creates and displays a new PIN dialog, or reuses the old dialog with just
+  // updating the parameters if active one exists.
+  // |extension_id| - the ID of the extension requesting the dialog.
+  // |extension_name| - the name of the extension requesting the dialog.
+  // |sign_request_id| - the ID given by Chrome when the extension was asked to
+  //     sign the data. It should be a valid, not expired ID at the time the
+  //     extension is requesting PIN the first time.
+  // |code_type| - the type of input requested: either "PIN" or "PUK".
+  // |error_label| - the error template to be displayed inside the dialog. If
+  //     |kNone|, no error is displayed.
+  // |attempts_left| - the number of attempts the user has to try the code. It
+  //     is informational only, and enforced on Chrome side only in case it's
+  //     zero. In that case the textfield is disabled and the user can't provide
+  //     any input to extension. If -1 the textfield from the dialog is enabled
+  //     but no information about the attepts left is not given to the user.
+  // |callback| - used to notify about the user input in the text_field from the
+  //     dialog.
+  // Returns |kSuccess| if the dialog is displayed and extension owns it.
+  // Otherwise the specific error is returned.
+  RequestPinResult RequestPin(const std::string& extension_id,
+                              const std::string& extension_name,
+                              int sign_request_id,
+                              security_token_pin::CodeType code_type,
+                              security_token_pin::ErrorLabel error_label,
+                              int attempts_left,
+                              RequestPinCallback callback);
+
+  // Updates the existing dialog with the error message. Returns whether the
+  // provided |extension_id| matches the extension owning the active dialog.
+  // When it is, the |callback| will be executed once the UI is completed (e.g.,
+  // the dialog with the error message is closed by the user).
+  StopPinRequestResult StopPinRequestWithError(
+      const std::string& extension_id,
+      security_token_pin::ErrorLabel error_label,
+      StopPinRequestCallback callback);
+
+  // Returns whether the last PIN dialog from this extension was closed by the
+  // user.
+  bool LastPinDialogClosed(const std::string& extension_id) const;
+
+  // Called when extension calls the stopPinRequest method. The active dialog is
+  // closed if the |extension_id| matches the |active_dialog_extension_id_|.
+  // Returns whether the dialog was closed.
+  bool CloseDialog(const std::string& extension_id);
+
+  // Resets the manager data related to the extension.
+  void ExtensionUnloaded(const std::string& extension_id);
+
+  // Dynamically adds the dialog host that can be used by this instance for
+  // showing new dialogs. There may be multiple hosts added, in which case the
+  // most recently added is used. Before any hosts have been added, the default
+  // (popup-based) host is used.
+  void AddPinDialogHost(SecurityTokenPinDialogHost* pin_dialog_host);
+  // Removes the previously added dialog host. If a dialog is still opened in
+  // this host, closes it beforehand.
+  void RemovePinDialogHost(SecurityTokenPinDialogHost* pin_dialog_host);
+
+  SecurityTokenPinDialogHostPopupImpl* default_dialog_host_for_testing() {
+    return &default_dialog_host_;
+  }
+
+ private:
+  struct SignRequestState {
+    SignRequestState(
+        base::Time begin_time,
+        const absl::optional<AccountId>& authenticating_user_account_id);
+    SignRequestState(const SignRequestState&);
+    SignRequestState& operator=(const SignRequestState&);
+    ~SignRequestState();
+
+    base::Time begin_time;
+    absl::optional<AccountId> authenticating_user_account_id;
+  };
+
+  // Holds information related to the currently opened PIN dialog.
+  struct ActiveDialogState {
+    ActiveDialogState(SecurityTokenPinDialogHost* host,
+                      const std::string& extension_id,
+                      const std::string& extension_name,
+                      int sign_request_id,
+                      security_token_pin::CodeType code_type);
+    ~ActiveDialogState();
+
+    // Remember the host that was used to open the active dialog, as new hosts
+    // could have been added since the dialog was opened, but we want to
+    // continue calling the same host when dealing with the same active dialog.
+    SecurityTokenPinDialogHost* const host;
+
+    const std::string extension_id;
+    const std::string extension_name;
+    const int sign_request_id;
+    const security_token_pin::CodeType code_type;
+    RequestPinCallback request_pin_callback;
+    StopPinRequestCallback stop_pin_request_callback;
+  };
+
+  using ExtensionNameRequestIdPair = std::pair<std::string, int>;
+
+  // Returns the sign request state for the given key, or null if not found.
+  SignRequestState* FindSignRequestState(const std::string& extension_id,
+                                         int sign_request_id);
+
+  // The callback that gets invoked once the user sends some input into the PIN
+  // dialog.
+  void OnPinEntered(const std::string& user_input);
+  // The callback that gets invoked once the PIN dialog gets closed.
+  void OnPinDialogClosed();
+
+  // Returns the dialog host that should own the new dialog. Currently returns
+  // the most recently added dialog host (falling back to the default one when
+  // no host has been added).
+  SecurityTokenPinDialogHost* GetHostForNewDialog();
+
+  // Closes the active dialog, if there's any, and runs the necessary callbacks.
+  void CloseActiveDialog();
+
+  // Tells whether user closed the last request PIN dialog issued by an
+  // extension. The extension_id is the key and value is true if user closed the
+  // dialog. Used to determine if the limit of dialogs rejected by the user has
+  // been exceeded.
+  std::unordered_map<std::string, bool> last_response_closed_;
+
+  // The map from extension_id and an active sign request id to the state of the
+  // request.
+  std::map<ExtensionNameRequestIdPair, SignRequestState> sign_requests_;
+
+  SecurityTokenPinDialogHostPopupImpl default_dialog_host_;
+  // The list of dynamically added dialog hosts, in the same order as they were
+  // added.
+  std::vector<SecurityTokenPinDialogHost*> added_dialog_hosts_;
+
+  // There can be only one active dialog to request the PIN at any point of
+  // time.
+  absl::optional<ActiveDialogState> active_dialog_state_;
+
+  base::WeakPtrFactory<PinDialogManager> weak_factory_{this};
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_PIN_DIALOG_MANAGER_H_
diff --git a/chrome/browser/certificate_provider/security_token_pin_dialog_host.h b/chrome/browser/certificate_provider/security_token_pin_dialog_host.h
new file mode 100644
index 0000000..a361d26
--- /dev/null
+++ b/chrome/browser/certificate_provider/security_token_pin_dialog_host.h
@@ -0,0 +1,71 @@
+// Copyright 2019 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_SECURITY_TOKEN_PIN_DIALOG_HOST_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_SECURITY_TOKEN_PIN_DIALOG_HOST_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "chromeos/components/security_token_pin/constants.h"
+#include "components/account_id/account_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace chromeos {
+
+// The interface that allows showing a PIN dialog requested by a certificate
+// provider extension (see the chrome.certificateProvider API).
+//
+// Only one dialog is supported at a time by a single host instance.
+class SecurityTokenPinDialogHost {
+ public:
+  using SecurityTokenPinEnteredCallback =
+      base::OnceCallback<void(const std::string& user_input)>;
+  using SecurityTokenPinDialogClosedCallback = base::OnceClosure;
+
+  // Note that this does NOT execute the callback.
+  virtual ~SecurityTokenPinDialogHost() = default;
+
+  // Shows the PIN dialog, or updates the existing one if it's already shown.
+  //
+  // Note that the caller is still responsible for closing the opened dialog, by
+  // calling CloseSecurityTokenPinDialog(), after the |callback| got executed
+  // with a non-empty |user_input|.
+  //
+  // Note also that when the existing dialog is updated, its old callbacks will
+  // NOT be called at all.
+  //
+  // |caller_extension_name| - name of the extension that requested this dialog.
+  // |code_type| - type of the code requested from the user.
+  // |enable_user_input| - when false, the UI will disable the controls that
+  //     allow user to enter the PIN/PUK. MUST be |false| when |attempts_left|
+  //     is zero.
+  // |error_label| - optionally, specifies the error that the UI should display
+  //     (note that a non-empty error does NOT disable the user input per se).
+  // |attempts_left| - when non-negative, the UI should indicate this number to
+  //     the user; otherwise must be equal to -1.
+  // |authenticating_user_account_id| - when set, is the ID of the user whose
+  //     authentication triggered this PIN request.
+  // |pin_entered_callback| - called when the user submits the input.
+  // |pin_dialog_closed_callback| - called when the dialog is closed (either by
+  //     the user or programmatically; it's optional whether to call it after
+  //     CloseSecurityTokenPinDialog()).
+  virtual void ShowSecurityTokenPinDialog(
+      const std::string& caller_extension_name,
+      security_token_pin::CodeType code_type,
+      bool enable_user_input,
+      security_token_pin::ErrorLabel error_label,
+      int attempts_left,
+      const absl::optional<AccountId>& authenticating_user_account_id,
+      SecurityTokenPinEnteredCallback pin_entered_callback,
+      SecurityTokenPinDialogClosedCallback pin_dialog_closed_callback) = 0;
+
+  // Closes the currently shown PIN dialog, if there's any. The implementation
+  // is NOT required to run |pin_dialog_closed_callback| after the closing.
+  virtual void CloseSecurityTokenPinDialog() = 0;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_SECURITY_TOKEN_PIN_DIALOG_HOST_H_
diff --git a/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.cc b/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.cc
new file mode 100644
index 0000000..d03dd9bb
--- /dev/null
+++ b/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.cc
@@ -0,0 +1,117 @@
+// Copyright 2019 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 "chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.h"
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "build/chromeos_buildflags.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/views/notifications/request_pin_view_chromeos.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_delegate.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/login/ui/login_display_host.h"
+#endif
+
+namespace chromeos {
+
+namespace {
+
+gfx::NativeWindow GetBrowserParentWindow() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (LoginDisplayHost::default_host())
+    return LoginDisplayHost::default_host()->GetNativeWindow();
+#endif
+  Browser* browser =
+      chrome::FindTabbedBrowser(ProfileManager::GetPrimaryUserProfile(), true);
+  if (browser)
+    return browser->window()->GetNativeWindow();
+
+  return nullptr;
+}
+
+}  // namespace
+
+SecurityTokenPinDialogHostPopupImpl::SecurityTokenPinDialogHostPopupImpl() =
+    default;
+
+SecurityTokenPinDialogHostPopupImpl::~SecurityTokenPinDialogHostPopupImpl() =
+    default;
+
+void SecurityTokenPinDialogHostPopupImpl::ShowSecurityTokenPinDialog(
+    const std::string& caller_extension_name,
+    security_token_pin::CodeType code_type,
+    bool enable_user_input,
+    security_token_pin::ErrorLabel error_label,
+    int attempts_left,
+    const absl::optional<AccountId>& /*authenticating_user_account_id*/,
+    SecurityTokenPinEnteredCallback pin_entered_callback,
+    SecurityTokenPinDialogClosedCallback pin_dialog_closed_callback) {
+  DCHECK(!caller_extension_name.empty());
+  DCHECK(!enable_user_input || attempts_left);
+  DCHECK_GE(attempts_left, -1);
+
+  pin_entered_callback_ = std::move(pin_entered_callback);
+  pin_dialog_closed_callback_ = std::move(pin_dialog_closed_callback);
+
+  if (active_pin_dialog_) {
+    active_pin_dialog_->SetDialogParameters(code_type, error_label,
+                                            attempts_left, enable_user_input);
+    active_pin_dialog_->SetExtensionName(caller_extension_name);
+    active_pin_dialog_->DialogModelChanged();
+  } else {
+    active_pin_dialog_ = new RequestPinView(
+        caller_extension_name, code_type, attempts_left,
+        base::BindRepeating(&SecurityTokenPinDialogHostPopupImpl::OnPinEntered,
+                            weak_ptr_factory_.GetWeakPtr()),
+        base::BindOnce(&SecurityTokenPinDialogHostPopupImpl::OnViewDestroyed,
+                       weak_ptr_factory_.GetWeakPtr()));
+    // If there is no parent, falls back to the root window for new windows.
+    active_window_ = views::DialogDelegate::CreateDialogWidget(
+        active_pin_dialog_, /*context=*/nullptr, GetBrowserParentWindow());
+    active_window_->Show();
+  }
+}
+
+void SecurityTokenPinDialogHostPopupImpl::CloseSecurityTokenPinDialog() {
+  if (!active_pin_dialog_)
+    return;
+  active_window_->Close();
+  // The view destruction may happen asynchronously, so clear our state and
+  // execute the callback immediately in order to follow our own API contract.
+  active_pin_dialog_ = nullptr;
+  active_window_ = nullptr;
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  if (pin_dialog_closed_callback_)
+    std::move(pin_dialog_closed_callback_).Run();
+}
+
+void SecurityTokenPinDialogHostPopupImpl::OnPinEntered(
+    const std::string& user_input) {
+  DCHECK(active_pin_dialog_);
+  DCHECK(active_window_);
+  std::move(pin_entered_callback_).Run(user_input);
+}
+
+void SecurityTokenPinDialogHostPopupImpl::OnViewDestroyed() {
+  DCHECK(active_pin_dialog_);
+  DCHECK(active_window_);
+
+  active_pin_dialog_ = nullptr;
+  active_window_ = nullptr;
+  if (pin_dialog_closed_callback_)
+    std::move(pin_dialog_closed_callback_).Run();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.h b/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.h
new file mode 100644
index 0000000..272656c
--- /dev/null
+++ b/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.h
@@ -0,0 +1,69 @@
+// Copyright 2019 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_SECURITY_TOKEN_PIN_DIALOG_HOST_POPUP_IMPL_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_SECURITY_TOKEN_PIN_DIALOG_HOST_POPUP_IMPL_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/certificate_provider/security_token_pin_dialog_host.h"
+
+namespace views {
+class Widget;
+}  // namespace views
+
+class RequestPinView;
+
+namespace chromeos {
+
+// The default implementation of the PIN dialog host. It renders the PIN dialog
+// as a popup with the RequestPinView view.
+class SecurityTokenPinDialogHostPopupImpl final
+    : public SecurityTokenPinDialogHost {
+ public:
+  SecurityTokenPinDialogHostPopupImpl();
+  SecurityTokenPinDialogHostPopupImpl(
+      const SecurityTokenPinDialogHostPopupImpl&) = delete;
+  SecurityTokenPinDialogHostPopupImpl& operator=(
+      const SecurityTokenPinDialogHostPopupImpl&) = delete;
+  ~SecurityTokenPinDialogHostPopupImpl() override;
+
+  // SecurityTokenPinDialogHost:
+  void ShowSecurityTokenPinDialog(
+      const std::string& caller_extension_name,
+      security_token_pin::CodeType code_type,
+      bool enable_user_input,
+      security_token_pin::ErrorLabel error_label,
+      int attempts_left,
+      const absl::optional<AccountId>& authenticating_user_account_id,
+      SecurityTokenPinEnteredCallback pin_entered_callback,
+      SecurityTokenPinDialogClosedCallback pin_dialog_closed_callback) override;
+  void CloseSecurityTokenPinDialog() override;
+
+  RequestPinView* active_view_for_testing() { return active_pin_dialog_; }
+  views::Widget* active_window_for_testing() { return active_window_; }
+
+ private:
+  // Called every time the user submits some input.
+  void OnPinEntered(const std::string& user_input);
+  // Called when the |active_pin_dialog_| view is being destroyed.
+  void OnViewDestroyed();
+
+  SecurityTokenPinEnteredCallback pin_entered_callback_;
+  SecurityTokenPinDialogClosedCallback pin_dialog_closed_callback_;
+
+  // Owned by |active_window_|.
+  RequestPinView* active_pin_dialog_ = nullptr;
+  // Owned by the UI code (NativeWidget).
+  views::Widget* active_window_ = nullptr;
+
+  base::WeakPtrFactory<SecurityTokenPinDialogHostPopupImpl> weak_ptr_factory_{
+      this};
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_SECURITY_TOKEN_PIN_DIALOG_HOST_POPUP_IMPL_H_
diff --git a/chrome/browser/certificate_provider/sign_requests.cc b/chrome/browser/certificate_provider/sign_requests.cc
new file mode 100644
index 0000000..a98de3dc
--- /dev/null
+++ b/chrome/browser/certificate_provider/sign_requests.cc
@@ -0,0 +1,95 @@
+// Copyright 2015 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 "chrome/browser/certificate_provider/sign_requests.h"
+
+namespace chromeos {
+namespace certificate_provider {
+
+SignRequests::Request::Request(
+    const scoped_refptr<net::X509Certificate>& certificate,
+    const absl::optional<AccountId>& authenticating_user_account_id,
+    net::SSLPrivateKey::SignCallback callback)
+    : certificate(certificate),
+      authenticating_user_account_id(authenticating_user_account_id),
+      callback(std::move(callback)) {}
+
+SignRequests::Request::Request(Request&& other) = default;
+
+SignRequests::Request::~Request() = default;
+
+SignRequests::Request& SignRequests::Request::operator=(Request&&) = default;
+
+SignRequests::RequestsState::RequestsState() {}
+
+SignRequests::RequestsState::RequestsState(RequestsState&& other) = default;
+
+SignRequests::RequestsState::~RequestsState() {}
+
+SignRequests::SignRequests() {}
+
+SignRequests::~SignRequests() {}
+
+int SignRequests::AddRequest(
+    const std::string& extension_id,
+    const scoped_refptr<net::X509Certificate>& certificate,
+    const absl::optional<AccountId>& authenticating_user_account_id,
+    net::SSLPrivateKey::SignCallback callback) {
+  RequestsState& state = extension_to_requests_[extension_id];
+  const int request_id = state.next_free_id++;
+  state.pending_requests.emplace(
+      request_id, Request(certificate, authenticating_user_account_id,
+                          std::move(callback)));
+  return request_id;
+}
+
+std::vector<SignRequests::ExtensionNameRequestIdPair>
+SignRequests::FindRequestsForAuthenticatingUser(
+    const AccountId& authenticating_user_account_id) const {
+  std::vector<ExtensionNameRequestIdPair> found_requests;
+  for (const auto& extension_entry : extension_to_requests_) {
+    const std::string& extension_id = extension_entry.first;
+    const RequestsState& extension_requests = extension_entry.second;
+    for (const auto& entry : extension_requests.pending_requests) {
+      const int request_id = entry.first;
+      const Request& request = entry.second;
+      if (request.authenticating_user_account_id ==
+          authenticating_user_account_id) {
+        found_requests.emplace_back(extension_id, request_id);
+      }
+    }
+  }
+  return found_requests;
+}
+
+bool SignRequests::RemoveRequest(
+    const std::string& extension_id,
+    int request_id,
+    scoped_refptr<net::X509Certificate>* certificate,
+    net::SSLPrivateKey::SignCallback* callback) {
+  RequestsState& state = extension_to_requests_[extension_id];
+  std::map<int, Request>& pending = state.pending_requests;
+  const auto it = pending.find(request_id);
+  if (it == pending.end())
+    return false;
+  Request& request = it->second;
+
+  *certificate = request.certificate;
+  *callback = std::move(request.callback);
+  pending.erase(it);
+  return true;
+}
+
+std::vector<net::SSLPrivateKey::SignCallback> SignRequests::RemoveAllRequests(
+    const std::string& extension_id) {
+  std::vector<net::SSLPrivateKey::SignCallback> callbacks;
+  for (auto& entry : extension_to_requests_[extension_id].pending_requests) {
+    callbacks.push_back(std::move(entry.second.callback));
+  }
+  extension_to_requests_.erase(extension_id);
+  return callbacks;
+}
+
+}  // namespace certificate_provider
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/sign_requests.h b/chrome/browser/certificate_provider/sign_requests.h
new file mode 100644
index 0000000..ccb3d20
--- /dev/null
+++ b/chrome/browser/certificate_provider/sign_requests.h
@@ -0,0 +1,92 @@
+// Copyright 2015 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_SIGN_REQUESTS_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_SIGN_REQUESTS_H_
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "components/account_id/account_id.h"
+#include "net/cert/x509_certificate.h"
+#include "net/ssl/ssl_private_key.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace chromeos {
+namespace certificate_provider {
+
+class SignRequests {
+ public:
+  using ExtensionNameRequestIdPair = std::pair<std::string, int>;
+
+  SignRequests();
+  ~SignRequests();
+
+  // Returns the id of the new request. The returned request id is specific to
+  // the given extension.
+  int AddRequest(
+      const std::string& extension_id,
+      const scoped_refptr<net::X509Certificate>& certificate,
+      const absl::optional<AccountId>& authenticating_user_account_id,
+      net::SSLPrivateKey::SignCallback callback);
+
+  // Returns the list of requests that correspond to the authentication of the
+  // given user.
+  std::vector<ExtensionNameRequestIdPair> FindRequestsForAuthenticatingUser(
+      const AccountId& authenticating_user_account_id) const;
+
+  // Returns false if no request with the given id for |extension_id|
+  // could be found. Otherwise removes the request and sets |certificate| and
+  // |callback| to the values that were provided with AddRequest().
+  bool RemoveRequest(const std::string& extension_id,
+                     int request_id,
+                     scoped_refptr<net::X509Certificate>* certificate,
+                     net::SSLPrivateKey::SignCallback* callback);
+
+  // Remove all pending requests for this extension and return their
+  // callbacks.
+  std::vector<net::SSLPrivateKey::SignCallback> RemoveAllRequests(
+      const std::string& extension_id);
+
+ private:
+  struct Request {
+    Request(const scoped_refptr<net::X509Certificate>& certificate,
+            const absl::optional<AccountId>& authenticating_user_account_id,
+            net::SSLPrivateKey::SignCallback callback);
+    Request(Request&& other);
+    Request& operator=(Request&&);
+    ~Request();
+
+    scoped_refptr<net::X509Certificate> certificate;
+    absl::optional<AccountId> authenticating_user_account_id;
+    net::SSLPrivateKey::SignCallback callback;
+  };
+
+  // Holds state of all sign requests to a single extension.
+  struct RequestsState {
+    RequestsState();
+    RequestsState(RequestsState&& other);
+    RequestsState& operator=(RequestsState&&);
+    ~RequestsState();
+
+    // Maps from request id to the request state.
+    std::map<int, Request> pending_requests;
+
+    // The request id that will be used for the next sign request to this
+    // extension.
+    int next_free_id = 0;
+  };
+
+  // Contains the state of all sign requests per extension.
+  std::map<std::string, RequestsState> extension_to_requests_;
+};
+
+}  // namespace certificate_provider
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_SIGN_REQUESTS_H_
diff --git a/chrome/browser/certificate_provider/test_certificate_provider_extension.cc b/chrome/browser/certificate_provider/test_certificate_provider_extension.cc
new file mode 100644
index 0000000..9e43711
--- /dev/null
+++ b/chrome/browser/certificate_provider/test_certificate_provider_extension.cc
@@ -0,0 +1,360 @@
+// Copyright 2019 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 "chrome/browser/certificate_provider/test_certificate_provider_extension.h"
+
+#include <cstdint>
+#include <utility>
+#include <vector>
+
+#include "base/containers/span.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/path_service.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/api/certificate_provider.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "crypto/rsa_private_key.h"
+#include "extensions/browser/api/test/test_api.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/notification_types.h"
+#include "extensions/common/api/test.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_data_directory.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/boringssl/src/include/openssl/rsa.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+
+namespace ash {
+
+namespace {
+
+constexpr char kExtensionId[] = "ecmhnokcdiianioonpgakiooenfnonid";
+// Paths relative to |chrome::DIR_TEST_DATA|:
+constexpr base::FilePath::CharType kExtensionPath[] =
+    FILE_PATH_LITERAL("extensions/test_certificate_provider/extension/");
+constexpr base::FilePath::CharType kExtensionPemPath[] =
+    FILE_PATH_LITERAL("extensions/test_certificate_provider/extension.pem");
+
+// List of algorithms that the extension claims to support for the returned
+// certificates.
+constexpr extensions::api::certificate_provider::Algorithm
+    kSupportedAlgorithms[] = {extensions::api::certificate_provider::Algorithm::
+                                  ALGORITHM_RSASSA_PKCS1_V1_5_SHA256,
+                              extensions::api::certificate_provider::Algorithm::
+                                  ALGORITHM_RSASSA_PKCS1_V1_5_SHA1};
+
+base::Value ConvertBytesToValue(base::span<const uint8_t> bytes) {
+  base::Value value(base::Value::Type::LIST);
+  for (auto byte : bytes)
+    value.Append(byte);
+  return value;
+}
+
+std::vector<uint8_t> ExtractBytesFromValue(const base::Value& value) {
+  std::vector<uint8_t> bytes;
+  for (const base::Value& item_value : value.GetListDeprecated())
+    bytes.push_back(base::checked_cast<uint8_t>(item_value.GetInt()));
+  return bytes;
+}
+
+base::span<const uint8_t> GetCertDer(const net::X509Certificate& certificate) {
+  return base::as_bytes(base::make_span(
+      net::x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer())));
+}
+
+base::Value MakeClientCertificateInfoValue(
+    const net::X509Certificate& certificate) {
+  base::Value cert_info_value(base::Value::Type::DICTIONARY);
+  base::Value certificate_chain(base::Value::Type::LIST);
+  certificate_chain.Append(ConvertBytesToValue(GetCertDer(certificate)));
+  cert_info_value.SetKey("certificateChain", std::move(certificate_chain));
+  base::Value supported_algorithms_value(base::Value::Type::LIST);
+  for (auto supported_algorithm : kSupportedAlgorithms) {
+    supported_algorithms_value.Append(
+        extensions::api::certificate_provider::ToString(supported_algorithm));
+  }
+  cert_info_value.SetKey("supportedAlgorithms",
+                         std::move(supported_algorithms_value));
+  return cert_info_value;
+}
+
+std::string ConvertValueToJson(const base::Value& value) {
+  std::string json;
+  CHECK(base::JSONWriter::Write(value, &json));
+  return json;
+}
+
+base::Value ParseJsonToValue(const std::string& json) {
+  absl::optional<base::Value> value = base::JSONReader::Read(json);
+  CHECK(value);
+  return std::move(*value);
+}
+
+bool RsaSignRawData(crypto::RSAPrivateKey* key,
+                    uint16_t openssl_signature_algorithm,
+                    const std::vector<uint8_t>& input,
+                    std::vector<uint8_t>* signature) {
+  const EVP_MD* const digest_algorithm =
+      SSL_get_signature_algorithm_digest(openssl_signature_algorithm);
+  bssl::ScopedEVP_MD_CTX ctx;
+  EVP_PKEY_CTX* pkey_ctx = nullptr;
+  if (!EVP_DigestSignInit(ctx.get(), &pkey_ctx, digest_algorithm,
+                          /*ENGINE* e=*/nullptr, key->key()))
+    return false;
+  if (SSL_is_signature_algorithm_rsa_pss(openssl_signature_algorithm)) {
+    // For RSA-PSS, configure the special padding and set the salt length to be
+    // equal to the hash size.
+    if (!EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) ||
+        !EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, /*salt_len=*/-1)) {
+      return false;
+    }
+  }
+  size_t sig_len = 0;
+  // Determine the signature length for the buffer.
+  if (!EVP_DigestSign(ctx.get(), /*out_sig=*/nullptr, &sig_len, input.data(),
+                      input.size()))
+    return false;
+  signature->resize(sig_len);
+  return EVP_DigestSign(ctx.get(), signature->data(), &sig_len, input.data(),
+                        input.size()) != 0;
+}
+
+void SendReplyToJs(ExtensionTestMessageListener* message_listener,
+                   const base::Value& response) {
+  message_listener->Reply(ConvertValueToJson(response));
+  message_listener->Reset();
+}
+
+std::unique_ptr<crypto::RSAPrivateKey> LoadPrivateKeyFromFile(
+    const base::FilePath& path) {
+  std::string key_pk8;
+  {
+    base::ScopedAllowBlockingForTesting allow_io;
+    EXPECT_TRUE(base::ReadFileToString(path, &key_pk8));
+  }
+  return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(
+      base::as_bytes(base::make_span(key_pk8)));
+}
+
+}  // namespace
+
+// static
+extensions::ExtensionId TestCertificateProviderExtension::extension_id() {
+  return kExtensionId;
+}
+
+// static
+base::FilePath TestCertificateProviderExtension::GetExtensionSourcePath() {
+  return base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
+      .Append(kExtensionPath);
+}
+
+// static
+base::FilePath TestCertificateProviderExtension::GetExtensionPemPath() {
+  return base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
+      .Append(kExtensionPemPath);
+}
+
+// static
+scoped_refptr<net::X509Certificate>
+TestCertificateProviderExtension::GetCertificate() {
+  return net::ImportCertFromFile(net::GetTestCertsDirectory(), "client_1.pem");
+}
+
+// static
+std::string TestCertificateProviderExtension::GetCertificateSpki() {
+  const scoped_refptr<net::X509Certificate> certificate = GetCertificate();
+  base::StringPiece spki_bytes;
+  if (!net::asn1::ExtractSPKIFromDERCert(
+          net::x509_util::CryptoBufferAsStringPiece(certificate->cert_buffer()),
+          &spki_bytes)) {
+    return {};
+  }
+  return std::string(spki_bytes);
+}
+
+TestCertificateProviderExtension::TestCertificateProviderExtension(
+    content::BrowserContext* browser_context)
+    : browser_context_(browser_context),
+      certificate_(GetCertificate()),
+      private_key_(LoadPrivateKeyFromFile(net::GetTestCertsDirectory().Append(
+          FILE_PATH_LITERAL("client_1.pk8")))),
+      message_listener_(ReplyBehavior::kWillReply) {
+  DCHECK(browser_context_);
+  CHECK(certificate_);
+  CHECK(private_key_);
+  // Ignore messages targeted to other extensions or browser contexts.
+  message_listener_.set_extension_id(kExtensionId);
+  message_listener_.set_browser_context(browser_context);
+  message_listener_.SetOnRepeatedlySatisfied(
+      base::BindRepeating(&TestCertificateProviderExtension::HandleMessage,
+                          base::Unretained(this)));
+}
+
+TestCertificateProviderExtension::~TestCertificateProviderExtension() = default;
+
+void TestCertificateProviderExtension::TriggerSetCertificates() {
+  base::Value message_data(base::Value::Type::DICTIONARY);
+  message_data.SetStringKey("name", "setCertificates");
+  base::Value cert_info_values(base::Value::Type::LIST);
+  if (should_provide_certificates_)
+    cert_info_values.Append(MakeClientCertificateInfoValue(*certificate_));
+  message_data.SetKey("certificateInfoList", std::move(cert_info_values));
+
+  auto message = std::make_unique<base::Value>(base::Value::Type::LIST);
+  message->Append(std::move(message_data));
+  auto event = std::make_unique<extensions::Event>(
+      extensions::events::FOR_TEST,
+      extensions::api::test::OnMessage::kEventName,
+      std::move(*message).TakeListDeprecated(), browser_context_);
+  extensions::EventRouter::Get(browser_context_)
+      ->DispatchEventToExtension(extension_id(), std::move(event));
+}
+
+void TestCertificateProviderExtension::HandleMessage(
+    const std::string& message) {
+  // Handle the request and reply to it (possibly, asynchronously).
+  base::Value message_value = ParseJsonToValue(message);
+  CHECK(message_value.is_list());
+  CHECK(message_value.GetListDeprecated().size());
+  CHECK(message_value.GetListDeprecated()[0].is_string());
+  const std::string& request_type =
+      message_value.GetListDeprecated()[0].GetString();
+  ReplyToJsCallback send_reply_to_js_callback =
+      base::BindOnce(&SendReplyToJs, &message_listener_);
+  if (request_type == "getCertificates") {
+    CHECK_EQ(message_value.GetListDeprecated().size(), 1U);
+    HandleCertificatesRequest(std::move(send_reply_to_js_callback));
+  } else if (request_type == "onSignatureRequested") {
+    CHECK_EQ(message_value.GetListDeprecated().size(), 4U);
+    HandleSignatureRequest(
+        /*sign_request=*/message_value.GetListDeprecated()[1],
+        /*pin_status=*/message_value.GetListDeprecated()[2],
+        /*pin=*/message_value.GetListDeprecated()[3],
+        std::move(send_reply_to_js_callback));
+  } else {
+    LOG(FATAL) << "Unexpected JS message type: " << request_type;
+  }
+}
+
+void TestCertificateProviderExtension::HandleCertificatesRequest(
+    ReplyToJsCallback callback) {
+  ++certificate_request_count_;
+  base::Value cert_info_values(base::Value::Type::LIST);
+  if (should_provide_certificates_)
+    cert_info_values.Append(MakeClientCertificateInfoValue(*certificate_));
+  std::move(callback).Run(cert_info_values);
+}
+
+void TestCertificateProviderExtension::HandleSignatureRequest(
+    const base::Value& sign_request,
+    const base::Value& pin_status,
+    const base::Value& pin,
+    ReplyToJsCallback callback) {
+  CHECK_EQ(*sign_request.FindKey("certificate"),
+           ConvertBytesToValue(GetCertDer(*certificate_)));
+  const std::string pin_status_string = pin_status.GetString();
+  const std::string pin_string = pin.GetString();
+
+  const int sign_request_id = sign_request.FindKey("signRequestId")->GetInt();
+  const std::vector<uint8_t> input =
+      ExtractBytesFromValue(*sign_request.FindKey("input"));
+
+  const extensions::api::certificate_provider::Algorithm algorithm =
+      extensions::api::certificate_provider::ParseAlgorithm(
+          sign_request.FindKey("algorithm")->GetString());
+  int openssl_signature_algorithm = 0;
+  if (algorithm == extensions::api::certificate_provider::Algorithm::
+                       ALGORITHM_RSASSA_PKCS1_V1_5_SHA256) {
+    openssl_signature_algorithm = SSL_SIGN_RSA_PKCS1_SHA256;
+  } else if (algorithm == extensions::api::certificate_provider::Algorithm::
+                              ALGORITHM_RSASSA_PKCS1_V1_5_SHA1) {
+    openssl_signature_algorithm = SSL_SIGN_RSA_PKCS1_SHA1;
+  } else {
+    LOG(FATAL) << "Unexpected signature request algorithm: " << algorithm;
+  }
+
+  if (should_fail_sign_digest_requests_) {
+    // Simulate a failure.
+    std::move(callback).Run(/*response=*/base::Value());
+    return;
+  }
+
+  base::Value response(base::Value::Type::DICTIONARY);
+  if (required_pin_.has_value()) {
+    if (pin_status_string == "not_requested") {
+      // The PIN is required but not specified yet, so request it via the JS
+      // side before generating the signature.
+      base::Value pin_request_parameters(base::Value::Type::DICTIONARY);
+      pin_request_parameters.SetIntKey("signRequestId", sign_request_id);
+      if (remaining_pin_attempts_ == 0) {
+        pin_request_parameters.SetStringKey("errorType",
+                                            "MAX_ATTEMPTS_EXCEEDED");
+      }
+      response.SetKey("requestPin", std::move(pin_request_parameters));
+      std::move(callback).Run(response);
+      return;
+    }
+    if (remaining_pin_attempts_ == 0) {
+      // The error about the lockout is already displayed, so fail immediately.
+      std::move(callback).Run(/*response=*/base::Value());
+      return;
+    }
+    if (pin_status_string == "canceled" ||
+        base::StartsWith(pin_status_string,
+                         "failed:", base::CompareCase::SENSITIVE)) {
+      // The PIN request failed.
+      LOG(WARNING) << "PIN request failed: " << pin_status_string;
+      // Respond with a failure.
+      std::move(callback).Run(/*response=*/base::Value());
+      return;
+    }
+    DCHECK_EQ(pin_status_string, "ok");
+    if (pin_string != *required_pin_) {
+      // The entered PIN is wrong, so decrement the remaining attempt count, and
+      // update the PIN dialog with displaying an error.
+      if (remaining_pin_attempts_ > 0)
+        --remaining_pin_attempts_;
+      base::Value pin_request_parameters(base::Value::Type::DICTIONARY);
+      pin_request_parameters.SetIntKey("signRequestId", sign_request_id);
+      pin_request_parameters.SetStringKey(
+          "errorType", remaining_pin_attempts_ == 0 ? "MAX_ATTEMPTS_EXCEEDED"
+                                                    : "INVALID_PIN");
+      if (remaining_pin_attempts_ > 0) {
+        pin_request_parameters.SetIntKey("attemptsLeft",
+                                         remaining_pin_attempts_);
+      }
+      response.SetKey("requestPin", std::move(pin_request_parameters));
+      std::move(callback).Run(response);
+      return;
+    }
+    // The entered PIN is correct. Stop the PIN request and proceed to
+    // generating the signature.
+    base::Value stop_pin_request_parameters(base::Value::Type::DICTIONARY);
+    stop_pin_request_parameters.SetIntKey("signRequestId", sign_request_id);
+    response.SetKey("stopPinRequest", std::move(stop_pin_request_parameters));
+  }
+  // Generate and return a valid signature.
+  std::vector<uint8_t> signature;
+  CHECK(RsaSignRawData(private_key_.get(), openssl_signature_algorithm, input,
+                       &signature));
+  response.SetKey("signature", ConvertBytesToValue(signature));
+  std::move(callback).Run(response);
+}
+
+}  // namespace ash
diff --git a/chrome/browser/certificate_provider/test_certificate_provider_extension.h b/chrome/browser/certificate_provider/test_certificate_provider_extension.h
new file mode 100644
index 0000000..beaba4f
--- /dev/null
+++ b/chrome/browser/certificate_provider/test_certificate_provider_extension.h
@@ -0,0 +1,124 @@
+// Copyright 2019 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_TEST_CERTIFICATE_PROVIDER_EXTENSION_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_TEST_CERTIFICATE_PROVIDER_EXTENSION_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/values.h"
+#include "extensions/common/extension_id.h"
+#include "extensions/test/extension_test_message_listener.h"
+#include "net/cert/x509_certificate.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/boringssl/src/include/openssl/base.h"
+
+namespace base {
+class FilePath;
+class Value;
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+}
+
+namespace crypto {
+class RSAPrivateKey;
+}
+
+namespace ash {
+
+// This class provides the C++ side of the test certificate provider extension's
+// implementation (the JavaScript side is in
+// chrome/test/data/extensions/test_certificate_provider).
+//
+// It subscribes itself for requests from the JavaScript side of the extension,
+// and implements the cryptographic operations using the "client_1" test
+// certificate and private key (see src/net/data/ssl/certificates). The
+// supported signature algorithms are currently hardcoded to PKCS #1 v1.5 with
+// SHA-1 and SHA-256.
+class TestCertificateProviderExtension final {
+ public:
+  static extensions::ExtensionId extension_id();
+  static base::FilePath GetExtensionSourcePath();
+  static base::FilePath GetExtensionPemPath();
+  // Returns the certificate provided by the extension.
+  static scoped_refptr<net::X509Certificate> GetCertificate();
+  static std::string GetCertificateSpki();
+
+  explicit TestCertificateProviderExtension(
+      content::BrowserContext* browser_context);
+
+  TestCertificateProviderExtension(const TestCertificateProviderExtension&) =
+      delete;
+  TestCertificateProviderExtension& operator=(
+      const TestCertificateProviderExtension&) = delete;
+
+  ~TestCertificateProviderExtension();
+
+  // Causes the extension to call chrome.certificateProvider.setCertificates,
+  // providing the certificates that are currently available.
+  void TriggerSetCertificates();
+
+  int certificate_request_count() const { return certificate_request_count_; }
+
+  // Sets the PIN that will be required when doing every signature request.
+  // (By default, no PIN is requested.)
+  void set_require_pin(const std::string& pin) { required_pin_ = pin; }
+
+  // Sets the number of remaining PIN attempts.
+  // Zero number means the lockout state, when no attempts are allowed anymore.
+  // A negative number denotes infinite number of attempts, which is the default
+  // behavior.
+  void set_remaining_pin_attempts(int remaining_pin_attempts) {
+    remaining_pin_attempts_ = remaining_pin_attempts;
+  }
+
+  // Sets whether the extension should return any certificates in response to a
+  // onCertificatesRequested request or a TriggerSetCertificates() call.
+  void set_should_provide_certificates(bool should_provide_certificates) {
+    should_provide_certificates_ = should_provide_certificates;
+  }
+
+  // Sets whether the extension should respond with a failure to the
+  // onSignDigestRequested requests.
+  void set_should_fail_sign_digest_requests(
+      bool should_fail_sign_digest_requests) {
+    should_fail_sign_digest_requests_ = should_fail_sign_digest_requests;
+  }
+
+ private:
+  using ReplyToJsCallback =
+      base::OnceCallback<void(const base::Value& response)>;
+
+  void HandleMessage(const std::string& message);
+
+  void HandleCertificatesRequest(ReplyToJsCallback callback);
+  void HandleSignatureRequest(const base::Value& sign_request,
+                              const base::Value& pin_status,
+                              const base::Value& pin,
+                              ReplyToJsCallback callback);
+
+  content::BrowserContext* const browser_context_;
+  const scoped_refptr<net::X509Certificate> certificate_;
+  std::unique_ptr<crypto::RSAPrivateKey> private_key_;
+  int certificate_request_count_ = 0;
+  // When non-empty, contains the expected PIN; the implementation will request
+  // the PIN on every signature request in this case.
+  absl::optional<std::string> required_pin_;
+  // The number of remaining PIN attempts.
+  // When equal to zero, signature requests will be failed immediately; when is
+  // negative, infinite number of attempts is allowed.
+  int remaining_pin_attempts_ = -1;
+  bool should_provide_certificates_ = true;
+  bool should_fail_sign_digest_requests_ = false;
+  ExtensionTestMessageListener message_listener_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_TEST_CERTIFICATE_PROVIDER_EXTENSION_H_
diff --git a/chrome/browser/certificate_provider/test_certificate_provider_extension_mixin.cc b/chrome/browser/certificate_provider/test_certificate_provider_extension_mixin.cc
new file mode 100644
index 0000000..e0babe8
--- /dev/null
+++ b/chrome/browser/certificate_provider/test_certificate_provider_extension_mixin.cc
@@ -0,0 +1,53 @@
+// Copyright 2022 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 "chrome/browser/certificate_provider/test_certificate_provider_extension_mixin.h"
+
+#include <memory>
+
+#include "chrome/browser/certificate_provider/test_certificate_provider_extension.h"
+#include "chrome/browser/policy/extension_force_install_mixin.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+
+TestCertificateProviderExtensionMixin::TestCertificateProviderExtensionMixin(
+    InProcessBrowserTestMixinHost* host,
+    ExtensionForceInstallMixin* extension_force_install_mixin)
+    : InProcessBrowserTestMixin(host),
+      extension_force_install_mixin_(extension_force_install_mixin) {}
+
+TestCertificateProviderExtensionMixin::
+    ~TestCertificateProviderExtensionMixin() = default;
+
+void TestCertificateProviderExtensionMixin::TearDownOnMainThread() {
+  certificate_provider_extension_.reset();
+}
+
+void TestCertificateProviderExtensionMixin::ForceInstall(
+    Profile* profile,
+    bool wait_on_extension_loaded,
+    bool immediately_provide_certificates) {
+  DCHECK(wait_on_extension_loaded || !immediately_provide_certificates)
+      << "When wait_on_extension_loaded is unset, "
+         "immediately_provide_certificates must also be unset!";
+  DCHECK(extension_force_install_mixin_->initialized())
+      << "ExtensionForceInstallMixin must be initialized before calling "
+         "ForceInstall!";
+  DCHECK(!certificate_provider_extension_) << "ForceInstall already called!";
+  certificate_provider_extension_ =
+      std::make_unique<TestCertificateProviderExtension>(profile);
+  ASSERT_TRUE(extension_force_install_mixin_->ForceInstallFromSourceDir(
+      TestCertificateProviderExtension::GetExtensionSourcePath(),
+      TestCertificateProviderExtension::GetExtensionPemPath(),
+      wait_on_extension_loaded
+          ? ExtensionForceInstallMixin::WaitMode::kBackgroundPageFirstLoad
+          : ExtensionForceInstallMixin::WaitMode::kPrefSet));
+  if (wait_on_extension_loaded && immediately_provide_certificates)
+    certificate_provider_extension_->TriggerSetCertificates();
+}
+
+}  // namespace ash
diff --git a/chrome/browser/certificate_provider/test_certificate_provider_extension_mixin.h b/chrome/browser/certificate_provider/test_certificate_provider_extension_mixin.h
new file mode 100644
index 0000000..5a23347c
--- /dev/null
+++ b/chrome/browser/certificate_provider/test_certificate_provider_extension_mixin.h
@@ -0,0 +1,65 @@
+// Copyright 2022 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_TEST_CERTIFICATE_PROVIDER_EXTENSION_MIXIN_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_TEST_CERTIFICATE_PROVIDER_EXTENSION_MIXIN_H_
+
+#include <memory>
+
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+
+class ExtensionForceInstallMixin;
+class Profile;
+
+namespace ash {
+
+class TestCertificateProviderExtension;
+
+// Mixin to automatically set up a TestCertificateProviderExtension.
+class TestCertificateProviderExtensionMixin final
+    : public InProcessBrowserTestMixin {
+ public:
+  explicit TestCertificateProviderExtensionMixin(
+      InProcessBrowserTestMixinHost* host,
+      ExtensionForceInstallMixin* extension_force_install_mixin);
+  TestCertificateProviderExtensionMixin(
+      const TestCertificateProviderExtensionMixin&) = delete;
+  TestCertificateProviderExtensionMixin& operator=(
+      const TestCertificateProviderExtensionMixin&) = delete;
+  ~TestCertificateProviderExtensionMixin() override;
+
+  // InProcessBrowserTestMixin:
+  void TearDownOnMainThread() override;
+
+  // Sets up the extension.
+  // Must be called after the `extension_force_install_mixin_` is initialized.
+  // Should only be called once.
+  // This asserts that the extension is installed correctly. To stop execution
+  // of a test on a failed assertion use the ASSERT_NO_FATAL_FAILURE macro.
+  // `wait_on_extension_loaded`: Waits until the extension is ready to provide
+  // certificates.
+  // `immediately_provide_certificates`: Causes the extension to provide
+  // certificates once loaded. Applies only when `wait_on_extension_loaded` is
+  // set.
+  void ForceInstall(Profile* profile,
+                    bool wait_on_extension_loaded = true,
+                    bool immediately_provide_certificates = true);
+
+  TestCertificateProviderExtension* extension() {
+    return certificate_provider_extension_.get();
+  }
+
+  const TestCertificateProviderExtension* extension() const {
+    return certificate_provider_extension_.get();
+  }
+
+ private:
+  ExtensionForceInstallMixin* const extension_force_install_mixin_;
+  std::unique_ptr<TestCertificateProviderExtension>
+      certificate_provider_extension_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_TEST_CERTIFICATE_PROVIDER_EXTENSION_MIXIN_H_
diff --git a/chrome/browser/certificate_provider/thread_safe_certificate_map.cc b/chrome/browser/certificate_provider/thread_safe_certificate_map.cc
new file mode 100644
index 0000000..a3c716f
--- /dev/null
+++ b/chrome/browser/certificate_provider/thread_safe_certificate_map.cc
@@ -0,0 +1,141 @@
+// Copyright 2015 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 "chrome/browser/certificate_provider/thread_safe_certificate_map.h"
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/certificate_provider/certificate_info.h"
+#include "net/base/hash_value.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+
+namespace chromeos {
+namespace certificate_provider {
+namespace {
+
+std::string GetSubjectPublicKeyInfo(const net::X509Certificate& certificate) {
+  base::StringPiece spki_bytes;
+  if (!net::asn1::ExtractSPKIFromDERCert(
+          net::x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer()),
+          &spki_bytes)) {
+    return {};
+  }
+  return std::string(spki_bytes);
+}
+
+}  // namespace
+
+ThreadSafeCertificateMap::ThreadSafeCertificateMap() {}
+
+ThreadSafeCertificateMap::~ThreadSafeCertificateMap() {}
+
+void ThreadSafeCertificateMap::UpdateCertificatesForExtension(
+    const std::string& extension_id,
+    const CertificateInfoList& certificates) {
+  base::AutoLock auto_lock(lock_);
+  RemoveCertificatesProvidedByExtension(extension_id);
+
+  for (const CertificateInfo& cert_info : certificates) {
+    const net::SHA256HashValue fingerprint =
+        net::X509Certificate::CalculateFingerprint256(
+            cert_info.certificate->cert_buffer());
+    fingerprint_to_extension_and_cert_[fingerprint][extension_id] = cert_info;
+
+    const std::string spki = GetSubjectPublicKeyInfo(*cert_info.certificate);
+    spki_to_extension_and_cert_[spki][extension_id] = cert_info;
+  }
+}
+
+std::vector<scoped_refptr<net::X509Certificate>>
+ThreadSafeCertificateMap::GetCertificates() {
+  base::AutoLock auto_lock(lock_);
+  std::vector<scoped_refptr<net::X509Certificate>> certificates;
+  for (const auto& fingerprint_entry : fingerprint_to_extension_and_cert_) {
+    const ExtensionToCertificateMap* extension_to_certificate_map =
+        &fingerprint_entry.second;
+    if (!extension_to_certificate_map->empty()) {
+      // If there are multiple entries with the same fingerprint, they are the
+      // same certificate as SHA256 should not have collisions.
+      // Since we need each certificate only once, we can return any entry.
+      certificates.push_back(
+          extension_to_certificate_map->begin()->second.certificate);
+    }
+  }
+  return certificates;
+}
+
+bool ThreadSafeCertificateMap::LookUpCertificate(
+    const net::X509Certificate& cert,
+    bool* is_currently_provided,
+    CertificateInfo* info,
+    std::string* extension_id) {
+  *is_currently_provided = false;
+  const net::SHA256HashValue fingerprint =
+      net::X509Certificate::CalculateFingerprint256(cert.cert_buffer());
+
+  base::AutoLock auto_lock(lock_);
+  const auto it = fingerprint_to_extension_and_cert_.find(fingerprint);
+  if (it == fingerprint_to_extension_and_cert_.end())
+    return false;
+
+  ExtensionToCertificateMap* const map = &it->second;
+  if (!map->empty()) {
+    // If multiple entries are found, it is unspecified which is returned.
+    const auto map_entry = map->begin();
+    *is_currently_provided = true;
+    *info = map_entry->second;
+    *extension_id = map_entry->first;
+  }
+  return true;
+}
+
+bool ThreadSafeCertificateMap::LookUpCertificateBySpki(
+    const std::string& subject_public_key_info,
+    bool* is_currently_provided,
+    CertificateInfo* info,
+    std::string* extension_id) {
+  *is_currently_provided = false;
+  base::AutoLock auto_lock(lock_);
+  const auto it = spki_to_extension_and_cert_.find(subject_public_key_info);
+  if (it == spki_to_extension_and_cert_.end())
+    return false;
+
+  ExtensionToCertificateMap* const map = &it->second;
+  if (!map->empty()) {
+    // If multiple entries are found, it is unspecified which is returned.
+    const auto map_entry = map->begin();
+    *is_currently_provided = true;
+    *info = map_entry->second;
+    *extension_id = map_entry->first;
+  }
+  return true;
+}
+
+void ThreadSafeCertificateMap::RemoveExtension(
+    const std::string& extension_id) {
+  base::AutoLock auto_lock(lock_);
+  RemoveCertificatesProvidedByExtension(extension_id);
+}
+
+void ThreadSafeCertificateMap::RemoveCertificatesProvidedByExtension(
+    const std::string& extension_id) {
+  for (auto& entry : fingerprint_to_extension_and_cert_) {
+    ExtensionToCertificateMap* map = &entry.second;
+    map->erase(extension_id);
+  }
+
+  for (auto& entry : spki_to_extension_and_cert_) {
+    ExtensionToCertificateMap* map = &entry.second;
+    map->erase(extension_id);
+  }
+}
+
+}  // namespace certificate_provider
+}  // namespace chromeos
diff --git a/chrome/browser/certificate_provider/thread_safe_certificate_map.h b/chrome/browser/certificate_provider/thread_safe_certificate_map.h
new file mode 100644
index 0000000..14b848fe
--- /dev/null
+++ b/chrome/browser/certificate_provider/thread_safe_certificate_map.h
@@ -0,0 +1,88 @@
+// Copyright 2015 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 CHROME_BROWSER_CERTIFICATE_PROVIDER_THREAD_SAFE_CERTIFICATE_MAP_H_
+#define CHROME_BROWSER_CERTIFICATE_PROVIDER_THREAD_SAFE_CERTIFICATE_MAP_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/certificate_provider/certificate_info.h"
+
+namespace net {
+class X509Certificate;
+struct SHA256HashValue;
+}  // namespace net
+
+namespace chromeos {
+namespace certificate_provider {
+
+class ThreadSafeCertificateMap {
+ public:
+  ThreadSafeCertificateMap();
+  ThreadSafeCertificateMap(const ThreadSafeCertificateMap&) = delete;
+  ThreadSafeCertificateMap& operator=(const ThreadSafeCertificateMap&) = delete;
+  ~ThreadSafeCertificateMap();
+
+  // Updates the certificates provided by extension |extension_id| to be
+  // |certificates|.
+  void UpdateCertificatesForExtension(const std::string& extension_id,
+                                      const CertificateInfoList& certificates);
+
+  // Returns all currently provided certificates.
+  std::vector<scoped_refptr<net::X509Certificate>> GetCertificates();
+
+  // Looks up the given certificate. If the certificate was added by any
+  // previous Update() call, returns true.
+  // If this certificate was provided in the most recent Update() call,
+  // |is_currently_provided| will be set to true, |extension_id| be set to that
+  // extension's id and |info| will be set to the stored info. Otherwise, if
+  // this certificate was not provided in the the most recent Update() call,
+  // sets |is_currently_provided| to false and doesn't modify |info| and
+  // |extension_id|.
+  bool LookUpCertificate(const net::X509Certificate& cert,
+                         bool* is_currently_provided,
+                         CertificateInfo* info,
+                         std::string* extension_id);
+
+  // Looks up for certificate and extension_id based on
+  // |subject_public_key_info|, which is a DER-encoded X.509 Subject Public Key
+  // Info. If the certificate was added by previous Update() call, returns true.
+  // If this certificate was provided in the most recent Update() call,
+  // |is_currently_provided| will be set to true and |info| and |extension_id|
+  // will be populated according to the data that have been mapped to this
+  // |subject_public_key_info|. Otherwise, if this certificate was not provided
+  // in the most recent Update() call, sets |is_currently_provided| to false and
+  // doesn't modify |info| and |extension_id|. If multiple entries are found, it
+  // is unspecified which one will be returned.
+  bool LookUpCertificateBySpki(const std::string& subject_public_key_info,
+                               bool* is_currently_provided,
+                               CertificateInfo* info,
+                               std::string* extension_id);
+
+  // Remove every association of stored certificates to the given extension.
+  // The certificates themselves will be remembered.
+  void RemoveExtension(const std::string& extension_id);
+
+ private:
+  void RemoveCertificatesProvidedByExtension(const std::string& extension_id);
+
+  base::Lock lock_;
+  using ExtensionToCertificateMap =
+      base::flat_map<std::string, CertificateInfo>;
+  // A map that has the certificates' fingerprints as keys.
+  base::flat_map<net::SHA256HashValue, ExtensionToCertificateMap>
+      fingerprint_to_extension_and_cert_;
+  // A map that has a DER-encoded X.509 Subject Public Key Info as keys.
+  base::flat_map<std::string, ExtensionToCertificateMap>
+      spki_to_extension_and_cert_;
+};
+
+}  // namespace certificate_provider
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CERTIFICATE_PROVIDER_THREAD_SAFE_CERTIFICATE_MAP_H_