Skip to content

Commit

Permalink
- Since it should be the same thing, use the JSSE code to load the ke…
Browse files Browse the repository at this point in the history
…ystore and truststore, if using the JSSE style configuration. Then proceed to get the certificate and use setCertificateRaw as before.

- If I missed something, let me know.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1716691 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
rmaucher committed Nov 26, 2015
1 parent 0a2c297 commit 2ef248b
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 158 deletions.
125 changes: 8 additions & 117 deletions java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
Expand Up @@ -16,36 +16,19 @@
*/ */
package org.apache.tomcat.util.net.openssl; package org.apache.tomcat.util.net.openssl;


import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;


import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLParameters;
Expand All @@ -61,11 +44,11 @@
import org.apache.tomcat.jni.Pool; import org.apache.tomcat.jni.Pool;
import org.apache.tomcat.jni.SSL; import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext; import org.apache.tomcat.jni.SSLContext;
import org.apache.tomcat.util.file.ConfigFileLoader;
import org.apache.tomcat.util.net.AbstractEndpoint; import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.Constants; import org.apache.tomcat.util.net.Constants;
import org.apache.tomcat.util.net.SSLHostConfig; import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SSLHostConfigCertificate; import org.apache.tomcat.util.net.SSLHostConfigCertificate;
import org.apache.tomcat.util.net.jsse.JSSEKeyManager;
import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser; import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser;
import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.res.StringManager;


Expand Down Expand Up @@ -326,16 +309,14 @@ public synchronized void init(KeyManager[] kms, TrustManager[] tms, SecureRandom
SSLContext.setCipherSuite(ctx, ciphers); SSLContext.setCipherSuite(ctx, ciphers);
// Load Server key and certificate // Load Server key and certificate
if (certificate.getCertificateFile() != null) { if (certificate.getCertificateFile() != null) {

// Set certificate
SSLContext.setCertificate(ctx, SSLContext.setCertificate(ctx,
SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()),
SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()), SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()),
certificate.getCertificateKeyPassword(), SSL.SSL_AIDX_RSA); certificate.getCertificateKeyPassword(), SSL.SSL_AIDX_RSA);

// Set certificate chain file // Set certificate chain file
SSLContext.setCertificateChainFile(ctx, SSLContext.setCertificateChainFile(ctx,
SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), false); SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), false);

// Support Client Certificates // Support Client Certificates
SSLContext.setCACertificate(ctx, SSLContext.setCACertificate(ctx,
SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()), SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()),
Expand All @@ -347,16 +328,14 @@ public synchronized void init(KeyManager[] kms, TrustManager[] tms, SecureRandom
SSLHostConfig.adjustRelativePath( SSLHostConfig.adjustRelativePath(
sslHostConfig.getCertificateRevocationListPath())); sslHostConfig.getCertificateRevocationListPath()));
} else { } else {
/* Try use keystore */ X509KeyManager keyManager = chooseKeyManager(kms);
X509KeyManager keyManager = getJSSEKeyManager(sslHostConfig); String alias = certificate.getCertificateKeyAlias();
String alias = getJSSEAlias(sslHostConfig, keyManager);
X509Certificate certificate = keyManager.getCertificateChain(alias)[0]; X509Certificate certificate = keyManager.getCertificateChain(alias)[0];
PrivateKey key = keyManager.getPrivateKey(alias); PrivateKey key = keyManager.getPrivateKey(alias);
StringBuilder sb = new StringBuilder(BEGIN_KEY); StringBuilder sb = new StringBuilder(BEGIN_KEY);
sb.append(Base64.getMimeEncoder(64, new byte[] {'\n'}).encodeToString(key.getEncoded())); sb.append(Base64.getMimeEncoder(64, new byte[] {'\n'}).encodeToString(key.getEncoded()));
sb.append(END_KEY); sb.append(END_KEY);
SSLContext.setCertificateRaw(ctx, certificate.getEncoded(), sb.toString().getBytes(StandardCharsets.US_ASCII), SSL.SSL_AIDX_RSA); SSLContext.setCertificateRaw(ctx, certificate.getEncoded(), sb.toString().getBytes(StandardCharsets.US_ASCII), SSL.SSL_AIDX_RSA);

} }
// Client certificate verification // Client certificate verification
int value = 0; int value = 0;
Expand Down Expand Up @@ -411,64 +390,16 @@ public boolean verify(long ssl, byte[][] chain, String auth) {
} }
} }


