All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.util.ssl.TLSCipherSuiteComparator Maven / Gradle / Ivy

/*
 * Copyright 2019-2024 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2019-2024 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-2024 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.Serializable;
import java.util.Comparator;

import com.unboundid.util.NotMutable;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;



/**
 * This class provides a comparator that may be used to order TLS cipher suites
 * from most-preferred to least-preferred.  Note that its behavior is undefined
 * for strings that are not valid TLS cipher suite names.
 * 

* This comparator uses the following logic: *
    *
  • * Cipher suite names that end with "_SCSV" will be ordered after those that * do not. These are signalling cipher suite values that indicate special * capabilities and aren't really cipher suites. *
  • * *
  • * Cipher suite names that contain "_NULL" will be ordered after those that * do not. *
  • * *
  • * Cipher suite names that contain "_ANON" will be ordered after those that * do not. *
  • * *
  • * Cipher suite names that contain "_EXPORT" will be ordered after those * that do not. *
  • * *
  • * Cipher suites will be ordered according to their prefix, as follows: *
      *
    • * Suite names starting with TLS_AES_ will come first, as they are * TLSv1.3 (or later) suites that use AES for bulk encryption. *
    • *
    • * Suite names starting with TLS_CHACHA20_ will come next, as they are * TLSv1.3 (or later) suites that use the ChaCha20 stream cipher, which * is less widely supported than AES. *
    • *
    • * Suite names starting with TLS_ECDHE_ will come next, as they use * elliptic curve Diffie-Hellman key exchange with ephemeral keys, * providing support for forward secrecy. *
    • *
    • * Suite names starting with TLS_DHE_ will come next, as they use * Diffie-Hellman key exchange with ephemeral keys, also providing * support for forward secrecy, but less efficient than the elliptic * curve variant. *
    • *
    • * Suite names starting with TLS_RSA_ will come next, as they use RSA * key exchange, which does not support forward secrecy, but is still * considered secure. *
    • *
    • * Suite names starting with TLS_ but that do not match any of the * above values will come next, as they are less desirable than any of * the more specific TLS-based suites. *
    • *
    • * Suite names starting with SSL_ will come next, as they are legacy * SSL-based protocols that should be considered weaker than TLS-based * protocol.s *
    • *
    • * Suite names that do not start with TLS_ or SSL_ will come last. No * such suites are expected. *
    • *
    *
  • * *
  • * Cipher suite names that contain _AES will be ordered before those that * contain _CHACHA20, as AES is a more widely supported bulk cipher than * ChaCha20. Suite names that do not contain either _AES or _CHACHA20 will * be ordered after those that contain _CHACHA20, as they likely use a bulk * cipher that is weaker or not as widely supported. *
  • * *
  • * Cipher suites that use AES with a GCM mode will be ordered before those * that use AES with a non-GCM mode. GCM (Galois/Counter Mode) uses * authenticated encryption, which provides better security guarantees than * non-authenticated encryption. *
  • * *
  • * Cipher suites that use AES with a 256-bit key will be ordered before * those that use AES with a 128-bit key. *
  • * *
  • * Cipher suites will be ordered according to their digest algorithm, as * follows: *
      *
    • * Suites that use a 512-bit SHA-2 digest will come first. At present, * no such suites are defined, but they may be added in the future. *
    • *
    • * Suites that use a 384-bit SHA-2 digest will come next. *
    • *
    • * Suites that use a 256-bit SHA-2 digest will come next. *
    • *
    • * Suites that use a SHA-1 digest will come next. *
    • *
    • * Suites that use any other digest algorithm will come last, as they * likely use an algorithm that is weaker or not as widely supported. *
    • *
    *
  • * *
  • * If none of the above criteria can be used to differentiate the cipher * suites, then it will fall back to simple lexicographic ordering. *
  • *
*/ @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public final class TLSCipherSuiteComparator implements Comparator, Serializable { /** * The singleton instance of this comparator. */ @NotNull private static final TLSCipherSuiteComparator INSTANCE = new TLSCipherSuiteComparator(); /** * The serial version UID for this serializable class. */ private static final long serialVersionUID = 7719643162516590858L; /** * Creates a new instance of this comparator. */ private TLSCipherSuiteComparator() { // No implementation is required. } /** * Retrieves the singleton instance of this TLS cipher suite comparator. * * @return The singleton instance of this TLS cipher suite comparator. */ @NotNull() public static TLSCipherSuiteComparator getInstance() { return INSTANCE; } /** * Compares the provided strings to determine the logical order of the TLS * cipher suites that they represent. * * @param s1 The first string to compare. It must not be {@code null}, and * it should represent a valid cipher suite name. * @param s2 The second string to compare. It must not be {@code null}, and * it should represent a valid cipher suite name. * * @return A negative integer value if the first cipher suite name should be * ordered before the second, a positive integer value if the first * cipher suite name should be ordered after the second, or zero if * the names are considered logically equivalent. */ @Override() public int compare(@NotNull final String s1, @NotNull final String s2) { final String cipherSuiteName1 = StaticUtils.toUpperCase(s1).replace('-', '_'); final String cipherSuiteName2 = StaticUtils.toUpperCase(s2).replace('-', '_'); final int scsvOrder = getSCSVOrder(cipherSuiteName1, cipherSuiteName2); if (scsvOrder != 0) { return scsvOrder; } final int explicitlyWeakOrder = getExplicitlyWeakOrder(cipherSuiteName1, cipherSuiteName2); if (explicitlyWeakOrder != 0) { return explicitlyWeakOrder; } final int prefixOrder = getPrefixOrder(cipherSuiteName1, cipherSuiteName2); if (prefixOrder != 0) { return prefixOrder; } final int blockCipherOrder = getBlockCipherOrder(cipherSuiteName1, cipherSuiteName2); if (blockCipherOrder != 0) { return blockCipherOrder; } final int digestOrder = getDigestOrder(cipherSuiteName1, cipherSuiteName2); if (digestOrder != 0) { return digestOrder; } return s1.compareTo(s2); } /** * Attempts to order the provided cipher suite names using signalling cipher * suite values. * * @param cipherSuiteName1 The first cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * @param cipherSuiteName2 The second cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * * @return A negative integer value if the first cipher suite name should be * ordered before the second, a positive integer value if the first * cipher suite should be ordered after the second, or zero if they * are considered logically equivalent for the purposes of this * method. */ private static int getSCSVOrder(@NotNull final String cipherSuiteName1, @NotNull final String cipherSuiteName2) { if (cipherSuiteName1.endsWith("_SCSV")) { if (cipherSuiteName2.endsWith("_SCSV")) { return 0; } else { return 1; } } else if (cipherSuiteName2.endsWith("_SCSV")) { return -1; } else { return 0; } } /** * Attempts to order the provided cipher suite names by whether the use a * null component. anonymous authentication, or export-grade encryption. * * @param cipherSuiteName1 The first cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * @param cipherSuiteName2 The second cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * * @return A negative integer value if the first cipher suite name should be * ordered before the second, a positive integer value if the first * cipher suite should be ordered after the second, or zero if they * are considered logically equivalent for the purposes of this * method. */ private static int getExplicitlyWeakOrder( @NotNull final String cipherSuiteName1, @NotNull final String cipherSuiteName2) { if (cipherSuiteName1.contains("_NULL")) { if (! cipherSuiteName2.contains("_NULL")) { return 1; } } else if (cipherSuiteName2.contains("_NULL")) { return -1; } if (cipherSuiteName1.contains("_ANON")) { if (! cipherSuiteName2.contains("_ANON")) { return 1; } } else if (cipherSuiteName2.contains("_ANON")) { return -1; } if (cipherSuiteName1.contains("_EXPORT")) { if (! cipherSuiteName2.contains("_EXPORT")) { return 1; } } else if (cipherSuiteName2.contains("_EXPORT")) { return -1; } return 0; } /** * Attempts to order the provided cipher suite names using the protocol and * key agreement algorithm. * * @param cipherSuiteName1 The first cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * @param cipherSuiteName2 The second cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * * @return A negative integer value if the first cipher suite name should be * ordered before the second, a positive integer value if the first * cipher suite should be ordered after the second, or zero if they * are considered logically equivalent for the purposes of this * method. */ private static int getPrefixOrder(@NotNull final String cipherSuiteName1, @NotNull final String cipherSuiteName2) { final int prefixValue1 = getPrefixValue(cipherSuiteName1); final int prefixValue2 = getPrefixValue(cipherSuiteName2); return prefixValue1 - prefixValue2; } /** * Retrieves an integer value for the provided cipher suite name based on the * protocol and key agreement algorithm. Lower values are preferred over * higher values. * * @param cipherSuiteName The cipher suite name for which to obtain the * prefix value. It must not be {@code null}, and it * should represent a valid cipher suite name. * * @return An integer value for the provided cipher suite name based on the * protocol and key agreement algorithm. */ private static int getPrefixValue(@NotNull final String cipherSuiteName) { if (cipherSuiteName.startsWith("TLS_AES_")) { return 1; } else if (cipherSuiteName.startsWith("TLS_CHACHA20_")) { return 2; } else if (cipherSuiteName.startsWith("TLS_ECDHE_")) { return 3; } else if (cipherSuiteName.startsWith("TLS_DHE_")) { return 4; } else if (cipherSuiteName.startsWith("TLS_RSA_")) { return 5; } else if (cipherSuiteName.startsWith("TLS_ECDH_")) { return 6; } else if (cipherSuiteName.startsWith("TLS_DH_")) { return 7; } else if (cipherSuiteName.startsWith("TLS_")) { return 8; } if (cipherSuiteName.startsWith("SSL_AES_")) { return 9; } else if (cipherSuiteName.startsWith("SSL_CHACHA20_")) { return 10; } else if (cipherSuiteName.startsWith("SSL_ECDHE_")) { return 11; } else if (cipherSuiteName.startsWith("SSL_DHE_")) { return 12; } else if (cipherSuiteName.startsWith("SSL_RSA_")) { return 13; } else if (cipherSuiteName.startsWith("SSL_ECDH_")) { return 14; } else if (cipherSuiteName.startsWith("SSL_DH_")) { return 15; } else if (cipherSuiteName.startsWith("SSL_")) { return 16; } else { return 17; } } /** * Attempts to order the provided cipher suite names using the block cipher * settings. * * @param cipherSuiteName1 The first cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * @param cipherSuiteName2 The second cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * * @return A negative integer value if the first cipher suite name should be * ordered before the second, a positive integer value if the first * cipher suite should be ordered after the second, or zero if they * are considered logically equivalent for the purposes of this * method. */ private static int getBlockCipherOrder(@NotNull final String cipherSuiteName1, @NotNull final String cipherSuiteName2) { final int blockCipherValue1 = getBlockCipherValue(cipherSuiteName1); final int blockCipherValue2 = getBlockCipherValue(cipherSuiteName2); return blockCipherValue1 - blockCipherValue2; } /** * Retrieves an integer value for the provided cipher suite name based on the * block cipher settings. Lower values are preferred over higher values. * * @param cipherSuiteName The cipher suite name for which to obtain the * prefix value. It must not be {@code null}, and it * should represent a valid cipher suite name. * * @return An integer value for the provided cipher suite name based on the * block cipher settings. */ private static int getBlockCipherValue(@NotNull final String cipherSuiteName) { if (cipherSuiteName.contains("_AES_256_GCM")) { return 1; } else if (cipherSuiteName.contains("_AES_128_GCM")) { return 2; } else if (cipherSuiteName.contains("_AES") && cipherSuiteName.contains("_GCM")) { return 3; } else if (cipherSuiteName.contains("_AES_256")) { return 4; } else if (cipherSuiteName.contains("_AES_128")) { return 5; } else if (cipherSuiteName.contains("_AES")) { return 6; } else if (cipherSuiteName.contains("_CHACHA20")) { return 7; } else if (cipherSuiteName.contains("_GCM")) { return 8; } else { return 9; } } /** * Attempts to order the provided cipher suite names using the block cipher * settings. * * @param cipherSuiteName1 The first cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * @param cipherSuiteName2 The second cipher suite name to compare. It must * not be {@code null}, and it should represent a * valid cipher suite name. * * @return A negative integer value if the first cipher suite name should be * ordered before the second, a positive integer value if the first * cipher suite should be ordered after the second, or zero if they * are considered logically equivalent for the purposes of this * method. */ private static int getDigestOrder(@NotNull final String cipherSuiteName1, @NotNull final String cipherSuiteName2) { final int digestValue1 = getDigestValue(cipherSuiteName1); final int digestValue2 = getDigestValue(cipherSuiteName2); return digestValue1 - digestValue2; } /** * Retrieves an integer value for the provided cipher suite name based on the * block cipher settings. Lower values are preferred over higher values. * * @param cipherSuiteName The cipher suite name for which to obtain the * prefix value. It must not be {@code null}, and it * should represent a valid cipher suite name. * * @return An integer value for the provided cipher suite name based on the * block cipher settings. */ private static int getDigestValue(@NotNull final String cipherSuiteName) { if (cipherSuiteName.endsWith("_SHA512")) { return 1; } else if (cipherSuiteName.endsWith("_SHA384")) { return 2; } else if (cipherSuiteName.endsWith("_SHA256")) { return 3; } else if (cipherSuiteName.endsWith("_SHA")) { return 4; } else { return 5; } } /** * Indicates whether the provided object is logically equivalent to this TLS * cipher suite comparator. * * @param o The object for which to make the determination. * * @return {@code true} if the provided object is logically equivalent to * this TLS cipher suite comparator. */ @Override() public boolean equals(@Nullable final Object o) { return ((o != null) && (o instanceof TLSCipherSuiteComparator)); } /** * Retrieves the hash code for this TLS cipher suite comparator. * * @return The hash code for this TLS cipher suite comparator. */ @Override() public int hashCode() { return 0; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy