001: /*
002: Copyright (C) 2002-2004 MySQL AB
003:
004: This program is free software; you can redistribute it and/or modify
005: it under the terms of version 2 of the GNU General Public License as
006: published by the Free Software Foundation.
007:
008: There are special exceptions to the terms and conditions of the GPL
009: as it is applied to this software. View the full text of the
010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
011: software distribution.
012:
013: This program is distributed in the hope that it will be useful,
014: but WITHOUT ANY WARRANTY; without even the implied warranty of
015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: GNU General Public License for more details.
017:
018: You should have received a copy of the GNU General Public License
019: along with this program; if not, write to the Free Software
020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021:
022:
023:
024: */
025: package com.mysql.jdbc;
026:
027: import java.io.BufferedInputStream;
028: import java.io.BufferedOutputStream;
029: import java.io.IOException;
030: import java.net.MalformedURLException;
031: import java.net.URL;
032: import java.security.KeyManagementException;
033: import java.security.KeyStore;
034: import java.security.KeyStoreException;
035: import java.security.NoSuchAlgorithmException;
036: import java.security.UnrecoverableKeyException;
037: import java.security.cert.CertificateException;
038: import java.sql.SQLException;
039:
040: import javax.net.ssl.KeyManagerFactory;
041: import javax.net.ssl.SSLContext;
042: import javax.net.ssl.SSLSocketFactory;
043: import javax.net.ssl.TrustManagerFactory;
044:
045: /**
046: * Holds functionality that falls under export-control regulations.
047: *
048: * @author Mark Matthews
049: *
050: * @version $Id: ExportControlled.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
051: * Exp $
052: */
053: public class ExportControlled {
054: private static final String SQL_STATE_BAD_SSL_PARAMS = "08000";
055:
056: protected static boolean enabled() {
057: // we may wish to un-static-ify this class
058: // this static method call may be removed entirely by the compiler
059: return true;
060: }
061:
062: /**
063: * Converts the socket being used in the given MysqlIO to an SSLSocket by
064: * performing the SSL/TLS handshake.
065: *
066: * @param mysqlIO
067: * the MysqlIO instance containing the socket to convert to an
068: * SSLSocket.
069: *
070: * @throws CommunicationsException
071: * if the handshake fails, or if this distribution of
072: * Connector/J doesn't contain the SSL crytpo hooks needed to
073: * perform the handshake.
074: */
075: protected static void transformSocketToSSLSocket(MysqlIO mysqlIO)
076: throws SQLException {
077: javax.net.ssl.SSLSocketFactory sslFact = getSSLSocketFactoryDefaultOrConfigured(mysqlIO);
078:
079: try {
080: mysqlIO.mysqlConnection = sslFact.createSocket(
081: mysqlIO.mysqlConnection, mysqlIO.host,
082: mysqlIO.port, true);
083:
084: // need to force TLSv1, or else JSSE tries to do a SSLv2 handshake
085: // which MySQL doesn't understand
086: ((javax.net.ssl.SSLSocket) mysqlIO.mysqlConnection)
087: .setEnabledProtocols(new String[] { "TLSv1" }); //$NON-NLS-1$
088: ((javax.net.ssl.SSLSocket) mysqlIO.mysqlConnection)
089: .startHandshake();
090:
091: if (mysqlIO.connection.getUseUnbufferedInput()) {
092: mysqlIO.mysqlInput = mysqlIO.mysqlConnection
093: .getInputStream();
094: } else {
095: mysqlIO.mysqlInput = new BufferedInputStream(
096: mysqlIO.mysqlConnection.getInputStream(), 16384);
097: }
098:
099: mysqlIO.mysqlOutput = new BufferedOutputStream(
100: mysqlIO.mysqlConnection.getOutputStream(), 16384);
101:
102: mysqlIO.mysqlOutput.flush();
103: } catch (IOException ioEx) {
104: throw SQLError.createCommunicationsException(
105: mysqlIO.connection, mysqlIO.lastPacketSentTimeMs,
106: ioEx);
107: }
108: }
109:
110: private ExportControlled() { /* prevent instantiation */
111: }
112:
113: private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured(
114: MysqlIO mysqlIO) throws SQLException {
115: String clientCertificateKeyStoreUrl = mysqlIO.connection
116: .getClientCertificateKeyStoreUrl();
117: String trustCertificateKeyStoreUrl = mysqlIO.connection
118: .getTrustCertificateKeyStoreUrl();
119: String clientCertificateKeyStoreType = mysqlIO.connection
120: .getClientCertificateKeyStoreType();
121: String clientCertificateKeyStorePassword = mysqlIO.connection
122: .getClientCertificateKeyStorePassword();
123: String trustCertificateKeyStoreType = mysqlIO.connection
124: .getTrustCertificateKeyStoreType();
125: String trustCertificateKeyStorePassword = mysqlIO.connection
126: .getTrustCertificateKeyStorePassword();
127:
128: if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)
129: && StringUtils
130: .isNullOrEmpty(trustCertificateKeyStoreUrl)) {
131: return (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory
132: .getDefault();
133: }
134:
135: TrustManagerFactory tmf = null;
136: KeyManagerFactory kmf = null;
137:
138: try {
139: tmf = TrustManagerFactory.getInstance(TrustManagerFactory
140: .getDefaultAlgorithm());
141: kmf = KeyManagerFactory.getInstance(KeyManagerFactory
142: .getDefaultAlgorithm());
143: } catch (NoSuchAlgorithmException nsae) {
144: throw SQLError
145: .createSQLException(
146: "Default algorithm definitions for TrustManager and/or KeyManager are invalid. Check java security properties file.",
147: SQL_STATE_BAD_SSL_PARAMS, 0, false);
148: }
149:
150: if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) {
151: try {
152: KeyStore clientKeyStore = KeyStore
153: .getInstance(clientCertificateKeyStoreType);
154: URL ksURL = new URL(clientCertificateKeyStoreUrl);
155: char[] password = (clientCertificateKeyStorePassword == null) ? new char[0]
156: : clientCertificateKeyStorePassword
157: .toCharArray();
158: clientKeyStore.load(ksURL.openStream(), password);
159: kmf.init(clientKeyStore, password);
160: } catch (UnrecoverableKeyException uke) {
161: throw SQLError
162: .createSQLException(
163: "Could not recover keys from client keystore. Check password?",
164: SQL_STATE_BAD_SSL_PARAMS, 0, false);
165: } catch (NoSuchAlgorithmException nsae) {
166: throw SQLError.createSQLException(
167: "Unsupported keystore algorithm ["
168: + nsae.getMessage() + "]",
169: SQL_STATE_BAD_SSL_PARAMS, 0, false);
170: } catch (KeyStoreException kse) {
171: throw SQLError.createSQLException(
172: "Could not create KeyStore instance ["
173: + kse.getMessage() + "]",
174: SQL_STATE_BAD_SSL_PARAMS, 0, false);
175: } catch (CertificateException nsae) {
176: throw SQLError
177: .createSQLException("Could not load client"
178: + clientCertificateKeyStoreType
179: + " keystore from "
180: + clientCertificateKeyStoreUrl);
181: } catch (MalformedURLException mue) {
182: throw SQLError
183: .createSQLException(
184: clientCertificateKeyStoreUrl
185: + " does not appear to be a valid URL.",
186: SQL_STATE_BAD_SSL_PARAMS, 0, false);
187: } catch (IOException ioe) {
188: throw SQLError.createSQLException("Cannot open "
189: + clientCertificateKeyStoreUrl + " ["
190: + ioe.getMessage() + "]",
191: SQL_STATE_BAD_SSL_PARAMS, 0, false);
192: }
193: }
194:
195: if (StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
196:
197: try {
198: KeyStore trustKeyStore = KeyStore
199: .getInstance(trustCertificateKeyStoreType);
200: URL ksURL = new URL(trustCertificateKeyStoreUrl);
201:
202: char[] password = (trustCertificateKeyStorePassword == null) ? new char[0]
203: : trustCertificateKeyStorePassword
204: .toCharArray();
205: trustKeyStore.load(ksURL.openStream(), password);
206: tmf.init(trustKeyStore);
207: } catch (NoSuchAlgorithmException nsae) {
208: throw SQLError.createSQLException(
209: "Unsupported keystore algorithm ["
210: + nsae.getMessage() + "]",
211: SQL_STATE_BAD_SSL_PARAMS, 0, false);
212: } catch (KeyStoreException kse) {
213: throw SQLError.createSQLException(
214: "Could not create KeyStore instance ["
215: + kse.getMessage() + "]",
216: SQL_STATE_BAD_SSL_PARAMS, 0, false);
217: } catch (CertificateException nsae) {
218: throw SQLError.createSQLException(
219: "Could not load trust"
220: + trustCertificateKeyStoreType
221: + " keystore from "
222: + trustCertificateKeyStoreUrl,
223: SQL_STATE_BAD_SSL_PARAMS, 0, false);
224: } catch (MalformedURLException mue) {
225: throw SQLError
226: .createSQLException(
227: trustCertificateKeyStoreUrl
228: + " does not appear to be a valid URL.",
229: SQL_STATE_BAD_SSL_PARAMS, 0, false);
230: } catch (IOException ioe) {
231: throw SQLError.createSQLException("Cannot open "
232: + trustCertificateKeyStoreUrl + " ["
233: + ioe.getMessage() + "]",
234: SQL_STATE_BAD_SSL_PARAMS, 0, false);
235: }
236: }
237:
238: SSLContext sslContext = null;
239:
240: try {
241: sslContext = SSLContext.getInstance("TLS");
242: sslContext.init(kmf.getKeyManagers(), tmf
243: .getTrustManagers(), null);
244:
245: return sslContext.getSocketFactory();
246: } catch (NoSuchAlgorithmException nsae) {
247: throw SQLError.createSQLException("TLS"
248: + " is not a valid SSL protocol.",
249: SQL_STATE_BAD_SSL_PARAMS, 0, false);
250: } catch (KeyManagementException kme) {
251: throw SQLError.createSQLException(
252: "KeyManagementException: " + kme.getMessage(),
253: SQL_STATE_BAD_SSL_PARAMS, 0, false);
254: }
255: }
256:
257: }
|