Skip to content

Commit ceb45b4

Browse files
authored
fix(bigquery-jdbc): fix pqc working in proxy env (#14599)
1 parent a37cff6 commit ceb45b4

2 files changed

Lines changed: 97 additions & 1 deletion

File tree

‎java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcProxyUtility.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,12 @@ private static HttpTransportFactory getHttpTransportFactory(
256256
}
257257
} else if (CONSCRYPT_PROVIDER != null) {
258258
try {
259+
TrustManagerFactory trustManagerFactory =
260+
TrustManagerFactory.getInstance(
261+
TrustManagerFactory.getDefaultAlgorithm(), CONSCRYPT_PROVIDER);
262+
trustManagerFactory.init((KeyStore) null);
259263
SSLContext sslContext = SSLContext.getInstance("TLS", CONSCRYPT_PROVIDER);
260-
sslContext.init(null, null, null);
264+
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
261265
SSLConnectionSocketFactory sslSocketFactory = createSslConnectionSocketFactory(sslContext);
262266
httpClientBuilder.setConnectionManager(
263267
PoolingHttpClientConnectionManagerBuilder.create()

‎java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcProxyUtilityTest.java‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,32 @@
2121
import static org.junit.jupiter.api.Assertions.assertNull;
2222
import static org.junit.jupiter.api.Assertions.assertThrows;
2323

24+
import com.google.api.client.http.GenericUrl;
25+
import com.google.api.client.http.HttpTransport;
2426
import com.google.api.gax.rpc.TransportChannelProvider;
2527
import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException;
2628
import com.google.cloud.http.HttpTransportOptions;
29+
import java.io.BufferedReader;
2730
import java.io.File;
2831
import java.io.FileNotFoundException;
2932
import java.io.IOException;
33+
import java.io.InputStream;
34+
import java.io.InputStreamReader;
35+
import java.io.OutputStream;
36+
import java.net.InetAddress;
37+
import java.net.ServerSocket;
38+
import java.net.Socket;
3039
import java.net.URISyntaxException;
3140
import java.net.URL;
41+
import java.nio.charset.StandardCharsets;
42+
import java.security.KeyStore;
3243
import java.util.Collections;
3344
import java.util.HashMap;
3445
import java.util.Map;
46+
import javax.net.ssl.KeyManagerFactory;
47+
import javax.net.ssl.SSLContext;
48+
import javax.net.ssl.SSLHandshakeException;
49+
import javax.net.ssl.SSLSocket;
3550
import org.junit.jupiter.api.Test;
3651

3752
public class BigQueryJdbcProxyUtilityTest {
@@ -419,4 +434,81 @@ public void testGetHttpTransportOptions_withSslTrustStore_withInvalidProvider()
419434
"TestClass"));
420435
assertThat(exception.getCause()).isInstanceOf(java.security.NoSuchProviderException.class);
421436
}
437+
438+
@Test
439+
public void testGetHttpTransportOptions_withProxyAndNoTrustStore_usesConscryptTrustManager()
440+
throws Exception {
441+
KeyStore serverKeyStore = KeyStore.getInstance("JKS");
442+
try (InputStream ksStream = getClass().getResourceAsStream("/localhost-keystore.jks")) {
443+
serverKeyStore.load(ksStream, "changeit".toCharArray());
444+
}
445+
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
446+
kmf.init(serverKeyStore, "changeit".toCharArray());
447+
SSLContext serverSslContext = SSLContext.getInstance("TLS");
448+
serverSslContext.init(kmf.getKeyManagers(), null, null);
449+
450+
try (ServerSocket proxyServer = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) {
451+
Thread serverThread =
452+
new Thread(
453+
() -> {
454+
try (Socket clientSocket = proxyServer.accept()) {
455+
BufferedReader reader =
456+
new BufferedReader(
457+
new InputStreamReader(
458+
clientSocket.getInputStream(), StandardCharsets.US_ASCII));
459+
String line;
460+
while ((line = reader.readLine()) != null && !line.isEmpty()) {
461+
// Read HTTP CONNECT request headers
462+
}
463+
OutputStream out = clientSocket.getOutputStream();
464+
out.write(
465+
"HTTP/1.1 200 Connection Established\r\n\r\n"
466+
.getBytes(StandardCharsets.US_ASCII));
467+
out.flush();
468+
469+
try (SSLSocket sslSocket =
470+
(SSLSocket)
471+
serverSslContext
472+
.getSocketFactory()
473+
.createSocket(
474+
clientSocket,
475+
clientSocket.getInetAddress().getHostAddress(),
476+
clientSocket.getPort(),
477+
true)) {
478+
sslSocket.setUseClientMode(false);
479+
sslSocket.startHandshake();
480+
}
481+
} catch (IOException ignored) {
482+
// Expected when client rejects self-signed server cert
483+
}
484+
});
485+
serverThread.setDaemon(true);
486+
serverThread.start();
487+
488+
Map<String, String> proxyProperties = new HashMap<>();
489+
proxyProperties.put(BigQueryJdbcUrlUtility.PROXY_HOST_PROPERTY_NAME, "127.0.0.1");
490+
proxyProperties.put(
491+
BigQueryJdbcUrlUtility.PROXY_PORT_PROPERTY_NAME,
492+
String.valueOf(proxyServer.getLocalPort()));
493+
494+
HttpTransportOptions options =
495+
BigQueryJdbcProxyUtility.getHttpTransportOptions(
496+
proxyProperties, null, null, null, null, null, null, "TestClass");
497+
HttpTransport transport = options.getHttpTransportFactory().create();
498+
499+
SSLHandshakeException ex =
500+
assertThrows(
501+
SSLHandshakeException.class,
502+
() ->
503+
transport
504+
.createRequestFactory()
505+
.buildGetRequest(new GenericUrl("https://localhost:8443/"))
506+
.execute());
507+
508+
// Conscrypt's TrustManagerImpl reports "Trust anchor for certification path not found",
509+
// whereas falling back to JDK's SunJSSE X509TrustManagerImpl reports "PKIX path building
510+
// failed" (and fails TLS 1.3 handshakes with "Unknown authType: GENERIC").
511+
assertThat(ex.getMessage()).contains("Trust anchor for certification path not found");
512+
}
513+
}
422514
}

0 commit comments

Comments
 (0)