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

Side by Side Diff: chrome/browser/ui/android/ssl_client_certificate_request.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: 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
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/android/ssl_client_certificate_request.h"
6
7 #include <openssl/evp.h>
8 #include <openssl/x509.h>
9
10 #include "base/android/jni_array.h"
11 #include "base/android/jni_string.h"
12 #include "base/android/scoped_java_ref.h"
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/logging.h"
16 #include "base/memory/ref_counted.h"
17 #include "chrome/browser/ssl/ssl_client_auth_observer.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "crypto/openssl_util.h"
20 #include "jni/SSLClientCertificateRequest_jni.h"
21 #include "net/android/keystore_openssl.h"
22 #include "net/base/host_port_pair.h"
23 #include "net/base/openssl_client_key_store.h"
24 #include "net/base/ssl_cert_request_info.h"
25 #include "net/base/ssl_client_cert_type.h"
26 #include "net/base/x509_certificate.h"
27
28 namespace browser {
29 namespace android {
30
31 using content::BrowserThread;
32
33 namespace {
34
35 typedef net::OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY;
36
37 } // namespace
38
39 SSLClientCertificateRequest::SSLClientCertificateRequest(
40 net::SSLCertRequestInfo* cert_request_info,
41 const chrome::SelectCertificateCallback& callback)
42 : cert_request_info_(cert_request_info),
43 client_cert_(NULL),
44 private_key_(NULL),
45 callback_(callback) {
46 }
47
48 SSLClientCertificateRequest::~SSLClientCertificateRequest() {
49 if (!callback_.is_null()) {
Ryan Sleevi 2013/02/28 19:42:42 Under what situations is this possible? I underst
digit1 2013/03/04 19:03:20 This can happen when an error exit happened in OnR
50 callback_.Run(NULL);
51 callback_.Reset();
52 }
53 }
54
55 // Start a new client certificate request. This launches a system
56 // UI dialog to let the user choose a certificate matching the
57 // SSLCertRequestInfo.
58 // |request_info| is the SSL client certificate request info.
59 // Returns true on success, or false otherwise.
60 // Note that success simply means that the system activity that
61 // implements client certificate selection has been launched. The only
62 // way to know if the user has properly selected a certificate (versus
63 // no certificate being available, or the user cancelling the operation)
64 // is to look at the value sent back in nativeOnRequestCompletion()
65 // below.
66 bool SSLClientCertificateRequest::Start() {
67 JNIEnv* env = base::android::AttachCurrentThread();
68 net::SSLCertRequestInfo* request_info = cert_request_info_;
69
70 // Convert the object's address into a pointer that will be passed
71 // to the Java method through JNI.
72 jint this_java = reinterpret_cast<jint>(this);
73
74 // Build the |key_types| JNI parameter, as a String[]
75 std::vector<std::string> key_types;
76 for (size_t n = 0; n < request_info->cert_key_types.size(); ++n) {
77 switch (request_info->cert_key_types[n]) {
78 case net::CLIENT_CERT_RSA_SIGN:
79 key_types.push_back("RSA");
80 break;
81 case net::CLIENT_CERT_DSS_SIGN:
82 key_types.push_back("DSA");
83 break;
84 case net::CLIENT_CERT_ECDSA_SIGN:
85 key_types.push_back("ECDSA");
86 break;
87 default:
88 // Ignore unknown types.
89 break;
90 }
91 }
92 ScopedJavaLocalRef<jobjectArray> key_types_ref =
93 base::android::ToJavaArrayOfStrings(env, key_types);
94 if (key_types_ref.is_null()) {
95 LOG(ERROR) << "Could not create key types array (String[])";
96 return false;
97 }
98
99 // Build the |encoded_principals| JNI parameter, as a byte[][]
100 ScopedJavaLocalRef<jobjectArray> principals_ref =
101 base::android::ToJavaArrayOfByteArray(
102 env, request_info->cert_authorities);
103 if (principals_ref.is_null()) {
104 LOG(ERROR) << "Could not create principals array (byte[][])";
105 return false;
106 }
107
108 // Build the |host_name| and |port| JNI parameters, as a String and
109 // a jint.
110 net::HostPortPair host_and_port =
111 net::HostPortPair::FromString(request_info->host_and_port);
112
113 ScopedJavaLocalRef<jstring> host_name_ref =
114 base::android::ConvertUTF8ToJavaString(env, host_and_port.host());
115 if (host_name_ref.is_null()) {
116 LOG(ERROR) << "Could not extract host name from: '"
117 << request_info->host_and_port << "'";
118 return false;
119 }
120
121 jint port = host_and_port.port();
122 if (port <= 0 || port > 65535) {
123 LOG(ERROR) << "Invalid port number in: "
124 << request_info->host_and_port << "'";
125 return false;
126 }
127
128 // Increment reference count to ensure request object is not deleted
129 // before OnRequestCompletion is called.
130 this->AddRef();
131
132 return Java_SSLClientCertificateRequest_selectClientCertificate(
133 env, this_java, key_types_ref.obj(), principals_ref.obj(),
134 host_name_ref.obj(), port);
135 }
136
137 void SSLClientCertificateRequest::OnRequestCompletion(
138 JNIEnv* env,
139 jobject obj,
140 jstring private_key_alias_ref,
141 jobjectArray encoded_chain_ref,
142 jobject private_key_ref) {
143
144 // Ensure that the request object is destroyed when the
145 // function leaves on error. Note that the destructor calls
146 // callback_.Run(NULL) automatically in this case.
147 scoped_refptr<SSLClientCertificateRequest> guard(this);
148
149 // Undo the AddRef() from Start().
150 this->Release();
151
152 // When the request is cancelled by the user.
153 if (private_key_alias_ref == NULL || private_key_ref == NULL) {
154 LOG(ERROR) << "Client certificate request cancelled";
155 return;
156 }
157
158 // Convert private key alias JNI reference to string.
159 std::string private_key_alias;
160 if (private_key_alias_ref) {
161 private_key_alias = base::android::ConvertJavaStringToUTF8(
162 env, private_key_alias_ref);
163 }
164
165 // Convert the encoded chain to a vector of strings.
166 std::vector<std::string> encoded_chain_strings;
167 if (encoded_chain_ref) {
168 base::android::JavaArrayOfByteArrayToStringVector(
169 env, encoded_chain_ref, &encoded_chain_strings);
170 }
171
172 std::vector<base::StringPiece> encoded_chain;
173 for (size_t n = 0; n < encoded_chain_strings.size(); ++n)
174 encoded_chain.push_back(encoded_chain_strings[n]);
175
176 // Create the X509Certificate object from the encoded chain.
177 client_cert_ =
178 net::X509Certificate::CreateFromDERCertChain(encoded_chain);
179 if (!client_cert_.get()) {
180 LOG(ERROR) << "Could not decode client certificate chain";
181 return;
182 }
183
184 // Create an EVP_PKEY wrapper for the private key JNI reference.
185 private_key_.reset(
186 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref));
187 if (!private_key_.get()) {
188 LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
189 return;
190 }
191
192 // The next step must happen in the I/O thread.
193 // This transfers ownership of the request object to the closure.
194 BrowserThread::PostTask(
195 BrowserThread::IO,
196 FROM_HERE,
197 base::Bind(
198 &SSLClientCertificateRequest::DoRecordClientCertificateKey,
199 this));
Ryan Sleevi 2013/02/28 19:42:42 Because this object is RefCounted, you're forcing
digit1 2013/03/04 19:03:20 Indeed, the object doesn't have to live in multipl
200 }
201
202 // Must be called on the I/O thread to add the client certificate's
203 // private key to the OpenSSLClientKeyStore.
204 // |request| is the target request object.
205 // |cert| is the client certificate.
206 // |private_key| is the client certificate's private key.
207 void SSLClientCertificateRequest::DoRecordClientCertificateKey() {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
209
210 net::OpenSSLClientKeyStore* key_store =
211 net::OpenSSLClientKeyStore::GetInstance();
212 if (!key_store->RecordClientCertPrivateKey(client_cert_.get(),
213 private_key_.get())) {
214 LOG(ERROR) << "Could not add key to OpenSSL private key store";
215 client_cert_ = NULL;
216 }
217
218 // Request ownership is transfered to new closure.
219 BrowserThread::PostTask(
220 BrowserThread::UI,
221 FROM_HERE,
222 base::Bind(
223 &SSLClientCertificateRequest::DoSendClientCertificate, this));
224 }
225
226
227 void SSLClientCertificateRequest::DoSendClientCertificate() {
228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
229 callback_.Run(client_cert_.get());
230 callback_.Reset();
231
232 // Request object will be destroyed when this method exits.
233 }
234
235 } // namespace android
236 } // namespace browser
237
238 bool RegisterSSLClientCertificateRequestAndroid(JNIEnv* env) {
239 return browser::android::RegisterNativesImpl(env);
240 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698