1 | /* |
---|
2 | Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. |
---|
3 | |
---|
4 | |
---|
5 | The MySQL Connector/J is licensed under the terms of the GPLv2 |
---|
6 | <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors. |
---|
7 | There are special exceptions to the terms and conditions of the GPLv2 as it is applied to |
---|
8 | this software, see the FLOSS License Exception |
---|
9 | <http://www.mysql.com/about/legal/licensing/foss-exception.html>. |
---|
10 | |
---|
11 | This program is free software; you can redistribute it and/or modify it under the terms |
---|
12 | of the GNU General Public License as published by the Free Software Foundation; version 2 |
---|
13 | of the License. |
---|
14 | |
---|
15 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
---|
16 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
---|
17 | See the GNU General Public License for more details. |
---|
18 | |
---|
19 | You should have received a copy of the GNU General Public License along with this |
---|
20 | program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth |
---|
21 | Floor, Boston, MA 02110-1301 USA |
---|
22 | |
---|
23 | |
---|
24 | |
---|
25 | */ |
---|
26 | package com.mysql.jdbc; |
---|
27 | |
---|
28 | import java.io.BufferedInputStream; |
---|
29 | import java.io.BufferedOutputStream; |
---|
30 | import java.io.IOException; |
---|
31 | import java.net.MalformedURLException; |
---|
32 | import java.net.URL; |
---|
33 | import java.security.KeyManagementException; |
---|
34 | import java.security.KeyStore; |
---|
35 | import java.security.KeyStoreException; |
---|
36 | import java.security.NoSuchAlgorithmException; |
---|
37 | import java.security.UnrecoverableKeyException; |
---|
38 | import java.security.cert.CertificateException; |
---|
39 | import java.security.cert.X509Certificate; |
---|
40 | import java.sql.SQLException; |
---|
41 | |
---|
42 | import javax.net.ssl.KeyManagerFactory; |
---|
43 | import javax.net.ssl.SSLContext; |
---|
44 | import javax.net.ssl.SSLSocketFactory; |
---|
45 | import javax.net.ssl.TrustManagerFactory; |
---|
46 | import javax.net.ssl.X509TrustManager; |
---|
47 | |
---|
48 | /** |
---|
49 | * Holds functionality that falls under export-control regulations. |
---|
50 | * |
---|
51 | * @author Mark Matthews |
---|
52 | * |
---|
53 | * @version $Id: ExportControlled.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews |
---|
54 | * Exp $ |
---|
55 | */ |
---|
56 | public class ExportControlled { |
---|
57 | private static final String SQL_STATE_BAD_SSL_PARAMS = "08000"; |
---|
58 | |
---|
59 | protected static boolean enabled() { |
---|
60 | // we may wish to un-static-ify this class |
---|
61 | // this static method call may be removed entirely by the compiler |
---|
62 | return true; |
---|
63 | } |
---|
64 | |
---|
65 | /** |
---|
66 | * Converts the socket being used in the given MysqlIO to an SSLSocket by |
---|
67 | * performing the SSL/TLS handshake. |
---|
68 | * |
---|
69 | * @param mysqlIO |
---|
70 | * the MysqlIO instance containing the socket to convert to an |
---|
71 | * SSLSocket. |
---|
72 | * |
---|
73 | * @throws CommunicationsException |
---|
74 | * if the handshake fails, or if this distribution of |
---|
75 | * Connector/J doesn't contain the SSL crytpo hooks needed to |
---|
76 | * perform the handshake. |
---|
77 | */ |
---|
78 | protected static void transformSocketToSSLSocket(MysqlIO mysqlIO) |
---|
79 | throws SQLException { |
---|
80 | javax.net.ssl.SSLSocketFactory sslFact = getSSLSocketFactoryDefaultOrConfigured(mysqlIO); |
---|
81 | |
---|
82 | try { |
---|
83 | mysqlIO.mysqlConnection = sslFact.createSocket( |
---|
84 | mysqlIO.mysqlConnection, mysqlIO.host, mysqlIO.port, true); |
---|
85 | |
---|
86 | // need to force TLSv1, or else JSSE tries to do a SSLv2 handshake |
---|
87 | // which MySQL doesn't understand |
---|
88 | ((javax.net.ssl.SSLSocket) mysqlIO.mysqlConnection) |
---|
89 | .setEnabledProtocols(new String[] { "TLSv1" }); //$NON-NLS-1$ |
---|
90 | ((javax.net.ssl.SSLSocket) mysqlIO.mysqlConnection) |
---|
91 | .startHandshake(); |
---|
92 | |
---|
93 | if (mysqlIO.connection.getUseUnbufferedInput()) { |
---|
94 | mysqlIO.mysqlInput = mysqlIO.mysqlConnection.getInputStream(); |
---|
95 | } else { |
---|
96 | mysqlIO.mysqlInput = new BufferedInputStream( |
---|
97 | mysqlIO.mysqlConnection.getInputStream(), 16384); |
---|
98 | } |
---|
99 | |
---|
100 | mysqlIO.mysqlOutput = new BufferedOutputStream( |
---|
101 | mysqlIO.mysqlConnection.getOutputStream(), 16384); |
---|
102 | |
---|
103 | mysqlIO.mysqlOutput.flush(); |
---|
104 | } catch (IOException ioEx) { |
---|
105 | throw SQLError.createCommunicationsException(mysqlIO.connection, |
---|
106 | mysqlIO.getLastPacketSentTimeMs(), mysqlIO.getLastPacketReceivedTimeMs(), |
---|
107 | ioEx, mysqlIO.getExceptionInterceptor()); |
---|
108 | } |
---|
109 | } |
---|
110 | |
---|
111 | private ExportControlled() { /* prevent instantiation */ |
---|
112 | } |
---|
113 | |
---|
114 | private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured( |
---|
115 | MysqlIO mysqlIO) throws SQLException { |
---|
116 | String clientCertificateKeyStoreUrl = mysqlIO.connection |
---|
117 | .getClientCertificateKeyStoreUrl(); |
---|
118 | String trustCertificateKeyStoreUrl = mysqlIO.connection |
---|
119 | .getTrustCertificateKeyStoreUrl(); |
---|
120 | String clientCertificateKeyStoreType = mysqlIO.connection |
---|
121 | .getClientCertificateKeyStoreType(); |
---|
122 | String clientCertificateKeyStorePassword = mysqlIO.connection |
---|
123 | .getClientCertificateKeyStorePassword(); |
---|
124 | String trustCertificateKeyStoreType = mysqlIO.connection |
---|
125 | .getTrustCertificateKeyStoreType(); |
---|
126 | String trustCertificateKeyStorePassword = mysqlIO.connection |
---|
127 | .getTrustCertificateKeyStorePassword(); |
---|
128 | |
---|
129 | if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl) |
---|
130 | && StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) { |
---|
131 | if (mysqlIO.connection.getVerifyServerCertificate()) { |
---|
132 | return (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory |
---|
133 | .getDefault(); |
---|
134 | } |
---|
135 | } |
---|
136 | |
---|
137 | TrustManagerFactory tmf = null; |
---|
138 | KeyManagerFactory kmf = null; |
---|
139 | |
---|
140 | try { |
---|
141 | tmf = TrustManagerFactory.getInstance(TrustManagerFactory |
---|
142 | .getDefaultAlgorithm()); |
---|
143 | kmf = KeyManagerFactory.getInstance(KeyManagerFactory |
---|
144 | .getDefaultAlgorithm()); |
---|
145 | } catch (NoSuchAlgorithmException nsae) { |
---|
146 | throw SQLError |
---|
147 | .createSQLException( |
---|
148 | "Default algorithm definitions for TrustManager and/or KeyManager are invalid. Check java security properties file.", |
---|
149 | SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
150 | } |
---|
151 | |
---|
152 | if (!StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) { |
---|
153 | try { |
---|
154 | if (!StringUtils.isNullOrEmpty(clientCertificateKeyStoreType)) { |
---|
155 | KeyStore clientKeyStore = KeyStore |
---|
156 | .getInstance(clientCertificateKeyStoreType); |
---|
157 | URL ksURL = new URL(clientCertificateKeyStoreUrl); |
---|
158 | char[] password = (clientCertificateKeyStorePassword == null) ? new char[0] |
---|
159 | : clientCertificateKeyStorePassword.toCharArray(); |
---|
160 | clientKeyStore.load(ksURL.openStream(), password); |
---|
161 | kmf.init(clientKeyStore, password); |
---|
162 | } |
---|
163 | } catch (UnrecoverableKeyException uke) { |
---|
164 | throw SQLError |
---|
165 | .createSQLException( |
---|
166 | "Could not recover keys from client keystore. Check password?", |
---|
167 | SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
168 | } catch (NoSuchAlgorithmException nsae) { |
---|
169 | throw SQLError.createSQLException( |
---|
170 | "Unsupported keystore algorithm [" + nsae.getMessage() |
---|
171 | + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
172 | } catch (KeyStoreException kse) { |
---|
173 | throw SQLError.createSQLException( |
---|
174 | "Could not create KeyStore instance [" |
---|
175 | + kse.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
176 | } catch (CertificateException nsae) { |
---|
177 | throw SQLError.createSQLException("Could not load client" |
---|
178 | + clientCertificateKeyStoreType + " keystore from " |
---|
179 | + clientCertificateKeyStoreUrl, mysqlIO.getExceptionInterceptor()); |
---|
180 | } catch (MalformedURLException mue) { |
---|
181 | throw SQLError.createSQLException(clientCertificateKeyStoreUrl |
---|
182 | + " does not appear to be a valid URL.", SQL_STATE_BAD_SSL_PARAMS, 0, |
---|
183 | false, mysqlIO.getExceptionInterceptor()); |
---|
184 | } catch (IOException ioe) { |
---|
185 | SQLException sqlEx = SQLError.createSQLException("Cannot open " |
---|
186 | + clientCertificateKeyStoreUrl + " [" |
---|
187 | + ioe.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
188 | sqlEx.initCause(ioe); |
---|
189 | |
---|
190 | throw sqlEx; |
---|
191 | } |
---|
192 | } |
---|
193 | |
---|
194 | if (!StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) { |
---|
195 | |
---|
196 | try { |
---|
197 | if (!StringUtils.isNullOrEmpty(trustCertificateKeyStoreType)) { |
---|
198 | KeyStore trustKeyStore = KeyStore |
---|
199 | .getInstance(trustCertificateKeyStoreType); |
---|
200 | URL ksURL = new URL(trustCertificateKeyStoreUrl); |
---|
201 | |
---|
202 | char[] password = (trustCertificateKeyStorePassword == null) ? new char[0] |
---|
203 | : trustCertificateKeyStorePassword.toCharArray(); |
---|
204 | trustKeyStore.load(ksURL.openStream(), password); |
---|
205 | tmf.init(trustKeyStore); |
---|
206 | } |
---|
207 | } catch (NoSuchAlgorithmException nsae) { |
---|
208 | throw SQLError.createSQLException( |
---|
209 | "Unsupported keystore algorithm [" + nsae.getMessage() |
---|
210 | + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
211 | } catch (KeyStoreException kse) { |
---|
212 | throw SQLError.createSQLException( |
---|
213 | "Could not create KeyStore instance [" |
---|
214 | + kse.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
215 | } catch (CertificateException nsae) { |
---|
216 | throw SQLError.createSQLException("Could not load trust" |
---|
217 | + trustCertificateKeyStoreType + " keystore from " |
---|
218 | + trustCertificateKeyStoreUrl, SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
219 | } catch (MalformedURLException mue) { |
---|
220 | throw SQLError.createSQLException(trustCertificateKeyStoreUrl |
---|
221 | + " does not appear to be a valid URL.", SQL_STATE_BAD_SSL_PARAMS, 0, |
---|
222 | false, mysqlIO.getExceptionInterceptor()); |
---|
223 | } catch (IOException ioe) { |
---|
224 | SQLException sqlEx = SQLError.createSQLException("Cannot open " |
---|
225 | + trustCertificateKeyStoreUrl + " [" + ioe.getMessage() |
---|
226 | + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
227 | |
---|
228 | sqlEx.initCause(ioe); |
---|
229 | |
---|
230 | throw sqlEx; |
---|
231 | } |
---|
232 | } |
---|
233 | |
---|
234 | SSLContext sslContext = null; |
---|
235 | |
---|
236 | try { |
---|
237 | sslContext = SSLContext.getInstance("TLS"); |
---|
238 | sslContext.init(StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl) ? null : kmf.getKeyManagers(), mysqlIO.connection |
---|
239 | .getVerifyServerCertificate() ? tmf.getTrustManagers() |
---|
240 | : new X509TrustManager[] { new X509TrustManager() { |
---|
241 | public void checkClientTrusted(X509Certificate[] chain, |
---|
242 | String authType) { |
---|
243 | // return without complaint |
---|
244 | } |
---|
245 | |
---|
246 | public void checkServerTrusted(X509Certificate[] chain, |
---|
247 | String authType) throws CertificateException { |
---|
248 | // return without complaint |
---|
249 | } |
---|
250 | |
---|
251 | public X509Certificate[] getAcceptedIssuers() { |
---|
252 | return null; |
---|
253 | } |
---|
254 | } }, null); |
---|
255 | |
---|
256 | return sslContext.getSocketFactory(); |
---|
257 | } catch (NoSuchAlgorithmException nsae) { |
---|
258 | throw SQLError.createSQLException("TLS" |
---|
259 | + " is not a valid SSL protocol.", |
---|
260 | SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
261 | } catch (KeyManagementException kme) { |
---|
262 | throw SQLError.createSQLException("KeyManagementException: " |
---|
263 | + kme.getMessage(), SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); |
---|
264 | } |
---|
265 | } |
---|
266 | } |
---|