Repository: ignite
Updated Branches:
  refs/heads/master a83f3038a -> 42161cdd5


IGNITE-6625: JDBC thin: support SSL connection to Ignite node

This closes #3233


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/42161cdd
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/42161cdd
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/42161cdd

Branch: refs/heads/master
Commit: 42161cdd5eafb6403aa6ed4beb75d2645f16f944
Parents: a83f303
Author: tledkov-gridgain <tled...@gridgain.com>
Authored: Thu Feb 8 15:29:42 2018 +0300
Committer: Igor Sapego <isap...@gridgain.com>
Committed: Thu Feb 8 15:31:29 2018 +0300

----------------------------------------------------------------------
 .../jdbc/suite/IgniteJdbcDriverTestSuite.java   |   2 +
 .../jdbc/thin/JdbcThinConnectionSSLTest.java    | 479 +++++++++++++++++++
 .../jdbc/thin/JdbcThinConnectionSelfTest.java   |  36 +-
 .../jdbc/thin/JdbcThinErrorsSelfTest.java       |   2 +-
 .../jdbc/thin/ConnectionProperties.java         | 206 ++++++++
 .../jdbc/thin/ConnectionPropertiesImpl.java     | 232 ++++++++-
 .../internal/jdbc/thin/JdbcThinConnection.java  |   5 +
 .../internal/jdbc/thin/JdbcThinSSLUtil.java     | 332 +++++++++++++
 .../internal/jdbc/thin/JdbcThinTcpIo.java       |  43 +-
 9 files changed, 1297 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
----------------------------------------------------------------------
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index 656e218..c004817 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -43,6 +43,7 @@ import 
org.apache.ignite.jdbc.thin.JdbcThinBulkLoadTransactionalPartitionedSelfT
 import 
org.apache.ignite.jdbc.thin.JdbcThinBulkLoadTransactionalReplicatedSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinComplexDmlDdlSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinComplexQuerySelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinConnectionSSLTest;
 import org.apache.ignite.jdbc.thin.JdbcThinConnectionSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinDeleteStatementSelfTest;
 import 
org.apache.ignite.jdbc.thin.JdbcThinDynamicIndexAtomicPartitionedNearSelfTest;
@@ -133,6 +134,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
 
         // New thin JDBC
         suite.addTest(new TestSuite(JdbcThinConnectionSelfTest.class));
