|
14 | 14 | package com.ibm.as400.access;
|
15 | 15 |
|
16 | 16 | import java.io.Serializable;
|
| 17 | +import java.net.InetAddress; |
| 18 | +import java.net.Socket; |
| 19 | +import java.net.UnknownHostException; |
| 20 | +import java.security.KeyStore; |
| 21 | +import java.security.cert.CertificateException; |
| 22 | +import java.security.cert.X509Certificate; |
| 23 | +import java.io.FileInputStream; |
17 | 24 | import java.io.IOException; // @W2a
|
18 | 25 | import java.sql.DriverPropertyInfo;
|
19 | 26 |
|
20 | 27 | import java.util.Enumeration;
|
21 | 28 | import java.util.Properties;
|
22 | 29 |
|
| 30 | +import javax.net.ssl.SSLContext; |
| 31 | +import javax.net.ssl.SSLSocketFactory; |
| 32 | +import javax.net.ssl.TrustManagerFactory; |
| 33 | +import javax.net.ssl.TrustManager; |
| 34 | +import javax.net.ssl.X509TrustManager; |
| 35 | + |
23 | 36 |
|
24 | 37 |
|
25 | 38 | /**
|
@@ -181,9 +194,12 @@ public class JDProperties implements Serializable, Cloneable //@PDC 550
|
181 | 194 | static final int ADDITIONAL_AUTHENTICATION_FACTOR=101;
|
182 | 195 | static final int STAY_ALIVE = 102;
|
183 | 196 |
|
| 197 | + static final int TLS_TRUSTSTORE_FILE = 103; |
| 198 | + static final int TLS_TRUSTSTORE_FILE_PASS = 104; |
| 199 | + |
184 | 200 | // @W2 always add to the end of the array!
|
185 | 201 |
|
186 |
| - private static final int NUMBER_OF_ATTRIBUTES_ = 103; |
| 202 | + private static final int NUMBER_OF_ATTRIBUTES_ = 105; |
187 | 203 |
|
188 | 204 |
|
189 | 205 | // Property names.
|
@@ -254,6 +270,8 @@ public class JDProperties implements Serializable, Cloneable //@PDC 550
|
254 | 270 | private static final String TIME_FORMAT_ = "time format";
|
255 | 271 | private static final String TIMESTAMP_FORMAT_ = "timestamp format";
|
256 | 272 | private static final String TIME_SEPARATOR_ = "time separator";
|
| 273 | + private static final String TLS_TRUSTSTORE_FILE_ = "tls truststore"; |
| 274 | + private static final String TLS_TRUSTSTORE_FILE_PASS_ = "tls truststore password"; |
257 | 275 | private static final String TRACE_ = "trace";
|
258 | 276 | private static final String TRACE_SERVER_ = "server trace"; // @j1a
|
259 | 277 | private static final String TRACE_TOOLBOX_ = "toolbox trace"; // @K1A
|
@@ -1652,8 +1670,22 @@ public class JDProperties implements Serializable, Cloneable //@PDC 550
|
1652 | 1670 | dpi_[i].required = false;
|
1653 | 1671 | dpi_[i].choices = new String[0];
|
1654 | 1672 | defaults_[i] = "0";
|
| 1673 | + |
| 1674 | + i = TLS_TRUSTSTORE_FILE; |
| 1675 | + dpi_[i] = new DriverPropertyInfo (TLS_TRUSTSTORE_FILE_, ""); |
| 1676 | + dpi_[i].description = "TLS_TRUSTSTORE_FILE"; |
| 1677 | + dpi_[i].required = false; |
| 1678 | + dpi_[i].choices = new String[0]; |
| 1679 | + defaults_[i] = EMPTY_; |
1655 | 1680 |
|
1656 | 1681 |
|
| 1682 | + i = TLS_TRUSTSTORE_FILE_PASS; |
| 1683 | + dpi_[i] = new DriverPropertyInfo (TLS_TRUSTSTORE_FILE_PASS_, ""); |
| 1684 | + dpi_[i].description = "TLS_TRUSTSTORE_FILE_PASS"; |
| 1685 | + dpi_[i].required = false; |
| 1686 | + dpi_[i].choices = new String[0]; |
| 1687 | + defaults_[i] = EMPTY_; |
| 1688 | + |
1657 | 1689 | }
|
1658 | 1690 |
|
1659 | 1691 |
|
@@ -2019,6 +2051,112 @@ String getString (int index)
|
2019 | 2051 | return value.trim();
|
2020 | 2052 | }
|
2021 | 2053 |
|
| 2054 | + /** |
| 2055 | + * Gets a custom SSL Socket Factory, or returns <tt>null</tt> if no custom SSL Socket factory is specified |
| 2056 | + * (in which case, the system default factory will be used). |
| 2057 | + * |
| 2058 | + * The custom SSL Socket Factory is determined as follows: |
| 2059 | + * <ul> |
| 2060 | + * <li>If an {@link SSLSocketFactory} object was provided through the special property defined by |
| 2061 | + * {@link AS400JDBCDriver#PROPERTY_SSL_SOCKET_FACTORY}, all other properties are ignored and |
| 2062 | + * that object is returned. |
| 2063 | + * <li>A {@link SSLSocketFactory} will be created if both the {@value #TLS_TRUSTSTORE_FILE_} {@value #TLS_TRUSTSTORE_FILE_PASS_} |
| 2064 | + * properties were specified, indicating a JKS-format truststore file and password. Note that the special value '*ANY' |
| 2065 | + * can be used to disable all verification. |
| 2066 | + * </ul> |
| 2067 | + */ |
| 2068 | + SSLSocketFactory getCustomSSLSocketFactory() { |
| 2069 | + Properties originalProps = this.getOriginalInfo(); |
| 2070 | + Object sslSocketFactoryObject = null == originalProps ? null: originalProps.get(AS400JDBCDriver.PROPERTY_SSL_SOCKET_FACTORY); |
| 2071 | + if ((sslSocketFactoryObject != null) && (sslSocketFactoryObject instanceof SSLSocketFactory)) { |
| 2072 | + return (SSLSocketFactory) sslSocketFactoryObject; |
| 2073 | + } |
| 2074 | + final String truststoreFile = getString(TLS_TRUSTSTORE_FILE); |
| 2075 | + final String truststorePass = getString(TLS_TRUSTSTORE_FILE_PASS); |
| 2076 | + if (null != truststoreFile && null != truststorePass && !truststoreFile.isEmpty() |
| 2077 | + && !truststorePass.isEmpty()) { |
| 2078 | + return new SSLSocketFactory() { |
| 2079 | + private SSLSocketFactory sslSocketFactory_ = null; |
| 2080 | + |
| 2081 | + private synchronized SSLSocketFactory getSSLSocketFactory() throws IOException { |
| 2082 | + if (null != sslSocketFactory_) { |
| 2083 | + return sslSocketFactory_; |
| 2084 | + } |
| 2085 | + if ("*ANY".equalsIgnoreCase(truststoreFile) && "*ANY".equalsIgnoreCase(truststorePass)) { |
| 2086 | + try { |
| 2087 | + SSLContext ctx = SSLContext.getInstance("TLS"); |
| 2088 | + //@formatter:off |
| 2089 | + ctx.init(null, new TrustManager[] { |
| 2090 | + new X509TrustManager() { |
| 2091 | + @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } |
| 2092 | + @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } |
| 2093 | + @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } |
| 2094 | + } |
| 2095 | + }, null); |
| 2096 | + //@formatter:on |
| 2097 | + return sslSocketFactory_ = ctx.getSocketFactory(); |
| 2098 | + } catch (Exception e) { |
| 2099 | + throw e instanceof IOException ? (IOException) e : new IOException(e); |
| 2100 | + } |
| 2101 | + } |
| 2102 | + try (FileInputStream trustFile = new FileInputStream(truststoreFile)) { |
| 2103 | + KeyStore myTrustStore = KeyStore.getInstance("JKS"); |
| 2104 | + myTrustStore.load(trustFile, truststorePass.toCharArray()); |
| 2105 | + TrustManagerFactory trustManagerFactory = TrustManagerFactory |
| 2106 | + .getInstance(TrustManagerFactory.getDefaultAlgorithm()); |
| 2107 | + trustManagerFactory.init(myTrustStore); |
| 2108 | + SSLContext ctx = SSLContext.getInstance("TLS"); |
| 2109 | + ctx.init(null, trustManagerFactory.getTrustManagers(), null); |
| 2110 | + return sslSocketFactory_ = ctx.getSocketFactory(); |
| 2111 | + } catch (Exception e) { |
| 2112 | + throw e instanceof IOException ? (IOException) e : new IOException(e); |
| 2113 | + } |
| 2114 | + } |
| 2115 | + |
| 2116 | + //@formatter:off |
| 2117 | + @Override |
| 2118 | + public String[] getDefaultCipherSuites() { |
| 2119 | + try { return getSSLSocketFactory().getDefaultCipherSuites();} catch (Exception e) { } |
| 2120 | + return ((SSLSocketFactory) SSLSocketFactory.getDefault()).getDefaultCipherSuites(); |
| 2121 | + } |
| 2122 | + |
| 2123 | + @Override |
| 2124 | + public String[] getSupportedCipherSuites() { |
| 2125 | + try { return getSSLSocketFactory().getSupportedCipherSuites(); } catch (Exception e) { } |
| 2126 | + return ((SSLSocketFactory) SSLSocketFactory.getDefault()).getSupportedCipherSuites(); |
| 2127 | + } |
| 2128 | + |
| 2129 | + @Override |
| 2130 | + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { |
| 2131 | + return getSSLSocketFactory().createSocket(s, host, port, autoClose); |
| 2132 | + } |
| 2133 | + |
| 2134 | + @Override |
| 2135 | + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { |
| 2136 | + return getSSLSocketFactory().createSocket(host, port); |
| 2137 | + } |
| 2138 | + |
| 2139 | + @Override |
| 2140 | + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { |
| 2141 | + return getSSLSocketFactory().createSocket(host, port, localHost, localPort); |
| 2142 | + } |
| 2143 | + |
| 2144 | + @Override |
| 2145 | + public Socket createSocket(InetAddress host, int port) throws IOException { |
| 2146 | + return getSSLSocketFactory().createSocket(host, port); |
| 2147 | + } |
| 2148 | + |
| 2149 | + @Override |
| 2150 | + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException { |
| 2151 | + return getSSLSocketFactory().createSocket(address, port, localAddress, localPort); |
| 2152 | + } |
| 2153 | + //@formatter:on |
| 2154 | + |
| 2155 | + }; |
| 2156 | + } |
| 2157 | + return null; |
| 2158 | + } |
| 2159 | + |
2022 | 2160 | /**
|
2023 | 2161 | * Get the clear password. The caller is responsible for clearing the array
|
2024 | 2162 | * after it is done with the password
|
|
0 commit comments