diff options
author | Brian Carlstrom <bdc@google.com> | 2010-05-04 10:47:14 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-05-04 10:47:14 -0700 |
commit | f4c62bce9efbba1ded656acacb0695694a16d309 (patch) | |
tree | 6de77ce3c3ef091e1b474ce6b1efa9e7308e1c94 /support | |
parent | 4cb7f05dc68abb23ae54a5891c369062185f2210 (diff) | |
parent | bcfb325d5b1f9529b439cc0805a1c140521510f7 (diff) | |
download | libcore-f4c62bce9efbba1ded656acacb0695694a16d309.zip libcore-f4c62bce9efbba1ded656acacb0695694a16d309.tar.gz libcore-f4c62bce9efbba1ded656acacb0695694a16d309.tar.bz2 |
Merge "OpenSSLSocket handshake overhaul" into dalvik-dev
Diffstat (limited to 'support')
4 files changed, 490 insertions, 0 deletions
diff --git a/support/src/test/java/javax/net/ssl/StandardNames.java b/support/src/test/java/javax/net/ssl/StandardNames.java new file mode 100644 index 0000000..ccd3ee1 --- /dev/null +++ b/support/src/test/java/javax/net/ssl/StandardNames.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.net.ssl; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * This class defines expected string names for protocols, key types, client and server auth types, cipher suites. + * Based on documentation from http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA + */ +public final class StandardNames { + + public static final Set<String> SSL_CONTEXT_PROTOCOLS = new HashSet<String>(Arrays.asList( + "SSL", + "SSLv2", + "SSLv3", + "TLS", + "TLSv1")); + + public static final Set<String> KEY_TYPES = new HashSet<String>(Arrays.asList( + "RSA", + "DSA", + "DH_RSA", + "DH_DSA")); + + public static final Set<String> SSL_SOCKET_PROTOCOLS = new HashSet<String>(Arrays.asList( + "SSLv2", + "SSLv3", + "TLSv1", + "SSLv2Hello")); + + public static final Set<String> CLIENT_AUTH_TYPES = new HashSet<String>(KEY_TYPES); + + public static final Set<String> SERVER_AUTH_TYPES = new HashSet<String>(Arrays.asList( + "DHE_DSS", + "DHE_DSS_EXPORT", + "DHE_RSA", + "DHE_RSA_EXPORT", + "DH_DSS_EXPORT", + "DH_RSA_EXPORT", + "DH_anon", + "DH_anon_EXPORT", + "KRB5", + "KRB5_EXPORT", + "RSA", + "RSA_EXPORT", + "RSA_EXPORT1024", + "UNKNOWN")); + + // removed cipher suites not actually found in RI + public static final Set<String> CIPHER_SUITES = new HashSet<String>(Arrays.asList( + "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + "SSL_DHE_DSS_WITH_DES_CBC_SHA", + "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", + "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", + "SSL_DHE_RSA_WITH_DES_CBC_SHA", + //"SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", + //"SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", + "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", + "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", + "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", + "SSL_DH_anon_WITH_DES_CBC_SHA", + "SSL_DH_anon_WITH_RC4_128_MD5", + //"SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA", + //"SSL_RSA_EXPORT1024_WITH_RC4_56_SHA", + "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", + //"SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", + "SSL_RSA_EXPORT_WITH_RC4_40_MD5", + "SSL_RSA_WITH_3DES_EDE_CBC_SHA", + "SSL_RSA_WITH_DES_CBC_SHA", + "SSL_RSA_WITH_NULL_MD5", + "SSL_RSA_WITH_NULL_SHA", + "SSL_RSA_WITH_RC4_128_MD5", + "SSL_RSA_WITH_RC4_128_SHA", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + //"TLS_DHE_DSS_WITH_AES_256_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + //"TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_DH_anon_WITH_AES_128_CBC_SHA", + //"TLS_DH_anon_WITH_AES_256_CBC_SHA", + "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", + "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", + //"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", + //"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", + "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", + "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", + "TLS_KRB5_WITH_3DES_EDE_CBC_MD5", + "TLS_KRB5_WITH_3DES_EDE_CBC_SHA", + "TLS_KRB5_WITH_DES_CBC_MD5", + "TLS_KRB5_WITH_DES_CBC_SHA", + "TLS_KRB5_WITH_RC4_128_MD5", + "TLS_KRB5_WITH_RC4_128_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA")); + //"TLS_RSA_WITH_AES_256_CBC_SHA")); +} diff --git a/support/src/test/java/javax/net/ssl/TestSSLContext.java b/support/src/test/java/javax/net/ssl/TestSSLContext.java new file mode 100644 index 0000000..44b21c9 --- /dev/null +++ b/support/src/test/java/javax/net/ssl/TestSSLContext.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.net.ssl; + +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; +import java.util.Hashtable; +import org.bouncycastle.jce.X509Principal; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.x509.X509V3CertificateGenerator; + +/** + * TestSSLContext is a convenience class for other tests that + * want a canned SSLContext and related state for testing so they + * don't have to duplicate the logic. + */ +public final class TestSSLContext { + + public static final boolean IS_RI = !"Dalvik Core Library".equals(System.getProperty("java.specification.name")); + public static final String PROVIDER_NAME = (IS_RI) ? "SunJSSE" : "HarmonyJSSE"; + + static { + if (IS_RI) { + Security.addProvider(new BouncyCastleProvider()); + } + } + + /** + * The Android SSLSocket and SSLServerSocket implementations are + * based on a version of OpenSSL which includes support for RFC + * 4507 session tickets. When using session tickets, the server + * does not need to keep a cache mapping session IDs to SSL + * sessions for reuse. Instead, the client presents the server + * with a session ticket it received from the server earlier, + * which is an SSL session encrypted by the server's secret + * key. Since in this case the server does not need to keep a + * cache, some tests may find different results depending on + * whether or not the session tickets are in use. These tests can + * use this function to determine if loopback SSL connections are + * expected to use session tickets and conditionalize their + * results appropriately. + */ + public static boolean sslServerSocketSupportsSessionTickets () { + return !IS_RI; + } + + public final KeyStore keyStore; + public final char[] keyStorePassword; + public final String publicAlias; + public final String privateAlias; + public final SSLContext sslContext; + public final SSLServerSocket serverSocket; + public final InetAddress host; + public final int port; + + private TestSSLContext(KeyStore keyStore, + char[] keyStorePassword, + String publicAlias, + String privateAlias, + SSLContext sslContext, + SSLServerSocket serverSocket, + InetAddress host, + int port) { + this.keyStore = keyStore; + this.keyStorePassword = keyStorePassword; + this.publicAlias = publicAlias; + this.privateAlias = privateAlias; + this.sslContext = sslContext; + this.serverSocket = serverSocket; + this.host = host; + this.port = port; + } + + public static TestSSLContext create() { + try { + char[] keyStorePassword = null; + String publicAlias = "public"; + String privateAlias = "private"; + return create(createKeyStore(keyStorePassword, publicAlias, privateAlias), + null, + publicAlias, + privateAlias); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static TestSSLContext create(KeyStore keyStore, + char[] keyStorePassword, + String publicAlias, + String privateAlias) { + try { + SSLContext sslContext = createSSLContext(keyStore, keyStorePassword); + + SSLServerSocket serverSocket = (SSLServerSocket) + sslContext.getServerSocketFactory().createServerSocket(0); + InetSocketAddress sa = (InetSocketAddress) serverSocket.getLocalSocketAddress(); + InetAddress host = sa.getAddress(); + int port = sa.getPort(); + + return new TestSSLContext(keyStore, keyStorePassword, publicAlias, privateAlias, + sslContext, serverSocket, host, port); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Create a BKS KeyStore containing an RSAPrivateKey with alias + * "private" and a X509Certificate based on the matching + * RSAPublicKey stored under the alias name publicAlias. + * + * The private key will have a certificate chain including the + * certificate stored under the alias name privateAlias. The + * certificate will be signed by the private key. The certificate + * Subject and Issuer Common-Name will be the local host's + * canonical hostname. The certificate will be valid for one day + * before and one day after the time of creation. + * + * The KeyStore is optionally password protected by the + * keyStorePassword argument, which can be null if a password is + * not desired. + * + * Based on: + * org.bouncycastle.jce.provider.test.SigTest + * org.bouncycastle.jce.provider.test.CertTest + */ + public static KeyStore createKeyStore(char[] keyStorePassword, + String publicAlias, + String privateAlias) + throws Exception { + + // 1.) we make the keys + int keysize = 1024; + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(keysize, new SecureRandom()); + KeyPair kp = kpg.generateKeyPair(); + RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate(); + RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic(); + + // 2.) use keys to make certficate + + // note that there doesn't seem to be a standard way to make a + // certificate using java.* or javax.*. The CertificateFactory + // interface assumes you want to read in a stream of bytes a + // factory specific format. So here we use Bouncy Castle's + // X509V3CertificateGenerator and related classes. + + Hashtable attributes = new Hashtable(); + attributes.put(X509Principal.CN, InetAddress.getLocalHost().getCanonicalHostName()); + X509Principal dn = new X509Principal(attributes); + + long millisPerDay = 24 * 60 * 60 * 1000; + long now = System.currentTimeMillis(); + Date start = new Date(now - millisPerDay); + Date end = new Date(now + millisPerDay); + BigInteger serial = BigInteger.valueOf(1); + + X509V3CertificateGenerator x509cg = new X509V3CertificateGenerator(); + x509cg.setSubjectDN(dn); + x509cg.setIssuerDN(dn); + x509cg.setNotBefore(start); + x509cg.setNotAfter(end); + x509cg.setPublicKey(publicKey); + x509cg.setSignatureAlgorithm("sha1WithRSAEncryption"); + x509cg.setSerialNumber(serial); + X509Certificate x509c = x509cg.generateX509Certificate(privateKey); + X509Certificate[] x509cc = new X509Certificate[] { x509c }; + + + // 3.) put certificate and private key to make a key store + KeyStore ks = KeyStore.getInstance("BKS"); + ks.load(null, null); + ks.setKeyEntry(privateAlias, privateKey, keyStorePassword, x509cc); + ks.setCertificateEntry(publicAlias, x509c); + return ks; + } + + /** + * Create a SSLContext with a KeyManager using the private key and + * certificate chain from the given KeyStore and a TrustManager + * using the certificates authorities from the same KeyStore. + */ + public static final SSLContext createSSLContext(final KeyStore keyStore, final char[] keyStorePassword) + throws Exception { + String kmfa = KeyManagerFactory.getDefaultAlgorithm(); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa); + kmf.init(keyStore, keyStorePassword); + + String tmfa = TrustManagerFactory.getDefaultAlgorithm(); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa); + tmf.init(keyStore); + + SSLContext context = SSLContext.getInstance("TLS"); + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); + return context; + } +} diff --git a/support/src/test/java/javax/net/ssl/TestSSLSessions.java b/support/src/test/java/javax/net/ssl/TestSSLSessions.java new file mode 100644 index 0000000..061a7f2 --- /dev/null +++ b/support/src/test/java/javax/net/ssl/TestSSLSessions.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.net.ssl; + +/** + * TestSSLSessions is a convenience class for other tests that want + * precreated SSLSessions for testing. It contains a connected + * client/server pair of SSLSession as well as an invalid SSLSession. + */ +public final class TestSSLSessions { + + /** + * An invalid session that is not connected + */ + public final SSLSession invalid; + + /** + * The server side of a connected session + */ + public final SSLSession server; + + /** + * The client side of a connected session + */ + public final SSLSession client; + + /** + * The associated SSLSocketTest.Helper that is the source of + * the client and server SSLSessions. + */ + public final TestSSLSocketPair s; + + private TestSSLSessions(SSLSession invalid, + SSLSession server, + SSLSession client, + TestSSLSocketPair s) { + this.invalid = invalid; + this.server = server; + this.client = client; + this.s = s; + } + + public static final TestSSLSessions create() { + try { + SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault(); + SSLSocket ssl = (SSLSocket) sf.createSocket(); + SSLSession invalid = ssl.getSession(); + TestSSLSocketPair s = TestSSLSocketPair.create_workaround(); + return new TestSSLSessions(invalid, s.server.getSession(), s.client.getSession(), s); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/support/src/test/java/javax/net/ssl/TestSSLSocketPair.java b/support/src/test/java/javax/net/ssl/TestSSLSocketPair.java new file mode 100644 index 0000000..6347877 --- /dev/null +++ b/support/src/test/java/javax/net/ssl/TestSSLSocketPair.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.net.ssl; + +/** + * TestSSLSocketPair is a convenience class for other tests that want + * a pair of connected and handshaked client and server SSLSockets for + * testing. + */ +public final class TestSSLSocketPair { + public final TestSSLContext c; + public final SSLSocket server; + public final SSLSocket client; + + private TestSSLSocketPair (TestSSLContext c, + SSLSocket server, + SSLSocket client) { + this.c = c; + this.server = server; + this.client = client; + } + + /** + * based on test_SSLSocket_startHandshake_workaround, should + * be written to non-workaround form when possible + */ + public static TestSSLSocketPair create_workaround () { + TestSSLContext c = TestSSLContext.create(); + SSLSocket[] sockets = connect_workaround(c, null); + return new TestSSLSocketPair(c, sockets[0], sockets[1]); + } + + /** + * Create a new connected server/client socket pair within a + * existing SSLContext. Optional clientCipherSuites allows + * forcing new SSLSession to test SSLSessionContext caching + */ + public static SSLSocket[] connect_workaround (final TestSSLContext c, + String[] clientCipherSuites) { + try { + final SSLSocket[] server = new SSLSocket[1]; + Thread thread = new Thread(new Runnable () { + public void run() { + try { + server[0] = (SSLSocket) c.serverSocket.accept(); + server[0].startHandshake(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + thread.start(); + SSLSocket client = (SSLSocket) + c.sslContext.getSocketFactory().createSocket(c.host, c.port); + if (clientCipherSuites != null) { + client.setEnabledCipherSuites(clientCipherSuites); + } + client.startHandshake(); + thread.join(); + return new SSLSocket[] { server[0], client }; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} + |