Skip to content

Commit

Permalink
Fix for BUG#20515688.
Browse files Browse the repository at this point in the history
  • Loading branch information
soklakov committed Apr 4, 2017
1 parent eecaf7e commit aeba572
Showing 1 changed file with 137 additions and 24 deletions.
161 changes: 137 additions & 24 deletions src/com/mysql/jdbc/ExportControlled.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2002, 2017, 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.
Expand Down Expand Up @@ -31,13 +31,22 @@
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorResult;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.PKIXParameters;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
Expand All @@ -48,10 +57,12 @@
import java.util.Properties;

import javax.crypto.Cipher;
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;

Expand Down Expand Up @@ -204,23 +215,123 @@ public boolean isLocallyConnected(ConnectionImpl conn) throws SQLException {
private ExportControlled() { /* prevent instantiation */
}

/**
* Implementation of X509TrustManager wrapping JVM X509TrustManagers to add expiration check
*/
public static class X509TrustManagerWrapper implements X509TrustManager {

private X509TrustManager origTm = null;
private boolean verifyServerCert = false;
private CertificateFactory certFactory = null;
private PKIXParameters validatorParams = null;
private CertPathValidator validator = null;

public X509TrustManagerWrapper(X509TrustManager tm, boolean verifyServerCertificate, KeyStore trustKeyStore) throws CertificateException {
this.origTm = tm;
this.verifyServerCert = verifyServerCertificate;

if (verifyServerCertificate) {
try {
this.validatorParams = new PKIXParameters(trustKeyStore);
this.validatorParams.setRevocationEnabled(false);
this.validator = CertPathValidator.getInstance("PKIX");
this.certFactory = CertificateFactory.getInstance("X.509");
} catch (Exception e) {
throw new CertificateException(e);
}
}

}

public X509TrustManagerWrapper() {
}

public X509Certificate[] getAcceptedIssuers() {
return this.origTm != null ? this.origTm.getAcceptedIssuers() : new X509Certificate[0];
}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (int i = 0; i < chain.length; i++) {
chain[i].checkValidity();
}

if (this.validatorParams != null) {

X509CertSelector certSelect = new X509CertSelector();
certSelect.setSerialNumber(chain[0].getSerialNumber());

try {
CertPath certPath = this.certFactory.generateCertPath(Arrays.asList(chain));
// Validate against truststore
CertPathValidatorResult result = this.validator.validate(certPath, this.validatorParams);
// Check expiration for the CA used to validate this path
((PKIXCertPathValidatorResult) result).getTrustAnchor().getTrustedCert().checkValidity();

} catch (InvalidAlgorithmParameterException e) {
throw new CertificateException(e);
} catch (CertPathValidatorException e) {
throw new CertificateException(e);
}
}

if (this.verifyServerCert) {
this.origTm.checkServerTrusted(chain, authType);
}
}

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
this.origTm.checkClientTrusted(chain, authType);
}
};

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 clientCertificateKeyStoreType = mysqlIO.connection.getClientCertificateKeyStoreType();
String trustCertificateKeyStoreUrl = mysqlIO.connection.getTrustCertificateKeyStoreUrl();
String trustCertificateKeyStorePassword = mysqlIO.connection.getTrustCertificateKeyStorePassword();
String trustCertificateKeyStoreType = mysqlIO.connection.getTrustCertificateKeyStoreType();

if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) {
clientCertificateKeyStoreUrl = System.getProperty("javax.net.ssl.keyStore");
clientCertificateKeyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
clientCertificateKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType");
if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreType)) {
clientCertificateKeyStoreType = "JKS";
}
// check URL
if (!StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) {
try {
new URL(clientCertificateKeyStoreUrl);
} catch (MalformedURLException e) {
clientCertificateKeyStoreUrl = "file:" + clientCertificateKeyStoreUrl;
}
}
}

if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl) && StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
if (mysqlIO.connection.getVerifyServerCertificate()) {
return (SSLSocketFactory) SSLSocketFactory.getDefault();
if (StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
trustCertificateKeyStoreUrl = System.getProperty("javax.net.ssl.trustStore");
trustCertificateKeyStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
trustCertificateKeyStoreType = System.getProperty("javax.net.ssl.trustStoreType");
if (StringUtils.isNullOrEmpty(trustCertificateKeyStoreType)) {
trustCertificateKeyStoreType = "JKS";
}
// check URL
if (!StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
try {
new URL(trustCertificateKeyStoreUrl);
} catch (MalformedURLException e) {
trustCertificateKeyStoreUrl = "file:" + trustCertificateKeyStoreUrl;
}
}
}

TrustManagerFactory tmf = null;
KeyManagerFactory kmf = null;

KeyManager[] kms = null;
List<TrustManager> tms = new ArrayList<TrustManager>();

try {
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Expand All @@ -240,6 +351,7 @@ private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured(MysqlIO m
ksIS = ksURL.openStream();
clientKeyStore.load(ksIS, password);
kmf.init(clientKeyStore, password);
kms = kmf.getKeyManagers();
}
} catch (UnrecoverableKeyException uke) {
throw SQLError.createSQLException("Could not recover keys from client keystore. Check password?", SQL_STATE_BAD_SSL_PARAMS, 0, false,
Expand Down Expand Up @@ -285,6 +397,17 @@ private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured(MysqlIO m
ksIS = ksURL.openStream();
trustKeyStore.load(ksIS, password);
tmf.init(trustKeyStore);

// building the customized list of TrustManagers from original one if it's available
TrustManager[] origTms = tmf.getTrustManagers();
final boolean verifyServerCertificate = mysqlIO.connection.getVerifyServerCertificate();

for (int j = 0; j < origTms.length; j++) {
tms.add(origTms[j] instanceof X509TrustManager
? new X509TrustManagerWrapper((X509TrustManager) origTms[j], verifyServerCertificate, trustKeyStore) // wrapping X509TrustManagers
: origTms[j] // for non-X509 TrustManagers putting original ones
);
}
}
} catch (NoSuchAlgorithmException nsae) {
throw SQLError.createSQLException("Unsupported keystore algorithm [" + nsae.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false,
Expand Down Expand Up @@ -316,26 +439,16 @@ private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured(MysqlIO m
}
}

SSLContext sslContext = null;
// if original TrustManagers are not available then putting one X509TrustManagerWrapper which take care only about expiration check
if (tms.size() == 0) {
tms.add(new X509TrustManagerWrapper());
}

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);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, tms.toArray(new TrustManager[tms.size()]), 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) {
Expand Down

0 comments on commit aeba572

Please sign in to comment.