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

com.bluecatcode.common.security.Protocols Maven / Gradle / Ivy

The newest version!
package com.bluecatcode.common.security;

import javax.annotation.Nonnull;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Each named cipher suite, e.g. TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
 * - defines a Key Exchange Algorithms,
 * - a bulk Encryption Algorithms,
 * - a Message Authentication Code (MAC) algorithm,
 * - and a pseudorandom function (PRF)
 *
 * OWASP Rules:
 * - Only Support Strong Protocols (TLS 1.2, TLS 1.1, avoid TLS 1.0 if possible)
 * - Highest Priority for Ephemeral Key Exchanges
 * - Favor DHE over ECDHE
 * - Use RSA-Keys (no DSA/DSS)
 * - Favor GCM over CBC, use Authenticated Encryption with Associated Data (AEAD), e.g. AES-GCM, AES-CCM
 * - Priorize the ciphers by the sizes of the Cipher and the MAC
 * - Use SHA1 or above for digests, prefer SHA2 (or equivalent)
 * - Ciphers should be usable for DH-Pamameters >= 2048 bits, without blocking legacy browsers (The cipher ‘DHE-RSA-AES128-SHA’ is suppressed as some browsers like to use it but are not capable to cope with DH-Params > 1024 bits.)
 * - candidate for minimal best compromise is TLS_RSA_WITH_3DES_EDE_CBC_SHA (0xa)
 *
 * Best Key Exchange Algorithms (Perfect Forward Secrecy):
 * - DHE-RSA
 * - ECDHE-RSA
 * - ECDHE-ECDSA
 *
 * Best Encryption Algorithms:
 * - AES-GCM (block)
 * - AES-CCM (block)
 * - ChaCha20-Poly1305 (stream)
 *
 * Best Message Authentication Codes (MAC):
 * - AEAD (used for GCM mode and CCM mode)
 * - HMAC-SHA256/384 (used for CBC and stream ciphers)
 *
 * Weak ciphers such as DES and RC4 should be disabled.
 *
 * Ideally, HTTPS service would enable only TLS 1.2 and enable only AEAD (Authenticated Encryption with Associated Data)
 * based cipher suites with SHA-2, 4096 bit DH parameters and 512 bit EC curves of a type that matches your requirements
 * (government approved or not government generated).
 *
 * Look at the java.security file properties, e.g.:
 * jdk.tls.client.protocols=TLSv1.2
 * jdk.tls.disabledAlgorithms=MD5, SHA1, RC4, DSA, RSA keySize < 2048, SSLv3
 * jdk.tls.ephemeralDHKeySize=2048
 * jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048
 *
 * @see Transport Layer Security
 * @see Transport Layer Protection Cheat Sheet
 * @see Server Side TLS
 * @see OpenSSL Ciphers
 * @see Secure TLS Cipher Suites
 */
public class Protocols {

    public static final String HTTPS_SCHEMA = "https";

    public static final String TLS = "TLS";
    public static final String TLS_V1 = "TLSv1";
    public static final String TLS_V1_1 = "TLSv1.1";
    public static final String TLS_V1_2 = "TLSv1.2";

    public static final String[] DEFAULT_TLS_PROTOCOLS = {
            TLS_V1_2,
            TLS_V1_1,
            TLS_V1
    };

    /**
     * TLS 1.2 AEAD only (all are SHA-2 as well)
     */
    public static final String[] STRONGEST_CIPHER_SUITES = new String[]{
            "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",           // (0x9f)
            "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",           // (0x9e)
            "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",     // (0xcc15)
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",         // (0xc030)
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",         // (0xc02f)
            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",   // (0xcc13)
            "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",       // (0xc02c)
            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",       // (0xc02b)
            "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", // (0xcc14)
    };

    /**
     * TLS 1.2 SHA2 family (using the much older CBC mode)
     */
    public static final String[] STRONG_CIPHER_SUITES = new String[]{
            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",        // (0x6b)
            "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",        // (0x67)
            "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",   // (0xc4)
            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",      // (0xc028)
            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",      // (0xc027)
            "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", // (0xc077)
            "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", // (0xc076)
            "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",    // (0xc024)
            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",    // (0xc023)
    };

    /**
     * TLS 1.0 and 1.1 with modern ciphers (and outdated hashes, since that's all that's available)
     */
    public static final String[] ACCEPTABLE_CIPHER_SUITES = new String[]{
            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",      // (0x39)
            "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",      // (0x33)
            "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", // (0x88)
            "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", // (0x45)
            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",    // (0xc014)
            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",    // (0xc013)
    };

    /**
     * TLS 1.0 and 1.1 with older but still reasonable ciphers and outdated hashes
     */
    public static final String[] MINIMAL_CIPHER_SUITES = new String[]{
            "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",     // (0x16)
            "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",   // (0xc012)
            "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", // (0xc008)
    };

    /**
     * List of sensible cipher suites for TLS Cipher hardening.
     * Versions: TLSv1, TLSv1.1, TLSv1.2
     */
    public static final String[] DEFAULT_CIPHER_SUITES = combine(
            STRONGEST_CIPHER_SUITES,
            STRONG_CIPHER_SUITES,
            ACCEPTABLE_CIPHER_SUITES,
            MINIMAL_CIPHER_SUITES
    );

    private static String[] combine(String[]... arrays) {
        List list = new ArrayList<>();
        for (String[] array : arrays) {
            Collections.addAll(list, array);
        }
        return list.toArray(new String[list.size()]);
    }

    public static SSLContext createTlsContext(KeyManager[] keyManagers, TrustManager[] trustManagers) {
        return createTlsContext(keyManagers, trustManagers, DEFAULT_TLS_PROTOCOLS, DEFAULT_CIPHER_SUITES);
    }

    public static SSLContext createTlsContext(KeyManager[] keyManagers, TrustManager[] trustManagers, String[] enabledProtocols, String[] supportedCipherSuites) {
        SSLContext sslContext;
        try {
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagers, trustManagers, new SecureRandom());
            sslContext.getDefaultSSLParameters().setProtocols(enabledProtocols);
            sslContext.getDefaultSSLParameters().setCipherSuites(supportedCipherSuites);
        } catch (NoSuchAlgorithmException | KeyManagementException ex) {
            throw new IllegalStateException(ex);
        }
        return sslContext;
    }

    public static HttpsURLConnection httpsConnection(URL url, HttpsConnectionInitializer initializer) throws IOException {
        if (url == null) {
            throw new IllegalArgumentException("Expected non-null url");
        }
        if (url.getProtocol().equalsIgnoreCase(HTTPS_SCHEMA)) {
            throw new IllegalArgumentException("Expected a HTTPS url");
        }
        if (initializer == null) {
            throw new IllegalArgumentException("Expected non-null initializer");
        }
        URLConnection urlConnection = url.openConnection();
        if (!(urlConnection instanceof HttpsURLConnection)) {
            throw new IllegalStateException("Expected a HttpsURLConnection");
        }
        HttpsURLConnection connection = (HttpsURLConnection) urlConnection;
        connection = initializer.initialize(connection);
        if (connection == null) {
            throw new IllegalStateException("Expected non-null connection returned by the initializer");
        }
        return connection;
    }

    public interface HttpsConnectionInitializer {
        @Nonnull
        HttpsURLConnection initialize(@Nonnull HttpsURLConnection connection);
    }

    private Protocols() {
        throw new UnsupportedOperationException();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy