https 証明書 java21

Eclipseを使用して、TLS 1.3をサポートし、PKCS#12形式のSSLルート証明書およびクライアント証明書を利用するJava 21のHTTPS通信を実装するには、以下の手順で2つのプロジェクトを作成します。

1. 証明書の作成

まず、keytoolを使用して、自己署名のCA証明書、サーバー証明書、およびクライアント証明書を作成します。以下の手順で進めます。

1.自己署名のCA証明書を作成

keytool -genkeypair -alias myca -keyalg RSA -keysize 2048 -validity 3650 -dname "CN=MyCA, OU=Dev, O=MyCompany, L=City, ST=State, C=JP" -keystore myca.p12 -storetype PKCS12 -storepass capassword -keypass capassword

2.CA証明書をエクスポート
keytool -exportcert -alias myca -keystore myca.p12 -storepass capassword -rfc -file myca.crt

3.サーバーのキーストアと証明書署名要求(CSR)を作成
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -validity 3650 -dname "CN=localhost, OU=Dev, O=MyCompany, L=City, ST=State, C=JP" -keystore server.p12 -storetype PKCS12 -storepass serverpassword -keypass serverpassword

keytool -certreq -alias server -keystore server.p12 -storepass serverpassword -file server.csr

4.CA証明書でサーバー証明書を署名

keytool -gencert -alias myca -keystore myca.p12 -storepass capassword -infile server.csr -outfile server.crt -validity 3650 -ext KU=digitalSignature,keyEncipherment -ext SAN=DNS:localhost

5.サーバーのキーストアにCA証明書と署名済みサーバー証明書をインポート
keytool -importcert -alias myca -file myca.crt -keystore server.p12 -storepass serverpassword -noprompt

keytool -importcert -alias server -file server.crt -keystore server.p12 -storepass serverpassword

6.クライアントのキーストアと証明書署名要求(CSR)を作成
keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -validity 3650 -dname "CN=Client, OU=Dev, O=MyCompany, L=City, ST=State, C=JP" -keystore client.p12 -storetype PKCS12 -storepass clientpassword -keypass clientpassword

keytool -certreq -alias client -keystore client.p12 -storepass clientpassword -file client.csr

7.CA証明書でクライアント証明書を署名
keytool -gencert -alias myca -keystore myca.p12 -storepass capassword -infile client.csr -outfile client.crt -validity 3650 -ext KU=digitalSignature,keyEncipherment


8.クライアントのキーストアにCA証明書と署名済みクライアント証明書をインポート
keytool -importcert -alias myca -file myca.crt -keystore client.p12 -storepass clientpassword -noprompt

keytool -importcert -alias client -file client.crt -keystore client.p12 -storepass clientpassword


サーバー側のjava
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

public class TLSClient {
    public static void main(String[] args) throws Exception {
        // クライアントキーストアのパスワード
        char[] clientPassphrase = "clientpassword".toCharArray();

        // クライアントキーストアの読み込み
        KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
        try (var fis = Files.newInputStream(Path.of("client.p12"))) {
            clientKeyStore.load(fis, clientPassphrase);
        }

        // キーマネージャーファクトリの初期化
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(clientKeyStore, clientPassphrase);

        // トラストストアの読み込み（サーバー証明書の検証用）
        KeyStore trustStore = KeyStore.getInstance("PKCS12");
        try (var fis = Files.newInputStream(Path.of("myca.p12"))) {
            trustStore.load(fis, "capassword".toCharArray());
        }

        // トラストマネージャーファクトリの初期化
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(trustStore);

        // SSLコンテキストの作成
        SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        // HttpClientの作成
        HttpClient client = HttpClient.newBuilder()
                .sslContext(sslContext)
                .build();

        // HTTPリクエストの作成
        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("https://localhost:8443"))
                .GET()
                .build();

        // リクエストの送信とレスポンスの取得
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // レスポンスの表示
        System.out.println("ステータスコード: " + response.statusCode());
        System.out.println("レスポンスボディ: " + response.body());
    }
}

クライアント側の実装

import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;

public class TLSServer {
    public static void main(String[] args) {
        try {
            // キーストアとトラストストアのパスワード
            char[] keystorePass = "serverpassword".toCharArray();
            char[] truststorePass = "capassword".toCharArray();

            // キーストアの読み込み
            KeyStore keystore = KeyStore.getInstance("PKCS12");
            try (FileInputStream keystoreFis = new FileInputStream("server.p12")) {
                keystore.load(keystoreFis, keystorePass);
            }

            // キーマネージャーファクトリの初期化
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(keystore, keystorePass);

            // トラストストアの読み込み
            KeyStore truststore = KeyStore.getInstance("PKCS12");
            try (FileInputStream truststoreFis = new FileInputStream("myca.p12")) {
                truststore.load(truststoreFis, truststorePass);
            }

            // トラストマネージャーファクトリの初期化
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(truststore);

            // SSLコンテキストの作成
            SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
            sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            // SSLサーバーソケットの作成
            SSLServerSocketFactory ssf = sslContext.getServerSocketFactory();
            try (SSLServerSocket serverSocket = (SSLServerSocket) ssf.createServerSocket(8443)) {
                serverSocket.setNeedClientAuth(true); // クライアント認証を要求

                System.out.println("TLS 1.3 サーバーが起動しました。ポート: 8443");

                while (true) {
                    try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
                        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

                        String line;
                        while ((line = in.readLine()) != null && !line.isEmpty()) {
                            System.out.println("受信: " + line);
                        }

                        // レスポンスの送信
                        out.write("HTTP/1.1 200 OK\r\n");
                        out.write("Content-Type: text/plain\r\n");
                        out.write("Content-Length: 13\r\n");
                        out.write("\r\n");
                        out.write("Hello, World!");
                        out.flush();
                    } catch (IOException e) {
                        System.err.println("クライアントとの通信中にエラーが発生しました: " + e.getMessage());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}