|
21 | 21 | import static org.junit.jupiter.api.Assertions.assertNull; |
22 | 22 | import static org.junit.jupiter.api.Assertions.assertThrows; |
23 | 23 |
|
| 24 | +import com.google.api.client.http.GenericUrl; |
| 25 | +import com.google.api.client.http.HttpTransport; |
24 | 26 | import com.google.api.gax.rpc.TransportChannelProvider; |
25 | 27 | import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException; |
26 | 28 | import com.google.cloud.http.HttpTransportOptions; |
| 29 | +import java.io.BufferedReader; |
27 | 30 | import java.io.File; |
28 | 31 | import java.io.FileNotFoundException; |
29 | 32 | 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; |
30 | 39 | import java.net.URISyntaxException; |
31 | 40 | import java.net.URL; |
| 41 | +import java.nio.charset.StandardCharsets; |
| 42 | +import java.security.KeyStore; |
32 | 43 | import java.util.Collections; |
33 | 44 | import java.util.HashMap; |
34 | 45 | 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; |
35 | 50 | import org.junit.jupiter.api.Test; |
36 | 51 |
|
37 | 52 | public class BigQueryJdbcProxyUtilityTest { |
@@ -419,4 +434,81 @@ public void testGetHttpTransportOptions_withSslTrustStore_withInvalidProvider() |
419 | 434 | "TestClass")); |
420 | 435 | assertThat(exception.getCause()).isInstanceOf(java.security.NoSuchProviderException.class); |
421 | 436 | } |
| 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 | + } |
422 | 514 | } |
0 commit comments