/*
Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
The MySQL Connector/J is licensed under the terms of the GPLv2
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
this software, see the FLOSS License Exception
<http://www.mysql.com/about/legal/licensing/foss-exception.html>.
This program is free software; you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation; version 2
of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.jdbc;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
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.security.cert.X509Certificate;
import java.sql.SQLException;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* Holds functionality that falls under export-control regulations.
*
* @author Mark Matthews
*
* @version $Id: ExportControlled.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
* Exp $
*/
public class ExportControlled {
private static final String SQL_STATE_BAD_SSL_PARAMS = "08000";
protected static boolean enabled() {
// we may wish to un-static-ify this class
// this static method call may be removed entirely by the compiler
return true;
}
/**
* Converts the socket being used in the given MysqlIO to an SSLSocket by
* performing the SSL/TLS handshake.
*
* @param mysqlIO
* the MysqlIO instance containing the socket to convert to an
* SSLSocket.
*
* @throws CommunicationsException
* if the handshake fails, or if this distribution of
* Connector/J doesn't contain the SSL crytpo hooks needed to
* perform the handshake.
*/
protected static void transformSocketToSSLSocket(MysqlIO mysqlIO)
throws SQLException {
javax.net.ssl.SSLSocketFactory sslFact = getSSLSocketFactoryDefaultOrConfigured(mysqlIO);
try {
mysqlIO.mysqlConnection = sslFact.createSocket(
mysqlIO.mysqlConnection, mysqlIO.host, mysqlIO.port, true);
// need to force TLSv1, or else JSSE tries to do a SSLv2 handshake
// which MySQL doesn't understand
((javax.net.ssl.SSLSocket) mysqlIO.mysqlConnection)
.setEnabledProtocols(new String[] { "TLSv1" }); //$NON-NLS-1$
((javax.net.ssl.SSLSocket) mysqlIO.mysqlConnection)
.startHandshake();
if (mysqlIO.connection.getUseUnbufferedInput()) {
mysqlIO.mysqlInput = mysqlIO.mysqlConnection.getInputStream();
} else {
mysqlIO.mysqlInput = new BufferedInputStream(
mysqlIO.mysqlConnection.getInputStream(), 16384);
}
mysqlIO.mysqlOutput = new BufferedOutputStream(
mysqlIO.mysqlConnection.getOutputStream(), 16384);
mysqlIO.mysqlOutput.flush();
} catch (IOException ioEx) {
throw SQLError.createCommunicationsException(mysqlIO.connection,
mysqlIO.getLastPacketSentTimeMs(), mysqlIO.getLastPacketReceivedTimeMs(),
ioEx, mysqlIO.getExceptionInterceptor());
}
}
private ExportControlled() { /* prevent instantiation */
}
private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured(
MysqlIO mysqlIO) throws SQLException {
String clientCertificateKeyStoreUrl = mysqlIO.connection
.getClientCertificateKeyStoreUrl();
String trustCertificateKeyStoreUrl = mysqlIO.connection
.getTrustCertificateKeyStoreUrl();
String clientCertificateKeyStoreType = mysqlIO.connection
.getClientCertificateKeyStoreType();
String clientCertificateKeyStorePassword = mysqlIO.connection
.getClientCertificateKeyStorePassword();
String trustCertificateKeyStoreType = mysqlIO.connection
.getTrustCertificateKeyStoreType();
String trustCertificateKeyStorePassword = mysqlIO.connection
.getTrustCertificateKeyStorePassword();
if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)
&& StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
if (mysqlIO.connection.getVerifyServerCertificate()) {
return (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory
.getDefault();
}
}
TrustManagerFactory tmf = null;
KeyManagerFactory kmf = null;
try {
tmf = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
} catch (NoSuchAlgorithmException nsae) {
throw SQLError
.createSQLException(
"Default algorithm definitions for TrustManager and/or KeyManager are invalid. Check java security properties file.",
SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
}
if (!StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) {
try {
if (!StringUtils.isNullOrEmpty(clientCertificateKeyStoreType)) {
KeyStore clientKeyStore = KeyStore
.getInstance(clientCertificateKeyStoreType);
URL ksURL = new URL(clientCertificateKeyStoreUrl);
char[] password = (clientCertificateKeyStorePassword == null) ? new char[0]
: clientCertificateKeyStorePassword.toCharArray();
clientKeyStore.load(ksURL.openStream(), password);
kmf.init(clientKeyStore, password);
}
} catch (UnrecoverableKeyException uke) {
throw SQLError
.createSQLException(
"Could not recover keys from client keystore. Check password?",
SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
} catch (NoSuchAlgorithmException nsae) {
throw SQLError.createSQLException(
"Unsupported keystore algorithm [" + nsae.getMessage()
+ "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
} catch (KeyStoreException kse) {
throw SQLError.createSQLException(
"Could not create KeyStore instance ["
+ kse.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
} catch (CertificateException nsae) {
throw SQLError.createSQLException("Could not load client"
+ clientCertificateKeyStoreType + " keystore from "
+ clientCertificateKeyStoreUrl, mysqlIO.getExceptionInterceptor());
} catch (MalformedURLException mue) {
throw SQLError.createSQLException(clientCertificateKeyStoreUrl
+ " does not appear to be a valid URL.", SQL_STATE_BAD_SSL_PARAMS, 0,
false, mysqlIO.getExceptionInterceptor());
} catch (IOException ioe) {
SQLException sqlEx = SQLError.createSQLException("Cannot open "
+ clientCertificateKeyStoreUrl + " ["
+ ioe.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
sqlEx.initCause(ioe);
throw sqlEx;
}
}
if (!StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
try {
if (!StringUtils.isNullOrEmpty(trustCertificateKeyStoreType)) {
KeyStore trustKeyStore = KeyStore
.getInstance(trustCertificateKeyStoreType);
URL ksURL = new URL(trustCertificateKeyStoreUrl);
char[] password = (trustCertificateKeyStorePassword == null) ? new char[0]
: trustCertificateKeyStorePassword.toCharArray();
trustKeyStore.load(ksURL.openStream(), password);
tmf.init(trustKeyStore);
}
} catch (NoSuchAlgorithmException nsae) {
throw SQLError.createSQLException(
"Unsupported keystore algorithm [" + nsae.getMessage()
+ "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
} catch (KeyStoreException kse) {
throw SQLError.createSQLException(
"Could not create KeyStore instance ["
+ kse.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
} catch (CertificateException nsae) {
throw SQLError.createSQLException("Could not load trust"
+ trustCertificateKeyStoreType + " keystore from "
+ trustCertificateKeyStoreUrl, SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
} catch (MalformedURLException mue) {
throw SQLError.createSQLException(trustCertificateKeyStoreUrl
+ " does not appear to be a valid URL.", SQL_STATE_BAD_SSL_PARAMS, 0,
false, mysqlIO.getExceptionInterceptor());
} catch (IOException ioe) {
SQLException sqlEx = SQLError.createSQLException("Cannot open "
+ trustCertificateKeyStoreUrl + " [" + ioe.getMessage()
+ "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
sqlEx.initCause(ioe);
throw sqlEx;
}
}
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl) ? null : kmf.getKeyManagers(), mysqlIO.connection
.getVerifyServerCertificate() ? tmf.getTrustManagers()
: new X509TrustManager[] { new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain,
String authType) {
// return without complaint
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// return without complaint
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
} }, null);
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException nsae) {
throw SQLError.createSQLException("TLS"
+ " is not a valid SSL protocol.",
SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
} catch (KeyManagementException kme) {
throw SQLError.createSQLException("KeyManagementException: "
+ kme.getMessage(), SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
}
}
}