diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index 64957d9..391c835 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -21,6 +21,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.security.KeyStore; +import java.security.SecureRandom; import java.sql.Array; import java.sql.Blob; import java.sql.CallableStatement; @@ -47,6 +48,9 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; import javax.security.sasl.Sasl; import javax.security.sasl.SaslException; @@ -336,11 +340,13 @@ public long getRetryInterval() { httpClientBuilder.addInterceptorFirst(requestInterceptor); // Configure http client for SSL if (useSsl) { + String useTwoWaySSL = sessConfMap.get(JdbcConnectionParams.USE_TWO_WAY_SSL); String sslTrustStorePath = sessConfMap.get(JdbcConnectionParams.SSL_TRUST_STORE); String sslTrustStorePassword = sessConfMap.get( JdbcConnectionParams.SSL_TRUST_STORE_PASSWORD); KeyStore sslTrustStore; SSLSocketFactory socketFactory; + /** * The code within the try block throws: * 1. SSLInitializationException @@ -354,11 +360,13 @@ public long getRetryInterval() { * and throw a SQLException. */ try { - if (sslTrustStorePath == null || sslTrustStorePath.isEmpty()) { + if (useTwoWaySSL != null && + useTwoWaySSL.equalsIgnoreCase(JdbcConnectionParams.TRUE_USE_TWO_WAY_SSL)) { + socketFactory = getTwoWaySSLSocketFactory(); + } else if (sslTrustStorePath == null || sslTrustStorePath.isEmpty()) { // Create a default socket factory based on standard JSSE trust material socketFactory = SSLSocketFactory.getSocketFactory(); - } - else { + } else { // Pick trust store config from the given path sslTrustStore = KeyStore.getInstance(JdbcConnectionParams.SSL_TRUST_STORE_TYPE); sslTrustStore.load(new FileInputStream(sslTrustStorePath), @@ -436,8 +444,22 @@ private TTransport createBinaryTransport() throws SQLException, TTransportExcept if (isSslConnection()) { // get SSL socket String sslTrustStore = sessConfMap.get(JdbcConnectionParams.SSL_TRUST_STORE); - String sslTrustStorePassword = sessConfMap.get(JdbcConnectionParams.SSL_TRUST_STORE_PASSWORD); - if (sslTrustStore == null || sslTrustStore.isEmpty()) { + String sslTrustStorePassword = sessConfMap.get( + JdbcConnectionParams.SSL_TRUST_STORE_PASSWORD); + String useTwoWaySSL = sessConfMap.get(JdbcConnectionParams.USE_TWO_WAY_SSL); + + if (useTwoWaySSL != null && + useTwoWaySSL.equalsIgnoreCase(JdbcConnectionParams.TRUE_USE_TWO_WAY_SSL)) { + String sslkeyStorePath = sessConfMap.get(JdbcConnectionParams.SSL_KEY_STORE); + String sslkeyStorePassword = sessConfMap.get( + JdbcConnectionParams.SSL_KEY_STORE_PASSWORD); + + if (sslkeyStorePath == null || sslkeyStorePath.isEmpty()) { + throw new SQLException("Keystore Path is empty for 2 way ssl"); + } + transport = HiveAuthFactory.getSSLSocket(host, port, loginTimeout, + sslTrustStore, sslTrustStorePassword, sslkeyStorePath, sslkeyStorePassword); + } else if (sslTrustStore == null || sslTrustStore.isEmpty()) { transport = HiveAuthFactory.getSSLSocket(host, port, loginTimeout); } else { transport = HiveAuthFactory.getSSLSocket(host, port, loginTimeout, @@ -462,6 +484,47 @@ private TTransport createBinaryTransport() throws SQLException, TTransportExcept return transport; } + SSLSocketFactory getTwoWaySSLSocketFactory() throws SQLException { + SSLSocketFactory socketFactory = null; + try { + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( + JdbcConnectionParams.SUNX509_ALGORITHM_STRING, + JdbcConnectionParams.SUNJSSE_ALGORITHM_STRING); + String keyStorePath = sessConfMap.get(JdbcConnectionParams.SSL_KEY_STORE); + String keyStorePassword = sessConfMap.get(JdbcConnectionParams.SSL_KEY_STORE_PASSWORD); + KeyStore sslKeyStore = KeyStore.getInstance(JdbcConnectionParams.SSL_KEY_STORE_TYPE); + + if (keyStorePath == null || keyStorePath.isEmpty()) { + throw new IllegalArgumentException(JdbcConnectionParams.SSL_KEY_STORE + + " Not configured for 2 way SSL connection"); + } + sslKeyStore.load(new FileInputStream(keyStorePath), + keyStorePassword.toCharArray()); + keyManagerFactory.init(sslKeyStore, keyStorePassword.toCharArray()); + + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( + JdbcConnectionParams.SUNX509_ALGORITHM_STRING); + String trustStorePath = sessConfMap.get(JdbcConnectionParams.SSL_TRUST_STORE); + String trustStorePassword = sessConfMap.get( + JdbcConnectionParams.SSL_TRUST_STORE_PASSWORD); + KeyStore sslTrustStore = KeyStore.getInstance(JdbcConnectionParams.SSL_TRUST_STORE_TYPE); + + if (trustStorePath == null || trustStorePath.isEmpty()) { + throw new IllegalArgumentException(JdbcConnectionParams.SSL_TRUST_STORE + + " Not configured for 2 way SSL connection"); + } + sslTrustStore.load(new FileInputStream(trustStorePath), + trustStorePassword.toCharArray()); + trustManagerFactory.init(sslTrustStore); + SSLContext context = SSLContext.getInstance("TLS"); + context.init(keyManagerFactory.getKeyManagers(), + trustManagerFactory.getTrustManagers(), new SecureRandom()); + socketFactory = new SSLSocketFactory(context); + } catch (Exception e) { + throw new SQLException("Error while initializing 2 way ssl socket factory ", e); + } + return socketFactory; + } // Lookup the delegation token. First in the connection URL, then Configuration private String getClientDelegationToken(Map jdbcConnConf) throws SQLException { diff --git a/jdbc/src/java/org/apache/hive/jdbc/Utils.java b/jdbc/src/java/org/apache/hive/jdbc/Utils.java index 7ad0710..fe36c1a 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/Utils.java +++ b/jdbc/src/java/org/apache/hive/jdbc/Utils.java @@ -110,6 +110,14 @@ static final String COOKIE_NAME = "cookieName"; // The default value of the cookie name when CookieAuth=true static final String DEFAULT_COOKIE_NAMES_HS2 = "hive.server2.auth"; + // Use two way ssl. This will take effect only when ssl=true + static final String USE_TWO_WAY_SSL = "twoWay"; + static final String TRUE_USE_TWO_WAY_SSL = "true"; + static final String SSL_KEY_STORE = "sslKeyStore"; + static final String SSL_KEY_STORE_PASSWORD = "keyStorePassword"; + static final String SSL_KEY_STORE_TYPE = "JKS"; + static final String SUNX509_ALGORITHM_STRING = "SunX509"; + static final String SUNJSSE_ALGORITHM_STRING = "SunJSSE"; // Non-configurable params: // Currently supports JKS keystore format diff --git a/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java b/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java index 1e6ac4f..fc7573d 100644 --- a/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java +++ b/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java @@ -229,6 +229,17 @@ public static TTransport getSSLSocket(String host, int port, int loginTimeout, return TSSLTransportFactory.getClientSocket(host, port, loginTimeout, params); } + public static TTransport getSSLSocket(String host, int port, int loginTimeout, + String trustStorePath, String trustStorePassWord, + String keyStorePath, String keyStorePassword) throws TTransportException { + TSSLTransportFactory.TSSLTransportParameters params = + new TSSLTransportFactory.TSSLTransportParameters(); + params.setTrustStore(trustStorePath, trustStorePassWord); + params.setKeyStore(keyStorePath, keyStorePassword); + params.requireClientAuth(true); + return TSSLTransportFactory.getClientSocket(host, port, loginTimeout, params); + } + public static TServerSocket getServerSocket(String hiveHost, int portNum) throws TTransportException { InetSocketAddress serverAddress;