OLD | NEW |
---|---|
(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 "base/android/jni_array.h" | |
8 #include "base/android/jni_string.h" | |
9 #include "base/android/scoped_java_ref.h" | |
10 #include "base/bind.h" | |
11 #include "base/callback_helpers.h" | |
12 #include "base/logging.h" | |
13 #include "chrome/browser/ssl/ssl_client_certificate_selector.h" | |
14 #include "content/public/browser/browser_thread.h" | |
15 #include "jni/SSLClientCertificateRequest_jni.h" | |
16 #include "net/android/keystore_openssl.h" | |
17 #include "net/base/host_port_pair.h" | |
18 #include "net/base/openssl_client_key_store.h" | |
19 #include "net/base/ssl_cert_request_info.h" | |
20 #include "net/base/ssl_client_cert_type.h" | |
21 #include "net/base/x509_certificate.h" | |
22 | |
23 namespace chrome { | |
24 | |
25 namespace { | |
26 | |
27 typedef net::OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY; | |
28 | |
29 // Must be called on the I/O thread to record a client certificate | |
30 // and its private key in the OpenSSLClientKeyStore. | |
31 void RecordClientCertificateKey( | |
32 scoped_refptr<net::X509Certificate> client_cert, | |
Ryan Sleevi
2013/03/06 21:50:43
I mentioned in previous reviews: Do not pass scope
digit1
2013/03/06 23:00:08
Ah I misunderstood what you meant. For the record,
| |
33 ScopedEVP_PKEY private_key) { | |
34 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
35 net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey( | |
36 client_cert.get(), private_key.get()); | |
37 } | |
38 | |
39 void StartClientCertificateRequest( | |
40 const net::SSLCertRequestInfo* cert_request_info, | |
41 const chrome::SelectCertificateCallback& callback) { | |
42 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
43 JNIEnv* env = base::android::AttachCurrentThread(); | |
Ryan Sleevi
2013/03/06 21:50:43
Should this be moved to line 73 - it seems unused
digit1
2013/03/06 23:00:08
Not sure what you mean, it's used at least three t
| |
44 | |
45 // Ensure that callback(NULL) is posted as a task on the UI thread | |
46 // in case of error exit. | |
Ryan Sleevi
2013/03/06 21:50:43
// in case of an error.
(I find "error exit" less
digit1
2013/03/06 23:00:08
Done.
| |
47 base::Closure post_task_closure = base::Bind( | |
48 base::IgnoreResult(&content::BrowserThread::PostTask), | |
49 content::BrowserThread::UI, | |
50 FROM_HERE, | |
51 base::Bind(callback, scoped_refptr<net::X509Certificate>())); | |
52 | |
53 base::ScopedClosureRunner guard(post_task_closure); | |
54 | |
55 // Build the |key_types| JNI parameter, as a String[] | |
56 std::vector<std::string> key_types; | |
57 for (size_t n = 0; n < cert_request_info->cert_key_types.size(); ++n) { | |
58 switch (cert_request_info->cert_key_types[n]) { | |
59 case net::CLIENT_CERT_RSA_SIGN: | |
60 key_types.push_back("RSA"); | |
61 break; | |
62 case net::CLIENT_CERT_DSS_SIGN: | |
63 key_types.push_back("DSA"); | |
64 break; | |
65 case net::CLIENT_CERT_ECDSA_SIGN: | |
66 key_types.push_back("ECDSA"); | |
67 break; | |
68 default: | |
69 // Ignore unknown types. | |
70 break; | |
71 } | |
72 } | |
73 ScopedJavaLocalRef<jobjectArray> key_types_ref = | |
74 base::android::ToJavaArrayOfStrings(env, key_types); | |
75 if (key_types_ref.is_null()) { | |
76 LOG(ERROR) << "Could not create key types array (String[])"; | |
77 return; | |
78 } | |
79 | |
80 // Build the |encoded_principals| JNI parameter, as a byte[][] | |
81 ScopedJavaLocalRef<jobjectArray> principals_ref = | |
82 base::android::ToJavaArrayOfByteArray( | |
83 env, cert_request_info->cert_authorities); | |
84 if (principals_ref.is_null()) { | |
85 LOG(ERROR) << "Could not create principals array (byte[][])"; | |
86 return; | |
87 } | |
88 | |
89 // Build the |host_name| and |port| JNI parameters, as a String and | |
90 // a jint. | |
91 net::HostPortPair host_and_port = | |
92 net::HostPortPair::FromString(cert_request_info->host_and_port); | |
93 | |
94 ScopedJavaLocalRef<jstring> host_name_ref = | |
95 base::android::ConvertUTF8ToJavaString(env, host_and_port.host()); | |
96 if (host_name_ref.is_null()) { | |
97 LOG(ERROR) << "Could not extract host name from: '" | |
98 << cert_request_info->host_and_port << "'"; | |
99 return; | |
100 } | |
101 | |
102 jint port = host_and_port.port(); | |
103 if (port <= 0 || port > 65535) { | |
104 LOG(ERROR) << "Invalid port number in: " | |
105 << cert_request_info->host_and_port << "'"; | |
106 return; | |
107 } | |
Ryan Sleevi
2013/03/06 21:50:43
This port number check seems superflous - it's not
digit1
2013/03/06 23:00:08
Removed it.
| |
108 | |
109 // Create a new req object and convert its address | |
Ryan Sleevi
2013/03/06 21:50:43
"req object" is not defined, contextually or exter
digit1
2013/03/06 23:00:08
Done.
| |
110 // into a pointer that will be passed to the Java method through JNI. | |
111 scoped_ptr<chrome::SelectCertificateCallback> request( | |
112 new chrome::SelectCertificateCallback(callback)); | |
113 | |
114 jint request_id = reinterpret_cast<jint>(request.get()); | |
115 | |
116 if (!chrome::android:: | |
117 Java_SSLClientCertificateRequest_selectClientCertificate( | |
118 env, request_id, key_types_ref.obj(), principals_ref.obj(), | |
119 host_name_ref.obj(), port)) { | |
120 return; | |
121 } | |
122 | |
123 guard.Release(); | |
124 | |
125 // Ownership was transfered to Java. | |
126 chrome::SelectCertificateCallback* dummy = request.release(); | |
127 (void) dummy; | |
Ryan Sleevi
2013/03/06 21:50:43
Why are you following this pattern? Trying to avoi
digit1
2013/03/06 23:00:08
yes, thanks for the pointer.
| |
128 } | |
129 | |
130 } // namespace | |
131 | |
132 namespace android { | |
133 | |
134 // Called from JNI on request completion/result. | |
135 // |env| is the current thread's JNIEnv. | |
136 // |clazz| is the SSLClientCertificateRequest JNI class reference. | |
137 // |request_id| is the id passed to | |
138 // Java_SSLClientCertificateRequest_selectClientCertificate() in Start(). | |
139 // |encoded_chain_ref| is a JNI reference to a Java array of byte arrays, | |
140 // each item holding a DER-encoded X.509 certificate. | |
141 // |private_key_ref| is the platform PrivateKey object JNI reference for | |
142 // the client certificate. | |
143 // Note: both |encoded_chain_ref| and |private_key_ref| will be NULL if | |
144 // the user didn't select a certificate. | |
145 static void OnSystemRequestCompletion( | |
146 JNIEnv* env, | |
147 jclass clazz, | |
148 jint request_id, | |
149 jobjectArray encoded_chain_ref, | |
150 jobject private_key_ref) { | |
151 // Take back ownership of the request object. | |
152 scoped_ptr<chrome::SelectCertificateCallback> callback( | |
153 reinterpret_cast<chrome::SelectCertificateCallback*>(request_id)); | |
154 | |
155 // Ensure that callback(NULL) is called on error exit. | |
156 base::Closure null_closure = | |
157 base::Bind(*callback, scoped_refptr<net::X509Certificate>()); | |
158 | |
159 base::ScopedClosureRunner guard(null_closure); | |
160 | |
161 // When the request is cancelled by the user, just send NULL | |
162 // to the callback immediately. | |
163 if (encoded_chain_ref == NULL || private_key_ref == NULL) { | |
164 LOG(ERROR) << "Client certificate request cancelled"; | |
165 return; | |
166 } | |
167 | |
168 // Convert the encoded chain to a vector of strings. | |
169 std::vector<std::string> encoded_chain_strings; | |
170 if (encoded_chain_ref) { | |
171 base::android::JavaArrayOfByteArrayToStringVector( | |
172 env, encoded_chain_ref, &encoded_chain_strings); | |
173 } | |
174 | |
175 std::vector<base::StringPiece> encoded_chain; | |
176 for (size_t n = 0; n < encoded_chain_strings.size(); ++n) | |
177 encoded_chain.push_back(encoded_chain_strings[n]); | |
178 | |
179 // Create the X509Certificate object from the encoded chain. | |
180 scoped_refptr<net::X509Certificate> client_cert( | |
181 net::X509Certificate::CreateFromDERCertChain(encoded_chain)); | |
182 if (!client_cert.get()) { | |
183 LOG(ERROR) << "Could not decode client certificate chain"; | |
184 return; | |
185 } | |
186 | |
187 // Create an EVP_PKEY wrapper for the private key JNI reference. | |
188 ScopedEVP_PKEY private_key( | |
189 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref)); | |
190 if (!private_key.get()) { | |
191 LOG(ERROR) << "Could not create OpenSSL wrapper for private key"; | |
192 return; | |
193 } | |
194 | |
195 guard.Release(); | |
196 | |
197 // RecordClientCertificateKey() must be called on the I/O thread, | |
198 // before the callback is called with the selected certificate on | |
199 // the UI thread. | |
200 content::BrowserThread::PostTaskAndReply( | |
201 content::BrowserThread::IO, | |
202 FROM_HERE, | |
203 base::Bind(&RecordClientCertificateKey, | |
204 client_cert, | |
205 base::Passed(&private_key)), | |
206 base::Bind(*callback, client_cert)); | |
207 } | |
208 | |
209 bool RegisterSSLClientCertificateRequestAndroid(JNIEnv* env) { | |
210 return RegisterNativesImpl(env); | |
211 } | |
212 | |
213 } // namespace android | |
214 | |
215 void ShowSSLClientCertificateSelector( | |
216 content::WebContents* contents, | |
217 const net::HttpNetworkSession* network_session, | |
218 net::SSLCertRequestInfo* cert_request_info, | |
219 const chrome::SelectCertificateCallback& callback) { | |
220 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
221 StartClientCertificateRequest(cert_request_info, callback); | |
222 } | |
223 | |
224 } // namespace chrome | |
OLD | NEW |