diff --git a/s2a/src/main/java/io/grpc/s2a/internal/handshaker/SslContextFactory.java b/s2a/src/main/java/io/grpc/s2a/internal/handshaker/SslContextFactory.java index 5d4ef9eb667..e586b51a76f 100644 --- a/s2a/src/main/java/io/grpc/s2a/internal/handshaker/SslContextFactory.java +++ b/s2a/src/main/java/io/grpc/s2a/internal/handshaker/SslContextFactory.java @@ -92,6 +92,9 @@ static SslContext createForClient( S2ATrustManager.createForClient(stub, targetName, localIdentity)); sslContextBuilder.option( OpenSslContextOption.PRIVATE_KEY_METHOD, S2APrivateKeyMethod.create(stub, localIdentity)); + sslContextBuilder.option( + OpenSslContextOption.GROUPS, + new String[] {"X25519MLKEM768", "x25519", "secp256r1", "secp384r1", "secp521r1"}); SslContext sslContext = sslContextBuilder.build(); SSLSessionContext sslSessionContext = sslContext.sessionContext(); diff --git a/s2a/src/test/java/io/grpc/s2a/IntegrationTest.java b/s2a/src/test/java/io/grpc/s2a/IntegrationTest.java index 1d3568808c6..b816ae53670 100644 --- a/s2a/src/test/java/io/grpc/s2a/IntegrationTest.java +++ b/s2a/src/test/java/io/grpc/s2a/IntegrationTest.java @@ -44,6 +44,7 @@ import io.grpc.testing.protobuf.SimpleResponse; import io.grpc.testing.protobuf.SimpleServiceGrpc; import io.netty.handler.ssl.ClientAuth; +import io.netty.handler.ssl.OpenSslContextOption; import io.netty.handler.ssl.OpenSslSessionContext; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; @@ -253,4 +254,41 @@ public void unaryRpc(SimpleRequest request, StreamObserver obser observer.onCompleted(); } } + + @Test + public void clientCommunicateWithPqcRequiredServer_succeeds() throws Exception { + ClassLoader classLoader = IntegrationTest.class.getClassLoader(); + SslContextBuilder serverSslContextBuilder = + SslContextBuilder.forServer( + classLoader.getResourceAsStream("cert_chain_ec.pem"), + classLoader.getResourceAsStream("leaf_key_ec.pem")); + SslContext serverSslContext = + GrpcSslContexts.configure(serverSslContextBuilder, SslProvider.OPENSSL) + .protocols("TLSv1.3") + .trustManager(classLoader.getResourceAsStream("root_cert_ec.pem")) + .clientAuth(ClientAuth.REQUIRE) + .option(OpenSslContextOption.GROUPS, new String[] {"X25519MLKEM768"}) + .build(); + + Server pqcServer = + NettyServerBuilder.forPort(0) + .addService(new SimpleServiceImpl()) + .sslContext(serverSslContext) + .build() + .start(); + int pqcServerPort = pqcServer.getPort(); + String pqcServerAddress = "localhost:" + pqcServerPort; + + try { + ChannelCredentials credentials = + S2AChannelCredentials.newBuilder(s2aAddress, InsecureChannelCredentials.create()) + .setLocalSpiffeId("test-spiffe-id").build(); + ManagedChannel channel = Grpc.newChannelBuilder(pqcServerAddress, credentials).build(); + + assertThat(doUnaryRpc(channel)).isTrue(); + } finally { + pqcServer.shutdown(); + pqcServer.awaitTermination(10, SECONDS); + } + } }