@@ -48,13 +48,30 @@ private static boolean isJDBCTestEnv() {
4848 */
4949 public static PoolingHttpClientConnectionManager getBaseConnectionManager (
5050 IDatabricksConnectionContext connectionContext ) throws DatabricksHttpException {
51+
52+ if (connectionContext .getSSLTrustStore () == null
53+ && connectionContext .checkCertificateRevocation ()
54+ && !connectionContext .acceptUndeterminedCertificateRevocation ()
55+ && !connectionContext .useSystemTrustStore ()
56+ && !connectionContext .allowSelfSignedCerts ()) {
57+ return new PoolingHttpClientConnectionManager ();
58+ }
59+
5160 // For test environments, use a trust-all socket factory
5261 if (isJDBCTestEnv ()) {
5362 LOGGER .info ("Using trust-all socket factory for JDBC test environment" );
5463 return new PoolingHttpClientConnectionManager (
5564 SocketFactoryUtil .getTrustAllSocketFactoryRegistry ());
5665 }
5766
67+ // If self-signed certificates are allowed, use a trust-all socket factory
68+ if (connectionContext .allowSelfSignedCerts ()) {
69+ LOGGER .warn (
70+ "Self-signed certificates are allowed. Please only use this parameter (AllowSelfSignedCerts) when you're sure of what you're doing. This is not recommended for production use." );
71+ return new PoolingHttpClientConnectionManager (
72+ SocketFactoryUtil .getTrustAllSocketFactoryRegistry ());
73+ }
74+
5875 // For standard SSL configuration, create a custom socket factory registry
5976 Registry <ConnectionSocketFactory > socketFactoryRegistry =
6077 createConnectionSocketFactoryRegistry (connectionContext );
@@ -71,7 +88,51 @@ public static PoolingHttpClientConnectionManager getBaseConnectionManager(
7188 public static Registry <ConnectionSocketFactory > createConnectionSocketFactoryRegistry (
7289 IDatabricksConnectionContext connectionContext ) throws DatabricksHttpException {
7390
74- return createRegistryWithSystemOrDefaultTrustStore (connectionContext );
91+ // First check if a custom trust store is specified
92+ if (connectionContext .getSSLTrustStore () != null ) {
93+ return createRegistryWithCustomTrustStore (connectionContext );
94+ } else {
95+ return createRegistryWithSystemOrDefaultTrustStore (connectionContext );
96+ }
97+ }
98+
99+ /**
100+ * Creates a socket factory registry using a custom trust store.
101+ *
102+ * @param connectionContext The connection context containing the trust store information.
103+ * @return A registry of connection socket factories.
104+ * @throws DatabricksHttpException If there is an error setting up the trust store.
105+ */
106+ private static Registry <ConnectionSocketFactory > createRegistryWithCustomTrustStore (
107+ IDatabricksConnectionContext connectionContext ) throws DatabricksHttpException {
108+
109+ try {
110+ KeyStore trustStore = loadTruststoreOrNull (connectionContext );
111+ if (trustStore == null ) {
112+ String errorMessage =
113+ "Specified trust store could not be loaded: " + connectionContext .getSSLTrustStore ();
114+ handleError (errorMessage , new IOException (errorMessage ));
115+ }
116+
117+ // Get trust anchors from custom store
118+ Set <TrustAnchor > trustAnchors = getTrustAnchorsFromTrustStore (trustStore );
119+ if (trustAnchors .isEmpty ()) {
120+ String errorMessage =
121+ "Custom trust store contains no trust anchors. Certificate validation will fail." ;
122+ handleError (errorMessage , new CertificateException (errorMessage ));
123+ }
124+
125+ LOGGER .info ("Using custom trust store: " + connectionContext .getSSLTrustStore ());
126+
127+ return createRegistryFromTrustAnchors (
128+ trustAnchors ,
129+ connectionContext ,
130+ "custom trust store: " + connectionContext .getSSLTrustStore ());
131+ } catch (Exception e ) {
132+ handleError (
133+ "Error while setting up custom trust store: " + connectionContext .getSSLTrustStore (), e );
134+ }
135+ return null ;
75136 }
76137
77138 /**
@@ -84,6 +145,7 @@ public static Registry<ConnectionSocketFactory> createConnectionSocketFactoryReg
84145 private static Registry <ConnectionSocketFactory > createRegistryWithSystemOrDefaultTrustStore (
85146 IDatabricksConnectionContext connectionContext ) throws DatabricksHttpException {
86147
148+ // Check if we should use the system property trust store based on useSystemTrustStore
87149 String sysTrustStore = null ;
88150 if (connectionContext .useSystemTrustStore ()) {
89151 // When useSystemTrustStore=true, check for javax.net.ssl.trustStore system property
@@ -276,6 +338,56 @@ private static X509TrustManager findX509TrustManager(TrustManager[] trustManager
276338 return null ;
277339 }
278340
341+ /**
342+ * Loads a trust store from the path specified in the connection context.
343+ *
344+ * @param connectionContext The connection context containing trust store configuration.
345+ * @return The loaded KeyStore or null if it could not be loaded.
346+ * @throws DatabricksHttpException If there is an error during loading.
347+ */
348+ public static KeyStore loadTruststoreOrNull (IDatabricksConnectionContext connectionContext )
349+ throws DatabricksHttpException {
350+ String trustStorePath = connectionContext .getSSLTrustStore ();
351+ if (trustStorePath == null ) {
352+ return null ;
353+ }
354+
355+ // If the specified file doesn't exist, throw a specific error
356+ File trustStoreFile = new File (trustStorePath );
357+ if (!trustStoreFile .exists ()) {
358+ String errorMessage = "Specified trust store file does not exist: " + trustStorePath ;
359+ LOGGER .error (errorMessage );
360+ throw new DatabricksHttpException (
361+ errorMessage , DatabricksDriverErrorCode .SSL_HANDSHAKE_ERROR );
362+ }
363+
364+ char [] password = null ;
365+ if (connectionContext .getSSLTrustStorePassword () != null ) {
366+ password = connectionContext .getSSLTrustStorePassword ().toCharArray ();
367+ }
368+
369+ String trustStoreType = connectionContext .getSSLTrustStoreType ();
370+
371+ try (FileInputStream trustStoreStream = new FileInputStream (trustStorePath )) {
372+ LOGGER .info ("Loading trust store as type: " + trustStoreType );
373+ KeyStore trustStore = KeyStore .getInstance (trustStoreType );
374+ trustStore .load (trustStoreStream , password );
375+ LOGGER .info ("Successfully loaded trust store: " + trustStorePath );
376+ return trustStore ;
377+ } catch (Exception e ) {
378+ String errorMessage =
379+ "Failed to load trust store: "
380+ + trustStorePath
381+ + " with type "
382+ + trustStoreType
383+ + ": "
384+ + e .getMessage ();
385+ LOGGER .error (errorMessage );
386+ throw new DatabricksHttpException (
387+ errorMessage , e , DatabricksDriverErrorCode .SSL_HANDSHAKE_ERROR );
388+ }
389+ }
390+
279391 /**
280392 * Extracts trust anchors from a KeyStore.
281393 *
@@ -308,6 +420,17 @@ public static Set<TrustAnchor> getTrustAnchorsFromTrustStore(KeyStore trustStore
308420 return Collections .emptySet ();
309421 }
310422
423+ /**
424+ * Builds trust manager parameters for certificate path validation including certificate
425+ * revocation checking.
426+ *
427+ * @param trustAnchors The trust anchors to use in the trust manager.
428+ * @param checkCertificateRevocation Whether to check certificate revocation.
429+ * @param acceptUndeterminedCertificateRevocation Whether to accept undetermined certificate
430+ * revocation status.
431+ * @return The trust manager parameters based on the input parameters.
432+ * @throws DatabricksHttpException If there is an error during configuration.
433+ */
311434 public static CertPathTrustManagerParameters buildTrustManagerParameters (
312435 Set <TrustAnchor > trustAnchors ,
313436 boolean checkCertificateRevocation ,
0 commit comments