Refactor client cert private key handling.
This removes the global FetchClientCertPrivateKey function, which needed to be able to find the private key given just an X509Certificate object. Now, ClientCertStore returns ClientCertIdentity objects, which contain a certificate and have a method to asynchronously retrieve the matching SSLPrivateKey object. Also re-enable SSLClientCertificateSelectorCocoaTest.Basic. BUG=394131,313243,222296 Review-Url: https://codereview.chromium.org/2898573002 Cr-Commit-Position: refs/heads/master@{#480556}
This commit is contained in:
android_webview/browser
aw_content_browser_client.ccaw_content_browser_client.haw_contents_client_bridge.ccaw_contents_client_bridge.haw_contents_client_bridge_unittest.cc
chrome/browser
chrome_content_browser_client.ccchrome_content_browser_client.h
chromeos
net
platform_keys
devtools
prerender
ssl
ssl_client_auth_observer.ccssl_client_auth_observer.hssl_client_auth_requestor_mock.ccssl_client_auth_requestor_mock.hssl_client_certificate_selector.h
ui
chromecast/browser
cast_content_browser_client.cccast_content_browser_client.hcast_network_delegate.hcast_network_delegate_simple.cc
content
browser
loader
ssl
public
shell
crypto
net
BUILD.gn
cert
http
failing_http_transaction_factory.cchttp_cache_transaction.cchttp_cache_transaction.hhttp_network_transaction.cchttp_network_transaction.hhttp_transaction.hhttp_transaction_test_util.cchttp_transaction_test_util.h
ssl
client_cert_identity.ccclient_cert_identity.hclient_cert_identity_mac.ccclient_cert_identity_mac.hclient_cert_identity_test_util.ccclient_cert_identity_test_util.hclient_cert_identity_unittest.ccclient_cert_store.hclient_cert_store_mac.ccclient_cert_store_mac.hclient_cert_store_mac_unittest.ccclient_cert_store_nss.ccclient_cert_store_nss.hclient_cert_store_nss_unittest.ccclient_cert_store_unittest-inl.hclient_cert_store_win.ccclient_cert_store_win.hclient_cert_store_win_unittest.ccclient_key_store.hopenssl_client_key_store.ccopenssl_client_key_store.hopenssl_client_key_store_unittest.ccssl_client_auth_cache.ccssl_client_auth_cache.hssl_platform_key.hssl_platform_key_android.ccssl_platform_key_chromecast.ccssl_platform_key_chromecast_unittest.ccssl_platform_key_mac.ccssl_platform_key_mac.hssl_platform_key_mac_unittest.ccssl_platform_key_nss.ccssl_platform_key_nss.hssl_platform_key_nss_unittest.ccssl_platform_key_win.ccssl_platform_key_win.htest_ssl_private_key.cctest_ssl_private_key.h
test
url_request
remoting/host
@ -390,7 +390,7 @@ void AwContentBrowserClient::AllowCertificateError(
|
||||
void AwContentBrowserClient::SelectClientCertificate(
|
||||
content::WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) {
|
||||
AwContentsClientBridge* client =
|
||||
AwContentsClientBridge::FromWebContents(web_contents);
|
||||
|
@ -88,7 +88,7 @@ class AwContentBrowserClient : public content::ContentBrowserClient {
|
||||
void SelectClientCertificate(
|
||||
content::WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
|
||||
bool CanCreateWindow(content::RenderFrameHost* opener,
|
||||
const GURL& opener_url,
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "base/android/jni_android.h"
|
||||
#include "base/android/jni_array.h"
|
||||
#include "base/android/jni_string.h"
|
||||
#include "base/callback_helpers.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
@ -27,7 +26,6 @@
|
||||
#include "jni/AwContentsClientBridge_jni.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/ssl/openssl_client_key_store.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_client_cert_type.h"
|
||||
#include "net/ssl/ssl_platform_key_android.h"
|
||||
@ -51,15 +49,6 @@ namespace android_webview {
|
||||
|
||||
namespace {
|
||||
|
||||
// Must be called on the I/O thread to record a client certificate
|
||||
// and its private key in the OpenSSLClientKeyStore.
|
||||
void RecordClientCertificateKey(net::X509Certificate* client_cert,
|
||||
scoped_refptr<net::SSLPrivateKey> private_key) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
|
||||
client_cert, std::move(private_key));
|
||||
}
|
||||
|
||||
const void* const kAwContentsClientBridge = &kAwContentsClientBridge;
|
||||
|
||||
// This class is invented so that the UserData registry that we inject the
|
||||
@ -135,12 +124,6 @@ AwContentsClientBridge::~AwContentsClientBridge() {
|
||||
// it is possible that java object lifetime can exceed the AwContens.
|
||||
Java_AwContentsClientBridge_setNativeContentsClientBridge(env, obj, 0);
|
||||
}
|
||||
|
||||
for (IDMap<content::ClientCertificateDelegate*>::iterator iter(
|
||||
&pending_client_cert_request_delegates_);
|
||||
!iter.IsAtEnd(); iter.Advance()) {
|
||||
delete iter.GetCurrentValue();
|
||||
}
|
||||
}
|
||||
|
||||
void AwContentsClientBridge::AllowCertificateError(
|
||||
@ -197,14 +180,6 @@ void AwContentsClientBridge::SelectClientCertificate(
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
// Add the callback to id map.
|
||||
int request_id =
|
||||
pending_client_cert_request_delegates_.Add(delegate.release());
|
||||
// Make sure callback is run on error.
|
||||
base::ScopedClosureRunner guard(base::Bind(
|
||||
&AwContentsClientBridge::HandleErrorInClientCertificateResponse,
|
||||
base::Unretained(this), request_id));
|
||||
|
||||
JNIEnv* env = base::android::AttachCurrentThread();
|
||||
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
|
||||
if (obj.is_null())
|
||||
@ -248,12 +223,11 @@ void AwContentsClientBridge::SelectClientCertificate(
|
||||
base::android::ConvertUTF8ToJavaString(
|
||||
env, cert_request_info->host_and_port.host());
|
||||
|
||||
int request_id =
|
||||
pending_client_cert_request_delegates_.Add(std::move(delegate));
|
||||
Java_AwContentsClientBridge_selectClientCertificate(
|
||||
env, obj, request_id, key_types_ref, principals_ref, host_name_ref,
|
||||
cert_request_info->host_and_port.port());
|
||||
|
||||
// Release the guard.
|
||||
ignore_result(guard.Release());
|
||||
}
|
||||
|
||||
// This method is inspired by OnSystemRequestCompletion() in
|
||||
@ -266,23 +240,17 @@ void AwContentsClientBridge::ProvideClientCertificateResponse(
|
||||
const JavaRef<jobject>& private_key_ref) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
content::ClientCertificateDelegate* delegate =
|
||||
pending_client_cert_request_delegates_.Lookup(request_id);
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate =
|
||||
pending_client_cert_request_delegates_.Replace(request_id, nullptr);
|
||||
pending_client_cert_request_delegates_.Remove(request_id);
|
||||
DCHECK(delegate);
|
||||
|
||||
if (encoded_chain_ref.is_null() || private_key_ref.is_null()) {
|
||||
LOG(ERROR) << "No client certificate selected";
|
||||
pending_client_cert_request_delegates_.Remove(request_id);
|
||||
delegate->ContinueWithCertificate(nullptr);
|
||||
delete delegate;
|
||||
delegate->ContinueWithCertificate(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure callback is run on error.
|
||||
base::ScopedClosureRunner guard(base::Bind(
|
||||
&AwContentsClientBridge::HandleErrorInClientCertificateResponse,
|
||||
base::Unretained(this), request_id));
|
||||
|
||||
// Convert the encoded chain to a vector of strings.
|
||||
std::vector<std::string> encoded_chain_strings;
|
||||
if (!encoded_chain_ref.is_null()) {
|
||||
@ -310,20 +278,8 @@ void AwContentsClientBridge::ProvideClientCertificateResponse(
|
||||
return;
|
||||
}
|
||||
|
||||
// Release the guard and |pending_client_cert_request_delegates_| references
|
||||
// to |delegate|.
|
||||
pending_client_cert_request_delegates_.Remove(request_id);
|
||||
ignore_result(guard.Release());
|
||||
|
||||
// RecordClientCertificateKey() must be called on the I/O thread,
|
||||
// before the delegate is called with the selected certificate on
|
||||
// the UI thread.
|
||||
content::BrowserThread::PostTaskAndReply(
|
||||
content::BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&RecordClientCertificateKey, base::RetainedRef(client_cert),
|
||||
base::Passed(&private_key)),
|
||||
base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate,
|
||||
base::Owned(delegate), base::RetainedRef(client_cert)));
|
||||
delegate->ContinueWithCertificate(std::move(client_cert),
|
||||
std::move(private_key));
|
||||
}
|
||||
|
||||
void AwContentsClientBridge::RunJavaScriptDialog(
|
||||
@ -576,16 +532,6 @@ void AwContentsClientBridge::CancelJsResult(JNIEnv*,
|
||||
pending_js_dialog_callbacks_.Remove(id);
|
||||
}
|
||||
|
||||
// Use to cleanup if there is an error in client certificate response.
|
||||
void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
|
||||
int request_id) {
|
||||
content::ClientCertificateDelegate* delegate =
|
||||
pending_client_cert_request_delegates_.Lookup(request_id);
|
||||
pending_client_cert_request_delegates_.Remove(request_id);
|
||||
|
||||
delete delegate;
|
||||
}
|
||||
|
||||
bool RegisterAwContentsClientBridge(JNIEnv* env) {
|
||||
return RegisterNativesImpl(env);
|
||||
}
|
||||
|
@ -122,8 +122,6 @@ class AwContentsClientBridge {
|
||||
void CancelJsResult(JNIEnv*, const base::android::JavaRef<jobject>&, int id);
|
||||
|
||||
private:
|
||||
void HandleErrorInClientCertificateResponse(int id);
|
||||
|
||||
JavaObjectWeakGlobalRef java_ref_;
|
||||
|
||||
typedef const base::Callback<void(content::CertificateRequestResultType)>
|
||||
@ -131,9 +129,7 @@ class AwContentsClientBridge {
|
||||
IDMap<std::unique_ptr<CertErrorCallback>> pending_cert_error_callbacks_;
|
||||
IDMap<std::unique_ptr<content::JavaScriptDialogManager::DialogClosedCallback>>
|
||||
pending_js_dialog_callbacks_;
|
||||
// |pending_client_cert_request_delegates_| owns its pointers, but IDMap
|
||||
// doesn't provide Release, so ownership is managed manually.
|
||||
IDMap<content::ClientCertificateDelegate*>
|
||||
IDMap<std::unique_ptr<content::ClientCertificateDelegate>>
|
||||
pending_client_cert_request_delegates_;
|
||||
};
|
||||
|
||||
|
@ -17,7 +17,9 @@
|
||||
#include "content/public/test/test_browser_thread_bundle.h"
|
||||
#include "jni/MockAwContentsClientBridge_jni.h"
|
||||
#include "net/android/net_jni_registrar.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
@ -25,6 +27,7 @@ using base::android::AttachCurrentThread;
|
||||
using base::android::ScopedJavaLocalRef;
|
||||
using net::SSLCertRequestInfo;
|
||||
using net::SSLClientCertType;
|
||||
using net::SSLPrivateKey;
|
||||
using net::X509Certificate;
|
||||
using testing::NotNull;
|
||||
using testing::Test;
|
||||
@ -39,7 +42,8 @@ class AwContentsClientBridgeTest : public Test {
|
||||
AwContentsClientBridgeTest() {}
|
||||
|
||||
// Callback method called when a cert is selected.
|
||||
void CertSelected(X509Certificate* cert);
|
||||
void CertSelected(scoped_refptr<X509Certificate> cert,
|
||||
scoped_refptr<SSLPrivateKey> key);
|
||||
|
||||
protected:
|
||||
void SetUp() override;
|
||||
@ -49,7 +53,8 @@ class AwContentsClientBridgeTest : public Test {
|
||||
base::android::ScopedJavaGlobalRef<jobject> jbridge_;
|
||||
std::unique_ptr<AwContentsClientBridge> bridge_;
|
||||
scoped_refptr<SSLCertRequestInfo> cert_request_info_;
|
||||
X509Certificate* selected_cert_;
|
||||
scoped_refptr<X509Certificate> selected_cert_;
|
||||
scoped_refptr<SSLPrivateKey> selected_key_;
|
||||
int cert_selected_callbacks_;
|
||||
JNIEnv* env_;
|
||||
};
|
||||
@ -61,8 +66,9 @@ class TestClientCertificateDelegate
|
||||
: test_(test) {}
|
||||
|
||||
// content::ClientCertificateDelegate.
|
||||
void ContinueWithCertificate(net::X509Certificate* cert) override {
|
||||
test_->CertSelected(cert);
|
||||
void ContinueWithCertificate(scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> key) override {
|
||||
test_->CertSelected(std::move(cert), std::move(key));
|
||||
test_ = nullptr;
|
||||
}
|
||||
|
||||
@ -88,8 +94,11 @@ void AwContentsClientBridgeTest::SetUp() {
|
||||
cert_request_info_ = new net::SSLCertRequestInfo;
|
||||
}
|
||||
|
||||
void AwContentsClientBridgeTest::CertSelected(X509Certificate* cert) {
|
||||
selected_cert_ = cert;
|
||||
void AwContentsClientBridgeTest::CertSelected(
|
||||
scoped_refptr<X509Certificate> cert,
|
||||
scoped_refptr<SSLPrivateKey> key) {
|
||||
selected_cert_ = std::move(cert);
|
||||
selected_key_ = std::move(key);
|
||||
cert_selected_callbacks_++;
|
||||
}
|
||||
|
||||
@ -137,7 +146,8 @@ TEST_F(AwContentsClientBridgeTest,
|
||||
Java_MockAwContentsClientBridge_createTestCertChain(env_, jbridge_),
|
||||
nullptr);
|
||||
base::RunLoop().RunUntilIdle();
|
||||
EXPECT_EQ(nullptr, selected_cert_);
|
||||
EXPECT_EQ(nullptr, selected_cert_.get());
|
||||
EXPECT_EQ(nullptr, selected_key_.get());
|
||||
EXPECT_EQ(1, cert_selected_callbacks_);
|
||||
}
|
||||
|
||||
@ -154,7 +164,8 @@ TEST_F(AwContentsClientBridgeTest,
|
||||
bridge_->ProvideClientCertificateResponse(env_, jbridge_, requestId, nullptr,
|
||||
nullptr);
|
||||
base::RunLoop().RunUntilIdle();
|
||||
EXPECT_EQ(nullptr, selected_cert_);
|
||||
EXPECT_EQ(nullptr, selected_cert_.get());
|
||||
EXPECT_EQ(nullptr, selected_key_.get());
|
||||
EXPECT_EQ(1, cert_selected_callbacks_);
|
||||
}
|
||||
|
||||
|
@ -2277,7 +2277,7 @@ void ChromeContentBrowserClient::AllowCertificateError(
|
||||
void ChromeContentBrowserClient::SelectClientCertificate(
|
||||
content::WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) {
|
||||
prerender::PrerenderContents* prerender_contents =
|
||||
prerender::PrerenderContents::FromWebContents(web_contents);
|
||||
@ -2306,9 +2306,17 @@ void ChromeContentBrowserClient::SelectClientCertificate(
|
||||
static_cast<base::DictionaryValue*>(filter.get());
|
||||
|
||||
for (size_t i = 0; i < client_certs.size(); ++i) {
|
||||
if (CertMatchesFilter(*client_certs[i].get(), *filter_dict)) {
|
||||
if (CertMatchesFilter(*client_certs[i]->certificate(), *filter_dict)) {
|
||||
// Use the first certificate that is matched by the filter.
|
||||
delegate->ContinueWithCertificate(client_certs[i].get());
|
||||
// The callback will own |client_certs[i]| and |delegate|, keeping
|
||||
// them alive until after ContinueWithCertificate is called.
|
||||
scoped_refptr<net::X509Certificate> cert =
|
||||
client_certs[i]->certificate();
|
||||
net::ClientCertIdentity::SelfOwningAcquirePrivateKey(
|
||||
std::move(client_certs[i]),
|
||||
base::Bind(
|
||||
&content::ClientCertificateDelegate::ContinueWithCertificate,
|
||||
base::Passed(&delegate), std::move(cert)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
|
||||
void SelectClientCertificate(
|
||||
content::WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
|
||||
content::MediaObserver* GetMediaObserver() override;
|
||||
content::PlatformNotificationService* GetPlatformNotificationService()
|
||||
|
@ -6,29 +6,51 @@
|
||||
|
||||
#include <cert.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/location.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/task_runner_util.h"
|
||||
#include "base/threading/worker_pool.h"
|
||||
#include "chrome/browser/chromeos/certificate_provider/certificate_provider.h"
|
||||
#include "crypto/nss_crypto_module_delegate.h"
|
||||
#include "net/ssl/client_key_store.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
|
||||
namespace chromeos {
|
||||
|
||||
namespace {
|
||||
|
||||
class ClientCertIdentityCros : public net::ClientCertIdentity {
|
||||
public:
|
||||
explicit ClientCertIdentityCros(scoped_refptr<net::X509Certificate> cert)
|
||||
: net::ClientCertIdentity(std::move(cert)) {}
|
||||
~ClientCertIdentityCros() override = default;
|
||||
|
||||
void AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<net::SSLPrivateKey>)>&
|
||||
private_key_callback) override {
|
||||
// There is only one implementation of ClientKeyStore and it doesn't do
|
||||
// anything blocking, so this doesn't need to run on a worker thread.
|
||||
private_key_callback.Run(
|
||||
net::ClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
|
||||
*certificate()));
|
||||
}
|
||||
};
|
||||
|
||||
class CertNotAllowedPredicate {
|
||||
public:
|
||||
explicit CertNotAllowedPredicate(
|
||||
const ClientCertStoreChromeOS::CertFilter* filter)
|
||||
: filter_(filter) {}
|
||||
bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
|
||||
return !filter_->IsCertAllowed(cert);
|
||||
bool operator()(
|
||||
const std::unique_ptr<net::ClientCertIdentity>& identity) const {
|
||||
return !filter_->IsCertAllowed(identity->certificate());
|
||||
}
|
||||
|
||||
private:
|
||||
@ -74,46 +96,41 @@ void ClientCertStoreChromeOS::GotAdditionalCerts(
|
||||
const net::SSLCertRequestInfo* request,
|
||||
const ClientCertListCallback& callback,
|
||||
const net::CertificateList& additional_certs) {
|
||||
std::unique_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate;
|
||||
if (!password_delegate_factory_.is_null()) {
|
||||
password_delegate.reset(
|
||||
password_delegate_factory_.Run(request->host_and_port));
|
||||
}
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate;
|
||||
if (!password_delegate_factory_.is_null())
|
||||
password_delegate = password_delegate_factory_.Run(request->host_and_port);
|
||||
if (base::PostTaskAndReplyWithResult(
|
||||
base::WorkerPool::GetTaskRunner(true /* task_is_slow */).get(),
|
||||
FROM_HERE,
|
||||
base::Bind(&ClientCertStoreChromeOS::GetAndFilterCertsOnWorkerThread,
|
||||
base::Unretained(this), base::Passed(&password_delegate),
|
||||
request, additional_certs),
|
||||
base::Unretained(this), password_delegate, request,
|
||||
additional_certs),
|
||||
callback)) {
|
||||
return;
|
||||
}
|
||||
// If the task could not be posted, behave as if there were no certificates.
|
||||
callback.Run(net::CertificateList());
|
||||
callback.Run(net::ClientCertIdentityList());
|
||||
}
|
||||
|
||||
net::CertificateList ClientCertStoreChromeOS::GetAndFilterCertsOnWorkerThread(
|
||||
std::unique_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
net::ClientCertIdentityList
|
||||
ClientCertStoreChromeOS::GetAndFilterCertsOnWorkerThread(
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate,
|
||||
const net::SSLCertRequestInfo* request,
|
||||
const net::CertificateList& additional_certs) {
|
||||
net::CertificateList unfiltered_certs;
|
||||
net::ClientCertIdentityList client_certs;
|
||||
net::ClientCertStoreNSS::GetPlatformCertsOnWorkerThread(
|
||||
std::move(password_delegate), &unfiltered_certs);
|
||||
std::move(password_delegate), &client_certs);
|
||||
|
||||
unfiltered_certs.erase(
|
||||
std::remove_if(unfiltered_certs.begin(), unfiltered_certs.end(),
|
||||
client_certs.erase(
|
||||
std::remove_if(client_certs.begin(), client_certs.end(),
|
||||
CertNotAllowedPredicate(cert_filter_.get())),
|
||||
unfiltered_certs.end());
|
||||
client_certs.end());
|
||||
|
||||
unfiltered_certs.insert(unfiltered_certs.end(), additional_certs.begin(),
|
||||
additional_certs.end());
|
||||
|
||||
net::CertificateList selected_certs;
|
||||
net::ClientCertStoreNSS::FilterCertsOnWorkerThread(unfiltered_certs, *request,
|
||||
&selected_certs);
|
||||
return selected_certs;
|
||||
for (const scoped_refptr<net::X509Certificate>& cert : additional_certs)
|
||||
client_certs.push_back(base::MakeUnique<ClientCertIdentityCros>(cert));
|
||||
net::ClientCertStoreNSS::FilterCertsOnWorkerThread(&client_certs, *request);
|
||||
return client_certs;
|
||||
}
|
||||
|
||||
} // namespace chromeos
|
||||
|
@ -58,8 +58,8 @@ class ClientCertStoreChromeOS : public net::ClientCertStore {
|
||||
const ClientCertListCallback& callback,
|
||||
const net::CertificateList& additional_certs);
|
||||
|
||||
net::CertificateList GetAndFilterCertsOnWorkerThread(
|
||||
std::unique_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
net::ClientCertIdentityList GetAndFilterCertsOnWorkerThread(
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate,
|
||||
const net::SSLCertRequestInfo* request,
|
||||
const net::CertificateList& additional_certs);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "chrome/browser/chromeos/certificate_provider/certificate_provider.h"
|
||||
#include "crypto/scoped_test_nss_db.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "net/test/test_data_directory.h"
|
||||
@ -74,10 +75,10 @@ class TestCertFilter : public ClientCertStoreChromeOS::CertFilter {
|
||||
scoped_refptr<net::X509Certificate> not_allowed_cert_;
|
||||
};
|
||||
|
||||
void SaveCertsAndQuitCallback(net::CertificateList* out_certs,
|
||||
base::Closure quit_closure,
|
||||
net::CertificateList in_certs) {
|
||||
*out_certs = std::move(in_certs);
|
||||
void SaveIdentitiesAndQuitCallback(net::ClientCertIdentityList* out_identities,
|
||||
base::Closure quit_closure,
|
||||
net::ClientCertIdentityList in_identities) {
|
||||
*out_identities = std::move(in_identities);
|
||||
quit_closure.Run();
|
||||
}
|
||||
|
||||
@ -119,24 +120,24 @@ TEST_F(ClientCertStoreChromeOSTest, RequestWaitsForNSSInitAndSucceeds) {
|
||||
scoped_refptr<net::SSLCertRequestInfo> request_all(
|
||||
new net::SSLCertRequestInfo());
|
||||
|
||||
net::CertificateList selected_certs;
|
||||
net::ClientCertIdentityList selected_identities;
|
||||
base::RunLoop run_loop;
|
||||
store.GetClientCerts(*request_all,
|
||||
base::Bind(SaveCertsAndQuitCallback, &selected_certs,
|
||||
run_loop.QuitClosure()));
|
||||
store.GetClientCerts(
|
||||
*request_all, base::Bind(SaveIdentitiesAndQuitCallback,
|
||||
&selected_identities, run_loop.QuitClosure()));
|
||||
|
||||
{
|
||||
base::RunLoop run_loop_inner;
|
||||
run_loop_inner.RunUntilIdle();
|
||||
// GetClientCerts should wait for the initialization of the filter to
|
||||
// finish.
|
||||
ASSERT_EQ(0u, selected_certs.size());
|
||||
ASSERT_EQ(0u, selected_identities.size());
|
||||
EXPECT_TRUE(cert_filter->init_called());
|
||||
}
|
||||
cert_filter->FinishInit();
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
}
|
||||
|
||||
// Ensure that cert requests, that are started after the filter was initialized,
|
||||
@ -158,13 +159,13 @@ TEST_F(ClientCertStoreChromeOSTest, RequestsAfterNSSInitSucceed) {
|
||||
new net::SSLCertRequestInfo());
|
||||
|
||||
base::RunLoop run_loop;
|
||||
net::CertificateList selected_certs;
|
||||
store.GetClientCerts(*request_all,
|
||||
base::Bind(SaveCertsAndQuitCallback, &selected_certs,
|
||||
run_loop.QuitClosure()));
|
||||
net::ClientCertIdentityList selected_identities;
|
||||
store.GetClientCerts(
|
||||
*request_all, base::Bind(SaveIdentitiesAndQuitCallback,
|
||||
&selected_identities, run_loop.QuitClosure()));
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
}
|
||||
|
||||
TEST_F(ClientCertStoreChromeOSTest, Filter) {
|
||||
@ -190,27 +191,27 @@ TEST_F(ClientCertStoreChromeOSTest, Filter) {
|
||||
{
|
||||
base::RunLoop run_loop;
|
||||
cert_filter->SetNotAllowedCert(cert_2);
|
||||
net::CertificateList selected_certs;
|
||||
store.GetClientCerts(*request_all,
|
||||
base::Bind(SaveCertsAndQuitCallback, &selected_certs,
|
||||
run_loop.QuitClosure()));
|
||||
net::ClientCertIdentityList selected_identities;
|
||||
store.GetClientCerts(
|
||||
*request_all, base::Bind(SaveIdentitiesAndQuitCallback,
|
||||
&selected_identities, run_loop.QuitClosure()));
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
EXPECT_TRUE(cert_1->Equals(selected_certs[0].get()));
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
EXPECT_TRUE(cert_1->Equals(selected_identities[0]->certificate()));
|
||||
}
|
||||
|
||||
{
|
||||
base::RunLoop run_loop;
|
||||
cert_filter->SetNotAllowedCert(cert_1);
|
||||
net::CertificateList selected_certs;
|
||||
store.GetClientCerts(*request_all,
|
||||
base::Bind(SaveCertsAndQuitCallback, &selected_certs,
|
||||
run_loop.QuitClosure()));
|
||||
net::ClientCertIdentityList selected_identities;
|
||||
store.GetClientCerts(
|
||||
*request_all, base::Bind(SaveIdentitiesAndQuitCallback,
|
||||
&selected_identities, run_loop.QuitClosure()));
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
EXPECT_TRUE(cert_2->Equals(selected_certs[0].get()));
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
EXPECT_TRUE(cert_2->Equals(selected_identities[0]->certificate()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,14 +242,14 @@ TEST_F(ClientCertStoreChromeOSTest, CertRequestMatching) {
|
||||
request->cert_authorities = authority_1;
|
||||
|
||||
base::RunLoop run_loop;
|
||||
net::CertificateList selected_certs;
|
||||
store.GetClientCerts(*request,
|
||||
base::Bind(SaveCertsAndQuitCallback, &selected_certs,
|
||||
run_loop.QuitClosure()));
|
||||
net::ClientCertIdentityList selected_identities;
|
||||
store.GetClientCerts(
|
||||
*request, base::Bind(SaveIdentitiesAndQuitCallback, &selected_identities,
|
||||
run_loop.QuitClosure()));
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
EXPECT_TRUE(cert_1->Equals(selected_certs[0].get()));
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
EXPECT_TRUE(cert_1->Equals(selected_identities[0]->certificate()));
|
||||
}
|
||||
|
||||
} // namespace chromeos
|
||||
|
@ -546,11 +546,17 @@ void SignRSAWithDB(std::unique_ptr<SignRSAState> state,
|
||||
// SelectCertificatesOnIOThread().
|
||||
void DidSelectCertificatesOnIOThread(
|
||||
std::unique_ptr<SelectCertificatesState> state,
|
||||
net::CertificateList certs) {
|
||||
net::ClientCertIdentityList identities) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
state->CallBack(FROM_HERE,
|
||||
base::MakeUnique<net::CertificateList>(std::move(certs)),
|
||||
std::string() /* no error */);
|
||||
// Convert the ClientCertIdentityList to a CertificateList since returning
|
||||
// ClientCertIdentities would require changing the platformKeys extension
|
||||
// api. This assumes that the necessary keys can be found later with
|
||||
// crypto::FindNSSKeyFromPublicKeyInfo.
|
||||
std::unique_ptr<net::CertificateList> certs =
|
||||
base::MakeUnique<net::CertificateList>();
|
||||
for (const std::unique_ptr<net::ClientCertIdentity>& identity : identities)
|
||||
certs->push_back(identity->certificate());
|
||||
state->CallBack(FROM_HERE, std::move(certs), std::string() /* no error */);
|
||||
}
|
||||
|
||||
// Continues selecting certificates on the IO thread. Used by
|
||||
|
@ -168,18 +168,18 @@ int DevToolsNetworkTransaction::RestartIgnoringLastError(
|
||||
}
|
||||
|
||||
int DevToolsNetworkTransaction::RestartWithCertificate(
|
||||
net::X509Certificate* client_cert,
|
||||
net::SSLPrivateKey* client_private_key,
|
||||
scoped_refptr<net::X509Certificate> client_cert,
|
||||
scoped_refptr<net::SSLPrivateKey> client_private_key,
|
||||
const net::CompletionCallback& callback) {
|
||||
if (CheckFailed())
|
||||
return net::ERR_INTERNET_DISCONNECTED;
|
||||
if (!interceptor_) {
|
||||
return network_transaction_->RestartWithCertificate(
|
||||
client_cert, client_private_key, callback);
|
||||
std::move(client_cert), std::move(client_private_key), callback);
|
||||
}
|
||||
|
||||
int result = network_transaction_->RestartWithCertificate(
|
||||
client_cert, client_private_key,
|
||||
std::move(client_cert), std::move(client_private_key),
|
||||
base::Bind(&DevToolsNetworkTransaction::IOCallback,
|
||||
base::Unretained(this), callback, true));
|
||||
return Throttle(callback, true, result);
|
||||
|
@ -59,9 +59,10 @@ class DevToolsNetworkTransaction
|
||||
const net::NetLogWithSource& net_log) override;
|
||||
int RestartIgnoringLastError(
|
||||
const net::CompletionCallback& callback) override;
|
||||
int RestartWithCertificate(net::X509Certificate* client_cert,
|
||||
net::SSLPrivateKey* client_private_key,
|
||||
const net::CompletionCallback& callback) override;
|
||||
int RestartWithCertificate(
|
||||
scoped_refptr<net::X509Certificate> client_cert,
|
||||
scoped_refptr<net::SSLPrivateKey> client_private_key,
|
||||
const net::CompletionCallback& callback) override;
|
||||
int RestartWithAuth(const net::AuthCredentials& credentials,
|
||||
const net::CompletionCallback& callback) override;
|
||||
bool IsReadyToRestartForAuth() override;
|
||||
|
@ -114,6 +114,7 @@
|
||||
#include "net/base/escape.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/dns/mock_host_resolver.h"
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
#include "net/ssl/client_cert_store.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_server_config.h"
|
||||
@ -2257,7 +2258,7 @@ class TestClientCertStore : public net::ClientCertStore {
|
||||
// net::ClientCertStore:
|
||||
void GetClientCerts(const net::SSLCertRequestInfo& cert_request_info,
|
||||
const ClientCertListCallback& callback) override {
|
||||
callback.Run(certs_);
|
||||
callback.Run(FakeClientCertIdentityListFromCertificateList(certs_));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include "chrome/browser/ssl/ssl_client_auth_observer.h"
|
||||
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/logging.h"
|
||||
@ -14,10 +14,12 @@
|
||||
#include "content/public/browser/notification_service.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
typedef std::pair<net::SSLCertRequestInfo*, net::X509Certificate*> CertDetails;
|
||||
using CertDetails = std::
|
||||
tuple<net::SSLCertRequestInfo*, net::X509Certificate*, net::SSLPrivateKey*>;
|
||||
|
||||
SSLClientAuthObserver::SSLClientAuthObserver(
|
||||
const content::BrowserContext* browser_context,
|
||||
@ -31,7 +33,8 @@ SSLClientAuthObserver::~SSLClientAuthObserver() {
|
||||
}
|
||||
|
||||
void SSLClientAuthObserver::CertificateSelected(
|
||||
net::X509Certificate* certificate) {
|
||||
net::X509Certificate* certificate,
|
||||
net::SSLPrivateKey* private_key) {
|
||||
if (!delegate_)
|
||||
return;
|
||||
|
||||
@ -39,16 +42,14 @@ void SSLClientAuthObserver::CertificateSelected(
|
||||
// avoid getting a self-notification.
|
||||
StopObserving();
|
||||
|
||||
CertDetails details;
|
||||
details.first = cert_request_info_.get();
|
||||
details.second = certificate;
|
||||
CertDetails details(cert_request_info_.get(), certificate, private_key);
|
||||
content::NotificationService* service =
|
||||
content::NotificationService::current();
|
||||
service->Notify(chrome::NOTIFICATION_SSL_CLIENT_AUTH_CERT_SELECTED,
|
||||
content::Source<content::BrowserContext>(browser_context_),
|
||||
content::Details<CertDetails>(&details));
|
||||
|
||||
delegate_->ContinueWithCertificate(certificate);
|
||||
delegate_->ContinueWithCertificate(certificate, private_key);
|
||||
delegate_.reset();
|
||||
}
|
||||
|
||||
@ -70,14 +71,15 @@ void SSLClientAuthObserver::Observe(
|
||||
DCHECK_EQ(chrome::NOTIFICATION_SSL_CLIENT_AUTH_CERT_SELECTED, type);
|
||||
|
||||
CertDetails* cert_details = content::Details<CertDetails>(details).ptr();
|
||||
if (!cert_details->first->host_and_port.Equals(
|
||||
cert_request_info_->host_and_port))
|
||||
if (!std::get<0>(*cert_details)
|
||||
->host_and_port.Equals(cert_request_info_->host_and_port))
|
||||
return;
|
||||
|
||||
DVLOG(1) << this << " got matching notification and selecting cert "
|
||||
<< cert_details->second;
|
||||
<< std::get<1>(*cert_details);
|
||||
StopObserving();
|
||||
delegate_->ContinueWithCertificate(cert_details->second);
|
||||
delegate_->ContinueWithCertificate(std::get<1>(*cert_details),
|
||||
std::get<2>(*cert_details));
|
||||
delegate_.reset();
|
||||
OnCertSelectedByNotification();
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
namespace net {
|
||||
class SSLCertRequestInfo;
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
}
|
||||
|
||||
@ -41,7 +42,8 @@ class SSLClientAuthObserver : public content::NotificationObserver {
|
||||
// Continues the request with a certificate. Can also call with NULL to
|
||||
// continue with no certificate. Derived classes must use this instead of
|
||||
// caching the delegate and calling it directly.
|
||||
void CertificateSelected(net::X509Certificate* cert);
|
||||
void CertificateSelected(net::X509Certificate* cert,
|
||||
net::SSLPrivateKey* private_key);
|
||||
|
||||
// Cancels the certificate selection and aborts the request.
|
||||
void CancelCertificateSelection();
|
||||
|
@ -24,8 +24,9 @@ class FakeClientCertificateDelegate
|
||||
}
|
||||
|
||||
// content::ClientCertificateDelegate implementation:
|
||||
void ContinueWithCertificate(net::X509Certificate* cert) override {
|
||||
requestor_->CertificateSelected(cert);
|
||||
void ContinueWithCertificate(scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> key) override {
|
||||
requestor_->CertificateSelected(cert.get(), key.get());
|
||||
requestor_ = nullptr;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ class ClientCertificateDelegate;
|
||||
|
||||
namespace net {
|
||||
class SSLCertRequestInfo;
|
||||
class SSLPrivateKey;
|
||||
class URLRequest;
|
||||
class X509Certificate;
|
||||
}
|
||||
@ -29,7 +30,8 @@ class SSLClientAuthRequestorMock
|
||||
|
||||
std::unique_ptr<content::ClientCertificateDelegate> CreateDelegate();
|
||||
|
||||
MOCK_METHOD1(CertificateSelected, void(net::X509Certificate* cert));
|
||||
MOCK_METHOD2(CertificateSelected,
|
||||
void(net::X509Certificate* cert, net::SSLPrivateKey* key));
|
||||
MOCK_METHOD0(CancelCertificateSelection, void());
|
||||
|
||||
scoped_refptr<net::SSLCertRequestInfo> cert_request_info_;
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
|
||||
namespace content {
|
||||
class ClientCertificateDelegate;
|
||||
@ -29,7 +29,7 @@ namespace chrome {
|
||||
void ShowSSLClientCertificateSelector(
|
||||
content::WebContents* contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate);
|
||||
|
||||
} // namespace chrome
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include "net/base/host_port_pair.h"
|
||||
#include "net/cert/cert_database.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/openssl_client_key_store.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_client_cert_type.h"
|
||||
#include "net/ssl/ssl_platform_key_android.h"
|
||||
@ -37,15 +36,6 @@ namespace chrome {
|
||||
|
||||
namespace {
|
||||
|
||||
// Must be called on the I/O thread to record a client certificate
|
||||
// and its private key in the OpenSSLClientKeyStore.
|
||||
void RecordClientCertificateKey(net::X509Certificate* client_cert,
|
||||
scoped_refptr<net::SSLPrivateKey> private_key) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
|
||||
client_cert, std::move(private_key));
|
||||
}
|
||||
|
||||
void StartClientCertificateRequest(
|
||||
const net::SSLCertRequestInfo* cert_request_info,
|
||||
ui::WindowAndroid* window,
|
||||
@ -135,7 +125,7 @@ static void OnSystemRequestCompletion(
|
||||
|
||||
if (encoded_chain_ref == NULL || private_key_ref == NULL) {
|
||||
LOG(ERROR) << "No client certificate selected";
|
||||
delegate->ContinueWithCertificate(nullptr);
|
||||
delegate->ContinueWithCertificate(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -166,16 +156,8 @@ static void OnSystemRequestCompletion(
|
||||
return;
|
||||
}
|
||||
|
||||
// RecordClientCertificateKey() must be called on the I/O thread,
|
||||
// before the callback is called with the selected certificate on
|
||||
// the UI thread.
|
||||
content::BrowserThread::PostTaskAndReply(
|
||||
content::BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&RecordClientCertificateKey, base::RetainedRef(client_cert),
|
||||
base::Passed(&private_key)),
|
||||
base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate,
|
||||
base::Owned(delegate.release()),
|
||||
base::RetainedRef(client_cert)));
|
||||
delegate->ContinueWithCertificate(std::move(client_cert),
|
||||
std::move(private_key));
|
||||
}
|
||||
|
||||
static void NotifyClientCertificatesChanged() {
|
||||
@ -204,7 +186,7 @@ bool RegisterSSLClientCertificateRequestAndroid(JNIEnv* env) {
|
||||
void ShowSSLClientCertificateSelector(
|
||||
content::WebContents* contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList unused_client_certs,
|
||||
net::ClientCertIdentityList unused_client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) {
|
||||
ui::WindowAndroid* window = ViewAndroidHelper::FromWebContents(contents)
|
||||
->GetViewAndroid()->GetWindowAndroid();
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "chrome/browser/ssl/ssl_client_certificate_selector.h"
|
||||
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
|
||||
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
@ -30,10 +30,10 @@ class SSLClientAuthObserverCocoaBridge;
|
||||
@interface SSLClientCertificateSelectorCocoa
|
||||
: NSObject<ConstrainedWindowSheet> {
|
||||
@private
|
||||
// The list of identities offered to the user.
|
||||
base::ScopedCFTypeRef<CFMutableArrayRef> identities_;
|
||||
// The corresponding list of certificates.
|
||||
std::vector<scoped_refptr<net::X509Certificate>> certificates_;
|
||||
// The list of SecIdentityRefs offered to the user.
|
||||
base::ScopedCFTypeRef<CFMutableArrayRef> sec_identities_;
|
||||
// The corresponding list of ClientCertIdentities.
|
||||
net::ClientCertIdentityList cert_identities_;
|
||||
// A C++ object to bridge SSLClientAuthObserver notifications to us.
|
||||
std::unique_ptr<SSLClientAuthObserverCocoaBridge> observer_;
|
||||
base::scoped_nsobject<SFChooseIdentityPanel> panel_;
|
||||
@ -56,7 +56,7 @@ class SSLClientAuthObserverCocoaBridge;
|
||||
(std::unique_ptr<content::ClientCertificateDelegate>)
|
||||
delegate;
|
||||
- (void)displayForWebContents:(content::WebContents*)webContents
|
||||
clientCerts:(net::CertificateList)inputClientCerts;
|
||||
clientCerts:(net::ClientCertIdentityList)inputClientCerts;
|
||||
- (void)closeWebContentsModalDialog;
|
||||
|
||||
- (NSWindow*)overlayWindow;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/cert/x509_util_mac.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_platform_key_mac.h"
|
||||
#include "ui/base/cocoa/window_size_constants.h"
|
||||
#include "ui/base/l10n/l10n_util_mac.h"
|
||||
|
||||
@ -36,7 +37,7 @@ using content::BrowserThread;
|
||||
// A system-private interface that dismisses a panel whose sheet was started by
|
||||
// -beginSheetForWindow:modalDelegate:didEndSelector:contextInfo:identities:message:
|
||||
// as though the user clicked the button identified by returnCode. Verified
|
||||
// present in 10.5 through 10.8.
|
||||
// present in 10.5 through 10.12.
|
||||
- (void)_dismissWithCode:(NSInteger)code;
|
||||
@end
|
||||
|
||||
@ -81,7 +82,7 @@ namespace chrome {
|
||||
void ShowSSLClientCertificateSelector(
|
||||
content::WebContents* contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
@ -177,13 +178,13 @@ void ClearTableViewDataSourcesIfNeeded(NSWindow*) {}
|
||||
- (void)sheetDidEnd:(NSWindow*)sheet
|
||||
returnCode:(NSInteger)returnCode
|
||||
context:(void*)context {
|
||||
net::X509Certificate* cert = NULL;
|
||||
net::ClientCertIdentity* cert = nullptr;
|
||||
if (returnCode == NSFileHandlingPanelOKButton) {
|
||||
CFRange range = CFRangeMake(0, CFArrayGetCount(identities_));
|
||||
CFRange range = CFRangeMake(0, CFArrayGetCount(sec_identities_));
|
||||
CFIndex index =
|
||||
CFArrayGetFirstIndexOfValue(identities_, range, [panel_ identity]);
|
||||
CFArrayGetFirstIndexOfValue(sec_identities_, range, [panel_ identity]);
|
||||
if (index != -1)
|
||||
cert = certificates_[index].get();
|
||||
cert = cert_identities_[index].get();
|
||||
else
|
||||
NOTREACHED();
|
||||
}
|
||||
@ -197,30 +198,32 @@ void ClearTableViewDataSourcesIfNeeded(NSWindow*) {}
|
||||
// certificate. Otherwise, tell the backend which identity (or none) the
|
||||
// user selected.
|
||||
userResponded_ = YES;
|
||||
observer_->CertificateSelected(cert);
|
||||
|
||||
if (cert) {
|
||||
observer_->CertificateSelected(
|
||||
cert->certificate(),
|
||||
CreateSSLPrivateKeyForSecIdentity(cert->certificate(),
|
||||
cert->sec_identity_ref())
|
||||
.get());
|
||||
} else {
|
||||
observer_->CertificateSelected(nullptr, nullptr);
|
||||
}
|
||||
|
||||
constrainedWindow_->CloseWebContentsModalDialog();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)displayForWebContents:(content::WebContents*)webContents
|
||||
clientCerts:(net::CertificateList)inputClientCerts {
|
||||
clientCerts:(net::ClientCertIdentityList)inputClientCerts {
|
||||
cert_identities_ = std::move(inputClientCerts);
|
||||
// Create an array of CFIdentityRefs for the certificates:
|
||||
size_t numCerts = inputClientCerts.size();
|
||||
identities_.reset(CFArrayCreateMutable(
|
||||
kCFAllocatorDefault, numCerts, &kCFTypeArrayCallBacks));
|
||||
size_t numCerts = cert_identities_.size();
|
||||
sec_identities_.reset(CFArrayCreateMutable(kCFAllocatorDefault, numCerts,
|
||||
&kCFTypeArrayCallBacks));
|
||||
for (size_t i = 0; i < numCerts; ++i) {
|
||||
base::ScopedCFTypeRef<SecCertificateRef> cert(
|
||||
net::x509_util::CreateSecCertificateFromX509Certificate(
|
||||
inputClientCerts[i].get()));
|
||||
if (!cert)
|
||||
continue;
|
||||
SecIdentityRef identity;
|
||||
if (SecIdentityCreateWithCertificate(NULL, cert, &identity) == noErr) {
|
||||
CFArrayAppendValue(identities_, identity);
|
||||
CFRelease(identity);
|
||||
certificates_.push_back(inputClientCerts[i]);
|
||||
}
|
||||
DCHECK(cert_identities_[i]->sec_identity_ref());
|
||||
CFArrayAppendValue(sec_identities_,
|
||||
cert_identities_[i]->sec_identity_ref());
|
||||
}
|
||||
|
||||
// Get the message to display:
|
||||
@ -265,7 +268,7 @@ void ClearTableViewDataSourcesIfNeeded(NSWindow*) {}
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(sheetDidEnd:returnCode:context:)
|
||||
contextInfo:NULL
|
||||
identities:base::mac::CFToNSCast(identities_)
|
||||
identities:base::mac::CFToNSCast(sec_identities_)
|
||||
message:title];
|
||||
}
|
||||
|
||||
|
@ -20,39 +20,56 @@
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/test/test_utils.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity_mac.h"
|
||||
#include "net/ssl/ssl_private_key_test_util.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "net/test/keychain_test_util_mac.h"
|
||||
#include "net/test/test_data_directory.h"
|
||||
#import "testing/gtest_mac.h"
|
||||
#include "ui/base/cocoa/window_size_constants.h"
|
||||
|
||||
using web_modal::WebContentsModalDialogManager;
|
||||
|
||||
@interface SFChooseIdentityPanel (SystemPrivate)
|
||||
// A system-private interface that dismisses a panel whose sheet was started by
|
||||
// -beginSheetForWindow:modalDelegate:didEndSelector:contextInfo:identities:message:
|
||||
// as though the user clicked the button identified by returnCode. Verified
|
||||
// present in 10.5 through 10.12.
|
||||
- (void)_dismissWithCode:(NSInteger)code;
|
||||
@end
|
||||
|
||||
namespace {
|
||||
|
||||
struct TestClientCertificateDelegateResults {
|
||||
bool destroyed = false;
|
||||
bool continue_with_certificate_called = false;
|
||||
scoped_refptr<net::X509Certificate> cert;
|
||||
scoped_refptr<net::SSLPrivateKey> key;
|
||||
};
|
||||
|
||||
class TestClientCertificateDelegate
|
||||
: public content::ClientCertificateDelegate {
|
||||
public:
|
||||
// Creates a ClientCertificateDelegate that sets |*destroyed| to true on
|
||||
// destruction.
|
||||
explicit TestClientCertificateDelegate(bool* destroyed)
|
||||
: destroyed_(destroyed) {}
|
||||
explicit TestClientCertificateDelegate(
|
||||
TestClientCertificateDelegateResults* results)
|
||||
: results_(results) {}
|
||||
|
||||
~TestClientCertificateDelegate() override {
|
||||
if (destroyed_ != nullptr)
|
||||
*destroyed_ = true;
|
||||
}
|
||||
~TestClientCertificateDelegate() override { results_->destroyed = true; }
|
||||
|
||||
// content::ClientCertificateDelegate.
|
||||
void ContinueWithCertificate(net::X509Certificate* cert) override {
|
||||
// TODO(davidben): Add a test which explicitly tests selecting a
|
||||
// certificate, or selecting no certificate, since closing the dialog
|
||||
// (normally by closing the tab) is not the same as explicitly selecting no
|
||||
// certificate.
|
||||
ADD_FAILURE() << "Certificate selected";
|
||||
void ContinueWithCertificate(scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> key) override {
|
||||
EXPECT_FALSE(results_->continue_with_certificate_called);
|
||||
results_->cert = cert;
|
||||
results_->key = key;
|
||||
results_->continue_with_certificate_called = true;
|
||||
// TODO(mattm): Add a test of selecting the 2nd certificate (if possible).
|
||||
}
|
||||
|
||||
private:
|
||||
bool* destroyed_;
|
||||
TestClientCertificateDelegateResults* results_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestClientCertificateDelegate);
|
||||
};
|
||||
@ -67,12 +84,16 @@ class SSLClientCertificateSelectorCocoaTest
|
||||
// InProcessBrowserTest:
|
||||
void SetUpInProcessBrowserTestFixture() override;
|
||||
|
||||
net::CertificateList GetTestCertificateList();
|
||||
net::ClientCertIdentityList GetTestCertificateList();
|
||||
|
||||
private:
|
||||
scoped_refptr<net::X509Certificate> mit_davidben_cert_;
|
||||
scoped_refptr<net::X509Certificate> foaf_me_chromium_test_cert_;
|
||||
net::CertificateList client_cert_list_;
|
||||
protected:
|
||||
scoped_refptr<net::X509Certificate> client_cert1_;
|
||||
scoped_refptr<net::X509Certificate> client_cert2_;
|
||||
std::string pkcs8_key1_;
|
||||
std::string pkcs8_key2_;
|
||||
net::ScopedTestKeychain scoped_keychain_;
|
||||
base::ScopedCFTypeRef<SecIdentityRef> sec_identity1_;
|
||||
base::ScopedCFTypeRef<SecIdentityRef> sec_identity2_;
|
||||
};
|
||||
|
||||
SSLClientCertificateSelectorCocoaTest::
|
||||
@ -83,40 +104,50 @@ void SSLClientCertificateSelectorCocoaTest::SetUpInProcessBrowserTestFixture() {
|
||||
|
||||
base::FilePath certs_dir = net::GetTestCertsDirectory();
|
||||
|
||||
mit_davidben_cert_ = net::ImportCertFromFile(certs_dir, "mit.davidben.der");
|
||||
ASSERT_TRUE(mit_davidben_cert_.get());
|
||||
client_cert1_ = net::ImportCertFromFile(certs_dir, "client_1.pem");
|
||||
ASSERT_TRUE(client_cert1_);
|
||||
client_cert2_ = net::ImportCertFromFile(certs_dir, "client_2.pem");
|
||||
ASSERT_TRUE(client_cert2_);
|
||||
|
||||
foaf_me_chromium_test_cert_ =
|
||||
net::ImportCertFromFile(certs_dir, "foaf.me.chromium-test-cert.der");
|
||||
ASSERT_TRUE(foaf_me_chromium_test_cert_.get());
|
||||
ASSERT_TRUE(base::ReadFileToString(certs_dir.AppendASCII("client_1.pk8"),
|
||||
&pkcs8_key1_));
|
||||
ASSERT_TRUE(base::ReadFileToString(certs_dir.AppendASCII("client_2.pk8"),
|
||||
&pkcs8_key2_));
|
||||
|
||||
client_cert_list_.push_back(mit_davidben_cert_);
|
||||
client_cert_list_.push_back(foaf_me_chromium_test_cert_);
|
||||
ASSERT_TRUE(scoped_keychain_.Initialize());
|
||||
|
||||
sec_identity1_ = net::ImportCertAndKeyToKeychain(
|
||||
client_cert1_.get(), pkcs8_key1_, scoped_keychain_.keychain());
|
||||
ASSERT_TRUE(sec_identity1_);
|
||||
sec_identity2_ = net::ImportCertAndKeyToKeychain(
|
||||
client_cert2_.get(), pkcs8_key2_, scoped_keychain_.keychain());
|
||||
ASSERT_TRUE(sec_identity2_);
|
||||
}
|
||||
|
||||
net::CertificateList
|
||||
net::ClientCertIdentityList
|
||||
SSLClientCertificateSelectorCocoaTest::GetTestCertificateList() {
|
||||
return client_cert_list_;
|
||||
net::ClientCertIdentityList client_cert_list;
|
||||
client_cert_list.push_back(base::MakeUnique<net::ClientCertIdentityMac>(
|
||||
client_cert1_, base::ScopedCFTypeRef<SecIdentityRef>(sec_identity1_)));
|
||||
client_cert_list.push_back(base::MakeUnique<net::ClientCertIdentityMac>(
|
||||
client_cert2_, base::ScopedCFTypeRef<SecIdentityRef>(sec_identity2_)));
|
||||
return client_cert_list;
|
||||
}
|
||||
|
||||
// Flaky on 10.7; crbug.com/313243
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, DISABLED_Basic) {
|
||||
// TODO(kbr): re-enable: http://crbug.com/222296
|
||||
return;
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, Basic) {
|
||||
content::WebContents* web_contents =
|
||||
browser()->tab_strip_model()->GetActiveWebContents();
|
||||
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
|
||||
WebContentsModalDialogManager::FromWebContents(web_contents);
|
||||
EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
|
||||
|
||||
bool destroyed = false;
|
||||
TestClientCertificateDelegateResults results;
|
||||
SSLClientCertificateSelectorCocoa* selector = [
|
||||
[SSLClientCertificateSelectorCocoa alloc]
|
||||
initWithBrowserContext:web_contents->GetBrowserContext()
|
||||
certRequestInfo:auth_requestor_->cert_request_info_.get()
|
||||
delegate:base::WrapUnique(new TestClientCertificateDelegate(
|
||||
&destroyed))];
|
||||
delegate:base::WrapUnique(
|
||||
new TestClientCertificateDelegate(&results))];
|
||||
[selector displayForWebContents:web_contents
|
||||
clientCerts:GetTestCertificateList()];
|
||||
content::RunAllPendingInMessageLoop();
|
||||
@ -129,19 +160,94 @@ IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, DISABLED_Basic) {
|
||||
content::RunAllPendingInMessageLoop();
|
||||
EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
|
||||
|
||||
EXPECT_TRUE(destroyed);
|
||||
EXPECT_TRUE(results.destroyed);
|
||||
EXPECT_FALSE(results.continue_with_certificate_called);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, Cancel) {
|
||||
content::WebContents* web_contents =
|
||||
browser()->tab_strip_model()->GetActiveWebContents();
|
||||
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
|
||||
WebContentsModalDialogManager::FromWebContents(web_contents);
|
||||
EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
|
||||
|
||||
TestClientCertificateDelegateResults results;
|
||||
SSLClientCertificateSelectorCocoa* selector = [
|
||||
[SSLClientCertificateSelectorCocoa alloc]
|
||||
initWithBrowserContext:web_contents->GetBrowserContext()
|
||||
certRequestInfo:auth_requestor_->cert_request_info_.get()
|
||||
delegate:base::WrapUnique(
|
||||
new TestClientCertificateDelegate(&results))];
|
||||
[selector displayForWebContents:web_contents
|
||||
clientCerts:GetTestCertificateList()];
|
||||
content::RunAllPendingInMessageLoop();
|
||||
EXPECT_TRUE([selector panel]);
|
||||
EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
|
||||
|
||||
// Cancel the selector. Dunno if there is a better way to do this.
|
||||
[[selector panel] _dismissWithCode:NSFileHandlingPanelCancelButton];
|
||||
content::RunAllPendingInMessageLoop();
|
||||
EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
|
||||
|
||||
// ContinueWithCertificate(nullptr, nullptr) should have been called.
|
||||
EXPECT_TRUE(results.destroyed);
|
||||
EXPECT_TRUE(results.continue_with_certificate_called);
|
||||
EXPECT_FALSE(results.cert);
|
||||
EXPECT_FALSE(results.key);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, Accept) {
|
||||
content::WebContents* web_contents =
|
||||
browser()->tab_strip_model()->GetActiveWebContents();
|
||||
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
|
||||
WebContentsModalDialogManager::FromWebContents(web_contents);
|
||||
EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
|
||||
|
||||
TestClientCertificateDelegateResults results;
|
||||
SSLClientCertificateSelectorCocoa* selector = [
|
||||
[SSLClientCertificateSelectorCocoa alloc]
|
||||
initWithBrowserContext:web_contents->GetBrowserContext()
|
||||
certRequestInfo:auth_requestor_->cert_request_info_.get()
|
||||
delegate:base::WrapUnique(
|
||||
new TestClientCertificateDelegate(&results))];
|
||||
[selector displayForWebContents:web_contents
|
||||
clientCerts:GetTestCertificateList()];
|
||||
content::RunAllPendingInMessageLoop();
|
||||
EXPECT_TRUE([selector panel]);
|
||||
EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
|
||||
|
||||
// Accept the selection. Dunno if there is a better way to do this.
|
||||
[[selector panel] _dismissWithCode:NSFileHandlingPanelOKButton];
|
||||
content::RunAllPendingInMessageLoop();
|
||||
EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
|
||||
|
||||
// The first cert in the list should have been selected.
|
||||
EXPECT_TRUE(results.destroyed);
|
||||
EXPECT_TRUE(results.continue_with_certificate_called);
|
||||
EXPECT_EQ(client_cert1_, results.cert);
|
||||
ASSERT_TRUE(results.key);
|
||||
|
||||
// All Mac keys are expected to have the same hash preferences.
|
||||
std::vector<net::SSLPrivateKey::Hash> expected_hashes = {
|
||||
net::SSLPrivateKey::Hash::SHA512, net::SSLPrivateKey::Hash::SHA384,
|
||||
net::SSLPrivateKey::Hash::SHA256, net::SSLPrivateKey::Hash::SHA1,
|
||||
};
|
||||
EXPECT_EQ(expected_hashes, results.key->GetDigestPreferences());
|
||||
|
||||
TestSSLPrivateKeyMatches(results.key.get(), pkcs8_key1_);
|
||||
}
|
||||
|
||||
// Test that switching to another tab correctly hides the sheet.
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, HideShow) {
|
||||
content::WebContents* web_contents =
|
||||
browser()->tab_strip_model()->GetActiveWebContents();
|
||||
TestClientCertificateDelegateResults results;
|
||||
SSLClientCertificateSelectorCocoa* selector = [
|
||||
[SSLClientCertificateSelectorCocoa alloc]
|
||||
initWithBrowserContext:web_contents->GetBrowserContext()
|
||||
certRequestInfo:auth_requestor_->cert_request_info_.get()
|
||||
delegate:base::WrapUnique(
|
||||
new TestClientCertificateDelegate(nullptr))];
|
||||
new TestClientCertificateDelegate(&results))];
|
||||
[selector displayForWebContents:web_contents
|
||||
clientCerts:GetTestCertificateList()];
|
||||
content::RunAllPendingInMessageLoop();
|
||||
@ -160,6 +266,9 @@ IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorCocoaTest, HideShow) {
|
||||
chrome::SelectNumberedTab(browser(), 0);
|
||||
EXPECT_EQ(1.0, [sheetWindow alphaValue]);
|
||||
EXPECT_FALSE([[selector overlayWindow] ignoresMouseEvents]);
|
||||
|
||||
EXPECT_FALSE(results.destroyed);
|
||||
EXPECT_FALSE(results.continue_with_certificate_called);
|
||||
}
|
||||
|
||||
@interface DeallocTrackingSSLClientCertificateSelectorCocoa
|
||||
|
@ -25,14 +25,16 @@ class ChromeNSSCryptoModuleDelegate
|
||||
ChromeNSSCryptoModuleDelegate(chrome::CryptoModulePasswordReason reason,
|
||||
const net::HostPortPair& server);
|
||||
|
||||
~ChromeNSSCryptoModuleDelegate() override;
|
||||
|
||||
// crypto::CryptoModuleBlockingPasswordDelegate implementation.
|
||||
std::string RequestPassword(const std::string& slot_name,
|
||||
bool retry,
|
||||
bool* cancelled) override;
|
||||
|
||||
private:
|
||||
friend class base::RefCountedThreadSafe<ChromeNSSCryptoModuleDelegate>;
|
||||
|
||||
~ChromeNSSCryptoModuleDelegate() override;
|
||||
|
||||
void ShowDialog(const std::string& slot_name, bool retry);
|
||||
|
||||
void GotPassword(const std::string& password);
|
||||
|
@ -45,8 +45,8 @@ const int CertificateSelector::kTableViewHeight = 150;
|
||||
|
||||
class CertificateSelector::CertificateTableModel : public ui::TableModel {
|
||||
public:
|
||||
// |certs| and |provider_names| must have the same size.
|
||||
CertificateTableModel(const net::CertificateList& certs,
|
||||
// |identities| and |provider_names| must have the same size.
|
||||
CertificateTableModel(const net::ClientCertIdentityList& identities,
|
||||
const std::vector<std::string>& provider_names);
|
||||
|
||||
// ui::TableModel:
|
||||
@ -67,11 +67,11 @@ class CertificateSelector::CertificateTableModel : public ui::TableModel {
|
||||
};
|
||||
|
||||
CertificateSelector::CertificateTableModel::CertificateTableModel(
|
||||
const net::CertificateList& certs,
|
||||
const net::ClientCertIdentityList& identities,
|
||||
const std::vector<std::string>& provider_names) {
|
||||
DCHECK_EQ(certs.size(), provider_names.size());
|
||||
for (size_t i = 0; i < certs.size(); i++) {
|
||||
net::X509Certificate* cert = certs[i].get();
|
||||
DCHECK_EQ(identities.size(), provider_names.size());
|
||||
for (size_t i = 0; i < identities.size(); i++) {
|
||||
net::X509Certificate* cert = identities[i]->certificate();
|
||||
Row row;
|
||||
row.subject = base::UTF8ToUTF16(cert->subject().GetDisplayName());
|
||||
row.issuer = base::UTF8ToUTF16(cert->issuer().GetDisplayName());
|
||||
@ -113,14 +113,13 @@ base::string16 CertificateSelector::CertificateTableModel::GetText(
|
||||
void CertificateSelector::CertificateTableModel::SetObserver(
|
||||
ui::TableModelObserver* observer) {}
|
||||
|
||||
CertificateSelector::CertificateSelector(
|
||||
const net::CertificateList& certificates,
|
||||
content::WebContents* web_contents)
|
||||
: web_contents_(web_contents), table_(nullptr), view_cert_button_(nullptr) {
|
||||
CertificateSelector::CertificateSelector(net::ClientCertIdentityList identities,
|
||||
content::WebContents* web_contents)
|
||||
: web_contents_(web_contents) {
|
||||
CHECK(web_contents_);
|
||||
|
||||
// |provider_names| and |certificates_| are parallel arrays.
|
||||
// The entry at index |i| is the provider name for |certificates_[i]|.
|
||||
// |provider_names| and |identities_| are parallel arrays.
|
||||
// The entry at index |i| is the provider name for |identities_[i]|.
|
||||
std::vector<std::string> provider_names;
|
||||
#if defined(OS_CHROMEOS)
|
||||
chromeos::CertificateProviderService* service =
|
||||
@ -130,11 +129,12 @@ CertificateSelector::CertificateSelector(
|
||||
extensions::ExtensionRegistryFactory::GetForBrowserContext(
|
||||
web_contents->GetBrowserContext());
|
||||
|
||||
for (const auto& cert : certificates) {
|
||||
for (auto& identity : identities) {
|
||||
std::string provider_name;
|
||||
bool has_extension = false;
|
||||
std::string extension_id;
|
||||
if (service->LookUpCertificate(*cert, &has_extension, &extension_id)) {
|
||||
if (service->LookUpCertificate(*identity->certificate(), &has_extension,
|
||||
&extension_id)) {
|
||||
if (!has_extension) {
|
||||
// This certificate was provided by an extension but isn't provided by
|
||||
// any extension currently. Don't expose it to the user.
|
||||
@ -151,15 +151,15 @@ CertificateSelector::CertificateSelector(
|
||||
show_provider_column_ = true;
|
||||
} // Otherwise the certificate is provided by the platform.
|
||||
|
||||
certificates_.push_back(cert);
|
||||
identities_.push_back(std::move(identity));
|
||||
provider_names.push_back(provider_name);
|
||||
}
|
||||
#else
|
||||
provider_names.assign(certificates.size(), std::string());
|
||||
certificates_ = certificates;
|
||||
provider_names.assign(identities.size(), std::string());
|
||||
identities_ = std::move(identities);
|
||||
#endif
|
||||
|
||||
model_.reset(new CertificateTableModel(certificates_, provider_names));
|
||||
model_.reset(new CertificateTableModel(identities_, provider_names));
|
||||
}
|
||||
|
||||
CertificateSelector::~CertificateSelector() {
|
||||
@ -181,11 +181,11 @@ void CertificateSelector::Show() {
|
||||
// TODO(isandrk): A certificate that was previously provided by *both* the
|
||||
// platform and an extension will get incorrectly filtered out if the
|
||||
// extension stops providing it (both instances will be filtered out), hence
|
||||
// the |certificates_| array will be empty. Displaying a dialog with an empty
|
||||
// the |identities_| array will be empty. Displaying a dialog with an empty
|
||||
// list won't make much sense for the user, and also there are some CHECKs in
|
||||
// the code that will fail when the list is empty and that's why an early exit
|
||||
// is performed here. See crbug.com/641440 for more details.
|
||||
if (certificates_.empty()) {
|
||||
if (identities_.empty()) {
|
||||
GetWidget()->Close();
|
||||
return;
|
||||
}
|
||||
@ -239,12 +239,22 @@ ui::TableModel* CertificateSelector::table_model_for_testing() const {
|
||||
return model_.get();
|
||||
}
|
||||
|
||||
net::X509Certificate* CertificateSelector::GetSelectedCert() const {
|
||||
net::ClientCertIdentity* CertificateSelector::GetSelectedCert() const {
|
||||
const int selected = table_->FirstSelectedRow();
|
||||
if (selected < 0) // Nothing is selected in |table_|.
|
||||
return nullptr;
|
||||
CHECK_LT(static_cast<size_t>(selected), certificates_.size());
|
||||
return certificates_[selected].get();
|
||||
DCHECK_LT(static_cast<size_t>(selected), identities_.size());
|
||||
return identities_[selected].get();
|
||||
}
|
||||
|
||||
bool CertificateSelector::Accept() {
|
||||
const int selected = table_->FirstSelectedRow();
|
||||
if (selected < 0) // Nothing is selected in |table_|.
|
||||
return false;
|
||||
|
||||
DCHECK_LT(static_cast<size_t>(selected), identities_.size());
|
||||
AcceptCertificate(std::move(identities_[selected]));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CertificateSelector::CanResize() const {
|
||||
@ -278,10 +288,12 @@ ui::ModalType CertificateSelector::GetModalType() const {
|
||||
void CertificateSelector::ButtonPressed(views::Button* sender,
|
||||
const ui::Event& event) {
|
||||
if (sender == view_cert_button_) {
|
||||
net::X509Certificate* const cert = GetSelectedCert();
|
||||
if (cert)
|
||||
net::ClientCertIdentity* const cert = GetSelectedCert();
|
||||
if (cert) {
|
||||
ShowCertificateViewer(web_contents_,
|
||||
web_contents_->GetTopLevelNativeWindow(), cert);
|
||||
web_contents_->GetTopLevelNativeWindow(),
|
||||
cert->certificate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
#include "ui/views/controls/button/button.h"
|
||||
#include "ui/views/controls/table/table_view_observer.h"
|
||||
#include "ui/views/window/dialog_delegate.h"
|
||||
@ -45,13 +45,18 @@ class CertificateSelector : public views::DialogDelegateView,
|
||||
static bool CanShow(content::WebContents* web_contents);
|
||||
|
||||
// |web_contents| must not be null.
|
||||
CertificateSelector(const net::CertificateList& certificates,
|
||||
CertificateSelector(net::ClientCertIdentityList identities,
|
||||
content::WebContents* web_contents);
|
||||
~CertificateSelector() override;
|
||||
|
||||
// Handles when the user chooses a certificate in the list.
|
||||
// The CertificateSelector will be destroyed after this method completes.
|
||||
virtual void AcceptCertificate(
|
||||
std::unique_ptr<net::ClientCertIdentity> identity) = 0;
|
||||
|
||||
// Returns the currently selected certificate or null if none is selected.
|
||||
// Must be called after |InitWithText()|.
|
||||
net::X509Certificate* GetSelectedCert() const;
|
||||
net::ClientCertIdentity* GetSelectedCert() const;
|
||||
|
||||
// Shows this dialog as a constrained web modal dialog and focuses the first
|
||||
// certificate.
|
||||
@ -59,6 +64,7 @@ class CertificateSelector : public views::DialogDelegateView,
|
||||
void Show();
|
||||
|
||||
// DialogDelegateView:
|
||||
bool Accept() override;
|
||||
bool CanResize() const override;
|
||||
base::string16 GetWindowTitle() const override;
|
||||
bool IsDialogButtonEnabled(ui::DialogButton button) const override;
|
||||
@ -88,7 +94,7 @@ class CertificateSelector : public views::DialogDelegateView,
|
||||
private:
|
||||
class CertificateTableModel;
|
||||
|
||||
net::CertificateList certificates_;
|
||||
net::ClientCertIdentityList identities_;
|
||||
|
||||
// Whether to show the provider column in the table or not. Certificates
|
||||
// provided by the platform show the empty string as provider. That column is
|
||||
@ -99,8 +105,8 @@ class CertificateSelector : public views::DialogDelegateView,
|
||||
|
||||
content::WebContents* const web_contents_;
|
||||
|
||||
views::TableView* table_;
|
||||
views::LabelButton* view_cert_button_;
|
||||
views::TableView* table_ = nullptr;
|
||||
views::LabelButton* view_cert_button_ = nullptr;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CertificateSelector);
|
||||
};
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "chrome/test/base/interactive_test_utils.h"
|
||||
#include "content/public/test/browser_test_utils.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "net/test/test_data_directory.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
@ -26,9 +27,9 @@ namespace {
|
||||
|
||||
class TestCertificateSelector : public chrome::CertificateSelector {
|
||||
public:
|
||||
TestCertificateSelector(const net::CertificateList& certificates,
|
||||
TestCertificateSelector(net::ClientCertIdentityList certificates,
|
||||
content::WebContents* web_contents)
|
||||
: CertificateSelector(certificates, web_contents) {}
|
||||
: CertificateSelector(std::move(certificates), web_contents) {}
|
||||
|
||||
~TestCertificateSelector() override {
|
||||
if (!on_destroy_.is_null())
|
||||
@ -40,10 +41,10 @@ class TestCertificateSelector : public chrome::CertificateSelector {
|
||||
base::ASCIIToUTF16("some arbitrary text")));
|
||||
}
|
||||
|
||||
bool Accept() override {
|
||||
void AcceptCertificate(
|
||||
std::unique_ptr<net::ClientCertIdentity> identity) override {
|
||||
if (accepted_)
|
||||
*accepted_ = true;
|
||||
return CertificateSelector::Accept();
|
||||
}
|
||||
|
||||
bool Cancel() override {
|
||||
@ -85,12 +86,10 @@ class CertificateSelectorTest : public InProcessBrowserTest {
|
||||
ASSERT_TRUE(content::WaitForLoadStop(
|
||||
browser()->tab_strip_model()->GetActiveWebContents()));
|
||||
|
||||
net::CertificateList certificates;
|
||||
certificates.push_back(client_1_);
|
||||
certificates.push_back(client_2_);
|
||||
|
||||
selector_ = new TestCertificateSelector(
|
||||
certificates, browser()->tab_strip_model()->GetActiveWebContents());
|
||||
net::FakeClientCertIdentityListFromCertificateList(
|
||||
{client_1_, client_2_}),
|
||||
browser()->tab_strip_model()->GetActiveWebContents());
|
||||
selector_->Init();
|
||||
selector_->Show();
|
||||
}
|
||||
@ -128,13 +127,16 @@ IN_PROC_BROWSER_TEST_F(CertificateSelectorTest, GetRowText) {
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(CertificateSelectorTest, GetSelectedCert) {
|
||||
EXPECT_EQ(client_1_.get(), selector_->GetSelectedCert());
|
||||
ASSERT_TRUE(selector_->GetSelectedCert());
|
||||
EXPECT_EQ(client_1_.get(), selector_->GetSelectedCert()->certificate());
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_DOWN, false,
|
||||
false, false, false));
|
||||
EXPECT_EQ(client_2_.get(), selector_->GetSelectedCert());
|
||||
ASSERT_TRUE(selector_->GetSelectedCert());
|
||||
EXPECT_EQ(client_2_.get(), selector_->GetSelectedCert()->certificate());
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_UP, false,
|
||||
false, false, false));
|
||||
EXPECT_EQ(client_1_.get(), selector_->GetSelectedCert());
|
||||
ASSERT_TRUE(selector_->GetSelectedCert());
|
||||
EXPECT_EQ(client_1_.get(), selector_->GetSelectedCert()->certificate());
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(CertificateSelectorTest, DoubleClick) {
|
||||
|
@ -8,23 +8,58 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/callback_helpers.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/browser/ui/browser_dialogs.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/gfx/font.h"
|
||||
#include "ui/views/controls/styled_label.h"
|
||||
|
||||
namespace chromeos {
|
||||
|
||||
namespace {
|
||||
|
||||
// Fake ClientCertIdentity that does not support retrieving the private key.
|
||||
// The platformKeys API currently only deals in certificates, not identities.
|
||||
// Looking up the private key by the certificate is done as a separate step.
|
||||
class ClientCertIdentityPlatformKeys : public net::ClientCertIdentity {
|
||||
public:
|
||||
explicit ClientCertIdentityPlatformKeys(
|
||||
scoped_refptr<net::X509Certificate> cert)
|
||||
: net::ClientCertIdentity(std::move(cert)) {}
|
||||
~ClientCertIdentityPlatformKeys() override = default;
|
||||
|
||||
void AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<net::SSLPrivateKey>)>&
|
||||
private_key_callback) override {
|
||||
NOTREACHED();
|
||||
}
|
||||
};
|
||||
|
||||
net::ClientCertIdentityList CertificateListToIdentityList(
|
||||
const net::CertificateList& certs) {
|
||||
net::ClientCertIdentityList identities;
|
||||
for (const auto& cert : certs) {
|
||||
identities.push_back(
|
||||
base::MakeUnique<ClientCertIdentityPlatformKeys>(cert));
|
||||
}
|
||||
return identities;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PlatformKeysCertificateSelector::PlatformKeysCertificateSelector(
|
||||
const net::CertificateList& certificates,
|
||||
const std::string& extension_name,
|
||||
const CertificateSelectedCallback& callback,
|
||||
content::WebContents* web_contents)
|
||||
: CertificateSelector(certificates, web_contents),
|
||||
: CertificateSelector(CertificateListToIdentityList(certificates),
|
||||
web_contents),
|
||||
extension_name_(extension_name),
|
||||
callback_(callback) {
|
||||
DCHECK(!callback_.is_null());
|
||||
@ -61,13 +96,11 @@ bool PlatformKeysCertificateSelector::Cancel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlatformKeysCertificateSelector::Accept() {
|
||||
void PlatformKeysCertificateSelector::AcceptCertificate(
|
||||
std::unique_ptr<net::ClientCertIdentity> identity) {
|
||||
DCHECK(!callback_.is_null());
|
||||
scoped_refptr<net::X509Certificate> cert = GetSelectedCert();
|
||||
if (!cert)
|
||||
return false;
|
||||
base::ResetAndReturn(&callback_).Run(cert);
|
||||
return true;
|
||||
base::ResetAndReturn(&callback_)
|
||||
.Run(make_scoped_refptr(identity->certificate()));
|
||||
}
|
||||
|
||||
void ShowPlatformKeysCertificateSelector(
|
||||
|
@ -37,7 +37,8 @@ class PlatformKeysCertificateSelector : public chrome::CertificateSelector {
|
||||
|
||||
// chrome::CertificateSelector:
|
||||
bool Cancel() override;
|
||||
bool Accept() override;
|
||||
void AcceptCertificate(
|
||||
std::unique_ptr<net::ClientCertIdentity> identity) override;
|
||||
|
||||
private:
|
||||
const std::string extension_name_;
|
||||
|
@ -10,31 +10,84 @@
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/ssl/ssl_client_auth_observer.h"
|
||||
#include "chrome/browser/ui/browser_dialogs.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/client_certificate_delegate.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
#if defined(USE_NSS_CERTS) && !defined(OS_CHROMEOS)
|
||||
#include "chrome/browser/ui/crypto_module_password_dialog_nss.h"
|
||||
#endif
|
||||
class SSLClientCertificateSelector::SSLClientAuthObserverImpl
|
||||
: public SSLClientAuthObserver,
|
||||
public content::WebContentsObserver {
|
||||
public:
|
||||
SSLClientAuthObserverImpl(
|
||||
content::WebContents* web_contents,
|
||||
const scoped_refptr<net::SSLCertRequestInfo>& cert_request_info,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate)
|
||||
: SSLClientAuthObserver(web_contents->GetBrowserContext(),
|
||||
cert_request_info,
|
||||
std::move(delegate)),
|
||||
content::WebContentsObserver(web_contents) {}
|
||||
|
||||
void Init(base::OnceClosure close_dialog_callback) {
|
||||
close_dialog_callback_ = std::move(close_dialog_callback);
|
||||
StartObserving();
|
||||
}
|
||||
|
||||
static void AcceptCertificate(
|
||||
std::unique_ptr<SSLClientAuthObserverImpl> self,
|
||||
std::unique_ptr<net::ClientCertIdentity> identity) {
|
||||
// Remove the observer before we try acquiring private key, otherwise we
|
||||
// might act on a notification while waiting for the callback, causing us
|
||||
// to delete ourself before the callback gets called, or to try to run
|
||||
// |close_dialog_callback_| on a dialog which is already closed.
|
||||
self->StopObserving();
|
||||
net::X509Certificate* cert = identity->certificate();
|
||||
net::ClientCertIdentity::SelfOwningAcquirePrivateKey(
|
||||
std::move(identity),
|
||||
base::Bind(&SSLClientAuthObserverImpl::GotPrivateKey,
|
||||
base::Passed(&self), base::Unretained(cert)));
|
||||
}
|
||||
|
||||
void GotPrivateKey(net::X509Certificate* cert,
|
||||
scoped_refptr<net::SSLPrivateKey> private_key) {
|
||||
CertificateSelected(cert, private_key.get());
|
||||
}
|
||||
|
||||
// SSLClientAuthObserver:
|
||||
void OnCertSelectedByNotification() override {
|
||||
std::move(close_dialog_callback_).Run();
|
||||
}
|
||||
|
||||
// content::WebContentsObserver:
|
||||
void WebContentsDestroyed() override {
|
||||
// If the tab is closed (either while the selector dialog is still showing,
|
||||
// or after the dialog has closed but the AcquirePrivateKey callback is
|
||||
// still pending), abort the request.
|
||||
CancelCertificateSelection();
|
||||
}
|
||||
|
||||
private:
|
||||
base::OnceClosure close_dialog_callback_;
|
||||
};
|
||||
|
||||
SSLClientCertificateSelector::SSLClientCertificateSelector(
|
||||
content::WebContents* web_contents,
|
||||
const scoped_refptr<net::SSLCertRequestInfo>& cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate)
|
||||
: CertificateSelector(std::move(client_certs), web_contents),
|
||||
SSLClientAuthObserver(web_contents->GetBrowserContext(),
|
||||
cert_request_info,
|
||||
std::move(delegate)),
|
||||
WebContentsObserver(web_contents) {
|
||||
auth_observer_impl_(
|
||||
base::MakeUnique<SSLClientAuthObserverImpl>(web_contents,
|
||||
cert_request_info,
|
||||
std::move(delegate))) {
|
||||
chrome::RecordDialogCreation(
|
||||
chrome::DialogIdentifier::SSL_CLIENT_CERTIFICATE_SELECTOR);
|
||||
}
|
||||
@ -42,11 +95,13 @@ SSLClientCertificateSelector::SSLClientCertificateSelector(
|
||||
SSLClientCertificateSelector::~SSLClientCertificateSelector() {}
|
||||
|
||||
void SSLClientCertificateSelector::Init() {
|
||||
StartObserving();
|
||||
auth_observer_impl_->Init(base::BindOnce(
|
||||
&SSLClientCertificateSelector::CloseDialog, base::Unretained(this)));
|
||||
std::unique_ptr<views::Label> text_label(
|
||||
new views::Label(l10n_util::GetStringFUTF16(
|
||||
IDS_CLIENT_CERT_DIALOG_TEXT,
|
||||
base::ASCIIToUTF16(cert_request_info()->host_and_port.ToString()))));
|
||||
base::ASCIIToUTF16(auth_observer_impl_->cert_request_info()
|
||||
->host_and_port.ToString()))));
|
||||
text_label->SetMultiLine(true);
|
||||
text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
||||
text_label->SetAllowCharacterBreak(true);
|
||||
@ -54,7 +109,7 @@ void SSLClientCertificateSelector::Init() {
|
||||
InitWithText(std::move(text_label));
|
||||
}
|
||||
|
||||
void SSLClientCertificateSelector::OnCertSelectedByNotification() {
|
||||
void SSLClientCertificateSelector::CloseDialog() {
|
||||
GetWidget()->Close();
|
||||
}
|
||||
|
||||
@ -63,40 +118,18 @@ void SSLClientCertificateSelector::DeleteDelegate() {
|
||||
// to abort instead of proceeding with a null certificate. (This will be
|
||||
// ignored if there was a previous call to CertificateSelected or
|
||||
// CancelCertificateSelection.)
|
||||
CertificateSelected(nullptr);
|
||||
if (auth_observer_impl_)
|
||||
auth_observer_impl_->CertificateSelected(nullptr, nullptr);
|
||||
chrome::CertificateSelector::DeleteDelegate();
|
||||
}
|
||||
|
||||
bool SSLClientCertificateSelector::Accept() {
|
||||
scoped_refptr<net::X509Certificate> cert = GetSelectedCert();
|
||||
if (cert.get()) {
|
||||
// Remove the observer before we try unlocking, otherwise we might act on a
|
||||
// notification while waiting for the unlock dialog, causing us to delete
|
||||
// ourself before the Unlocked callback gets called.
|
||||
StopObserving();
|
||||
#if defined(USE_NSS_CERTS) && !defined(OS_CHROMEOS)
|
||||
chrome::UnlockCertSlotIfNecessary(
|
||||
cert.get(), chrome::kCryptoModulePasswordClientAuth,
|
||||
cert_request_info()->host_and_port, GetWidget()->GetNativeView(),
|
||||
base::Bind(&SSLClientCertificateSelector::Unlocked,
|
||||
base::Unretained(this), base::RetainedRef(cert)));
|
||||
#else
|
||||
Unlocked(cert.get());
|
||||
#endif
|
||||
return false; // Unlocked() will close the dialog.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SSLClientCertificateSelector::WebContentsDestroyed() {
|
||||
// If the dialog is closed by closing the containing tab, abort the request.
|
||||
CancelCertificateSelection();
|
||||
}
|
||||
|
||||
void SSLClientCertificateSelector::Unlocked(net::X509Certificate* cert) {
|
||||
CertificateSelected(cert);
|
||||
GetWidget()->Close();
|
||||
void SSLClientCertificateSelector::AcceptCertificate(
|
||||
std::unique_ptr<net::ClientCertIdentity> identity) {
|
||||
// The SSLClientCertificateSelector will be destroyed after this method
|
||||
// returns, so the SSLClientAuthObserverImpl manages its own lifetime while
|
||||
// acquiring the private key from |identity|.
|
||||
SSLClientAuthObserverImpl::AcceptCertificate(std::move(auth_observer_impl_),
|
||||
std::move(identity));
|
||||
}
|
||||
|
||||
namespace chrome {
|
||||
@ -104,7 +137,7 @@ namespace chrome {
|
||||
void ShowSSLClientCertificateSelector(
|
||||
content::WebContents* contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
||||
|
@ -6,10 +6,8 @@
|
||||
#define CHROME_BROWSER_UI_VIEWS_SSL_CLIENT_CERTIFICATE_SELECTOR_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "chrome/browser/ssl/ssl_client_auth_observer.h"
|
||||
#include "chrome/browser/ssl/ssl_client_certificate_selector.h"
|
||||
#include "chrome/browser/ui/views/certificate_selector.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
|
||||
// This header file exists only for testing. Chrome should access the
|
||||
// certificate selector only through the cross-platform interface
|
||||
@ -21,35 +19,29 @@ class WebContents;
|
||||
|
||||
namespace net {
|
||||
class SSLCertRequestInfo;
|
||||
class X509Certificate;
|
||||
}
|
||||
|
||||
class SSLClientCertificateSelector : public chrome::CertificateSelector,
|
||||
public SSLClientAuthObserver,
|
||||
public content::WebContentsObserver {
|
||||
class SSLClientCertificateSelector : public chrome::CertificateSelector {
|
||||
public:
|
||||
SSLClientCertificateSelector(
|
||||
content::WebContents* web_contents,
|
||||
const scoped_refptr<net::SSLCertRequestInfo>& cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate);
|
||||
~SSLClientCertificateSelector() override;
|
||||
|
||||
void Init();
|
||||
|
||||
// SSLClientAuthObserver:
|
||||
void OnCertSelectedByNotification() override;
|
||||
void CloseDialog();
|
||||
|
||||
// chrome::CertificateSelector:
|
||||
void DeleteDelegate() override;
|
||||
bool Accept() override;
|
||||
|
||||
// content::WebContentsObserver:
|
||||
void WebContentsDestroyed() override;
|
||||
void AcceptCertificate(
|
||||
std::unique_ptr<net::ClientCertIdentity> identity) override;
|
||||
|
||||
private:
|
||||
// Callback after unlocking certificate slot.
|
||||
void Unlocked(net::X509Certificate* cert);
|
||||
class SSLClientAuthObserverImpl;
|
||||
|
||||
std::unique_ptr<SSLClientAuthObserverImpl> auth_observer_impl_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SSLClientCertificateSelector);
|
||||
};
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "net/base/request_priority.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/http/http_transaction_factory.h"
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "net/test/test_data_directory.h"
|
||||
@ -31,10 +32,6 @@
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "ui/views/test/widget_test.h"
|
||||
|
||||
#if defined(USE_NSS_CERTS)
|
||||
#include "crypto/scoped_test_nss_db.h"
|
||||
#endif
|
||||
|
||||
using ::testing::Mock;
|
||||
using ::testing::StrictMock;
|
||||
using content::BrowserThread;
|
||||
@ -55,28 +52,15 @@ class SSLClientCertificateSelectorTest : public InProcessBrowserTest {
|
||||
void SetUpInProcessBrowserTestFixture() override {
|
||||
base::FilePath certs_dir = net::GetTestCertsDirectory();
|
||||
|
||||
#if defined(USE_NSS_CERTS)
|
||||
// If USE_NSS_CERTS, the selector tries to unlock the slot where the
|
||||
// private key of each certificate is stored. If no private key is found,
|
||||
// the slot would be null and the unlock will crash.
|
||||
ASSERT_TRUE(test_nssdb_.is_open());
|
||||
client_cert_1_ = net::ImportClientCertAndKeyFromFile(
|
||||
certs_dir, "client_1.pem", "client_1.pk8", test_nssdb_.slot());
|
||||
client_cert_2_ = net::ImportClientCertAndKeyFromFile(
|
||||
certs_dir, "client_2.pem", "client_2.pk8", test_nssdb_.slot());
|
||||
#else
|
||||
// No unlock is attempted if !USE_NSS_CERTS. Thus, there is no need to
|
||||
// import a private key.
|
||||
client_cert_1_ = net::ImportCertFromFile(certs_dir, "client_1.pem");
|
||||
client_cert_2_ = net::ImportCertFromFile(certs_dir, "client_2.pem");
|
||||
#endif
|
||||
ASSERT_NE(nullptr, client_cert_1_.get());
|
||||
ASSERT_NE(nullptr, client_cert_2_.get());
|
||||
cert_identity_1_ = net::FakeClientCertIdentity::CreateFromCertAndKeyFiles(
|
||||
certs_dir, "client_1.pem", "client_1.pk8");
|
||||
ASSERT_TRUE(cert_identity_1_);
|
||||
cert_identity_2_ = net::FakeClientCertIdentity::CreateFromCertAndKeyFiles(
|
||||
certs_dir, "client_2.pem", "client_2.pk8");
|
||||
ASSERT_TRUE(cert_identity_2_);
|
||||
|
||||
cert_request_info_ = new net::SSLCertRequestInfo;
|
||||
cert_request_info_->host_and_port = net::HostPortPair("foo", 123);
|
||||
client_certs_.push_back(client_cert_1_);
|
||||
client_certs_.push_back(client_cert_2_);
|
||||
}
|
||||
|
||||
void SetUpOnMainThread() override {
|
||||
@ -91,14 +75,19 @@ class SSLClientCertificateSelectorTest : public InProcessBrowserTest {
|
||||
|
||||
content::WaitForLoadStop(
|
||||
browser()->tab_strip_model()->GetActiveWebContents());
|
||||
net::ClientCertIdentityList cert_identity_list;
|
||||
cert_identity_list.push_back(cert_identity_1_->Copy());
|
||||
cert_identity_list.push_back(cert_identity_2_->Copy());
|
||||
selector_ = new SSLClientCertificateSelector(
|
||||
browser()->tab_strip_model()->GetActiveWebContents(),
|
||||
auth_requestor_->cert_request_info_, client_certs_,
|
||||
auth_requestor_->cert_request_info_, std::move(cert_identity_list),
|
||||
auth_requestor_->CreateDelegate());
|
||||
selector_->Init();
|
||||
selector_->Show();
|
||||
|
||||
EXPECT_EQ(client_cert_1_.get(), selector_->GetSelectedCert());
|
||||
ASSERT_TRUE(selector_->GetSelectedCert());
|
||||
EXPECT_EQ(cert_identity_1_->certificate(),
|
||||
selector_->GetSelectedCert()->certificate());
|
||||
}
|
||||
|
||||
virtual void SetUpOnIOThread() {
|
||||
@ -143,16 +132,12 @@ class SSLClientCertificateSelectorTest : public InProcessBrowserTest {
|
||||
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
|
||||
net::URLRequest* url_request_;
|
||||
|
||||
scoped_refptr<net::X509Certificate> client_cert_1_;
|
||||
scoped_refptr<net::X509Certificate> client_cert_2_;
|
||||
net::CertificateList client_certs_;
|
||||
std::unique_ptr<net::FakeClientCertIdentity> cert_identity_1_;
|
||||
std::unique_ptr<net::FakeClientCertIdentity> cert_identity_2_;
|
||||
scoped_refptr<net::SSLCertRequestInfo> cert_request_info_;
|
||||
scoped_refptr<StrictMock<SSLClientAuthRequestorMock> > auth_requestor_;
|
||||
// The selector will be deleted when a cert is selected or the tab is closed.
|
||||
SSLClientCertificateSelector* selector_;
|
||||
#if defined(USE_NSS_CERTS)
|
||||
crypto::ScopedTestNSSDB test_nssdb_;
|
||||
#endif
|
||||
};
|
||||
|
||||
class SSLClientCertificateSelectorMultiTabTest
|
||||
@ -163,13 +148,9 @@ class SSLClientCertificateSelectorMultiTabTest
|
||||
|
||||
cert_request_info_1_ = new net::SSLCertRequestInfo;
|
||||
cert_request_info_1_->host_and_port = net::HostPortPair("bar", 123);
|
||||
client_certs_1_.push_back(client_cert_1_);
|
||||
client_certs_1_.push_back(client_cert_2_);
|
||||
|
||||
cert_request_info_2_ = new net::SSLCertRequestInfo;
|
||||
cert_request_info_2_->host_and_port = net::HostPortPair("bar", 123);
|
||||
client_certs_2_.push_back(client_cert_1_);
|
||||
client_certs_2_.push_back(client_cert_2_);
|
||||
}
|
||||
|
||||
void SetUpOnMainThread() override {
|
||||
@ -184,22 +165,33 @@ class SSLClientCertificateSelectorMultiTabTest
|
||||
content::WaitForLoadStop(browser()->tab_strip_model()->GetWebContentsAt(1));
|
||||
content::WaitForLoadStop(browser()->tab_strip_model()->GetWebContentsAt(2));
|
||||
|
||||
net::ClientCertIdentityList cert_identity_list_1;
|
||||
cert_identity_list_1.push_back(cert_identity_1_->Copy());
|
||||
cert_identity_list_1.push_back(cert_identity_2_->Copy());
|
||||
selector_1_ = new SSLClientCertificateSelector(
|
||||
browser()->tab_strip_model()->GetWebContentsAt(1),
|
||||
auth_requestor_1_->cert_request_info_, client_certs_1_,
|
||||
auth_requestor_1_->cert_request_info_, std::move(cert_identity_list_1),
|
||||
auth_requestor_1_->CreateDelegate());
|
||||
selector_1_->Init();
|
||||
selector_1_->Show();
|
||||
|
||||
net::ClientCertIdentityList cert_identity_list_2;
|
||||
cert_identity_list_2.push_back(cert_identity_1_->Copy());
|
||||
cert_identity_list_2.push_back(cert_identity_2_->Copy());
|
||||
selector_2_ = new SSLClientCertificateSelector(
|
||||
browser()->tab_strip_model()->GetWebContentsAt(2),
|
||||
auth_requestor_2_->cert_request_info_, client_certs_2_,
|
||||
auth_requestor_2_->cert_request_info_, std::move(cert_identity_list_2),
|
||||
auth_requestor_2_->CreateDelegate());
|
||||
selector_2_->Init();
|
||||
selector_2_->Show();
|
||||
|
||||
EXPECT_EQ(2, browser()->tab_strip_model()->active_index());
|
||||
EXPECT_EQ(client_cert_1_.get(), selector_1_->GetSelectedCert());
|
||||
EXPECT_EQ(client_cert_1_.get(), selector_2_->GetSelectedCert());
|
||||
ASSERT_TRUE(selector_1_->GetSelectedCert());
|
||||
EXPECT_EQ(cert_identity_1_->certificate(),
|
||||
selector_1_->GetSelectedCert()->certificate());
|
||||
ASSERT_TRUE(selector_2_->GetSelectedCert());
|
||||
EXPECT_EQ(cert_identity_1_->certificate(),
|
||||
selector_2_->GetSelectedCert()->certificate());
|
||||
}
|
||||
|
||||
void SetUpOnIOThread() override {
|
||||
@ -235,8 +227,6 @@ class SSLClientCertificateSelectorMultiTabTest
|
||||
net::URLRequest* url_request_2_;
|
||||
scoped_refptr<net::SSLCertRequestInfo> cert_request_info_1_;
|
||||
scoped_refptr<net::SSLCertRequestInfo> cert_request_info_2_;
|
||||
net::CertificateList client_certs_1_;
|
||||
net::CertificateList client_certs_2_;
|
||||
scoped_refptr<StrictMock<SSLClientAuthRequestorMock> > auth_requestor_1_;
|
||||
scoped_refptr<StrictMock<SSLClientAuthRequestorMock> > auth_requestor_2_;
|
||||
SSLClientCertificateSelector* selector_1_;
|
||||
@ -251,8 +241,6 @@ class SSLClientCertificateSelectorMultiProfileTest
|
||||
|
||||
cert_request_info_1_ = new net::SSLCertRequestInfo;
|
||||
cert_request_info_1_->host_and_port = net::HostPortPair("foo", 123);
|
||||
client_certs_1_.push_back(client_cert_1_);
|
||||
client_certs_1_.push_back(client_cert_2_);
|
||||
}
|
||||
|
||||
void SetUpOnMainThread() override {
|
||||
@ -262,9 +250,12 @@ class SSLClientCertificateSelectorMultiProfileTest
|
||||
// Also calls SetUpOnIOThread.
|
||||
SSLClientCertificateSelectorTest::SetUpOnMainThread();
|
||||
|
||||
net::ClientCertIdentityList cert_identity_list;
|
||||
cert_identity_list.push_back(cert_identity_1_->Copy());
|
||||
cert_identity_list.push_back(cert_identity_2_->Copy());
|
||||
selector_1_ = new SSLClientCertificateSelector(
|
||||
browser_1_->tab_strip_model()->GetActiveWebContents(),
|
||||
auth_requestor_1_->cert_request_info_, client_certs_1_,
|
||||
auth_requestor_1_->cert_request_info_, std::move(cert_identity_list),
|
||||
auth_requestor_1_->CreateDelegate());
|
||||
selector_1_->Init();
|
||||
selector_1_->Show();
|
||||
@ -275,7 +266,9 @@ class SSLClientCertificateSelectorMultiProfileTest
|
||||
views::test::WidgetActivationWaiter waiter(widget, true);
|
||||
waiter.Wait();
|
||||
|
||||
EXPECT_EQ(client_cert_1_.get(), selector_1_->GetSelectedCert());
|
||||
ASSERT_TRUE(selector_1_->GetSelectedCert());
|
||||
EXPECT_EQ(cert_identity_1_->certificate(),
|
||||
selector_1_->GetSelectedCert()->certificate());
|
||||
}
|
||||
|
||||
void SetUpOnIOThread() override {
|
||||
@ -304,7 +297,6 @@ class SSLClientCertificateSelectorMultiProfileTest
|
||||
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_1_;
|
||||
net::URLRequest* url_request_1_;
|
||||
scoped_refptr<net::SSLCertRequestInfo> cert_request_info_1_;
|
||||
net::CertificateList client_certs_1_;
|
||||
scoped_refptr<StrictMock<SSLClientAuthRequestorMock> > auth_requestor_1_;
|
||||
SSLClientCertificateSelector* selector_1_;
|
||||
};
|
||||
@ -316,7 +308,7 @@ IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, SelectNone) {
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, Escape) {
|
||||
EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(NULL));
|
||||
EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(nullptr, nullptr));
|
||||
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
|
||||
browser(), ui::VKEY_ESCAPE, false, false, false, false));
|
||||
@ -326,7 +318,8 @@ IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, Escape) {
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, SelectDefault) {
|
||||
EXPECT_CALL(*auth_requestor_.get(),
|
||||
CertificateSelected(client_cert_1_.get()));
|
||||
CertificateSelected(cert_identity_1_->certificate(),
|
||||
cert_identity_1_->ssl_private_key()));
|
||||
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
|
||||
browser(), ui::VKEY_RETURN, false, false, false, false));
|
||||
@ -338,8 +331,8 @@ IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, Escape) {
|
||||
// auth_requestor_1_ should get selected automatically by the
|
||||
// SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have
|
||||
// the same host:port.
|
||||
EXPECT_CALL(*auth_requestor_1_.get(), CertificateSelected(NULL));
|
||||
EXPECT_CALL(*auth_requestor_2_.get(), CertificateSelected(NULL));
|
||||
EXPECT_CALL(*auth_requestor_1_.get(), CertificateSelected(nullptr, nullptr));
|
||||
EXPECT_CALL(*auth_requestor_2_.get(), CertificateSelected(nullptr, nullptr));
|
||||
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
|
||||
browser(), ui::VKEY_ESCAPE, false, false, false, false));
|
||||
@ -358,16 +351,24 @@ IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, SelectSecond) {
|
||||
// SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have
|
||||
// the same host:port.
|
||||
EXPECT_CALL(*auth_requestor_1_.get(),
|
||||
CertificateSelected(client_cert_2_.get()));
|
||||
CertificateSelected(cert_identity_2_->certificate(),
|
||||
cert_identity_2_->ssl_private_key()));
|
||||
EXPECT_CALL(*auth_requestor_2_.get(),
|
||||
CertificateSelected(client_cert_2_.get()));
|
||||
CertificateSelected(cert_identity_2_->certificate(),
|
||||
cert_identity_2_->ssl_private_key()));
|
||||
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
|
||||
browser(), ui::VKEY_DOWN, false, false, false, false));
|
||||
|
||||
EXPECT_EQ(client_cert_1_.get(), selector_->GetSelectedCert());
|
||||
EXPECT_EQ(client_cert_1_.get(), selector_1_->GetSelectedCert());
|
||||
EXPECT_EQ(client_cert_2_.get(), selector_2_->GetSelectedCert());
|
||||
ASSERT_TRUE(selector_->GetSelectedCert());
|
||||
EXPECT_EQ(cert_identity_1_->certificate(),
|
||||
selector_->GetSelectedCert()->certificate());
|
||||
ASSERT_TRUE(selector_1_->GetSelectedCert());
|
||||
EXPECT_EQ(cert_identity_1_->certificate(),
|
||||
selector_1_->GetSelectedCert()->certificate());
|
||||
ASSERT_TRUE(selector_2_->GetSelectedCert());
|
||||
EXPECT_EQ(cert_identity_2_->certificate(),
|
||||
selector_2_->GetSelectedCert()->certificate());
|
||||
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
|
||||
browser(), ui::VKEY_RETURN, false, false, false, false));
|
||||
@ -382,7 +383,7 @@ IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, SelectSecond) {
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiProfileTest, Escape) {
|
||||
EXPECT_CALL(*auth_requestor_1_.get(), CertificateSelected(NULL));
|
||||
EXPECT_CALL(*auth_requestor_1_.get(), CertificateSelected(nullptr, nullptr));
|
||||
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
|
||||
browser_1_, ui::VKEY_ESCAPE, false, false, false, false));
|
||||
@ -398,7 +399,8 @@ IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiProfileTest, Escape) {
|
||||
IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiProfileTest,
|
||||
SelectDefault) {
|
||||
EXPECT_CALL(*auth_requestor_1_.get(),
|
||||
CertificateSelected(client_cert_1_.get()));
|
||||
CertificateSelected(cert_identity_1_->certificate(),
|
||||
cert_identity_1_->ssl_private_key()));
|
||||
|
||||
EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
|
||||
browser_1_, ui::VKEY_RETURN, false, false, false, false));
|
||||
|
@ -420,14 +420,14 @@ void CastContentBrowserClient::AllowCertificateError(
|
||||
void CastContentBrowserClient::SelectClientCertificate(
|
||||
content::WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) {
|
||||
GURL requesting_url("https://" + cert_request_info->host_and_port.ToString());
|
||||
|
||||
if (!requesting_url.is_valid()) {
|
||||
LOG(ERROR) << "Invalid URL string: "
|
||||
<< requesting_url.possibly_invalid_spec();
|
||||
delegate->ContinueWithCertificate(nullptr);
|
||||
delegate->ContinueWithCertificate(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -441,32 +441,44 @@ void CastContentBrowserClient::SelectClientCertificate(
|
||||
//
|
||||
// TODO(davidben): Stop using child ID to identify an app.
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
content::BrowserThread::PostTaskAndReplyWithResult(
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&CastContentBrowserClient::SelectClientCertificateOnIOThread,
|
||||
base::Unretained(this), requesting_url,
|
||||
web_contents->GetRenderProcessHost()->GetID()),
|
||||
base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate,
|
||||
base::Owned(delegate.release())));
|
||||
base::BindOnce(
|
||||
&CastContentBrowserClient::SelectClientCertificateOnIOThread,
|
||||
base::Unretained(this), requesting_url,
|
||||
web_contents->GetRenderProcessHost()->GetID(),
|
||||
base::SequencedTaskRunnerHandle::Get(),
|
||||
base::Bind(
|
||||
&content::ClientCertificateDelegate::ContinueWithCertificate,
|
||||
base::Owned(delegate.release()))));
|
||||
}
|
||||
|
||||
net::X509Certificate*
|
||||
CastContentBrowserClient::SelectClientCertificateOnIOThread(
|
||||
void CastContentBrowserClient::SelectClientCertificateOnIOThread(
|
||||
GURL requesting_url,
|
||||
int render_process_id) {
|
||||
int render_process_id,
|
||||
scoped_refptr<base::SequencedTaskRunner> original_runner,
|
||||
const base::Callback<void(scoped_refptr<net::X509Certificate>,
|
||||
scoped_refptr<net::SSLPrivateKey>)>&
|
||||
continue_callback) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
CastNetworkDelegate* network_delegate =
|
||||
url_request_context_factory_->app_network_delegate();
|
||||
if (network_delegate->IsWhitelisted(requesting_url,
|
||||
render_process_id, false)) {
|
||||
return CastNetworkDelegate::DeviceCert();
|
||||
original_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(continue_callback,
|
||||
make_scoped_refptr(CastNetworkDelegate::DeviceCert()),
|
||||
make_scoped_refptr(CastNetworkDelegate::DeviceKey())));
|
||||
return;
|
||||
} else {
|
||||
LOG(ERROR) << "Invalid host for client certificate request: "
|
||||
<< requesting_url.host()
|
||||
<< " with render_process_id: "
|
||||
<< render_process_id;
|
||||
return NULL;
|
||||
}
|
||||
original_runner->PostTask(FROM_HERE,
|
||||
base::Bind(continue_callback, nullptr, nullptr));
|
||||
}
|
||||
|
||||
bool CastContentBrowserClient::CanCreateWindow(
|
||||
|
@ -30,6 +30,7 @@ class MetricsService;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class SSLPrivateKey;
|
||||
class URLRequestContextGetter;
|
||||
class X509Certificate;
|
||||
}
|
||||
@ -141,7 +142,7 @@ class CastContentBrowserClient : public content::ContentBrowserClient {
|
||||
void SelectClientCertificate(
|
||||
content::WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
|
||||
bool CanCreateWindow(content::RenderFrameHost* opener,
|
||||
const GURL& opener_url,
|
||||
@ -182,10 +183,13 @@ class CastContentBrowserClient : public content::ContentBrowserClient {
|
||||
void AddNetworkHintsMessageFilter(int render_process_id,
|
||||
net::URLRequestContext* context);
|
||||
|
||||
net::X509Certificate* SelectClientCertificateOnIOThread(
|
||||
void SelectClientCertificateOnIOThread(
|
||||
GURL requesting_url,
|
||||
int render_process_id);
|
||||
|
||||
int render_process_id,
|
||||
scoped_refptr<base::SequencedTaskRunner> original_runner,
|
||||
const base::Callback<void(scoped_refptr<net::X509Certificate>,
|
||||
scoped_refptr<net::SSLPrivateKey>)>&
|
||||
continue_callback);
|
||||
#if !defined(OS_ANDROID)
|
||||
// Returns the crash signal FD corresponding to the current process type.
|
||||
int GetCrashSignalFD(const base::CommandLine& command_line);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "net/base/network_delegate_impl.h"
|
||||
|
||||
namespace net {
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
}
|
||||
|
||||
@ -19,6 +20,7 @@ class CastNetworkDelegate : public net::NetworkDelegateImpl {
|
||||
public:
|
||||
static CastNetworkDelegate* Create();
|
||||
static net::X509Certificate* DeviceCert();
|
||||
static net::SSLPrivateKey* DeviceKey();
|
||||
|
||||
CastNetworkDelegate();
|
||||
~CastNetworkDelegate() override;
|
||||
|
@ -39,5 +39,10 @@ net::X509Certificate* CastNetworkDelegate::DeviceCert() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// static
|
||||
net::SSLPrivateKey* CastNetworkDelegate::DeviceKey() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
} // namespace chromecast
|
||||
|
@ -37,8 +37,6 @@
|
||||
#include "net/nqe/effective_connection_type.h"
|
||||
#include "net/nqe/network_quality_estimator.h"
|
||||
#include "net/ssl/client_cert_store.h"
|
||||
#include "net/ssl/ssl_platform_key.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/url_request/redirect_info.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_status.h"
|
||||
@ -469,16 +467,12 @@ void ResourceLoader::ContinueSSLRequest() {
|
||||
request_->ContinueDespiteLastError();
|
||||
}
|
||||
|
||||
void ResourceLoader::ContinueWithCertificate(net::X509Certificate* cert) {
|
||||
void ResourceLoader::ContinueWithCertificate(
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> private_key) {
|
||||
DCHECK(ssl_client_auth_handler_);
|
||||
ssl_client_auth_handler_.reset();
|
||||
if (!cert) {
|
||||
request_->ContinueWithCertificate(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
scoped_refptr<net::SSLPrivateKey> private_key =
|
||||
net::FetchClientCertPrivateKey(cert);
|
||||
request_->ContinueWithCertificate(cert, private_key.get());
|
||||
request_->ContinueWithCertificate(std::move(cert), std::move(private_key));
|
||||
}
|
||||
|
||||
void ResourceLoader::CancelCertificateSelection() {
|
||||
|
@ -81,7 +81,9 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate,
|
||||
void ContinueSSLRequest() override;
|
||||
|
||||
// SSLClientAuthHandler::Delegate implementation.
|
||||
void ContinueWithCertificate(net::X509Certificate* cert) override;
|
||||
void ContinueWithCertificate(
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> private_key) override;
|
||||
void CancelCertificateSelection() override;
|
||||
|
||||
// These correspond to Controller's methods.
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/nqe/effective_connection_type.h"
|
||||
#include "net/nqe/network_quality_estimator_test_util.h"
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
#include "net/ssl/client_cert_store.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
@ -79,7 +80,7 @@ class ClientCertStoreStub : public net::ClientCertStore {
|
||||
ClientCertStoreStub(const net::CertificateList& response,
|
||||
int* request_count,
|
||||
std::vector<std::string>* requested_authorities)
|
||||
: response_(response),
|
||||
: response_(std::move(response)),
|
||||
requested_authorities_(requested_authorities),
|
||||
request_count_(request_count) {
|
||||
requested_authorities_->clear();
|
||||
@ -94,7 +95,7 @@ class ClientCertStoreStub : public net::ClientCertStore {
|
||||
*requested_authorities_ = cert_request_info.cert_authorities;
|
||||
++(*request_count_);
|
||||
|
||||
callback.Run(response_);
|
||||
callback.Run(net::FakeClientCertIdentityListFromCertificateList(response_));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -134,7 +135,7 @@ class LoaderDestroyingCertStore : public net::ClientCertStore {
|
||||
const ClientCertListCallback& cert_selected_callback,
|
||||
const base::Closure& on_loader_deleted_callback) {
|
||||
loader->reset();
|
||||
cert_selected_callback.Run(net::CertificateList());
|
||||
cert_selected_callback.Run(net::ClientCertIdentityList());
|
||||
on_loader_deleted_callback.Run();
|
||||
}
|
||||
|
||||
@ -168,8 +169,9 @@ class MockClientCertURLRequestJob : public net::URLRequestTestJob {
|
||||
base::RetainedRef(cert_request_info)));
|
||||
}
|
||||
|
||||
void ContinueWithCertificate(net::X509Certificate* cert,
|
||||
net::SSLPrivateKey* private_key) override {
|
||||
void ContinueWithCertificate(
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> private_key) override {
|
||||
net::URLRequestTestJob::Start();
|
||||
}
|
||||
|
||||
@ -273,28 +275,31 @@ class SelectCertificateBrowserClient : public TestContentBrowserClient {
|
||||
void SelectClientCertificate(
|
||||
WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<ClientCertificateDelegate> delegate) override {
|
||||
EXPECT_FALSE(delegate_.get());
|
||||
|
||||
++call_count_;
|
||||
passed_certs_ = std::move(client_certs);
|
||||
passed_identities_ = std::move(client_certs);
|
||||
delegate_ = std::move(delegate);
|
||||
select_certificate_run_loop_.Quit();
|
||||
}
|
||||
|
||||
int call_count() { return call_count_; }
|
||||
net::CertificateList passed_certs() { return passed_certs_; }
|
||||
const net::ClientCertIdentityList& passed_identities() {
|
||||
return passed_identities_;
|
||||
}
|
||||
|
||||
void ContinueWithCertificate(net::X509Certificate* cert) {
|
||||
delegate_->ContinueWithCertificate(cert);
|
||||
void ContinueWithCertificate(scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> private_key) {
|
||||
delegate_->ContinueWithCertificate(std::move(cert), std::move(private_key));
|
||||
delegate_.reset();
|
||||
}
|
||||
|
||||
void CancelCertificateSelection() { delegate_.reset(); }
|
||||
|
||||
private:
|
||||
net::CertificateList passed_certs_;
|
||||
net::ClientCertIdentityList passed_identities_;
|
||||
int call_count_;
|
||||
std::unique_ptr<ClientCertificateDelegate> delegate_;
|
||||
|
||||
@ -595,8 +600,10 @@ TEST_F(ClientCertResourceLoaderTest, WithStoreLookup) {
|
||||
// Set up the test client cert store.
|
||||
int store_request_count;
|
||||
std::vector<std::string> store_requested_authorities;
|
||||
net::CertificateList dummy_certs(
|
||||
1, net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem"));
|
||||
scoped_refptr<net::X509Certificate> test_cert =
|
||||
net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
|
||||
ASSERT_TRUE(test_cert);
|
||||
net::CertificateList dummy_certs(1, test_cert);
|
||||
std::unique_ptr<ClientCertStoreStub> test_store(new ClientCertStoreStub(
|
||||
dummy_certs, &store_request_count, &store_requested_authorities));
|
||||
SetClientCertStore(std::move(test_store));
|
||||
@ -619,10 +626,11 @@ TEST_F(ClientCertResourceLoaderTest, WithStoreLookup) {
|
||||
// Check if the retrieved certificates were passed to the content browser
|
||||
// client.
|
||||
EXPECT_EQ(1, test_client.call_count());
|
||||
EXPECT_EQ(dummy_certs, test_client.passed_certs());
|
||||
EXPECT_EQ(1U, test_client.passed_identities().size());
|
||||
EXPECT_EQ(test_cert.get(), test_client.passed_identities()[0]->certificate());
|
||||
|
||||
// Continue the request.
|
||||
test_client.ContinueWithCertificate(nullptr);
|
||||
test_client.ContinueWithCertificate(nullptr, nullptr);
|
||||
raw_ptr_resource_handler_->WaitUntilResponseComplete();
|
||||
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
|
||||
|
||||
@ -644,10 +652,10 @@ TEST_F(ClientCertResourceLoaderTest, WithNullStore) {
|
||||
// Check if the SelectClientCertificate was called on the content browser
|
||||
// client.
|
||||
EXPECT_EQ(1, test_client.call_count());
|
||||
EXPECT_EQ(net::CertificateList(), test_client.passed_certs());
|
||||
EXPECT_EQ(net::ClientCertIdentityList(), test_client.passed_identities());
|
||||
|
||||
// Continue the request.
|
||||
test_client.ContinueWithCertificate(nullptr);
|
||||
test_client.ContinueWithCertificate(nullptr, nullptr);
|
||||
raw_ptr_resource_handler_->WaitUntilResponseComplete();
|
||||
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
|
||||
|
||||
@ -668,7 +676,7 @@ TEST_F(ClientCertResourceLoaderTest, CancelSelection) {
|
||||
// Check if the SelectClientCertificate was called on the content browser
|
||||
// client.
|
||||
EXPECT_EQ(1, test_client.call_count());
|
||||
EXPECT_EQ(net::CertificateList(), test_client.passed_certs());
|
||||
EXPECT_EQ(net::ClientCertIdentityList(), test_client.passed_identities());
|
||||
|
||||
// Cancel the request.
|
||||
test_client.CancelCertificateSelection();
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "content/public/browser/content_browser_client.h"
|
||||
#include "content/public/browser/resource_request_info.h"
|
||||
#include "net/ssl/client_cert_store.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
|
||||
namespace content {
|
||||
@ -36,13 +37,14 @@ class ClientCertificateDelegateImpl : public ClientCertificateDelegate {
|
||||
}
|
||||
|
||||
// ClientCertificateDelegate implementation:
|
||||
void ContinueWithCertificate(net::X509Certificate* cert) override {
|
||||
void ContinueWithCertificate(scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> key) override {
|
||||
DCHECK(!continue_called_);
|
||||
continue_called_ = true;
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&SSLClientAuthHandler::ContinueWithCertificate, handler_,
|
||||
base::RetainedRef(cert)));
|
||||
std::move(cert), std::move(key)));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -55,7 +57,7 @@ class ClientCertificateDelegateImpl : public ClientCertificateDelegate {
|
||||
void SelectCertificateOnUIThread(
|
||||
const ResourceRequestInfo::WebContentsGetter& wc_getter,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
const base::WeakPtr<SSLClientAuthHandler>& handler) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
@ -96,7 +98,7 @@ class SSLClientAuthHandler::Core : public base::RefCountedThreadSafe<Core> {
|
||||
*cert_request_info_,
|
||||
base::Bind(&SSLClientAuthHandler::Core::DidGetClientCerts, this));
|
||||
} else {
|
||||
DidGetClientCerts(net::CertificateList());
|
||||
DidGetClientCerts(net::ClientCertIdentityList());
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +108,7 @@ class SSLClientAuthHandler::Core : public base::RefCountedThreadSafe<Core> {
|
||||
~Core() {}
|
||||
|
||||
// Called when |client_cert_store_| is done retrieving the cert list.
|
||||
void DidGetClientCerts(net::CertificateList client_certs) {
|
||||
void DidGetClientCerts(net::ClientCertIdentityList client_certs) {
|
||||
if (handler_)
|
||||
handler_->DidGetClientCerts(std::move(client_certs));
|
||||
}
|
||||
@ -144,9 +146,11 @@ void SSLClientAuthHandler::SelectCertificate() {
|
||||
// static
|
||||
void SSLClientAuthHandler::ContinueWithCertificate(
|
||||
const base::WeakPtr<SSLClientAuthHandler>& handler,
|
||||
net::X509Certificate* cert) {
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> key) {
|
||||
if (handler)
|
||||
handler->delegate_->ContinueWithCertificate(cert);
|
||||
handler->delegate_->ContinueWithCertificate(std::move(cert),
|
||||
std::move(key));
|
||||
}
|
||||
|
||||
// static
|
||||
@ -157,7 +161,7 @@ void SSLClientAuthHandler::CancelCertificateSelection(
|
||||
}
|
||||
|
||||
void SSLClientAuthHandler::DidGetClientCerts(
|
||||
net::CertificateList client_certs) {
|
||||
net::ClientCertIdentityList client_certs) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
// Note that if |client_cert_store_| is NULL, we intentionally fall through to
|
||||
@ -174,17 +178,17 @@ void SSLClientAuthHandler::DidGetClientCerts(
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&SSLClientAuthHandler::ContinueWithCertificate,
|
||||
weak_factory_.GetWeakPtr(), nullptr));
|
||||
weak_factory_.GetWeakPtr(), nullptr, nullptr));
|
||||
return;
|
||||
}
|
||||
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&SelectCertificateOnUIThread,
|
||||
ResourceRequestInfo::ForRequest(request_)
|
||||
->GetWebContentsGetterForRequest(),
|
||||
base::RetainedRef(cert_request_info_), std::move(client_certs),
|
||||
weak_factory_.GetWeakPtr()));
|
||||
base::BindOnce(&SelectCertificateOnUIThread,
|
||||
ResourceRequestInfo::ForRequest(request_)
|
||||
->GetWebContentsGetterForRequest(),
|
||||
base::RetainedRef(cert_request_info_),
|
||||
std::move(client_certs), weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
|
@ -13,11 +13,12 @@
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "content/common/content_export.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
|
||||
namespace net {
|
||||
class ClientCertStore;
|
||||
class SSLPrivateKey;
|
||||
class URLRequest;
|
||||
class X509Certificate;
|
||||
} // namespace net
|
||||
@ -37,7 +38,9 @@ class SSLClientAuthHandler {
|
||||
Delegate() {}
|
||||
|
||||
// Called to continue the request with |cert|. |cert| may be nullptr.
|
||||
virtual void ContinueWithCertificate(net::X509Certificate* cert) = 0;
|
||||
virtual void ContinueWithCertificate(
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> private_key) = 0;
|
||||
|
||||
// Called to cancel the certificate selection and abort the request.
|
||||
virtual void CancelCertificateSelection() = 0;
|
||||
@ -64,7 +67,8 @@ class SSLClientAuthHandler {
|
||||
// is static to avoid deleting |handler| while it is on the stack.
|
||||
static void ContinueWithCertificate(
|
||||
const base::WeakPtr<SSLClientAuthHandler>& handler,
|
||||
net::X509Certificate* cert);
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> key);
|
||||
|
||||
// Called to abort the request associated with |handler|. This is static to
|
||||
// avoid deleting |handler| while it is on the stack.
|
||||
@ -75,7 +79,7 @@ class SSLClientAuthHandler {
|
||||
class Core;
|
||||
|
||||
// Called when |core_| is done retrieving the cert list.
|
||||
void DidGetClientCerts(net::CertificateList client_certs);
|
||||
void DidGetClientCerts(net::ClientCertIdentityList client_certs);
|
||||
|
||||
// A reference-counted core so the ClientCertStore may outlive
|
||||
// SSLClientAuthHandler if the handler is destroyed while an operation on the
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace net {
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
}
|
||||
|
||||
@ -21,10 +22,12 @@ class ClientCertificateDelegate {
|
||||
ClientCertificateDelegate() {}
|
||||
virtual ~ClientCertificateDelegate() {}
|
||||
|
||||
// Continue the request with |cert|. |cert| may be nullptr to continue without
|
||||
// supplying a certificate. This decision will be remembered for future
|
||||
// requests to the domain.
|
||||
virtual void ContinueWithCertificate(net::X509Certificate* cert) = 0;
|
||||
// Continue the request with |cert| and matching |key|. |cert| may be nullptr
|
||||
// to continue without supplying a certificate. This decision will be
|
||||
// remembered for future requests to the domain.
|
||||
virtual void ContinueWithCertificate(
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<net::SSLPrivateKey> key) = 0;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ClientCertificateDelegate);
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "media/audio/audio_manager.h"
|
||||
#include "media/base/cdm_factory.h"
|
||||
#include "media/media_features.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
#include "storage/browser/quota/quota_manager.h"
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
#include "url/gurl.h"
|
||||
@ -249,7 +249,7 @@ void ContentBrowserClient::AllowCertificateError(
|
||||
void ContentBrowserClient::SelectClientCertificate(
|
||||
WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<ClientCertificateDelegate> delegate) {}
|
||||
|
||||
net::URLRequestContext* ContentBrowserClient::OverrideRequestContextForURL(
|
||||
|
@ -73,14 +73,14 @@ struct BindSourceInfo;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class ClientCertIdentity;
|
||||
using ClientCertIdentityList = std::vector<std::unique_ptr<ClientCertIdentity>>;
|
||||
class CookieOptions;
|
||||
class NetLog;
|
||||
class SSLCertRequestInfo;
|
||||
class SSLInfo;
|
||||
class URLRequest;
|
||||
class URLRequestContext;
|
||||
class X509Certificate;
|
||||
typedef std::vector<scoped_refptr<X509Certificate>> CertificateList;
|
||||
}
|
||||
|
||||
namespace rappor {
|
||||
@ -483,7 +483,7 @@ class CONTENT_EXPORT ContentBrowserClient {
|
||||
virtual void SelectClientCertificate(
|
||||
WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<ClientCertificateDelegate> delegate);
|
||||
|
||||
// Returns a class to get notifications about media event. The embedder can
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "content/test/data/mojo_layouttest_test.mojom.h"
|
||||
#include "media/mojo/features.h"
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
#include "services/service_manager/public/cpp/bind_source_info.h"
|
||||
#include "storage/browser/quota/quota_settings.h"
|
||||
@ -323,7 +324,7 @@ void ShellContentBrowserClient::GetQuotaSettings(
|
||||
void ShellContentBrowserClient::SelectClientCertificate(
|
||||
WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<ClientCertificateDelegate> delegate) {
|
||||
if (!select_client_certificate_callback_.is_null())
|
||||
select_client_certificate_callback_.Run();
|
||||
|
@ -56,7 +56,7 @@ class ShellContentBrowserClient : public ContentBrowserClient {
|
||||
void SelectClientCertificate(
|
||||
WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
net::CertificateList client_certs,
|
||||
net::ClientCertIdentityList client_certs,
|
||||
std::unique_ptr<ClientCertificateDelegate> delegate) override;
|
||||
SpeechRecognitionManagerDelegate* CreateSpeechRecognitionManagerDelegate()
|
||||
override;
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/ref_counted.h"
|
||||
|
||||
namespace crypto {
|
||||
|
||||
// PK11_SetPasswordFunc is a global setting. An implementation of
|
||||
@ -14,9 +16,9 @@ namespace crypto {
|
||||
// user data argument (|wincx|) to relevant NSS functions, which the global
|
||||
// password handler will call to do the actual work. This delegate should only
|
||||
// be used in NSS calls on worker threads due to the blocking nature.
|
||||
class CryptoModuleBlockingPasswordDelegate {
|
||||
class CryptoModuleBlockingPasswordDelegate
|
||||
: public base::RefCountedThreadSafe<CryptoModuleBlockingPasswordDelegate> {
|
||||
public:
|
||||
virtual ~CryptoModuleBlockingPasswordDelegate() {}
|
||||
|
||||
// Return a value suitable for passing to the |wincx| argument of relevant NSS
|
||||
// functions. This should be used instead of passing the object pointer
|
||||
@ -32,6 +34,11 @@ class CryptoModuleBlockingPasswordDelegate {
|
||||
// user entered.
|
||||
virtual std::string RequestPassword(const std::string& slot_name, bool retry,
|
||||
bool* cancelled) = 0;
|
||||
|
||||
protected:
|
||||
friend class base::RefCountedThreadSafe<CryptoModuleBlockingPasswordDelegate>;
|
||||
|
||||
virtual ~CryptoModuleBlockingPasswordDelegate() {}
|
||||
};
|
||||
|
||||
} // namespace crypto
|
||||
|
35
net/BUILD.gn
35
net/BUILD.gn
@ -321,12 +321,14 @@ component("net") {
|
||||
"ssl/channel_id_service.h",
|
||||
"ssl/channel_id_store.cc",
|
||||
"ssl/channel_id_store.h",
|
||||
"ssl/client_cert_identity.cc",
|
||||
"ssl/client_cert_identity.h",
|
||||
"ssl/client_cert_identity_mac.cc",
|
||||
"ssl/client_cert_identity_mac.h",
|
||||
"ssl/client_key_store.cc",
|
||||
"ssl/client_key_store.h",
|
||||
"ssl/default_channel_id_store.cc",
|
||||
"ssl/default_channel_id_store.h",
|
||||
"ssl/openssl_client_key_store.cc",
|
||||
"ssl/openssl_client_key_store.h",
|
||||
"ssl/openssl_ssl_util.cc",
|
||||
"ssl/openssl_ssl_util.h",
|
||||
"ssl/ssl_cert_request_info.cc",
|
||||
@ -1638,12 +1640,12 @@ component("net") {
|
||||
"ssl/ssl_config_service_defaults.h",
|
||||
"ssl/ssl_key_logger.cc",
|
||||
"ssl/ssl_key_logger.h",
|
||||
"ssl/ssl_platform_key.h",
|
||||
"ssl/ssl_platform_key_android.cc",
|
||||
"ssl/ssl_platform_key_android.h",
|
||||
"ssl/ssl_platform_key_mac.cc",
|
||||
"ssl/ssl_platform_key_mac.h",
|
||||
"ssl/ssl_platform_key_nss.cc",
|
||||
"ssl/ssl_platform_key_nss.h",
|
||||
"ssl/ssl_platform_key_util.cc",
|
||||
"ssl/ssl_platform_key_util.h",
|
||||
"ssl/ssl_platform_key_win.cc",
|
||||
@ -1787,8 +1789,6 @@ component("net") {
|
||||
sources -= [
|
||||
"cert/cert_database_openssl.cc",
|
||||
"cert/x509_certificate_openssl.cc",
|
||||
"ssl/openssl_client_key_store.cc",
|
||||
"ssl/openssl_client_key_store.h",
|
||||
]
|
||||
} else {
|
||||
if (is_android) {
|
||||
@ -1860,8 +1860,11 @@ component("net") {
|
||||
}
|
||||
|
||||
if (is_chromecast && use_nss_certs) {
|
||||
sources += [ "ssl/ssl_platform_key_chromecast.cc" ]
|
||||
sources -= [ "ssl/ssl_platform_key_nss.cc" ]
|
||||
sources -= [
|
||||
"ssl/client_cert_store_nss.cc",
|
||||
"ssl/client_cert_store_nss.h",
|
||||
"ssl/ssl_platform_key_nss.cc",
|
||||
]
|
||||
}
|
||||
|
||||
if (!enable_mdns) {
|
||||
@ -2476,6 +2479,10 @@ static_library("test_support") {
|
||||
"socket/socket_test_util.h",
|
||||
"spdy/chromium/spdy_test_util_common.cc",
|
||||
"spdy/chromium/spdy_test_util_common.h",
|
||||
"ssl/client_cert_identity_test_util.cc",
|
||||
"ssl/client_cert_identity_test_util.h",
|
||||
"ssl/ssl_private_key_test_util.cc",
|
||||
"ssl/ssl_private_key_test_util.h",
|
||||
"test/cert_test_util.cc",
|
||||
"test/cert_test_util.h",
|
||||
"test/cert_test_util_nss.cc",
|
||||
@ -5029,12 +5036,12 @@ test("net_unittests") {
|
||||
"spdy/core/spdy_test_utils.h",
|
||||
"spdy/platform/api/spdy_string_utils_test.cc",
|
||||
"ssl/channel_id_service_unittest.cc",
|
||||
"ssl/client_cert_identity_unittest.cc",
|
||||
"ssl/client_cert_store_mac_unittest.cc",
|
||||
"ssl/client_cert_store_nss_unittest.cc",
|
||||
"ssl/client_cert_store_unittest-inl.h",
|
||||
"ssl/client_cert_store_win_unittest.cc",
|
||||
"ssl/default_channel_id_store_unittest.cc",
|
||||
"ssl/openssl_client_key_store_unittest.cc",
|
||||
"ssl/ssl_cipher_suite_names_unittest.cc",
|
||||
"ssl/ssl_client_auth_cache_unittest.cc",
|
||||
"ssl/ssl_client_session_cache_unittest.cc",
|
||||
@ -5046,8 +5053,6 @@ test("net_unittests") {
|
||||
"ssl/ssl_platform_key_nss_unittest.cc",
|
||||
"ssl/ssl_platform_key_util_unittest.cc",
|
||||
"ssl/ssl_platform_key_win_unittest.cc",
|
||||
"ssl/ssl_private_key_test_util.cc",
|
||||
"ssl/ssl_private_key_test_util.h",
|
||||
"test/embedded_test_server/embedded_test_server_unittest.cc",
|
||||
"test/embedded_test_server/http_request_unittest.cc",
|
||||
"test/embedded_test_server/http_response_unittest.cc",
|
||||
@ -5235,10 +5240,6 @@ test("net_unittests") {
|
||||
sources -= [ "cert_net/nss_ocsp_unittest.cc" ]
|
||||
}
|
||||
|
||||
if (!use_openssl_certs) {
|
||||
sources -= [ "ssl/openssl_client_key_store_unittest.cc" ]
|
||||
}
|
||||
|
||||
if (enable_websockets) {
|
||||
sources += [
|
||||
"server/http_connection_unittest.cc",
|
||||
@ -5449,8 +5450,10 @@ test("net_unittests") {
|
||||
}
|
||||
|
||||
if (is_chromecast && use_nss_certs) {
|
||||
sources += [ "ssl/ssl_platform_key_chromecast_unittest.cc" ]
|
||||
sources -= [ "ssl/ssl_platform_key_nss_unittest.cc" ]
|
||||
sources -= [
|
||||
"ssl/client_cert_store_nss_unittest.cc",
|
||||
"ssl/ssl_platform_key_nss_unittest.cc",
|
||||
]
|
||||
}
|
||||
|
||||
# Include transport_security_state_generator tests.
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/observer_list_threadsafe.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/ssl/openssl_client_key_store.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
@ -19,12 +18,6 @@ CertDatabase::~CertDatabase() {}
|
||||
|
||||
void CertDatabase::OnAndroidKeyStoreChanged() {
|
||||
NotifyObserversCertDBChanged();
|
||||
// Dump the OpenSSLClientKeyStore to drop references to now disconnected
|
||||
// PrivateKeys stored in the in-memory key store. Note: this assumes that
|
||||
// every SSLClientAuthCache is dumped as part of notifying
|
||||
// OnCertDBChanged. Otherwise client auth decisions will be silently converted
|
||||
// to no-certificate decisions. See https://crbug.com/382696
|
||||
OpenSSLClientKeyStore::GetInstance()->Flush();
|
||||
}
|
||||
|
||||
void CertDatabase::OnAndroidKeyChainChanged() {
|
||||
|
@ -60,39 +60,6 @@ static const uint16_t kRSAKeyLength = 1024;
|
||||
// CreateKeyAndChannelIDEC will be signed using this digest algorithm.
|
||||
static const DigestAlgorithm kSignatureDigestAlgorithm = DIGEST_SHA256;
|
||||
|
||||
ClientCertSorter::ClientCertSorter() : now_(base::Time::Now()) {}
|
||||
|
||||
bool ClientCertSorter::operator()(
|
||||
const scoped_refptr<X509Certificate>& a,
|
||||
const scoped_refptr<X509Certificate>& b) const {
|
||||
// Certificates that are null are sorted last.
|
||||
if (!a.get() || !b.get())
|
||||
return a.get() && !b.get();
|
||||
|
||||
// Certificates that are expired/not-yet-valid are sorted last.
|
||||
bool a_is_valid = now_ >= a->valid_start() && now_ <= a->valid_expiry();
|
||||
bool b_is_valid = now_ >= b->valid_start() && now_ <= b->valid_expiry();
|
||||
if (a_is_valid != b_is_valid)
|
||||
return a_is_valid && !b_is_valid;
|
||||
|
||||
// Certificates with longer expirations appear as higher priority (less
|
||||
// than) certificates with shorter expirations.
|
||||
if (a->valid_expiry() != b->valid_expiry())
|
||||
return a->valid_expiry() > b->valid_expiry();
|
||||
|
||||
// If the expiration dates are equivalent, certificates that were issued
|
||||
// more recently should be prioritized over older certificates.
|
||||
if (a->valid_start() != b->valid_start())
|
||||
return a->valid_start() > b->valid_start();
|
||||
|
||||
// Otherwise, prefer client certificates with shorter chains.
|
||||
const X509Certificate::OSCertHandles& a_intermediates =
|
||||
a->GetIntermediateCertificates();
|
||||
const X509Certificate::OSCertHandles& b_intermediates =
|
||||
b->GetIntermediateCertificates();
|
||||
return a_intermediates.size() < b_intermediates.size();
|
||||
}
|
||||
|
||||
bool CreateKeyAndSelfSignedCert(const std::string& subject,
|
||||
uint32_t serial_number,
|
||||
base::Time not_valid_before,
|
||||
|
@ -86,28 +86,6 @@ NET_EXPORT bool ParseCertificateSandboxed(
|
||||
std::vector<std::string>* dns_names,
|
||||
std::vector<std::string>* ip_addresses);
|
||||
|
||||
// Comparator for use in STL algorithms that will sort client certificates by
|
||||
// order of preference.
|
||||
// Returns true if |a| is more preferable than |b|, allowing it to be used
|
||||
// with any algorithm that compares according to strict weak ordering.
|
||||
//
|
||||
// Criteria include:
|
||||
// - Prefer certificates that have a longer validity period (later
|
||||
// expiration dates)
|
||||
// - If equal, prefer certificates that were issued more recently
|
||||
// - If equal, prefer shorter chains (if available)
|
||||
class NET_EXPORT_PRIVATE ClientCertSorter {
|
||||
public:
|
||||
ClientCertSorter();
|
||||
|
||||
bool operator()(
|
||||
const scoped_refptr<X509Certificate>& a,
|
||||
const scoped_refptr<X509Certificate>& b) const;
|
||||
|
||||
private:
|
||||
base::Time now_;
|
||||
};
|
||||
|
||||
// Returns a CRYPTO_BUFFER_POOL for deduplicating certificates.
|
||||
NET_EXPORT CRYPTO_BUFFER_POOL* GetBufferPool();
|
||||
|
||||
|
@ -17,67 +17,6 @@ namespace net {
|
||||
|
||||
namespace x509_util {
|
||||
|
||||
TEST(X509UtilTest, SortClientCertificates) {
|
||||
CertificateList certs;
|
||||
certs.push_back(nullptr);
|
||||
|
||||
std::unique_ptr<crypto::RSAPrivateKey> key(
|
||||
crypto::RSAPrivateKey::Create(1024));
|
||||
ASSERT_TRUE(key);
|
||||
|
||||
scoped_refptr<X509Certificate> cert;
|
||||
std::string der_cert;
|
||||
|
||||
ASSERT_TRUE(CreateSelfSignedCert(key.get(), x509_util::DIGEST_SHA1,
|
||||
"CN=expired", 1, base::Time::UnixEpoch(),
|
||||
base::Time::UnixEpoch(), &der_cert));
|
||||
cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
|
||||
ASSERT_TRUE(cert);
|
||||
certs.push_back(cert);
|
||||
|
||||
const base::Time now = base::Time::Now();
|
||||
|
||||
ASSERT_TRUE(CreateSelfSignedCert(
|
||||
key.get(), x509_util::DIGEST_SHA1, "CN=not yet valid", 2,
|
||||
now + base::TimeDelta::FromDays(10), now + base::TimeDelta::FromDays(15),
|
||||
&der_cert));
|
||||
cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
|
||||
ASSERT_TRUE(cert);
|
||||
certs.push_back(cert);
|
||||
|
||||
ASSERT_TRUE(
|
||||
CreateSelfSignedCert(key.get(), x509_util::DIGEST_SHA1, "CN=older cert",
|
||||
3, now - base::TimeDelta::FromDays(5),
|
||||
now + base::TimeDelta::FromDays(5), &der_cert));
|
||||
cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
|
||||
ASSERT_TRUE(cert);
|
||||
certs.push_back(cert);
|
||||
|
||||
certs.push_back(nullptr);
|
||||
|
||||
ASSERT_TRUE(
|
||||
CreateSelfSignedCert(key.get(), x509_util::DIGEST_SHA1, "CN=newer cert",
|
||||
2, now - base::TimeDelta::FromDays(3),
|
||||
now + base::TimeDelta::FromDays(5), &der_cert));
|
||||
cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
|
||||
ASSERT_TRUE(cert);
|
||||
certs.push_back(cert);
|
||||
|
||||
std::sort(certs.begin(), certs.end(), ClientCertSorter());
|
||||
|
||||
ASSERT_EQ(6u, certs.size());
|
||||
ASSERT_TRUE(certs[0].get());
|
||||
EXPECT_EQ("newer cert", certs[0]->subject().common_name);
|
||||
ASSERT_TRUE(certs[1].get());
|
||||
EXPECT_EQ("older cert", certs[1]->subject().common_name);
|
||||
ASSERT_TRUE(certs[2].get());
|
||||
EXPECT_EQ("not yet valid", certs[2]->subject().common_name);
|
||||
ASSERT_TRUE(certs[3].get());
|
||||
EXPECT_EQ("expired", certs[3]->subject().common_name);
|
||||
ASSERT_FALSE(certs[4].get());
|
||||
ASSERT_FALSE(certs[5].get());
|
||||
}
|
||||
|
||||
// This test creates a self-signed cert and a private key and then verifies the
|
||||
// content of the certificate.
|
||||
TEST(X509UtilTest, CreateKeyAndSelfSigned) {
|
||||
|
@ -41,8 +41,8 @@ class FailingHttpTransaction : public HttpTransaction {
|
||||
const CompletionCallback& callback,
|
||||
const NetLogWithSource& net_log) override;
|
||||
int RestartIgnoringLastError(const CompletionCallback& callback) override;
|
||||
int RestartWithCertificate(X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
int RestartWithCertificate(scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) override;
|
||||
int RestartWithAuth(const AuthCredentials& credentials,
|
||||
const CompletionCallback& callback) override;
|
||||
@ -96,8 +96,8 @@ int FailingHttpTransaction::RestartIgnoringLastError(
|
||||
}
|
||||
|
||||
int FailingHttpTransaction::RestartWithCertificate(
|
||||
X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
|
@ -305,8 +305,8 @@ int HttpCache::Transaction::RestartIgnoringLastError(
|
||||
}
|
||||
|
||||
int HttpCache::Transaction::RestartWithCertificate(
|
||||
X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) {
|
||||
DCHECK(!callback.is_null());
|
||||
|
||||
@ -316,8 +316,8 @@ int HttpCache::Transaction::RestartWithCertificate(
|
||||
if (!cache_.get())
|
||||
return ERR_UNEXPECTED;
|
||||
|
||||
int rv =
|
||||
RestartNetworkRequestWithCertificate(client_cert, client_private_key);
|
||||
int rv = RestartNetworkRequestWithCertificate(std::move(client_cert),
|
||||
std::move(client_private_key));
|
||||
|
||||
if (rv == ERR_IO_PENDING)
|
||||
callback_ = callback;
|
||||
@ -2367,15 +2367,15 @@ int HttpCache::Transaction::RestartNetworkRequest() {
|
||||
}
|
||||
|
||||
int HttpCache::Transaction::RestartNetworkRequestWithCertificate(
|
||||
X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key) {
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key) {
|
||||
DCHECK(mode_ & WRITE || mode_ == NONE);
|
||||
DCHECK(network_trans_.get());
|
||||
DCHECK_EQ(STATE_NONE, next_state_);
|
||||
|
||||
next_state_ = STATE_SEND_REQUEST_COMPLETE;
|
||||
int rv = network_trans_->RestartWithCertificate(
|
||||
client_cert, client_private_key, io_callback_);
|
||||
std::move(client_cert), std::move(client_private_key), io_callback_);
|
||||
if (rv != ERR_IO_PENDING)
|
||||
return DoLoop(rv);
|
||||
return rv;
|
||||
|
@ -138,8 +138,8 @@ class HttpCache::Transaction : public HttpTransaction {
|
||||
const CompletionCallback& callback,
|
||||
const NetLogWithSource& net_log) override;
|
||||
int RestartIgnoringLastError(const CompletionCallback& callback) override;
|
||||
int RestartWithCertificate(X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
int RestartWithCertificate(scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) override;
|
||||
int RestartWithAuth(const AuthCredentials& credentials,
|
||||
const CompletionCallback& callback) override;
|
||||
@ -357,8 +357,9 @@ class HttpCache::Transaction : public HttpTransaction {
|
||||
|
||||
// Called to restart a network transaction with a client certificate.
|
||||
// Returns network error code.
|
||||
int RestartNetworkRequestWithCertificate(X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key);
|
||||
int RestartNetworkRequestWithCertificate(
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key);
|
||||
|
||||
// Called to restart a network transaction with authentication credentials.
|
||||
// Returns network error code.
|
||||
|
@ -150,8 +150,8 @@ int HttpNetworkTransaction::RestartIgnoringLastError(
|
||||
}
|
||||
|
||||
int HttpNetworkTransaction::RestartWithCertificate(
|
||||
X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) {
|
||||
// In HandleCertificateRequest(), we always tear down existing stream
|
||||
// requests to force a new connection. So we shouldn't have one here.
|
||||
@ -165,8 +165,8 @@ int HttpNetworkTransaction::RestartWithCertificate(
|
||||
ssl_config->client_cert = client_cert;
|
||||
ssl_config->client_private_key = client_private_key;
|
||||
session_->ssl_client_auth_cache()->Add(
|
||||
response_.cert_request_info->host_and_port, client_cert,
|
||||
client_private_key);
|
||||
response_.cert_request_info->host_and_port, std::move(client_cert),
|
||||
std::move(client_private_key));
|
||||
// Reset the other member variables.
|
||||
// Note: this is necessary only with SSL renegotiation.
|
||||
ResetStateForRestart();
|
||||
|
@ -62,8 +62,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction
|
||||
const CompletionCallback& callback,
|
||||
const NetLogWithSource& net_log) override;
|
||||
int RestartIgnoringLastError(const CompletionCallback& callback) override;
|
||||
int RestartWithCertificate(X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
int RestartWithCertificate(scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) override;
|
||||
int RestartWithAuth(const AuthCredentials& credentials,
|
||||
const CompletionCallback& callback) override;
|
||||
|
@ -86,9 +86,10 @@ class NET_EXPORT_PRIVATE HttpTransaction {
|
||||
virtual int RestartIgnoringLastError(const CompletionCallback& callback) = 0;
|
||||
|
||||
// Restarts the HTTP transaction with a client certificate.
|
||||
virtual int RestartWithCertificate(X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
const CompletionCallback& callback) = 0;
|
||||
virtual int RestartWithCertificate(
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) = 0;
|
||||
|
||||
// Restarts the HTTP transaction with authentication credentials.
|
||||
virtual int RestartWithAuth(const AuthCredentials& credentials,
|
||||
|
@ -262,8 +262,8 @@ int MockNetworkTransaction::RestartIgnoringLastError(
|
||||
}
|
||||
|
||||
int MockNetworkTransaction::RestartWithCertificate(
|
||||
X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
|
@ -189,8 +189,8 @@ class MockNetworkTransaction
|
||||
|
||||
int RestartIgnoringLastError(const CompletionCallback& callback) override;
|
||||
|
||||
int RestartWithCertificate(X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key,
|
||||
int RestartWithCertificate(scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key,
|
||||
const CompletionCallback& callback) override;
|
||||
|
||||
int RestartWithAuth(const AuthCredentials& credentials,
|
||||
|
84
net/ssl/client_cert_identity.cc
Normal file
84
net/ssl/client_cert_identity.cc
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
void IdentityOwningPrivateKeyCallback(
|
||||
std::unique_ptr<ClientCertIdentity> identity,
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback,
|
||||
scoped_refptr<SSLPrivateKey> private_key) {
|
||||
private_key_callback.Run(std::move(private_key));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ClientCertIdentity::ClientCertIdentity(scoped_refptr<net::X509Certificate> cert)
|
||||
: cert_(std::move(cert)) {}
|
||||
ClientCertIdentity::~ClientCertIdentity() = default;
|
||||
|
||||
// static
|
||||
void ClientCertIdentity::SelfOwningAcquirePrivateKey(
|
||||
std::unique_ptr<ClientCertIdentity> self,
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback) {
|
||||
ClientCertIdentity* self_ptr = self.get();
|
||||
auto wrapped_private_key_callback =
|
||||
base::Bind(&IdentityOwningPrivateKeyCallback, base::Passed(&self),
|
||||
private_key_callback);
|
||||
self_ptr->AcquirePrivateKey(wrapped_private_key_callback);
|
||||
}
|
||||
|
||||
void ClientCertIdentity::SetIntermediates(
|
||||
X509Certificate::OSCertHandles intermediates) {
|
||||
cert_ =
|
||||
X509Certificate::CreateFromHandle(cert_->os_cert_handle(), intermediates);
|
||||
// |cert_->os_cert_handle()| was already successfully parsed, so this should
|
||||
// never fail.
|
||||
DCHECK(cert_);
|
||||
}
|
||||
|
||||
ClientCertIdentitySorter::ClientCertIdentitySorter()
|
||||
: now_(base::Time::Now()) {}
|
||||
|
||||
bool ClientCertIdentitySorter::operator()(
|
||||
const std::unique_ptr<ClientCertIdentity>& a_identity,
|
||||
const std::unique_ptr<ClientCertIdentity>& b_identity) const {
|
||||
X509Certificate* a = a_identity->certificate();
|
||||
X509Certificate* b = b_identity->certificate();
|
||||
DCHECK(a);
|
||||
DCHECK(b);
|
||||
|
||||
// Certificates that are expired/not-yet-valid are sorted last.
|
||||
bool a_is_valid = now_ >= a->valid_start() && now_ <= a->valid_expiry();
|
||||
bool b_is_valid = now_ >= b->valid_start() && now_ <= b->valid_expiry();
|
||||
if (a_is_valid != b_is_valid)
|
||||
return a_is_valid && !b_is_valid;
|
||||
|
||||
// Certificates with longer expirations appear as higher priority (less
|
||||
// than) certificates with shorter expirations.
|
||||
if (a->valid_expiry() != b->valid_expiry())
|
||||
return a->valid_expiry() > b->valid_expiry();
|
||||
|
||||
// If the expiration dates are equivalent, certificates that were issued
|
||||
// more recently should be prioritized over older certificates.
|
||||
if (a->valid_start() != b->valid_start())
|
||||
return a->valid_start() > b->valid_start();
|
||||
|
||||
// Otherwise, prefer client certificates with shorter chains.
|
||||
const X509Certificate::OSCertHandles& a_intermediates =
|
||||
a->GetIntermediateCertificates();
|
||||
const X509Certificate::OSCertHandles& b_intermediates =
|
||||
b->GetIntermediateCertificates();
|
||||
return a_intermediates.size() < b_intermediates.size();
|
||||
}
|
||||
|
||||
} // namespace net
|
90
net/ssl/client_cert_identity.h
Normal file
90
net/ssl/client_cert_identity.h
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef NET_SSL_SSL_CLIENT_CERT_IDENTITY_H_
|
||||
#define NET_SSL_SSL_CLIENT_CERT_IDENTITY_H_
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "net/base/net_export.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include <Security/SecBase.h>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
class Time;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
|
||||
class SSLPrivateKey;
|
||||
|
||||
// Represents a client certificate and a promise to retrieve the associated
|
||||
// private key.
|
||||
class NET_EXPORT ClientCertIdentity {
|
||||
public:
|
||||
explicit ClientCertIdentity(scoped_refptr<net::X509Certificate> cert);
|
||||
virtual ~ClientCertIdentity();
|
||||
|
||||
// Returns the certificate.
|
||||
X509Certificate* certificate() const { return cert_.get(); }
|
||||
|
||||
// Passes the private key to |private_key_callback| on the same sequence
|
||||
// AcquirePrivateKey is called on, or nullptr on error. The callback may be
|
||||
// run synchronously or asynchronously. The caller is responsible for
|
||||
// keeping the ClientCertIdentity alive until the callback is run.
|
||||
virtual void AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback) = 0;
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// Returns the SecIdentityRef for this identity.
|
||||
virtual SecIdentityRef sec_identity_ref() const = 0;
|
||||
#endif
|
||||
|
||||
// Acquires the private key for |identity|, taking ownership of |identity| so
|
||||
// that the caller does not need to manage its lifetime. The other semantics
|
||||
// are the same as for AcquirePrivateKey above.
|
||||
static void SelfOwningAcquirePrivateKey(
|
||||
std::unique_ptr<ClientCertIdentity> identity,
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback);
|
||||
|
||||
// Sets the intermediates of |certificate()| to |intermediates|. Note that
|
||||
// this will change the value of |certificate()|, and any references that
|
||||
// were retained to the previous value will not reflect the updated
|
||||
// intermediates list.
|
||||
void SetIntermediates(X509Certificate::OSCertHandles intermediates);
|
||||
|
||||
private:
|
||||
scoped_refptr<net::X509Certificate> cert_;
|
||||
};
|
||||
|
||||
// Comparator for use in STL algorithms that will sort client certificates by
|
||||
// order of preference.
|
||||
// Returns true if |a| is more preferable than |b|, allowing it to be used
|
||||
// with any algorithm that compares according to strict weak ordering.
|
||||
//
|
||||
// Criteria include:
|
||||
// - Prefer certificates that have a longer validity period (later
|
||||
// expiration dates)
|
||||
// - If equal, prefer certificates that were issued more recently
|
||||
// - If equal, prefer shorter chains (if available)
|
||||
class NET_EXPORT_PRIVATE ClientCertIdentitySorter {
|
||||
public:
|
||||
ClientCertIdentitySorter();
|
||||
|
||||
bool operator()(const std::unique_ptr<ClientCertIdentity>& a,
|
||||
const std::unique_ptr<ClientCertIdentity>& b) const;
|
||||
|
||||
private:
|
||||
base::Time now_;
|
||||
};
|
||||
|
||||
using ClientCertIdentityList = std::vector<std::unique_ptr<ClientCertIdentity>>;
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_SSL_SSL_CLIENT_CERT_IDENTITY_H_
|
32
net/ssl/client_cert_identity_mac.cc
Normal file
32
net/ssl/client_cert_identity_mac.cc
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/ssl/client_cert_identity_mac.h"
|
||||
|
||||
#include "net/ssl/ssl_platform_key_mac.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
ClientCertIdentityMac::ClientCertIdentityMac(
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
base::ScopedCFTypeRef<SecIdentityRef> sec_identity)
|
||||
: ClientCertIdentity(std::move(cert)), identity_(std::move(sec_identity)) {}
|
||||
|
||||
ClientCertIdentityMac::~ClientCertIdentityMac() = default;
|
||||
|
||||
void ClientCertIdentityMac::AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback) {
|
||||
// This only adds a ref to and returns the private key from identity_ so it
|
||||
// doesn't need to run on a worker thread.
|
||||
private_key_callback.Run(
|
||||
CreateSSLPrivateKeyForSecIdentity(certificate(), identity_.get()));
|
||||
}
|
||||
|
||||
SecIdentityRef ClientCertIdentityMac::sec_identity_ref() const {
|
||||
return identity_.get();
|
||||
}
|
||||
|
||||
} // namespace net
|
34
net/ssl/client_cert_identity_mac.h
Normal file
34
net/ssl/client_cert_identity_mac.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef NET_SSL_SSL_CLIENT_CERT_IDENTITY_MAC_H_
|
||||
#define NET_SSL_SSL_CLIENT_CERT_IDENTITY_MAC_H_
|
||||
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
|
||||
#include <Security/SecBase.h>
|
||||
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "net/base/net_export.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
class NET_EXPORT_PRIVATE ClientCertIdentityMac : public ClientCertIdentity {
|
||||
public:
|
||||
ClientCertIdentityMac(scoped_refptr<net::X509Certificate> cert,
|
||||
base::ScopedCFTypeRef<SecIdentityRef> sec_identity);
|
||||
~ClientCertIdentityMac() override;
|
||||
|
||||
void AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback) override;
|
||||
SecIdentityRef sec_identity_ref() const override;
|
||||
|
||||
private:
|
||||
base::ScopedCFTypeRef<SecIdentityRef> identity_;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_SSL_SSL_CLIENT_CERT_IDENTITY_MAC_H_
|
82
net/ssl/client_cert_identity_test_util.cc
Normal file
82
net/ssl/client_cert_identity_test_util.cc
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/ssl/test_ssl_private_key.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "third_party/boringssl/src/include/openssl/bytestring.h"
|
||||
#include "third_party/boringssl/src/include/openssl/evp.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
FakeClientCertIdentity::FakeClientCertIdentity(
|
||||
scoped_refptr<X509Certificate> cert,
|
||||
scoped_refptr<SSLPrivateKey> key)
|
||||
: ClientCertIdentity(std::move(cert)), key_(std::move(key)) {}
|
||||
|
||||
FakeClientCertIdentity::~FakeClientCertIdentity() = default;
|
||||
|
||||
// static
|
||||
std::unique_ptr<FakeClientCertIdentity>
|
||||
FakeClientCertIdentity::CreateFromCertAndKeyFiles(
|
||||
const base::FilePath& dir,
|
||||
const std::string& cert_filename,
|
||||
const std::string& key_filename) {
|
||||
scoped_refptr<X509Certificate> cert =
|
||||
net::ImportCertFromFile(dir, cert_filename);
|
||||
if (!cert)
|
||||
return nullptr;
|
||||
|
||||
std::string pkcs8;
|
||||
if (!base::ReadFileToString(dir.AppendASCII(key_filename), &pkcs8))
|
||||
return nullptr;
|
||||
|
||||
CBS cbs;
|
||||
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pkcs8.data()), pkcs8.size());
|
||||
bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs));
|
||||
if (!pkey || CBS_len(&cbs) != 0)
|
||||
return nullptr;
|
||||
|
||||
scoped_refptr<SSLPrivateKey> ssl_private_key =
|
||||
WrapOpenSSLPrivateKey(std::move(pkey));
|
||||
if (!ssl_private_key)
|
||||
return nullptr;
|
||||
|
||||
return base::MakeUnique<FakeClientCertIdentity>(cert, ssl_private_key);
|
||||
}
|
||||
|
||||
std::unique_ptr<FakeClientCertIdentity> FakeClientCertIdentity::Copy() {
|
||||
return base::MakeUnique<FakeClientCertIdentity>(certificate(), key_);
|
||||
}
|
||||
|
||||
void FakeClientCertIdentity::AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback) {
|
||||
private_key_callback.Run(key_);
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
SecIdentityRef FakeClientCertIdentity::sec_identity_ref() const {
|
||||
// Any tests that depend on having a real SecIdentityRef should use a real
|
||||
// ClientCertIdentityMac.
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
ClientCertIdentityList FakeClientCertIdentityListFromCertificateList(
|
||||
const CertificateList& certs) {
|
||||
ClientCertIdentityList result;
|
||||
for (const auto& cert : certs) {
|
||||
result.push_back(base::MakeUnique<FakeClientCertIdentity>(cert, nullptr));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace net
|
57
net/ssl/client_cert_identity_test_util.h
Normal file
57
net/ssl/client_cert_identity_test_util.h
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef NET_SSL_SSL_CLIENT_CERT_IDENTITY_TEST_UTIL_H_
|
||||
#define NET_SSL_SSL_CLIENT_CERT_IDENTITY_TEST_UTIL_H_
|
||||
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
|
||||
namespace base {
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
|
||||
// Simple ClientCertIdentity implementation for testing.
|
||||
// Note: this implementation of AcquirePrivateKey will always call the callback
|
||||
// synchronously.
|
||||
class FakeClientCertIdentity : public ClientCertIdentity {
|
||||
public:
|
||||
FakeClientCertIdentity(scoped_refptr<X509Certificate> cert,
|
||||
scoped_refptr<SSLPrivateKey> key);
|
||||
~FakeClientCertIdentity() override;
|
||||
|
||||
// Creates a FakeClientCertIdentity from a certificate file (DER or PEM) and
|
||||
// private key file (unencrypted pkcs8). Returns nullptr on error.
|
||||
static std::unique_ptr<FakeClientCertIdentity> CreateFromCertAndKeyFiles(
|
||||
const base::FilePath& dir,
|
||||
const std::string& cert_filename,
|
||||
const std::string& key_filename);
|
||||
|
||||
// Duplicates the FakeClientCertIdentity.
|
||||
std::unique_ptr<FakeClientCertIdentity> Copy();
|
||||
|
||||
// Returns the SSLPrivateKey in a more convenient way, for tests.
|
||||
SSLPrivateKey* ssl_private_key() const { return key_.get(); }
|
||||
|
||||
// ClientCertIdentity implementation:
|
||||
void AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback) override;
|
||||
#if defined(OS_MACOSX)
|
||||
SecIdentityRef sec_identity_ref() const override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
scoped_refptr<SSLPrivateKey> key_;
|
||||
};
|
||||
|
||||
// Converts a CertificateList to a ClientCertIdentityList of
|
||||
// FakeClientCertIdentity, with null private keys.
|
||||
ClientCertIdentityList FakeClientCertIdentityListFromCertificateList(
|
||||
const CertificateList& certs);
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_SSL_SSL_CLIENT_CERT_IDENTITY_TEST_UTIL_H_
|
72
net/ssl/client_cert_identity_unittest.cc
Normal file
72
net/ssl/client_cert_identity_unittest.cc
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "crypto/rsa_private_key.h"
|
||||
#include "net/cert/x509_util.h"
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
TEST(ClientCertIdentitySorter, SortClientCertificates) {
|
||||
ClientCertIdentityList certs;
|
||||
|
||||
std::unique_ptr<crypto::RSAPrivateKey> key(
|
||||
crypto::RSAPrivateKey::Create(1024));
|
||||
ASSERT_TRUE(key);
|
||||
|
||||
scoped_refptr<X509Certificate> cert;
|
||||
std::string der_cert;
|
||||
|
||||
ASSERT_TRUE(x509_util::CreateSelfSignedCert(
|
||||
key.get(), x509_util::DIGEST_SHA1, "CN=expired", 1,
|
||||
base::Time::UnixEpoch(), base::Time::UnixEpoch(), &der_cert));
|
||||
cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
|
||||
ASSERT_TRUE(cert);
|
||||
certs.push_back(base::MakeUnique<FakeClientCertIdentity>(cert, nullptr));
|
||||
|
||||
const base::Time now = base::Time::Now();
|
||||
|
||||
ASSERT_TRUE(x509_util::CreateSelfSignedCert(
|
||||
key.get(), x509_util::DIGEST_SHA1, "CN=not yet valid", 2,
|
||||
now + base::TimeDelta::FromDays(10), now + base::TimeDelta::FromDays(15),
|
||||
&der_cert));
|
||||
cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
|
||||
ASSERT_TRUE(cert);
|
||||
certs.push_back(base::MakeUnique<FakeClientCertIdentity>(cert, nullptr));
|
||||
|
||||
ASSERT_TRUE(x509_util::CreateSelfSignedCert(
|
||||
key.get(), x509_util::DIGEST_SHA1, "CN=older cert", 3,
|
||||
now - base::TimeDelta::FromDays(5), now + base::TimeDelta::FromDays(5),
|
||||
&der_cert));
|
||||
cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
|
||||
ASSERT_TRUE(cert);
|
||||
certs.push_back(base::MakeUnique<FakeClientCertIdentity>(cert, nullptr));
|
||||
|
||||
ASSERT_TRUE(x509_util::CreateSelfSignedCert(
|
||||
key.get(), x509_util::DIGEST_SHA1, "CN=newer cert", 2,
|
||||
now - base::TimeDelta::FromDays(3), now + base::TimeDelta::FromDays(5),
|
||||
&der_cert));
|
||||
cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
|
||||
ASSERT_TRUE(cert);
|
||||
certs.push_back(base::MakeUnique<FakeClientCertIdentity>(cert, nullptr));
|
||||
|
||||
std::sort(certs.begin(), certs.end(), ClientCertIdentitySorter());
|
||||
|
||||
ASSERT_EQ(4u, certs.size());
|
||||
ASSERT_TRUE(certs[0].get());
|
||||
EXPECT_EQ("newer cert", certs[0]->certificate()->subject().common_name);
|
||||
ASSERT_TRUE(certs[1].get());
|
||||
EXPECT_EQ("older cert", certs[1]->certificate()->subject().common_name);
|
||||
ASSERT_TRUE(certs[2].get());
|
||||
EXPECT_EQ("not yet valid", certs[2]->certificate()->subject().common_name);
|
||||
ASSERT_TRUE(certs[3].get());
|
||||
EXPECT_EQ("expired", certs[3]->certificate()->subject().common_name);
|
||||
}
|
||||
|
||||
} // namespace net
|
@ -9,6 +9,7 @@
|
||||
#include "base/macros.h"
|
||||
#include "net/base/net_export.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
@ -22,13 +23,12 @@ class NET_EXPORT ClientCertStore {
|
||||
public:
|
||||
virtual ~ClientCertStore() {}
|
||||
|
||||
using ClientCertListCallback = base::Callback<void(CertificateList)>;
|
||||
using ClientCertListCallback = base::Callback<void(ClientCertIdentityList)>;
|
||||
|
||||
// Get client certs matching the |cert_request_info|. On completion, the
|
||||
// results will be stored in |selected_certs| and the |callback| will be run.
|
||||
// The |callback| may be called sychronously. The caller must ensure the
|
||||
// ClientCertStore and |cert_request_info| remain alive until the callback
|
||||
// has been run.
|
||||
// Get client certs matching the |cert_request_info| and pass them to the
|
||||
// |callback|. The |callback| may be called sychronously. The caller must
|
||||
// ensure the ClientCertStore and |cert_request_info| remain alive until the
|
||||
// callback has been run.
|
||||
virtual void GetClientCerts(const SSLCertRequestInfo& cert_request_info,
|
||||
const ClientCertListCallback& callback) = 0;
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/mac_logging.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "crypto/mac_security_services_lock.h"
|
||||
@ -24,6 +25,7 @@
|
||||
#include "net/cert/x509_util.h"
|
||||
#include "net/cert/x509_util_ios_and_mac.h"
|
||||
#include "net/cert/x509_util_mac.h"
|
||||
#include "net/ssl/client_cert_identity_mac.h"
|
||||
|
||||
using base::ScopedCFTypeRef;
|
||||
|
||||
@ -83,18 +85,18 @@ OSStatus CopyCertChain(SecCertificateRef cert_handle,
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns true if |*cert| is issued by an authority in |valid_issuers|
|
||||
// according to Keychain Services, rather than using |cert|'s intermediate
|
||||
// certificates. If it is, |*cert| is updated to point to the completed
|
||||
// certificate
|
||||
// Returns true if |*identity| is issued by an authority in |valid_issuers|
|
||||
// according to Keychain Services, rather than using |identity|'s intermediate
|
||||
// certificates. If it is, |*identity| is updated to include the intermediates.
|
||||
bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
|
||||
scoped_refptr<X509Certificate>* cert) {
|
||||
DCHECK(cert);
|
||||
DCHECK(cert->get());
|
||||
ClientCertIdentity* identity) {
|
||||
DCHECK(identity);
|
||||
DCHECK(identity->sec_identity_ref());
|
||||
|
||||
base::ScopedCFTypeRef<SecCertificateRef> os_cert(
|
||||
x509_util::CreateSecCertificateFromX509Certificate(cert->get()));
|
||||
if (!os_cert)
|
||||
ScopedCFTypeRef<SecCertificateRef> os_cert;
|
||||
int err = SecIdentityCopyCertificate(identity->sec_identity_ref(),
|
||||
os_cert.InitializeInto());
|
||||
if (err != noErr)
|
||||
return false;
|
||||
CFArrayRef cert_chain = NULL;
|
||||
OSStatus result = CopyCertChain(os_cert.get(), &cert_chain);
|
||||
@ -109,9 +111,9 @@ bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
|
||||
std::vector<SecCertificateRef> intermediates;
|
||||
for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain);
|
||||
i < chain_count; ++i) {
|
||||
SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
|
||||
SecCertificateRef sec_cert = reinterpret_cast<SecCertificateRef>(
|
||||
const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
|
||||
intermediates.push_back(cert);
|
||||
intermediates.push_back(sec_cert);
|
||||
}
|
||||
|
||||
scoped_refptr<X509Certificate> new_cert(
|
||||
@ -122,7 +124,7 @@ bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
|
||||
if (!new_cert || !new_cert->IsIssuedByEncoded(valid_issuers))
|
||||
return false;
|
||||
|
||||
cert->swap(new_cert);
|
||||
identity->SetIntermediates(new_cert->GetIntermediateCertificates());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -177,58 +179,65 @@ bool SupportsSSLClientAuth(SecCertificateRef cert) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Examines the certificates in |preferred_cert| and |regular_certs| to find
|
||||
// all certificates that match the client certificate request in |request|,
|
||||
// storing the matching certificates in |selected_certs|.
|
||||
// Examines the certificates in |preferred_identity| and |regular_identities| to
|
||||
// find all certificates that match the client certificate request in |request|,
|
||||
// storing the matching certificates in |selected_identities|.
|
||||
// If |query_keychain| is true, Keychain Services will be queried to construct
|
||||
// full certificate chains. If it is false, only the the certificates and their
|
||||
// intermediates (available via X509Certificate::GetIntermediateCertificates())
|
||||
// will be considered.
|
||||
void GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert,
|
||||
const CertificateList& regular_certs,
|
||||
void GetClientCertsImpl(std::unique_ptr<ClientCertIdentity> preferred_identity,
|
||||
ClientCertIdentityList regular_identities,
|
||||
const SSLCertRequestInfo& request,
|
||||
bool query_keychain,
|
||||
CertificateList* selected_certs) {
|
||||
CertificateList preliminary_list;
|
||||
if (preferred_cert.get())
|
||||
preliminary_list.push_back(preferred_cert);
|
||||
preliminary_list.insert(preliminary_list.end(), regular_certs.begin(),
|
||||
regular_certs.end());
|
||||
ClientCertIdentityList* selected_identities) {
|
||||
scoped_refptr<X509Certificate> preferred_cert_orig;
|
||||
ClientCertIdentityList preliminary_list(std::move(regular_identities));
|
||||
if (preferred_identity) {
|
||||
preferred_cert_orig = preferred_identity->certificate();
|
||||
preliminary_list.insert(preliminary_list.begin(),
|
||||
std::move(preferred_identity));
|
||||
}
|
||||
|
||||
selected_certs->clear();
|
||||
selected_identities->clear();
|
||||
for (size_t i = 0; i < preliminary_list.size(); ++i) {
|
||||
scoped_refptr<X509Certificate>& cert = preliminary_list[i];
|
||||
if (cert->HasExpired())
|
||||
std::unique_ptr<ClientCertIdentity>& cert = preliminary_list[i];
|
||||
if (cert->certificate()->HasExpired())
|
||||
continue;
|
||||
|
||||
// Skip duplicates (a cert may be in multiple keychains).
|
||||
auto cert_iter = std::find_if(
|
||||
selected_certs->begin(), selected_certs->end(),
|
||||
[&cert](const scoped_refptr<X509Certificate>& other_cert) {
|
||||
return X509Certificate::IsSameOSCert(cert->os_cert_handle(),
|
||||
other_cert->os_cert_handle());
|
||||
selected_identities->begin(), selected_identities->end(),
|
||||
[&cert](
|
||||
const std::unique_ptr<ClientCertIdentity>& other_cert_identity) {
|
||||
return X509Certificate::IsSameOSCert(
|
||||
cert->certificate()->os_cert_handle(),
|
||||
other_cert_identity->certificate()->os_cert_handle());
|
||||
});
|
||||
if (cert_iter != selected_certs->end())
|
||||
if (cert_iter != selected_identities->end())
|
||||
continue;
|
||||
|
||||
// Check if the certificate issuer is allowed by the server.
|
||||
if (request.cert_authorities.empty() ||
|
||||
cert->IsIssuedByEncoded(request.cert_authorities) ||
|
||||
cert->certificate()->IsIssuedByEncoded(request.cert_authorities) ||
|
||||
(query_keychain &&
|
||||
IsIssuedByInKeychain(request.cert_authorities, &cert))) {
|
||||
selected_certs->push_back(cert);
|
||||
IsIssuedByInKeychain(request.cert_authorities, cert.get()))) {
|
||||
selected_identities->push_back(std::move(cert));
|
||||
}
|
||||
}
|
||||
|
||||
// Preferred cert should appear first in the ui, so exclude it from the
|
||||
// sorting.
|
||||
CertificateList::iterator sort_begin = selected_certs->begin();
|
||||
CertificateList::iterator sort_end = selected_certs->end();
|
||||
if (preferred_cert.get() && sort_begin != sort_end &&
|
||||
sort_begin->get() == preferred_cert.get()) {
|
||||
// sorting. Compare the os_cert_handle since the X509Certificate object may
|
||||
// have changed if intermediates were added.
|
||||
ClientCertIdentityList::iterator sort_begin = selected_identities->begin();
|
||||
ClientCertIdentityList::iterator sort_end = selected_identities->end();
|
||||
if (preferred_cert_orig && sort_begin != sort_end &&
|
||||
X509Certificate::IsSameOSCert(
|
||||
sort_begin->get()->certificate()->os_cert_handle(),
|
||||
preferred_cert_orig->os_cert_handle())) {
|
||||
++sort_begin;
|
||||
}
|
||||
sort(sort_begin, sort_end, x509_util::ClientCertSorter());
|
||||
sort(sort_begin, sort_end, ClientCertIdentitySorter());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -242,12 +251,12 @@ void ClientCertStoreMac::GetClientCerts(
|
||||
const ClientCertListCallback& callback) {
|
||||
std::string server_domain = request.host_and_port.host();
|
||||
|
||||
ScopedCFTypeRef<SecIdentityRef> preferred_identity;
|
||||
ScopedCFTypeRef<SecIdentityRef> preferred_sec_identity;
|
||||
if (!server_domain.empty()) {
|
||||
// See if there's an identity preference for this domain:
|
||||
ScopedCFTypeRef<CFStringRef> domain_str(
|
||||
base::SysUTF8ToCFStringRef("https://" + server_domain));
|
||||
SecIdentityRef identity = NULL;
|
||||
SecIdentityRef sec_identity = NULL;
|
||||
// While SecIdentityCopyPreferences appears to take a list of CA issuers
|
||||
// to restrict the identity search to, within Security.framework the
|
||||
// argument is ignored and filtering unimplemented. See
|
||||
@ -255,14 +264,15 @@ void ClientCertStoreMac::GetClientCerts(
|
||||
// _SecIdentityCopyPreferenceMatchingName().
|
||||
{
|
||||
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
|
||||
if (SecIdentityCopyPreference(domain_str, 0, NULL, &identity) == noErr)
|
||||
preferred_identity.reset(identity);
|
||||
if (SecIdentityCopyPreference(domain_str, 0, NULL, &sec_identity) ==
|
||||
noErr)
|
||||
preferred_sec_identity.reset(sec_identity);
|
||||
}
|
||||
}
|
||||
|
||||
// Now enumerate the identities in the available keychains.
|
||||
scoped_refptr<X509Certificate> preferred_cert = NULL;
|
||||
CertificateList regular_certs;
|
||||
std::unique_ptr<ClientCertIdentity> preferred_identity;
|
||||
ClientCertIdentityList regular_identities;
|
||||
|
||||
SecIdentitySearchRef search = NULL;
|
||||
OSStatus err;
|
||||
@ -271,71 +281,76 @@ void ClientCertStoreMac::GetClientCerts(
|
||||
err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
|
||||
}
|
||||
if (err) {
|
||||
callback.Run(CertificateList());
|
||||
callback.Run(ClientCertIdentityList());
|
||||
return;
|
||||
}
|
||||
ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search);
|
||||
while (!err) {
|
||||
SecIdentityRef identity = NULL;
|
||||
ScopedCFTypeRef<SecIdentityRef> sec_identity;
|
||||
{
|
||||
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
|
||||
err = SecIdentitySearchCopyNext(search, &identity);
|
||||
err = SecIdentitySearchCopyNext(search, sec_identity.InitializeInto());
|
||||
}
|
||||
if (err)
|
||||
break;
|
||||
ScopedCFTypeRef<SecIdentityRef> scoped_identity(identity);
|
||||
|
||||
SecCertificateRef cert_handle;
|
||||
err = SecIdentityCopyCertificate(identity, &cert_handle);
|
||||
ScopedCFTypeRef<SecCertificateRef> cert_handle;
|
||||
err = SecIdentityCopyCertificate(sec_identity.get(),
|
||||
cert_handle.InitializeInto());
|
||||
if (err != noErr)
|
||||
continue;
|
||||
ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle);
|
||||
|
||||
if (!SupportsSSLClientAuth(cert_handle))
|
||||
if (!SupportsSSLClientAuth(cert_handle.get()))
|
||||
continue;
|
||||
|
||||
scoped_refptr<X509Certificate> cert(
|
||||
x509_util::CreateX509CertificateFromSecCertificate(
|
||||
cert_handle, std::vector<SecCertificateRef>()));
|
||||
cert_handle.get(), std::vector<SecCertificateRef>()));
|
||||
if (!cert)
|
||||
continue;
|
||||
|
||||
if (preferred_identity && CFEqual(preferred_identity, identity)) {
|
||||
if (preferred_sec_identity &&
|
||||
CFEqual(preferred_sec_identity, sec_identity.get())) {
|
||||
// Only one certificate should match.
|
||||
DCHECK(!preferred_cert.get());
|
||||
preferred_cert = cert;
|
||||
DCHECK(!preferred_identity.get());
|
||||
preferred_identity = base::MakeUnique<ClientCertIdentityMac>(
|
||||
std::move(cert), std::move(sec_identity));
|
||||
} else {
|
||||
regular_certs.push_back(cert);
|
||||
regular_identities.push_back(base::MakeUnique<ClientCertIdentityMac>(
|
||||
std::move(cert), std::move(sec_identity)));
|
||||
}
|
||||
}
|
||||
|
||||
if (err != errSecItemNotFound) {
|
||||
OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error";
|
||||
callback.Run(CertificateList());
|
||||
callback.Run(ClientCertIdentityList());
|
||||
return;
|
||||
}
|
||||
|
||||
CertificateList selected_certs;
|
||||
GetClientCertsImpl(preferred_cert, regular_certs, request, true,
|
||||
&selected_certs);
|
||||
callback.Run(std::move(selected_certs));
|
||||
ClientCertIdentityList selected_identities;
|
||||
GetClientCertsImpl(std::move(preferred_identity),
|
||||
std::move(regular_identities), request, true,
|
||||
&selected_identities);
|
||||
callback.Run(std::move(selected_identities));
|
||||
}
|
||||
|
||||
bool ClientCertStoreMac::SelectClientCertsForTesting(
|
||||
const CertificateList& input_certs,
|
||||
ClientCertIdentityList input_identities,
|
||||
const SSLCertRequestInfo& request,
|
||||
CertificateList* selected_certs) {
|
||||
GetClientCertsImpl(NULL, input_certs, request, false, selected_certs);
|
||||
ClientCertIdentityList* selected_identities) {
|
||||
GetClientCertsImpl(NULL, std::move(input_identities), request, false,
|
||||
selected_identities);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting(
|
||||
const scoped_refptr<X509Certificate>& preferred_cert,
|
||||
const CertificateList& regular_certs,
|
||||
std::unique_ptr<ClientCertIdentity> preferred_identity,
|
||||
ClientCertIdentityList regular_identities,
|
||||
const SSLCertRequestInfo& request,
|
||||
CertificateList* selected_certs) {
|
||||
GetClientCertsImpl(
|
||||
preferred_cert, regular_certs, request, false, selected_certs);
|
||||
ClientCertIdentityList* selected_identities) {
|
||||
GetClientCertsImpl(std::move(preferred_identity),
|
||||
std::move(regular_identities), request, false,
|
||||
selected_identities);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -26,24 +26,24 @@ class NET_EXPORT ClientCertStoreMac : public ClientCertStore {
|
||||
friend class ClientCertStoreMacTest;
|
||||
friend class ClientCertStoreMacTestDelegate;
|
||||
|
||||
// A hook for testing. Filters |input_certs| using the logic being used to
|
||||
// filter the system store when GetClientCerts() is called.
|
||||
// Implemented by creating a list of certificates that otherwise would be
|
||||
// extracted from the system store and filtering it using the common logic
|
||||
// (less adequate than the approach used on Windows).
|
||||
bool SelectClientCertsForTesting(const CertificateList& input_certs,
|
||||
// A hook for testing. Filters |input_identities| using the logic being used
|
||||
// to filter the system store when GetClientCerts() is called. Implemented by
|
||||
// creating a list of certificates that otherwise would be extracted from the
|
||||
// system store and filtering it using the common logic (less adequate than
|
||||
// the approach used on Windows).
|
||||
bool SelectClientCertsForTesting(ClientCertIdentityList input_identities,
|
||||
const SSLCertRequestInfo& cert_request_info,
|
||||
CertificateList* selected_certs);
|
||||
ClientCertIdentityList* selected_identities);
|
||||
|
||||
// Testing hook specific to Mac, where the internal logic recognizes preferred
|
||||
// certificates for particular domains. If the preferred certificate is
|
||||
// present in the output list (i.e. it doesn't get filtered out), it should
|
||||
// always come first.
|
||||
bool SelectClientCertsGivenPreferredForTesting(
|
||||
const scoped_refptr<X509Certificate>& preferred_cert,
|
||||
const CertificateList& regular_certs,
|
||||
std::unique_ptr<ClientCertIdentity> preferred_identity,
|
||||
ClientCertIdentityList regular_identities,
|
||||
const SSLCertRequestInfo& request,
|
||||
CertificateList* selected_certs);
|
||||
ClientCertIdentityList* selected_identities);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ClientCertStoreMac);
|
||||
};
|
||||
|
@ -4,7 +4,10 @@
|
||||
|
||||
#include "net/ssl/client_cert_store_mac.h"
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
#include "net/ssl/client_cert_store_unittest-inl.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
@ -12,9 +15,10 @@ class ClientCertStoreMacTestDelegate {
|
||||
public:
|
||||
bool SelectClientCerts(const CertificateList& input_certs,
|
||||
const SSLCertRequestInfo& cert_request_info,
|
||||
CertificateList* selected_certs) {
|
||||
ClientCertIdentityList* selected_certs) {
|
||||
return store_.SelectClientCertsForTesting(
|
||||
input_certs, cert_request_info, selected_certs);
|
||||
FakeClientCertIdentityListFromCertificateList(input_certs),
|
||||
cert_request_info, selected_certs);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -31,9 +35,14 @@ class ClientCertStoreMacTest : public ::testing::Test {
|
||||
const scoped_refptr<X509Certificate>& preferred_cert,
|
||||
const CertificateList& regular_certs,
|
||||
const SSLCertRequestInfo& request,
|
||||
CertificateList* selected_certs) {
|
||||
ClientCertIdentityList* selected_certs) {
|
||||
std::unique_ptr<ClientCertIdentity> preferred_identity(
|
||||
base::MakeUnique<FakeClientCertIdentity>(preferred_cert, nullptr));
|
||||
|
||||
return store_.SelectClientCertsGivenPreferredForTesting(
|
||||
preferred_cert, regular_certs, request, selected_certs);
|
||||
std::move(preferred_identity),
|
||||
FakeClientCertIdentityListFromCertificateList(regular_certs), request,
|
||||
selected_certs);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -56,7 +65,7 @@ TEST_F(ClientCertStoreMacTest, FilterOutThePreferredCert) {
|
||||
scoped_refptr<SSLCertRequestInfo> request(new SSLCertRequestInfo());
|
||||
request->cert_authorities = authority_2;
|
||||
|
||||
std::vector<scoped_refptr<X509Certificate> > selected_certs;
|
||||
ClientCertIdentityList selected_certs;
|
||||
bool rv = SelectClientCertsGivenPreferred(
|
||||
cert_1, certs, *request.get(), &selected_certs);
|
||||
EXPECT_TRUE(rv);
|
||||
@ -77,13 +86,13 @@ TEST_F(ClientCertStoreMacTest, PreferredCertGoesFirst) {
|
||||
certs.push_back(cert_2);
|
||||
scoped_refptr<SSLCertRequestInfo> request(new SSLCertRequestInfo());
|
||||
|
||||
std::vector<scoped_refptr<X509Certificate> > selected_certs;
|
||||
ClientCertIdentityList selected_certs;
|
||||
bool rv = SelectClientCertsGivenPreferred(
|
||||
cert_1, certs, *request.get(), &selected_certs);
|
||||
EXPECT_TRUE(rv);
|
||||
ASSERT_EQ(2u, selected_certs.size());
|
||||
EXPECT_TRUE(selected_certs[0]->Equals(cert_1.get()));
|
||||
EXPECT_TRUE(selected_certs[1]->Equals(cert_2.get()));
|
||||
EXPECT_TRUE(selected_certs[0]->certificate()->Equals(cert_1.get()));
|
||||
EXPECT_TRUE(selected_certs[1]->certificate()->Equals(cert_2.get()));
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "base/bind_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_runner_util.h"
|
||||
#include "base/threading/worker_pool.h"
|
||||
@ -23,10 +24,47 @@
|
||||
#include "net/cert/scoped_nss_types.h"
|
||||
#include "net/cert/x509_util.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_platform_key_nss.h"
|
||||
#include "net/ssl/threaded_ssl_private_key.h"
|
||||
#include "net/third_party/nss/ssl/cmpcert.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
class ClientCertIdentityNSS : public ClientCertIdentity {
|
||||
public:
|
||||
ClientCertIdentityNSS(
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate)
|
||||
: ClientCertIdentity(std::move(cert)),
|
||||
password_delegate_(std::move(password_delegate)) {}
|
||||
~ClientCertIdentityNSS() override = default;
|
||||
|
||||
void AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback) override {
|
||||
if (base::PostTaskAndReplyWithResult(
|
||||
base::WorkerPool::GetTaskRunner(true /* task_is_slow */).get(),
|
||||
FROM_HERE,
|
||||
base::Bind(&FetchClientCertPrivateKey,
|
||||
base::RetainedRef(certificate()),
|
||||
base::RetainedRef(password_delegate_)),
|
||||
private_key_callback)) {
|
||||
return;
|
||||
}
|
||||
// If the task could not be posted, behave as if there was no key.
|
||||
private_key_callback.Run(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ClientCertStoreNSS::ClientCertStoreNSS(
|
||||
const PasswordDelegateFactory& password_delegate_factory)
|
||||
: password_delegate_factory_(password_delegate_factory) {}
|
||||
@ -36,40 +74,37 @@ ClientCertStoreNSS::~ClientCertStoreNSS() {}
|
||||
void ClientCertStoreNSS::GetClientCerts(
|
||||
const SSLCertRequestInfo& request,
|
||||
const ClientCertListCallback& callback) {
|
||||
std::unique_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate;
|
||||
if (!password_delegate_factory_.is_null()) {
|
||||
password_delegate.reset(
|
||||
password_delegate_factory_.Run(request.host_and_port));
|
||||
}
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate;
|
||||
if (!password_delegate_factory_.is_null())
|
||||
password_delegate = password_delegate_factory_.Run(request.host_and_port);
|
||||
if (base::PostTaskAndReplyWithResult(
|
||||
base::WorkerPool::GetTaskRunner(true /* task_is_slow */).get(),
|
||||
FROM_HERE,
|
||||
base::Bind(&ClientCertStoreNSS::GetAndFilterCertsOnWorkerThread,
|
||||
// Caller is responsible for keeping the ClientCertStore
|
||||
// alive until the callback is run.
|
||||
base::Unretained(this), base::Passed(&password_delegate),
|
||||
base::Unretained(this), std::move(password_delegate),
|
||||
&request),
|
||||
callback)) {
|
||||
return;
|
||||
}
|
||||
// If the task could not be posted, behave as if there were no certificates.
|
||||
callback.Run(CertificateList());
|
||||
callback.Run(ClientCertIdentityList());
|
||||
}
|
||||
|
||||
// static
|
||||
void ClientCertStoreNSS::FilterCertsOnWorkerThread(
|
||||
const CertificateList& certs,
|
||||
const SSLCertRequestInfo& request,
|
||||
CertificateList* filtered_certs) {
|
||||
DCHECK(filtered_certs);
|
||||
|
||||
filtered_certs->clear();
|
||||
|
||||
ClientCertIdentityList* identities,
|
||||
const SSLCertRequestInfo& request) {
|
||||
size_t num_raw = 0;
|
||||
for (const auto& cert : certs) {
|
||||
|
||||
auto keep_iter = identities->begin();
|
||||
|
||||
for (auto examine_iter = identities->begin();
|
||||
examine_iter != identities->end(); ++examine_iter) {
|
||||
++num_raw;
|
||||
X509Certificate::OSCertHandle handle = cert->os_cert_handle();
|
||||
X509Certificate::OSCertHandle handle =
|
||||
(*examine_iter)->certificate()->os_cert_handle();
|
||||
|
||||
// Only offer unexpired certificates.
|
||||
if (CERT_CheckCertValidTimes(handle, PR_Now(), PR_TRUE) !=
|
||||
@ -97,34 +132,36 @@ void ClientCertStoreNSS::FilterCertsOnWorkerThread(
|
||||
// Retain a copy of the intermediates. Some deployments expect the client to
|
||||
// supply intermediates out of the local store. See
|
||||
// https://crbug.com/548631.
|
||||
filtered_certs->push_back(
|
||||
X509Certificate::CreateFromHandle(handle, intermediates_raw));
|
||||
// |handle| was successfully parsed by |cert|, so this should never fail.
|
||||
DCHECK(filtered_certs->back());
|
||||
}
|
||||
DVLOG(2) << "num_raw:" << num_raw
|
||||
<< " num_filtered:" << filtered_certs->size();
|
||||
(*examine_iter)->SetIntermediates(intermediates_raw);
|
||||
|
||||
std::sort(filtered_certs->begin(), filtered_certs->end(),
|
||||
x509_util::ClientCertSorter());
|
||||
if (examine_iter == keep_iter)
|
||||
++keep_iter;
|
||||
else
|
||||
*keep_iter++ = std::move(*examine_iter);
|
||||
}
|
||||
identities->erase(keep_iter, identities->end());
|
||||
|
||||
DVLOG(2) << "num_raw:" << num_raw << " num_filtered:" << identities->size();
|
||||
|
||||
std::sort(identities->begin(), identities->end(), ClientCertIdentitySorter());
|
||||
}
|
||||
|
||||
CertificateList ClientCertStoreNSS::GetAndFilterCertsOnWorkerThread(
|
||||
std::unique_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
ClientCertIdentityList ClientCertStoreNSS::GetAndFilterCertsOnWorkerThread(
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate,
|
||||
const SSLCertRequestInfo* request) {
|
||||
CertificateList platform_certs;
|
||||
GetPlatformCertsOnWorkerThread(std::move(password_delegate), &platform_certs);
|
||||
CertificateList selected_certs;
|
||||
FilterCertsOnWorkerThread(platform_certs, *request, &selected_certs);
|
||||
return selected_certs;
|
||||
ClientCertIdentityList selected_identities;
|
||||
GetPlatformCertsOnWorkerThread(std::move(password_delegate),
|
||||
&selected_identities);
|
||||
FilterCertsOnWorkerThread(&selected_identities, *request);
|
||||
return selected_identities;
|
||||
}
|
||||
|
||||
// static
|
||||
void ClientCertStoreNSS::GetPlatformCertsOnWorkerThread(
|
||||
std::unique_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate,
|
||||
net::CertificateList* certs) {
|
||||
ClientCertIdentityList* identities) {
|
||||
CERTCertList* found_certs =
|
||||
CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageSSLClient,
|
||||
PR_FALSE, PR_FALSE, password_delegate.get());
|
||||
@ -140,7 +177,8 @@ void ClientCertStoreNSS::GetPlatformCertsOnWorkerThread(
|
||||
DVLOG(2) << "X509Certificate::CreateFromHandle failed";
|
||||
continue;
|
||||
}
|
||||
certs->push_back(std::move(cert));
|
||||
identities->push_back(
|
||||
base::MakeUnique<ClientCertIdentityNSS>(cert, password_delegate));
|
||||
}
|
||||
CERT_DestroyCertList(found_certs);
|
||||
}
|
||||
|
@ -35,26 +35,24 @@ class NET_EXPORT ClientCertStoreNSS : public ClientCertStore {
|
||||
void GetClientCerts(const SSLCertRequestInfo& cert_request_info,
|
||||
const ClientCertListCallback& callback) override;
|
||||
|
||||
// Examines the certificates in |certs| to find all certificates that match
|
||||
// the client certificate request in |request|, storing the matching
|
||||
// certificates in |filtered_certs|. Any previous content of |filtered_certs|
|
||||
// will be removed.
|
||||
// Examines the certificates in |identities| to find all certificates that
|
||||
// match the client certificate request in |request|, removing any that don't.
|
||||
// The remaining certs will be updated to include intermediates.
|
||||
// Must be called from a worker thread.
|
||||
static void FilterCertsOnWorkerThread(const CertificateList& certs,
|
||||
const SSLCertRequestInfo& request,
|
||||
CertificateList* filtered_certs);
|
||||
static void FilterCertsOnWorkerThread(ClientCertIdentityList* identities,
|
||||
const SSLCertRequestInfo& request);
|
||||
|
||||
// Retrieves all client certificates that are stored by NSS and adds them to
|
||||
// |certs|. |password_delegate| is used to unlock slots if required.
|
||||
// |identities|. |password_delegate| is used to unlock slots if required.
|
||||
// Must be called from a worker thread.
|
||||
static void GetPlatformCertsOnWorkerThread(
|
||||
std::unique_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate,
|
||||
net::CertificateList* certs);
|
||||
ClientCertIdentityList* identities);
|
||||
|
||||
private:
|
||||
CertificateList GetAndFilterCertsOnWorkerThread(
|
||||
std::unique_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
ClientCertIdentityList GetAndFilterCertsOnWorkerThread(
|
||||
scoped_refptr<crypto::CryptoModuleBlockingPasswordDelegate>
|
||||
password_delegate,
|
||||
const SSLCertRequestInfo* request);
|
||||
|
||||
|
@ -12,12 +12,17 @@
|
||||
#include <string>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "crypto/scoped_test_nss_db.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_cert_identity_test_util.h"
|
||||
#include "net/ssl/client_cert_store_unittest-inl.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/ssl/ssl_private_key_test_util.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
@ -25,10 +30,17 @@ namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
void SaveCertsAndQuitCallback(CertificateList* out_certs,
|
||||
base::Closure quit_closure,
|
||||
CertificateList in_certs) {
|
||||
*out_certs = std::move(in_certs);
|
||||
void SaveIdentitiesAndQuitCallback(ClientCertIdentityList* out_identities,
|
||||
base::Closure quit_closure,
|
||||
ClientCertIdentityList in_identities) {
|
||||
*out_identities = std::move(in_identities);
|
||||
quit_closure.Run();
|
||||
}
|
||||
|
||||
void SavePrivateKeyAndQuitCallback(scoped_refptr<net::SSLPrivateKey>* out_key,
|
||||
base::Closure quit_closure,
|
||||
scoped_refptr<net::SSLPrivateKey> in_key) {
|
||||
*out_key = std::move(in_key);
|
||||
quit_closure.Run();
|
||||
}
|
||||
|
||||
@ -40,11 +52,14 @@ class ClientCertStoreNSSTestDelegate {
|
||||
|
||||
bool SelectClientCerts(const CertificateList& input_certs,
|
||||
const SSLCertRequestInfo& cert_request_info,
|
||||
CertificateList* selected_certs) {
|
||||
// Filters |input_certs| using the logic being used to filter the system
|
||||
// store when GetClientCerts() is called.
|
||||
ClientCertStoreNSS::FilterCertsOnWorkerThread(
|
||||
input_certs, cert_request_info, selected_certs);
|
||||
ClientCertIdentityList* selected_identities) {
|
||||
*selected_identities =
|
||||
FakeClientCertIdentityListFromCertificateList(input_certs);
|
||||
|
||||
// Filters |selected_identities| using the logic being used to filter the
|
||||
// system store when GetClientCerts() is called.
|
||||
ClientCertStoreNSS::FilterCertsOnWorkerThread(selected_identities,
|
||||
cert_request_info);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -68,29 +83,50 @@ TEST(ClientCertStoreNSSTest, BuildsCertificateChain) {
|
||||
PK11_ImportCert(test_db.slot(), client_1_ca->os_cert_handle(),
|
||||
CK_INVALID_HANDLE, "client_1_ca",
|
||||
PR_FALSE /* includeTrust (unused) */));
|
||||
std::string pkcs8_key;
|
||||
ASSERT_TRUE(base::ReadFileToString(
|
||||
GetTestCertsDirectory().AppendASCII("client_1.pk8"), &pkcs8_key));
|
||||
|
||||
std::unique_ptr<ClientCertStoreNSS> store(
|
||||
new ClientCertStoreNSS(ClientCertStoreNSS::PasswordDelegateFactory()));
|
||||
|
||||
// All NSS keys are expected to have the same hash preferences.
|
||||
const std::vector<SSLPrivateKey::Hash> expected_hashes = {
|
||||
SSLPrivateKey::Hash::SHA512, SSLPrivateKey::Hash::SHA384,
|
||||
SSLPrivateKey::Hash::SHA256, SSLPrivateKey::Hash::SHA1,
|
||||
};
|
||||
|
||||
{
|
||||
// Request certificates matching B CA, |client_1|'s issuer.
|
||||
scoped_refptr<SSLCertRequestInfo> request(new SSLCertRequestInfo);
|
||||
request->cert_authorities.push_back(std::string(
|
||||
reinterpret_cast<const char*>(kAuthority1DN), sizeof(kAuthority1DN)));
|
||||
|
||||
CertificateList selected_certs;
|
||||
ClientCertIdentityList selected_identities;
|
||||
base::RunLoop loop;
|
||||
store->GetClientCerts(*request.get(),
|
||||
base::Bind(SaveCertsAndQuitCallback, &selected_certs,
|
||||
loop.QuitClosure()));
|
||||
base::Bind(SaveIdentitiesAndQuitCallback,
|
||||
&selected_identities, loop.QuitClosure()));
|
||||
loop.Run();
|
||||
|
||||
// The result be |client_1| with no intermediates.
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
scoped_refptr<X509Certificate> selected_cert = selected_certs[0];
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
scoped_refptr<X509Certificate> selected_cert =
|
||||
selected_identities[0]->certificate();
|
||||
EXPECT_TRUE(X509Certificate::IsSameOSCert(client_1->os_cert_handle(),
|
||||
selected_cert->os_cert_handle()));
|
||||
ASSERT_EQ(0u, selected_cert->GetIntermediateCertificates().size());
|
||||
|
||||
scoped_refptr<SSLPrivateKey> ssl_private_key;
|
||||
base::RunLoop key_loop;
|
||||
selected_identities[0]->AcquirePrivateKey(
|
||||
base::Bind(SavePrivateKeyAndQuitCallback, &ssl_private_key,
|
||||
key_loop.QuitClosure()));
|
||||
key_loop.Run();
|
||||
|
||||
ASSERT_TRUE(ssl_private_key);
|
||||
EXPECT_EQ(expected_hashes, ssl_private_key->GetDigestPreferences());
|
||||
TestSSLPrivateKeyMatches(ssl_private_key.get(), pkcs8_key);
|
||||
}
|
||||
|
||||
{
|
||||
@ -100,23 +136,36 @@ TEST(ClientCertStoreNSSTest, BuildsCertificateChain) {
|
||||
std::string(reinterpret_cast<const char*>(kAuthorityRootDN),
|
||||
sizeof(kAuthorityRootDN)));
|
||||
|
||||
CertificateList selected_certs;
|
||||
ClientCertIdentityList selected_identities;
|
||||
base::RunLoop loop;
|
||||
store->GetClientCerts(*request.get(),
|
||||
base::Bind(SaveCertsAndQuitCallback, &selected_certs,
|
||||
loop.QuitClosure()));
|
||||
base::Bind(SaveIdentitiesAndQuitCallback,
|
||||
&selected_identities, loop.QuitClosure()));
|
||||
loop.Run();
|
||||
|
||||
// The result be |client_1| with |client_1_ca| as an intermediate.
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
scoped_refptr<X509Certificate> selected_cert = selected_certs[0];
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
scoped_refptr<X509Certificate> selected_cert =
|
||||
selected_identities[0]->certificate();
|
||||
EXPECT_TRUE(X509Certificate::IsSameOSCert(client_1->os_cert_handle(),
|
||||
selected_cert->os_cert_handle()));
|
||||
ASSERT_EQ(1u, selected_cert->GetIntermediateCertificates().size());
|
||||
EXPECT_TRUE(X509Certificate::IsSameOSCert(
|
||||
client_1_ca->os_cert_handle(),
|
||||
selected_cert->GetIntermediateCertificates()[0]));
|
||||
|
||||
scoped_refptr<SSLPrivateKey> ssl_private_key;
|
||||
base::RunLoop key_loop;
|
||||
selected_identities[0]->AcquirePrivateKey(
|
||||
base::Bind(SavePrivateKeyAndQuitCallback, &ssl_private_key,
|
||||
key_loop.QuitClosure()));
|
||||
key_loop.Run();
|
||||
ASSERT_TRUE(ssl_private_key);
|
||||
EXPECT_EQ(expected_hashes, ssl_private_key->GetDigestPreferences());
|
||||
TestSSLPrivateKeyMatches(ssl_private_key.get(), pkcs8_key);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mattm): is it possible to unittest slot unlocking?
|
||||
|
||||
} // namespace net
|
||||
|
@ -52,7 +52,7 @@ const unsigned char kAuthorityRootDN[] = {
|
||||
// the platform implementation should implement this method:
|
||||
// bool SelectClientCerts(const CertificateList& input_certs,
|
||||
// const SSLCertRequestInfo& cert_request_info,
|
||||
// CertificateList* selected_certs);
|
||||
// ClientCertIdentityList* selected_identities);
|
||||
template <typename T>
|
||||
class ClientCertStoreTest : public ::testing::Test {
|
||||
public:
|
||||
@ -62,14 +62,14 @@ class ClientCertStoreTest : public ::testing::Test {
|
||||
TYPED_TEST_CASE_P(ClientCertStoreTest);
|
||||
|
||||
TYPED_TEST_P(ClientCertStoreTest, EmptyQuery) {
|
||||
std::vector<scoped_refptr<X509Certificate> > certs;
|
||||
CertificateList certs;
|
||||
scoped_refptr<SSLCertRequestInfo> request(new SSLCertRequestInfo());
|
||||
|
||||
std::vector<scoped_refptr<X509Certificate> > selected_certs;
|
||||
bool rv = this->delegate_.SelectClientCerts(
|
||||
certs, *request.get(), &selected_certs);
|
||||
ClientCertIdentityList selected_identities;
|
||||
bool rv = this->delegate_.SelectClientCerts(certs, *request.get(),
|
||||
&selected_identities);
|
||||
EXPECT_TRUE(rv);
|
||||
EXPECT_EQ(0u, selected_certs.size());
|
||||
EXPECT_EQ(0u, selected_identities.size());
|
||||
}
|
||||
|
||||
// Verify that CertRequestInfo with empty |cert_authorities| matches all
|
||||
@ -83,12 +83,12 @@ TYPED_TEST_P(ClientCertStoreTest, AllIssuersAllowed) {
|
||||
certs.push_back(cert);
|
||||
scoped_refptr<SSLCertRequestInfo> request(new SSLCertRequestInfo());
|
||||
|
||||
std::vector<scoped_refptr<X509Certificate> > selected_certs;
|
||||
bool rv = this->delegate_.SelectClientCerts(
|
||||
certs, *request.get(), &selected_certs);
|
||||
ClientCertIdentityList selected_identities;
|
||||
bool rv = this->delegate_.SelectClientCerts(certs, *request.get(),
|
||||
&selected_identities);
|
||||
EXPECT_TRUE(rv);
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
EXPECT_TRUE(selected_certs[0]->Equals(cert.get()));
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
EXPECT_TRUE(selected_identities[0]->certificate()->Equals(cert.get()));
|
||||
}
|
||||
|
||||
// Verify that certificates are correctly filtered against CertRequestInfo with
|
||||
@ -119,12 +119,12 @@ TYPED_TEST_P(ClientCertStoreTest, DISABLED_CertAuthorityFiltering) {
|
||||
scoped_refptr<SSLCertRequestInfo> request(new SSLCertRequestInfo());
|
||||
request->cert_authorities = authority_1;
|
||||
|
||||
std::vector<scoped_refptr<X509Certificate> > selected_certs;
|
||||
bool rv = this->delegate_.SelectClientCerts(
|
||||
certs, *request.get(), &selected_certs);
|
||||
ClientCertIdentityList selected_identities;
|
||||
bool rv = this->delegate_.SelectClientCerts(certs, *request.get(),
|
||||
&selected_identities);
|
||||
EXPECT_TRUE(rv);
|
||||
ASSERT_EQ(1u, selected_certs.size());
|
||||
EXPECT_TRUE(selected_certs[0]->Equals(cert_1.get()));
|
||||
ASSERT_EQ(1u, selected_identities.size());
|
||||
EXPECT_TRUE(selected_identities[0]->certificate()->Equals(cert_1.get()));
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_CASE_P(ClientCertStoreTest,
|
||||
|
@ -11,15 +11,55 @@
|
||||
#include <windows.h>
|
||||
#include <security.h>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/task_runner_util.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "crypto/wincrypt_shim.h"
|
||||
#include "net/cert/x509_util.h"
|
||||
#include "net/ssl/ssl_platform_key_win.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
class ClientCertIdentityWin : public ClientCertIdentity {
|
||||
public:
|
||||
// Takes ownership of |cert_context|.
|
||||
ClientCertIdentityWin(
|
||||
scoped_refptr<net::X509Certificate> cert,
|
||||
PCCERT_CONTEXT cert_context,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> key_task_runner)
|
||||
: ClientCertIdentity(std::move(cert)),
|
||||
cert_context_(cert_context),
|
||||
key_task_runner_(key_task_runner) {}
|
||||
~ClientCertIdentityWin() override {
|
||||
CertFreeCertificateContext(cert_context_);
|
||||
}
|
||||
|
||||
void AcquirePrivateKey(
|
||||
const base::Callback<void(scoped_refptr<SSLPrivateKey>)>&
|
||||
private_key_callback) override {
|
||||
if (base::PostTaskAndReplyWithResult(
|
||||
key_task_runner_.get(), FROM_HERE,
|
||||
base::Bind(&FetchClientCertPrivateKey,
|
||||
base::Unretained(certificate()), cert_context_),
|
||||
private_key_callback)) {
|
||||
return;
|
||||
}
|
||||
// If the task could not be posted, behave as if there was no key.
|
||||
private_key_callback.Run(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
PCCERT_CONTEXT cert_context_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> key_task_runner_;
|
||||
};
|
||||
|
||||
// Callback required by Windows API function CertFindChainInStore(). In addition
|
||||
// to filtering by extended/enhanced key usage, we do not show expired
|
||||
// certificates and require digital signature usage in the key usage extension.
|
||||
@ -65,8 +105,11 @@ static BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context,
|
||||
|
||||
void GetClientCertsImpl(HCERTSTORE cert_store,
|
||||
const SSLCertRequestInfo& request,
|
||||
CertificateList* selected_certs) {
|
||||
selected_certs->clear();
|
||||
ClientCertIdentityList* selected_identities) {
|
||||
selected_identities->clear();
|
||||
|
||||
scoped_refptr<base::SingleThreadTaskRunner> current_thread =
|
||||
base::ThreadTaskRunnerHandle::Get();
|
||||
|
||||
const size_t auth_count = request.cert_authorities.size();
|
||||
std::vector<CERT_NAME_BLOB> issuers(auth_count);
|
||||
@ -149,15 +192,19 @@ void GetClientCertsImpl(HCERTSTORE cert_store,
|
||||
// pair<X509Certificate, SSLPrivateKeyCallback>.
|
||||
scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
|
||||
cert_context2, intermediates);
|
||||
if (cert)
|
||||
selected_certs->push_back(std::move(cert));
|
||||
CertFreeCertificateContext(cert_context2);
|
||||
if (cert) {
|
||||
selected_identities->push_back(base::MakeUnique<ClientCertIdentityWin>(
|
||||
std::move(cert),
|
||||
cert_context2, // Takes ownership of |cert_context2|.
|
||||
current_thread)); // The key must be acquired on the same thread, as
|
||||
// the PCCERT_CONTEXT may not be thread safe.
|
||||
}
|
||||
for (size_t i = 0; i < intermediates.size(); ++i)
|
||||
CertFreeCertificateContext(intermediates[i]);
|
||||
}
|
||||
|
||||
std::sort(selected_certs->begin(), selected_certs->end(),
|
||||
x509_util::ClientCertSorter());
|
||||
std::sort(selected_identities->begin(), selected_identities->end(),
|
||||
ClientCertIdentitySorter());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -174,13 +221,13 @@ ClientCertStoreWin::~ClientCertStoreWin() {}
|
||||
void ClientCertStoreWin::GetClientCerts(
|
||||
const SSLCertRequestInfo& request,
|
||||
const ClientCertListCallback& callback) {
|
||||
CertificateList selected_certs;
|
||||
ClientCertIdentityList selected_identities;
|
||||
if (cert_store_) {
|
||||
// Use the existing client cert store. Note: Under some situations,
|
||||
// it's possible for this to return certificates that aren't usable
|
||||
// (see below).
|
||||
GetClientCertsImpl(cert_store_, request, &selected_certs);
|
||||
callback.Run(std::move(selected_certs));
|
||||
GetClientCertsImpl(cert_store_, request, &selected_identities);
|
||||
callback.Run(std::move(selected_identities));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -191,18 +238,18 @@ void ClientCertStoreWin::GetClientCerts(
|
||||
ScopedHCERTSTORE my_cert_store(CertOpenSystemStore(NULL, L"MY"));
|
||||
if (!my_cert_store) {
|
||||
PLOG(ERROR) << "Could not open the \"MY\" system certificate store: ";
|
||||
callback.Run(CertificateList());
|
||||
callback.Run(ClientCertIdentityList());
|
||||
return;
|
||||
}
|
||||
|
||||
GetClientCertsImpl(my_cert_store, request, &selected_certs);
|
||||
callback.Run(std::move(selected_certs));
|
||||
GetClientCertsImpl(my_cert_store, request, &selected_identities);
|
||||
callback.Run(std::move(selected_identities));
|
||||
}
|
||||
|
||||
bool ClientCertStoreWin::SelectClientCertsForTesting(
|
||||
const CertificateList& input_certs,
|
||||
const SSLCertRequestInfo& request,
|
||||
CertificateList* selected_certs) {
|
||||
ClientCertIdentityList* selected_identities) {
|
||||
ScopedHCERTSTORE test_store(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0,
|
||||
NULL));
|
||||
if (!test_store)
|
||||
@ -232,7 +279,7 @@ bool ClientCertStoreWin::SelectClientCertsForTesting(
|
||||
return false;
|
||||
}
|
||||
|
||||
GetClientCertsImpl(test_store.get(), request, selected_certs);
|
||||
GetClientCertsImpl(test_store.get(), request, selected_identities);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ class NET_EXPORT ClientCertStoreWin : public ClientCertStore {
|
||||
// using the common logic.
|
||||
bool SelectClientCertsForTesting(const CertificateList& input_certs,
|
||||
const SSLCertRequestInfo& cert_request_info,
|
||||
CertificateList* selected_certs);
|
||||
ClientCertIdentityList* selected_identities);
|
||||
|
||||
ScopedHCERTSTORE cert_store_;
|
||||
|
||||
|
@ -12,7 +12,7 @@ class ClientCertStoreWinTestDelegate {
|
||||
public:
|
||||
bool SelectClientCerts(const CertificateList& input_certs,
|
||||
const SSLCertRequestInfo& cert_request_info,
|
||||
CertificateList* selected_certs) {
|
||||
ClientCertIdentityList* selected_certs) {
|
||||
return store_.SelectClientCertsForTesting(
|
||||
input_certs, cert_request_info, selected_certs);
|
||||
}
|
||||
|
@ -19,6 +19,10 @@ namespace net {
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
|
||||
// TODO(mattm): This is now used only by
|
||||
// chrome/browser/chromeos/net/client_cert_store_chromeos.cc. Move it to
|
||||
// chrome/browser/chromeos/net, or just have client_cert_store_chromeos.cc
|
||||
// directly call whatever.
|
||||
// TODO(rsleevi, davidben): Remove this once https://crbug.com/394131 is fixed.
|
||||
// A certificate and key store that allows several external certificate
|
||||
// providers to expose certificates and keys through this store. All currently
|
||||
|
@ -1,98 +0,0 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/ssl/openssl_client_key_store.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/memory/singleton.h"
|
||||
#include "net/cert/asn1_util.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "third_party/boringssl/src/include/openssl/evp.h"
|
||||
#include "third_party/boringssl/src/include/openssl/mem.h"
|
||||
#include "third_party/boringssl/src/include/openssl/x509.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
// Serializes the SubjectPublicKeyInfo for |cert|.
|
||||
bool GetCertificateSPKI(const X509Certificate* cert, std::string* spki) {
|
||||
#if BUILDFLAG(USE_BYTE_CERTS)
|
||||
base::StringPiece cert_der(
|
||||
reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert->os_cert_handle())),
|
||||
CRYPTO_BUFFER_len(cert->os_cert_handle()));
|
||||
base::StringPiece spki_tmp;
|
||||
if (!asn1::ExtractSPKIFromDERCert(cert_der, &spki_tmp))
|
||||
return false;
|
||||
spki_tmp.CopyToString(spki);
|
||||
return true;
|
||||
#else
|
||||
bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(cert->os_cert_handle()));
|
||||
if (!pkey) {
|
||||
LOG(ERROR) << "Can't extract private key from certificate!";
|
||||
return false;
|
||||
}
|
||||
|
||||
bssl::ScopedCBB cbb;
|
||||
uint8_t* der;
|
||||
size_t der_len;
|
||||
if (!CBB_init(cbb.get(), 0) ||
|
||||
!EVP_marshal_public_key(cbb.get(), pkey.get()) ||
|
||||
!CBB_finish(cbb.get(), &der, &der_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
spki->assign(reinterpret_cast<char*>(der),
|
||||
reinterpret_cast<char*>(der) + der_len);
|
||||
OPENSSL_free(der);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OpenSSLClientKeyStore* OpenSSLClientKeyStore::GetInstance() {
|
||||
return base::Singleton<OpenSSLClientKeyStore>::get();
|
||||
}
|
||||
|
||||
bool OpenSSLClientKeyStore::RecordClientCertPrivateKey(
|
||||
const X509Certificate* client_cert,
|
||||
scoped_refptr<SSLPrivateKey> private_key) {
|
||||
DCHECK(client_cert);
|
||||
DCHECK(private_key);
|
||||
|
||||
std::string spki;
|
||||
if (!GetCertificateSPKI(client_cert, &spki))
|
||||
return false;
|
||||
|
||||
key_map_[spki] = std::move(private_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
scoped_refptr<SSLPrivateKey> OpenSSLClientKeyStore::FetchClientCertPrivateKey(
|
||||
const X509Certificate* client_cert) {
|
||||
DCHECK(client_cert);
|
||||
|
||||
std::string spki;
|
||||
if (!GetCertificateSPKI(client_cert, &spki))
|
||||
return nullptr;
|
||||
|
||||
auto iter = key_map_.find(spki);
|
||||
if (iter == key_map_.end())
|
||||
return nullptr;
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void OpenSSLClientKeyStore::Flush() {
|
||||
key_map_.clear();
|
||||
}
|
||||
|
||||
OpenSSLClientKeyStore::OpenSSLClientKeyStore() {}
|
||||
|
||||
OpenSSLClientKeyStore::~OpenSSLClientKeyStore() {}
|
||||
|
||||
} // namespace net
|
@ -1,71 +0,0 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef NET_SSL_OPENSSL_CLIENT_KEY_STORE_H_
|
||||
#define NET_SSL_OPENSSL_CLIENT_KEY_STORE_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "net/base/net_export.h"
|
||||
#include "third_party/boringssl/src/include/openssl/base.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
|
||||
// OpenSSLClientKeyStore implements an in-memory store for client
|
||||
// certificate private keys, because the platforms where OpenSSL is
|
||||
// used do not provide a way to retrieve the private key of a known
|
||||
// certificate.
|
||||
//
|
||||
// This class is not thread-safe and should only be used from the network
|
||||
// thread.
|
||||
class NET_EXPORT OpenSSLClientKeyStore {
|
||||
public:
|
||||
// Platforms must define this factory function as appropriate.
|
||||
static OpenSSLClientKeyStore* GetInstance();
|
||||
|
||||
// Record the association between a certificate and its
|
||||
// private key. This method should be called _before_
|
||||
// FetchClientCertPrivateKey to ensure that the private key is returned
|
||||
// when it is called later. The association is recorded in memory
|
||||
// exclusively.
|
||||
// |cert| is a handle to a certificate object.
|
||||
// |private_key| is an SSLPrivateKey that corresponds to the certificate's
|
||||
// private key.
|
||||
// Returns false if an error occured.
|
||||
bool RecordClientCertPrivateKey(const X509Certificate* cert,
|
||||
scoped_refptr<SSLPrivateKey> key);
|
||||
|
||||
// Given a certificate's |public_key|, return the corresponding private
|
||||
// key that has been recorded previously by RecordClientCertPrivateKey().
|
||||
// |cert| is a client certificate.
|
||||
// Returns its matching private key on success, NULL otherwise.
|
||||
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* cert);
|
||||
|
||||
// Flush all recorded keys.
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
OpenSSLClientKeyStore();
|
||||
~OpenSSLClientKeyStore();
|
||||
|
||||
// Maps from the serialized SubjectPublicKeyInfo structure to the
|
||||
// corresponding private key.
|
||||
std::map<std::string, scoped_refptr<net::SSLPrivateKey>> key_map_;
|
||||
|
||||
friend struct base::DefaultSingletonTraits<OpenSSLClientKeyStore>;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OpenSSLClientKeyStore);
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_SSL_OPENSSL_CLIENT_KEY_STORE_H_
|
@ -1,161 +0,0 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/ssl/openssl_client_key_store.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "net/test/test_data_directory.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
// A common test class to ensure that the store is flushed after
|
||||
// each test.
|
||||
class OpenSSLClientKeyStoreTest : public ::testing::Test {
|
||||
public:
|
||||
OpenSSLClientKeyStoreTest()
|
||||
: store_(OpenSSLClientKeyStore::GetInstance()) {
|
||||
}
|
||||
|
||||
~OpenSSLClientKeyStoreTest() override {
|
||||
if (store_)
|
||||
store_->Flush();
|
||||
}
|
||||
|
||||
protected:
|
||||
OpenSSLClientKeyStore* store_;
|
||||
};
|
||||
|
||||
class MockSSLPrivateKey : public SSLPrivateKey {
|
||||
public:
|
||||
MockSSLPrivateKey() : on_destroyed_(nullptr) {}
|
||||
|
||||
void set_on_destroyed(bool* on_destroyed) { on_destroyed_ = on_destroyed; }
|
||||
|
||||
std::vector<Hash> GetDigestPreferences() override {
|
||||
NOTREACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
void SignDigest(Hash hash,
|
||||
const base::StringPiece& input,
|
||||
const SignCallback& callback) override {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
private:
|
||||
~MockSSLPrivateKey() override {
|
||||
if (on_destroyed_)
|
||||
*on_destroyed_ = true;
|
||||
}
|
||||
|
||||
bool* on_destroyed_;
|
||||
};
|
||||
|
||||
// Check that GetInstance() returns non-null
|
||||
TEST_F(OpenSSLClientKeyStoreTest, GetInstance) {
|
||||
ASSERT_TRUE(store_);
|
||||
}
|
||||
|
||||
// Check that Flush() works correctly.
|
||||
TEST_F(OpenSSLClientKeyStoreTest, Flush) {
|
||||
ASSERT_TRUE(store_);
|
||||
|
||||
scoped_refptr<X509Certificate> cert_1(
|
||||
ImportCertFromFile(GetTestCertsDirectory(), "client_1.pem"));
|
||||
ASSERT_TRUE(cert_1);
|
||||
|
||||
EXPECT_TRUE(store_->RecordClientCertPrivateKey(
|
||||
cert_1.get(), make_scoped_refptr(new MockSSLPrivateKey)));
|
||||
|
||||
store_->Flush();
|
||||
|
||||
// Retrieve the private key. This should fail because the store
|
||||
// was flushed.
|
||||
EXPECT_FALSE(store_->FetchClientCertPrivateKey(cert_1.get()));
|
||||
}
|
||||
|
||||
// Check that trying to retrieve the private key of an unknown certificate
|
||||
// simply fails by returning null.
|
||||
TEST_F(OpenSSLClientKeyStoreTest, FetchEmptyPrivateKey) {
|
||||
ASSERT_TRUE(store_);
|
||||
|
||||
scoped_refptr<X509Certificate> cert_1(
|
||||
ImportCertFromFile(GetTestCertsDirectory(), "client_1.pem"));
|
||||
ASSERT_TRUE(cert_1);
|
||||
|
||||
// Retrieve the private key now. This should fail because it was
|
||||
// never recorded in the store.
|
||||
EXPECT_FALSE(store_->FetchClientCertPrivateKey(cert_1.get()));
|
||||
}
|
||||
|
||||
// Check that any private key recorded through RecordClientCertPrivateKey
|
||||
// can be retrieved with FetchClientCertPrivateKey.
|
||||
TEST_F(OpenSSLClientKeyStoreTest, RecordAndFetchPrivateKey) {
|
||||
ASSERT_TRUE(store_);
|
||||
|
||||
// Any certificate / key pair will do, the store is not supposed to
|
||||
// check that the private and certificate public keys match. This is
|
||||
// by design since the private EVP_PKEY could be a wrapper around a
|
||||
// JNI reference, with no way to access the real private key bits.
|
||||
scoped_refptr<X509Certificate> cert_1(
|
||||
ImportCertFromFile(GetTestCertsDirectory(), "client_1.pem"));
|
||||
ASSERT_TRUE(cert_1);
|
||||
|
||||
bool on_destroyed = false;
|
||||
scoped_refptr<MockSSLPrivateKey> priv_key(new MockSSLPrivateKey);
|
||||
priv_key->set_on_destroyed(&on_destroyed);
|
||||
|
||||
// Add a key twice.
|
||||
EXPECT_TRUE(store_->RecordClientCertPrivateKey(cert_1.get(), priv_key));
|
||||
EXPECT_TRUE(store_->RecordClientCertPrivateKey(cert_1.get(), priv_key));
|
||||
|
||||
// Retrieve the private key.
|
||||
scoped_refptr<SSLPrivateKey> pkey2 =
|
||||
store_->FetchClientCertPrivateKey(cert_1.get());
|
||||
EXPECT_EQ(pkey2.get(), priv_key.get());
|
||||
|
||||
// Flush the key store and release all references. At this point, the private
|
||||
// key should be cleanly destroyed.
|
||||
store_->Flush();
|
||||
priv_key = nullptr;
|
||||
pkey2 = nullptr;
|
||||
EXPECT_TRUE(on_destroyed);
|
||||
}
|
||||
|
||||
// Same test, but with two certificates / private keys.
|
||||
TEST_F(OpenSSLClientKeyStoreTest, RecordAndFetchTwoPrivateKeys) {
|
||||
scoped_refptr<X509Certificate> cert_1(
|
||||
ImportCertFromFile(GetTestCertsDirectory(), "client_1.pem"));
|
||||
ASSERT_TRUE(cert_1);
|
||||
|
||||
scoped_refptr<X509Certificate> cert_2(
|
||||
ImportCertFromFile(GetTestCertsDirectory(), "client_2.pem"));
|
||||
ASSERT_TRUE(cert_2);
|
||||
|
||||
scoped_refptr<SSLPrivateKey> priv_key1(new MockSSLPrivateKey);
|
||||
scoped_refptr<SSLPrivateKey> priv_key2(new MockSSLPrivateKey);
|
||||
|
||||
EXPECT_TRUE(store_->RecordClientCertPrivateKey(cert_1.get(), priv_key1));
|
||||
EXPECT_TRUE(store_->RecordClientCertPrivateKey(cert_2.get(), priv_key2));
|
||||
|
||||
scoped_refptr<SSLPrivateKey> fetch_key1 =
|
||||
store_->FetchClientCertPrivateKey(cert_1.get());
|
||||
scoped_refptr<SSLPrivateKey> fetch_key2 =
|
||||
store_->FetchClientCertPrivateKey(cert_2.get());
|
||||
|
||||
EXPECT_TRUE(fetch_key1);
|
||||
EXPECT_TRUE(fetch_key2);
|
||||
|
||||
EXPECT_EQ(fetch_key1.get(), priv_key1.get());
|
||||
EXPECT_EQ(fetch_key2.get(), priv_key2.get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace net
|
@ -33,9 +33,10 @@ bool SSLClientAuthCache::Lookup(const HostPortPair& server,
|
||||
}
|
||||
|
||||
void SSLClientAuthCache::Add(const HostPortPair& server,
|
||||
X509Certificate* certificate,
|
||||
SSLPrivateKey* private_key) {
|
||||
cache_[server] = std::make_pair(certificate, private_key);
|
||||
scoped_refptr<X509Certificate> certificate,
|
||||
scoped_refptr<SSLPrivateKey> private_key) {
|
||||
cache_[server] =
|
||||
std::make_pair(std::move(certificate), std::move(private_key));
|
||||
|
||||
// TODO(wtc): enforce a maximum number of entries.
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ class NET_EXPORT_PRIVATE SSLClientAuthCache : public CertDatabase::Observer {
|
||||
// overwritten. A NULL |client_cert| indicates a preference that no client
|
||||
// certificate should be sent to |server|.
|
||||
void Add(const HostPortPair& server,
|
||||
X509Certificate* client_cert,
|
||||
SSLPrivateKey* private_key);
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> private_key);
|
||||
|
||||
// Remove the client certificate for |server| from the cache, if one exists.
|
||||
void Remove(const HostPortPair& server);
|
||||
|
@ -1,26 +0,0 @@
|
||||
// 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 NET_SSL_SSL_PLATFORM_KEY_H_
|
||||
#define NET_SSL_SSL_PLATFORM_KEY_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "net/base/net_export.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
|
||||
// Returns an SSLPrivateKey backed by the platform private key that corresponds
|
||||
// to |certificate|'s public key. If |keychain| is nullptr, the process's
|
||||
// default search list is used instead.
|
||||
NET_EXPORT scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* certificate);
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_SSL_SSL_PLATFORM_KEY_H_
|
@ -19,8 +19,6 @@
|
||||
#include "net/android/keystore.h"
|
||||
#include "net/android/legacy_openssl.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/ssl/openssl_client_key_store.h"
|
||||
#include "net/ssl/ssl_platform_key.h"
|
||||
#include "net/ssl/ssl_platform_key_util.h"
|
||||
#include "net/ssl/threaded_ssl_private_key.h"
|
||||
#include "third_party/boringssl/src/include/openssl/ecdsa.h"
|
||||
@ -219,10 +217,4 @@ scoped_refptr<SSLPrivateKey> WrapJavaPrivateKey(
|
||||
GetSSLPlatformKeyTaskRunner()));
|
||||
}
|
||||
|
||||
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* certificate) {
|
||||
return OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
|
||||
certificate);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
@ -1,123 +0,0 @@
|
||||
// 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 <keyhi.h>
|
||||
#include <pk11pub.h>
|
||||
#include <prerror.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "crypto/scoped_nss_types.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_key_store.h"
|
||||
#include "net/ssl/ssl_platform_key.h"
|
||||
#include "net/ssl/ssl_platform_key_util.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/ssl/threaded_ssl_private_key.h"
|
||||
#include "third_party/boringssl/src/include/openssl/mem.h"
|
||||
#include "third_party/boringssl/src/include/openssl/nid.h"
|
||||
#include "third_party/boringssl/src/include/openssl/rsa.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
void LogPRError() {
|
||||
PRErrorCode err = PR_GetError();
|
||||
const char* err_name = PR_ErrorToName(err);
|
||||
if (err_name == nullptr)
|
||||
err_name = "";
|
||||
LOG(ERROR) << "Could not sign digest: " << err << " (" << err_name << ")";
|
||||
}
|
||||
|
||||
class SSLPlatformKeyChromecast : public ThreadedSSLPrivateKey::Delegate {
|
||||
public:
|
||||
SSLPlatformKeyChromecast(crypto::ScopedSECKEYPrivateKey key)
|
||||
: key_(std::move(key)) {}
|
||||
~SSLPlatformKeyChromecast() override {}
|
||||
|
||||
std::vector<SSLPrivateKey::Hash> GetDigestPreferences() override {
|
||||
return std::vector<SSLPrivateKey::Hash>{SSLPrivateKey::Hash::SHA256,
|
||||
SSLPrivateKey::Hash::SHA1};
|
||||
}
|
||||
|
||||
Error SignDigest(SSLPrivateKey::Hash hash,
|
||||
const base::StringPiece& input,
|
||||
std::vector<uint8_t>* signature) override {
|
||||
SECItem digest_item;
|
||||
digest_item.data =
|
||||
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(input.data()));
|
||||
digest_item.len = input.size();
|
||||
|
||||
bssl::UniquePtr<uint8_t> free_digest_info;
|
||||
// PK11_Sign expects the caller to prepend the DigestInfo.
|
||||
int hash_nid = NID_undef;
|
||||
switch (hash) {
|
||||
case SSLPrivateKey::Hash::MD5_SHA1:
|
||||
hash_nid = NID_md5_sha1;
|
||||
break;
|
||||
case SSLPrivateKey::Hash::SHA1:
|
||||
hash_nid = NID_sha1;
|
||||
break;
|
||||
case SSLPrivateKey::Hash::SHA256:
|
||||
hash_nid = NID_sha256;
|
||||
break;
|
||||
default:
|
||||
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
||||
}
|
||||
DCHECK_NE(NID_undef, hash_nid);
|
||||
int is_alloced;
|
||||
size_t prefix_len;
|
||||
if (!RSA_add_pkcs1_prefix(&digest_item.data, &prefix_len, &is_alloced,
|
||||
hash_nid, digest_item.data, digest_item.len)) {
|
||||
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
||||
}
|
||||
digest_item.len = prefix_len;
|
||||
if (is_alloced)
|
||||
free_digest_info.reset(digest_item.data);
|
||||
|
||||
int len = PK11_SignatureLen(key_.get());
|
||||
if (len <= 0) {
|
||||
LogPRError();
|
||||
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
||||
}
|
||||
signature->resize(len);
|
||||
SECItem signature_item;
|
||||
signature_item.data = signature->data();
|
||||
signature_item.len = signature->size();
|
||||
|
||||
SECStatus rv = PK11_Sign(key_.get(), &signature_item, &digest_item);
|
||||
if (rv != SECSuccess) {
|
||||
LogPRError();
|
||||
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
||||
}
|
||||
signature->resize(signature_item.len);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
private:
|
||||
crypto::ScopedSECKEYPrivateKey key_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyChromecast);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* certificate) {
|
||||
crypto::ScopedSECKEYPrivateKey key(
|
||||
PK11_FindKeyByAnyCert(certificate->os_cert_handle(), nullptr));
|
||||
if (!key) {
|
||||
return ClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
|
||||
*certificate);
|
||||
}
|
||||
|
||||
return make_scoped_refptr(new ThreadedSSLPrivateKey(
|
||||
base::MakeUnique<SSLPlatformKeyChromecast>(std::move(key)),
|
||||
GetSSLPlatformKeyTaskRunner()));
|
||||
}
|
||||
|
||||
} // namespace net
|
@ -1,52 +0,0 @@
|
||||
// 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 "net/ssl/ssl_platform_key.h"
|
||||
|
||||
#include <pk11pub.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "crypto/scoped_test_nss_db.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/ssl/ssl_private_key_test_util.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "net/test/test_data_directory.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
TEST(SSLPlatformKeyChromecastTest, KeyMatches) {
|
||||
std::string pkcs8;
|
||||
base::FilePath pkcs8_path =
|
||||
GetTestCertsDirectory().AppendASCII("client_1.pk8");
|
||||
ASSERT_TRUE(base::ReadFileToString(pkcs8_path, &pkcs8));
|
||||
|
||||
// Import the key into a test NSS database.
|
||||
crypto::ScopedTestNSSDB test_db;
|
||||
scoped_refptr<X509Certificate> cert = ImportClientCertAndKeyFromFile(
|
||||
GetTestCertsDirectory(), "client_1.pem", "client_1.pk8", test_db.slot());
|
||||
ASSERT_TRUE(cert);
|
||||
|
||||
// Look up the key.
|
||||
scoped_refptr<SSLPrivateKey> key = FetchClientCertPrivateKey(cert.get());
|
||||
ASSERT_TRUE(key);
|
||||
|
||||
// Only support SHA-256 and SHA-1.
|
||||
std::vector<SSLPrivateKey::Hash> expected_hashes = {
|
||||
SSLPrivateKey::Hash::SHA256, SSLPrivateKey::Hash::SHA1,
|
||||
};
|
||||
EXPECT_EQ(expected_hashes, key->GetDigestPreferences());
|
||||
|
||||
TestSSLPrivateKeyMatches(key.get(), pkcs8);
|
||||
}
|
||||
|
||||
} // namespace net
|
@ -31,7 +31,6 @@
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/cert/x509_util_mac.h"
|
||||
#include "net/ssl/ssl_platform_key.h"
|
||||
#include "net/ssl/ssl_platform_key_util.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/ssl/threaded_ssl_private_key.h"
|
||||
@ -78,37 +77,6 @@ class ScopedCSSM_CC_HANDLE {
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedCSSM_CC_HANDLE);
|
||||
};
|
||||
|
||||
// Looks up the private key for |certificate| in |keychain| and returns
|
||||
// a SecKeyRef or nullptr on failure. The caller takes ownership of the
|
||||
// result.
|
||||
SecKeyRef FetchSecKeyRefForCertificate(const X509Certificate* certificate,
|
||||
SecKeychainRef keychain) {
|
||||
OSStatus status;
|
||||
base::ScopedCFTypeRef<SecIdentityRef> identity;
|
||||
{
|
||||
base::ScopedCFTypeRef<SecCertificateRef> os_cert(
|
||||
x509_util::CreateSecCertificateFromX509Certificate(certificate));
|
||||
if (!os_cert)
|
||||
return nullptr;
|
||||
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
|
||||
status = SecIdentityCreateWithCertificate(keychain, os_cert.get(),
|
||||
identity.InitializeInto());
|
||||
}
|
||||
if (status != noErr) {
|
||||
OSSTATUS_LOG(WARNING, status);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
base::ScopedCFTypeRef<SecKeyRef> private_key;
|
||||
status = SecIdentityCopyPrivateKey(identity, private_key.InitializeInto());
|
||||
if (status != noErr) {
|
||||
OSSTATUS_LOG(WARNING, status);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return private_key.release();
|
||||
}
|
||||
|
||||
// These symbols were added in the 10.12 SDK, but we currently use an older SDK,
|
||||
// so look them up with dlsym.
|
||||
//
|
||||
@ -376,17 +344,9 @@ class SSLPlatformKeySecKey : public ThreadedSSLPrivateKey::Delegate {
|
||||
DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeySecKey);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKeyFromKeychain(
|
||||
scoped_refptr<SSLPrivateKey> CreateSSLPrivateKeyForSecKey(
|
||||
const X509Certificate* certificate,
|
||||
SecKeychainRef keychain) {
|
||||
// Look up the private key.
|
||||
base::ScopedCFTypeRef<SecKeyRef> private_key(
|
||||
FetchSecKeyRefForCertificate(certificate, keychain));
|
||||
if (!private_key)
|
||||
return nullptr;
|
||||
|
||||
SecKeyRef private_key) {
|
||||
int key_type;
|
||||
size_t max_length;
|
||||
if (!GetClientCertInfo(certificate, &key_type, &max_length))
|
||||
@ -395,26 +355,37 @@ scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKeyFromKeychain(
|
||||
if (base::mac::IsAtLeastOS10_12()) {
|
||||
return make_scoped_refptr(
|
||||
new ThreadedSSLPrivateKey(base::MakeUnique<SSLPlatformKeySecKey>(
|
||||
key_type, max_length, private_key.get()),
|
||||
key_type, max_length, private_key),
|
||||
GetSSLPlatformKeyTaskRunner()));
|
||||
}
|
||||
|
||||
const CSSM_KEY* cssm_key;
|
||||
OSStatus status = SecKeyGetCSSMKey(private_key.get(), &cssm_key);
|
||||
OSStatus status = SecKeyGetCSSMKey(private_key, &cssm_key);
|
||||
if (status != noErr) {
|
||||
OSSTATUS_LOG(WARNING, status);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return make_scoped_refptr(new ThreadedSSLPrivateKey(
|
||||
base::MakeUnique<SSLPlatformKeyCSSM>(key_type, max_length,
|
||||
private_key.get(), cssm_key),
|
||||
base::MakeUnique<SSLPlatformKeyCSSM>(key_type, max_length, private_key,
|
||||
cssm_key),
|
||||
GetSSLPlatformKeyTaskRunner()));
|
||||
}
|
||||
|
||||
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* certificate) {
|
||||
return FetchClientCertPrivateKeyFromKeychain(certificate, nullptr);
|
||||
} // namespace
|
||||
|
||||
scoped_refptr<SSLPrivateKey> CreateSSLPrivateKeyForSecIdentity(
|
||||
const X509Certificate* certificate,
|
||||
SecIdentityRef identity) {
|
||||
base::ScopedCFTypeRef<SecKeyRef> private_key;
|
||||
OSStatus status =
|
||||
SecIdentityCopyPrivateKey(identity, private_key.InitializeInto());
|
||||
if (status != noErr) {
|
||||
OSSTATUS_LOG(WARNING, status);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateSSLPrivateKeyForSecKey(certificate, private_key.get());
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop // "-Wdeprecated-declarations"
|
||||
|
@ -15,12 +15,11 @@ namespace net {
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
|
||||
// Returns an SSLPrivateKey backed by the platform private key in |keychain|
|
||||
// that corresponds to |certificate|'s public key. If |keychain| is nullptr, the
|
||||
// user's default search list is used instead.
|
||||
NET_EXPORT_PRIVATE scoped_refptr<SSLPrivateKey>
|
||||
FetchClientCertPrivateKeyFromKeychain(const X509Certificate* certificate,
|
||||
SecKeychainRef keychain);
|
||||
// Returns an SSLPrivateKey backed by the platform private key in |identity|
|
||||
// which must correspond to |certificate|'s public key.
|
||||
NET_EXPORT scoped_refptr<SSLPrivateKey> CreateSSLPrivateKeyForSecIdentity(
|
||||
const X509Certificate* certificate,
|
||||
SecIdentityRef identity);
|
||||
|
||||
} // namespace net
|
||||
|
||||
|
@ -69,7 +69,7 @@ TEST_P(SSLPlatformKeyMacTest, KeyMatches) {
|
||||
|
||||
// Finally, test the code to look up the key.
|
||||
scoped_refptr<SSLPrivateKey> key =
|
||||
FetchClientCertPrivateKeyFromKeychain(cert.get(), keychain);
|
||||
CreateSSLPrivateKeyForSecIdentity(cert.get(), sec_identity.get());
|
||||
ASSERT_TRUE(key);
|
||||
|
||||
// All Mac keys are expected to have the same hash preferences.
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/ssl/ssl_platform_key_nss.h"
|
||||
|
||||
#include <cert.h>
|
||||
#include <keyhi.h>
|
||||
#include <pk11pub.h>
|
||||
@ -12,10 +14,9 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "crypto/nss_crypto_module_delegate.h"
|
||||
#include "crypto/scoped_nss_types.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/client_key_store.h"
|
||||
#include "net/ssl/ssl_platform_key.h"
|
||||
#include "net/ssl/ssl_platform_key_util.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/ssl/threaded_ssl_private_key.h"
|
||||
@ -153,13 +154,13 @@ class SSLPlatformKeyNSS : public ThreadedSSLPrivateKey::Delegate {
|
||||
} // namespace
|
||||
|
||||
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* certificate) {
|
||||
const X509Certificate* certificate,
|
||||
crypto::CryptoModuleBlockingPasswordDelegate* password_delegate) {
|
||||
void* wincx = password_delegate ? password_delegate->wincx() : nullptr;
|
||||
crypto::ScopedSECKEYPrivateKey key(
|
||||
PK11_FindKeyByAnyCert(certificate->os_cert_handle(), nullptr));
|
||||
if (!key) {
|
||||
return ClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
|
||||
*certificate);
|
||||
}
|
||||
PK11_FindKeyByAnyCert(certificate->os_cert_handle(), wincx));
|
||||
if (!key)
|
||||
return nullptr;
|
||||
|
||||
int type;
|
||||
size_t max_length;
|
||||
|
31
net/ssl/ssl_platform_key_nss.h
Normal file
31
net/ssl/ssl_platform_key_nss.h
Normal file
@ -0,0 +1,31 @@
|
||||
// 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 NET_SSL_SSL_PLATFORM_KEY_NSS_H_
|
||||
#define NET_SSL_SSL_PLATFORM_KEY_NSS_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "net/base/net_export.h"
|
||||
|
||||
namespace crypto {
|
||||
class CryptoModuleBlockingPasswordDelegate;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
|
||||
// Returns an SSLPrivateKey backed by the NSS private key that corresponds to
|
||||
// |certificate|'s public key. If |password_delegate| is non-null, it will be
|
||||
// used to prompt for a password if necessary to unlock a slot.
|
||||
NET_EXPORT scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* certificate,
|
||||
crypto::CryptoModuleBlockingPasswordDelegate* password_delegate);
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_SSL_SSL_PLATFORM_KEY_NSS_H_
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/ssl/ssl_platform_key.h"
|
||||
#include "net/ssl/ssl_platform_key_nss.h"
|
||||
|
||||
#include <keyhi.h>
|
||||
#include <pk11pub.h>
|
||||
@ -121,7 +121,8 @@ TEST_P(SSLPlatformKeyNSSTest, KeyMatches) {
|
||||
}
|
||||
|
||||
// Look up the key.
|
||||
scoped_refptr<SSLPrivateKey> key = FetchClientCertPrivateKey(cert.get());
|
||||
scoped_refptr<SSLPrivateKey> key =
|
||||
FetchClientCertPrivateKey(cert.get(), nullptr);
|
||||
ASSERT_TRUE(key);
|
||||
|
||||
// All NSS keys are expected to have the same hash preferences.
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "crypto/scoped_capi_types.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/ssl/ssl_platform_key.h"
|
||||
#include "net/ssl/ssl_platform_key_util.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "net/ssl/threaded_ssl_private_key.h"
|
||||
@ -261,9 +260,8 @@ scoped_refptr<SSLPrivateKey> WrapCNGPrivateKey(
|
||||
}
|
||||
|
||||
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* certificate) {
|
||||
PCCERT_CONTEXT cert_context = certificate->os_cert_handle();
|
||||
|
||||
const X509Certificate* certificate,
|
||||
PCCERT_CONTEXT cert_context) {
|
||||
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE prov_or_key = 0;
|
||||
DWORD key_spec = 0;
|
||||
BOOL must_free = FALSE;
|
||||
|
@ -19,6 +19,12 @@ namespace net {
|
||||
class SSLPrivateKey;
|
||||
class X509Certificate;
|
||||
|
||||
// Returns an SSLPrivateKey backed by the platform private key for
|
||||
// |cert_context| which must correspond to |certificate|.
|
||||
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
|
||||
const X509Certificate* certificate,
|
||||
PCCERT_CONTEXT cert_context);
|
||||
|
||||
// Returns an SSLPrivateKey backed by |prov| and |key_spec|, which must
|
||||
// correspond to |certificate|'s public key. Takes ownership of |prov|.
|
||||
NET_EXPORT_PRIVATE scoped_refptr<SSLPrivateKey> WrapCAPIPrivateKey(
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "crypto/rsa_private_key.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/ssl/ssl_platform_key_util.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
@ -108,4 +109,11 @@ scoped_refptr<SSLPrivateKey> WrapOpenSSLPrivateKey(
|
||||
GetSSLPlatformKeyTaskRunner()));
|
||||
}
|
||||
|
||||
scoped_refptr<SSLPrivateKey> WrapRSAPrivateKey(
|
||||
crypto::RSAPrivateKey* rsa_private_key) {
|
||||
EVP_PKEY_up_ref(rsa_private_key->key());
|
||||
return net::WrapOpenSSLPrivateKey(
|
||||
bssl::UniquePtr<EVP_PKEY>(rsa_private_key->key()));
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
@ -9,6 +9,10 @@
|
||||
#include "net/base/net_export.h"
|
||||
#include "third_party/boringssl/src/include/openssl/base.h"
|
||||
|
||||
namespace crypto {
|
||||
class RSAPrivateKey;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
|
||||
class SSLPrivateKey;
|
||||
@ -17,6 +21,8 @@ class SSLPrivateKey;
|
||||
// nullptr on error.
|
||||
NET_EXPORT scoped_refptr<SSLPrivateKey> WrapOpenSSLPrivateKey(
|
||||
bssl::UniquePtr<EVP_PKEY> key);
|
||||
NET_EXPORT scoped_refptr<SSLPrivateKey> WrapRSAPrivateKey(
|
||||
crypto::RSAPrivateKey* rsa_private_key);
|
||||
|
||||
} // namespace net
|
||||
|
||||
|
@ -128,8 +128,8 @@ int URLRequestMockDataJob::ReadRawData(IOBuffer* buf, int buf_size) {
|
||||
}
|
||||
|
||||
void URLRequestMockDataJob::ContinueWithCertificate(
|
||||
X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key) {
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key) {
|
||||
DCHECK(request_client_certificate_);
|
||||
NotifyHeadersComplete();
|
||||
}
|
||||
|
@ -30,8 +30,9 @@ class URLRequestMockDataJob : public URLRequestJob {
|
||||
void Start() override;
|
||||
int ReadRawData(IOBuffer* buf, int buf_size) override;
|
||||
void GetResponseInfo(HttpResponseInfo* info) override;
|
||||
void ContinueWithCertificate(X509Certificate* client_cert,
|
||||
SSLPrivateKey* client_private_key) override;
|
||||
void ContinueWithCertificate(
|
||||
scoped_refptr<X509Certificate> client_cert,
|
||||
scoped_refptr<SSLPrivateKey> client_private_key) override;
|
||||
|
||||
// Adds the testing URLs to the URLRequestFilter.
|
||||
static void AddUrlHandler();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user