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

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: a few more nits 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/callback_helpers.h"
7 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/ref_counted.h"
12 #include "chrome/browser/ui/android/ssl_client_certificate_request.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/android/keystore_openssl.h"
15 #include "net/base/openssl_client_key_store.h"
16 #include "net/base/ssl_cert_request_info.h"
17 #include "net/base/x509_certificate.h"
18
19 // On other platforms, the list of client certificates compatible with
20 // the SSLCertRequestInfo is built using system APIs that do not require
21 // user interaction. After this, ShowSSLClientCertificateSelector() is
22 // merely used to display a tab sub-window asking the user to select
23 // one of these certificates.
24
25 // On Android, things are a bit different, because getting the list of
26 // compatible client certificates is only possible using an API that shows
27 // a system UI dialog. More precisely:
28 //
29 // - The application must call KeyChain.choosePrivateKeyAlias() and
30 // pass it the request parameters directly.
31 //
32 // - This API always launches a system activity (CertInstaller), that
33 // will display a list of compatible installed client certificates,
34 // if any, or prompt the user to install one manually otherwise.
35 //
36 // - Also, the first time this API is called, the CertInstaller will
37 // first prompt the user to enter the secure storage's password
38 // (which is the user's PIN code / password by default). This establishes
39 // a trust relationship between the KeyChain system application, and
40 // the application calling the API. It persists until the application
41 // is killed.
42 //
43 // - The client certificate selection result is sent back to the
44 // application through a UI thread callback. It only contains a
45 // string alias for the selected certificate, or 'null' to indicate
46 // that the user has canceled the selection, or couldn't unlock
47 // access to the secure storage.
48 //
8 49
9 namespace chrome { 50 namespace chrome {
10 51
11 // Client Auth is not implemented on Android yet. 52 using browser::android::SSLClientCertificateRequest;
53
54 namespace {
55
56 typedef net::OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY;
57
58 // Helper class used to guarantee that a callback.Run(NULL) is called on
59 // scope exit, unless Reset() was used before.
60 class CallbackGuard {
Ryan Sleevi 2013/03/04 23:47:21 We already have this, in src/base/bind_helpers.h
digit1 2013/03/05 16:54:00 I'm not sure I didn't already mentioned already, b
Ryan Sleevi 2013/03/05 18:02:41 Can you paste the error? I really want to avoid cr
digit1 2013/03/06 01:48:33 Ok, the error was due to the NULL inside the base:
61 public:
62 explicit CallbackGuard(chrome::SelectCertificateCallback& callback)
Ryan Sleevi 2013/03/04 23:47:21 I've mentioned it in past reviews, but in Google C
digit1 2013/03/05 16:54:00 Sorry about that, I've fixed it.
63 : callback_(&callback) {
64 }
65
66 ~CallbackGuard() {
67 if (callback_)
68 base::ResetAndReturn(callback_).Run(NULL);
69 }
70
71 void Reset() {
72 callback_ = NULL;
73 }
74
75 private:
76 chrome::SelectCertificateCallback* callback_;
77
78 DISALLOW_COPY_AND_ASSIGN(CallbackGuard);
79 };
80
81 // Helper class used to pass a (certificate,private key) pair between
82 // threads.
83 class CertificateAndKey
84 : public base::RefCountedThreadSafe<CertificateAndKey> {
Ryan Sleevi 2013/03/04 23:47:21 1) We have RefCountedData<T> for this 2) You shoul
digit1 2013/03/05 16:54:00 1) It looks like RefCountedData<> would be appropr
85 public:
86 CertificateAndKey() { }
87
88 scoped_refptr<net::X509Certificate> client_cert;
89 ScopedEVP_PKEY private_key;
90 private:
91 friend class base::RefCountedThreadSafe<CertificateAndKey>;
92
93 ~CertificateAndKey() { }
94
95 DISALLOW_COPY_AND_ASSIGN(CertificateAndKey);
96 };
97
98 // A SSLClientCertificateRequest used to pass the client
99 // certificate to the proper callback, after recording its private
100 // key into the net::OpenSSLClientKeyStore.
101 class Request : public SSLClientCertificateRequest {
Ryan Sleevi 2013/03/04 23:47:21 I'm not sure why you've got the inheritance route.
digit1 2013/03/05 16:54:00 This change reflect's Marcus' recommendation. He m
Ryan Sleevi 2013/03/05 18:02:41 Can you not just pass a base::Closure/Callback in
102 public:
103 explicit Request(const chrome::SelectCertificateCallback& callback)
104 : callback_(callback) {
105 }
106
107 virtual void OnCompletion(
108 std::vector<base::StringPiece>* encoded_chain,
109 jobject private_key) OVERRIDE;
110
111 protected:
112 ~Request() { }
113
114 private:
115 Request();
116
117 // Called on the I/O thread to record a (certificate,private_key) pair
118 // into the net::OpenSSLClientKeyStore.
119 static void DoRecordClientCertificateKey(
120 scoped_refptr<CertificateAndKey> pair);
Ryan Sleevi 2013/03/04 23:47:21 I've pointed out in past reviews, only scoped_ptr<
digit1 2013/03/05 16:54:00 The above line comes from the fact that a scoped_p
Ryan Sleevi 2013/03/05 18:02:41 I think you missed what I was saying: scoped_refpt
121
122 // Called on the UI thread, as a reply to DoRecordClientCertificateKey(),
123 // to send the selected certificate to the callback.
124 void DoSendCertificate(scoped_refptr<net::X509Certificate> client_cert);
125
126 chrome::SelectCertificateCallback callback_;
127 };
128
129 // Called on the UI thread once the user has selected a client certificate
130 // using the system-provided dialog, or failed to select one.
131 // |encoded_chain| is a pointer to the encoded client certificate chain.
132 // Each item in the list is a DER-encoded X.509 certificate.
133 // |private_key| is a pointer to a JNI local reference to the platform
134 // PrivateKey object for this client certificate chain.
135 void Request::OnCompletion(std::vector<base::StringPiece>* encoded_chain,
136 jobject private_key_ref) {
137 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
138
139 // Ensure callback_.Run(NULL) is called on error exit.
140 CallbackGuard guard(callback_);
141
142 if (encoded_chain == NULL || private_key_ref == NULL) {
143 LOG(ERROR) << "Client certificate selection cancelled by the user";
144 return;
145 }
146
147 scoped_refptr<CertificateAndKey> pair(new CertificateAndKey);
148
149 // Create the X509Certificate object from the encoded chain.
150 pair->client_cert =
151 net::X509Certificate::CreateFromDERCertChain(*encoded_chain);
152 if (!pair->client_cert.get()) {
153 LOG(ERROR) << "Could not decode client certificate chain";
154 return;
155 }
156
157 // Create an EVP_PKEY wrapper for the private key JNI reference.
158 pair->private_key.reset(
159 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref));
160 if (!pair->private_key.get()) {
161 LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
162 return;
163 }
164
165 guard.Reset();
166
167 // DoRecordClientCertificateKey() must be called on the I/O thread,
168 // before DoSendCertificate() can be called on the UI thread to pass
169 // the client certificate to the callback.
170 content::BrowserThread::PostTaskAndReply(
171 content::BrowserThread::IO,
172 FROM_HERE,
173 base::Bind(&Request::DoRecordClientCertificateKey, pair),
174 base::Bind(&Request::DoSendCertificate, this, pair->client_cert));
Ryan Sleevi 2013/03/04 23:47:21 Why have a DoSendCertificate helper? Why not simp
digit1 2013/03/05 16:54:00 That's an excellent suggestion, I didn't think abo
175 }
176
177 void Request::DoRecordClientCertificateKey(
178 scoped_refptr<CertificateAndKey> pair) {
179 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
180 net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
181 pair->client_cert.get(), pair->private_key.get());
182 }
183
184 void Request::DoSendCertificate(
185 scoped_refptr<net::X509Certificate> client_cert) {
186 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
187 base::ResetAndReturn(&callback_).Run(client_cert);
188 }
189
190 } // namespace
191
12 void ShowSSLClientCertificateSelector( 192 void ShowSSLClientCertificateSelector(
13 content::WebContents* contents, 193 content::WebContents* contents,
14 const net::HttpNetworkSession* network_session, 194 const net::HttpNetworkSession* network_session,
15 net::SSLCertRequestInfo* cert_request_info, 195 net::SSLCertRequestInfo* cert_request_info,
16 const base::Callback<void(net::X509Certificate*)>& callback) { 196 const chrome::SelectCertificateCallback& callback) {
17 NOTIMPLEMENTED(); 197 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
198 scoped_refptr<Request> request(new Request(callback));
Ryan Sleevi 2013/03/04 23:47:21 Not sure why the RefCounting
digit1 2013/03/05 16:54:00 The refcounting is used to ensure that the request
199 if (!request->Start(cert_request_info)) {
200 LOG(ERROR) << "Could not start client certificate request!";
201 callback.Run(NULL);
202 }
18 } 203 }
19 204
20 } // namespace chrome 205 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698