/*
* Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.grizzly.ssl;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.glassfish.grizzly.Grizzly;
Utility class, which helps to configure SSLContext
. Author: Alexey Stashok, Hubert Iwaniuk, Bruno Harbulot
/**
* Utility class, which helps to configure {@link SSLContext}.
*
* @author Alexey Stashok
* @author Hubert Iwaniuk
* @author Bruno Harbulot
*/
public class SSLContextConfigurator {
public static final String TRUST_STORE_PROVIDER = "javax.net.ssl.trustStoreProvider";
public static final String KEY_STORE_PROVIDER = "javax.net.ssl.keyStoreProvider";
public static final String TRUST_STORE_FILE = "javax.net.ssl.trustStore";
public static final String KEY_STORE_FILE = "javax.net.ssl.keyStore";
public static final String TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword";
public static final String KEY_STORE_PASSWORD = "javax.net.ssl.keyStorePassword";
public static final String TRUST_STORE_TYPE = "javax.net.ssl.trustStoreType";
public static final String KEY_STORE_TYPE = "javax.net.ssl.keyStoreType";
public static final String KEY_FACTORY_MANAGER_ALGORITHM = "ssl.KeyManagerFactory.algorithm";
public static final String TRUST_FACTORY_MANAGER_ALGORITHM = "ssl.TrustManagerFactory.algorithm";
Default Logger.
/**
* Default Logger.
*/
private static final Logger LOGGER = Grizzly.logger(SSLContextConfigurator.class);
Default SSL configuration. If you have changed any of System.getProperties()
of javax.net.ssl family you should refresh this configuration by calling retrieve(Properties)
. /**
* Default SSL configuration. If you have changed any of {@link System#getProperties()} of javax.net.ssl family you
* should refresh this configuration by calling {@link #retrieve(java.util.Properties)}.
*/
public static final SSLContextConfigurator DEFAULT_CONFIG = new SSLContextConfigurator();
private String trustStoreProvider;
private String keyStoreProvider;
private String trustStoreType;
private String keyStoreType;
private char[] trustStorePass;
private char[] keyStorePass;
private char[] keyPass;
private String trustStoreFile;
private String keyStoreFile;
private byte[] trustStoreBytes;
private byte[] keyStoreBytes;
private String trustManagerFactoryAlgorithm;
private String keyManagerFactoryAlgorithm;
private String securityProtocol = "TLS";
Default constructor. Reads configuration properties from System.getProperties()
. Calls SSLContextConfigurator(boolean)
with true
.
/**
* Default constructor. Reads configuration properties from {@link System#getProperties()}. Calls
* {@link #SSLContextConfigurator(boolean)} with <code>true</code>.
*/
public SSLContextConfigurator() {
this(true);
}
Constructor that allows you creating empty configuration.
Params: - readSystemProperties – If
true
populates configuration from System.getProperties()
, else you have empty configuration.
/**
* Constructor that allows you creating empty configuration.
*
* @param readSystemProperties If <code>true</code> populates configuration from {@link System#getProperties()}, else
* you have empty configuration.
*/
public SSLContextConfigurator(boolean readSystemProperties) {
if (readSystemProperties) {
retrieve(System.getProperties());
}
}
Sets the trust store provider name.
Params: - trustStoreProvider – Trust store provider to set.
/**
* Sets the <em>trust</em> store provider name.
*
* @param trustStoreProvider <em>Trust</em> store provider to set.
*/
public void setTrustStoreProvider(String trustStoreProvider) {
this.trustStoreProvider = trustStoreProvider;
}
Sets the key store provider name.
Params: - keyStoreProvider – Key store provider to set.
/**
* Sets the <em>key</em> store provider name.
*
* @param keyStoreProvider <em>Key</em> store provider to set.
*/
public void setKeyStoreProvider(String keyStoreProvider) {
this.keyStoreProvider = keyStoreProvider;
}
Type of trust store.
Params: - trustStoreType – Type of trust store to set.
/**
* Type of <em>trust</em> store.
*
* @param trustStoreType Type of <em>trust</em> store to set.
*/
public void setTrustStoreType(String trustStoreType) {
this.trustStoreType = trustStoreType;
}
Type of key store.
Params: - keyStoreType – Type of key store to set.
/**
* Type of <em>key</em> store.
*
* @param keyStoreType Type of <em>key</em> store to set.
*/
public void setKeyStoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
}
Password of trust store.
Params: - trustStorePass – Password of trust store to set.
/**
* Password of <em>trust</em> store.
*
* @param trustStorePass Password of <em>trust</em> store to set.
*/
public void setTrustStorePass(String trustStorePass) {
this.trustStorePass = trustStorePass.toCharArray();
}
Password of key store.
Params: - keyStorePass – Password of key store to set.
/**
* Password of <em>key</em> store.
*
* @param keyStorePass Password of <em>key</em> store to set.
*/
public void setKeyStorePass(String keyStorePass) {
this.keyStorePass = keyStorePass.toCharArray();
}
Password of key store.
Params: - keyStorePass – Password of key store to set.
/**
* Password of <em>key</em> store.
*
* @param keyStorePass Password of <em>key</em> store to set.
*/
public void setKeyStorePass(char[] keyStorePass) {
this.keyStorePass = keyStorePass;
}
Password of the key in the key store.
Params: - keyPass – Password of key to set.
/**
* Password of the key in the <em>key</em> store.
*
* @param keyPass Password of <em>key</em> to set.
*/
public void setKeyPass(String keyPass) {
this.keyPass = keyPass.toCharArray();
}
Password of the key in the key store.
Params: - keyPass – Password of key to set.
/**
* Password of the key in the <em>key</em> store.
*
* @param keyPass Password of <em>key</em> to set.
*/
public void setKeyPass(char[] keyPass) {
this.keyPass = keyPass;
}
Sets trust store file name, also makes sure that if other trust store configuration parameters are not set to set them to default values. Method resets trust store bytes if any have been set before via setTrustStoreBytes(byte[])
. Params: - trustStoreFile – File name of trust store.
/**
* Sets trust store file name, also makes sure that if other trust store configuration parameters are not set to set
* them to default values. Method resets trust store bytes if any have been set before via
* {@link #setTrustStoreBytes(byte[])}.
*
* @param trustStoreFile File name of trust store.
*/
public void setTrustStoreFile(String trustStoreFile) {
this.trustStoreFile = trustStoreFile;
this.trustStoreBytes = null;
}
Sets trust store payload as byte array. Method resets trust store file if any has been set before via setTrustStoreFile(String)
. Params: - trustStoreBytes – trust store payload.
/**
* Sets trust store payload as byte array. Method resets trust store file if any has been set before via
* {@link #setTrustStoreFile(java.lang.String)}.
*
* @param trustStoreBytes trust store payload.
*/
public void setTrustStoreBytes(byte[] trustStoreBytes) {
this.trustStoreBytes = trustStoreBytes;
this.trustStoreFile = null;
}
Sets key store file name, also makes sure that if other key store configuration parameters are not set to set them to default values. Method resets key store bytes if any have been set before via setKeyStoreBytes(byte[])
. Params: - keyStoreFile – File name of key store.
/**
* Sets key store file name, also makes sure that if other key store configuration parameters are not set to set them to
* default values. Method resets key store bytes if any have been set before via {@link #setKeyStoreBytes(byte[])}.
*
* @param keyStoreFile File name of key store.
*/
public void setKeyStoreFile(String keyStoreFile) {
this.keyStoreFile = keyStoreFile;
this.keyStoreBytes = null;
}
Sets key store payload as byte array. Method resets key store file if any has been set before via setKeyStoreFile(String)
. Params: - keyStoreBytes – key store payload.
/**
* Sets key store payload as byte array. Method resets key store file if any has been set before via
* {@link #setKeyStoreFile(java.lang.String)}.
*
* @param keyStoreBytes key store payload.
*/
public void setKeyStoreBytes(byte[] keyStoreBytes) {
this.keyStoreBytes = keyStoreBytes;
this.keyStoreFile = null;
}
Sets the trust manager factory algorithm.
Params: - trustManagerFactoryAlgorithm – the trust manager factory algorithm.
/**
* Sets the trust manager factory algorithm.
*
* @param trustManagerFactoryAlgorithm the trust manager factory algorithm.
*/
public void setTrustManagerFactoryAlgorithm(String trustManagerFactoryAlgorithm) {
this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm;
}
Sets the key manager factory algorithm.
Params: - keyManagerFactoryAlgorithm – the key manager factory algorithm.
/**
* Sets the key manager factory algorithm.
*
* @param keyManagerFactoryAlgorithm the key manager factory algorithm.
*/
public void setKeyManagerFactoryAlgorithm(String keyManagerFactoryAlgorithm) {
this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm;
}
Sets the SSLContext protocol. The default value is TLS
if this is null.
Params: - securityProtocol – Protocol for
SSLContext.getProtocol()
.
/**
* Sets the SSLContext protocol. The default value is <code>TLS</code> if this is null.
*
* @param securityProtocol Protocol for {@link javax.net.ssl.SSLContext#getProtocol()}.
*/
public void setSecurityProtocol(String securityProtocol) {
this.securityProtocol = securityProtocol;
}
Validates SSLContextConfigurator
configuration. Returns: true
if configuration is valid, else false
.Deprecated: Use createSSLContext(boolean)
.
/**
* Validates {@link SSLContextConfigurator} configuration.
*
* @return <code>true</code> if configuration is valid, else <code>false</code>.
*
* @deprecated Use {@link #createSSLContext(boolean)}.
*/
@Deprecated
public boolean validateConfiguration() {
return validateConfiguration(false);
}
Validates SSLContextConfigurator
configuration. Params: - needsKeyStore – forces failure if no keystore is specified.
Returns: true
if configuration is valid, else false
.Deprecated: Use createSSLContext(boolean)
.
/**
* Validates {@link SSLContextConfigurator} configuration.
*
* @param needsKeyStore forces failure if no keystore is specified.
* @return <code>true</code> if configuration is valid, else <code>false</code>.
*
* @deprecated Use {@link #createSSLContext(boolean)}.
*/
@Deprecated
public boolean validateConfiguration(boolean needsKeyStore) {
boolean valid = true;
if (keyStoreBytes != null || keyStoreFile != null) {
try {
KeyStore keyStore;
if (keyStoreProvider != null) {
keyStore = KeyStore.getInstance(keyStoreType != null ? keyStoreType : KeyStore.getDefaultType(), keyStoreProvider);
} else {
keyStore = KeyStore.getInstance(keyStoreType != null ? keyStoreType : KeyStore.getDefaultType());
}
loadBytes(keyStoreBytes, keyStoreFile, keyStorePass, keyStore);
String kmfAlgorithm = keyManagerFactoryAlgorithm;
if (kmfAlgorithm == null) {
kmfAlgorithm = System.getProperty(KEY_FACTORY_MANAGER_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm());
}
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(kmfAlgorithm);
keyManagerFactory.init(keyStore, keyPass != null ? keyPass : keyStorePass);
} catch (KeyStoreException e) {
LOGGER.log(Level.FINE, "Error initializing key store", e);
valid = false;
} catch (CertificateException e) {
LOGGER.log(Level.FINE, "Key store certificate exception.", e);
valid = false;
} catch (UnrecoverableKeyException e) {
LOGGER.log(Level.FINE, "Key store unrecoverable exception.", e);
valid = false;
} catch (FileNotFoundException e) {
LOGGER.log(Level.FINE, "Can't find key store file: " + keyStoreFile, e);
valid = false;
} catch (IOException e) {
LOGGER.log(Level.FINE, "Error loading key store from file: " + keyStoreFile, e);
valid = false;
} catch (NoSuchAlgorithmException e) {
LOGGER.log(Level.FINE, "Error initializing key manager factory (no such algorithm)", e);
valid = false;
} catch (NoSuchProviderException e) {
LOGGER.log(Level.FINE, "Error initializing key store (no such provider)", e);
valid = false;
}
} else {
valid = !needsKeyStore;
}
if (trustStoreBytes != null || trustStoreFile != null) {
try {
KeyStore trustStore;
if (trustStoreProvider != null) {
trustStore = KeyStore.getInstance(trustStoreType != null ? trustStoreType : KeyStore.getDefaultType(), trustStoreProvider);
} else {
trustStore = KeyStore.getInstance(trustStoreType != null ? trustStoreType : KeyStore.getDefaultType());
}
loadBytes(trustStoreBytes, trustStoreFile, trustStorePass, trustStore);
String tmfAlgorithm = trustManagerFactoryAlgorithm;
if (tmfAlgorithm == null) {
tmfAlgorithm = System.getProperty(TRUST_FACTORY_MANAGER_ALGORITHM, TrustManagerFactory.getDefaultAlgorithm());
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
trustManagerFactory.init(trustStore);
} catch (KeyStoreException e) {
LOGGER.log(Level.FINE, "Error initializing trust store", e);
valid = false;
} catch (CertificateException e) {
LOGGER.log(Level.FINE, "Trust store certificate exception.", e);
valid = false;
} catch (FileNotFoundException e) {
LOGGER.log(Level.FINE, "Can't find trust store file: " + trustStoreFile, e);
valid = false;
} catch (IOException e) {
LOGGER.log(Level.FINE, "Error loading trust store from file: " + trustStoreFile, e);
valid = false;
} catch (NoSuchAlgorithmException e) {
LOGGER.log(Level.FINE, "Error initializing trust manager factory (no such algorithm)", e);
valid = false;
} catch (NoSuchProviderException e) {
LOGGER.log(Level.FINE, "Error initializing trust store (no such provider)", e);
valid = false;
}
}
return valid;
}
Create a new SSLContext
. Note that if there are any problems with the key or trust stores, that no exception will be thrown. Returns: a new SSLContext
Deprecated: Use createSSLContext(boolean)
.
/**
* Create a new {@link SSLContext}. Note that if there are any problems with the key or trust stores, that no exception
* will be thrown.
*
* @return a new {@link SSLContext}
*
* @deprecated Use {@link #createSSLContext(boolean)}.
*/
@Deprecated
public SSLContext createSSLContext() {
return createSSLContext(false);
}
Create a new SSLContext
. If the SSLContext
cannot be created for whatever reason, a GenericStoreException
will be raised containing the root cause of the failure. Params: - throwException –
true
if an exception should be raised upon failure.
Throws: - GenericStoreException –
throwException
is true
and the SSLContext cannot be created
Returns: a new SSLContext
Since: 2.3.28
/**
* Create a new {@link SSLContext}. If the {@link SSLContext} cannot be created for whatever reason, a
* {@link GenericStoreException} will be raised containing the root cause of the failure.
*
* @param throwException <code>true</code> if an exception should be raised upon failure.
*
* @return a new {@link SSLContext}
*
* @throws GenericStoreException <code>throwException</code> is <code>true</code> and the SSLContext cannot be created
*
* @since 2.3.28
*/
public SSLContext createSSLContext(final boolean throwException) {
SSLContext sslContext = null;
try {
TrustManagerFactory trustManagerFactory = null;
KeyManagerFactory keyManagerFactory = null;
if (keyStoreBytes != null || keyStoreFile != null) {
try {
KeyStore keyStore;
if (keyStoreProvider != null) {
keyStore = KeyStore.getInstance(keyStoreType != null ? keyStoreType : KeyStore.getDefaultType(), keyStoreProvider);
} else {
keyStore = KeyStore.getInstance(keyStoreType != null ? keyStoreType : KeyStore.getDefaultType());
}
loadBytes(keyStoreBytes, keyStoreFile, keyStorePass, keyStore);
String kmfAlgorithm = keyManagerFactoryAlgorithm;
if (kmfAlgorithm == null) {
kmfAlgorithm = System.getProperty(KEY_FACTORY_MANAGER_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm());
}
keyManagerFactory = KeyManagerFactory.getInstance(kmfAlgorithm);
keyManagerFactory.init(keyStore, keyPass != null ? keyPass : keyStorePass);
} catch (KeyStoreException e) {
LOGGER.log(Level.FINE, "Error initializing key store", e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (CertificateException e) {
LOGGER.log(Level.FINE, "Key store certificate exception.", e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (UnrecoverableKeyException e) {
LOGGER.log(Level.FINE, "Key store unrecoverable exception.", e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (FileNotFoundException e) {
LOGGER.log(Level.FINE, "Can't find key store file: " + keyStoreFile, e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (IOException e) {
LOGGER.log(Level.FINE, "Error loading key store from file: " + keyStoreFile, e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.log(Level.FINE, "Error initializing key manager factory (no such algorithm)", e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (NoSuchProviderException e) {
LOGGER.log(Level.FINE, "Error initializing key store (no such provider)", e);
}
}
if (trustStoreBytes != null || trustStoreFile != null) {
try {
KeyStore trustStore;
if (trustStoreProvider != null) {
trustStore = KeyStore.getInstance(trustStoreType != null ? trustStoreType : KeyStore.getDefaultType(), trustStoreProvider);
} else {
trustStore = KeyStore.getInstance(trustStoreType != null ? trustStoreType : KeyStore.getDefaultType());
}
loadBytes(trustStoreBytes, trustStoreFile, trustStorePass, trustStore);
String tmfAlgorithm = trustManagerFactoryAlgorithm;
if (tmfAlgorithm == null) {
tmfAlgorithm = System.getProperty(TRUST_FACTORY_MANAGER_ALGORITHM, TrustManagerFactory.getDefaultAlgorithm());
}
trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
trustManagerFactory.init(trustStore);
} catch (KeyStoreException e) {
LOGGER.log(Level.FINE, "Error initializing trust store", e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (CertificateException e) {
LOGGER.log(Level.FINE, "Trust store certificate exception.", e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (FileNotFoundException e) {
LOGGER.log(Level.FINE, "Can't find trust store file: " + trustStoreFile, e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (IOException e) {
LOGGER.log(Level.FINE, "Error loading trust store from file: " + trustStoreFile, e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.log(Level.FINE, "Error initializing trust manager factory (no such algorithm)", e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (NoSuchProviderException e) {
LOGGER.log(Level.FINE, "Error initializing trust store (no such provider)", e);
if (throwException) {
throw new GenericStoreException(e);
}
}
}
String secProtocol = "TLS";
if (securityProtocol != null) {
secProtocol = securityProtocol;
}
sslContext = SSLContext.getInstance(secProtocol);
sslContext.init(keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null,
trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null, null);
} catch (KeyManagementException e) {
LOGGER.log(Level.FINE, "Key management error.", e);
if (throwException) {
throw new GenericStoreException(e);
}
} catch (NoSuchAlgorithmException e) {
LOGGER.log(Level.FINE, "Error initializing algorithm.", e);
if (throwException) {
throw new GenericStoreException(e);
}
}
return sslContext;
}
public void retrieve(Properties props) {
trustStoreProvider = props.getProperty(TRUST_STORE_PROVIDER);
keyStoreProvider = props.getProperty(KEY_STORE_PROVIDER);
trustStoreType = props.getProperty(TRUST_STORE_TYPE);
keyStoreType = props.getProperty(KEY_STORE_TYPE);
if (props.getProperty(TRUST_STORE_PASSWORD) != null) {
trustStorePass = props.getProperty(TRUST_STORE_PASSWORD).toCharArray();
} else {
trustStorePass = null;
}
if (props.getProperty(KEY_STORE_PASSWORD) != null) {
keyStorePass = props.getProperty(KEY_STORE_PASSWORD).toCharArray();
} else {
keyStorePass = null;
}
trustStoreFile = props.getProperty(TRUST_STORE_FILE);
keyStoreFile = props.getProperty(KEY_STORE_FILE);
trustStoreBytes = null;
keyStoreBytes = null;
securityProtocol = "TLS";
}
private static void loadBytes(final byte[] bytes, final String storeFile, final char[] password, final KeyStore store)
throws IOException, CertificateException, NoSuchAlgorithmException {
InputStream inputStream = null;
try {
if (bytes != null) {
inputStream = new ByteArrayInputStream(bytes);
} else if (!"NONE".equals(storeFile)) {
inputStream = new FileInputStream(storeFile);
}
store.load(inputStream, password);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException ignored) {
}
}
}
public static final class GenericStoreException extends RuntimeException {
public GenericStoreException(Throwable cause) {
super(cause);
}
}
}