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/compiler_specific.h" | |
13 #include "base/logging.h" | |
14 #include "chrome/browser/ssl/ssl_client_certificate_selector.h" | |
15 #include "content/public/browser/browser_thread.h" | |
16 #include "jni/SSLClientCertificateRequest_jni.h" | |
17 #include "net/android/keystore_openssl.h" | |
18 #include "net/base/host_port_pair.h" | |
19 #include "net/base/openssl_client_key_store.h" | |
20 #include "net/base/ssl_cert_request_info.h" | |
21 #include "net/base/ssl_client_cert_type.h" | |
22 #include "net/base/x509_certificate.h" | |
23 | |
24 namespace chrome { | |
25 | |
26 namespace { | |
27 | |
28 typedef net::OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY; | |
29 | |
30 // Must be called on the I/O thread to record a client certificate | |
31 // and its private key in the OpenSSLClientKeyStore. | |
32 void RecordClientCertificateKey( | |
33 const scoped_refptr<net::X509Certificate>& client_cert, | |
34 ScopedEVP_PKEY private_key) { | |
35 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
36 net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey( | |
37 client_cert.get(), private_key.get()); | |
38 } | |
39 | |
40 void StartClientCertificateRequest( | |
41 const net::SSLCertRequestInfo* cert_request_info, | |
42 const chrome::SelectCertificateCallback& callback) { | |
43 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
44 | |
45 // Ensure that callback(NULL) is posted as a task on the UI thread | |
46 // in case of an error. | |
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 | |
74 JNIEnv* env = base::android::AttachCurrentThread(); | |
75 ScopedJavaLocalRef<jobjectArray> key_types_ref = | |
76 base::android::ToJavaArrayOfStrings(env, key_types); | |
77 if (key_types_ref.is_null()) { | |
78 LOG(ERROR) << "Could not create key types array (String[])"; | |
79 return; | |
80 } | |
81 | |
82 // Build the |encoded_principals| JNI parameter, as a byte[][] | |
83 ScopedJavaLocalRef<jobjectArray> principals_ref = | |
84 base::android::ToJavaArrayOfByteArray( | |
85 env, cert_request_info->cert_authorities); | |
86 if (principals_ref.is_null()) { | |
87 LOG(ERROR) << "Could not create principals array (byte[][])"; | |
88 return; | |
89 } | |
90 | |
91 // Build the |host_name| and |port| JNI parameters, as a String and | |
92 // a jint. | |
93 net::HostPortPair host_and_port = | |
94 net::HostPortPair::FromString(cert_request_info->host_and_port); | |
95 | |
96 ScopedJavaLocalRef<jstring> host_name_ref = | |
97 base::android::ConvertUTF8ToJavaString(env, host_and_port.host()); | |
98 if (host_name_ref.is_null()) { | |
99 LOG(ERROR) << "Could not extract host name from: '" | |
100 << cert_request_info->host_and_port << "'"; | |
101 return; | |
102 } | |
103 | |
104 // Create a copy of the callback on the heap so that its address | |
105 // and ownership can be passed through and returned from Java via JNI. | |
106 scoped_ptr<chrome::SelectCertificateCallback> request( | |
107 new chrome::SelectCertificateCallback(callback)); | |
108 | |
109 jint request_id = reinterpret_cast<jint>(request.get()); | |
110 | |
111 if (!chrome::android:: | |
112 Java_SSLClientCertificateRequest_selectClientCertificate( | |
113 env, request_id, key_types_ref.obj(), principals_ref.obj(), | |
114 host_name_ref.obj(), host_and_port.port())) { | |
115 return; | |
116 } | |
117 | |
118 guard.Release(); | |
119 | |
120 // Ownership was transferred to Java. | |
121 chrome::SelectCertificateCallback* ALLOW_UNUSED dummy = | |
122 request.release(); | |
123 } | |
124 | |
125 } // namespace | |
126 | |
127 namespace android { | |
128 | |
129 // Called from JNI on request completion/result. | |
130 // |env| is the current thread's JNIEnv. | |
131 // |clazz| is the SSLClientCertificateRequest JNI class reference. | |
132 // |request_id| is the id passed to | |
133 // Java_SSLClientCertificateRequest_selectClientCertificate() in Start(). | |
134 // |encoded_chain_ref| is a JNI reference to a Java array of byte arrays, | |
135 // each item holding a DER-encoded X.509 certificate. | |
136 // |private_key_ref| is the platform PrivateKey object JNI reference for | |
137 // the client certificate. | |
138 // Note: both |encoded_chain_ref| and |private_key_ref| will be NULL if | |
139 // the user didn't select a certificate. | |
140 static void OnSystemRequestCompletion( | |
141 JNIEnv* env, | |
142 jclass clazz, | |
143 jint request_id, | |
144 jobjectArray encoded_chain_ref, | |
145 jobject private_key_ref) { | |
joth
2013/03/09 01:17:22
DCHECK(content::BrowserThread::CurrentlyOn(content
digit1
2013/03/11 21:41:21
Done.
| |
146 // Take back ownership of the request object. | |
147 scoped_ptr<chrome::SelectCertificateCallback> callback( | |
148 reinterpret_cast<chrome::SelectCertificateCallback*>(request_id)); | |
149 | |
150 // Ensure that callback(NULL) is called in case of an error. | |
151 base::Closure null_closure = | |
152 base::Bind(*callback, scoped_refptr<net::X509Certificate>()); | |
153 | |
154 base::ScopedClosureRunner guard(null_closure); | |
155 | |
156 if (encoded_chain_ref == NULL || private_key_ref == NULL) { | |
157 LOG(ERROR) << "Client certificate request cancelled"; | |
158 return; | |
159 } | |
160 | |
161 // Convert the encoded chain to a vector of strings. | |
162 std::vector<std::string> encoded_chain_strings; | |
163 if (encoded_chain_ref) { | |
164 base::android::JavaArrayOfByteArrayToStringVector( | |
165 env, encoded_chain_ref, &encoded_chain_strings); | |
166 } | |
167 | |
168 std::vector<base::StringPiece> encoded_chain; | |
169 for (size_t n = 0; n < encoded_chain_strings.size(); ++n) | |
170 encoded_chain.push_back(encoded_chain_strings[n]); | |
171 | |
172 // Create the X509Certificate object from the encoded chain. | |
173 scoped_refptr<net::X509Certificate> client_cert( | |
174 net::X509Certificate::CreateFromDERCertChain(encoded_chain)); | |
175 if (!client_cert.get()) { | |
176 LOG(ERROR) << "Could not decode client certificate chain"; | |
177 return; | |
178 } | |
179 | |
180 // Create an EVP_PKEY wrapper for the private key JNI reference. | |
181 ScopedEVP_PKEY private_key( | |
182 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref)); | |
183 if (!private_key.get()) { | |
184 LOG(ERROR) << "Could not create OpenSSL wrapper for private key"; | |
185 return; | |
joth
2013/03/09 01:17:22
nit - wrong indent
digit1
2013/03/11 21:41:21
Done.
| |
186 } | |
187 | |
188 guard.Release(); | |
189 | |
190 // RecordClientCertificateKey() must be called on the I/O thread, | |
191 // before the callback is called with the selected certificate on | |
192 // the UI thread. | |
193 content::BrowserThread::PostTaskAndReply( | |
194 content::BrowserThread::IO, | |
195 FROM_HERE, | |
196 base::Bind(&RecordClientCertificateKey, | |
197 client_cert, | |
198 base::Passed(&private_key)), | |
199 base::Bind(*callback, client_cert)); | |
200 } | |
201 | |
202 bool RegisterSSLClientCertificateRequestAndroid(JNIEnv* env) { | |
203 return RegisterNativesImpl(env); | |
204 } | |
205 | |
206 } // namespace android | |
207 | |
208 void ShowSSLClientCertificateSelector( | |
209 content::WebContents* contents, | |
210 const net::HttpNetworkSession* network_session, | |
211 net::SSLCertRequestInfo* cert_request_info, | |
212 const chrome::SelectCertificateCallback& callback) { | |
213 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
214 StartClientCertificateRequest(cert_request_info, callback); | |
215 } | |
216 | |
217 } // namespace chrome | |
OLD | NEW |