Skip to content

Commit c00f8a5

Browse files
committed
test(showcase): add test for explicit security provider override in HttpJson PQC and use doNotValidateCertificate
TAG=agy CONV=385b9ab5-874c-4c9a-b331-66dab51fef61
1 parent 360cf6f commit c00f8a5

2 files changed

Lines changed: 64 additions & 89 deletions

File tree

build-with-local-http-client.sh

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,6 @@ PARENT_DIR="$(cd "${MONOREPO_DIR}/.." && pwd)"
2525
HTTP_CLIENT_DIR="${HTTP_CLIENT_DIR:-${PARENT_DIR}/google-http-java-client}"
2626
HTTP_CLIENT_BRANCH="${HTTP_CLIENT_BRANCH:-pqc-support-conscrypt}"
2727

28-
# Use JDK 17 by default for compiling and formatting (required for Spotify fmt plugin)
29-
# If SDKMAN is installed, try using its JDK 17
30-
if [ -d "$HOME/.sdkman/candidates/java/17.0.19-tem" ]; then
31-
export JAVA_HOME="$HOME/.sdkman/candidates/java/17.0.19-tem"
32-
elif [ -d "/usr/lib/jvm/java-17-openjdk-amd64" ]; then
33-
export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"
34-
fi
35-
36-
if [ -n "$JAVA_HOME" ]; then
37-
echo "Using JAVA_HOME: $JAVA_HOME"
38-
export PATH="$JAVA_HOME/bin:$PATH"
39-
else
40-
echo "WARNING: JAVA_HOME for JDK 17 was not found. Using default java: $(java -version 2>&1 | head -n 1)"
41-
fi
4228

4329
echo "========================================================================="
4430
echo "Building and installing google-http-java-client snapshot..."
@@ -64,27 +50,13 @@ else
6450
git checkout "${HTTP_CLIENT_BRANCH}"
6551

6652
echo "Running maven install..."
67-
mvn clean install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Dclirr.skip=true
53+
mvn clean install -pl google-http-client -am -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Dclirr.skip=true
6854
popd
6955
fi
7056

7157
echo "========================================================================="
7258
echo "Building and verifying gapic-showcase with PQC in google-cloud-java..."
7359
echo "========================================================================="
7460

75-
# We need Java 21+ to run the showcase tests because of Conscrypt TLS requirements,
76-
# but if the user has custom JDK, we will respect it.
77-
# Let's try to locate JDK 21 for showcase run if it exists, or just use the active JDK.
78-
if [ -d "$HOME/.sdkman/candidates/java/17.0.19-tem" ]; then
79-
# JDK 17 also works for running show-case tests if Conscrypt loads successfully
80-
export JAVA_HOME="$HOME/.sdkman/candidates/java/17.0.19-tem"
81-
elif [ -d "/usr/lib/jvm/java-21-openjdk-amd64" ]; then
82-
export JAVA_HOME="/usr/lib/jvm/java-21-openjdk-amd64"
83-
fi
84-
85-
if [ -n "$JAVA_HOME" ]; then
86-
export PATH="$JAVA_HOME/bin:$PATH"
87-
fi
88-
8961
# Run the showcase tests
90-
mvn test -pl java-showcase/gapic-showcase -Dtest=ITPqc -Dshowcase.ca.cert="${HOME}/pqc-certs/ca.crt"
62+
mvn test -pl java-showcase/gapic-showcase -Dtest=ITPqc

java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPqc.java

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.google.showcase.v1beta1.it;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20-
import static org.junit.jupiter.api.Assumptions.assumeTrue;
2120

