CLOVER🍀

That was when it all began.

JSSEでサポートしているSSL/TLSプロトコル、SSLSocketFactoryでサポートしている暗号スイートを確認する

これは、なにをしたくて書いたもの?

使用しているJava環境でサポートしているSSL/TLSプロトコルや、暗号スイートを確認したいなと思いまして。

特にこれを書こうと思ったきっかけはSSLSocketFactory#getSupportedCipherSuitesとSSLSocketFactory#getDefaultCipherSuitesの
違いがよくわからなかったからです。

JSSEでのプロトコルや暗号スイートの設定に関するドキュメントを見る

この話で見るべきドキュメントはJSSE(Java Secure Socket Extension)のものでしょうね。

Java Secure Socket Extension (JSSE)リファレンス・ガイド

ドキュメント上は、以下のプロトコルをサポートしていることになっています。

  • DTLSv1.0、1.2
  • TLSv1.0、1.1、1.2、1.3
  • SSLv3.0

Java Secure Socket Extension (JSSE)リファレンス・ガイド / JSSEの概要

とはいえ、実際にはjava.securityファイルのjdk.tls.disabledAlgorithmsセキュリティプロパティーで無効にされているプロトコルがあります。

Java Secure Socket Extension (JSSE)リファレンス・ガイド / JSSEクラスとインタフェース / サポート・クラスとインタフェース

また、SSL/TLS通信時に利用できる暗号スイートはJSSEプロバイダーによって決まっていますが、さらにシステムプロパティや
アプリケーション内でカスタマイズできます。

デフォルトで有効な暗号化方式群は、アプリケーション内で、またはシステム・プロパティjdk.tls.client.cipherSuitesとjdk.tls.server.cipherSuitesを使用して指定できます。

Java Secure Socket Extension (JSSE)リファレンス・ガイド / JSSEのカスタマイズ / デフォルトで有効な暗号化方式群の指定

優先順位は以下のようです。

デフォルトで有効化する暗号化方式群のセットは、この優先度順に従い、次のいずれかの方法で決定されます。

  • アプリケーションによる明示的な設定
  • システム・プロパティによる指定
  • JSSEプロバイダのデフォルトによる指定

たとえば、デフォルトで有効な暗号化方式群をアプリケーション内で明示的に設定すると、これにより、jdk.tls.client.cipherSuitesまたはjdk.tls.server.cipherSuitesおよびJSSEプロバイダのデフォルトで指定された設定がオーバーライドされます。

アプリケーションでの明示的な指定は、以下を指しています。

  • SSLSocket#setEnabledCipherSuites
  • SSLEngine#setEnabledCipherSuites
  • SSLServerSocket#setEnabledCipherSuites
  • SSLParametersのコンストラクターによる指定
  • HttpsURLConnectionのhttps.cipherSuitesシステム・プロパティ

システムプロパティーでもいろいろカスタマイズできるんですね。

設定できるセキュリティプロパティーおよびシステムプロパティーの一覧は以下に記載されています。

Java Secure Socket Extension (JSSE)リファレンス・ガイド / JSSEのカスタマイズ

SSLContext、SSLSocketFactoryのSupported〜とDefault〜の違い

ところで、SSLContextにはgetSupportedSSLParametersとgetDefaultSSLParameters、SSLSocketFactoryとSSLServerSocketFactoryには
それぞれgetSupportedCipherSuitesとgetDefaultCipherSuitesと似たような名前のメソッドがあります。
※SSLContext#getSupportedSSLParametersまたはSSLContextgetDefaultSSLParametersから取得できるSSLParameters`からプロトコルの情報を取得できます

SSLContext (Java SE 21 & JDK 21)

SSLSocketFactory (Java SE 21 & JDK 21)

SSLServerSocketFactory (Java SE 21 & JDK 21)

この違いはなんだろうと思っていたのですが、SSLSocketFactoryのJavadocを見ると意味がわかります。

getSupportedCipherSuites
SSL接続で使用可能にできる暗号化方式群の名前を返します。 通常は、その一部のみがデフォルトで使用可能になります。デフォルトのサービス品質要件を満たしていない暗号化方式群は、使用不可になります。 それらの暗号化方式群は、特殊なアプリケーションで使用します。

getDefaultCipherSuites
デフォルトで使用可能になっている暗号化方式群のリストを返します。 別のリストが使用可能になっていないかぎり、SSL接続のハンドシェークではこの暗号化方式群のいずれかが使用されます。 デフォルトの暗号化方式群を使用するとき、最小限のサービス品質を保証するには、機密性保護とサーバー認証が必要です(つまり、匿名の暗号化方式群は使用できない)。

なので、プロバイダーでサポートしている範囲がgetSupported〜で表現され、実際に使えるものがgetDefault〜で返されるようです。
つまり、getSupported〜よりもgetDefault〜の方が少なくなることがあるということです。

実行環境で実際に使える情報を見たい場合は、getDefault〜の方を使った方がよさそうですね。

SSLSocketFactoryやSSLServerSocketFactoryの実装を見ると、getDefault〜の方はクライアント用途なのかサーバー用途なのかといった
ロールに合わせたフィルタリングをしていることも確認できます。

    /**
     * Returns the subset of the supported cipher suites which are
     * enabled by default.  These cipher suites all provide a minimum
     * quality of service whereby the server authenticates itself
     * (preventing person-in-the-middle attacks) and where traffic
     * is encrypted to provide confidentiality.
     */
    @Override
    public String[] getDefaultCipherSuites() {
        return CipherSuite.namesOf(context.getDefaultCipherSuites(false));
    }

    /**
     * Returns the names of the cipher suites which could be enabled for use
     * on an SSL connection.  Normally, only a subset of these will actually
     * be enabled by default, since this list may include cipher suites which
     * do not support the mutual authentication of servers and clients, or
     * which do not protect data confidentiality.  Servers may also need
     * certain kinds of certificates to use certain cipher suites.
     */
    @Override
    public String[] getSupportedCipherSuites() {
        return CipherSuite.namesOf(context.getSupportedCipherSuites());
    }

https://github.com/openjdk/jdk21u/blob/jdk-21.0.2%2B13/src/java.base/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java#L172-L195

    /**
     * Returns the subset of the supported cipher suites which are
     * enabled by default.  These cipher suites all provide a minimum
     * quality of service whereby the server authenticates itself
     * (preventing person-in-the-middle attacks) and where traffic
     * is encrypted to provide confidentiality.
     */
    @Override
    public String[] getDefaultCipherSuites() {
        return CipherSuite.namesOf(context.getDefaultCipherSuites(true));
    }

    /**
     * Returns the names of the cipher suites which could be enabled for use
     * on an SSL connection.  Normally, only a subset of these will actually
     * be enabled by default, since this list may include cipher suites which
     * do not support the mutual authentication of servers and clients, or
     * which do not protect data confidentiality.  Servers may also need
     * certain kinds of certificates to use certain cipher suites.
     *
     * @return an array of cipher suite names
     */
    @Override
    public String[] getSupportedCipherSuites() {
        return CipherSuite.namesOf(context.getSupportedCipherSuites());
    }

https://github.com/openjdk/jdk21u/blob/jdk-21.0.2%2B13/src/java.base/share/classes/sun/security/ssl/SSLServerSocketFactoryImpl.java#L90-L115

こちらですね。

    // SSLSocketFactoryImpl
    @Override
    public String[] getDefaultCipherSuites() {
        return CipherSuite.namesOf(context.getDefaultCipherSuites(false));
    }


    // SSLServerSocketFactoryImpl
    @Override
    public String[] getDefaultCipherSuites() {
        return CipherSuite.namesOf(context.getDefaultCipherSuites(true));
    }

この後、さらにSSLSocket#setEnabledCipherSuitesなどで使用する暗号スイートを絞り込んだりできるわけですが。

今回、SSLContextやSSLSocketFactoryから利用できるプロトコルや暗号スイートを確認しつつ、getSupported〜やgetDefault〜の
違いを見ていこうと思います。

環境

今回の環境はこちら。

$ java --version
openjdk 21.0.2 2024-01-16
OpenJDK Runtime Environment (build 21.0.2+13-Ubuntu-122.04.1)
OpenJDK 64-Bit Server VM (build 21.0.2+13-Ubuntu-122.04.1, mixed mode, sharing)


$ mvn --version
Apache Maven 3.9.7 (8b094c9513efc1b9ce2d952b3b9c8eaedaf8cbf0)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 21.0.2, vendor: Private Build, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-107-generic", arch: "amd64", family: "unix"

java.securityファイルの内容を確認する

最初にjava.securityファイルの内容を確認してみましょう。コメントを除外して確認。

$ grep -vE '^ *#' /usr/lib/jvm/java-21-openjdk-amd64/conf/security/java.security | grep -v '^$'

jdk.tls.disabledAlgorithmsセキュリティプロパティーは、このようになっていました。

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \
    MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
    ECDH

準備

それでは、動作確認を行うソースコードを書く準備をします。

Maven依存関係等はこちら。

    <properties>
        <maven.compiler.release>21</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.26.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.1.2</version>
            </plugin>
        </plugins>
    </build>

動作確認はテストコードで行うことにします。その雛形はこちら。

src/test/java/org/littlewings/ssl/SslProtocolsTest.java

package org.littlewings.ssl;

import org.junit.jupiter.api.Test;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import static org.assertj.core.api.Assertions.assertThat;

class SslProtocolsTest {
    // ここに、テストを書く
}

以降では、テストコードを書いていきます。

サポートしているSSL/TLSプロトコルを確認する

まずはサポートしているSSL/TLSプロトコルを確認してみます。

最初はデフォルトのSSLContextで確認してみます。このような結果になりました。

    @Test
    void defaultContextSupportedProtocols() throws NoSuchAlgorithmException {
        SSLContext sslContext = SSLContext.getDefault();

        assertThat(sslContext.getProtocol()).isEqualTo("Default");

        assertThat(sslContext.getSupportedSSLParameters().getProtocols()).containsExactly(
                "TLSv1.3",
                "TLSv1.2",
                "TLSv1.1",
                "TLSv1",
                "SSLv3",
                "SSLv2Hello"
        );
        assertThat(sslContext.getDefaultSSLParameters().getProtocols()).containsExactly(
                "TLSv1.3",
                "TLSv1.2"
        );
    }

こう見ると、確かにプロバイダーで利用可能になっているのがgetSupported〜、実際に利用できるのはgetDefault〜を見るというのは
合っていそうです。

java.securityファイルでSSLv3やTLSv1〜1.1までは無効になっていましたからね。

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \
    MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
    ECDH

明示的にTLSv1.3を指定して確認。

    @Test
    void tls13ContextSupportedProtocols() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
        sslContext.init(null, null, new SecureRandom());

        assertThat(sslContext.getProtocol()).isEqualTo("TLSv1.3");

        assertThat(sslContext.getSupportedSSLParameters().getProtocols()).containsExactly(
                "TLSv1.3",
                "TLSv1.2",
                "TLSv1.1",
                "TLSv1",
                "SSLv3",
                "SSLv2Hello"
        );
        assertThat(sslContext.getDefaultSSLParameters().getProtocols()).containsExactly(
                "TLSv1.3",
                "TLSv1.2"
        );
    }

SSLContext#getProtocolの結果が変わるくらいで、ここでgetSupported〜とgetDefault〜の差は出ませんでした。

サポートしている暗号スイートを確認する

次は、SSLSocketFactoryおよびSSLServerSocketFactoryでサポートしている暗号スイートを確認してみます。

最初はデフォルトのSSLContextからSSLSocketFactoryおよびSSLServerSocketFactoryを作成して確認します。

    @Test
    void defaultContextSupportedCipherSuites() throws NoSuchAlgorithmException {
        SSLContext sslContext = SSLContext.getDefault();

        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        assertThat(sslSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",

                // 省略

                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",

                // 省略

                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .containsExactly(sslSocketFactory.getDefaultCipherSuites());
        assertThat(sslSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslSocketFactory.getDefaultCipherSuites()).hasSize(37);

        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",

                // 省略

                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",

                // 省略

                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getSupportedCipherSuites())
                .containsExactly(sslServerSocketFactory.getDefaultCipherSuites());
        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).hasSize(37);

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isEqualTo(sslServerSocketFactory.getSupportedCipherSuites());
        assertThat(sslSocketFactory.getDefaultCipherSuites())
                .isEqualTo(sslServerSocketFactory.getDefaultCipherSuites());
    }

暗号スイートを全部並べると長いので、途中は省略します。

