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 <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 } | |
OLD | NEW |