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 2019-2021 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright 2019-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) 2019-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.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPRuntimeException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.Version;
import com.unboundid.util.CommandLineTool;
import com.unboundid.util.CryptoHelper;
import com.unboundid.util.Debug;
import com.unboundid.util.NotMutable;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.ObjectPair;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import static com.unboundid.util.ssl.SSLMessages.*;
/**
* This class provides a utility for selecting the cipher suites that should be
* supported for TLS communication. The logic used to select the recommended
* TLS cipher suites is as follows:
*
*
* Only cipher suites that use the TLS protocol will be recommended. Legacy
* SSL suites will not be recommended, nor will any suites that use an
* unrecognized protocol.
*
*
*
* Any cipher suite that uses a NULL key exchange, authentication, bulk
* encryption, or digest algorithm will not be recommended.
*
*
*
* Any cipher suite that uses anonymous authentication will not be
* recommended.
*
*
*
* Any cipher suite that uses weakened export-grade encryption will not be
* recommended.
*
*
*
* By default, only cipher suites that use the ECDHE or DHE key exchange
* algorithms will be recommended, as they allow for forward secrecy.
* Suites that use RSA key exchange algorithms (which don't support forward
* secrecy) will only be recommended if the JVM doesn't support either
* TLSv1.3 or TLSv1.2, or if overridden programmatically or by system
* property. Other key agreement algorithms (like ECDH, DH, and KRB5) will
* not be recommended. Similarly, cipher suites that use a pre-shared key
* or password will not be recommended.
*
*
*
* Only cipher suites that use AES or ChaCha20 bulk encryption ciphers will
* be recommended. Other bulk cipher algorithms (like RC4, DES, 3DES, IDEA,
* Camellia, and ARIA) will not be recommended.
*
*
*
* By default, only cipher suites that use SHA-2 digests will be
* recommended. SHA-1 suites will only be recommended if the JVM doesn't
* support either TLSv1.3 or TLSv1.2, or if overridden programmatically or
* by system property. All other digest algorithms (like MD5) will not be
* recommended.
*
*
*
* Also note that this class can be used as a command-line tool for debugging
* purposes.
*/
@NotMutable()
@ThreadSafety(level= ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class TLSCipherSuiteSelector
extends CommandLineTool
{
/**
* An instance of this TLS cipher suite selector that will be used by the
* static methods.
*/
@NotNull private static final AtomicReference
STATIC_INSTANCE = new AtomicReference<>();
/**
* The name of a system property
* (com.unboundid.util.ssl.TLSCipherSuiteSelector.allowRSAKeyExchange) that
* can be used to indicate whether to recommend cipher suites that use the RSA
* key exchange algorithm. RSA key exchange does not support forward secrecy,
* so it will not be recommended by default unless the JVM doesn't support
* either TLSv1.3 or TLSv1.2. This can be overridden via the
* {@link #setAllowRSAKeyExchange(boolean)} method.
*/
@NotNull public static final String PROPERTY_ALLOW_RSA_KEY_EXCHANGE =
TLSCipherSuiteSelector.class.getName() + ".allowRSAKeyExchange";
/**
* The name of a system property
* (com.unboundid.util.ssl.TLSCipherSuiteSelector.allowSHA1) that can be used
* to indicate whether to recommend cipher suites that use the SHA-1 digest
* algorithm. The SHA-1 digest is now considered weak, so it will not be
* recommended by default unless the JVM doesn't support either TLSv1.3 or
* TLSv1.2. This can be overridden via the {@link #setAllowSHA1(boolean)}
* method.
*/
@NotNull public static final String PROPERTY_ALLOW_SHA_1 =
TLSCipherSuiteSelector.class.getName() + ".allowSHA1";
/**
* A flag that indicates whether to allow the RSA key exchange algorithm.
*/
@NotNull private static final AtomicBoolean ALLOW_RSA_KEY_EXCHANGE =
new AtomicBoolean(false);
/**
* A flag that indicates whether to allow cipher suites that use the SHA-1
* digest algorithm.
*/
@NotNull private static final AtomicBoolean ALLOW_SHA_1 =
new AtomicBoolean(false);
static
{
final boolean allowRSA;
final String allowRSAPropertyValue =
StaticUtils.getSystemProperty(PROPERTY_ALLOW_RSA_KEY_EXCHANGE);
if (allowRSAPropertyValue != null)
{
allowRSA = allowRSAPropertyValue.equalsIgnoreCase("true");
}
else
{
allowRSA = false;
}
final boolean allowSHA1;
final String allowSHA1PropertyValue =
StaticUtils.getSystemProperty(PROPERTY_ALLOW_SHA_1);
if (allowSHA1PropertyValue != null)
{
allowSHA1 = allowSHA1PropertyValue.equalsIgnoreCase("true");
}
else
{
allowSHA1 = false;
}
ALLOW_RSA_KEY_EXCHANGE.set(allowRSA);
ALLOW_SHA_1.set(allowSHA1);
}
// Indicates whether SSL/TLS debugging is expected to be enabled in the JVM,
// based on the value of the javax.net.debug system property.
private final boolean jvmSSLDebuggingEnabled;
// Retrieves a map of the supported cipher suites that are not recommended
// for use, mapped to a list of the reasons that the cipher suites are not
// recommended.
@NotNull private final SortedMap>
nonRecommendedCipherSuites;
// The set of TLS cipher suites enabled in the JVM by default, sorted in
// order of most preferred to least preferred.
@NotNull private final SortedSet defaultCipherSuites;
// The recommended set of TLS cipher suites selected by this class, sorted in
// order of most preferred to least preferred.
@NotNull private final SortedSet recommendedCipherSuites;
// The full set of TLS cipher suites supported in the JVM, sorted in order of
// most preferred to least preferred.
@NotNull private final SortedSet supportedCipherSuites;
// The recommended set of TLS cipher suites as an array rather than a set.
@NotNull private final String[] recommendedCipherSuiteArray;
/**
* Invokes this command-line program with the provided set of arguments.
*
* @param args The command-line arguments provided to this program.
*/
public static void main(@NotNull final String... args)
{
final ResultCode resultCode = main(System.out, System.err, args);
if (resultCode != ResultCode.SUCCESS)
{
System.exit(resultCode.intValue());
}
}
/**
* Invokes this command-line program with the provided set of arguments.
*
* @param out The output stream to use for standard output. It may be
* {@code null} if standard output should be suppressed.
* @param err The output stream to use for standard error. It may be
* {@code null} if standard error should be suppressed.
* @param args The command-line arguments provided to this program.
*
* @return A result code that indicates whether the processing was
* successful.
*/
@NotNull()
public static ResultCode main(@Nullable final OutputStream out,
@Nullable final OutputStream err,
@NotNull final String... args)
{
final TLSCipherSuiteSelector tool = new TLSCipherSuiteSelector(out, err);
return tool.runTool(args);
}
/**
* Creates a new instance of this TLS cipher suite selector that will suppress
* all output.
*
* @param useJVMDefaults Indicates whether to use the JVM-default settings.
* This should only be {@code true} for the initial
* instance created before the static initializer has
* run.
*/
private TLSCipherSuiteSelector(final boolean useJVMDefaults)
{
this(null, null, useJVMDefaults);
}
/**
* Creates a new instance of this TLS cipher suite selector that will use the
* provided output streams. Note that this constructor should only be used
* when invoking it as a command-line tool.
*
* @param out The output stream to use for standard output. It may be
* {@code null} if standard output should be suppressed.
* @param err The output stream to use for standard error. It may be
* {@code null} if standard error should be suppressed.
*/
public TLSCipherSuiteSelector(@Nullable final OutputStream out,
@Nullable final OutputStream err)
{
this(out, err, false);
}
/**
* Creates a new instance of this TLS cipher suite selector that will use the
* provided output streams. Note that this constructor should only be used
* when invoking it as a command-line tool.
*
* @param out The output stream to use for standard output. It
* may be {@code null} if standard output should be
* suppressed.
* @param err The output stream to use for standard error. It
* may be {@code null} if standard error should be
* suppressed.
* @param useJVMDefaults Indicates whether to use the JVM-default settings.
* This should only be {@code true} for the initial
* instance created before the static initializer has
* run.
*/
public TLSCipherSuiteSelector(@Nullable final OutputStream out,
@Nullable final OutputStream err,
final boolean useJVMDefaults)
{
super(out, err);
try
{
final SSLContext sslContext;
if (useJVMDefaults)
{
sslContext = SSLContext.getDefault();
}
else
{
sslContext = CryptoHelper.getDefaultSSLContext();
}
final SSLParameters supportedParameters =
sslContext.getSupportedSSLParameters();
final TreeSet supportedSet =
new TreeSet<>(TLSCipherSuiteComparator.getInstance());
supportedSet.addAll(Arrays.asList(supportedParameters.getCipherSuites()));
supportedCipherSuites = Collections.unmodifiableSortedSet(supportedSet);
final SSLParameters defaultParameters =
sslContext.getDefaultSSLParameters();
final TreeSet defaultSet =
new TreeSet<>(TLSCipherSuiteComparator.getInstance());
defaultSet.addAll(Arrays.asList(defaultParameters.getCipherSuites()));
defaultCipherSuites = Collections.unmodifiableSortedSet(defaultSet);
if (useJVMDefaults)
{
recommendedCipherSuites = defaultCipherSuites;
nonRecommendedCipherSuites = Collections.unmodifiableSortedMap(
new TreeMap>());
}
else
{
final ObjectPair,SortedMap>>
selectedPair = selectCipherSuites(
supportedParameters.getCipherSuites());
if (selectedPair.getFirst().isEmpty())
{
// We couldn't identify any recommended suites. Just fall back on the
// JVM-default suites.
recommendedCipherSuites = defaultCipherSuites;
nonRecommendedCipherSuites = Collections.unmodifiableSortedMap(
new TreeMap>());
}
else
{
recommendedCipherSuites =
Collections.unmodifiableSortedSet(selectedPair.getFirst());
nonRecommendedCipherSuites =
Collections.unmodifiableSortedMap(selectedPair.getSecond());
}
}
recommendedCipherSuiteArray =
recommendedCipherSuites.toArray(StaticUtils.NO_STRINGS);
}
catch (final Exception e)
{
Debug.debugException(e);
// This should never happen.
throw new LDAPRuntimeException(new LDAPException(ResultCode.LOCAL_ERROR,
ERR_TLS_CIPHER_SUITE_SELECTOR_INIT_ERROR.get(
StaticUtils.getExceptionMessage(e)),
e));
}
// See if the JVM's TLS debugging support is enabled. If so, then invoke the
// tool and send its output to standard error.
final String javaxNetDebugPropertyValue =
StaticUtils.getSystemProperty("javax.net.debug");
if (javaxNetDebugPropertyValue == null)
{
jvmSSLDebuggingEnabled = false;
}
else
{
final String lowerValue =
StaticUtils.toLowerCase(javaxNetDebugPropertyValue);
jvmSSLDebuggingEnabled =
(lowerValue.contains("all") || lowerValue.contains("ssl"));
if (jvmSSLDebuggingEnabled)
{
System.err.println();
System.err.println(getClass().getName() + " Results:");
generateOutput(System.err);
System.err.println();
}
}
}
/**
* Retrieves the set of all TLS cipher suites supported by the JVM. The set
* will be sorted in order of most preferred to least preferred, as determined
* by the {@link TLSCipherSuiteComparator}.
*
* @return The set of all TLS cipher suites supported by the JVM.
*/
@NotNull()
public static SortedSet getSupportedCipherSuites()
{
return getStaticInstance().supportedCipherSuites;
}
/**
* Retrieves the set of TLS cipher suites enabled by default in the JVM. The
* set will be sorted in order of most preferred to least preferred, as
* determined by the {@link TLSCipherSuiteComparator}.
*
* @return The set of TLS cipher suites enabled by default in the JVM.
*/
@NotNull()
public static SortedSet getDefaultCipherSuites()
{
return getStaticInstance().defaultCipherSuites;
}
/**
* Retrieves the recommended set of TLS cipher suites as selected by this
* class. The set will be sorted in order of most preferred to least
* preferred, as determined by the {@link TLSCipherSuiteComparator}.
*
* @return The recommended set of TLS cipher suites as selected by this
* class.
*/
@NotNull()
public static SortedSet getRecommendedCipherSuites()
{
return getStaticInstance().recommendedCipherSuites;
}
/**
* Retrieves an array containing the recommended set of TLS cipher suites as
* selected by this class. The array will be sorted in order of most
* preferred to least preferred, as determined by the
* {@link TLSCipherSuiteComparator}.
*
* @return An array containing the recommended set of TLS cipher suites as
* selected by this class.
*/
@NotNull()
public static String[] getRecommendedCipherSuiteArray()
{
return getStaticInstance().recommendedCipherSuiteArray.clone();
}
/**
* Retrieves a map containing the TLS cipher suites that are supported by the
* JVM but are not recommended for use. The keys of the map will be the names
* of the non-recommended cipher suites, sorted in order of most preferred to
* least preferred, as determined by the {@link TLSCipherSuiteComparator}.
* Each TLS cipher suite name will be mapped to a list of the reasons it is
* not recommended for use.
*
* @return A map containing the TLS cipher suites that are supported by the
* JVM but are not recommended for use
*/
@NotNull()
public static SortedMap> getNonRecommendedCipherSuites()
{
return getStaticInstance().nonRecommendedCipherSuites;
}
/**
* Organizes the provided set of cipher suites into recommended and
* non-recommended sets.
*
* @param cipherSuiteArray An array of the cipher suites to be organized.
*
* @return An object pair in which the first element is the sorted set of
* recommended cipher suites, and the second element is the sorted
* map of non-recommended cipher suites and the reasons they are not
* recommended for use.
*/
@NotNull()
static ObjectPair,SortedMap>>
selectCipherSuites(@NotNull final String[] cipherSuiteArray)
{
return selectCipherSuites(cipherSuiteArray, false);
}
/**
* Organizes the provided set of cipher suites into recommended and
* non-recommended sets.
*
* @param cipherSuiteArray An array of the cipher suites to be organized.
* @param includeSSLSuites Indicates whether to allow suites that start
* with "SSL_". If this is {@code false} (which
* should be the case for all calls to this method
* that don't come directly from this method), then
* only suites that start with "TLS_" will be
* included. If this is {@code true}, then suites
* that start with "SSL_" may be included. This is
* necessary because some JVMs (for example, the IBM
* JVM) only report suites that start with "SSL_"
* and none with "TLS_". In that case, we'll rely
* only on other logic to determine which suites to
* recommend and which to exclude.
*
* @return An object pair in which the first element is the sorted set of
* recommended cipher suites, and the second element is the sorted
* map of non-recommended cipher suites and the reasons they are not
* recommended for use.
*/
@NotNull()
private static ObjectPair,SortedMap>>
selectCipherSuites(@NotNull final String[] cipherSuiteArray,
final boolean includeSSLSuites)
{
final SortedSet recommendedSet =
new TreeSet<>(TLSCipherSuiteComparator.getInstance());
final SortedMap> nonRecommendedMap =
new TreeMap<>(TLSCipherSuiteComparator.getInstance());
boolean anyTLSSuitesFound = false;
for (final String cipherSuiteName : cipherSuiteArray)
{
String name =
StaticUtils.toUpperCase(cipherSuiteName).replace('-', '_');
// Signalling cipher suite values (which indicate capabilities of the
// implementation and aren't really cipher suites on their own) will
// always be accepted.
if (name.endsWith("_SCSV"))
{
recommendedSet.add(cipherSuiteName);
continue;
}
// Only cipher suites using the TLS protocol will be accepted.
final List nonRecommendedReasons = new ArrayList<>(5);
if (name.startsWith("SSL_") && (! includeSSLSuites))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_LEGACY_SSL_PROTOCOL.get());
}
else if (name.startsWith("TLS_") || name.startsWith("SSL_"))
{
if (name.startsWith("TLS_"))
{
anyTLSSuitesFound = true;
}
else
{
name = "TLS_" + name.substring(4);
}
// Only TLS cipher suites using a recommended key exchange algorithm
// will be accepted.
if (name.startsWith("TLS_AES_") ||
name.startsWith("TLS_CHACHA20_") ||
name.startsWith("TLS_ECDHE_") ||
name.startsWith("TLS_DHE_"))
{
// These are recommended key exchange algorithms.
}
else if (name.startsWith("TLS_RSA_"))
{
if (ALLOW_RSA_KEY_EXCHANGE.get())
{
// This will be considered a recommended key exchange algorithm.
}
else
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_KE_ALG.get(
"RSA"));
}
}
else if (name.startsWith("TLS_ECDH_"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_KE_ALG.get(
"ECDH"));
}
else if (name.startsWith("TLS_DH_"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_KE_ALG.get(
"DH"));
}
else if (name.startsWith("TLS_KRB5_"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_KE_ALG.get(
"KRB5"));
}
else
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_UNKNOWN_KE_ALG.
get());
}
}
else
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_UNRECOGNIZED_PROTOCOL.get());
}
// Cipher suites that rely on pre-shared keys will not be accepted.
if (name.contains("_PSK"))
{
nonRecommendedReasons.add(ERR_TLS_CIPHER_SUITE_SELECTOR_PSK.get());
}
// Cipher suites that use a null component will not be accepted.
if (name.contains("_NULL"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NULL_COMPONENT.get());
}
// Cipher suites that use anonymous authentication will not be accepted.
if (name.contains("_ANON"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_ANON_AUTH.get());
}
// Cipher suites that use export-grade encryption will not be accepted.
if (name.contains("_EXPORT"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_EXPORT_ENCRYPTION.get());
}
// Only cipher suites that use AES or ChaCha20 will be accepted.
if (name.contains("_AES") || name.contains("_CHACHA20"))
{
// These are recommended bulk cipher algorithms.
}
else if (name.contains("_RC4"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_BE_ALG.get(
"RC4"));
}
else if (name.contains("_3DES"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_BE_ALG.get(
"3DES"));
}
else if (name.contains("_DES"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_BE_ALG.get(
"DES"));
}
else if (name.contains("_IDEA"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_BE_ALG.get(
"IDEA"));
}
else if (name.contains("_CAMELLIA"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_BE_ALG.get(
"Camellia"));
}
else if (name.contains("_ARIA"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_BE_ALG.get(
"ARIA"));
}
else
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_UNKNOWN_BE_ALG.
get());
}
// Only cipher suites that use a SHA-1 or SHA-2 digest algorithm will be
// accepted.
if (name.endsWith("_SHA512") ||
name.endsWith("_SHA384") ||
name.endsWith("_SHA256"))
{
// These are recommended digest algorithms.
}
else if (name.endsWith("_SHA"))
{
if (ALLOW_SHA_1.get())
{
// This will be considered a recommended digest algorithm.
}
else
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_DIGEST_ALG.
get("SHA-1"));
}
}
else if (name.endsWith("_MD5"))
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_KNOWN_DIGEST_ALG.get(
"MD5"));
}
else
{
nonRecommendedReasons.add(
ERR_TLS_CIPHER_SUITE_SELECTOR_NON_RECOMMENDED_UNKNOWN_DIGEST_ALG.
get());
}
// Determine whether to recommend the cipher suite based on whether there
// are any non-recommended reasons.
if (nonRecommendedReasons.isEmpty())
{
recommendedSet.add(cipherSuiteName);
}
else
{
nonRecommendedMap.put(cipherSuiteName,
Collections.unmodifiableList(nonRecommendedReasons));
}
}
if (recommendedSet.isEmpty() && (! anyTLSSuitesFound) &&
(! includeSSLSuites))
{
// We didn't find any suite names starting with "TLS_". Assume that the
// JVM only reports suites that start with "SSL_" and try again, allowing
// those suites.
return selectCipherSuites(cipherSuiteArray, true);
}
return new ObjectPair<>(recommendedSet, nonRecommendedMap);
}
/**
* {@inheritDoc}
*/
@Override()
@NotNull()
public String getToolName()
{
return "tls-cipher-suite-selector";
}
/**
* {@inheritDoc}
*/
@Override()
@NotNull()
public String getToolDescription()
{
return INFO_TLS_CIPHER_SUITE_SELECTOR_TOOL_DESC.get();
}
/**
* {@inheritDoc}
*/
@Override()
@NotNull()
public String getToolVersion()
{
return Version.NUMERIC_VERSION_STRING;
}
/**
* {@inheritDoc}
*/
@Override()
public void addToolArguments(@NotNull final ArgumentParser parser)
throws ArgumentException
{
// This tool does not require any arguments.
}
/**
* {@inheritDoc}
*/
@Override()
@NotNull()
public ResultCode doToolProcessing()
{
generateOutput(getOut());
return ResultCode.SUCCESS;
}
/**
* Writes the output to the provided print stream.
*
* @param s The print stream to which the output should be written.
*/
private void generateOutput(@NotNull final PrintStream s)
{
try
{
final SSLContext sslContext = CryptoHelper.getDefaultSSLContext();
s.println("Supported TLS Protocols:");
for (final String protocol :
sslContext.getSupportedSSLParameters().getProtocols())
{
s.println("* " + protocol);
}
s.println();
s.println("Enabled TLS Protocols:");
for (final String protocol : SSLUtil.getEnabledSSLProtocols())
{
s.println("* " + protocol);
}
s.println();
}
catch (final Exception e)
{
Debug.debugException(e);
}
s.println("Supported TLS Cipher Suites:");
for (final String cipherSuite : supportedCipherSuites)
{
s.println("* " + cipherSuite);
}
s.println();
s.println("JVM-Default TLS Cipher Suites:");
for (final String cipherSuite : defaultCipherSuites)
{
s.println("* " + cipherSuite);
}
s.println();
s.println("Non-Recommended TLS Cipher Suites:");
for (final Map.Entry> e :
nonRecommendedCipherSuites.entrySet())
{
s.println("* " + e.getKey());
for (final String reason : e.getValue())
{
s.println(" - " + reason);
}
}
s.println();
s.println("Recommended TLS Cipher Suites:");
for (final String cipherSuite : recommendedCipherSuites)
{
s.println("* " + cipherSuite);
}
}
/**
* Filters the provided collection of potential cipher suite names to retrieve
* a set of the suites that are supported by the JVM.
*
* @param potentialSuiteNames The collection of cipher suite names to be
* filtered.
*
* @return The set of provided cipher suites that are supported by the JVM,
* or an empty set if none of the potential provided suite names are
* supported by the JVM.
*/
@NotNull()
public static Set selectSupportedCipherSuites(
@Nullable final Collection potentialSuiteNames)
{
if (potentialSuiteNames == null)
{
return Collections.emptySet();
}
final TLSCipherSuiteSelector instance = getStaticInstance();
final int capacity = StaticUtils.computeMapCapacity(
instance.supportedCipherSuites.size());
final Map supportedMap = new HashMap<>(capacity);
for (final String supportedSuite : instance.supportedCipherSuites)
{
supportedMap.put(
StaticUtils.toUpperCase(supportedSuite).replace('-', '_'),
supportedSuite);
}
final Set selectedSet = new LinkedHashSet<>(capacity);
for (final String potentialSuite : potentialSuiteNames)
{
final String supportedName = supportedMap.get(
StaticUtils.toUpperCase(potentialSuite).replace('-', '_'));
if (supportedName != null)
{
selectedSet.add(supportedName);
}
}
return Collections.unmodifiableSet(selectedSet);
}
/**
* Indicates whether cipher suites that use the RSA key exchange algorithm
* should be recommended by default.
*
* @return {@code true} if cipher suites that use the RSA key exchange
* algorithm should be recommended by default, or {@code false} if
* not.
*/
public static boolean allowRSAKeyExchange()
{
return ALLOW_RSA_KEY_EXCHANGE.get();
}
/**
* Specifies whether cipher suites that use the RSA key exchange algorithm
* should be recommended by default.
*
* @param allowRSAKeyExchange Indicates whether cipher suites that use the
* RSA key exchange algorithm should be
* recommended by default.
*/
public static void setAllowRSAKeyExchange(final boolean allowRSAKeyExchange)
{
ALLOW_RSA_KEY_EXCHANGE.set(allowRSAKeyExchange);
recompute();
}
/**
* Indicates whether cipher suites that use the SHA-1 digest algorithm should
* be recommended by default.
*
* @return {@code true} if cipher suites that use the SHA-1 digest algorithm
* should be recommended by default, or {@code false} if not.
*/
public static boolean allowSHA1()
{
return ALLOW_SHA_1.get();
}
/**
* Specifies whether cipher suites that use the SHA-1 digest algorithm should
* be recommended by default.
*
* @param allowSHA1 Indicates whether cipher suites that use the SHA-1
* digest algorithm should be recommended by default.
*/
public static void setAllowSHA1(final boolean allowSHA1)
{
ALLOW_SHA_1.set(allowSHA1);
recompute();
}
/**
* Retrieves the static instance of this TLS cipher suite selector.
*
* @return The static instance of this TLS cipher suite selector.
*/
@NotNull()
private static TLSCipherSuiteSelector getStaticInstance()
{
TLSCipherSuiteSelector instance = STATIC_INSTANCE.get();
if (instance == null)
{
synchronized (TLSCipherSuiteSelector.class)
{
STATIC_INSTANCE.compareAndSet(null,
new TLSCipherSuiteSelector(null, null, false));
instance = STATIC_INSTANCE.get();
}
}
return instance;
}
/**
* Re-computes the default instance of this cipher suite selector. This may
* be necessary after certain actions that alter the supported set of TLS
* cipher suites (for example, installing or removing cryptographic
* providers).
*/
public static void recompute()
{
synchronized (TLSCipherSuiteSelector.class)
{
STATIC_INSTANCE.set(null);
}
}
/**
* Indicates whether SSL/TLS debugging is expected to be enabled in the JVM,
* based on the value of the javax.net.debug system property.
*
* @return {@code true} if SSL/TLS debugging is expected to be enabled in
* the JVM, ro {@code false} if not.
*/
static boolean jvmSSLDebuggingEnabled()
{
return getStaticInstance().jvmSSLDebuggingEnabled;
}
}