デフォルトのSSLContextから得られたSSLSocketFactoryとSSLServerSocketFactoryでは、getSupportedCipherSuitesと
getDefaultCipherSuites結果が同じでした。クライアント用、サーバー用をまたいで比較しても同じでしたね。

なので、この結果としてはサポートしている暗号スイートと利用できるようになっている暗号スイートが同じということです。

次に、TLSv1.2として作成したSSLContextで試してみます。

    @Test
    void tls12ContextSupportedCipherSuites() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, null, new SecureRandom());

        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();


        assertThat(sslSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",

                // 省略

                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",

                // 省略

                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        // 異なる
        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isNotEqualTo(sslSocketFactory.getDefaultCipherSuites());
        assertThat(sslSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslSocketFactory.getDefaultCipherSuites()).hasSize(34);

        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",

                // 省略

                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",

                // 省略

                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getSupportedCipherSuites())
                .containsExactly(sslServerSocketFactory.getDefaultCipherSuites());
        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).hasSize(37);
        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).hasSize(37);

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isEqualTo(sslServerSocketFactory.getSupportedCipherSuites());
        // 異なる
        assertThat(sslSocketFactory.getDefaultCipherSuites())
                .isNotEqualTo(sslServerSocketFactory.getDefaultCipherSuites());
    }

こちらはSSLSocketFactory、つまりクライアント側でgetSupportedCipherSuitesとgetDefaultCipherSuitesの結果が異なりましたね。
getDefaultCipherSuitesの方が数が少なくなりました。

        // 異なる
        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isNotEqualTo(sslSocketFactory.getDefaultCipherSuites());
        assertThat(sslSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslSocketFactory.getDefaultCipherSuites()).hasSize(34);

サーバー側であるSSLServerSocketFactoryではgetSupportedCipherSuitesとgetDefaultCipherSuitesの結果は同じですが、クライアント側と
サーバー側で利用できる暗号スイートが異なることになります。

というわけで、最初に仮定した実行環境で実際に使える情報を見たい場合は、getDefault〜の方を使った方がよさそうという考え方は
合っていそうなことが確認できました。

jdk.tls.client.cipherSuitesおよびjdk.tls.server.cipherSuitesシステムプロパティーを設定する

最後にjdk.tls.client.cipherSuitesおよびjdk.tls.server.cipherSuitesを設定して確認してみましょう。

jdk.tls.disabledAlgorithmsがセキュリティプロパティーだったのに対して、jdk.tls.client.cipherSuitesとjdk.tls.server.cipherSuitesは
システムプロパティーです。

これらのシステムプロパティーで、クライアント側またはサーバー側で利用できる暗号スイートを限定することができます。

確認に使用したテストコードはこちら。

src/test/java/org/littlewings/ssl/SslLimitedCipherSuitesTest.java

package org.littlewings.ssl;

import org.junit.jupiter.api.Test;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.security.NoSuchAlgorithmException;

import static org.assertj.core.api.Assertions.assertThat;

class SslLimitedCipherSuitesTest {
    @Test
    void cipherSuites() throws NoSuchAlgorithmException {
        System.setProperty("jdk.tls.client.cipherSuites", "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256");
        System.setProperty("jdk.tls.server.cipherSuites", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");

        SSLContext sslContext = SSLContext.getDefault();

        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        assertThat(sslSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256"
        );

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isNotEqualTo(sslSocketFactory.getDefaultCipherSuites());
        assertThat(sslSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslSocketFactory.getDefaultCipherSuites()).hasSize(2);

        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
        );

        assertThat(sslServerSocketFactory.getSupportedCipherSuites())
                .isNotEqualTo(sslServerSocketFactory.getDefaultCipherSuites());
        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).hasSize(2);

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isEqualTo(sslServerSocketFactory.getSupportedCipherSuites());
        assertThat(sslSocketFactory.getDefaultCipherSuites())
                .isNotEqualTo(sslServerSocketFactory.getDefaultCipherSuites());
    }
}

今回は、System#setPropertyで指定することにしました。クライアント側、サーバー側それぞれ別のものを指定しています。

        System.setProperty("jdk.tls.client.cipherSuites", "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256");
        System.setProperty("jdk.tls.server.cipherSuites", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");

結果、getSupportedCipherSuitesは変わりませんがgetDefaultCipherSuitesで得られる暗号スイートはそれぞれのシステムプロパティーで
指定したものに限定されることになりました。

        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        assertThat(sslSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256"
        );

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isNotEqualTo(sslSocketFactory.getDefaultCipherSuites());
        assertThat(sslSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslSocketFactory.getDefaultCipherSuites()).hasSize(2);

        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
        );

