aboutsummaryrefslogtreecommitdiffstats
path: root/security-utils/src/main/java/com/yahoo/security/tls/TlsContext.java
blob: 8e146f36907cf499f5e0592c1c6fb955fae1150a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.security.tls;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import static java.util.stream.Collectors.toSet;

/**
 * A simplified version of {@link SSLContext} modelled as an interface.
 *
 * @author bjorncs
 */
public interface TlsContext extends AutoCloseable {

    /**
     * Handpicked subset of supported ciphers from https://www.openssl.org/docs/manmaster/man1/ciphers.html
     * based on Modern spec from https://wiki.mozilla.org/Security/Server_Side_TLS
     * For TLSv1.2 we only allow RSA and ECDSA with ephemeral key exchange and GCM.
     * For TLSv1.3 we allow the DEFAULT group ciphers.
     * Note that we _only_ allow AEAD ciphers for either TLS version.
     *
     * TODO(bjorncs) Add new ciphers once migrated to JDK-17 (also available in 11.0.13):
     * - TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
     */
    Set<String> ALLOWED_CIPHER_SUITES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
            "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
            "TLS_AES_128_GCM_SHA256", // TLSv1.3
            "TLS_AES_256_GCM_SHA384" // TLSv1.3
            )));

    // TODO Enable TLSv1.3 after upgrading to JDK 17
    Set<String> ALLOWED_PROTOCOLS = Collections.singleton("TLSv1.2");

    /** 
     * {@link SSLContext} protocol name that supports at least oldest protocol listed in {@link #ALLOWED_PROTOCOLS}
     * @see SSLContext#getInstance(String)
     */
    String SSL_CONTEXT_VERSION = "TLSv1.2";

    /**
     * @return the allowed cipher suites supported by the provided context instance
     */
    static Set<String> getAllowedCipherSuites(SSLContext context) {
        String[] supportedCiphers = context.getSupportedSSLParameters().getCipherSuites();
        Set<String> enabledCiphers = Arrays.stream(supportedCiphers)
                .filter(ALLOWED_CIPHER_SUITES::contains)
                .collect(toSet());
        if (enabledCiphers.isEmpty()) {
            throw new IllegalArgumentException(
                    String.format("Non of the allowed ciphers are supported (allowed=%s, supported=%s)",
                                  ALLOWED_CIPHER_SUITES, Arrays.toString(supportedCiphers)));

        }
        return enabledCiphers;
    }

    static Set<String> getAllowedCipherSuites() { return getAllowedCipherSuites(defaultSslContext()); }

    /**
     * @return the allowed protocols supported by the provided context instance
     */
    static Set<String> getAllowedProtocols(SSLContext context) {
        String[] supportedProtocols = context.getSupportedSSLParameters().getProtocols();
        Set<String> enabledProtocols = Arrays.stream(supportedProtocols)
                .filter(ALLOWED_PROTOCOLS::contains)
                .collect(toSet());
        if (enabledProtocols.isEmpty()) {
            throw new IllegalArgumentException(
                    String.format("Non of the allowed protocols are supported (allowed=%s, supported=%s)",
                                  ALLOWED_PROTOCOLS, Arrays.toString(supportedProtocols)));
        }
        return enabledProtocols;
    }

    static Set<String> getAllowedProtocols() { return getAllowedProtocols(defaultSslContext()); }

    /** @return Default {@link SSLContext} instance without certificate and using JDK's default trust store */
    static SSLContext defaultSslContext() {
        try {
            var ctx = SSLContext.getInstance(SSL_CONTEXT_VERSION);
            ctx.init(null, null, null);
            return ctx;
        } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e);
        } catch (KeyManagementException e) { throw new IllegalStateException(e); }
    }

    SSLContext context();

    SSLParameters parameters();

    SSLEngine createSslEngine();

    SSLEngine createSslEngine(String peerHost, int peerPort);

    @Override default void close() {}

}