diff options
author | Neil Fuller <nfuller@google.com> | 2014-10-24 08:14:27 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-10-24 08:14:28 +0000 |
commit | ed7443236cd820032b3c44f1db34ee384eede59b (patch) | |
tree | f0f0cb4cba57d7d7141bf1e630b1b80dacebafd6 /luni/src/test | |
parent | d2ad744e930a855fbfa63e3f31f31bc391c37a50 (diff) | |
parent | 7618d0b86f53ada960898d531e53e1980914539f (diff) | |
download | libcore-ed7443236cd820032b3c44f1db34ee384eede59b.zip libcore-ed7443236cd820032b3c44f1db34ee384eede59b.tar.gz libcore-ed7443236cd820032b3c44f1db34ee384eede59b.tar.bz2 |
Merge "Changes to support TLS fallback SCSV"
Diffstat (limited to 'luni/src/test')
-rw-r--r-- | luni/src/test/java/libcore/java/net/URLConnectionTest.java | 226 |
1 files changed, 220 insertions, 6 deletions
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java index 335683e..40c9f5e 100644 --- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java +++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java @@ -36,6 +36,7 @@ import java.net.PasswordAuthentication; import java.net.ProtocolException; import java.net.Proxy; import java.net.ResponseCache; +import java.net.Socket; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URL; @@ -56,15 +57,18 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +import javax.net.SocketFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import libcore.java.security.StandardNames; import libcore.java.security.TestKeyStore; import libcore.java.util.AbstractResourceLeakageDetectorTestCase; import libcore.javax.net.ssl.TestSSLContext; @@ -2186,13 +2190,24 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest public void testSslFallback() throws Exception { TestSSLContext testSSLContext = TestSSLContext.create(); - server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); + + // This server socket factory only supports SSLv3. This is to avoid issues due to SCSV + // checks. See https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 + SSLSocketFactory serverSocketFactory = + new LimitedProtocolsSocketFactory( + testSSLContext.serverContext.getSocketFactory(), + "SSLv3"); + + server.useHttps(serverSocketFactory, false); server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE)); server.enqueue(new MockResponse().setBody("This required a 2nd handshake")); server.play(); HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); - connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); + // Keep track of the client sockets created so that we can interrogate them. + RecordingSocketFactory clientSocketFactory = + new RecordingSocketFactory(testSSLContext.clientContext.getSocketFactory()); + connection.setSSLSocketFactory(clientSocketFactory); assertEquals("This required a 2nd handshake", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); @@ -2201,6 +2216,26 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest RecordedRequest retry = server.takeRequest(); assertEquals(0, retry.getSequenceNumber()); assertEquals("SSLv3", retry.getSslProtocol()); + + // Confirm the client fallback looks ok. + List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets(); + assertEquals(2, createdSockets.size()); + SSLSocket clientSocket1 = createdSockets.get(0); + List<String> clientSocket1EnabledProtocols = Arrays.asList( + clientSocket1.getEnabledProtocols()); + assertContains(clientSocket1EnabledProtocols, "TLSv1.2"); + List<String> clientSocket1EnabledCiphers = + Arrays.asList(clientSocket1.getEnabledCipherSuites()); + assertContainsNoneMatching( + clientSocket1EnabledCiphers, StandardNames.CIPHER_SUITE_FALLBACK); + + SSLSocket clientSocket2 = createdSockets.get(1); + List<String> clientSocket2EnabledProtocols = + Arrays.asList(clientSocket2.getEnabledProtocols()); + assertContainsNoneMatching(clientSocket2EnabledProtocols, "TLSv1.2"); + List<String> clientSocket2EnabledCiphers = + Arrays.asList(clientSocket2.getEnabledCipherSuites()); + assertContains(clientSocket2EnabledCiphers, StandardNames.CIPHER_SUITE_FALLBACK); } public void testInspectSslBeforeConnect() throws Exception { @@ -2284,12 +2319,12 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest assertContent(expected, connection, Integer.MAX_VALUE); } - private void assertContains(List<String> headers, String header) { - assertTrue(headers.toString(), headers.contains(header)); + private void assertContains(List<String> list, String value) { + assertTrue(list.toString(), list.contains(value)); } - private void assertContainsNoneMatching(List<String> headers, String pattern) { - for (String header : headers) { + private void assertContainsNoneMatching(List<String> list, String pattern) { + for (String header : list) { if (header.matches(pattern)) { fail("Header " + header + " matches " + pattern); } @@ -2438,4 +2473,183 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest : null; } } + + /** + * An SSLSocketFactory that delegates all calls. + */ + private static class DelegatingSSLSocketFactory extends SSLSocketFactory { + + protected final SSLSocketFactory delegate; + + public DelegatingSSLSocketFactory(SSLSocketFactory delegate) { + this.delegate = delegate; + } + + @Override + public String[] getDefaultCipherSuites() { + return delegate.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return delegate.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + return delegate.createSocket(s, host, port, autoClose); + } + + @Override + public Socket createSocket() throws IOException { + return delegate.createSocket(); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return delegate.createSocket(host, port); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, + int localPort) throws IOException, UnknownHostException { + return delegate.createSocket(host, port, localHost, localPort); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return delegate.createSocket(host, port); + } + + @Override + public Socket createSocket(InetAddress address, int port, + InetAddress localAddress, int localPort) throws IOException { + return delegate.createSocket(address, port, localAddress, localPort); + } + + } + + /** + * An SSLSocketFactory that delegates calls but limits the enabled protocols for any created + * sockets. + */ + private static class LimitedProtocolsSocketFactory extends DelegatingSSLSocketFactory { + + private final String[] protocols; + + private LimitedProtocolsSocketFactory(SSLSocketFactory delegate, String... protocols) { + super(delegate); + this.protocols = protocols; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + SSLSocket socket = (SSLSocket) delegate.createSocket(s, host, port, autoClose); + socket.setEnabledProtocols(protocols); + return socket; + } + + @Override + public Socket createSocket() throws IOException { + SSLSocket socket = (SSLSocket) delegate.createSocket(); + socket.setEnabledProtocols(protocols); + return socket; + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + SSLSocket socket = (SSLSocket) delegate.createSocket(host, port); + socket.setEnabledProtocols(protocols); + return socket; + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, + int localPort) throws IOException, UnknownHostException { + SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort); + socket.setEnabledProtocols(protocols); + return socket; + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + SSLSocket socket = (SSLSocket) delegate.createSocket(host, port); + socket.setEnabledProtocols(protocols); + return socket; + } + + @Override + public Socket createSocket(InetAddress address, int port, + InetAddress localAddress, int localPort) throws IOException { + SSLSocket socket = + (SSLSocket) delegate.createSocket(address, port, localAddress, localPort); + socket.setEnabledProtocols(protocols); + return socket; + } + } + + /** + * An SSLSocketFactory that delegates calls and keeps a record of any sockets created. + */ + private static class RecordingSocketFactory extends DelegatingSSLSocketFactory { + + private final List<SSLSocket> createdSockets = new ArrayList<SSLSocket>(); + + private RecordingSocketFactory(SSLSocketFactory delegate) { + super(delegate); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + SSLSocket socket = (SSLSocket) delegate.createSocket(s, host, port, autoClose); + createdSockets.add(socket); + return socket; + } + + @Override + public Socket createSocket() throws IOException { + SSLSocket socket = (SSLSocket) delegate.createSocket(); + createdSockets.add(socket); + return socket; + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + SSLSocket socket = (SSLSocket) delegate.createSocket(host, port); + createdSockets.add(socket); + return socket; + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, + int localPort) throws IOException, UnknownHostException { + SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort); + createdSockets.add(socket); + return socket; + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + SSLSocket socket = (SSLSocket) delegate.createSocket(host, port); + createdSockets.add(socket); + return socket; + } + + @Override + public Socket createSocket(InetAddress address, int port, + InetAddress localAddress, int localPort) throws IOException { + SSLSocket socket = + (SSLSocket) delegate.createSocket(address, port, localAddress, localPort); + createdSockets.add(socket); + return socket; + } + + public List<SSLSocket> getCreatedSockets() { + return createdSockets; + } + } + } |