TestSSLContext.java revision 9a106a63508697a6f5f02c20b7cc6b7c6152695f
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 javax.net.ssl; 18 19import java.net.InetAddress; 20import java.net.InetSocketAddress; 21import java.security.KeyStore; 22import java.security.Principal; 23import java.security.SecureRandom; 24import java.security.Security; 25import java.security.StandardNames; 26import java.security.cert.Certificate; 27import java.security.cert.X509Certificate; 28import java.util.Collections; 29import junit.framework.Assert; 30import org.bouncycastle.jce.provider.BouncyCastleProvider; 31 32/** 33 * TestSSLContext is a convenience class for other tests that 34 * want a canned SSLContext and related state for testing so they 35 * don't have to duplicate the logic. 36 */ 37public final class TestSSLContext extends Assert { 38 39 /* 40 * The RI and Android have very different default SSLSession cache behaviors. 41 * The RI keeps an unlimited number of SSLSesions around for 1 day. 42 * Android keeps 10 SSLSessions forever. 43 */ 44 private static final boolean IS_RI = StandardNames.IS_RI; 45 public static final int EXPECTED_DEFAULT_CLIENT_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 10; 46 public static final int EXPECTED_DEFAULT_SERVER_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 100; 47 public static final int EXPECTED_DEFAULT_SSL_SESSION_CACHE_TIMEOUT = (IS_RI) ? 86400 : 0; 48 static { 49 if (IS_RI) { 50 Security.addProvider(new BouncyCastleProvider()); 51 } 52 } 53 54 /** 55 * The Android SSLSocket and SSLServerSocket implementations are 56 * based on a version of OpenSSL which includes support for RFC 57 * 4507 session tickets. When using session tickets, the server 58 * does not need to keep a cache mapping session IDs to SSL 59 * sessions for reuse. Instead, the client presents the server 60 * with a session ticket it received from the server earlier, 61 * which is an SSL session encrypted by the server's secret 62 * key. Since in this case the server does not need to keep a 63 * cache, some tests may find different results depending on 64 * whether or not the session tickets are in use. These tests can 65 * use this function to determine if loopback SSL connections are 66 * expected to use session tickets and conditionalize their 67 * results appropriately. 68 */ 69 public static boolean sslServerSocketSupportsSessionTickets () { 70 // Disabled session tickets for better compatability b/2682876 71 // return !IS_RI; 72 return false; 73 } 74 75 public final KeyStore keyStore; 76 public final char[] keyStorePassword; 77 public final SSLContext sslContext; 78 public final SSLServerSocket serverSocket; 79 public final InetAddress host; 80 public final int port; 81 82 private TestSSLContext(KeyStore keyStore, 83 char[] keyStorePassword, 84 SSLContext sslContext, 85 SSLServerSocket serverSocket, 86 InetAddress host, 87 int port) { 88 this.keyStore = keyStore; 89 this.keyStorePassword = keyStorePassword; 90 this.sslContext = sslContext; 91 this.serverSocket = serverSocket; 92 this.host = host; 93 this.port = port; 94 } 95 96 /** 97 * Usual TestSSLContext creation method, creates underlying 98 * SSLContext with certificate and key as well as SSLServerSocket 99 * listening provided host and port. 100 */ 101 public static TestSSLContext create() { 102 TestKeyStore testKeyStore = TestKeyStore.get(); 103 return create(testKeyStore.keyStore, testKeyStore.keyStorePassword); 104 } 105 106 /** 107 * TestSSLContext creation method that allows separate creation of key store 108 */ 109 public static TestSSLContext create(KeyStore keyStore, char[] keyStorePassword) { 110 try { 111 SSLContext sslContext = createSSLContext(keyStore, keyStorePassword); 112 113 SSLServerSocket serverSocket = (SSLServerSocket) 114 sslContext.getServerSocketFactory().createServerSocket(0); 115 InetSocketAddress sa = (InetSocketAddress) serverSocket.getLocalSocketAddress(); 116 InetAddress host = sa.getAddress(); 117 int port = sa.getPort(); 118 119 return new TestSSLContext(keyStore, keyStorePassword, 120 sslContext, serverSocket, host, port); 121 } catch (RuntimeException e) { 122 throw e; 123 } catch (Exception e) { 124 throw new RuntimeException(e); 125 } 126 } 127 128 /** 129 * Create a client version of the server TestSSLContext. The 130 * client will trust the server's certificate, but not contain any 131 * keys of its own. 132 */ 133 public static TestSSLContext createClient(TestSSLContext server) { 134 try { 135 KeyStore keyStore = KeyStore.getInstance("BKS"); 136 keyStore.load(null, null); 137 for (String alias: Collections.list(server.keyStore.aliases())) { 138 if (!server.keyStore.isCertificateEntry(alias)) { 139 continue; 140 } 141 Certificate cert = server.keyStore.getCertificate(alias); 142 keyStore.setCertificateEntry(alias, cert); 143 } 144 145 char[] keyStorePassword = server.keyStorePassword; 146 147 String tmfa = TrustManagerFactory.getDefaultAlgorithm(); 148 TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa); 149 tmf.init(keyStore); 150 151 SSLContext sslContext = SSLContext.getInstance("TLS"); 152 sslContext.init(null, tmf.getTrustManagers(), new SecureRandom()); 153 154 return new TestSSLContext(keyStore, keyStorePassword, 155 sslContext, null, null, -1); 156 } catch (RuntimeException e) { 157 throw e; 158 } catch (Exception e) { 159 throw new RuntimeException(e); 160 } 161 } 162 163 /** 164 * Create a SSLContext with a KeyManager using the private key and 165 * certificate chain from the given KeyStore and a TrustManager 166 * using the certificates authorities from the same KeyStore. 167 */ 168 public static final SSLContext createSSLContext(final KeyStore keyStore, 169 final char[] keyStorePassword) 170 throws Exception { 171 String kmfa = KeyManagerFactory.getDefaultAlgorithm(); 172 KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa); 173 kmf.init(keyStore, keyStorePassword); 174 175 String tmfa = TrustManagerFactory.getDefaultAlgorithm(); 176 TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa); 177 tmf.init(keyStore); 178 179 SSLContext context = SSLContext.getInstance("TLS"); 180 context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); 181 return context; 182 } 183 184 public static void assertCertificateInKeyStore(Principal principal, 185 KeyStore keyStore) throws Exception { 186 String subjectName = principal.getName(); 187 boolean found = false; 188 for (String alias: Collections.list(keyStore.aliases())) { 189 if (!keyStore.isCertificateEntry(alias)) { 190 continue; 191 } 192 X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias); 193 if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) { 194 found = true; 195 break; 196 } 197 } 198 assertTrue(found); 199 } 200 201 public static void assertCertificateInKeyStore(Certificate certificate, 202 KeyStore keyStore) throws Exception { 203 boolean found = false; 204 for (String alias: Collections.list(keyStore.aliases())) { 205 if (!keyStore.isCertificateEntry(alias)) { 206 continue; 207 } 208 Certificate keyStoreCertificate = keyStore.getCertificate(alias); 209 if (certificate.equals(keyStoreCertificate)) { 210 found = true; 211 break; 212 } 213 } 214 assertTrue(found); 215 } 216} 217