2221
import com.google.api.client.http.javanet.NetHttpTransport;
2322
import com.google.api.gax.core.NoCredentialsProvider;
@@ -38,24 +37,19 @@
3837
import com.google.showcase.v1beta1.EchoResponse;
3938
import com.google.showcase.v1beta1.EchoSettings;
4039
import io.grpc.Channel;
41-
import io.grpc.ChannelCredentials;
4240
import io.grpc.ClientCall;
4341
import io.grpc.ClientInterceptor;
4442
import io.grpc.ForwardingClientCall;
4543
import io.grpc.ForwardingClientCallListener;
46-
import io.grpc.Grpc;
4744
import io.grpc.ManagedChannel;
4845
import io.grpc.Metadata;
4946
import io.grpc.MethodDescriptor;
50-
import io.grpc.TlsChannelCredentials;
51-
import java.io.File;
52-
import java.io.InputStream;
53-
import java.nio.file.Files;
54-
import java.nio.file.Paths;
55-
import java.security.KeyStore;
56-
import java.security.Security;
57-
import java.security.cert.Certificate;
58-
import java.security.cert.CertificateFactory;
47+
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
48+
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
49+
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
50+
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
51+
import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
52+
import java.security.Provider;
5953
import java.util.Collections;
6054
import java.util.concurrent.TimeUnit;
6155
import org.conscrypt.Conscrypt;
@@ -64,45 +58,23 @@
6458