これでだいたい使い方はわかった感じですね。

オマケ

最後に、暗号スイートを列挙した箇所を省略していないソースコードを載せておきます。

src/test/java/org/littlewings/ssl/SslProtocolsTest.java

package org.littlewings.ssl;

import org.junit.jupiter.api.Test;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import static org.assertj.core.api.Assertions.assertThat;

class SslProtocolsTest {
    @Test
    void defaultContextSupportedProtocols() throws NoSuchAlgorithmException {
        SSLContext sslContext = SSLContext.getDefault();

        assertThat(sslContext.getProtocol()).isEqualTo("Default");

        assertThat(sslContext.getSupportedSSLParameters().getProtocols()).containsExactly(
                "TLSv1.3",
                "TLSv1.2",
                "TLSv1.1",
                "TLSv1",
                "SSLv3",
                "SSLv2Hello"
        );
        assertThat(sslContext.getDefaultSSLParameters().getProtocols()).containsExactly(
                "TLSv1.3",
                "TLSv1.2"
        );
    }

    @Test
    void tls13ContextSupportedProtocols() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
        sslContext.init(null, null, new SecureRandom());

        assertThat(sslContext.getProtocol()).isEqualTo("TLSv1.3");

        assertThat(sslContext.getSupportedSSLParameters().getProtocols()).containsExactly(
                "TLSv1.3",
                "TLSv1.2",
                "TLSv1.1",
                "TLSv1",
                "SSLv3",
                "SSLv2Hello"
        );
        assertThat(sslContext.getDefaultSSLParameters().getProtocols()).containsExactly(
                "TLSv1.3",
                "TLSv1.2"
        );
    }

    @Test
    void defaultContextSupportedCipherSuites() throws NoSuchAlgorithmException {
        SSLContext sslContext = SSLContext.getDefault();

        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        assertThat(sslSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .containsExactly(sslSocketFactory.getDefaultCipherSuites());
        assertThat(sslSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslSocketFactory.getDefaultCipherSuites()).hasSize(37);

        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getSupportedCipherSuites())
                .containsExactly(sslServerSocketFactory.getDefaultCipherSuites());
        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).hasSize(37);

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isEqualTo(sslServerSocketFactory.getSupportedCipherSuites());
        assertThat(sslSocketFactory.getDefaultCipherSuites())
                .isEqualTo(sslServerSocketFactory.getDefaultCipherSuites());
    }

    @Test
    void tls12ContextSupportedCipherSuites() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, null, new SecureRandom());

        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        assertThat(sslSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        // 異なる
        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isNotEqualTo(sslSocketFactory.getDefaultCipherSuites());
        assertThat(sslSocketFactory.getSupportedCipherSuites()).hasSize(37);
        assertThat(sslSocketFactory.getDefaultCipherSuites()).hasSize(34);

        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).containsExactly(
                "TLS_AES_256_GCM_SHA384",
                "TLS_AES_128_GCM_SHA256",
                "TLS_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        );

        assertThat(sslServerSocketFactory.getSupportedCipherSuites())
                .containsExactly(sslServerSocketFactory.getDefaultCipherSuites());
        assertThat(sslServerSocketFactory.getDefaultCipherSuites()).hasSize(37);
        assertThat(sslServerSocketFactory.getSupportedCipherSuites()).hasSize(37);

        assertThat(sslSocketFactory.getSupportedCipherSuites())
                .isEqualTo(sslServerSocketFactory.getSupportedCipherSuites());
        // 異なる
        assertThat(sslSocketFactory.getDefaultCipherSuites())
                .isNotEqualTo(sslServerSocketFactory.getDefaultCipherSuites());
    }
}

おわりに

JSSEでサポートしているSSL/TLSプロトコル、SSLSocketFactoryでサポートしている暗号スイートを確認する方法や、設定変更について
調べてみました。

このあたり、ざっくりとしか見ていなかったのでSSL/TLSまわりのカスタマイズについていろいろと勉強になりました。

そんなに頻繁に使う知識でもないと思いますが、せっかくなのでメモとして。