HTTPの普及下でHTTPSが必要とされる理由

HTTPは平文通信を行うため、データが第三者に盗聴されるリスクがあります。HTTPSはSSL/TLSプロトコルを採用し、通信を暗号化することで、データの機密性と整合性を確保します。また、CA(証明機関)発行のデジタル証明書によるサーバー認証機能も備えており、フィッシング攻撃の防止に役立ちます。

さらに、検索エンジンのランキング向上や、最新のHTTP/2やTLS 1.3の導入による性能改善も、HTTPS採用の推進要因となっています。

自署名証明書の生成

コマンドラインツールを使用して、自署名証明書を生成します。

keytool -genkeypair -alias secure -keyalg RSA -keysize 4096 -validity 730 -storetype PKCS12 -keystore secure.p12 -storepass mysecret

各パラメータの説明:

  • -alias secure:キーのエイリアス名
  • -keysize 4096:鍵の長さを4096ビットに設定
  • -validity 730:有効期限を730日(2年)に設定
  • -storepass mysecret:キーストアのパスワード

生成時に組織情報の入力を求められますが、自動入力する場合は-dnameオプションを使用できます。

keytool -genkeypair -alias secure -keyalg RSA -keysize 4096 -validity 730 -storetype PKCS12 -keystore secure.p12 -storepass mysecret -dname "CN=localhost, OU=IT, O=Example, L=Tokyo, ST=Tokyo, C=JP"

JavaによるHTTPSサーバー実装

以下は、生成した証明書を使用したHTTPSサーバーのコード例です。

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.security.KeyStore;

public class SecureHttpServer {
    public static void main(String[] args) throws Exception {
        KeyStore ks = KeyStore.getInstance("PKCS12");
        try (FileInputStream fis = new FileInputStream("secure.p12")) {
            ks.load(fis, "mysecret".toCharArray());
        }

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, "mysecret".toCharArray());

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(ks);

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        HttpsServer server = HttpsServer.create(new InetSocketAddress(8443), 0);
        server.setHttpsConfigurator(new HttpsConfigurator(sslContext));

        server.createContext("/secure", new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) {
                if ("POST".equals(exchange.getRequestMethod())) {
                    try {
                        var json = new String(exchange.getRequestBody().readAllBytes());
                        System.out.println("受信データ: " + json);
                        String response = "{\"status\":\"success\"}";
                        exchange.sendResponseHeaders(200, response.getBytes().length);
                        try (var os = exchange.getResponseBody()) {
                            os.write(response.getBytes());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    exchange.sendResponseHeaders(405, -1);
                }
            }
        });

        server.start();
        System.out.println("HTTPSサーバー起動中: https://localhost:8443/secure");
    }
}

HTTPSクライアントテスト

サーバーを起動した後、以下のようなクライアントコードでテストできます。

import javax.net.ssl.*;
import java.io.OutputStream;
import java.net.URL;
import java.security.cert.X509Certificate;

public class SecureClient {
    public static void main(String[] args) throws Exception {
        String urlStr = "https://localhost:8443/secure";
        String jsonData = "{\"data\":\"test\"}";

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
            public void checkClientTrusted(X509Certificate[] certs, String authType) {}
            public void checkServerTrusted(X509Certificate[] certs, String authType) {}
        }}, new java.security.SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);

        URL url = new URL(urlStr);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setDoOutput(true);

        try (OutputStream os = conn.getOutputStream()) {
            os.write(jsonData.getBytes());
        }

        try (var is = conn.getInputStream()) {
            var response = new String(is.readAllBytes());
            System.out.println("レスポンス: " + response);
        }
    }
}

コマンドラインでのテストには、curlコマンドを使用できます。

curl -X POST https://localhost:8443/secure -H "Content-Type: application/json" -d '{"data":"test"}' -k

HTTP 405エラーが発生した場合は、リクエストメソッドがPOSTであることを確認してください。サーバー側ではPOSTメソッドのみを許可しています。

タグ: tls SSL PKCS12 Java HTTPS

8月15日 23:06 投稿