From f8903866cf1c05ab9451e6e665f6442b49770df0 Mon Sep 17 00:00:00 2001 From: Matt Hildebrand Date: Sat, 16 Jan 2016 19:22:41 -0500 Subject: [PATCH 1/3] Add some initial integration tests for GRPC's TLS support. --- interop-testing/build.gradle | 1 + .../testing/integration/echo_service.proto | 50 ++++ .../io/grpc/testing/integration/TlsTest.java | 271 ++++++++++++++++++ .../main/java/io/grpc/testing/TestUtils.java | 21 +- testing/src/main/resources/certs/README | 14 + .../main/resources/certs/localhost_server.key | 16 ++ .../main/resources/certs/localhost_server.pem | 18 ++ 7 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 interop-testing/src/main/proto/io/grpc/testing/integration/echo_service.proto create mode 100644 interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java create mode 100644 testing/src/main/resources/certs/localhost_server.key create mode 100644 testing/src/main/resources/certs/localhost_server.pem diff --git a/interop-testing/build.gradle b/interop-testing/build.gradle index fdc524ac12c..edeff2335cf 100644 --- a/interop-testing/build.gradle +++ b/interop-testing/build.gradle @@ -23,6 +23,7 @@ dependencies { project(':grpc-testing'), libraries.junit, libraries.mockito, + libraries.netty_tcnative, libraries.oauth_client } diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/echo_service.proto b/interop-testing/src/main/proto/io/grpc/testing/integration/echo_service.proto new file mode 100644 index 00000000000..4d62ebf0841 --- /dev/null +++ b/interop-testing/src/main/proto/io/grpc/testing/integration/echo_service.proto @@ -0,0 +1,50 @@ + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A dummy GRPC service for use in tests. +syntax = "proto3"; + +package grpc.testing; + +option java_package = "io.grpc.testing.integration"; + + +message EchoRequest { + string text = 1; +} + +message EchoResponse { + string text = 1; +} + + +service EchoService { + rpc Echo (EchoRequest) returns (EchoResponse); +} diff --git a/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java b/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java new file mode 100644 index 00000000000..9750db82b4f --- /dev/null +++ b/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java @@ -0,0 +1,271 @@ +/* + * Copyright 2014, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.testing.integration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.NegotiationType; +import io.grpc.netty.NettyChannelBuilder; +import io.grpc.netty.NettyServerBuilder; +import io.grpc.testing.TestUtils; +import io.grpc.testing.integration.EchoServiceGrpc.EchoServiceBlockingStub; +import io.grpc.testing.integration.EchoServiceOuterClass.EchoRequest; +import io.grpc.testing.integration.EchoServiceOuterClass.EchoResponse; +import io.netty.handler.ssl.ClientAuth; +import io.netty.handler.ssl.OpenSsl; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.IOException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLException; + +/** + * Integration tests for GRPC's TLS support. + */ +// TODO: Use @RunWith(Parameterized.class) to run these tests for all TLS providers. +@RunWith(JUnit4.class) +public class TlsTest { + private static class DummyEchoRpcService implements EchoServiceGrpc.EchoService { + @Override + public void echo(EchoRequest request, StreamObserver responseObserver) { + EchoResponse response = EchoResponse.newBuilder() + .setText("Request said: " + request.getText()) + .build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + } + + + private static final File TESTDATA_ROOT = new File("src/test/resources/pki"); + + + /** + * Tests that a client and a server configured using GrpcSslContexts can successfully + * communicate with each other. + */ + @Test + public void basicClientServerIntegrationTest() throws Exception { + int port = TestUtils.pickUnusedPort(); + + // Create & start a server. + File serverCertFile = TestUtils.loadCert("localhost_server.pem"); + File serverPrivateKeyFile = TestUtils.loadCert("localhost_server.key"); + X509Certificate[] serverTrustedCaCerts = { + TestUtils.loadX509Cert("ca.pem") + }; + Server server = serverBuilder(port, serverCertFile, serverPrivateKeyFile, serverTrustedCaCerts) + .addService(EchoServiceGrpc.bindService(new DummyEchoRpcService())) + .build() + .start(); + + try { + // Create a client. + File clientCertFile = TestUtils.loadCert("client.pem"); + File clientPrivateKeyFile = TestUtils.loadCert("client.key"); + X509Certificate[] clientTrustedCaCerts = { + TestUtils.loadX509Cert("ca.pem") + }; + ManagedChannel channel = clientChannel("localhost", port, clientCertFile, + clientPrivateKeyFile, clientTrustedCaCerts); + EchoServiceBlockingStub client = EchoServiceGrpc.newBlockingStub(channel); + + // Send an actual request, via the full GRPC & network stack, and check that a proper + // response comes back. + EchoRequest request = EchoRequest.newBuilder() + .setText("dummy text") + .build(); + EchoResponse response = client.echo(request); + assertEquals("Request said: dummy text", response.getText()); + } finally { + server.shutdown(); + } + } + + + /** + * Tests that a server configured to require client authentication refuses to accept connections + * from a client that has an untrusted certificate. + */ + @Test + public void serverRejectsUntrustedClientCert() throws Exception { + int port = TestUtils.pickUnusedPort(); + + // Create & start a server. It requires client authentication and trusts only the test CA. + File serverCertFile = TestUtils.loadCert("localhost_server.pem"); + File serverPrivateKeyFile = TestUtils.loadCert("localhost_server.key"); + X509Certificate[] serverTrustedCaCerts = { + TestUtils.loadX509Cert("ca.pem") + }; + Server server = serverBuilder(port, serverCertFile, serverPrivateKeyFile, serverTrustedCaCerts) + .addService(EchoServiceGrpc.bindService(new DummyEchoRpcService())) + .build() + .start(); + + try { + // Create a client. Its credentials come from a CA that the server does not trust. The client + // trusts both test CAs, so we can be sure that the handshake failure is due to the server + // rejecting the client's cert, not the client rejecting the server's cert. + File clientCertFile = TestUtils.loadCert("badclient.pem"); + File clientPrivateKeyFile = TestUtils.loadCert("badclient.key"); + X509Certificate[] clientTrustedCaCerts = { + TestUtils.loadX509Cert("ca.pem"), + TestUtils.loadX509Cert("badclient.pem") // Cert is self-signed, and so is its own issuer. + }; + ManagedChannel channel = clientChannel("localhost", port, clientCertFile, clientPrivateKeyFile, clientTrustedCaCerts); + EchoServiceBlockingStub client = EchoServiceGrpc.newBlockingStub(channel); + + // Check that the TLS handshake fails. + EchoRequest request = EchoRequest.newBuilder() + .setText("dummy text") + .build(); + try { + EchoResponse response = client.echo(request); + fail("TLS handshake should have failed, but didn't; received RPC response: " + response); + } catch (StatusRuntimeException e) { + // GRPC reports this situation by throwing a StatusRuntimeException that wraps either a + // javax.net.ssl.SSLHandshakeException or a java.nio.channels.ClosedChannelException. + // Thus, reliably detecting the underlying cause is not feasible. + assertEquals(Status.Code.UNAVAILABLE, e.getStatus().getCode()); + } + } finally { + server.shutdown(); + } + } + + + /** + * Tests that a server configured to require client authentication actually does require client + * authentication. + */ + @Test + public void noClientAuthFailure() throws Exception { + int port = TestUtils.pickUnusedPort(); + + // Create & start a server. + File serverCertFile = TestUtils.loadCert("localhost_server.pem"); + File serverPrivateKeyFile = TestUtils.loadCert("localhost_server.key"); + X509Certificate[] serverTrustedCaCerts = { + TestUtils.loadX509Cert("ca.pem") + }; + Server server = serverBuilder(port, serverCertFile, serverPrivateKeyFile, serverTrustedCaCerts) + .addService(EchoServiceGrpc.bindService(new DummyEchoRpcService())) + .build() + .start(); + + try { + // Create a client. It has no credentials. + ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", port) + .negotiationType(NegotiationType.TLS) + .build(); + EchoServiceBlockingStub client = EchoServiceGrpc.newBlockingStub(channel); + + // Check that the TLS handshake fails. + EchoRequest request = EchoRequest.newBuilder() + .setText("dummy text") + .build(); + try { + EchoResponse response = client.echo(request); + fail("TLS handshake should have failed, but didn't; received RPC response: " + response); + } catch (Exception expected) { + assertTlsException(expected); + } + } finally { + server.shutdown(); + } + } + + + /** + * Checks that 'e' either *is* an SSLException or *has* such an exception somewhere in its cause + * chain. + */ + private static void assertTlsException(Exception e) { + Throwable t = e; + while (t != null) { + if (t instanceof SSLException) { + return; + } + t = t.getCause(); + } + + fail("Error not caused by SSLException: " + e.toString()); + } + + + private static ServerBuilder serverBuilder(int port, File serverCertChainFile, + File serverPrivateKeyFile, + X509Certificate[] serverTrustedCaCerts) + throws IOException { + SslContext sslContext = GrpcSslContexts.forServer(serverCertChainFile, serverPrivateKeyFile) + .trustManager(serverTrustedCaCerts) + .clientAuth(ClientAuth.REQUIRE) + .build(); + + return NettyServerBuilder.forPort(port) + .sslContext(sslContext); + } + + + private static ManagedChannel clientChannel(String serverHost, int serverPort, + File clientCertChainFile, + File clientPrivateKeyFile, + X509Certificate[] clientTrustedCaCerts) + throws IOException { + SslContext sslContext = GrpcSslContexts.forClient() + .keyManager(clientCertChainFile, clientPrivateKeyFile) + .trustManager(clientTrustedCaCerts) + .build(); + + return NettyChannelBuilder.forAddress(serverHost, serverPort) + .negotiationType(NegotiationType.TLS) + .sslContext(sslContext) + .build(); + } +} diff --git a/testing/src/main/java/io/grpc/testing/TestUtils.java b/testing/src/main/java/io/grpc/testing/TestUtils.java index f29baec2afd..dfff2ce987e 100644 --- a/testing/src/main/java/io/grpc/testing/TestUtils.java +++ b/testing/src/main/java/io/grpc/testing/TestUtils.java @@ -53,6 +53,7 @@ import java.net.UnknownHostException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -191,7 +192,8 @@ public static List preferredTestCiphers() { } /** - * Load a file from the resources folder. + * Saves a file from the classpath resources in src/main/resources/certs as a file on the + * filesystem. * * @param name name of a file in src/main/resources/certs. */ @@ -213,6 +215,23 @@ public static File loadCert(String name) throws IOException { return tmpFile; } + /** + * Loads an X.509 certificate from the classpath resources in src/main/resources/certs. + * + * @param fileName name of a file in src/main/resources/certs. + */ + public static X509Certificate loadX509Cert(String fileName) + throws CertificateException, IOException { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + InputStream in = TestUtils.class.getResourceAsStream("/certs/" + fileName); + try { + return (X509Certificate) cf.generateCertificate(in); + } finally { + in.close(); + } + } + /** * Creates an SSLSocketFactory which contains {@code certChainFile} as its only root certificate. */ diff --git a/testing/src/main/resources/certs/README b/testing/src/main/resources/certs/README index e6d411ad293..c8f86d6e71e 100644 --- a/testing/src/main/resources/certs/README +++ b/testing/src/main/resources/certs/README @@ -64,6 +64,20 @@ common name which is set to *.test.google.com. $ openssl ca -in server1.csr -out server1.pem -keyfile ca.key -cert ca.pem -verbose -config server1-openssl.cnf -days 3650 -extensions v3_req -updatedb $ openssl x509 -in server1.pem -out server1.pem -outform PEM +localhost_server is issued by CA with the common name "localhost": +------------------------------------------------------------------ + +$ openssl genrsa -out localhost_server.key.rsa 1024 +$ openssl pkcs8 -topk8 -in localhost_server.key.rsa -out localhost_server.key -nocrypt +$ rm localhost_server.key.rsa +$ openssl req -new -key localhost_server.key -out localhost_server.csr + +When prompted for certificate information, everything is default except the +common name which is set to "localhost". + +$ openssl ca -in localhost_server.csr -out localhost_server.pem -keyfile ca.key -cert ca.pem -verbose -config openssl.cnf -days 3650 -updatedb -create_serial +$ openssl x509 -in localhost_server.pem -out localhost_server.pem -outform PEM + Gotchas ======= diff --git a/testing/src/main/resources/certs/localhost_server.key b/testing/src/main/resources/certs/localhost_server.key new file mode 100644 index 00000000000..6a2822990c9 --- /dev/null +++ b/testing/src/main/resources/certs/localhost_server.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOx9w33imY4WuDSD +4+SGrUlCDPiRXltJ4QW3KQqA++vaUt3msE3Y6pmagS7hcEDU6IpJIFfX506xA/Oc +yKBxUI/om/fcTEXHmq/hPyOrVcED8iii5k55mGnsuBxtGZc9yJu0ocnSNfuW/pDW +oIA0ZFypxw1IZ1o/1PYb1cpcArGtAgMBAAECgYEAj73ZRvimUMDqcbEAoXRiezaU +X7kr2tzK0wiC/4lqle57k7iVzJtd7MMGZhJMgntmZDcSW5I1W5UoS7guEacOSV7h +Lw37Ni3i4w5iccPLfjSey7ChYB1PHx/4LaxEgP3NQXxlIbKoSSP9FoDCInqx4C6b +CkEo26T9/qVrWYenXIECQQD9cbPHjHmg3jt5dT6Sv8KPswpC/YAy6RNcS6Tg/CNN +CGVlbd2dhxaUBNfLwwE14x6tK/vNvmF8H+NpAp5leyUxAkEA7uBLnlRk14o0dvi+ +j2gvRTWx9dzLw+uAeM3Bl7Hcweliz1V02dyQVQEIgLah6U6yeTYFy06/vDQKaXH3 ++kblPQJBAOPgzhLH/bxk1Pj6ME7mWFu4Uau2HwSniJ7d7NvWGS90Myclx7OR+P0R +9a3iIj5/fd+awodVfHWMfn62uhDozqECQQCCGetVioV51y4H9iZjmMzWFw6b5+ub +A3LvWLEt25NukZxdbB++YKDDi1KEN/QrS89ssP2q43MOIBHjqEz1JRPJAkEAyIZQ ++5y/mg7MuAR5zguzhr+eqQ1FhHyhhYfMD73DTEjFWbfdq9/bdWW/+yNNU4f2GZ7A +lknwMAXWAJu2njYQYw== +-----END PRIVATE KEY----- diff --git a/testing/src/main/resources/certs/localhost_server.pem b/testing/src/main/resources/certs/localhost_server.pem new file mode 100644 index 00000000000..5062935df19 --- /dev/null +++ b/testing/src/main/resources/certs/localhost_server.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8DCCAlmgAwIBAgIJALkk95OtCy9MMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNTEyMjkyMTM5Mjha +Fw0yNTEyMjYyMTM5MjhaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0 +YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMM +CWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA7H3DfeKZjha4 +NIPj5IatSUIM+JFeW0nhBbcpCoD769pS3eawTdjqmZqBLuFwQNToikkgV9fnTrED +85zIoHFQj+ib99xMRcear+E/I6tVwQPyKKLmTnmYaey4HG0Zlz3Im7ShydI1+5b+ +kNaggDRkXKnHDUhnWj/U9hvVylwCsa0CAwEAAaOBwjCBvzAJBgNVHRMEAjAAMAsG +A1UdDwQEAwIF4DAdBgNVHQ4EFgQUBXX49FvGG7KQfqtWcr0N7wffn28wcAYDVR0j +BGkwZ6FapFgwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAf +BgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAxMGdGVzdGNh +ggkAkcYZHh0aKgcwCQYDVR0RBAIwADAJBgNVHRIEAjAAMA0GCSqGSIb3DQEBCwUA +A4GBAEZyKEh3IqPxUP46GfIaJRjIVuFSI5iRO/7jqnoHDNbv+nRX/vxfrHldCgv2 +EQ4+ip8X5fzEz2TOeTj5MDwnO29r5DZi7/SoedU8hfREhszQyF9pjfV7dLq1vvtl +DobLdaex1IxUoy6Yt8Am0mUfMb31gIxakl0UIQokfLJ83he1 +-----END CERTIFICATE----- From 139bdfe1dee452ea6adf54ac738677e357cd107d Mon Sep 17 00:00:00 2001 From: Matt Hildebrand Date: Sat, 16 Jan 2016 20:01:49 -0500 Subject: [PATCH 2/3] Accommodate nondeterminism in TLS integration tests, plus minor polishing. --- .../io/grpc/testing/integration/TlsTest.java | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java b/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java index 9750db82b4f..d4069e5a753 100644 --- a/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java +++ b/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java @@ -50,9 +50,7 @@ import io.grpc.testing.integration.EchoServiceOuterClass.EchoRequest; import io.grpc.testing.integration.EchoServiceOuterClass.EchoResponse; import io.netty.handler.ssl.ClientAuth; -import io.netty.handler.ssl.OpenSsl; import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; import org.junit.Assert; import org.junit.Test; @@ -68,7 +66,8 @@ /** * Integration tests for GRPC's TLS support. */ -// TODO: Use @RunWith(Parameterized.class) to run these tests for all TLS providers. +// TODO: Use @RunWith(Parameterized.class) to run these tests for all TLS providers. Doing so will +// require changes to allow programmatically choosing which TLS provider to use. @RunWith(JUnit4.class) public class TlsTest { private static class DummyEchoRpcService implements EchoServiceGrpc.EchoService { @@ -158,7 +157,8 @@ public void serverRejectsUntrustedClientCert() throws Exception { TestUtils.loadX509Cert("ca.pem"), TestUtils.loadX509Cert("badclient.pem") // Cert is self-signed, and so is its own issuer. }; - ManagedChannel channel = clientChannel("localhost", port, clientCertFile, clientPrivateKeyFile, clientTrustedCaCerts); + ManagedChannel channel = clientChannel("localhost", port, clientCertFile, + clientPrivateKeyFile, clientTrustedCaCerts); EchoServiceBlockingStub client = EchoServiceGrpc.newBlockingStub(channel); // Check that the TLS handshake fails. @@ -213,8 +213,11 @@ public void noClientAuthFailure() throws Exception { try { EchoResponse response = client.echo(request); fail("TLS handshake should have failed, but didn't; received RPC response: " + response); - } catch (Exception expected) { - assertTlsException(expected); + } catch (StatusRuntimeException e) { + // GRPC reports this situation by throwing a StatusRuntimeException that wraps either a + // javax.net.ssl.SSLHandshakeException or a java.nio.channels.ClosedChannelException. + // Thus, reliably detecting the underlying cause is not feasible. + assertEquals(Status.Code.UNAVAILABLE, e.getStatus().getCode()); } } finally { server.shutdown(); @@ -222,23 +225,6 @@ public void noClientAuthFailure() throws Exception { } - /** - * Checks that 'e' either *is* an SSLException or *has* such an exception somewhere in its cause - * chain. - */ - private static void assertTlsException(Exception e) { - Throwable t = e; - while (t != null) { - if (t instanceof SSLException) { - return; - } - t = t.getCause(); - } - - fail("Error not caused by SSLException: " + e.toString()); - } - - private static ServerBuilder serverBuilder(int port, File serverCertChainFile, File serverPrivateKeyFile, X509Certificate[] serverTrustedCaCerts) From eb27c5d60229cc7801aaf146bf424103679cfe5c Mon Sep 17 00:00:00 2001 From: Matt Hildebrand Date: Sat, 16 Jan 2016 20:38:34 -0500 Subject: [PATCH 3/3] Fix style violations. --- .../src/test/java/io/grpc/testing/integration/TlsTest.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java b/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java index d4069e5a753..4ffe698b49b 100644 --- a/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java +++ b/interop-testing/src/test/java/io/grpc/testing/integration/TlsTest.java @@ -32,7 +32,6 @@ package io.grpc.testing.integration; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import io.grpc.ManagedChannel; @@ -40,11 +39,11 @@ import io.grpc.ServerBuilder; import io.grpc.Status; import io.grpc.StatusRuntimeException; -import io.grpc.stub.StreamObserver; import io.grpc.netty.GrpcSslContexts; import io.grpc.netty.NegotiationType; import io.grpc.netty.NettyChannelBuilder; import io.grpc.netty.NettyServerBuilder; +import io.grpc.stub.StreamObserver; import io.grpc.testing.TestUtils; import io.grpc.testing.integration.EchoServiceGrpc.EchoServiceBlockingStub; import io.grpc.testing.integration.EchoServiceOuterClass.EchoRequest; @@ -52,7 +51,6 @@ import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.SslContext; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -61,8 +59,6 @@ import java.io.IOException; import java.security.cert.X509Certificate; -import javax.net.ssl.SSLException; - /** * Integration tests for GRPC's TLS support. */