+        suite.addTest(new TestSuite(JdbcThinConnectionSSLTest.class));
         suite.addTest(new TestSuite(JdbcThinPreparedStatementSelfTest.class));
         suite.addTest(new TestSuite(JdbcThinResultSetSelfTest.class));
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSSLTest.java
----------------------------------------------------------------------
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSSLTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSSLTest.java
new file mode 100644
index 0000000..cc71f51
--- /dev/null
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSSLTest.java
@@ -0,0 +1,479 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.jdbc.thin;
+
+import java.security.NoSuchAlgorithmException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.concurrent.Callable;
+import javax.cache.configuration.Factory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.ssl.SslContextFactory;
+import org.apache.ignite.testframework.GridTestUtils;
+
+/**
+ * SSL connection test.
+ */
+@SuppressWarnings("ThrowableNotThrown")
+public class JdbcThinConnectionSSLTest extends JdbcThinAbstractSelfTest {
+    /** IP finder. */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new 
TcpDiscoveryVmIpFinder(true);
+
+    /** Client key store path. */
+    private static final String CLI_KEY_STORE_PATH = U.getIgniteHome() +
+        "/modules/clients/src/test/keystore/client.jks";
+
+    /** Server key store path. */
+    private static final String SRV_KEY_STORE_PATH = U.getIgniteHome() +
+        "/modules/clients/src/test/keystore/server.jks";
+
+    /** Trust key store path. */
+    private static final String TRUST_KEY_STORE_PATH = U.getIgniteHome() +
+        "/modules/clients/src/test/keystore/trust.jks";
+
+    /** SSL context factory. */
+    private static Factory<SSLContext> sslCtxFactory;
+
+    /** Set SSL context factory to client listener. */
+    private static boolean setSslCtxFactoryToCli;
+
+    /** Set SSL context factory to ignite. */
+    private static boolean setSslCtxFactoryToIgnite;
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("deprecation")
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+        disco.setIpFinder(IP_FINDER);
+
+        cfg.setDiscoverySpi(disco);
+
+        cfg.setMarshaller(new BinaryMarshaller());
+
+        cfg.setClientConnectorConfiguration(
+            new ClientConnectorConfiguration()
+                .setSslEnabled(true)
+                .setUseIgniteSslContextFactory(setSslCtxFactoryToIgnite)
+                .setSslClientAuth(true)
+                .setSslContextFactory(setSslCtxFactoryToCli ? sslCtxFactory : 
null));
+
+        cfg.setSslContextFactory(setSslCtxFactoryToIgnite ? sslCtxFactory : 
null);
+
+        return cfg;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConnection() throws Exception {
+        setSslCtxFactoryToCli = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try {
+            try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                "&sslClientCertificateKeyStoreUrl=" + CLI_KEY_STORE_PATH +
+                "&sslClientCertificateKeyStorePassword=123456" +
+                "&sslTrustCertificateKeyStoreUrl=" + TRUST_KEY_STORE_PATH +
+                "&sslTrustCertificateKeyStorePassword=123456")) {
+                checkConnection(conn);
+            }
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConnectionTrustAll() throws Exception {
+        setSslCtxFactoryToCli = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try {
+            try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                "&sslClientCertificateKeyStoreUrl=" + CLI_KEY_STORE_PATH +
+                "&sslClientCertificateKeyStorePassword=123456" +
+                "&sslTrustAll=true")) {
+                checkConnection(conn);
+            }
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConnectionUseIgniteFactory() throws Exception {
+        setSslCtxFactoryToIgnite = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try {
+            try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                "&sslClientCertificateKeyStoreUrl=" + CLI_KEY_STORE_PATH +
+                "&sslClientCertificateKeyStorePassword=123456" +
+                "&sslTrustCertificateKeyStoreUrl=" + TRUST_KEY_STORE_PATH +
+                "&sslTrustCertificateKeyStorePassword=123456")) {
+                checkConnection(conn);
+            }
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDefaultContext() throws Exception {
+        setSslCtxFactoryToCli = true;
+        // Factory return default SSL context
+        sslCtxFactory = new Factory<SSLContext>() {
+            @Override public SSLContext create() {
+                try {
+                    return SSLContext.getDefault();
+                }
+                catch (NoSuchAlgorithmException e) {
+                    throw new IgniteException(e);
+                }
+            }
+        };
+
+        System.setProperty("javax.net.ssl.keyStore", CLI_KEY_STORE_PATH);
+        System.setProperty("javax.net.ssl.keyStorePassword", "123456");
+        System.setProperty("javax.net.ssl.trustStore", TRUST_KEY_STORE_PATH);
+        System.setProperty("javax.net.ssl.trustStorePassword", "123456");
+
+        startGrids(1);
+
+        try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require")) {
+            checkConnection(conn);
+        }
+        finally {
+            System.getProperties().remove("javax.net.ssl.keyStore");
+            System.getProperties().remove("javax.net.ssl.keyStorePassword");
+            System.getProperties().remove("javax.net.ssl.trustStore");
+            System.getProperties().remove("javax.net.ssl.trustStorePassword");
+
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testContextFactory() throws Exception {
+        setSslCtxFactoryToCli = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+            "&sslFactory=" + TestSSLFactory.class.getName())) {
+            checkConnection(conn);
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testSslServerAndPlainClient() throws Exception {
+        setSslCtxFactoryToCli = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try {
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1");
+
+                    return null;
+                }
+            }, SQLException.class, "Failed to connect to Ignite cluster");
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testInvalidKeystoreConfig() throws Exception {
+        setSslCtxFactoryToCli = true;
+
+        startGrids(1);
+
+        try {
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        
"&sslClientCertificateKeyStoreUrl=invalid_client_keystore_path" +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "Could not open client key store");
+
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
CLI_KEY_STORE_PATH +
+                        
"&sslClientCertificateKeyStorePassword=invalid_cli_passwd" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "Could not open client key store");
+
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
CLI_KEY_STORE_PATH +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        
"&sslTrustCertificateKeyStoreUrl=invalid_trust_keystore_path" +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "Could not open trusted key store");
+
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
CLI_KEY_STORE_PATH +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        
"&sslTrustCertificateKeyStorePassword=invalid_trust_passwd");
+
+                    return null;
+                }
+            }, SQLException.class, "Could not open trusted key store");
+
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
CLI_KEY_STORE_PATH +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        "&sslClientCertificateKeyStoreType=INVALID" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "Could not create client KeyStore 
instance");
+
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
CLI_KEY_STORE_PATH +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslTrustCertificateKeyStoreType=INVALID" +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "Could not create trust KeyStore instance");
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testUnknownClientCertificate() throws Exception {
+        setSslCtxFactoryToCli = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try {
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    Connection c = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "Failed to SSL connect to server");
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testUnsupportedSslProtocol() throws Exception {
+        setSslCtxFactoryToCli = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try {
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    Connection c = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslProtocol=TLSv1.3" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
CLI_KEY_STORE_PATH +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "TLSv1.3 is not a valid SSL protocol");
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testInvalidKeyAlgorithm() throws Exception {
+        setSslCtxFactoryToCli = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try {
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    Connection c = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslKeyAlgorithm=INVALID" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
CLI_KEY_STORE_PATH +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "Default algorithm definitions for 
TrustManager and/or KeyManager are invalid");
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testInvalidKeyStoreType() throws Exception {
+        setSslCtxFactoryToCli = true;
+        sslCtxFactory = getTestSslContextFactory();
+
+        startGrids(1);
+
+        try {
+            GridTestUtils.assertThrows(log, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    Connection c = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                        "&sslClientCertificateKeyStoreType=PKCS12" +
+                        "&sslClientCertificateKeyStoreUrl=" + 
CLI_KEY_STORE_PATH +
+                        "&sslClientCertificateKeyStorePassword=123456" +
+                        "&sslTrustCertificateKeyStoreUrl=" + 
TRUST_KEY_STORE_PATH +
+                        "&sslTrustCertificateKeyStorePassword=123456");
+
+                    return null;
+                }
+            }, SQLException.class, "Could not open client key store");
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @param conn Connection to check.
+     * @throws SQLException On failed.
+     */
+    public void checkConnection(Connection conn) throws SQLException {
+        assertEquals("PUBLIC", conn.getSchema());
+
+        try (Statement stmt = conn.createStatement()) {
+            ResultSet rs = stmt.executeQuery("SELECT 1");
+
+            assertTrue(rs.next());
+
+            assertEquals(1, rs.getInt(1));
+        }
+    }
+
+    /**
+     * @return Test SSL context factory.
+     */
+    private static Factory<SSLContext> getTestSslContextFactory() {
+        SslContextFactory factory = new SslContextFactory();
+
+        factory.setKeyStoreFilePath(SRV_KEY_STORE_PATH);
+        factory.setKeyStorePassword("123456".toCharArray());
+        factory.setTrustStoreFilePath(TRUST_KEY_STORE_PATH);
+        factory.setTrustStorePassword("123456".toCharArray());
+
+        return factory;
+    }
+
+    /**
+     *
+     */
+    public static class TestSSLFactory implements Factory<SSLSocketFactory> {
+        /** {@inheritDoc} */
+        @Override public SSLSocketFactory create() {
+            return getTestSslContextFactory().create().getSocketFactory();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
index 0cf6ab6..ad98683 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
@@ -38,7 +38,7 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.internal.jdbc.thin.JdbcThinConnection;
 import org.apache.ignite.internal.jdbc.thin.JdbcThinTcpIo;
-import org.apache.ignite.internal.jdbc.thin.JdbcThinUtils;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
@@ -68,6 +68,14 @@ public class JdbcThinConnectionSelfTest extends 
JdbcThinAbstractSelfTest {
     /** */
     private static final String URL = "jdbc:ignite:thin://127.0.0.1";
 
+    /** Client key store path. */
+    private static final String CLI_KEY_STORE_PATH = U.getIgniteHome() +
+        "/modules/clients/src/test/keystore/client.jks";
+
+    /** Server key store path. */
+    private static final String SRV_KEY_STORE_PATH = U.getIgniteHome() +
+        "/modules/clients/src/test/keystore/server.jks";
+
     /** {@inheritDoc} */
     @SuppressWarnings("deprecation")
     @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
@@ -263,16 +271,16 @@ public class JdbcThinConnectionSelfTest extends 
JdbcThinAbstractSelfTest {
      */
     public void testTcpNoDelay() throws Exception {
         assertInvalid("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=0",
-            "Failed to parse boolean property [name=tcpNoDelay, value=0]");
+            "Invalid property value. [name=tcpNoDelay, val=0, choices=[true, 
false]]");
 
         assertInvalid("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=1",
-            "Failed to parse boolean property [name=tcpNoDelay, value=1]");
+            "Invalid property value. [name=tcpNoDelay, val=1, choices=[true, 
false]]");
 
         assertInvalid("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=false1",
-            "Failed to parse boolean property [name=tcpNoDelay, 
value=false1]");
+            "Invalid property value. [name=tcpNoDelay, val=false1, 
choices=[true, false]]");
 
         assertInvalid("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=true1",
-            "Failed to parse boolean property [name=tcpNoDelay, value=true1]");
+            "Invalid property value. [name=tcpNoDelay, val=true1, 
choices=[true, false]]");
 
         try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1")) {
             assertTrue(io(conn).connectionProperties().isTcpNoDelay());
@@ -303,7 +311,7 @@ public class JdbcThinConnectionSelfTest extends 
JdbcThinAbstractSelfTest {
     public void testAutoCloseServerCursorProperty() throws Exception {
         String url = "jdbc:ignite:thin://127.0.0.1?autoCloseServerCursor";
 
-        String err = "Failed to parse boolean property 
[name=autoCloseServerCursor";
+        String err = "Invalid property value. [name=autoCloseServerCursor";
 
         assertInvalid(url + "=0", err);
         assertInvalid(url + "=1", err);
@@ -1757,6 +1765,22 @@ public class JdbcThinConnectionSelfTest extends 
JdbcThinAbstractSelfTest {
     }
 
     /**
+     */
+    public void testSslClientAndPlainServer()  {
+        GridTestUtils.assertThrows(log, new Callable<Object>() {
+            @Override public Object call() throws Exception {
+                
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
+                    "&sslClientCertificateKeyStoreUrl=" + CLI_KEY_STORE_PATH +
+                    "&sslClientCertificateKeyStorePassword=123456" +
+                    "&sslTrustCertificateKeyStoreUrl=" + SRV_KEY_STORE_PATH +
+                    "&sslTrustCertificateKeyStorePassword=123456");
+
+                return null;
+            }
+        }, SQLException.class, "Failed to SSL connect to server");
+    }
+
+    /**
      * @return Savepoint.
      */
     private Savepoint getFakeSavepoint() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinErrorsSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinErrorsSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinErrorsSelfTest.java
index 90588c4..c01764c 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinErrorsSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinErrorsSelfTest.java
@@ -48,7 +48,7 @@ public class JdbcThinErrorsSelfTest extends 
JdbcErrorsAbstractSelfTest {
 
                 return null;
             }
-        }, "08001", "Failed to connect to Ignite cluster [host=unknown.host");
+        }, "08001", "Failed to connect to server [host=unknown.host");
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
index d793484..169f85b 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
@@ -23,6 +23,12 @@ import java.sql.SQLException;
  * Provide access and manipulations with connection JDBC properties.
  */
 public interface ConnectionProperties {
+    /** SSL mode: DISABLE. */
+    public static final String SSL_MODE_DISABLE = "disable";
+
+    /** SSL mode: REQUIRE. */
+    public static final String SSL_MODE_REQUIRE = "require";
+
     /**
      * @return Host name or host's IP to connect.
      */
@@ -145,4 +151,204 @@ public interface ConnectionProperties {
      * @param skipReducerOnUpdate Skip reducer on update flag.
      */
     public void setSkipReducerOnUpdate(boolean skipReducerOnUpdate);
+
+    /**
+     * Gets SSL connection mode.
+     *
+     * @return Use SSL flag.
+     * @see #setSslMode(String).
+     */
+    public String getSslMode();
+
+    /**
+     * Use SSL connection to Ignite node. In case set to {@code "require"} SSL 
context must be configured.
+     * {@link #setSslClientCertificateKeyStoreUrl} property and related 
properties must be set up
+     * or JSSE properties must be set up (see {@code javax.net.ssl.keyStore} 
and other {@code javax.net.ssl.*}
+     * properties)
+     *
+     * In case set to {@code "disable"} plain connection is used.
+     * Available modes: {@code "disable", "require"}. Default value is {@code 
"disable"}
+     *
+     * @param mode SSL mode.
+     */
+    public void setSslMode(String mode);
+
+    /**
+     * Gets protocol for secure transport.
+     *
+     * @return SSL protocol name.
+     */
+    public String getSslProtocol();
+
+    /**
+     * Sets protocol for secure transport. If not specified, TLS protocol will 
be used.
+     * Protocols implementations supplied by JSEE: SSLv3 (SSL), TLSv1 (TLS), 
TLSv1.1, TLSv1.2
+     *
+     * <p>See more at JSSE Reference Guide.
+     *
+     * @param sslProtocol SSL protocol name.
+     */
+    public void setSslProtocol(String sslProtocol);
+
+    /**
+     * Gets algorithm that will be used to create a key manager.
+     *
+     * @return Key manager algorithm.
+     */
+    public String getSslKeyAlgorithm();
+
+    /**
+     * Sets key manager algorithm that will be used to create a key manager. 
Notice that in most cased default value
+     * suites well, however, on Android platform this value need to be set to 
<tt>X509<tt/>.
+     * Algorithms implementations supplied by JSEE: PKIX (X509 or SunPKIX), 
SunX509
+     *
+     * <p>See more at JSSE Reference Guide.
+     *
+     * @param keyAlgorithm Key algorithm name.
+     */
+    public void setSslKeyAlgorithm(String keyAlgorithm);
+
+    /**
+     * Gets the key store URL.
+     *
+     * @return Client certificate KeyStore URL.
+     */
+    public String getSslClientCertificateKeyStoreUrl();
+
+    /**
+     * Sets path to the key store file. This is a mandatory parameter since
+     * ssl context could not be initialized without key manager.
+     *
+     * In case {@link #getSslMode()} is {@code required} and key store URL 
isn't specified by Ignite properties
+     * (e.g. at JDBC URL) the JSSE property {@code javax.net.ssl.keyStore} 
will be used.
+     *
+     * @param url Client certificate KeyStore URL.
+     */
+    public void setSslClientCertificateKeyStoreUrl(String url);
+
+    /**
+     * Gets key store password.
+     *
+     * @return Client certificate KeyStore password.
+     */
+    public String getSslClientCertificateKeyStorePassword();
+
+    /**
+     * Sets key store password.
+     *
+     * In case {@link #getSslMode()} is {@code required}  and key store 
password isn't specified by Ignite properties
+     * (e.g. at JDBC URL) the JSSE property {@code 
javax.net.ssl.keyStorePassword} will be used.
+     *
+     * @param passwd Client certificate KeyStore password.
+     */
+    public void setSslClientCertificateKeyStorePassword(String passwd);
+
+    /**
+     * Gets key store type used for context creation.
+     *
+     * @return Client certificate KeyStore type.
+     */
+    public String getSslClientCertificateKeyStoreType();
+
+    /**
+     * Sets key store type used in context initialization.
+     *
+     * In case {@link #getSslMode()} is {@code required} and key store type 
isn't specified by Ignite properties
+     *  (e.g. at JDBC URL)the JSSE property {@code javax.net.ssl.keyStoreType} 
will be used.
+     * In case both Ignite properties and JSSE properties are not set the 
default 'JKS' type is used.
+     *
+     * <p>See more at JSSE Reference Guide.
+     *
+     * @param ksType Client certificate KeyStore type.
+     */
+    public void setSslClientCertificateKeyStoreType(String ksType);
+
+    /**
+     * Gets the trust store URL.
+     *
+     * @return Trusted certificate KeyStore URL.
+     */
+    public String getSslTrustCertificateKeyStoreUrl();
+
+    /**
+     * Sets path to the trust store file. This is an optional parameter,
+     * however one of the {@code setSslTrustCertificateKeyStoreUrl(String)}, 
{@link #setSslTrustAll(boolean)}
+     * properties must be set.
+     *
+     * In case {@link #getSslMode()} is {@code required} and trust store URL 
isn't specified by Ignite properties
+     * (e.g. at JDBC URL) the JSSE property {@code javax.net.ssl.trustStore} 
will be used.
+     *
+     * @param url Trusted certificate KeyStore URL.
+     */
+    public void setSslTrustCertificateKeyStoreUrl(String url);
+
+    /**
+     * Gets trust store password.
+     *
+     * @return Trusted certificate KeyStore password.
+     */
+    public String getSslTrustCertificateKeyStorePassword();
+
+    /**
+     * Sets trust store password.
+     *
+     * In case {@link #getSslMode()} is {@code required} and trust store 
password isn't specified by Ignite properties
+     * (e.g. at JDBC URL) the JSSE property {@code 
javax.net.ssl.trustStorePassword} will be used.
+     *
+     * @param passwd Trusted certificate KeyStore password.
+     */
+    public void setSslTrustCertificateKeyStorePassword(String passwd);
+
+    /**
+     * Gets trust store type.
+     *
+     * @return Trusted certificate KeyStore type.
+     */
+    public String getSslTrustCertificateKeyStoreType();
+
+    /**
+     * Sets trust store type.
+     *
+     * In case {@link #getSslMode()} is {@code required} and trust store type 
isn't specified by Ignite properties
+     * (e.g. at JDBC URL) the JSSE property {@code 
javax.net.ssl.trustStoreType} will be used.
+     * In case both Ignite properties and JSSE properties are not set the 
default 'JKS' type is used.
+     *
+     * @param ksType Trusted certificate KeyStore type.
+     */
+    public void setSslTrustCertificateKeyStoreType(String ksType);
+
+    /**
+     * Gets trust any server certificate flag.
+     *
+     * @return Trust all certificates flag.
+     */
+    public boolean isSslTrustAll();
+
+    /**
+     * Sets to {@code true} to trust any server certificate (revoked, expired 
or self-signed SSL certificates).
+     *
+     * <p> Defaults is {@code false}.
+     *
+     * Note: Do not enable this option in production you are ever going to use
+     * on a network you do not entirely trust. Especially anything going over 
the public internet.
+     *
+     * @param trustAll Trust all certificates flag.
+     */
+    public void setSslTrustAll(boolean trustAll);
+
+    /**
+     * Gets the class name of the custom implementation of the 
Factory&lt;SSLSocketFactory&gt;.
+     *
+     * @return Custom class name that implements 
Factory&lt;SSLSocketFactory&gt;.
+     */
+    public String getSslFactory();
+
+    /**
+     * Sets the class name of the custom implementation of the 
Factory&lt;SSLSocketFactory&gt;.
+     * If {@link #getSslMode()} is {@code required} and factory is specified 
the custom factory will be used
+     * instead of JSSE socket factory. So, other SSL properties will be 
ignored.
+     *
+     * @param sslFactory Custom class name that implements 
Factory&lt;SSLSocketFactory&gt;.
+     */
+    public void setSslFactory(String sslFactory);
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
index 86ba2fa..471381b 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.jdbc.thin;
 import java.io.Serializable;
 import java.sql.DriverPropertyInfo;
 import java.sql.SQLException;
+import java.util.Arrays;
 import java.util.Properties;
 import javax.naming.RefAddr;
 import javax.naming.Reference;
@@ -39,14 +40,8 @@ public class ConnectionPropertiesImpl implements 
ConnectionProperties, Serializa
 
     /** Host name property. */
     private StringProperty host = new StringProperty(
-        "host", "Ignite node IP to connect", null, null, true, new 
PropertyValidator() {
-        private static final long serialVersionUID = 0L;
-
-        @Override public void validate(String host) throws SQLException {
-            if (F.isEmpty(host))
-                throw new SQLException("Host name is empty", 
SqlStateCode.CLIENT_CONNECTION_FAILED);
-        }
-    });
+        "host", "Ignite node IP to connect", null, null, true,
+        new EmptyStringValidator("Host name is empty"));
 
     /** Connection port property. */
     private IntegerProperty port = new IntegerProperty(
@@ -96,11 +91,70 @@ public class ConnectionPropertiesImpl implements 
ConnectionProperties, Serializa
     private BooleanProperty skipReducerOnUpdate = new BooleanProperty(
         "skipReducerOnUpdate", "Enable execution update queries on ignite 
server nodes", false, false);
 
+    /** SSL: Use SSL connection to Ignite node. */
+    private StringProperty sslMode = new StringProperty("sslMode",
+        "The SSL mode of the connection", SSL_MODE_DISABLE,
+        new String[] {SSL_MODE_DISABLE, SSL_MODE_REQUIRE}, false, null);
+
+    /** SSL: Client certificate key store url. */
+    private StringProperty sslProtocol = new StringProperty("sslProtocol",
+        "SSL protocol name", null,  null, false, null);
+
+    /** SSL: Key algorithm name. */
+    private StringProperty sslKeyAlgorithm = new 
StringProperty("sslKeyAlgorithm",
+        "SSL key algorithm name", "SunX509",  null, false, null);
+
+    /** SSL: Client certificate key store url. */
+    private StringProperty sslClientCertificateKeyStoreUrl =
+        new StringProperty("sslClientCertificateKeyStoreUrl",
+        "Client certificate key store URL",
+        null, null, false, null);
+
+    /** SSL: Client certificate key store password. */
+    private StringProperty sslClientCertificateKeyStorePassword =
+        new StringProperty("sslClientCertificateKeyStorePassword",
+        "Client certificate key store password",
+            null, null, false, null);
+
+    /** SSL: Client certificate key store type. */
+    private StringProperty sslClientCertificateKeyStoreType =
+        new StringProperty("sslClientCertificateKeyStoreType",
+        "Client certificate key store type",
+            null, null, false, null);
+
+    /** SSL: Trusted certificate key store url. */
+    private StringProperty sslTrustCertificateKeyStoreUrl =
+        new StringProperty("sslTrustCertificateKeyStoreUrl",
+        "Trusted certificate key store URL", null, null, false, null);
+
+    /** SSL Trusted certificate key store password. */
+    private StringProperty sslTrustCertificateKeyStorePassword =
+        new StringProperty("sslTrustCertificateKeyStorePassword",
+        "Trusted certificate key store password", null, null, false, null);
+
+    /** SSL: Trusted certificate key store type. */
+    private StringProperty sslTrustCertificateKeyStoreType =
+        new StringProperty("sslTrustCertificateKeyStoreType",
+        "Trusted certificate key store type",
+            null, null, false, null);
+
+    /** SSL: Trust all certificates. */
+    private BooleanProperty sslTrustAll = new BooleanProperty("sslTrustAll",
+        "Trust all certificates",false, false);
+
+    /** SSL: Custom class name that implements 
Factory&lt;SSLSocketFactory&gt;. */
+    private StringProperty sslFactory = new StringProperty("sslFactory",
+        "Custom class name that implements Factory<SSLSocketFactory>", null, 
null, false, null);
+
     /** Properties array. */
     private final ConnectionProperty [] propsArray = {
         host, port,
         distributedJoins, enforceJoinOrder, collocated, replicatedOnly, 
autoCloseServerCursor,
-        tcpNoDelay, lazy, socketSendBuffer, socketReceiveBuffer, 
skipReducerOnUpdate
+        tcpNoDelay, lazy, socketSendBuffer, socketReceiveBuffer, 
skipReducerOnUpdate,
+        sslMode, sslProtocol, sslKeyAlgorithm,
+        sslClientCertificateKeyStoreUrl, sslClientCertificateKeyStorePassword, 
sslClientCertificateKeyStoreType,
+        sslTrustCertificateKeyStoreUrl, sslTrustCertificateKeyStorePassword, 
sslTrustCertificateKeyStoreType,
+        sslTrustAll, sslFactory
     };
 
     /** {@inheritDoc} */
@@ -223,6 +277,116 @@ public class ConnectionPropertiesImpl implements 
ConnectionProperties, Serializa
         skipReducerOnUpdate.setValue(val);
     }
 
+    /** {@inheritDoc} */
+    @Override public String getSslMode() {
+        return sslMode.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslMode(String mode) {
+        sslMode.setValue(mode);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslProtocol() {
+        return sslProtocol.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslProtocol(String sslProtocol) {
+        this.sslProtocol.setValue(sslProtocol);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslKeyAlgorithm() {
+        return sslKeyAlgorithm.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslKeyAlgorithm(String keyAlgorithm) {
+        sslKeyAlgorithm.setValue(keyAlgorithm);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslClientCertificateKeyStoreUrl() {
+        return sslClientCertificateKeyStoreUrl.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslClientCertificateKeyStoreUrl(String url) {
+        sslClientCertificateKeyStoreUrl.setValue(url);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslClientCertificateKeyStorePassword() {
+        return sslClientCertificateKeyStorePassword.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslClientCertificateKeyStorePassword(String 
passwd) {
+        sslClientCertificateKeyStorePassword.setValue(passwd);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslClientCertificateKeyStoreType() {
+        return sslClientCertificateKeyStoreType.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslClientCertificateKeyStoreType(String ksType) {
+        sslClientCertificateKeyStoreType.setValue(ksType);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslTrustCertificateKeyStoreUrl() {
+        return sslTrustCertificateKeyStoreUrl.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslTrustCertificateKeyStoreUrl(String url) {
+        sslTrustCertificateKeyStoreUrl.setValue(url);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslTrustCertificateKeyStorePassword() {
+        return sslTrustCertificateKeyStorePassword.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslTrustCertificateKeyStorePassword(String 
passwd) {
+        sslTrustCertificateKeyStorePassword.setValue(passwd);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslTrustCertificateKeyStoreType() {
+        return sslTrustCertificateKeyStoreType.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslTrustCertificateKeyStoreType(String ksType) {
+        sslTrustCertificateKeyStoreType.setValue(ksType);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isSslTrustAll() {
+        return sslTrustAll.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslTrustAll(boolean trustAll) {
+        this.sslTrustAll.setValue(trustAll);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSslFactory() {
+        return sslFactory.value();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSslFactory(String sslFactory) {
+        this.sslFactory.setValue(sslFactory);
+    }
+
     /**
      * @param props Environment properties.
      * @throws SQLException On error.
@@ -273,6 +437,30 @@ public class ConnectionPropertiesImpl implements 
ConnectionProperties, Serializa
     /**
      *
      */
+    private static class EmptyStringValidator implements PropertyValidator {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** Error message. */
+        private final String errMsg;
+
+        /**
+         * @param msg Error message.
+         */
+        private EmptyStringValidator(String msg) {
+            errMsg = msg;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void validate(String val) throws SQLException {
+            if (F.isEmpty(val))
+                throw new SQLException(errMsg, 
SqlStateCode.CLIENT_CONNECTION_FAILED);
+        }
+    }
+
+    /**
+     *
+     */
     private abstract static class ConnectionProperty implements Serializable {
         /** */
         private static final long serialVersionUID = 0L;
@@ -365,6 +553,8 @@ public class ConnectionPropertiesImpl implements 
ConnectionProperties, Serializa
                     SqlStateCode.CLIENT_CONNECTION_FAILED);
             }
 
+            checkChoices(strVal);
+
             if (validator != null)
                 validator.validate(strVal);
 
@@ -374,6 +564,25 @@ public class ConnectionPropertiesImpl implements 
ConnectionProperties, Serializa
         }
 
         /**
+         * @param strVal Checked value.
+         * @throws SQLException On check error.
+         */
+        protected void checkChoices(String strVal) throws SQLException {
+            if (strVal == null)
+                return;
+
+            if (choices != null) {
+                for (String ch : choices) {
+                    if (ch.equalsIgnoreCase(strVal))
+                        return;
+                }
+
+                throw new SQLException("Invalid property value. [name=" + name 
+ ", val=" + strVal
+                    + ", choices=" + Arrays.toString(choices) + ']', 
SqlStateCode.CLIENT_CONNECTION_FAILED);
+            }
+        }
+
+        /**
          * @param ref Reference object.
          * @throws SQLException On error.
          */
@@ -626,7 +835,10 @@ public class ConnectionPropertiesImpl implements 
ConnectionProperties, Serializa
 
         /** {@inheritDoc} */
         @Override void init(String str) throws SQLException {
-            val = str;
+            if (str == null)
+                val = (String)dfltVal;
+            else
+                val = str;
         }
 
         /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
index 999c793..ac9925d 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
@@ -122,6 +122,11 @@ public class JdbcThinConnection implements Connection {
 
             cliIo.start();
         }
+        catch (SQLException e) {
+            cliIo.close();
+
+            throw e;
+        }
         catch (Exception e) {
             cliIo.close();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinSSLUtil.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinSSLUtil.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinSSLUtil.java
new file mode 100644
index 0000000..d259883
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinSSLUtil.java
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc.thin;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.FileSystems;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.cache.configuration.Factory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import org.apache.ignite.internal.processors.odbc.SqlStateCode;
+import org.apache.ignite.internal.util.typedef.F;
+
+/**
+ * SSL utility method to create SSL connetion.
+ */
+public class JdbcThinSSLUtil {
+    /** Trust all certificates manager. */
+    private final static X509TrustManager TRUST_ALL_MANAGER = new 
X509TrustManager() {
+        @Override public X509Certificate[] getAcceptedIssuers() {
+            return null;
+        }
+
+        @Override public void checkServerTrusted(X509Certificate[] arg0, 
String arg1) {
+        }
+
+        @Override public void checkClientTrusted(X509Certificate[] arg0, 
String arg1) {
+        }
+    };
+
+    /**
+     *
+     */
+    private JdbcThinSSLUtil() {
+        // No-op.
+    }
+
+    /**
+     * @param connProps Connection properties.
+     * @throws SQLException On connection error or reject.
+     * @throws IOException On IO error in handshake.
+     * @return SSL socket.
+     */
+    public static SSLSocket createSSLSocket(ConnectionProperties connProps) 
throws SQLException, IOException {
+        try {
+            SSLSocketFactory sslSocketFactory = getSSLSocketFactory(connProps);
+
+            SSLSocket sock = 
(SSLSocket)sslSocketFactory.createSocket(connProps.getHost(), 
connProps.getPort());
+
+            sock.setUseClientMode(true);
+
+            sock.startHandshake();
+
+            return sock;
+        }
+        catch (IOException e) {
+            throw new SQLException("Failed to SSL connect to server [host=" + 
connProps.getHost() +
+                ", port=" + connProps.getPort() + ']', 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+    }
+
+   /**
+     * @param connProps Connection properties.
+     * @return SSL socket factory.
+     * @throws SQLException On error.
+     */
+    private static SSLSocketFactory getSSLSocketFactory(ConnectionProperties 
connProps) throws SQLException {
+        String sslFactory = connProps.getSslFactory();
+        String cliCertKeyStoreUrl = 
connProps.getSslClientCertificateKeyStoreUrl();
+        String cliCertKeyStorePwd = 
connProps.getSslClientCertificateKeyStorePassword();
+        String cliCertKeyStoreType = 
connProps.getSslClientCertificateKeyStoreType();
+        String trustCertKeyStoreUrl = 
connProps.getSslTrustCertificateKeyStoreUrl();
+        String trustCertKeyStorePwd = 
connProps.getSslTrustCertificateKeyStorePassword();
+        String trustCertKeyStoreType = 
connProps.getSslTrustCertificateKeyStoreType();
+        String sslProtocol = connProps.getSslProtocol();
+        String keyAlgorithm = connProps.getSslKeyAlgorithm();
+
+        if (!F.isEmpty(sslFactory)) {
+            try {
+                Class<Factory<SSLSocketFactory>> cls = 
(Class<Factory<SSLSocketFactory>>)JdbcThinSSLUtil.class
+                    .getClassLoader().loadClass(sslFactory);
+
+                Factory<SSLSocketFactory> f = cls.newInstance();
+
+                return f.create();
+            }
+            catch (ClassNotFoundException | IllegalAccessException | 
InstantiationException e) {
+                throw new SQLException("Could not fount SSL factory class: " + 
sslFactory,
+                    SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+        }
+
+        if (cliCertKeyStoreUrl == null && cliCertKeyStorePwd == null && 
cliCertKeyStoreType == null
+            && trustCertKeyStoreUrl == null && trustCertKeyStorePwd == null && 
trustCertKeyStoreType == null
+            && sslProtocol == null) {
+            try {
+                return SSLContext.getDefault().getSocketFactory();
+            }
+            catch (NoSuchAlgorithmException e) {
+                throw new SQLException("Could not create default SSL context",
+                    SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+        }
+
+        if (cliCertKeyStoreUrl == null)
+            cliCertKeyStoreUrl = System.getProperty("javax.net.ssl.keyStore");
+
+        if (cliCertKeyStorePwd == null)
+            cliCertKeyStorePwd = 
System.getProperty("javax.net.ssl.keyStorePassword");
+
+        if (cliCertKeyStoreType == null)
+            cliCertKeyStoreType = 
System.getProperty("javax.net.ssl.keyStoreType", "JKS");
+
+        if (trustCertKeyStoreUrl == null)
+            trustCertKeyStoreUrl = 
System.getProperty("javax.net.ssl.trustStore");
+
+        if (trustCertKeyStorePwd == null)
+            trustCertKeyStorePwd = 
System.getProperty("javax.net.ssl.trustStorePassword");
+
+        if (trustCertKeyStoreType == null)
+            trustCertKeyStoreType = 
System.getProperty("javax.net.ssl.trustStoreType", "JKS");
+
+        if (sslProtocol == null)
+            sslProtocol = "TLS";
+
+        if (!F.isEmpty(cliCertKeyStoreUrl))
+            cliCertKeyStoreUrl = checkAndConvertUrl(cliCertKeyStoreUrl);
+
+        if (!F.isEmpty(trustCertKeyStoreUrl))
+            trustCertKeyStoreUrl = checkAndConvertUrl(trustCertKeyStoreUrl);
+
+        TrustManagerFactory tmf;
+        KeyManagerFactory kmf;
+
+        KeyManager[] kms = null;
+
+        try {
+            tmf = TrustManagerFactory.getInstance(keyAlgorithm);
+
+            kmf = KeyManagerFactory.getInstance(keyAlgorithm);
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new SQLException("Default algorithm definitions for 
TrustManager and/or KeyManager are invalid." +
+                " Check java security properties file.", 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+
+        InputStream ksInputStream = null;
+
+        try {
+            if (!F.isEmpty(cliCertKeyStoreUrl) && 
!F.isEmpty(cliCertKeyStoreType)) {
+                KeyStore clientKeyStore = 
KeyStore.getInstance(cliCertKeyStoreType);
+
+                URL ksURL = new URL(cliCertKeyStoreUrl);
+
+                char[] password = (cliCertKeyStorePwd == null) ? new char[0] : 
cliCertKeyStorePwd.toCharArray();
+
+                ksInputStream = ksURL.openStream();
+
+                clientKeyStore.load(ksInputStream, password);
+
+                kmf.init(clientKeyStore, password);
+
+                kms = kmf.getKeyManagers();
+            }
+        }
+        catch (UnrecoverableKeyException e) {
+            throw new SQLException("Could not recover keys from client 
keystore.",
+                SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new SQLException("Unsupported keystore algorithm.", 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+        catch (KeyStoreException e) {
+            throw new SQLException("Could not create client KeyStore 
instance.",
+                SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+        catch (CertificateException e) {
+            throw new SQLException("Could not load client key store. 
[storeType=" + cliCertKeyStoreType
+                + ", cliStoreUrl=" + cliCertKeyStoreUrl + ']', 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+        catch (MalformedURLException e) {
+            throw new SQLException("Invalid client key store URL. [url=" + 
cliCertKeyStoreUrl + ']',
+                SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+        catch (IOException e) {
+            throw new SQLException("Could not open client key store.[url=" + 
cliCertKeyStoreUrl + ']',
+                SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+        finally {
+            if (ksInputStream != null) {
+                try {
+                    ksInputStream.close();
+                }
+                catch (IOException e) {
+                    // can't close input stream, but keystore can be properly 
initialized
+                    // so we shouldn't throw this exception
+                }
+            }
+        }
+
+        InputStream tsInputStream = null;
+
+        List<TrustManager> tms;
+
+        if (connProps.isSslTrustAll())
+            tms = Collections.<TrustManager>singletonList(TRUST_ALL_MANAGER);
+        else {
+            tms = new ArrayList<>();
+
+            try {
+                KeyStore trustKeyStore = null;
+
+                if (!F.isEmpty(trustCertKeyStoreUrl) && 
!F.isEmpty(trustCertKeyStoreType)) {
+                    char[] trustStorePassword = (trustCertKeyStorePwd == null) 
? new char[0]
+                        : trustCertKeyStorePwd.toCharArray();
+
+                    tsInputStream = new URL(trustCertKeyStoreUrl).openStream();
+
+                    trustKeyStore = 
KeyStore.getInstance(trustCertKeyStoreType);
+
+                    trustKeyStore.load(tsInputStream, trustStorePassword);
+                }
+
+                tmf.init(trustKeyStore);
+
+                TrustManager[] origTms = tmf.getTrustManagers();
+
+                Collections.addAll(tms, origTms);
+            }
+            catch (NoSuchAlgorithmException e) {
+                throw new SQLException("Unsupported keystore algorithm.", 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+            catch (KeyStoreException e) {
+                throw new SQLException("Could not create trust KeyStore 
instance.",
+                    SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+            catch (CertificateException e) {
+                throw new SQLException("Could not load trusted key store. 
[storeType=" + trustCertKeyStoreType +
+                    ", cliStoreUrl=" + trustCertKeyStoreUrl + ']', 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+            catch (MalformedURLException e) {
+                throw new SQLException("Invalid trusted key store URL. [url=" 
+ trustCertKeyStoreUrl + ']',
+                    SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+            catch (IOException e) {
+                throw new SQLException("Could not open trusted key store. 
[url=" + cliCertKeyStoreUrl + ']',
+                    SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+            finally {
+                if (tsInputStream != null) {
+                    try {
+                        tsInputStream.close();
+                    }
+                    catch (IOException e) {
+                        // can't close input stream, but keystore can be 
properly initialized
+                        // so we shouldn't throw this exception
+                    }
+                }
+            }
+        }
+
+        assert tms.size() != 0;
+
+        try {
+            SSLContext sslContext = SSLContext.getInstance(sslProtocol);
+
+            sslContext.init(kms, tms.toArray(new TrustManager[tms.size()]), 
null);
+
+            return sslContext.getSocketFactory();
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new SQLException(sslProtocol + " is not a valid SSL 
protocol.", SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+        catch (KeyManagementException e) {
+            throw new SQLException("Cannot init SSL context.", 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
+    }
+
+    /**
+     * @param url URL or path to check and convert to URL.
+     * @return URL.
+     * @throws SQLException If URL is invalid.
+     */
+    private static String checkAndConvertUrl(String url) throws SQLException {
+        try {
+            return new URL(url).toString();
+        }
+        catch (MalformedURLException e) {
+            try {
+                return 
FileSystems.getDefault().getPath(url).toUri().toURL().toString();
+            }
+            catch (MalformedURLException e1) {
+                throw new SQLException("Invalid keystore UR: " + url,
+                    SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/42161cdd/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
index fec218e..4b2c477 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
@@ -20,27 +20,9 @@ package org.apache.ignite.internal.jdbc.thin;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.InetSocketAddress;
-import java.net.MalformedURLException;
 import java.net.Socket;
-import java.net.URL;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
 import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.binary.BinaryReaderExImpl;
 import org.apache.ignite.internal.binary.BinaryWriterExImpl;
@@ -57,7 +39,6 @@ import 
org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryMetadataRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResponse;
 import org.apache.ignite.internal.util.ipc.loopback.IpcClientTcpEndpoint;
-import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteProductVersion;
 
@@ -130,7 +111,25 @@ public class JdbcThinTcpIo {
      * @throws IOException On IO error in handshake.
      */
     public void start() throws SQLException, IOException {
-        Socket sock = new Socket();
+        Socket sock;
+
+        if 
(ConnectionProperties.SSL_MODE_REQUIRE.equalsIgnoreCase(connProps.getSslMode()))
+            sock = JdbcThinSSLUtil.createSSLSocket(connProps);
+        else if 
(ConnectionProperties.SSL_MODE_DISABLE.equalsIgnoreCase(connProps.getSslMode()))
 {
+            sock = new Socket();
+
+            try {
+                sock.connect(new InetSocketAddress(connProps.getHost(), 
connProps.getPort()));
+            }
+            catch (IOException e) {
+                throw new SQLException("Failed to connect to server [host=" + 
connProps.getHost() +
+                    ", port=" + connProps.getPort() + ']', 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            }
+        }
+        else {
+            throw new SQLException("Unknown sslMode. [sslMode=" + 
connProps.getSslMode() + ']',
+                SqlStateCode.CLIENT_CONNECTION_FAILED);
+        }
 
         if (connProps.getSocketSendBuffer() != 0)
             sock.setSendBufferSize(connProps.getSocketSendBuffer());
@@ -141,14 +140,12 @@ public class JdbcThinTcpIo {
         sock.setTcpNoDelay(connProps.isTcpNoDelay());
 
         try {
-            sock.connect(new InetSocketAddress(connProps.getHost(), 
connProps.getPort()));
-
             endpoint = new IpcClientTcpEndpoint(sock);
 
             out = new BufferedOutputStream(endpoint.outputStream());
             in = new BufferedInputStream(endpoint.inputStream());
         }
-        catch (IOException | IgniteCheckedException e) {
+        catch (IgniteCheckedException e) {
             throw new SQLException("Failed to connect to server [host=" + 
connProps.getHost() +
                 ", port=" + connProps.getPort() + ']', 
SqlStateCode.CLIENT_CONNECTION_FAILED, e);
         }

Reply via email to