6559
public class ITPqc {
6660

67-
private static String caCertPath;
68-
private static String grpcEndpoint;
69-
private static String httpjsonEndpoint;
70-
private static boolean hasCert;
61+
private static final String GRPC_ENDPOINT = "localhost:7470";
62+
private static final String HTTPJSON_ENDPOINT = "https://localhost:7470";
7163

7264
@BeforeAll
7365
static void setUp() {
74-
caCertPath = System.getProperty("showcase.ca.cert", "../../certs/ca.crt");
75-
grpcEndpoint = System.getProperty("showcase.secure-grpc.endpoint", "localhost:7470");
76-
httpjsonEndpoint =
77-
System.getProperty("showcase.secure-httpjson.endpoint", "https://localhost:7470");
78-
79-
File certFile = new File(caCertPath);
80-
hasCert = certFile.exists() && certFile.isFile();
81-
8266
// Force Conscrypt and OpenJDK to prefer X25519MLKEM768 for TLS 1.3
8367
System.setProperty("jdk.tls.namedGroups", "X25519MLKEM768,X25519,secp256r1");
84-
85-
// Register Conscrypt provider if available and not already registered
86-
if (hasCert) {
87-
try {
88-
if (Security.getProvider("Conscrypt") == null) {
89-
Security.insertProviderAt(Conscrypt.newProvider(), 1);
90-
}
91-
} catch (Throwable t) {
92-
System.err.println("Failed to register Conscrypt provider: " + t.getMessage());
93-
}
94-
}
9568
}
9669

9770
@Test
9871
void testGrpcPqc() throws Exception {
99-
assumeTrue(hasCert, "CA Certificate not found at " + caCertPath + ". Skipping gRPC PQC test.");
72+
// Build insecure Netty SslContext to bypass certificate validation for testing
73+
SslContext sslContext = GrpcSslContexts.configure(
74+
SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)).build();
10075

101-
// Create channel credentials trusting the custom CA
102-
ChannelCredentials creds =
103-
TlsChannelCredentials.newBuilder().trustManager(new File(caCertPath)).build();
104-
105-
ManagedChannel channel = Grpc.newChannelBuilder(grpcEndpoint, creds).build();
76+
ManagedChannel channel =
77+
NettyChannelBuilder.forTarget(GRPC_ENDPOINT).sslContext(sslContext).build();
10678
TransportChannel transportChannel = GrpcTransportChannel.create(channel);
10779

10880
GrpcHeaderCapturingInterceptor interceptor = new GrpcHeaderCapturingInterceptor();
@@ -153,30 +125,15 @@ void testGrpcPqc() throws Exception {
153125

154126
@Test
155127
void testHttpJsonPqc() throws Exception {
156-
assumeTrue(
157-
hasCert, "CA Certificate not found at " + caCertPath + ". Skipping HttpJson PQC test.");
158-
159-
// Build custom TrustManager trusting the CA cert
160-
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
161-
trustStore.load(null, null);
162-
CertificateFactory cf = CertificateFactory.getInstance("X.509");
163-
try (InputStream is = Files.newInputStream(Paths.get(caCertPath))) {
164-
Certificate cert = cf.generateCertificate(is);
165-
trustStore.setCertificateEntry("showcase-ca", cert);
166-
}
167-
168-
// Since Conscrypt was registered at position 1 in setUp(),
169-
// trustCertificates will resolve SSLContext using Conscrypt,
170-
// and NetHttpTransport will automatically wrap the socket factory to enforce PQC.
171-
NetHttpTransport transport =
172-
new NetHttpTransport.Builder().trustCertificates(trustStore).build();
128+
// Build NetHttpTransport with certificate validation disabled
129+
NetHttpTransport transport = new NetHttpTransport.Builder().doNotValidateCertificate().build();
173130

174131
HttpJsonHeaderCapturingInterceptor interceptor = new HttpJsonHeaderCapturingInterceptor();
175132

176133
InstantiatingHttpJsonChannelProvider transportChannelProvider =
177134
EchoSettings.defaultHttpJsonTransportProviderBuilder()
178135
.setHttpTransport(transport)
179-
.setEndpoint(httpjsonEndpoint)
136+
.setEndpoint(HTTPJSON_ENDPOINT)
180137
.setInterceptorProvider(() -> Collections.singletonList(interceptor))
181138
.build();
182139

@@ -209,6 +166,52 @@ void testHttpJsonPqc() throws Exception {
209166
}
210167
}
211168

169+
@Test
170+
void testHttpJsonPqc_withExplicitSecurityProvider() throws Exception {
171+
Provider explicitConscryptProvider = Conscrypt.newProvider();
172+
173+
// Build NetHttpTransport specifying the Conscrypt provider explicitly
174+
NetHttpTransport transport =
175+
new NetHttpTransport.Builder()
176+
.setSecurityProvider(explicitConscryptProvider)
177+
.doNotValidateCertificate()
178+
.build();
179+
180+
HttpJsonHeaderCapturingInterceptor interceptor = new HttpJsonHeaderCapturingInterceptor();
181+
182+
InstantiatingHttpJsonChannelProvider transportChannelProvider =
183+
EchoSettings.defaultHttpJsonTransportProviderBuilder()
184+
.setHttpTransport(transport)
185+
.setEndpoint(HTTPJSON_ENDPOINT)
186+
.setInterceptorProvider(() -> Collections.singletonList(interceptor))
187+
.build();
188+
189+
EchoSettings settings =
190+
EchoSettings.newHttpJsonBuilder()
191+
.setCredentialsProvider(NoCredentialsProvider.create())
192+
.setTransportChannelProvider(transportChannelProvider)
193+
.build();
194+
195+
try (EchoClient client = EchoClient.create(settings)) {
196+
EchoResponse response =
197+
client.echo(
198+
EchoRequest.newBuilder().setContent("pqc-httpjson-explicit-provider-test").build());
199+
assertThat(response.getContent()).isEqualTo("pqc-httpjson-explicit-provider-test");
200+
201+
HttpJsonMetadata capturedHeaders = interceptor.getCapturedHeaders();
202+
assertThat(capturedHeaders).isNotNull();
203+
204+
String negotiatedGroup = getSingleHeaderString(capturedHeaders, "x-showcase-tls-group");
205+
assertThat(negotiatedGroup).isEqualTo("X25519MLKEM768");
206+
207+
String tlsVersion = getSingleHeaderString(capturedHeaders, "x-showcase-tls-version");
208+
assertThat(tlsVersion).isEqualTo("TLS 1.3");
209+
210+
String tlsCipher = getSingleHeaderString(capturedHeaders, "x-showcase-tls-cipher");
211+
assertThat(tlsCipher).isEqualTo("TLS_AES_128_GCM_SHA256");
212+
}
213+
}
214+
212215
private static class GrpcHeaderCapturingInterceptor implements ClientInterceptor {
213216
private Metadata capturedHeaders;
214217

0 commit comments

Comments
 (0)