7
0
forked from pool/log4j
Files
log4j/log4j-CVE-2025-68161.patch
2026-01-05 19:01:16 +01:00

806 lines
36 KiB
Diff

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java
index 9f0f5f5e91..a5deb220cc 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java
@@ -22,8 +22,17 @@ import java.io.Serializable;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
+import java.security.KeyStoreException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.Enumeration;
import java.util.List;
-
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
@@ -31,36 +40,57 @@ import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
import org.apache.logging.log4j.util.Strings;
-/**
- *
- */
public class SslSocketManager extends TcpSocketManager {
+
public static final int DEFAULT_PORT = 6514;
+
private static final SslSocketManagerFactory FACTORY = new SslSocketManagerFactory();
+
private final SslConfiguration sslConfig;
/**
*
*
- * @param name The unique name of this connection.
- * @param os The OutputStream.
- * @param sock The Socket.
- * @param inetAddress The Internet address of the host.
- * @param host The name of the host.
- * @param port The port number on the host.
- * @param connectTimeoutMillis the connect timeout in milliseconds.
+ * @param name the unique name of this connection
+ * @param os the OutputStream
+ * @param sock the Socket
+ * @param inetAddress the Internet address of the host
+ * @param host the name of the host
+ * @param port the port number on the host
+ * @param connectTimeoutMillis the connect timeout in milliseconds
* @param reconnectionDelayMillis Reconnection interval.
- * @param immediateFail
- * @param layout The Layout.
+ * @param immediateFail True if the write should fail if no socket is immediately available.
+ * @param layout the Layout
* @param bufferSize The buffer size.
* @deprecated Use {@link #SslSocketManager(String, OutputStream, Socket, SslConfiguration, InetAddress, String, int, int, int, boolean, Layout, int, SocketOptions)}.
*/
@Deprecated
- public SslSocketManager(final String name, final OutputStream os, final Socket sock,
- final SslConfiguration sslConfig, final InetAddress inetAddress, final String host, final int port,
- final int connectTimeoutMillis, final int reconnectionDelayMillis, final boolean immediateFail,
- final Layout<? extends Serializable> layout, final int bufferSize) {
- super(name, os, sock, inetAddress, host, port, connectTimeoutMillis, reconnectionDelayMillis, immediateFail, layout, bufferSize, null);
+ public SslSocketManager(
+ final String name,
+ final OutputStream os,
+ final Socket sock,
+ final SslConfiguration sslConfig,
+ final InetAddress inetAddress,
+ final String host,
+ final int port,
+ final int connectTimeoutMillis,
+ final int reconnectionDelayMillis,
+ final boolean immediateFail,
+ final Layout<? extends Serializable> layout,
+ final int bufferSize) {
+ super(
+ name,
+ os,
+ sock,
+ inetAddress,
+ host,
+ port,
+ connectTimeoutMillis,
+ reconnectionDelayMillis,
+ immediateFail,
+ layout,
+ bufferSize,
+ null);
this.sslConfig = sslConfig;
}
@@ -75,25 +105,61 @@ public class SslSocketManager extends TcpSocketManager {
* @param port The port number on the host.
* @param connectTimeoutMillis the connect timeout in milliseconds.
* @param reconnectionDelayMillis Reconnection interval.
- * @param immediateFail
+ * @param immediateFail True if the write should fail if no socket is immediately available.
* @param layout The Layout.
* @param bufferSize The buffer size.
*/
- public SslSocketManager(final String name, final OutputStream os, final Socket sock,
- final SslConfiguration sslConfig, final InetAddress inetAddress, final String host, final int port,
- final int connectTimeoutMillis, final int reconnectionDelayMillis, final boolean immediateFail,
- final Layout<? extends Serializable> layout, final int bufferSize, final SocketOptions socketOptions) {
- super(name, os, sock, inetAddress, host, port, connectTimeoutMillis, reconnectionDelayMillis, immediateFail, layout, bufferSize, socketOptions);
+ public SslSocketManager(
+ final String name,
+ final OutputStream os,
+ final Socket sock,
+ final SslConfiguration sslConfig,
+ final InetAddress inetAddress,
+ final String host,
+ final int port,
+ final int connectTimeoutMillis,
+ final int reconnectionDelayMillis,
+ final boolean immediateFail,
+ final Layout<? extends Serializable> layout,
+ final int bufferSize,
+ final SocketOptions socketOptions) {
+ super(
+ name,
+ os,
+ sock,
+ inetAddress,
+ host,
+ port,
+ connectTimeoutMillis,
+ reconnectionDelayMillis,
+ immediateFail,
+ layout,
+ bufferSize,
+ socketOptions);
this.sslConfig = sslConfig;
}
private static class SslFactoryData extends FactoryData {
protected SslConfiguration sslConfiguration;
- public SslFactoryData(final SslConfiguration sslConfiguration, final String host, final int port,
- final int connectTimeoutMillis, final int reconnectDelayMillis, final boolean immediateFail,
- final Layout<? extends Serializable> layout, final int bufferSize, final SocketOptions socketOptions) {
- super(host, port, connectTimeoutMillis, reconnectDelayMillis, immediateFail, layout, bufferSize,
+ public SslFactoryData(
+ final SslConfiguration sslConfiguration,
+ final String host,
+ final int port,
+ final int connectTimeoutMillis,
+ final int reconnectDelayMillis,
+ final boolean immediateFail,
+ final Layout<? extends Serializable> layout,
+ final int bufferSize,
+ final SocketOptions socketOptions) {
+ super(
+ host,
+ port,
+ connectTimeoutMillis,
+ reconnectDelayMillis,
+ immediateFail,
+ layout,
+ bufferSize,
socketOptions);
this.sslConfiguration = sslConfiguration;
}
@@ -111,15 +177,39 @@ public class SslSocketManager extends TcpSocketManager {
* @deprecated Use {@link SslSocketManager#getSocketManager(SslConfiguration, String, int, int, int, boolean, Layout, int, SocketOptions)}.
*/
@Deprecated
- public static SslSocketManager getSocketManager(final SslConfiguration sslConfig, final String host, final int port,
- final int connectTimeoutMillis, final int reconnectDelayMillis, final boolean immediateFail,
- final Layout<? extends Serializable> layout, final int bufferSize) {
- return getSocketManager(sslConfig, host, port, connectTimeoutMillis, reconnectDelayMillis, immediateFail, layout, bufferSize, null);
+ public static SslSocketManager getSocketManager(
+ final SslConfiguration sslConfig,
+ final String host,
+ final int port,
+ final int connectTimeoutMillis,
+ final int reconnectDelayMillis,
+ final boolean immediateFail,
+ final Layout<? extends Serializable> layout,
+ final int bufferSize) {
+ return getSocketManager(
+ sslConfig,
+ host,
+ port,
+ connectTimeoutMillis,
+ reconnectDelayMillis,
+ immediateFail,
+ layout,
+ bufferSize,
+ null);
}
- public static SslSocketManager getSocketManager(final SslConfiguration sslConfig, final String host, int port,
- final int connectTimeoutMillis, int reconnectDelayMillis, final boolean immediateFail,
- final Layout<? extends Serializable> layout, final int bufferSize, final SocketOptions socketOptions) {
+ public static SslSocketManager getSocketManager(
+ final SslConfiguration sslConfig,
+ final String host,
+ int port,
+ final int connectTimeoutMillis,
+ int reconnectDelayMillis,
+ final boolean immediateFail,
+ final Layout<? extends Serializable> layout,
+ final int bufferSize,
+ final SocketOptions socketOptions) {
+
+ // Check arguments
if (Strings.isEmpty(host)) {
throw new IllegalArgumentException("A host name is required");
}
@@ -129,71 +219,215 @@ public class SslSocketManager extends TcpSocketManager {
if (reconnectDelayMillis == 0) {
reconnectDelayMillis = DEFAULT_RECONNECTION_DELAY_MILLIS;
}
- final String name = "TLS:" + host + ':' + port;
- return (SslSocketManager) getManager(name, new SslFactoryData(sslConfig, host, port, connectTimeoutMillis,
- reconnectDelayMillis, immediateFail, layout, bufferSize, socketOptions), FACTORY);
+
+ // Create an ID associated with the SSL configuration. This is necessary to make sure a new `name` is generated
+ // (and consequently a new connection pool is created) upon reconfiguration with a different configuration;
+ // e.g., change in the SSL certificate content, even though the certificate file locations are still the same.
+ // See #2767 and LOG4J2-2988 for details.
+ final String sslConfigId = createSslConfigurationId(sslConfig);
+ final String name = String.format("%s:%s:%d:%s", sslConfig.getProtocol(), host, port, sslConfigId);
+
+ return (SslSocketManager) getManager(
+ name,
+ new SslFactoryData(
+ sslConfig,
+ host,
+ port,
+ connectTimeoutMillis,
+ reconnectDelayMillis,
+ immediateFail,
+ layout,
+ bufferSize,
+ socketOptions),
+ FACTORY);
+ }
+
+ /**
+ * Creates a unique identifier using the certificate issuers and serial numbers of the given SSL configuration.
+ *
+ * @param sslConfig an SSL configuration
+ * @return a unique identifier extracted from the given SSL configuration
+ */
+ private static String createSslConfigurationId(final SslConfiguration sslConfig) {
+ return String.valueOf(Stream.of(sslConfig.getKeyStoreConfig(), sslConfig.getTrustStoreConfig())
+ .filter(Objects::nonNull)
+ .flatMap(keyStoreConfig -> {
+ final Enumeration<String> aliases;
+ try {
+ aliases = keyStoreConfig.getKeyStore().aliases();
+ } catch (final KeyStoreException error) {
+ LOGGER.error(
+ "Failed reading the aliases for the key store located at `{}`",
+ keyStoreConfig.getLocation(),
+ error);
+ return Stream.empty();
+ }
+ return Collections.list(aliases).stream().sorted().flatMap(alias -> {
+ final X509Certificate certificate;
+ try {
+ certificate = (X509Certificate)
+ keyStoreConfig.getKeyStore().getCertificate(alias);
+ } catch (final KeyStoreException error) {
+ LOGGER.error(
+ "Failed reading the certificate of alias `{}` for the key store located at `{}`",
+ alias,
+ keyStoreConfig.getLocation(),
+ error);
+ return Stream.empty();
+ }
+ final String issuer =
+ certificate.getIssuerX500Principal().getName();
+ final String serialNumber =
+ certificate.getSerialNumber().toString();
+ return Stream.of(issuer, serialNumber);
+ });
+ })
+ .collect(Collectors.toList())
+ .hashCode());
}
@Override
protected Socket createSocket(final InetSocketAddress socketAddress) throws IOException {
- final SSLSocketFactory socketFactory = createSslSocketFactory(sslConfig);
- final Socket newSocket = socketFactory.createSocket();
- newSocket.connect(socketAddress, getConnectTimeoutMillis());
- return newSocket;
+ return createSocket(getHost(), socketAddress, getConnectTimeoutMillis(), sslConfig, getSocketOptions());
}
private static SSLSocketFactory createSslSocketFactory(final SslConfiguration sslConf) {
- SSLSocketFactory socketFactory;
-
if (sslConf != null) {
- socketFactory = sslConf.getSslSocketFactory();
- } else {
- socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
+ final SSLContext sslContext = sslConf.getSslContext();
+ if (sslContext != null) {
+ return sslContext.getSocketFactory();
}
-
- return socketFactory;
}
-
+ return (SSLSocketFactory) SSLSocketFactory.getDefault();
+ }
private static class SslSocketManagerFactory extends TcpSocketManagerFactory<SslSocketManager, SslFactoryData> {
@Override
- SslSocketManager createManager(final String name, final OutputStream os, final Socket socket, final InetAddress inetAddress,
+ SslSocketManager createManager(
+ final String name,
+ final OutputStream os,
+ final Socket socket,
+ final InetAddress inetAddress,
final SslFactoryData data) {
- return new SslSocketManager(name, os, socket, data.sslConfiguration, inetAddress, data.host, data.port,
- data.connectTimeoutMillis, data.reconnectDelayMillis, data.immediateFail, data.layout, data.bufferSize,
+ return new SslSocketManager(
+ name,
+ os,
+ socket,
+ data.sslConfiguration,
+ inetAddress,
+ data.host,
+ data.port,
+ data.connectTimeoutMillis,
+ data.reconnectDelayMillis,
+ data.immediateFail,
+ data.layout,
+ data.bufferSize,
data.socketOptions);
}
@Override
Socket createSocket(final SslFactoryData data) throws IOException {
- List<InetSocketAddress> socketAddresses = RESOLVER.resolveHost(data.host, data.port);
+ final List<InetSocketAddress> socketAddresses = RESOLVER.resolveHost(data.host, data.port);
IOException ioe = null;
for (InetSocketAddress socketAddress : socketAddresses) {
try {
- return SslSocketManager.createSocket(socketAddress, data.connectTimeoutMillis,
- data.sslConfiguration, data.socketOptions);
+ return SslSocketManager.createSocket(
+ data.host,
+ socketAddress,
+ data.connectTimeoutMillis,
+ data.sslConfiguration,
+ data.socketOptions);
} catch (IOException ex) {
- ioe = ex;
+ final String message = String.format(
+ "failed create a socket to `%s:%s` that is resolved to address `%s`",
+ data.host, data.port, socketAddress);
+ final IOException newEx = new IOException(message, ex);
+ if (ioe == null) {
+ ioe = newEx;
+ } else {
+ ioe.addSuppressed(newEx);
+ }
}
}
throw new IOException(errorMessage(data, socketAddresses) , ioe);
}
}
- static Socket createSocket(final InetSocketAddress socketAddress, final int connectTimeoutMillis,
- final SslConfiguration sslConfiguration, final SocketOptions socketOptions) throws IOException {
+ private static Socket createSocket(
+ final String hostName,
+ final InetSocketAddress socketAddress,
+ final int connectTimeoutMillis,
+ final SslConfiguration sslConfiguration,
+ final SocketOptions socketOptions)
+ throws IOException {
+
+ // Create the `SSLSocket`
final SSLSocketFactory socketFactory = createSslSocketFactory(sslConfiguration);
final SSLSocket socket = (SSLSocket) socketFactory.createSocket();
+
+ // Apply socket options before `connect()`
if (socketOptions != null) {
- // Not sure which options must be applied before or after the connect() call.
socketOptions.apply(socket);
}
+
+ // Connect the socket
socket.connect(socketAddress, connectTimeoutMillis);
- if (socketOptions != null) {
- // Not sure which options must be applied before or after the connect() call.
- socketOptions.apply(socket);
+
+ // Verify the host name
+ if (sslConfiguration.isVerifyHostName()) {
+ // Allowed endpoint identification algorithms: HTTPS and LDAPS.
+ // https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html#endpoint-identification-algorithms
+ final SSLParameters sslParameters = socket.getSSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+ final SNIHostName serverName = createSniHostName(hostName);
+ if (serverName != null) {
+ sslParameters.setServerNames(Collections.singletonList(serverName));
+ }
+ socket.setSSLParameters(sslParameters);
}
+
+ // Force the handshake right after `connect()` instead of waiting for read/write to trigger it indirectly at a
+ // later stage
+ socket.startHandshake();
+
return socket;
}
+
+ /**
+ * {@return an {@link SNIHostName} instance if the provided host name is not an IP literal (RFC 6066), and constitutes a valid host name (RFC 1035); null otherwise}
+ *
+ * @param hostName a host name
+ *
+ * @see <a href="https://www.rfc-editor.org/rfc/rfc6066.html#section-3">Literal IPv4 and IPv6 addresses are not permitted in "HostName" (RFC 6066)</a>
+ * @see <a href="https://www.rfc-editor.org/rfc/rfc1035.html">Domain Names - Implementation and Specification (RFC 1035)</a>
+ */
+ static SNIHostName createSniHostName(String hostName) {
+ // The actual check should be
+ //
+ // !isIPv4(h) && !isIPv6(h) && isValidHostName(h)
+ //
+ // Though we translate this into
+ //
+ // !h.matches("\d+[.]\d+[.]\d+[.]\d+") && new SNIServerName(h)
+ //
+ // This simplification is possible because
+ //
+ // - The `\d+[.]\d+[.]\d+[.]\d+` is sufficient to eliminate IPv4 addresses.
+ // Any sequence of four numeric labels (e.g., `1234.2345.3456.4567`) is not a valid host name.
+ // Hence, false positives are not a problem, they would be eliminated by `isValidHostName()` anyway.
+ //
+ // - `SNIServerName::new` throws an exception on invalid host names.
+ // This check is performed using `IDN.toASCII(hostName, IDN.USE_STD3_ASCII_RULES)`.
+ // IPv6 literals don't qualify as a valid host name by `IDN::toASCII`.
+ // This assumption on `IDN` is unlikely to change in the foreseeable future.
+ if (!hostName.matches("\\d+[.]\\d+[.]\\d+[.]\\d+")) {
+ try {
+ return new SNIHostName(hostName);
+ } catch (IllegalArgumentException ignored) {
+ // Do nothing
+ }
+ }
+ return null;
+ }
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfiguration.java
index fa23b89759..8a967d6294 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfiguration.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfiguration.java
@@ -16,12 +16,8 @@
*/
package org.apache.logging.log4j.core.net.ssl;
-import java.security.KeyManagementException;
-import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
import java.util.Objects;
-
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
@@ -42,20 +38,30 @@ import org.apache.logging.log4j.status.StatusLogger;
*/
@Plugin(name = "Ssl", category = Core.CATEGORY_NAME, printObject = true)
public class SslConfiguration {
+
private static final StatusLogger LOGGER = StatusLogger.getLogger();
- private final KeyStoreConfiguration keyStoreConfig;
- private final TrustStoreConfiguration trustStoreConfig;
- private final SSLContext sslContext;
+
private final String protocol;
+
private final boolean verifyHostName;
- private SslConfiguration(final String protocol, final KeyStoreConfiguration keyStoreConfig,
- final TrustStoreConfiguration trustStoreConfig, boolean verifyHostName) {
+ private final KeyStoreConfiguration keyStoreConfig;
+
+ private final TrustStoreConfiguration trustStoreConfig;
+
+ private final transient SSLContext sslContext;
+
+ private SslConfiguration(
+ final String protocol,
+ final boolean verifyHostName,
+ final KeyStoreConfiguration keyStoreConfig,
+ final TrustStoreConfiguration trustStoreConfig) {
this.keyStoreConfig = keyStoreConfig;
this.trustStoreConfig = trustStoreConfig;
- this.protocol = protocol == null ? SslConfigurationDefaults.PROTOCOL : protocol;
- this.sslContext = this.createSslContext();
+ final String effectiveProtocol = protocol == null ? SslConfigurationDefaults.PROTOCOL : protocol;
+ this.protocol = effectiveProtocol;
this.verifyHostName = verifyHostName;
+ this.sslContext = createSslContext(effectiveProtocol, keyStoreConfig, trustStoreConfig);
}
/**
@@ -70,160 +76,91 @@ public class SslConfiguration {
}
}
+ /**
+ * Gets the SSL socket factory of the configured SSL context.
+ *
+ * @return the SSL socket factory of the configured SSL context
+ * @deprecated Use {@link SSLContext#getSocketFactory()} on {@link #getSslContext()}
+ */
+ @Deprecated
public SSLSocketFactory getSslSocketFactory() {
- return sslContext.getSocketFactory();
+ return sslContext != null ? sslContext.getSocketFactory() : null;
}
+ /**
+ * Gets the SSL server socket factory of the configured SSL context.
+ *
+ * @return the SSL server socket factory of the configured SSL context
+ * @deprecated Use {@link SSLContext#getServerSocketFactory()} on {@link #getSslContext()}
+ */
+ @Deprecated
public SSLServerSocketFactory getSslServerSocketFactory() {
- return sslContext.getServerSocketFactory();
- }
-
- private SSLContext createSslContext() {
- SSLContext context = null;
-
- try {
- context = createSslContextBasedOnConfiguration();
- LOGGER.debug("Creating SSLContext with the given parameters");
- }
- catch (final TrustStoreConfigurationException e) {
- context = createSslContextWithTrustStoreFailure();
- }
- catch (final KeyStoreConfigurationException e) {
- context = createSslContextWithKeyStoreFailure();
- }
- return context;
- }
-
- private SSLContext createSslContextWithTrustStoreFailure() {
- SSLContext context;
-
- try {
- context = createSslContextWithDefaultTrustManagerFactory();
- LOGGER.debug("Creating SSLContext with default truststore");
- }
- catch (final KeyStoreConfigurationException e) {
- context = createDefaultSslContext();
- LOGGER.debug("Creating SSLContext with default configuration");
- }
- return context;
- }
-
- private SSLContext createSslContextWithKeyStoreFailure() {
- SSLContext context;
-
- try {
- context = createSslContextWithDefaultKeyManagerFactory();
- LOGGER.debug("Creating SSLContext with default keystore");
- }
- catch (final TrustStoreConfigurationException e) {
- context = createDefaultSslContext();
- LOGGER.debug("Creating SSLContext with default configuration");
- }
- return context;
- }
-
- private SSLContext createSslContextBasedOnConfiguration() throws KeyStoreConfigurationException, TrustStoreConfigurationException {
- return createSslContext(false, false);
- }
-
- private SSLContext createSslContextWithDefaultKeyManagerFactory() throws TrustStoreConfigurationException {
- try {
- return createSslContext(true, false);
- } catch (final KeyStoreConfigurationException dummy) {
- LOGGER.debug("Exception occurred while using default keystore. This should be a BUG");
- return null;
- }
+ return sslContext != null ? sslContext.getServerSocketFactory() : null;
}
- private SSLContext createSslContextWithDefaultTrustManagerFactory() throws KeyStoreConfigurationException {
- try {
- return createSslContext(false, true);
- }
- catch (final TrustStoreConfigurationException dummy) {
- LOGGER.debug("Exception occurred while using default truststore. This should be a BUG");
- return null;
- }
- }
-
- private SSLContext createDefaultSslContext() {
+ private static SSLContext createDefaultSslContext(final String protocol) {
try {
return SSLContext.getDefault();
- } catch (final NoSuchAlgorithmException e) {
- LOGGER.error("Failed to create an SSLContext with default configuration", e);
+ } catch (final NoSuchAlgorithmException defaultContextError) {
+ LOGGER.error(
+ "Failed to create an `SSLContext` using the default configuration, falling back to creating an empty one",
+ defaultContextError);
+ try {
+ final SSLContext emptyContext = SSLContext.getInstance(protocol);
+ emptyContext.init(new KeyManager[0], new TrustManager[0], null);
+ return emptyContext;
+ } catch (final Exception emptyContextError) {
+ LOGGER.error("Failed to create an empty `SSLContext`", emptyContextError);
return null;
}
}
-
- private SSLContext createSslContext(final boolean loadDefaultKeyManagerFactory, final boolean loadDefaultTrustManagerFactory)
- throws KeyStoreConfigurationException, TrustStoreConfigurationException {
- try {
- KeyManager[] kManagers = null;
- TrustManager[] tManagers = null;
-
- final SSLContext newSslContext = SSLContext.getInstance(this.protocol);
- if (!loadDefaultKeyManagerFactory) {
- final KeyManagerFactory kmFactory = loadKeyManagerFactory();
- kManagers = kmFactory.getKeyManagers();
- }
- if (!loadDefaultTrustManagerFactory) {
- final TrustManagerFactory tmFactory = loadTrustManagerFactory();
- tManagers = tmFactory.getTrustManagers();
}
- newSslContext.init(kManagers, tManagers, null);
- return newSslContext;
- }
- catch (final NoSuchAlgorithmException e) {
- LOGGER.error("No Provider supports a TrustManagerFactorySpi implementation for the specified protocol", e);
- throw new TrustStoreConfigurationException(e);
- }
- catch (final KeyManagementException e) {
- LOGGER.error("Failed to initialize the SSLContext", e);
- throw new KeyStoreConfigurationException(e);
+ private static SSLContext createSslContext(
+ final String protocol,
+ final KeyStoreConfiguration keyStoreConfig,
+ final TrustStoreConfiguration trustStoreConfig) {
+ try {
+ final SSLContext sslContext = SSLContext.getInstance(protocol);
+ final KeyManager[] keyManagers = loadKeyManagers(keyStoreConfig);
+ final TrustManager[] trustManagers = loadTrustManagers(trustStoreConfig);
+ sslContext.init(keyManagers, trustManagers, null);
+ return sslContext;
+ } catch (final Exception error) {
+ LOGGER.error(
+ "Failed to create an `SSLContext` using the provided configuration, falling back to a default instance",
+ error);
+ return createDefaultSslContext(protocol);
}
}
- private TrustManagerFactory loadTrustManagerFactory() throws TrustStoreConfigurationException {
- if (trustStoreConfig == null) {
- throw new TrustStoreConfigurationException(new Exception("The trustStoreConfiguration is null"));
+ private static KeyManager[] loadKeyManagers(final KeyStoreConfiguration config) throws Exception {
+ if (config == null) {
+ return null;
}
-
+ final KeyManagerFactory factory = KeyManagerFactory.getInstance(config.getKeyManagerFactoryAlgorithm());
+ final char[] password = config.getPasswordAsCharArray();
try {
- return trustStoreConfig.initTrustManagerFactory();
- }
- catch (final NoSuchAlgorithmException e) {
- LOGGER.error("The specified algorithm is not available from the specified provider", e);
- throw new TrustStoreConfigurationException(e);
- } catch (final KeyStoreException e) {
- LOGGER.error("Failed to initialize the TrustManagerFactory", e);
- throw new TrustStoreConfigurationException(e);
+ factory.init(config.getKeyStore(), password);
+ } finally {
+ config.clearSecrets();
}
- }
-
- private KeyManagerFactory loadKeyManagerFactory() throws KeyStoreConfigurationException {
- if (keyStoreConfig == null) {
- throw new KeyStoreConfigurationException(new Exception("The keyStoreConfiguration is null"));
+ return factory.getKeyManagers();
}
- try {
- return keyStoreConfig.initKeyManagerFactory();
- }
- catch (final NoSuchAlgorithmException e) {
- LOGGER.error("The specified algorithm is not available from the specified provider", e);
- throw new KeyStoreConfigurationException(e);
- } catch (final KeyStoreException e) {
- LOGGER.error("Failed to initialize the TrustManagerFactory", e);
- throw new KeyStoreConfigurationException(e);
- } catch (final UnrecoverableKeyException e) {
- LOGGER.error("The key cannot be recovered (e.g. the given password is wrong)", e);
- throw new KeyStoreConfigurationException(e);
+ private static TrustManager[] loadTrustManagers(final TrustStoreConfiguration config) throws Exception {
+ if (config == null) {
+ return null;
}
+ final TrustManagerFactory factory = TrustManagerFactory.getInstance(config.getTrustManagerFactoryAlgorithm());
+ factory.init(config.getKeyStore());
+ return factory.getTrustManagers();
}
/**
* Creates an SslConfiguration from a KeyStoreConfiguration and a TrustStoreConfiguration.
*
- * @param protocol The protocol, see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
+ * @param protocol The protocol, see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">SSLContext Algorithms</a>
* @param keyStoreConfig The KeyStoreConfiguration.
* @param trustStoreConfig The TrustStoreConfiguration.
* @return a new SslConfiguration
@@ -235,13 +172,13 @@ public class SslConfiguration {
@PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig,
@PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig) {
// @formatter:on
- return new SslConfiguration(protocol, keyStoreConfig, trustStoreConfig, false);
+ return new SslConfiguration(protocol, false, keyStoreConfig, trustStoreConfig);
}
/**
* Creates an SslConfiguration from a KeyStoreConfiguration and a TrustStoreConfiguration.
*
- * @param protocol The protocol, see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
+ * @param protocol The protocol, see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">SSLContext Algorithms</a>
* @param keyStoreConfig The KeyStoreConfiguration.
* @param trustStoreConfig The TrustStoreConfiguration.
* @param verifyHostName whether or not to perform host name verification
@@ -255,7 +192,7 @@ public class SslConfiguration {
@PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig,
@PluginAttribute("verifyHostName") final boolean verifyHostName) {
// @formatter:on
- return new SslConfiguration(protocol, keyStoreConfig, trustStoreConfig, verifyHostName);
+ return new SslConfiguration(protocol, verifyHostName, keyStoreConfig, trustStoreConfig);
}
@Override
@@ -275,13 +212,13 @@ public class SslConfiguration {
return false;
}
final SslConfiguration other = (SslConfiguration) obj;
- if (!Objects.equals(keyStoreConfig, other.keyStoreConfig)) {
+ if (!Objects.equals(protocol, other.protocol)) {
return false;
}
- if (!Objects.equals(protocol, other.protocol)) {
+ if (!Objects.equals(verifyHostName, other.verifyHostName)) {
return false;
}
- if (!Objects.equals(sslContext, other.sslContext)) {
+ if (!Objects.equals(keyStoreConfig, other.keyStoreConfig)) {
return false;
}
if (!Objects.equals(trustStoreConfig, other.trustStoreConfig)) {
@@ -290,6 +227,14 @@ public class SslConfiguration {
return true;
}
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public boolean isVerifyHostName() {
+ return verifyHostName;
+ }
+
public KeyStoreConfiguration getKeyStoreConfig() {
return keyStoreConfig;
}
@@ -301,12 +246,4 @@ public class SslConfiguration {
public SSLContext getSslContext() {
return sslContext;
}
-
- public String getProtocol() {
- return protocol;
- }
-
- public boolean isVerifyHostName() {
- return verifyHostName;
- }
}