1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.core; 18 19 import junit.framework.TestCase; 20 21 import com.android.org.conscrypt.FileClientSessionCache; 22 import com.android.org.conscrypt.OpenSSLContextImpl; 23 import com.android.org.conscrypt.SSLClientSessionCache; 24 import org.apache.commons.codec.binary.Base64; 25 26 import java.io.ByteArrayInputStream; 27 import java.io.DataInputStream; 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.io.PrintWriter; 33 import java.net.InetSocketAddress; 34 import java.net.Socket; 35 import java.security.KeyStore; 36 import java.security.KeyManagementException; 37 import java.security.cert.X509Certificate; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Random; 44 45 import javax.net.ssl.KeyManager; 46 import javax.net.ssl.KeyManagerFactory; 47 import javax.net.ssl.SSLContext; 48 import javax.net.ssl.SSLServerSocket; 49 import javax.net.ssl.SSLSession; 50 import javax.net.ssl.SSLSocket; 51 import javax.net.ssl.SSLSocketFactory; 52 import javax.net.ssl.TrustManager; 53 import javax.net.ssl.X509TrustManager; 54 55 /** 56 * SSL integration tests that hit real servers. 57 */ 58 public class SSLSocketTest extends TestCase { 59 60 private static SSLSocketFactory clientFactory = 61 (SSLSocketFactory) SSLSocketFactory.getDefault(); 62 63 /** 64 * Does a number of HTTPS requests on some host and consumes the response. 65 * We don't use the HttpsUrlConnection class, but do this on our own 66 * with the SSLSocket class. This gives us a chance to test the basic 67 * behavior of SSL. 68 * 69 * @param host The host name the request is being sent to. 70 * @param port The port the request is being sent to. 71 * @param path The path being requested (e.g. "/index.html"). 72 * @param outerLoop The number of times we reconnect and do the request. 73 * @param innerLoop The number of times we do the request for each 74 * connection (using HTTP keep-alive). 75 * @param delay The delay after each request (in seconds). 76 * @throws IOException When a problem occurs. 77 */ 78 private void fetch(SSLSocketFactory socketFactory, String host, int port, 79 boolean secure, String path, int outerLoop, int innerLoop, 80 int delay, int timeout) throws IOException { 81 InetSocketAddress address = new InetSocketAddress(host, port); 82 83 for (int i = 0; i < outerLoop; i++) { 84 // Connect to the remote host 85 Socket socket = secure ? socketFactory.createSocket() 86 : new Socket(); 87 if (timeout >= 0) { 88 socket.setKeepAlive(true); 89 socket.setSoTimeout(timeout * 1000); 90 } 91 socket.connect(address); 92 93 // Get the streams 94 OutputStream output = socket.getOutputStream(); 95 PrintWriter writer = new PrintWriter(output); 96 97 try { 98 DataInputStream input = new DataInputStream(socket.getInputStream()); 99 try { 100 for (int j = 0; j < innerLoop; j++) { 101 android.util.Log.d("SSLSocketTest", 102 "GET https://" + host + path + " HTTP/1.1"); 103 104 // Send a request 105 writer.println("GET https://" + host + path + " HTTP/1.1\r"); 106 writer.println("Host: " + host + "\r"); 107 writer.println("Connection: " + 108 (j == innerLoop - 1 ? "Close" : "Keep-Alive") 109 + "\r"); 110 writer.println("\r"); 111 writer.flush(); 112 113 int length = -1; 114 boolean chunked = false; 115 116 String line = input.readLine(); 117 118 if (line == null) { 119 throw new IOException("No response from server"); 120 // android.util.Log.d("SSLSocketTest", "No response from server"); 121 } 122 123 // Consume the headers, check content length and encoding type 124 while (line != null && line.length() != 0) { 125 // System.out.println(line); 126 int dot = line.indexOf(':'); 127 if (dot != -1) { 128 String key = line.substring(0, dot).trim(); 129 String value = line.substring(dot + 1).trim(); 130 131 if ("Content-Length".equalsIgnoreCase(key)) { 132 length = Integer.valueOf(value); 133 } else if ("Transfer-Encoding".equalsIgnoreCase(key)) { 134 chunked = "Chunked".equalsIgnoreCase(value); 135 } 136 137 } 138 line = input.readLine(); 139 } 140 141 assertTrue("Need either content length or chunked encoding", length != -1 142 || chunked); 143 144 // Consume the content itself 145 if (chunked) { 146 length = Integer.parseInt(input.readLine(), 16); 147 while (length != 0) { 148 byte[] buffer = new byte[length]; 149 input.readFully(buffer); 150 input.readLine(); 151 length = Integer.parseInt(input.readLine(), 16); 152 } 153 input.readLine(); 154 } else { 155 byte[] buffer = new byte[length]; 156 input.readFully(buffer); 157 } 158 159 // Sleep for the given number of seconds 160 try { 161 Thread.sleep(delay * 1000); 162 } catch (InterruptedException ex) { 163 // Shut up! 164 } 165 } 166 } finally { 167 input.close(); 168 } 169 } finally { 170 writer.close(); 171 } 172 // Close the connection 173 socket.close(); 174 } 175 } 176 177 /** 178 * Invokes fetch() with the default socket factory. 179 */ 180 private void fetch(String host, int port, boolean secure, String path, 181 int outerLoop, int innerLoop, 182 int delay, int timeout) throws IOException { 183 fetch(clientFactory, host, port, secure, path, outerLoop, innerLoop, 184 delay, timeout); 185 } 186 187 /** 188 * Does a single request for each of the hosts. Consumes the response. 189 * 190 * @throws IOException If a problem occurs. 191 */ 192 public void testSimple() throws IOException { 193 fetch("www.fortify.net", 443, true, "/sslcheck.html", 1, 1, 0, 60); 194 fetch("mail.google.com", 443, true, "/mail/", 1, 1, 0, 60); 195 fetch("www.paypal.com", 443, true, "/", 1, 1, 0, 60); 196 fetch("www.yellownet.ch", 443, true, "/", 1, 1, 0, 60); 197 } 198 199 /** 200 * Does repeated requests for each of the hosts, with the connection being 201 * closed in between. 202 * 203 * @throws IOException If a problem occurs. 204 */ 205 public void testRepeatedClose() throws IOException { 206 fetch("www.fortify.net", 443, true, "/sslcheck.html", 10, 1, 0, 60); 207 fetch("mail.google.com", 443, true, "/mail/", 10, 1, 0, 60); 208 fetch("www.paypal.com", 443, true, "/", 10, 1, 0, 60); 209 fetch("www.yellownet.ch", 443, true, "/", 10, 1, 0, 60); 210 } 211 212 /** 213 * Does repeated requests for each of the hosts, with the connection being 214 * kept alive in between. 215 * 216 * @throws IOException If a problem occurs. 217 */ 218 public void testRepeatedKeepAlive() throws IOException { 219 fetch("www.fortify.net", 443, true, "/sslcheck.html", 1, 10, 0, 60); 220 fetch("mail.google.com", 443, true, "/mail/", 1, 10, 0, 60); 221 222 // These two don't accept keep-alive 223 // fetch("www.paypal.com", 443, "/", 1, 10); 224 // fetch("www.yellownet.ch", 443, "/", 1, 10); 225 } 226 227 /** 228 * Does repeated requests for each of the hosts, with the connection being 229 * closed in between. Waits a couple of seconds after each request, but 230 * stays within a reasonable timeout. Expectation is that the connection 231 * stays open. 232 * 233 * @throws IOException If a problem occurs. 234 */ 235 public void testShortTimeout() throws IOException { 236 fetch("www.fortify.net", 443, true, "/sslcheck.html", 1, 10, 5, 60); 237 fetch("mail.google.com", 443, true, "/mail/", 1, 10, 5, 60); 238 239 // These two don't accept keep-alive 240 // fetch("www.paypal.com", 443, "/", 1, 10); 241 // fetch("www.yellownet.ch", 443, "/", 1, 10); 242 } 243 244 /** 245 * Does repeated requests for each of the hosts, with the connection being 246 * kept alive in between. Waits a longer time after each request. 247 * Expectation is that the host closes the connection. 248 */ 249 public void testLongTimeout() { 250 // Seems to have a veeeery long timeout. 251 // fetch("www.fortify.net", 443, "/sslcheck.html", 1, 2, 60); 252 253 // Google has a 60s timeout, so 90s of waiting should trigger it. 254 try { 255 fetch("mail.google.com", 443, true, "/mail/", 1, 2, 90, 180); 256 fail("Oops - timeout expected."); 257 } catch (IOException ex) { 258 // Expected. 259 } 260 261 // These two don't accept keep-alive 262 // fetch("www.paypal.com", 443, "/", 1, 10); 263 // fetch("www.yellownet.ch", 443, "/", 1, 10); 264 } 265 266 /** 267 * Does repeated requests for each of the hosts, with the connection being 268 * closed in between. Waits a longer time after each request. Expectation is 269 * that the host closes the connection. 270 */ 271 // These two need manual interaction to reproduce... 272 public void xxtestBrokenConnection() { 273 try { 274 fetch("www.fortify.net", 443, true, "/sslcheck.html", 1, 2, 60, 60); 275 fail("Oops - timeout expected."); 276 } catch (IOException ex) { 277 android.util.Log.d("SSLSocketTest", "Exception", ex); 278 // Expected. 279 } 280 281 // These two don't accept keep-alive 282 // fetch("www.paypal.com", 443, "/", 1, 10); 283 // fetch("www.yellownet.ch", 443, "/", 1, 10); 284 } 285 286 /** 287 * Does repeated requests for each of the hosts, with the connection being 288 * closed in between. Waits a longer time after each request. Expectation is 289 * that the host closes the connection. 290 */ 291 // These two need manual interaction to reproduce... 292 public void xxtestBrokenConnection2() { 293 try { 294 fetch("www.heise.de", 80, false, "/index.html", 1, 2, 60, 60); 295 fail("Oops - timeout expected."); 296 } catch (IOException ex) { 297 android.util.Log.d("SSLSocketTest", "Exception", ex); 298 // Expected. 299 } 300 301 // These two don't accept keep-alive 302 // fetch("www.paypal.com", 443, "/", 1, 10); 303 // fetch("www.yellownet.ch", 443, "/", 1, 10); 304 } 305 306 /** 307 * Regression test for 865926: SSLContext.init() should 308 * use default values for null arguments. 309 */ 310 public void testContextInitNullArgs() throws Exception { 311 SSLContext ctx = SSLContext.getInstance("TLS"); 312 ctx.init(null, null, null); 313 } 314 315 /** 316 * Regression test for 963650: javax.net.ssl.KeyManager has no implemented 317 * (documented?) algorithms. 318 */ 319 public void testDefaultAlgorithms() throws Exception { 320 SSLContext ctx = SSLContext.getInstance("TLS"); 321 KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509"); 322 KeyStore ks = KeyStore.getInstance("BKS"); 323 324 assertEquals("X509", kmf.getAlgorithm()); 325 assertEquals("X509", KeyManagerFactory.getDefaultAlgorithm()); 326 327 assertEquals("BKS", ks.getType()); 328 assertEquals("BKS", KeyStore.getDefaultType()); 329 } 330 331 /** 332 * Regression test for problem where close() resulted in a hand if 333 * a different thread was sitting in a blocking read or write. 334 */ 335 public void testMultithreadedClose() throws Exception { 336 InetSocketAddress address = new InetSocketAddress("www.fortify.net", 443); 337 final Socket socket = clientFactory.createSocket(); 338 socket.connect(address); 339 340 Thread reader = new Thread() { 341 @Override 342 public void run() { 343 try { 344 byte[] buffer = new byte[512]; 345 InputStream stream = socket.getInputStream(); 346 socket.getInputStream().read(buffer); 347 } catch (Exception ex) { 348 android.util.Log.d("SSLSocketTest", 349 "testMultithreadedClose() reader got " + ex.toString()); 350 } 351 } 352 }; 353 354 Thread closer = new Thread() { 355 @Override 356 public void run() { 357 try { 358 Thread.sleep(5000); 359 socket.close(); 360 } catch (Exception ex) { 361 android.util.Log.d("SSLSocketTest", 362 "testMultithreadedClose() closer got " + ex.toString()); 363 } 364 } 365 }; 366 367 android.util.Log.d("SSLSocketTest", "testMultithreadedClose() starting reader..."); 368 reader.start(); 369 android.util.Log.d("SSLSocketTest", "testMultithreadedClose() starting closer..."); 370 closer.start(); 371 372 long t1 = System.currentTimeMillis(); 373 android.util.Log.d("SSLSocketTest", "testMultithreadedClose() joining reader..."); 374 reader.join(30000); 375 android.util.Log.d("SSLSocketTest", "testMultithreadedClose() joining closer..."); 376 closer.join(30000); 377 long t2 = System.currentTimeMillis(); 378 379 assertTrue("Concurrent close() hangs", t2 - t1 < 30000); 380 } 381 382 private int multithreadedFetchRuns; 383 384 private int multithreadedFetchWins; 385 386 private Random multithreadedFetchRandom = new Random(); 387 388 /** 389 * Regression test for problem where multiple threads with multiple SSL 390 * connection would cause problems due to either missing native locking 391 * or the slowness of the SSL connections. 392 */ 393 public void testMultithreadedFetch() { 394 Thread[] threads = new Thread[10]; 395 396 for (int i = 0; i < threads.length; i++) { 397 threads[i] = new Thread() { 398 @Override 399 public void run() { 400 for (int i = 0; i < 10; i++) { 401 try { 402 multithreadedFetchRuns++; 403 switch (multithreadedFetchRandom.nextInt(4)) { 404 case 0: { 405 fetch("www.fortify.net", 443, 406 true, "/sslcheck.html", 1, 1, 0, 60); 407 break; 408 } 409 410 case 1: { 411 fetch("mail.google.com", 443, true, "/mail/", 1, 1, 0, 60); 412 break; 413 } 414 415 case 2: { 416 fetch("www.paypal.com", 443, true, "/", 1, 1, 0, 60); 417 break; 418 } 419 420 case 3: { 421 fetch("www.yellownet.ch", 443, true, "/", 1, 1, 0, 60); 422 break; 423 } 424 } 425 multithreadedFetchWins++; 426 } catch (Exception ex) { 427 android.util.Log.d("SSLSocketTest", 428 "testMultithreadedFetch() got Exception", ex); 429 } 430 } 431 } 432 }; 433 threads[i].start(); 434 435 android.util.Log.d("SSLSocketTest", "testMultithreadedFetch() started thread #" + i); 436 } 437 438 for (int i = 0; i < threads.length; i++) { 439 try { 440 threads[i].join(); 441 android.util.Log.d("SSLSocketTest", "testMultithreadedFetch() joined thread #" + i); 442 } catch (InterruptedException ex) { 443 // Not interested. 444 } 445 } 446 447 assertTrue("At least 95% of multithreaded SSL connections must succeed", 448 multithreadedFetchWins >= (multithreadedFetchRuns * 95) / 100); 449 } 450 451 // ------------------------------------------------------------------------- 452 // Regression test for #1204316: Missing client cert unit test. Passes on 453 // both Android and the RI. To use on the RI, install Apache Commons and 454 // replace the references to the base64-encoded keys by the JKS versions. 455 // ------------------------------------------------------------------------- 456 457 /** 458 * Defines the keystore contents for the server, JKS version. Holds just a 459 * single self-generated key. The subject name is "Test Server". 460 */ 461 private static final String SERVER_KEYS_JKS = 462 "/u3+7QAAAAIAAAABAAAAAQAFbXlrZXkAAAEaWFfBeAAAArowggK2MA4GCisGAQQBKgIRAQEFAASC" + 463 "AqI2kp5XjnF8YZkhcF92YsJNQkvsmH7zqMM87j23zSoV4DwyE3XeC/gZWq1ToScIhoqZkzlbWcu4" + 464 "T/Zfc/DrfGk/rKbBL1uWKGZ8fMtlZk8KoAhxZk1JSyJvdkyKxqmzUbxk1OFMlN2VJNu97FPVH+du" + 465 "dvjTvmpdoM81INWBW/1fZJeQeDvn4mMbbe0IxgpiLnI9WSevlaDP/sm1X3iO9yEyzHLL+M5Erspo" + 466 "Cwa558fOu5DdsICMXhvDQxjWFKFhPHnKtGe+VvwkG9/bAaDgx3kfhk0w5zvdnkKb+8Ed9ylNRzdk" + 467 "ocAa/mxlMTOsTvDKXjjsBupNPIIj7OP4GNnZaxkJjSs98pEO67op1GX2qhy6FSOPNuq8k/65HzUc" + 468 "PYn6voEeh6vm02U/sjEnzRevQ2+2wXoAdp0EwtQ/DlMe+NvcwPGWKuMgX4A4L93DZGb04N2VmAU3" + 469 "YLOtZwTO0LbuWrcCM/q99G/7LcczkxIVrO2I/rh8RXVczlf9QzcrFObFv4ATuspWJ8xG7DhsMbnk" + 470 "rT94Pq6TogYeoz8o8ZMykesAqN6mt/9+ToIemmXv+e+KU1hI5oLwWMnUG6dXM6hIvrULY6o+QCPH" + 471 "172YQJMa+68HAeS+itBTAF4Clm/bLn6reHCGGU6vNdwU0lYldpiOj9cB3t+u2UuLo6tiFWjLf5Zs" + 472 "EQJETd4g/EK9nHxJn0GAKrWnTw7pEHQJ08elzUuy04C/jEEG+4QXU1InzS4o/kR0Sqz2WTGDoSoq" + 473 "ewuPRU5bzQs/b9daq3mXrnPtRBL6HfSDAdpTK76iHqLCGdqx3avHjVSBm4zFvEuYBCev+3iKOBmg" + 474 "yh7eQRTjz4UOWfy85omMBr7lK8PtfVBDzOXpasxS0uBgdUyBDX4tO6k9jZ8a1kmQRQAAAAEABVgu" + 475 "NTA5AAACSDCCAkQwggGtAgRIR8SKMA0GCSqGSIb3DQEBBAUAMGkxCzAJBgNVBAYTAlVTMRMwEQYD" + 476 "VQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQHEwNNVFYxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMH" + 477 "QW5kcm9pZDEUMBIGA1UEAxMLVGVzdCBTZXJ2ZXIwHhcNMDgwNjA1MTA0ODQyWhcNMDgwOTAzMTA0" + 478 "ODQyWjBpMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8w" + 479 "DQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMIGf" + 480 "MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwoC6chqCI84rj1PrXuJgbiit4EV909zR6N0jNlYfg" + 481 "itwB39bP39wH03rFm8T59b3mbSptnGmCIpLZn25KPPFsYD3JJ+wFlmiUdEP9H05flfwtFQJnw9uT" + 482 "3rRIdYVMPcQ3RoZzwAMliGr882I2thIDbA6xjGU/1nRIdvk0LtxH3QIDAQABMA0GCSqGSIb3DQEB" + 483 "BAUAA4GBAJn+6YgUlY18Ie+0+Vt8oEi81DNi/bfPrAUAh63fhhBikx/3R9dl3wh09Z6p7cIdNxjW" + 484 "n2ll+cRW9eqF7z75F0Omm0C7/KAEPjukVbszmzeU5VqzkpSt0j84YWi+TfcHRrfvhLbrlmGITVpY" + 485 "ol5pHLDyqGmDs53pgwipWqsn/nEXEBgj3EoqPeqHbDf7YaP8h/5BSt0="; 486 487 /** 488 * Defines the keystore contents for the server, BKS version. Holds just a 489 * single self-generated key. The subject name is "Test Server". 490 */ 491 private static final String SERVER_KEYS_BKS = 492 "AAAAAQAAABQDkebzoP1XwqyWKRCJEpn/t8dqIQAABDkEAAVteWtleQAAARpYl20nAAAAAQAFWC41" + 493 "MDkAAAJNMIICSTCCAbKgAwIBAgIESEfU1jANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET" + 494 "MBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNV" + 495 "BAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMB4XDTA4MDYwNTExNTgxNFoXDTA4MDkw" + 496 "MzExNTgxNFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAcTA01U" + 497 "VjEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRQwEgYDVQQDEwtUZXN0IFNlcnZl" + 498 "cjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0LIdKaIr9/vsTq8BZlA3R+NFWRaH4lGsTAQy" + 499 "DPMF9ZqEDOaL6DJuu0colSBBBQ85hQTPa9m9nyJoN3pEi1hgamqOvQIWcXBk+SOpUGRZZFXwniJV" + 500 "zDKU5nE9MYgn2B9AoiH3CSuMz6HRqgVaqtppIe1jhukMc/kHVJvlKRNy9XMCAwEAATANBgkqhkiG" + 501 "9w0BAQUFAAOBgQC7yBmJ9O/eWDGtSH9BH0R3dh2NdST3W9hNZ8hIa8U8klhNHbUCSSktZmZkvbPU" + 502 "hse5LI3dh6RyNDuqDrbYwcqzKbFJaq/jX9kCoeb3vgbQElMRX8D2ID1vRjxwlALFISrtaN4VpWzV" + 503 "yeoHPW4xldeZmoVtjn8zXNzQhLuBqX2MmAAAAqwAAAAUvkUScfw9yCSmALruURNmtBai7kQAAAZx" + 504 "4Jmijxs/l8EBaleaUru6EOPioWkUAEVWCxjM/TxbGHOi2VMsQWqRr/DZ3wsDmtQgw3QTrUK666sR" + 505 "MBnbqdnyCyvM1J2V1xxLXPUeRBmR2CXorYGF9Dye7NkgVdfA+9g9L/0Au6Ugn+2Cj5leoIgkgApN" + 506 "vuEcZegFlNOUPVEs3SlBgUF1BY6OBM0UBHTPwGGxFBBcetcuMRbUnu65vyDG0pslT59qpaR0TMVs" + 507 "P+tcheEzhyjbfM32/vwhnL9dBEgM8qMt0sqF6itNOQU/F4WGkK2Cm2v4CYEyKYw325fEhzTXosck" + 508 "MhbqmcyLab8EPceWF3dweoUT76+jEZx8lV2dapR+CmczQI43tV9btsd1xiBbBHAKvymm9Ep9bPzM" + 509 "J0MQi+OtURL9Lxke/70/MRueqbPeUlOaGvANTmXQD2OnW7PISwJ9lpeLfTG0LcqkoqkbtLKQLYHI" + 510 "rQfV5j0j+wmvmpMxzjN3uvNajLa4zQ8l0Eok9SFaRr2RL0gN8Q2JegfOL4pUiHPsh64WWya2NB7f" + 511 "V+1s65eA5ospXYsShRjo046QhGTmymwXXzdzuxu8IlnTEont6P4+J+GsWk6cldGbl20hctuUKzyx" + 512 "OptjEPOKejV60iDCYGmHbCWAzQ8h5MILV82IclzNViZmzAapeeCnexhpXhWTs+xDEYSKEiG/camt" + 513 "bhmZc3BcyVJrW23PktSfpBQ6D8ZxoMfF0L7V2GQMaUg+3r7ucrx82kpqotjv0xHghNIm95aBr1Qw" + 514 "1gaEjsC/0wGmmBDg1dTDH+F1p9TInzr3EFuYD0YiQ7YlAHq3cPuyGoLXJ5dXYuSBfhDXJSeddUkl" + 515 "k1ufZyOOcskeInQge7jzaRfmKg3U94r+spMEvb0AzDQVOKvjjo1ivxMSgFRZaDb/4qw="; 516 517 /** 518 * Defines the keystore contents for the client, JKS version. Holds just a 519 * single self-generated key. The subject name is "Test Client". 520 */ 521 private static final String CLIENT_KEYS_JKS = 522 "/u3+7QAAAAIAAAABAAAAAQAFbXlrZXkAAAEaWFhyMAAAArkwggK1MA4GCisGAQQBKgIRAQEFAASC" + 523 "AqGVSfXolBStZy4nnRNn4fAr+S7kfU2BS23wwW8uB2Ru3GvtLzlK9q08Gvq/LNqBafjyFTVL5FV5" + 524 "SED/8YomO5a98GpskSeRvytCiTBLJdgGhws5TOGekgIAcBROPGIyOtJPQ0HfOQs+BqgzGDHzHQhw" + 525 "u/8Tm6yQwiP+W/1I9B1QnaEztZA3mhTyMMJsmsFTYroGgAog885D5Cmzd8sYGfxec3R6I+xcmBAY" + 526 "eibR5kGpWwt1R+qMvRrtBqh5r6WSKhCBNax+SJVbtUNRiKyjKccdJg6fGqIWWeivwYTy0OhjA6b4" + 527 "NiZ/ZZs5pxFGWUj/Rlp0RYy8fCF6aw5/5s4Bf4MI6dPSqMG8Hf7sJR91GbcELyzPdM0h5lNavgit" + 528 "QPEzKeuDrGxhY1frJThBsNsS0gxeu+OgfJPEb/H4lpYX5IvuIGbWKcxoO9zq4/fimIZkdA8A+3eY" + 529 "mfDaowvy65NBVQPJSxaOyFhLHfeLqOeCsVENAea02vA7andZHTZehvcrqyKtm+z8ncHGRC2H9H8O" + 530 "jKwKHfxxrYY/jMAKLl00+PBb3kspO+BHI2EcQnQuMw/zr83OR9Meq4TJ0TMuNkApZELAeFckIBbS" + 531 "rBr8NNjAIfjuCTuKHhsTFWiHfk9ZIzigxXagfeDRiyVc6khOuF/bGorj23N2o7Rf3uLoU6PyXWi4" + 532 "uhctR1aL6NzxDoK2PbYCeA9hxbDv8emaVPIzlVwpPK3Ruvv9mkjcOhZ74J8bPK2fQmbplbOljcZi" + 533 "tZijOfzcO/11JrwhuJZRA6wanTqHoujgChV9EukVrmbWGGAcewFnAsSbFXIik7/+QznXaDIt5NgL" + 534 "H/Bcz4Z/fdV7Ae1eUaxKXdPbI//4J+8liVT/d8awjW2tldIaDlmGMR3aoc830+3mAAAAAQAFWC41" + 535 "MDkAAAJIMIICRDCCAa0CBEhHxLgwDQYJKoZIhvcNAQEEBQAwaTELMAkGA1UEBhMCVVMxEzARBgNV" + 536 "BAgTCkNhbGlmb3JuaWExDDAKBgNVBAcTA01UVjEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdB" + 537 "bmRyb2lkMRQwEgYDVQQDEwtUZXN0IENsaWVudDAeFw0wODA2MDUxMDQ5MjhaFw0wODA5MDMxMDQ5" + 538 "MjhaMGkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQHEwNNVFYxDzAN" + 539 "BgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHQW5kcm9pZDEUMBIGA1UEAxMLVGVzdCBDbGllbnQwgZ8w" + 540 "DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAIK3Q+KiFbmCGg422TAo4gggdhMH6FJhiuz8DxRyeMKR" + 541 "UAfP4MK0wtc8N42waZ6OKvxpBFUy0BRfBsX0GD4Ku99yu9/tavSigTraeJtwV3WWRRjIqk7L3wX5" + 542 "cmgS2KSD43Y0rNUKrko26lnt9N4qiYRBSj+tcAN3Lx9+ptqk1LApAgMBAAEwDQYJKoZIhvcNAQEE" + 543 "BQADgYEANb7Q1GVSuy1RPJ0FmiXoMYCCtvlRLkmJphwxovK0cAQK12Vll+yAzBhHiQHy/RA11mng" + 544 "wYudC7u3P8X/tBT8GR1Yk7QW3KgFyPafp3lQBBCraSsfrjKj+dCLig1uBLUr4f68W8VFWZWWTHqp" + 545 "NMGpCX6qmjbkJQLVK/Yfo1ePaUexPSOX0G9m8+DoV3iyNw6at01NRw=="; 546 547 /** 548 * Defines the keystore contents for the client, BKS version. Holds just a 549 * single self-generated key. The subject name is "Test Client". 550 */ 551 private static final String CLIENT_KEYS_BKS = 552 "AAAAAQAAABT4Rka6fxbFps98Y5k2VilmbibNkQAABfQEAAVteWtleQAAARpYl+POAAAAAQAFWC41" + 553 "MDkAAAJNMIICSTCCAbKgAwIBAgIESEfU9TANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET" + 554 "MBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNV" + 555 "BAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgQ2xpZW50MB4XDTA4MDYwNTExNTg0NVoXDTA4MDkw" + 556 "MzExNTg0NVowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAcTA01U" + 557 "VjEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRQwEgYDVQQDEwtUZXN0IENsaWVu" + 558 "dDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApUvmWsQDHPpbDKK13Yez2/q54tTOmRml/qva" + 559 "2K6dZjkjSTW0iRuk7ztaVEvdJpfVIDv1oBsCI51ttyLHROy1epjF+GoL74mJb7fkcd0VOoSOTjtD" + 560 "+3GgZkHPAm5YmUYxiJXqxKKJJqMCTIW46eJaA2nAep9QIwZ14/NFAs4ObV8CAwEAATANBgkqhkiG" + 561 "9w0BAQUFAAOBgQCJrCr3hZQFDlLIfsSKI1/w+BLvyf4fubOid0pBxfklR8KBNPTiqjSmu7pd/C/F" + 562 "1FR8CdZUDoPflZHCOU+fj5r5KUC1HyigY/tEUvlforBpfB0uCF+tXW4DbUfOWhfMtLV4nCOJOOZg" + 563 "awfZLJWBJouLKOp427vDftxTSB+Ks8YjlgAAAqwAAAAU+NH6TtrzjyDdCXm5B6Vo7xX5G4YAAAZx" + 564 "EAUkcZtmykn7YdaYxC1jRFJ+GEJpC8nZVg83QClVuCSIS8a5f8Hl44Bk4oepOZsPzhtz3RdVzDVi" + 565 "RFfoyZFsrk9F5bDTVJ6sQbb/1nfJkLhZFXokka0vND5AXMSoD5Bj1Fqem3cK7fSUyqKvFoRKC3XD" + 566 "FQvhqoam29F1rbl8FaYdPvhhZo8TfZQYUyUKwW+RbR44M5iHPx+ykieMe/C/4bcM3z8cwIbYI1aO" + 567 "gjQKS2MK9bs17xaDzeAh4sBKrskFGrDe+2dgvrSKdoakJhLTNTBSG6m+rzqMSCeQpafLKMSjTSSz" + 568 "+KoQ9bLyax8cbvViGGju0SlVhquloZmKOfHr8TukIoV64h3uCGFOVFtQjCYDOq6NbfRvMh14UVF5" + 569 "zgDIGczoD9dMoULWxBmniGSntoNgZM+QP6Id7DBasZGKfrHIAw3lHBqcvB5smemSu7F4itRoa3D8" + 570 "N7hhUEKAc+xA+8NKmXfiCBoHfPHTwDvt4IR7gWjeP3Xv5vitcKQ/MAfO5RwfzkYCXQ3FfjfzmsE1" + 571 "1IfLRDiBj+lhQSulhRVStKI88Che3M4JUNGKllrc0nt1pWa1vgzmUhhC4LSdm6trTHgyJnB6OcS9" + 572 "t2furYjK88j1AuB4921oxMxRm8c4Crq8Pyuf+n3YKi8Pl2BzBtw++0gj0ODlgwut8SrVj66/nvIB" + 573 "jN3kLVahR8nZrEFF6vTTmyXi761pzq9yOVqI57wJGx8o3Ygox1p+pWUPl1hQR7rrhUbgK/Q5wno9" + 574 "uJk07h3IZnNxE+/IKgeMTP/H4+jmyT4mhsexJ2BFHeiKF1KT/FMcJdSi+ZK5yoNVcYuY8aZbx0Ef" + 575 "lHorCXAmLFB0W6Cz4KPP01nD9YBB4olxiK1t7m0AU9zscdivNiuUaB5OIEr+JuZ6dNw="; 576 /** 577 * Defines the password for the keystore. 578 */ 579 private static final String PASSWORD = "android"; 580 581 /** 582 * Implements basically a dummy TrustManager. It stores the certificate 583 * chain it sees, so it can later be queried. 584 */ 585 class TestTrustManager implements X509TrustManager { 586 587 private X509Certificate[] chain; 588 589 private String authType; 590 591 public void checkClientTrusted(X509Certificate[] chain, String authType) { 592 this.chain = chain; 593 this.authType = authType; 594 } 595 596 public void checkServerTrusted(X509Certificate[] chain, String authType) { 597 this.chain = chain; 598 this.authType = authType; 599 } 600 601 public X509Certificate[] getAcceptedIssuers() { 602 return new X509Certificate[0]; 603 } 604 605 public X509Certificate[] getChain() { 606 return chain; 607 } 608 609 public String getAuthType() { 610 return authType; 611 } 612 613 } 614 615 /** 616 * Implements a test SSL socket server. It wait for a connection on a given 617 * port, requests client authentication (if specified), and read 256 bytes 618 * from the socket. 619 */ 620 class TestServer implements Runnable { 621 622 public static final int CLIENT_AUTH_NONE = 0; 623 624 public static final int CLIENT_AUTH_WANTED = 1; 625 626 public static final int CLIENT_AUTH_NEEDED = 2; 627 628 private TestTrustManager trustManager; 629 630 private Exception exception; 631 632 private int port; 633 634 private int clientAuth; 635 636 private boolean provideKeys; 637 638 public TestServer(int port, boolean provideKeys, int clientAuth) { 639 this.port = port; 640 this.clientAuth = clientAuth; 641 this.provideKeys = provideKeys; 642 643 trustManager = new TestTrustManager(); 644 } 645 646 public void run() { 647 try { 648 KeyManager[] keyManagers = provideKeys 649 ? getKeyManagers(SERVER_KEYS_BKS) : null; 650 TrustManager[] trustManagers = new TrustManager[] { 651 trustManager }; 652 653 SSLContext sslContext = SSLContext.getInstance("TLS"); 654 sslContext.init(keyManagers, trustManagers, null); 655 656 SSLServerSocket serverSocket 657 = (SSLServerSocket) sslContext.getServerSocketFactory() 658 .createServerSocket(); 659 660 if (clientAuth == CLIENT_AUTH_WANTED) { 661 serverSocket.setWantClientAuth(true); 662 } else if (clientAuth == CLIENT_AUTH_NEEDED) { 663 serverSocket.setNeedClientAuth(true); 664 } else { 665 serverSocket.setWantClientAuth(false); 666 } 667 668 serverSocket.bind(new InetSocketAddress(port)); 669 670 SSLSocket clientSocket = (SSLSocket) serverSocket.accept(); 671 672 InputStream stream = clientSocket.getInputStream(); 673 674 for (int i = 0; i < 256; i++) { 675 int j = stream.read(); 676 if (i != j) { 677 throw new RuntimeException("Error reading socket," 678 + " expected " + i + ", got " + j); 679 } 680 } 681 682 stream.close(); 683 clientSocket.close(); 684 serverSocket.close(); 685 686 } catch (Exception ex) { 687 exception = ex; 688 } 689 } 690 691 public Exception getException() { 692 return exception; 693 } 694 695 public X509Certificate[] getChain() { 696 return trustManager.getChain(); 697 } 698 699 } 700 701 /** 702 * Implements a test SSL socket client. It open a connection to localhost on 703 * a given port and writes 256 bytes to the socket. 704 */ 705 class TestClient implements Runnable { 706 707 private TestTrustManager trustManager; 708 709 private Exception exception; 710 711 private int port; 712 713 private boolean provideKeys; 714 715 public TestClient(int port, boolean provideKeys) { 716 this.port = port; 717 this.provideKeys = provideKeys; 718 719 trustManager = new TestTrustManager(); 720 } 721 722 public void run() { 723 try { 724 KeyManager[] keyManagers = provideKeys 725 ? getKeyManagers(CLIENT_KEYS_BKS) : null; 726 TrustManager[] trustManagers = new TrustManager[] { 727 trustManager }; 728 729 SSLContext sslContext = SSLContext.getInstance("TLS"); 730 sslContext.init(keyManagers, trustManagers, null); 731 732 SSLSocket socket = (SSLSocket) sslContext.getSocketFactory() 733 .createSocket(); 734 735 socket.connect(new InetSocketAddress(port)); 736 socket.startHandshake(); 737 738 OutputStream stream = socket.getOutputStream(); 739 740 for (int i = 0; i < 256; i++) { 741 stream.write(i); 742 } 743 744 stream.flush(); 745 stream.close(); 746 socket.close(); 747 748 } catch (Exception ex) { 749 exception = ex; 750 } 751 } 752 753 public Exception getException() { 754 return exception; 755 } 756 757 public X509Certificate[] getChain() { 758 return trustManager.getChain(); 759 } 760 761 } 762 763 /** 764 * Loads a keystore from a base64-encoded String. Returns the KeyManager[] 765 * for the result. 766 */ 767 private KeyManager[] getKeyManagers(String keys) throws Exception { 768 byte[] bytes = new Base64().decode(keys.getBytes()); 769 InputStream inputStream = new ByteArrayInputStream(bytes); 770 771 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 772 keyStore.load(inputStream, PASSWORD.toCharArray()); 773 inputStream.close(); 774 775 String algorithm = KeyManagerFactory.getDefaultAlgorithm(); 776 KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm); 777 keyManagerFactory.init(keyStore, PASSWORD.toCharArray()); 778 779 return keyManagerFactory.getKeyManagers(); 780 } 781 782 /** 783 * Implements the actual test case. Launches a server and a client, requires 784 * client authentication and checks the certificates afterwards (not in the 785 * usual sense, we just make sure that we got the expected certificates, 786 * because our self-signed test certificates are not valid.) 787 */ 788 public void testClientAuth() { 789 try { 790 TestServer server = new TestServer(8088, true, TestServer.CLIENT_AUTH_WANTED); 791 TestClient client = new TestClient(8088, true); 792 793 Thread serverThread = new Thread(server); 794 Thread clientThread = new Thread(client); 795 796 serverThread.start(); 797 clientThread.start(); 798 799 serverThread.join(); 800 clientThread.join(); 801 802 // The server must have completed without an exception. 803 if (server.getException() != null) { 804 throw new RuntimeException(server.getException()); 805 } 806 807 // The client must have completed without an exception. 808 if (client.getException() != null) { 809 throw new RuntimeException(client.getException()); 810 } 811 812 // Caution: The clientChain is the certificate chain from our 813 // client object. It contains the server certificates, of course! 814 X509Certificate[] clientChain = client.getChain(); 815 assertTrue("Client cert chain must not be null", clientChain != null); 816 assertTrue("Client cert chain must not be empty", clientChain.length != 0); 817 assertEquals("CN=Test Server, OU=Android, O=Google, L=MTV, ST=California, C=US", clientChain[0].getSubjectDN().toString()); 818 // Important part ------^ 819 820 // Caution: The serverChain is the certificate chain from our 821 // server object. It contains the client certificates, of course! 822 X509Certificate[] serverChain = server.getChain(); 823 assertTrue("Server cert chain must not be null", serverChain != null); 824 assertTrue("Server cert chain must not be empty", serverChain.length != 0); 825 assertEquals("CN=Test Client, OU=Android, O=Google, L=MTV, ST=California, C=US", serverChain[0].getSubjectDN().toString()); 826 // Important part ------^ 827 828 } catch (Exception ex) { 829 throw new RuntimeException(ex); 830 } 831 } 832 833 // ------------------------------------------------------------------------- 834 private SSLSocket handshakeSocket; 835 836 private Exception handshakeException; 837 838 839 public void testSSLHandshakeHangTimeout() { 840 841 Thread thread = new Thread() { 842 @Override 843 public void run() { 844 try { 845 SSLSocket socket = (SSLSocket)clientFactory.createSocket( 846 "www.heise.de", 80); 847 socket.setSoTimeout(5000); 848 socket.startHandshake(); 849 socket.close(); 850 } catch (Exception ex) { 851 handshakeException = ex; 852 } 853 } 854 }; 855 856 thread.start(); 857 858 try { 859 thread.join(10000); 860 } catch (InterruptedException ex) { 861 // Ignore. 862 } 863 864 if (handshakeException == null) { 865 fail("SSL handshake should have failed."); 866 } 867 } 868 869 public void testSSLHandshakeHangClose() { 870 871 Thread thread = new Thread() { 872 @Override 873 public void run() { 874 try { 875 handshakeSocket = (SSLSocket)clientFactory.createSocket( 876 "www.heise.de", 80); 877 handshakeSocket.startHandshake(); 878 } catch (Exception ex) { 879 handshakeException = ex; 880 } 881 } 882 }; 883 884 thread.start(); 885 886 887 try { 888 Thread.sleep(5000); 889 try { 890 handshakeSocket.close(); 891 } catch (Exception ex) { 892 throw new RuntimeException(ex); 893 } 894 895 thread.join(5000); 896 } catch (InterruptedException ex) { 897 // Ignore. 898 } 899 900 if (handshakeException == null) { 901 fail("SSL handshake should have failed."); 902 } 903 } 904 905 /** 906 * Tests our in-memory and persistent caching support. 907 */ 908 public void testClientSessionCaching() throws IOException, 909 KeyManagementException { 910 OpenSSLContextImpl context = new OpenSSLContextImpl(); 911 912 // Cache size = 2. 913 FakeClientSessionCache fakeCache = new FakeClientSessionCache(); 914 context.engineInit(null, null, null); 915 context.engineGetClientSessionContext().setPersistentCache(fakeCache); 916 SSLSocketFactory socketFactory = context.engineGetSocketFactory(); 917 context.engineGetClientSessionContext().setSessionCacheSize(2); 918 makeRequests(socketFactory); 919 List<String> smallCacheOps = Arrays.asList( 920 "get www.fortify.net", 921 "put www.fortify.net", 922 "get www.paypal.com", 923 "put www.paypal.com", 924 "get www.yellownet.ch", 925 "put www.yellownet.ch", 926 927 // At this point, all in-memory cache requests should miss, 928 // but the sessions will still be in the persistent cache. 929 "get www.fortify.net", 930 "get www.paypal.com", 931 "get www.yellownet.ch" 932 ); 933 assertEquals(smallCacheOps, fakeCache.ops); 934 935 // Cache size = 3. 936 fakeCache = new FakeClientSessionCache(); 937 context.engineInit(null, null, null); 938 context.engineGetClientSessionContext().setPersistentCache(fakeCache); 939 socketFactory = context.engineGetSocketFactory(); 940 context.engineGetClientSessionContext().setSessionCacheSize(3); 941 makeRequests(socketFactory); 942 List<String> bigCacheOps = Arrays.asList( 943 "get www.fortify.net", 944 "put www.fortify.net", 945 "get www.paypal.com", 946 "put www.paypal.com", 947 "get www.yellownet.ch", 948 "put www.yellownet.ch" 949 950 // At this point, all results should be in the in-memory 951 // cache, and the persistent cache shouldn't be hit anymore. 952 ); 953 assertEquals(bigCacheOps, fakeCache.ops); 954 955 // Cache size = 4. 956 fakeCache = new FakeClientSessionCache(); 957 context.engineInit(null, null, null); 958 context.engineGetClientSessionContext().setPersistentCache(fakeCache); 959 socketFactory = context.engineGetSocketFactory(); 960 context.engineGetClientSessionContext().setSessionCacheSize(4); 961 makeRequests(socketFactory); 962 assertEquals(bigCacheOps, fakeCache.ops); 963 } 964 965 /** 966 * Executes sequence of requests twice using given socket factory. 967 */ 968 private void makeRequests(SSLSocketFactory socketFactory) 969 throws IOException { 970 for (int i = 0; i < 2; i++) { 971 fetch(socketFactory, "www.fortify.net", 443, true, "/sslcheck.html", 972 1, 1, 0, 60); 973 fetch(socketFactory, "www.paypal.com", 443, true, "/", 974 1, 1, 0, 60); 975 fetch(socketFactory, "www.yellownet.ch", 443, true, "/", 976 1, 1, 0, 60); 977 } 978 } 979 980 /** 981 * Fake in the sense that it doesn't actually persist anything. 982 */ 983 static class FakeClientSessionCache implements SSLClientSessionCache { 984 985 List<String> ops = new ArrayList<String>(); 986 Map<String, byte[]> sessions = new HashMap<String, byte[]>(); 987 988 public byte[] getSessionData(String host, int port) { 989 ops.add("get " + host); 990 return sessions.get(host); 991 } 992 993 public void putSessionData(SSLSession session, byte[] sessionData) { 994 String host = session.getPeerHost(); 995 System.err.println("length: " + sessionData.length); 996 ops.add("put " + host); 997 sessions.put(host, sessionData); 998 } 999 } 1000 1001 public void testFileBasedClientSessionCache() throws IOException, 1002 KeyManagementException { 1003 OpenSSLContextImpl context = new OpenSSLContextImpl(); 1004 String tmpDir = System.getProperty("java.io.tmpdir"); 1005 if (tmpDir == null) { 1006 fail("Please set 'java.io.tmpdir' system property."); 1007 } 1008 File cacheDir = new File(tmpDir 1009 + "/" + SSLSocketTest.class.getName() + "/cache"); 1010 deleteDir(cacheDir); 1011 SSLClientSessionCache fileCache 1012 = FileClientSessionCache.usingDirectory(cacheDir); 1013 try { 1014 ClientSessionCacheProxy cacheProxy 1015 = new ClientSessionCacheProxy(fileCache); 1016 context.engineInit(null, null, null); 1017 context.engineGetClientSessionContext().setPersistentCache(cacheProxy); 1018 SSLSocketFactory socketFactory = context.engineGetSocketFactory(); 1019 context.engineGetClientSessionContext().setSessionCacheSize(1); 1020 makeRequests(socketFactory); 1021 List<String> expected = Arrays.asList( 1022 "unsuccessful get www.fortify.net", 1023 "put www.fortify.net", 1024 "unsuccessful get www.paypal.com", 1025 "put www.paypal.com", 1026 "unsuccessful get www.yellownet.ch", 1027 "put www.yellownet.ch", 1028 1029 // At this point, all in-memory cache requests should miss, 1030 // but the sessions will still be in the persistent cache. 1031 "successful get www.fortify.net", 1032 "successful get www.paypal.com", 1033 "successful get www.yellownet.ch" 1034 ); 1035 assertEquals(expected, cacheProxy.ops); 1036 1037 // Try again now that file-based cache is populated. 1038 fileCache = FileClientSessionCache.usingDirectory(cacheDir); 1039 cacheProxy = new ClientSessionCacheProxy(fileCache); 1040 context.engineInit(null, null, null); 1041 context.engineGetClientSessionContext().setPersistentCache(cacheProxy); 1042 socketFactory = context.engineGetSocketFactory(); 1043 context.engineGetClientSessionContext().setSessionCacheSize(1); 1044 makeRequests(socketFactory); 1045 expected = Arrays.asList( 1046 "successful get www.fortify.net", 1047 "successful get www.paypal.com", 1048 "successful get www.yellownet.ch", 1049 "successful get www.fortify.net", 1050 "successful get www.paypal.com", 1051 "successful get www.yellownet.ch" 1052 ); 1053 assertEquals(expected, cacheProxy.ops); 1054 } finally { 1055 deleteDir(cacheDir); 1056 } 1057 } 1058 1059 private static void deleteDir(File directory) { 1060 if (!directory.exists()) { 1061 return; 1062 } 1063 for (File file : directory.listFiles()) { 1064 file.delete(); 1065 } 1066 directory.delete(); 1067 } 1068 1069 static class ClientSessionCacheProxy implements SSLClientSessionCache { 1070 1071 final SSLClientSessionCache delegate; 1072 final List<String> ops = new ArrayList<String>(); 1073 1074 ClientSessionCacheProxy(SSLClientSessionCache delegate) { 1075 this.delegate = delegate; 1076 } 1077 1078 public byte[] getSessionData(String host, int port) { 1079 byte[] sessionData = delegate.getSessionData(host, port); 1080 ops.add((sessionData == null ? "unsuccessful" : "successful") 1081 + " get " + host); 1082 return sessionData; 1083 } 1084 1085 public void putSessionData(SSLSession session, byte[] sessionData) { 1086 delegate.putSessionData(session, sessionData); 1087 ops.add("put " + session.getPeerHost()); 1088 } 1089 } 1090 1091 public static void main(String[] args) throws KeyManagementException, IOException { 1092 new SSLSocketTest().testFileBasedClientSessionCache(); 1093 } 1094 } 1095