Chromium Code Reviews| 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 |