1/* 2 * Copyright (C) 2010 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 17package libcore.javax.net.ssl; 18 19import java.io.ByteArrayInputStream; 20import java.io.ByteArrayOutputStream; 21import java.io.IOException; 22import java.io.ObjectInputStream; 23import java.io.ObjectOutput; 24import java.io.ObjectOutputStream; 25import java.io.OutputStream; 26import java.net.InetAddress; 27import java.net.InetSocketAddress; 28import java.net.Socket; 29import java.net.UnknownHostException; 30import java.security.KeyStore; 31import java.security.Principal; 32import java.security.SecureRandom; 33import java.security.cert.Certificate; 34import java.security.cert.CertificateException; 35import java.security.cert.X509Certificate; 36import java.util.Collections; 37import javax.net.ssl.KeyManager; 38import javax.net.ssl.SSLContext; 39import javax.net.ssl.SSLServerSocket; 40import javax.net.ssl.SSLSocket; 41import javax.net.ssl.SSLSocketFactory; 42import javax.net.ssl.TrustManager; 43import javax.net.ssl.X509ExtendedTrustManager; 44import javax.net.ssl.X509TrustManager; 45import junit.framework.Assert; 46import libcore.java.security.StandardNames; 47import libcore.java.security.TestKeyStore; 48 49/** 50 * TestSSLContext is a convenience class for other tests that 51 * want a canned SSLContext and related state for testing so they 52 * don't have to duplicate the logic. 53 */ 54public final class TestSSLContext extends Assert { 55 56 /* 57 * The RI and Android have very different default SSLSession cache behaviors. 58 * The RI keeps an unlimited number of SSLSesions around for 1 day. 59 * Android keeps 10 SSLSessions forever. 60 */ 61 private static final boolean IS_RI = StandardNames.IS_RI; 62 public static final int EXPECTED_DEFAULT_CLIENT_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 10; 63 public static final int EXPECTED_DEFAULT_SERVER_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 100; 64 public static final int EXPECTED_DEFAULT_SSL_SESSION_CACHE_TIMEOUT = 65 (IS_RI) ? 24 * 3600 : 8 * 3600; 66 67 /** 68 * The Android SSLSocket and SSLServerSocket implementations are 69 * based on a version of OpenSSL which includes support for RFC 70 * 4507 session tickets. When using session tickets, the server 71 * does not need to keep a cache mapping session IDs to SSL 72 * sessions for reuse. Instead, the client presents the server 73 * with a session ticket it received from the server earlier, 74 * which is an SSL session encrypted by the server's secret 75 * key. Since in this case the server does not need to keep a 76 * cache, some tests may find different results depending on 77 * whether or not the session tickets are in use. These tests can 78 * use this function to determine if loopback SSL connections are 79 * expected to use session tickets and conditionalize their 80 * results appropriately. 81 */ 82 public static boolean sslServerSocketSupportsSessionTickets () { 83 // Disabled session tickets for better compatability b/2682876 84 // return !IS_RI; 85 return false; 86 } 87 88 public final KeyStore clientKeyStore; 89 public final char[] clientStorePassword; 90 public final KeyStore serverKeyStore; 91 public final char[] serverStorePassword; 92 public final KeyManager[] clientKeyManagers; 93 public final KeyManager[] serverKeyManagers; 94 public final X509ExtendedTrustManager clientTrustManager; 95 public final X509ExtendedTrustManager serverTrustManager; 96 public final SSLContext clientContext; 97 public final SSLContext serverContext; 98 public final SSLServerSocket serverSocket; 99 public final InetAddress host; 100 public final int port; 101 102 /** 103 * Used for replacing the hostname in an InetSocketAddress object during 104 * serialization. 105 */ 106 private static class HostnameRewritingObjectOutputStream extends ObjectOutputStream { 107 private final String hostname; 108 109 public HostnameRewritingObjectOutputStream(OutputStream out, String hostname) 110 throws IOException { 111 super(out); 112 this.hostname = hostname; 113 } 114 115 @Override 116 public PutField putFields() throws IOException { 117 return new PutFieldProxy(super.putFields(), hostname); 118 } 119 120 private static class PutFieldProxy extends ObjectOutputStream.PutField { 121 private final PutField delegate; 122 private final String hostname; 123 124 public PutFieldProxy(ObjectOutputStream.PutField delegate, String hostname) { 125 this.delegate = delegate; 126 this.hostname = hostname; 127 } 128 129 @Override 130 public void put(String name, boolean val) { 131 delegate.put(name, val); 132 } 133 134 @Override 135 public void put(String name, byte val) { 136 delegate.put(name, val); 137 } 138 139 @Override 140 public void put(String name, char val) { 141 delegate.put(name, val); 142 } 143 144 @Override 145 public void put(String name, short val) { 146 delegate.put(name, val); 147 } 148 149 @Override 150 public void put(String name, int val) { 151 delegate.put(name, val); 152 } 153 154 @Override 155 public void put(String name, long val) { 156 delegate.put(name, val); 157 } 158 159 @Override 160 public void put(String name, float val) { 161 delegate.put(name, val); 162 } 163 164 @Override 165 public void put(String name, double val) { 166 delegate.put(name, val); 167 } 168 169 @Override 170 public void put(String name, Object val) { 171 if ("hostname".equals(name)) { 172 delegate.put(name, hostname); 173 } else { 174 delegate.put(name, val); 175 } 176 } 177 178 @SuppressWarnings("deprecation") 179 @Override 180 public void write(ObjectOutput out) throws IOException { 181 delegate.write(out); 182 } 183 } 184 } 185 186 /** 187 * Creates an InetSocketAddress where the hostname points to an arbitrary 188 * hostname, but the address points to the loopback address. Useful for 189 * testing SNI where both "localhost" and IP addresses are not allowed. 190 */ 191 public InetSocketAddress getLoopbackAsHostname(String hostname, int port) throws IOException, ClassNotFoundException { 192 InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), port); 193 194 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 195 HostnameRewritingObjectOutputStream oos = new HostnameRewritingObjectOutputStream(baos, hostname); 196 oos.writeObject(addr); 197 oos.close(); 198 199 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); 200 return (InetSocketAddress) ois.readObject(); 201 } 202 203 private TestSSLContext(KeyStore clientKeyStore, 204 char[] clientStorePassword, 205 KeyStore serverKeyStore, 206 char[] serverStorePassword, 207 KeyManager[] clientKeyManagers, 208 KeyManager[] serverKeyManagers, 209 X509ExtendedTrustManager clientTrustManager, 210 X509ExtendedTrustManager serverTrustManager, 211 SSLContext clientContext, 212 SSLContext serverContext, 213 SSLServerSocket serverSocket, 214 InetAddress host, 215 int port) { 216 this.clientKeyStore = clientKeyStore; 217 this.clientStorePassword = clientStorePassword; 218 this.serverKeyStore = serverKeyStore; 219 this.serverStorePassword = serverStorePassword; 220 this.clientKeyManagers = clientKeyManagers; 221 this.serverKeyManagers = serverKeyManagers; 222 this.clientTrustManager = clientTrustManager; 223 this.serverTrustManager = serverTrustManager; 224 this.clientContext = clientContext; 225 this.serverContext = serverContext; 226 this.serverSocket = serverSocket; 227 this.host = host; 228 this.port = port; 229 } 230 231 public void close() { 232 try { 233 serverSocket.close(); 234 } catch (Exception e) { 235 throw new RuntimeException(e); 236 } 237 } 238 239 /** 240 * Usual TestSSLContext creation method, creates underlying 241 * SSLContext with certificate and key as well as SSLServerSocket 242 * listening provided host and port. 243 */ 244 public static TestSSLContext create() { 245 return create(TestKeyStore.getClient(), 246 TestKeyStore.getServer()); 247 } 248 249 /** 250 * TestSSLContext creation method that allows separate creation of server key store 251 */ 252 public static TestSSLContext create(TestKeyStore client, TestKeyStore server) { 253 return createWithAdditionalKeyManagers(client, server, null, null); 254 } 255 256 /** 257 * TestSSLContext creation method that allows separate creation of server key store and 258 * the use of additional {@code KeyManager} instances 259 */ 260 public static TestSSLContext createWithAdditionalKeyManagers( 261 TestKeyStore client, TestKeyStore server, 262 KeyManager[] additionalClientKeyManagers, KeyManager[] additionalServerKeyManagers) { 263 String protocol = "TLSv1.2"; 264 KeyManager[] clientKeyManagers = concat(client.keyManagers, additionalClientKeyManagers); 265 KeyManager[] serverKeyManagers = concat(server.keyManagers, additionalServerKeyManagers); 266 SSLContext clientContext = 267 createSSLContext(protocol, clientKeyManagers, client.trustManagers); 268 SSLContext serverContext = 269 createSSLContext(protocol, serverKeyManagers, server.trustManagers); 270 return create(client.keyStore, client.storePassword, 271 server.keyStore, server.storePassword, 272 clientKeyManagers, 273 serverKeyManagers, 274 client.trustManagers[0], 275 server.trustManagers[0], 276 clientContext, 277 serverContext); 278 } 279 280 /** 281 * TestSSLContext creation method that allows separate creation of client and server key store 282 */ 283 public static TestSSLContext create(KeyStore clientKeyStore, char[] clientStorePassword, 284 KeyStore serverKeyStore, char[] serverStorePassword, 285 KeyManager[] clientKeyManagers, 286 KeyManager[] serverKeyManagers, 287 TrustManager clientTrustManagers, 288 TrustManager serverTrustManagers, 289 SSLContext clientContext, 290 SSLContext serverContext) { 291 try { 292 SSLServerSocket serverSocket = (SSLServerSocket) 293 serverContext.getServerSocketFactory().createServerSocket(0); 294 InetAddress host = InetAddress.getLocalHost(); 295 int port = serverSocket.getLocalPort(); 296 297 return new TestSSLContext(clientKeyStore, clientStorePassword, 298 serverKeyStore, serverStorePassword, 299 clientKeyManagers, 300 serverKeyManagers, 301 (X509ExtendedTrustManager) clientTrustManagers, 302 (X509ExtendedTrustManager) serverTrustManagers, 303 clientContext, serverContext, 304 serverSocket, host, port); 305 } catch (RuntimeException e) { 306 throw e; 307 } catch (Exception e) { 308 throw new RuntimeException(e); 309 } 310 } 311 312 /** 313 * Create a SSLContext with a KeyManager using the private key and 314 * certificate chain from the given KeyStore and a TrustManager 315 * using the certificates authorities from the same KeyStore. 316 */ 317 public static final SSLContext createSSLContext(final String protocol, 318 final KeyManager[] keyManagers, 319 final TrustManager[] trustManagers) 320 { 321 try { 322 SSLContext context = SSLContext.getInstance(protocol); 323 context.init(keyManagers, trustManagers, new SecureRandom()); 324 return context; 325 } catch (Exception e) { 326 throw new RuntimeException(e); 327 } 328 } 329 330 public static void assertCertificateInKeyStore(Principal principal, 331 KeyStore keyStore) throws Exception { 332 String subjectName = principal.getName(); 333 boolean found = false; 334 for (String alias: Collections.list(keyStore.aliases())) { 335 if (!keyStore.isCertificateEntry(alias)) { 336 continue; 337 } 338 X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias); 339 if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) { 340 found = true; 341 break; 342 } 343 } 344 assertTrue(found); 345 } 346 347 public static void assertCertificateInKeyStore(Certificate certificate, 348 KeyStore keyStore) throws Exception { 349 boolean found = false; 350 for (String alias: Collections.list(keyStore.aliases())) { 351 if (!keyStore.isCertificateEntry(alias)) { 352 continue; 353 } 354 Certificate keyStoreCertificate = keyStore.getCertificate(alias); 355 if (certificate.equals(keyStoreCertificate)) { 356 found = true; 357 break; 358 } 359 } 360 assertTrue(found); 361 } 362 363 public static void assertServerCertificateChain(X509TrustManager trustManager, 364 Certificate[] serverChain) 365 throws CertificateException { 366 X509Certificate[] chain = (X509Certificate[]) serverChain; 367 trustManager.checkServerTrusted(chain, chain[0].getPublicKey().getAlgorithm()); 368 } 369 370 public static void assertClientCertificateChain(X509TrustManager trustManager, 371 Certificate[] clientChain) 372 throws CertificateException { 373 X509Certificate[] chain = (X509Certificate[]) clientChain; 374 trustManager.checkClientTrusted(chain, chain[0].getPublicKey().getAlgorithm()); 375 } 376 377 /** 378 * Returns an SSLSocketFactory that calls setWantClientAuth and 379 * setNeedClientAuth as specified on all returned sockets. 380 */ 381 public static SSLSocketFactory clientAuth(final SSLSocketFactory sf, 382 final boolean want, 383 final boolean need) { 384 return new SSLSocketFactory() { 385 private SSLSocket set(Socket socket) { 386 SSLSocket s = (SSLSocket) socket; 387 s.setWantClientAuth(want); 388 s.setNeedClientAuth(need); 389 return s; 390 } 391 public Socket createSocket(String host, int port) 392 throws IOException, UnknownHostException { 393 return set(sf.createSocket(host, port)); 394 } 395 public Socket createSocket(String host, int port, InetAddress localHost, int localPort) 396 throws IOException, UnknownHostException { 397 return set(sf.createSocket(host, port, localHost, localPort)); 398 } 399 public Socket createSocket(InetAddress host, int port) throws IOException { 400 return set(sf.createSocket(host, port)); 401 } 402 public Socket createSocket(InetAddress address, int port, 403 InetAddress localAddress, int localPort) throws IOException { 404 return set(sf.createSocket(address, port)); 405 } 406 407 public String[] getDefaultCipherSuites() { 408 return sf.getDefaultCipherSuites(); 409 } 410 public String[] getSupportedCipherSuites() { 411 return sf.getSupportedCipherSuites(); 412 } 413 414 public Socket createSocket(Socket s, String host, int port, boolean autoClose) 415 throws IOException { 416 return set(sf.createSocket(s, host, port, autoClose)); 417 } 418 }; 419 } 420 421 private static KeyManager[] concat(KeyManager[] a, KeyManager[] b) { 422 if ((a == null) || (a.length == 0)) { 423 return b; 424 } 425 if ((b == null) || (b.length == 0)) { 426 return a; 427 } 428 KeyManager[] result = new KeyManager[a.length + b.length]; 429 System.arraycopy(a, 0, result, 0, a.length); 430 System.arraycopy(b, 0, result, a.length, b.length); 431 return result; 432 } 433} 434