String getJSSEAlias(SSLHostConfig sslHostConfig, X509KeyManager keyManager) { private static JSSEKeyManager chooseKeyManager(KeyManager[] managers) throws Exception {
String alias = null;
// TODO make sure we get the right one...
if (certificate.getCertificateKeyAlias() != null)
alias = certificate.getCertificateKeyAlias();
return alias;
}
/**
* get the JSSE key manager for the keystore
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws UnrecoverableKeyException
* @throws IOException
* @throws CertificateException
*
*/
static X509KeyManager getJSSEKeyManager(SSLHostConfig sslHostConfig) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, CertificateException, IOException {
String keystoretype = null;
String keystoreprovider = null;
String keystorefile = null;
String password = null;
// TODO make sure we get the right one...
for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) {
if (certificate.getCertificateKeystoreFile() != null)
keystorefile = certificate.getCertificateKeystoreFile();
if (certificate.getCertificateKeystorePassword() != null)
password = certificate.getCertificateKeystorePassword();
if (certificate.getCertificateKeystoreType() != null)
keystoretype = certificate.getCertificateKeystoreType();
if (certificate.getCertificateKeystoreProvider() != null)
keystoreprovider = certificate.getCertificateKeystoreProvider();
}
KeyStore ks = KeyStore.getInstance(keystoretype);
InputStream stream = ConfigFileLoader.getInputStream(keystorefile);
ks.load(stream, password.toCharArray());
KeyManagerFactory kmf;
if (keystoreprovider != null)
kmf = KeyManagerFactory.getInstance(keystoreprovider);
else
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
if (kms == null) {
return null;
}
return (X509KeyManager) kms[0];
}

static OpenSSLKeyManager chooseKeyManager(KeyManager[] managers) throws Exception {
for (KeyManager manager : managers) { for (KeyManager manager : managers) {
if (manager instanceof OpenSSLKeyManager) { if (manager instanceof JSSEKeyManager) {
return (OpenSSLKeyManager) manager; return (JSSEKeyManager) manager;
} }
} }
throw new IllegalStateException(sm.getString("openssl.keyManagerMissing")); throw new IllegalStateException(sm.getString("openssl.keyManagerMissing"));
} }


