Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(424)

Side by Side Diff: chrome/browser/ui/android/ssl_client_certificate_selector.cc

Issue 12374020: Add Android support for SSL client authentication to the browser layer. (Closed) Base URL: http://git.chromium.org/chromium/src.git@client-cert-test
Patch Set: optimizations Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ssl/ssl_client_certificate_selector.h" 5 #include "chrome/browser/ssl/ssl_client_certificate_selector.h"
6 6
7 #include <openssl/evp.h>
8
9 #include "base/bind_helpers.h"
10 #include "base/callback_helpers.h"
7 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "chrome/browser/ui/android/ssl_client_certificate_request.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "net/android/keystore_openssl.h"
16 #include "net/base/openssl_client_key_store.h"
17 #include "net/base/ssl_cert_request_info.h"
18 #include "net/base/x509_certificate.h"
19
20 // On other platforms, the list of client certificates compatible with
21 // the SSLCertRequestInfo is built using system APIs that do not require
22 // user interaction. After this, ShowSSLClientCertificateSelector() is
23 // merely used to display a tab sub-window asking the user to select
24 // one of these certificates.
25
26 // On Android, things are a bit different, because getting the list of
27 // compatible client certificates is only possible using an API that shows
28 // a system UI dialog. More precisely:
29 //
30 // - The application must call KeyChain.choosePrivateKeyAlias() and
31 // pass it the request parameters directly.
32 //
33 // - This API always launches a system activity (CertInstaller), that
34 // will display a list of compatible installed client certificates,
35 // if any, or prompt the user to install one manually otherwise.
36 //
37 // - Also, the first time this API is called, the CertInstaller will
38 // first prompt the user to enter the secure storage's password
39 // (which is the user's PIN code / password by default). This establishes
40 // a trust relationship between the KeyChain system application, and
41 // the application calling the API. It persists until the application
42 // is killed.
43 //
44 // - The client certificate selection result is sent back to the
45 // application through a UI thread callback. It only contains a
46 // string alias for the selected certificate, or 'null' to indicate
47 // that the user has canceled the selection, or couldn't unlock
48 // access to the secure storage.
49 //
8 50
9 namespace chrome { 51 namespace chrome {
10 52
11 // Client Auth is not implemented on Android yet. 53 using chrome::android::SSLClientCertificateRequest;
54
55 namespace {
56
57 typedef net::OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY;
58
59 // Helper class used to guarantee that a callback.Run(NULL) is called on
60 // scope exit, unless Release() was used before.
61 class CallbackGuard {
62 public:
63 explicit CallbackGuard(chrome::SelectCertificateCallback* callback)
64 : callback_(callback) {
65 }
66
67 ~CallbackGuard() {
68 if (callback_)
69 base::ResetAndReturn(callback_).Run(NULL);
70 }
71
72 void Release() {
73 callback_ = NULL;
74 }
75
76 private:
77 chrome::SelectCertificateCallback* callback_;
78
79 DISALLOW_COPY_AND_ASSIGN(CallbackGuard);
80 };
81
82 // Helper class used to pass a (certificate,private key) pair between
83 // threads.
84 class CertificateAndKey {
Ryan Sleevi 2013/03/05 18:02:41 Why is this a class and not a struct
digit1 2013/03/06 01:48:33 It used to require to be a class when if was refco
85 public:
86 CertificateAndKey() { }
87 ~CertificateAndKey() { }
Ryan Sleevi 2013/03/05 18:02:41 no spaces in braces
88 scoped_refptr<net::X509Certificate> client_cert;
89 ScopedEVP_PKEY private_key;
Ryan Sleevi 2013/03/05 18:02:41 It's still not clear to me why you need this class
digit1 2013/03/06 01:48:33 I've originally tried passing a ScopedEVP_PKEY dir
Ryan Sleevi 2013/03/06 01:55:29 You have to use base::Passed() here, which will cr
digit1 2013/03/06 21:34:11 thanks, this works and has been implemented in the
90 private:
91 DISALLOW_COPY_AND_ASSIGN(CertificateAndKey);
92 };
93
94 // A SSLClientCertificateRequest used to pass the client
95 // certificate to the proper callback, after recording its private
96 // key into the net::OpenSSLClientKeyStore.
97 class Request : public SSLClientCertificateRequest {
98 public:
99 explicit Request(const chrome::SelectCertificateCallback& callback)
100 : callback_(callback) {
101 }
102
103 virtual void OnCertificateSelected(
104 std::vector<base::StringPiece>* encoded_chain,
105 jobject private_key) OVERRIDE;
106
107 protected:
108 ~Request() { }
109
110 private:
111 Request();
112
113 // Called on the I/O thread to record a (certificate,private_key) pair
114 // into the net::OpenSSLClientKeyStore.
115 static void DoRecordClientCertificateKey(
116 scoped_ptr<CertificateAndKey> pair);
117
118 chrome::SelectCertificateCallback callback_;
119 };
120
121 // Called on the UI thread once the user has selected a client certificate
122 // using the system-provided dialog, or failed to select one.
123 // |encoded_chain| is a pointer to the encoded client certificate chain.
124 // Each item in the list is a DER-encoded X.509 certificate.
125 // |private_key| is a pointer to a JNI local reference to the platform
126 // PrivateKey object for this client certificate chain.
127 void Request::OnCertificateSelected(
128 std::vector<base::StringPiece>* encoded_chain,
129 jobject private_key_ref) {
130 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
131
132 // Ensure callback_.Run(NULL) is called on error exit.
133 CallbackGuard guard(&callback_);
134
135 if (encoded_chain == NULL || private_key_ref == NULL) {
136 LOG(ERROR) << "Client certificate selection cancelled by the user";
137 return;
138 }
139
140 scoped_ptr<CertificateAndKey> pair(new CertificateAndKey);
141
142 // Create the X509Certificate object from the encoded chain.
143 pair->client_cert =
144 net::X509Certificate::CreateFromDERCertChain(*encoded_chain);
145 if (!pair->client_cert.get()) {
146 LOG(ERROR) << "Could not decode client certificate chain";
147 return;
148 }
149
150 // Create an EVP_PKEY wrapper for the private key JNI reference.
151 pair->private_key.reset(
152 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref));
153 if (!pair->private_key.get()) {
154 LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
155 return;
156 }
157
158 guard.Release();
159
160 // DoRecordClientCertificateKey() must be called on the I/O thread,
161 // before the callback is called with the selected certificate on
162 // the UI thread.
163 //
164 // Note that the UI thread closure is computed first, because |pair|
165 // becomes NULL after the base::Passed() call, and the order of
166 // parameter evaluation is unspecified in C++.
167 base::Closure on_ui_thread =
168 base::Bind(callback_, pair->client_cert);
169
170 content::BrowserThread::PostTaskAndReply(
171 content::BrowserThread::IO,
172 FROM_HERE,
173 base::Bind(&Request::DoRecordClientCertificateKey,
174 base::Passed(&pair)),
175 on_ui_thread);
176 }
177
178 void Request::DoRecordClientCertificateKey(
179 scoped_ptr<CertificateAndKey> pair) {
180 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
181 net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
182 pair->client_cert.get(), pair->private_key.get());
183 }
184
185 } // namespace
186
12 void ShowSSLClientCertificateSelector( 187 void ShowSSLClientCertificateSelector(
13 content::WebContents* contents, 188 content::WebContents* contents,
14 const net::HttpNetworkSession* network_session, 189 const net::HttpNetworkSession* network_session,
15 net::SSLCertRequestInfo* cert_request_info, 190 net::SSLCertRequestInfo* cert_request_info,
16 const base::Callback<void(net::X509Certificate*)>& callback) { 191 const chrome::SelectCertificateCallback& callback) {
17 NOTIMPLEMENTED(); 192 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
193 scoped_refptr<Request> request(new Request(callback));
194 if (!request->Start(cert_request_info)) {
195 LOG(ERROR) << "Could not start client certificate request!";
196 callback.Run(NULL);
Ryan Sleevi 2013/03/05 18:02:41 DESIGN: In general, we avoid recursing directly in
digit1 2013/03/06 01:48:33 This has been addressed in the last patch, thanks.
197 }
18 } 198 }
19 199
20 } // namespace chrome 200 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698