Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2008-2021 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright 2008-2021 Ping Identity Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2008-2021 Ping Identity Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* 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, see .
*/
package com.unboundid.util.ssl;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import javax.security.auth.x500.X500Principal;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.CryptoHelper;
import com.unboundid.util.Debug;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadLocalSecureRandom;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.Validator;
import static com.unboundid.util.ssl.SSLMessages.*;
/**
* This class provides a simple interface for creating {@code SSLContext} and
* {@code SSLSocketFactory} instances, which may be used to create SSL-based
* connections, or secure existing connections with StartTLS. By default, only
* the TLSv1.2 and TLSv1.3 (if supported by the JVM) will be enabled, with the
* higher protocol version being the default and preferred for use. The TLSv1.1
* or TLSv1 protocol will only be enabled if the JVM does not support either
* TLSv1.2 or TLSv1.3.
*
*
Example 1
* The following example demonstrates the use of the SSL helper to create an
* SSL-based LDAP connection that will blindly trust any certificate that the
* server presents. Using the {@code TrustAllTrustManager} is only recommended
* for testing purposes, since blindly trusting any certificate is not secure.
*
* // Create an SSLUtil instance that is configured to trust any certificate,
* // and use it to create a socket factory.
* SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
* SSLSocketFactory sslSocketFactory = sslUtil.createSSLSocketFactory();
*
* // Establish a secure connection using the socket factory.
* LDAPConnection connection = new LDAPConnection(sslSocketFactory);
* connection.connect(serverAddress, serverSSLPort);
*
* // Process operations using the connection....
* RootDSE rootDSE = connection.getRootDSE();
*
* connection.close();
*
*
*
Example 2
* The following example demonstrates the use of the SSL helper to create a
* non-secure LDAP connection and then use the StartTLS extended operation to
* secure it. It will use a trust store to determine whether to trust the
* server certificate.
*
* // Establish a non-secure connection to the server.
* LDAPConnection connection = new LDAPConnection(serverAddress, serverPort);
*
* // Create an SSLUtil instance that is configured to trust certificates in
* // a specified trust store file, and use it to create an SSLContext that
* // will be used for StartTLS processing.
* SSLUtil sslUtil = new SSLUtil(new TrustStoreTrustManager(trustStorePath));
* SSLContext sslContext = sslUtil.createSSLContext();
*
* // Use the StartTLS extended operation to secure the connection.
* StartTLSExtendedRequest startTLSRequest =
* new StartTLSExtendedRequest(sslContext);
* ExtendedResult startTLSResult;
* try
* {
* startTLSResult = connection.processExtendedOperation(startTLSRequest);
* }
* catch (LDAPException le)
* {
* startTLSResult = new ExtendedResult(le);
* }
* LDAPTestUtils.assertResultCodeEquals(startTLSResult, ResultCode.SUCCESS);
*
* // Process operations using the connection....
* RootDSE rootDSE = connection.getRootDSE();
*
* connection.close();
*
*/
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class SSLUtil
{
/**
* The name of a system property
* (com.unboundid.util.SSLUtil.defaultSSLProtocol) that can be used to specify
* the initial value for the default SSL protocol that should be used. If
* this is not set, then the default SSL protocol will be dynamically
* determined. This can be overridden via the
* {@link #setDefaultSSLProtocol(String)} method.
*/
@NotNull public static final String PROPERTY_DEFAULT_SSL_PROTOCOL =
"com.unboundid.util.SSLUtil.defaultSSLProtocol";
/**
* The name of a system property
* (com.unboundid.util.SSLUtil.enabledSSLProtocols) that can be used to
* provide the initial set of enabled SSL protocols that should be used, as a
* comma-delimited list. If this is not set, then the enabled SSL protocols
* will be dynamically determined. This can be overridden via the
* {@link #setEnabledSSLProtocols(Collection)} method.
*/
@NotNull public static final String PROPERTY_ENABLED_SSL_PROTOCOLS =
"com.unboundid.util.SSLUtil.enabledSSLProtocols";
/**
* The name of a system property
* (com.unboundid.util.SSLUtil.enabledSSLCipherSuites) that can be used to
* provide the initial set of enabled SSL cipher suites that should be used,
* as a comma-delimited list. If this is not set, then the enabled SSL cipher
* suites will be dynamically determined using the
* {@link TLSCipherSuiteSelector}. This can be overridden via the
* {@link #setEnabledSSLCipherSuites(Collection)} method.
*/
@NotNull public static final String PROPERTY_ENABLED_SSL_CIPHER_SUITES =
"com.unboundid.util.SSLUtil.enabledSSLCipherSuites";
/**
* The name of the SSL protocol that can be used to request TLSv1.3.
*/
@NotNull public static final String SSL_PROTOCOL_TLS_1_3 = "TLSv1.3";
/**
* The name of the SSL protocol that can be used to request TLSv1.2.
*/
@NotNull public static final String SSL_PROTOCOL_TLS_1_2 = "TLSv1.2";
/**
* The name of the SSL protocol that can be used to request TLSv1.1.
*/
@NotNull public static final String SSL_PROTOCOL_TLS_1_1 = "TLSv1.1";
/**
* The name of the SSL protocol that can be used to request TLSv1.
*/
@NotNull public static final String SSL_PROTOCOL_TLS_1 = "TLSv1";
/**
* The name of the SSL protocol that can be used to request SSLv3.
*/
@NotNull public static final String SSL_PROTOCOL_SSL_3 = "SSLv3";
/**
* The name of the SSL protocol that can be used to request SSLv2Hello.
*/
@NotNull public static final String SSL_PROTOCOL_SSL_2_HELLO = "SSLv2Hello";
/**
* The default protocol string that will be used to create SSL contexts when
* no explicit protocol is specified.
*/
@NotNull private static final AtomicReference DEFAULT_SSL_PROTOCOL =
new AtomicReference<>(SSL_PROTOCOL_TLS_1_2);
/**
* The default set of SSL cipher suites that will be enabled for use if
* available for SSL sockets created within the LDAP SDK.
*/
@NotNull private static final AtomicReference>
ENABLED_SSL_CIPHER_SUITES = new AtomicReference<>(
(Set) new LinkedHashSet<>(
TLSCipherSuiteSelector.getRecommendedCipherSuites()));
/**
* The default set of SSL protocols that will be enabled for use if available
* for SSL sockets created within the LDAP SDK.
*/
@NotNull private static final AtomicReference>
ENABLED_SSL_PROTOCOLS = new AtomicReference<>(
StaticUtils.setOf(SSL_PROTOCOL_TLS_1_2));
/**
* The name of the service type that providers use to indicate the
* {@code SSLContext} algorithms that they support.
*/
@NotNull static final String PROVIDER_SERVICE_TYPE_SSL_CONTEXT =
"SSLContext";
/**
* Indicates whether SSL/TLS debugging is expected to be enabled, based on
* the javax.net.debug system property.
*/
private static final boolean JVM_SSL_DEBUGGING_ENABLED =
TLSCipherSuiteSelector.jvmSSLDebuggingEnabled();
static
{
configureSSLDefaults();
}
// Indicates whether any of the provided key managers is a PKCS #11 key
// manager.
private final boolean usingPKCS11KeyManager;
// The set of key managers to be used.
@Nullable private final KeyManager[] keyManagers;
// The set of trust managers to be used.
@Nullable private final TrustManager[] trustManagers;
/**
* Creates a new SSLUtil instance that will not have a custom key manager or
* trust manager. It will not be able to provide a certificate to the server
* if one is requested, and it will only trust certificates signed by a
* predefined set of authorities.
*/
public SSLUtil()
{
keyManagers = null;
trustManagers = null;
usingPKCS11KeyManager = false;
}
/**
* Creates a new SSLUtil instance that will use the provided trust manager to
* determine whether to trust server certificates presented to the client.
* It will not be able to provide a certificate to the server if one is
* requested.
*
* @param trustManager The trust manager to use to determine whether to
* trust server certificates presented to the client.
* It may be {@code null} if the default set of trust
* managers should be used.
*/
public SSLUtil(@Nullable final TrustManager trustManager)
{
keyManagers = null;
usingPKCS11KeyManager = false;
if (trustManager == null)
{
trustManagers = null;
}
else
{
trustManagers = new TrustManager[] { trustManager };
}
}
/**
* Creates a new SSLUtil instance that will use the provided trust managers
* to determine whether to trust server certificates presented to the client.
* It will not be able to provide a certificate to the server if one is
* requested.
*
* @param trustManagers The set of trust managers to use to determine
* whether to trust server certificates presented to
* the client. It may be {@code null} or empty if the
* default set of trust managers should be used.
*/
public SSLUtil(@Nullable final TrustManager[] trustManagers)
{
keyManagers = null;
usingPKCS11KeyManager = false;
if ((trustManagers == null) || (trustManagers.length == 0))
{
this.trustManagers = null;
}
else
{
this.trustManagers = trustManagers;
}
}
/**
* Creates a new SSLUtil instance that will use the provided key manager to
* obtain certificates to present to the server, and the provided trust
* manager to determine whether to trust server certificates presented to the
* client.
*
* @param keyManager The key manager to use to obtain certificates to
* present to the server if requested. It may be
* {@code null} if no client certificates will be
* required or should be provided.
* @param trustManager The trust manager to use to determine whether to
* trust server certificates presented to the client.
* It may be {@code null} if the default set of trust
* managers should be used.
*/
public SSLUtil(@Nullable final KeyManager keyManager,
@Nullable final TrustManager trustManager)
{
if (keyManager == null)
{
keyManagers = null;
usingPKCS11KeyManager = false;
}
else
{
keyManagers = new KeyManager[] { keyManager };
usingPKCS11KeyManager = (keyManager instanceof PKCS11KeyManager);
}
if (trustManager == null)
{
trustManagers = null;
}
else
{
trustManagers = new TrustManager[] { trustManager };
}
}
/**
* Creates a new SSLUtil instance that will use the provided key managers to
* obtain certificates to present to the server, and the provided trust
* managers to determine whether to trust server certificates presented to the
* client.
*
* @param keyManagers The set of key managers to use to obtain
* certificates to present to the server if requested.
* It may be {@code null} or empty if no client
* certificates will be required or should be provided.
* @param trustManagers The set of trust managers to use to determine
* whether to trust server certificates presented to
* the client. It may be {@code null} or empty if the
* default set of trust managers should be used.
*/
public SSLUtil(@Nullable final KeyManager[] keyManagers,
@Nullable final TrustManager[] trustManagers)
{
if ((keyManagers == null) || (keyManagers.length == 0))
{
this.keyManagers = null;
usingPKCS11KeyManager = false;
}
else
{
this.keyManagers = keyManagers;
boolean usingPKCS11 = false;
for (final KeyManager km : keyManagers)
{
if (km instanceof PKCS11KeyManager)
{
usingPKCS11 = true;
break;
}
}
usingPKCS11KeyManager = usingPKCS11;
}
if ((trustManagers == null) || (trustManagers.length == 0))
{
this.trustManagers = null;
}
else
{
this.trustManagers = trustManagers;
}
}
/**
* Retrieves the set of key managers configured for use by this class, if any.
*
* @return The set of key managers configured for use by this class, or
* {@code null} if none were provided.
*/
@Nullable()
public KeyManager[] getKeyManagers()
{
return keyManagers;
}
/**
* Retrieves the set of trust managers configured for use by this class, if
* any.
*
* @return The set of trust managers configured for use by this class, or
* {@code null} if none were provided.
*/
@Nullable()
public TrustManager[] getTrustManagers()
{
return trustManagers;
}
/**
* Creates an initialized SSL context created with the configured key and
* trust managers. It will use the protocol returned by the
* {@link #getDefaultSSLProtocol} method and the JVM-default provider.
*
* @return The created SSL context.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL context.
*/
@NotNull()
public SSLContext createSSLContext()
throws GeneralSecurityException
{
return createSSLContext(DEFAULT_SSL_PROTOCOL.get());
}
/**
* Creates an initialized SSL context created with the configured key and
* trust managers. It will use a default provider.
*
* @param protocol The SSL protocol to use. The Java Secure Socket
* Extension (JSSE) Reference Guide provides a list of the
* supported protocols, but commonly used values are
* "TLSv1.3", "TLSv1.2", "TLSv1.1", and "TLSv1". This must
* not be {@code null}.
*
*
* @return The created SSL context.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL context.
*/
@NotNull()
public SSLContext createSSLContext(@NotNull final String protocol)
throws GeneralSecurityException
{
Validator.ensureNotNull(protocol);
SSLContext sslContext = null;
if (usingPKCS11KeyManager)
{
final Provider pkcs11JSSEProvider =
PKCS11KeyManager.getPKCS11JSSESProvider();
if ((pkcs11JSSEProvider != null) && (pkcs11JSSEProvider.getService(
PROVIDER_SERVICE_TYPE_SSL_CONTEXT, protocol) != null))
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLContext creating a PKCS #11 " +
"SSLContext for protocol " + protocol);
}
sslContext = CryptoHelper.getSSLContext(protocol, pkcs11JSSEProvider);
}
}
if (sslContext == null)
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLContext creating an SSLContext " +
"for protocol " + protocol);
}
sslContext = CryptoHelper.getSSLContext(protocol);
}
sslContext.init(keyManagers, trustManagers, ThreadLocalSecureRandom.get());
return sslContext;
}
/**
* Creates an initialized SSL context created with the configured key and
* trust managers.
*
* @param protocol The SSL protocol to use. The Java Secure Socket
* Extension (JSSE) Reference Guide provides a list of the
* supported protocols, but commonly used values are
* "TLSv1.3", "TLSv1.2", "TLSv1.1", and "TLSv1". This must
* not be {@code null}.
* @param provider The name of the provider to use for cryptographic
* operations. It must not be {@code null}.
*
* @return The created SSL context.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL context.
*/
@NotNull()
public SSLContext createSSLContext(@NotNull final String protocol,
@NotNull final String provider)
throws GeneralSecurityException
{
Validator.ensureNotNull(protocol, provider);
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLContext creating an SSLContext " +
"for protocol " + protocol + " and provider " + provider);
}
final SSLContext sslContext =
CryptoHelper.getSSLContext(protocol, provider);
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}
/**
* Creates an SSL socket factory using the configured key and trust manager
* providers. It will use the protocol returned by the
* {@link #getDefaultSSLProtocol} method and the JVM-default provider.
*
* @return The created SSL socket factory.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL socket factory.
*/
@NotNull()
public SSLSocketFactory createSSLSocketFactory()
throws GeneralSecurityException
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLSocketFactory creating a " +
"SetEnabledProtocolsAndCipherSuitesSSLSocketFactory with enabled " +
"protocols " + ENABLED_SSL_PROTOCOLS.get() +
" and enabled cipher suites " + ENABLED_SSL_CIPHER_SUITES.get());
}
return new SetEnabledProtocolsAndCipherSuitesSSLSocketFactory(
createSSLContext().getSocketFactory(),
ENABLED_SSL_PROTOCOLS.get(), ENABLED_SSL_CIPHER_SUITES.get());
}
/**
* Creates an SSL socket factory with the configured key and trust managers.
* It will use the default provider.
*
* @param protocol The SSL protocol to use. The Java Secure Socket
* Extension (JSSE) Reference Guide provides a list of the
* supported protocols, but commonly used values are
* "TLSv1.3", "TLSv1.2", "TLSv1.1", and "TLSv1". This must
* not be {@code null}.
*
* @return The created SSL socket factory.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL socket factory.
*/
@NotNull()
public SSLSocketFactory createSSLSocketFactory(
@NotNull final String protocol)
throws GeneralSecurityException
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLSocketFactory creating a " +
"SetEnabledProtocolsAndCipherSuitesSSLSocketFactory with protocol " +
protocol + " and enabled cipher suites " +
ENABLED_SSL_CIPHER_SUITES.get());
}
return new SetEnabledProtocolsAndCipherSuitesSSLSocketFactory(
createSSLContext(protocol).getSocketFactory(), protocol,
ENABLED_SSL_CIPHER_SUITES.get());
}
/**
* Creates an SSL socket factory with the configured key and trust managers.
*
* @param protocol The SSL protocol to use. The Java Secure Socket
* Extension (JSSE) Reference Guide provides a list of the
* supported protocols, but commonly used values are
* "TLSv1.3", "TLSv1.2", "TLSv1.1", and "TLSv1". This must
* not be {@code null}.
* @param provider The name of the provider to use for cryptographic
* operations. It must not be {@code null}.
*
* @return The created SSL socket factory.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL socket factory.
*/
@NotNull()
public SSLSocketFactory createSSLSocketFactory(@NotNull final String protocol,
@NotNull final String provider)
throws GeneralSecurityException
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLSocketFactory creating an " +
"SetEnabledProtocolsAndCipherSuitesSSLSocketFactory with protocol " +
protocol + ", provider " + provider +
", and enabled cipher suites " + ENABLED_SSL_CIPHER_SUITES.get());
}
return new SetEnabledProtocolsAndCipherSuitesSSLSocketFactory(
createSSLContext(protocol, provider).getSocketFactory(), protocol,
ENABLED_SSL_CIPHER_SUITES.get());
}
/**
* Creates an SSL server socket factory using the configured key and trust
* manager providers. It will use the protocol returned by the
* {@link #getDefaultSSLProtocol} method and the JVM-default provider.
*
* @return The created SSL server socket factory.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL server socket
* factory.
*/
@NotNull()
public SSLServerSocketFactory createSSLServerSocketFactory()
throws GeneralSecurityException
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLServerSocketFactory creating a " +
"SetEnabledProtocolsAndCipherSuitesSSLServerSocketFactory with " +
"enabled protocols " + ENABLED_SSL_PROTOCOLS.get() +
" and enabled cipher suites " + ENABLED_SSL_CIPHER_SUITES.get());
}
return new SetEnabledProtocolsAndCipherSuitesSSLServerSocketFactory(
createSSLContext().getServerSocketFactory(),
ENABLED_SSL_PROTOCOLS.get(), ENABLED_SSL_CIPHER_SUITES.get());
}
/**
* Creates an SSL server socket factory using the configured key and trust
* manager providers. It will use the JVM-default provider.
*
* @param protocol The SSL protocol to use. The Java Secure Socket
* Extension (JSSE) Reference Guide provides a list of the
* supported protocols, but commonly used values are
* "TLSv1.3", "TLSv1.2", "TLSv1.1", and "TLSv1". This must
* not be {@code null}.
*
* @return The created SSL server socket factory.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL server socket
* factory.
*/
@NotNull()
public SSLServerSocketFactory createSSLServerSocketFactory(
@NotNull final String protocol)
throws GeneralSecurityException
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLServerSocketFactory creating a " +
"SetEnabledProtocolsAndCipherSuitesSSLServerSocketFactory with " +
"protocol " + protocol + " and enabled cipher suites " +
ENABLED_SSL_CIPHER_SUITES.get());
}
return new SetEnabledProtocolsAndCipherSuitesSSLServerSocketFactory(
createSSLContext(protocol).getServerSocketFactory(), protocol,
ENABLED_SSL_CIPHER_SUITES.get());
}
/**
* Creates an SSL server socket factory using the configured key and trust
* manager providers.
*
* @param protocol The SSL protocol to use. The Java Secure Socket
* Extension (JSSE) Reference Guide provides a list of the
* supported protocols, but commonly used values are
* "TLSv1.3", "TLSv1.2", "TLSv1.1", and "TLSv1". This must
* not be {@code null}.
* @param provider The name of the provider to use for cryptographic
* operations. It must not be {@code null}.
*
* @return The created SSL server socket factory.
*
* @throws GeneralSecurityException If a problem occurs while creating or
* initializing the SSL server socket
* factory.
*/
@NotNull()
public SSLServerSocketFactory createSSLServerSocketFactory(
@NotNull final String protocol,
@NotNull final String provider)
throws GeneralSecurityException
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.createSSLServerSocketFactory creating a " +
"SetEnabledProtocolsAndCipherSuitesSSLServerSocketFactory with " +
"protocol " + protocol + ", provider " + provider +
", and enabled cipher suites " + ENABLED_SSL_CIPHER_SUITES.get());
}
return new SetEnabledProtocolsAndCipherSuitesSSLServerSocketFactory(
createSSLContext(protocol, provider).getServerSocketFactory(),
protocol, ENABLED_SSL_CIPHER_SUITES.get());
}
/**
* Retrieves the SSL protocol string that will be used by calls to
* {@link #createSSLContext()} that do not explicitly specify which protocol
* to use.
*
* @return The SSL protocol string that will be used by calls to create an
* SSL context that do not explicitly specify which protocol to use.
*/
@NotNull()
public static String getDefaultSSLProtocol()
{
return DEFAULT_SSL_PROTOCOL.get();
}
/**
* Specifies the SSL protocol string that will be used by calls to
* {@link #createSSLContext()} that do not explicitly specify which protocol
* to use.
*
* @param defaultSSLProtocol The SSL protocol string that will be used by
* calls to create an SSL context that do not
* explicitly specify which protocol to use. It
* must not be {@code null}.
*/
public static void setDefaultSSLProtocol(
@NotNull final String defaultSSLProtocol)
{
Validator.ensureNotNull(defaultSSLProtocol);
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.setDefaultSSLProtocol setting the " +
"default SSL protocol to " + defaultSSLProtocol);
}
DEFAULT_SSL_PROTOCOL.set(defaultSSLProtocol);
}
/**
* Retrieves the set of SSL protocols that will be enabled for use, if
* available, for SSL sockets created within the LDAP SDK.
*
* @return The set of SSL protocols that will be enabled for use, if
* available, for SSL sockets created within the LDAP SDK.
*/
@NotNull()
public static Set getEnabledSSLProtocols()
{
return ENABLED_SSL_PROTOCOLS.get();
}
/**
* Specifies the set of SSL protocols that will be enabled for use for SSL
* sockets created within the LDAP SDK. When creating an SSL socket, the
* {@code SSLSocket.getSupportedProtocols} method will be used to determine
* which protocols are supported for that socket, and then the
* {@code SSLSocket.setEnabledProtocols} method will be used to enable those
* protocols which are listed as both supported by the socket and included in
* this set. If the provided set is {@code null} or empty, then the default
* set of enabled protocols will be used.
*
* @param enabledSSLProtocols The set of SSL protocols that will be enabled
* for use for SSL sockets created within the
* LDAP SDK. It may be {@code null} or empty to
* indicate that the JDK-default set of enabled
* protocols should be used for the socket.
*/
public static void setEnabledSSLProtocols(
@Nullable final Collection enabledSSLProtocols)
{
if (enabledSSLProtocols == null)
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.setEnabledSSLProtocols setting the " +
"enabled SSL protocols to an empty set");
}
ENABLED_SSL_PROTOCOLS.set(Collections.emptySet());
}
else
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.setEnabledSSLProtocols setting the " +
"enabled SSL protocols to " + enabledSSLProtocols);
}
ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(
new LinkedHashSet<>(enabledSSLProtocols)));
}
}
/**
* Updates the provided socket to apply the appropriate set of enabled SSL
* protocols. This will only have any effect for sockets that are instances
* of {@code javax.net.ssl.SSLSocket}, but it is safe to call for any kind of
* {@code java.net.Socket}. This should be called before attempting any
* communication over the socket.
*
* @param socket The socket on which to apply the configured set of enabled
* SSL protocols.
*
* @throws LDAPException If {@link #getEnabledSSLProtocols} returns a
* non-empty set but none of the values in that set
* are supported by the socket.
*/
public static void applyEnabledSSLProtocols(@NotNull final Socket socket)
throws LDAPException
{
try
{
applyEnabledSSLProtocols(socket, ENABLED_SSL_PROTOCOLS.get());
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
throw new LDAPException(ResultCode.CONNECT_ERROR, ioe.getMessage(), ioe);
}
}
/**
* Updates the provided socket to apply the appropriate set of enabled SSL
* protocols. This will only have any effect for sockets that are instances
* of {@code javax.net.ssl.SSLSocket}, but it is safe to call for any kind of
* {@code java.net.Socket}. This should be called before attempting any
* communication over the socket.
*
* @param socket The socket on which to apply the configured set of
* enabled SSL protocols.
* @param protocols The set of protocols that should be enabled for the
* socket, if available.
*
* @throws IOException If a problem is encountered while applying the
* desired set of enabled protocols to the given socket.
*/
static void applyEnabledSSLProtocols(@Nullable final Socket socket,
@NotNull final Set protocols)
throws IOException
{
if ((socket == null) || (!(socket instanceof SSLSocket)) ||
protocols.isEmpty())
{
return;
}
final SSLSocket sslSocket = (SSLSocket) socket;
final String[] protocolsToEnable =
getSSLProtocolsToEnable(protocols, sslSocket.getSupportedProtocols());
try
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.applyEnabledSSLProtocols applying " +
"protocolsToEnable " + Arrays.toString(protocolsToEnable) +
" to SSLSocket " + sslSocket);
}
sslSocket.setEnabledProtocols(protocolsToEnable);
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
/**
* Updates the provided server socket to apply the appropriate set of enabled
* SSL protocols. This will only have any effect for server sockets that are
* instances of {@code javax.net.ssl.SSLServerSocket}, but it is safe to call
* for any kind of {@code java.net.ServerSocket}. This should be called
* before attempting any communication over the socket.
*
* @param serverSocket The server socket on which to apply the configured
* set of enabled SSL protocols.
* @param protocols The set of protocols that should be enabled for the
* server socket, if available.
*
* @throws IOException If a problem is encountered while applying the
* desired set of enabled protocols to the given server
* socket.
*/
static void applyEnabledSSLProtocols(
@Nullable final ServerSocket serverSocket,
@NotNull final Set protocols)
throws IOException
{
if ((serverSocket == null) ||
(!(serverSocket instanceof SSLServerSocket)) ||
protocols.isEmpty())
{
return;
}
final SSLServerSocket sslServerSocket = (SSLServerSocket) serverSocket;
final String[] protocolsToEnable = getSSLProtocolsToEnable(protocols,
sslServerSocket.getSupportedProtocols());
try
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.applyEnabledSSLProtocols applying " +
"protocolsToEnable " + Arrays.toString(protocolsToEnable) +
" to SSLServerSocket " + sslServerSocket);
}
sslServerSocket.setEnabledProtocols(protocolsToEnable);
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
/**
* Retrieves the names of the SSL protocols that should be enabled given the
* provided information.
*
* @param desiredProtocols The set of protocols that are desired to be
* enabled.
* @param supportedProtocols The set of all protocols that are supported.
*
* @return The names of the SSL protocols that should be enabled.
*
* @throws IOException If none of the desired values are included in the
* supported set.
*/
@NotNull()
private static String[] getSSLProtocolsToEnable(
@NotNull final Set desiredProtocols,
@NotNull final String[] supportedProtocols)
throws IOException
{
final Set lowerProtocols = new LinkedHashSet<>(
StaticUtils.computeMapCapacity(desiredProtocols.size()));
for (final String s : desiredProtocols)
{
lowerProtocols.add(StaticUtils.toLowerCase(s));
}
final ArrayList enabledList =
new ArrayList<>(supportedProtocols.length);
for (final String supportedProtocol : supportedProtocols)
{
if (lowerProtocols.contains(StaticUtils.toLowerCase(supportedProtocol)))
{
enabledList.add(supportedProtocol);
}
}
if (enabledList.isEmpty())
{
final StringBuilder enabledBuffer = new StringBuilder();
final Iterator enabledIterator = desiredProtocols.iterator();
while (enabledIterator.hasNext())
{
enabledBuffer.append('\'');
enabledBuffer.append(enabledIterator.next());
enabledBuffer.append('\'');
if (enabledIterator.hasNext())
{
enabledBuffer.append(", ");
}
}
final StringBuilder supportedBuffer = new StringBuilder();
for (int i=0; i < supportedProtocols.length; i++)
{
if (i > 0)
{
supportedBuffer.append(", ");
}
supportedBuffer.append('\'');
supportedBuffer.append(supportedProtocols[i]);
supportedBuffer.append('\'');
}
throw new IOException(
ERR_NO_ENABLED_SSL_PROTOCOLS_AVAILABLE_FOR_SOCKET.get(
enabledBuffer.toString(), supportedBuffer.toString(),
PROPERTY_ENABLED_SSL_PROTOCOLS,
SSLUtil.class.getName() + ".setEnabledSSLProtocols"));
}
else
{
return enabledList.toArray(StaticUtils.NO_STRINGS);
}
}
/**
* Retrieves the set of SSL cipher suites that will be enabled for use, if
* available, for SSL sockets created within the LDAP SDK.
*
* @return The set of SSL cipher suites that will be enabled for use, if
* available, for SSL sockets created within the LDAP SDK.
*/
@NotNull()
public static Set getEnabledSSLCipherSuites()
{
return ENABLED_SSL_CIPHER_SUITES.get();
}
/**
* Specifies the set of SSL cipher suites that will be enabled for SSL sockets
* created within the LDAP SDK. When creating an SSL socket, the
* {@code SSLSocket.getSupportedCipherSuites} method will be used to determine
* which cipher suites are supported for that socket, and then the
* {@code SSLSocket.setEnabledCipherSuites} method will be used to enable
* those suites which are listed as both supported by the socket and included
* in this set. If the provided set is {@code null} or empty, then the
* default set of enabled cipher suites will be used.
*
* @param enabledSSLCipherSuites The set of SSL cipher suites that will be
* enabled for use for SSL sockets created
* within the LDAP SDK. It may be
* {@code null} or empty to indicate that the
* JDK-default set of enabled cipher suites
* should be used for the socket.
*/
public static void setEnabledSSLCipherSuites(
@Nullable final Collection enabledSSLCipherSuites)
{
if (enabledSSLCipherSuites == null)
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.setEnabledSSLCipherSuites setting the " +
"enabled SSL cipher suites to an empty set");
}
ENABLED_SSL_CIPHER_SUITES.set(Collections.emptySet());
}
else
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.setEnabledSSLCipherSuites setting the " +
"enabled SSL cipher suites to " + enabledSSLCipherSuites);
}
ENABLED_SSL_CIPHER_SUITES.set(Collections.unmodifiableSet(
new LinkedHashSet<>(enabledSSLCipherSuites)));
}
}
/**
* Updates the provided socket to apply the appropriate set of enabled SSL
* cipher suites. This will only have any effect for sockets that are
* instances of {@code javax.net.ssl.SSLSocket}, but it is safe to call for
* any kind of {@code java.net.Socket}. This should be called before
* attempting any communication over the socket.
*
* @param socket The socket on which to apply the configured set of enabled
* SSL cipher suites.
*
* @throws LDAPException If {@link #getEnabledSSLCipherSuites} returns a
* non-empty set but none of the values in that set
* are supported by the socket.
*/
public static void applyEnabledSSLCipherSuites(@NotNull final Socket socket)
throws LDAPException
{
try
{
applyEnabledSSLCipherSuites(socket, ENABLED_SSL_CIPHER_SUITES.get());
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
throw new LDAPException(ResultCode.CONNECT_ERROR, ioe.getMessage(), ioe);
}
}
/**
* Updates the provided socket to apply the appropriate set of enabled SSL
* cipher suites. This will only have any effect for sockets that are
* instances of {@code javax.net.ssl.SSLSocket}, but it is safe to call for
* any kind of {@code java.net.Socket}. This should be called before
* attempting any communication over the socket.
*
* @param socket The socket on which to apply the configured set of
* enabled SSL cipher suites.
* @param cipherSuites The set of cipher suites that should be enabled for
* the socket, if available.
*
* @throws IOException If a problem is encountered while applying the
* desired set of enabled cipher suites to the given
* socket.
*/
static void applyEnabledSSLCipherSuites(@Nullable final Socket socket,
@NotNull final Set cipherSuites)
throws IOException
{
if ((socket == null) || (!(socket instanceof SSLSocket)) ||
cipherSuites.isEmpty())
{
return;
}
final SSLSocket sslSocket = (SSLSocket) socket;
final String[] cipherSuitesToEnable =
getSSLCipherSuitesToEnable(cipherSuites,
sslSocket.getSupportedCipherSuites());
try
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.applyEnabledSSLCipherSuites applying " +
"cinpherSuitesToEnable " + Arrays.toString(cipherSuitesToEnable) +
" to SSLSocket " + sslSocket);
}
sslSocket.setEnabledCipherSuites(cipherSuitesToEnable);
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
/**
* Updates the provided server socket to apply the appropriate set of enabled
* SSL cipher suites. This will only have any effect for server sockets that
* are instances of {@code javax.net.ssl.SSLServerSocket}, but it is safe to
* call for any kind of {@code java.net.ServerSocket}. This should be called
* before attempting any communication over the socket.
*
* @param serverSocket The server socket on which to apply the configured
* set of enabled SSL cipher suites.
* @param cipherSuites The set of cipher suites that should be enabled
* for the server socket, if available.
*
* @throws IOException If a problem is encountered while applying the
* desired set of enabled cipher suites to the given
* server socket.
*/
static void applyEnabledSSLCipherSuites(
@Nullable final ServerSocket serverSocket,
@NotNull final Set cipherSuites)
throws IOException
{
if ((serverSocket == null) ||
(! (serverSocket instanceof SSLServerSocket)) ||
cipherSuites.isEmpty())
{
return;
}
final SSLServerSocket sslServerSocket = (SSLServerSocket) serverSocket;
final String[] cipherSuitesToEnable =
getSSLCipherSuitesToEnable(cipherSuites,
sslServerSocket.getSupportedCipherSuites());
try
{
if (JVM_SSL_DEBUGGING_ENABLED)
{
System.err.println("SSLUtil.applyEnabledSSLCipherSuites applying " +
"cinpherSuitesToEnable " + Arrays.toString(cipherSuitesToEnable) +
" to SSLServerSocket " + sslServerSocket);
}
sslServerSocket.setEnabledCipherSuites(cipherSuitesToEnable);
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
/**
* Retrieves the names of the SSL cipher suites that should be enabled given
* the provided information.
*
* @param desiredCipherSuites The set of cipher suites that are desired to
* be enabled.
* @param supportedCipherSuites The set of all cipher suites that are
* supported.
*
* @return The names of the SSL cipher suites that should be enabled.
*
* @throws IOException If none of the desired values are included in the
* supported set.
*/
@NotNull()
private static String[] getSSLCipherSuitesToEnable(
@NotNull final Set desiredCipherSuites,
@NotNull final String[] supportedCipherSuites)
throws IOException
{
final Set upperCipherSuites = new LinkedHashSet<>(
StaticUtils.computeMapCapacity(desiredCipherSuites.size()));
for (final String s : desiredCipherSuites)
{
upperCipherSuites.add(StaticUtils.toUpperCase(s));
}
final ArrayList enabledList =
new ArrayList<>(supportedCipherSuites.length);
for (final String supportedCipherSuite : supportedCipherSuites)
{
if (upperCipherSuites.contains(StaticUtils.toUpperCase(
supportedCipherSuite)))
{
enabledList.add(supportedCipherSuite);
}
}
if (enabledList.isEmpty())
{
final StringBuilder enabledBuffer = new StringBuilder();
final Iterator enabledIterator = desiredCipherSuites.iterator();
while (enabledIterator.hasNext())
{
enabledBuffer.append('\'');
enabledBuffer.append(enabledIterator.next());
enabledBuffer.append('\'');
if (enabledIterator.hasNext())
{
enabledBuffer.append(", ");
}
}
final StringBuilder supportedBuffer = new StringBuilder();
for (int i=0; i < supportedCipherSuites.length; i++)
{
if (i > 0)
{
supportedBuffer.append(", ");
}
supportedBuffer.append('\'');
supportedBuffer.append(supportedCipherSuites[i]);
supportedBuffer.append('\'');
}
throw new IOException(
ERR_NO_ENABLED_SSL_CIPHER_SUITES_AVAILABLE_FOR_SOCKET.get(
enabledBuffer.toString(), supportedBuffer.toString(),
PROPERTY_ENABLED_SSL_CIPHER_SUITES,
SSLUtil.class.getName() + ".setEnabledSSLCipherSuites"));
}
else
{
return enabledList.toArray(StaticUtils.NO_STRINGS);
}
}
/**
* Configures SSL default settings for the LDAP SDK. This method is
* non-private for purposes of easier test coverage.
*/
static void configureSSLDefaults()
{
// Determine the set of TLS protocols that the JVM supports.
String tls13Protocol = null;
String tls12Protocol = null;
String tls11Protocol = null;
String tls1Protocol = null;
try
{
final SSLContext defaultContext = CryptoHelper.getDefaultSSLContext();
for (final String supportedProtocol :
defaultContext.getSupportedSSLParameters().getProtocols())
{
if (supportedProtocol.equalsIgnoreCase(SSL_PROTOCOL_TLS_1_3))
{
tls13Protocol = supportedProtocol;
}
else if (supportedProtocol.equalsIgnoreCase(SSL_PROTOCOL_TLS_1_2))
{
tls12Protocol = supportedProtocol;
}
else if (supportedProtocol.equalsIgnoreCase(SSL_PROTOCOL_TLS_1_1))
{
tls11Protocol = supportedProtocol;
}
else if (supportedProtocol.equalsIgnoreCase(SSL_PROTOCOL_TLS_1))
{
tls1Protocol = supportedProtocol;
}
}
}
catch (final Exception e)
{
Debug.debugException(e);
}
// Determine the set of TLS protocols that should be enabled.
final String enabledProtocolsPropertyValue =
StaticUtils.getSystemProperty(PROPERTY_ENABLED_SSL_PROTOCOLS);
final Set enabledProtocols = new LinkedHashSet<>();
if (enabledProtocolsPropertyValue != null)
{
final StringTokenizer tokenizer =
new StringTokenizer(enabledProtocolsPropertyValue, ", ", false);
while (tokenizer.hasMoreTokens())
{
final String enabledProtocol = tokenizer.nextToken().trim();
if (! enabledProtocol.isEmpty())
{
enabledProtocols.add(enabledProtocol);
}
}
}
else
{
if (tls13Protocol != null)
{
enabledProtocols.add(tls13Protocol);
if (tls12Protocol != null)
{
enabledProtocols.add(tls12Protocol);
}
}
else if (tls12Protocol != null)
{
enabledProtocols.add(tls12Protocol);
}
else if (tls11Protocol != null)
{
enabledProtocols.add(tls11Protocol);
}
else if (tls1Protocol != null)
{
enabledProtocols.add(tls1Protocol);
}
}
ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(enabledProtocols));
// Determine the default TLS protocol.
final String defaultProtocol;
final String defaultProtocolPropertyValue =
StaticUtils.getSystemProperty(PROPERTY_DEFAULT_SSL_PROTOCOL);
if (defaultProtocolPropertyValue != null)
{
defaultProtocol = defaultProtocolPropertyValue;
}
else
{
defaultProtocol = enabledProtocols.iterator().next();
}
DEFAULT_SSL_PROTOCOL.set(defaultProtocol);
// Determine the set of TLS cipher suites to enable by default.
TLSCipherSuiteSelector.recompute();
final String enabledSuitesPropertyValue =
StaticUtils.getSystemProperty(PROPERTY_ENABLED_SSL_CIPHER_SUITES);
final LinkedHashSet enabledCipherSuites = new LinkedHashSet<>();
if ((enabledSuitesPropertyValue != null) &&
(! enabledSuitesPropertyValue.isEmpty()))
{
final StringTokenizer tokenizer =
new StringTokenizer(enabledSuitesPropertyValue, ", ", false);
while (tokenizer.hasMoreTokens())
{
final String token = tokenizer.nextToken().trim();
if (! token.isEmpty())
{
enabledCipherSuites.add(token);
}
}
}
else
{
enabledCipherSuites.addAll(
TLSCipherSuiteSelector.getRecommendedCipherSuites());
}
ENABLED_SSL_CIPHER_SUITES.set(enabledCipherSuites);
}
/**
* Creates a string representation of the provided certificate.
*
* @param certificate The certificate for which to generate the string
* representation. It must not be {@code null}.
*
* @return A string representation of the provided certificate.
*/
@NotNull()
public static String certificateToString(
@NotNull final X509Certificate certificate)
{
final StringBuilder buffer = new StringBuilder();
certificateToString(certificate, buffer);
return buffer.toString();
}
/**
* Appends a string representation of the provided certificate to the given
* buffer.
*
* @param certificate The certificate for which to generate the string
* representation. It must not be {@code null}.
* @param buffer The buffer to which to append the string
* representation.
*/
public static void certificateToString(
@NotNull final X509Certificate certificate,
@NotNull final StringBuilder buffer)
{
buffer.append("Certificate(subject='");
buffer.append(
certificate.getSubjectX500Principal().getName(X500Principal.RFC2253));
buffer.append("', serialNumber=");
buffer.append(certificate.getSerialNumber());
buffer.append(", notBefore=");
StaticUtils.encodeGeneralizedTime(certificate.getNotBefore());
buffer.append(", notAfter=");
StaticUtils.encodeGeneralizedTime(certificate.getNotAfter());
buffer.append(", signatureAlgorithm='");
buffer.append(certificate.getSigAlgName());
buffer.append("', signatureBytes='");
StaticUtils.toHex(certificate.getSignature(), buffer);
buffer.append("', issuerSubject='");
buffer.append(
certificate.getIssuerX500Principal().getName(X500Principal.RFC2253));
buffer.append("')");
}
}