static X509TrustManager chooseTrustManager(TrustManager[] managers) { private static X509TrustManager chooseTrustManager(TrustManager[] managers) {
for (TrustManager m : managers) { for (TrustManager m : managers) {
if (m instanceof X509TrustManager) { if (m instanceof X509TrustManager) {
return (X509TrustManager) m; return (X509TrustManager) m;
Expand Down Expand Up @@ -506,46 +437,6 @@ public SSLParameters getSupportedSSLParameters() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }


/**
* Generates a key specification for an (encrypted) private key.
*
* @param password characters, if {@code null} or empty an unencrypted key
* is assumed
* @param key bytes of the DER encoded private key
*
* @return a key specification
*
* @throws IOException if parsing {@code key} fails
* @throws NoSuchAlgorithmException if the algorithm used to encrypt
* {@code key} is unknown
* @throws NoSuchPaddingException if the padding scheme specified in the
* decryption algorithm is unknown
* @throws InvalidKeySpecException if the decryption key based on
* {@code password} cannot be generated
* @throws InvalidKeyException if the decryption key based on
* {@code password} cannot be used to decrypt {@code key}
* @throws InvalidAlgorithmParameterException if decryption algorithm
* parameters are somehow faulty
*/
protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
InvalidKeyException, InvalidAlgorithmParameterException {

if (password == null || password.length == 0) {
return new PKCS8EncodedKeySpec(key);
}

EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);

Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());

return encryptedPrivateKeyInfo.getKeySpec(cipher);
}

@Override @Override
protected final void finalize() throws Throwable { protected final void finalize() throws Throwable {
super.finalize(); super.finalize();
Expand Down
64 changes: 23 additions & 41 deletions java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java
Expand Up @@ -16,32 +16,37 @@
*/ */
package org.apache.tomcat.util.net.openssl; package org.apache.tomcat.util.net.openssl;


import java.io.InputStream;
import java.security.KeyStore;
import java.util.List; import java.util.List;


import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;


import org.apache.tomcat.util.file.ConfigFileLoader;
import org.apache.tomcat.util.net.SSLContext; import org.apache.tomcat.util.net.SSLContext;
import org.apache.tomcat.util.net.SSLHostConfig; import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SSLHostConfigCertificate; import org.apache.tomcat.util.net.SSLHostConfigCertificate;
import org.apache.tomcat.util.net.SSLUtil; import org.apache.tomcat.util.net.SSLUtil;
import org.apache.tomcat.util.net.jsse.JSSESocketFactory;


public class OpenSSLUtil implements SSLUtil { public class OpenSSLUtil implements SSLUtil {


private final SSLHostConfig sslHostConfig; private final SSLHostConfig sslHostConfig;
private final SSLHostConfigCertificate certificate; private final SSLHostConfigCertificate certificate;
private final JSSESocketFactory jsseUtil;


private String[] enabledProtocols = null; private String[] enabledProtocols = null;
private String[] enabledCiphers = null; private String[] enabledCiphers = null;


public OpenSSLUtil(SSLHostConfig sslHostConfig, SSLHostConfigCertificate certificate) { public OpenSSLUtil(SSLHostConfig sslHostConfig, SSLHostConfigCertificate certificate) {
this.sslHostConfig = sslHostConfig; this.sslHostConfig = sslHostConfig;
this.certificate = certificate; this.certificate = certificate;
if (certificate.getCertificateFile() == null) {
// Using JSSE configuration for keystore and truststore
jsseUtil = new JSSESocketFactory(sslHostConfig, certificate);
} else {
// Use OpenSSL configuration for certificates
jsseUtil = null;
}
} }


@Override @Override
Expand All @@ -51,50 +56,27 @@ public SSLContext createSSLContext(List<String> negotiableProtocols) throws Exce


@Override @Override
public KeyManager[] getKeyManagers() throws Exception { public KeyManager[] getKeyManagers() throws Exception {
KeyManager[] managers = { if (jsseUtil != null) {
new OpenSSLKeyManager(SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), return jsseUtil.getKeyManagers();
SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile())) } else {
}; // Return something although it is not actually used
return managers; KeyManager[] managers = {
new OpenSSLKeyManager(SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()),
SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()))
};
return managers;
}
} }


/* In fact we can use the JSSE one for the moment */
@Override @Override
public TrustManager[] getTrustManagers() throws Exception { public TrustManager[] getTrustManagers() throws Exception {
String storefile = System.getProperty("java.home") + "/lib/security/cacerts"; if (jsseUtil != null) {
String password = "changeit"; return jsseUtil.getTrustManagers();
String type = "jks"; } else {
String algorithm = null; return null;
if (sslHostConfig.getTruststoreFile() != null) {
storefile = sslHostConfig.getTruststoreFile();
}
if (sslHostConfig.getTruststorePassword() != null) {
password = sslHostConfig.getTruststorePassword();
}
if (sslHostConfig.getTruststoreType() != null) {
type = sslHostConfig.getTruststoreType();
}
if (sslHostConfig.getTruststoreAlgorithm() != null) {
algorithm = sslHostConfig.getTruststoreAlgorithm();
} }

TrustManagerFactory factory;
if (algorithm == null)
factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
else
factory = TrustManagerFactory.getInstance(algorithm);

KeyStore keystore = KeyStore.getInstance(type);
try (InputStream stream = ConfigFileLoader.getInputStream(storefile)) {
keystore.load(stream, password.toCharArray());
}

factory.init(keystore);
TrustManager[] managers = factory.getTrustManagers();
return managers;
} }



@Override @Override
public void configureSessionContext(SSLSessionContext sslSessionContext) { public void configureSessionContext(SSLSessionContext sslSessionContext) {
// do nothing. configuration is done in the init phase // do nothing. configuration is done in the init phase
Expand Down

0 comments on commit 2ef248b

Please sign in to comment.