LoginSignup
4
7

More than 1 year has passed since last update.

JavaのSSLSocketを使う

Last updated at Posted at 2021-06-19

はじめに

JavaのSSLSocketを使って暗号化通信してみる。ついでにクライアント証明書も。
まずはサーバ側。

証明書の準備

サーバ側はWebサーバで使う証明書と同じく、サーバ証明書・秘密鍵・CA局証明書がまずは必要。
これをまとめてpkcs12形式にしておく。
クライアント証明書を使う場合はクライアント証明書のCA局証明書も使う。こちらは秘密鍵なしで使えるJKS形式で。
pkcs12に証明書だけ入れてもできそうなのだが、なぜか認識されなかった。

サーバ側証明書の読み込み

サーバ証明書は秘密鍵・CA局証明書とセットにしたpkcs12ファイルを読み込む。
FileInputStreamで開いて、java.security.KeyStore の load で読み込む。
読み込んだら javax.net.ssl.KeyManagerFactory の init に指定する。

クライアント証明書用CA局証明書も KeyStore の load で読み込むが、KeyManagerFactory ではなく、javax.net.ssl.TrustManagerFactory の init に指定する。
クライアント証明書を使わない場合はTrustManagerは不要。

コードとしては以下のようになる。

import java.net.Socket;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import java.io.FileInputStream;

public class App
{
    private static void initSSL()
    {
 
        try (
            FileInputStream p12_file = new FileInputStream("servercert.p12");
            FileInputStream jks_file = new FileInputStream("clienttrust.jks");
        ) {
            KeyManagerFactory kmf;
            KeyStore ks;
            ks = KeyStore.getInstance("pkcs12");
            ks.load(p12_file, "passphrase".toCharArray());
            kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, "passphrase".toCharArray());

            // ここからはクライアント証明書のためのもの
            TrustManagerFactory tmf;
            KeyStore ts;
            ts = KeyStore.getInstance("JKS");
            ts.load(jks_file, "jkspass".toCharArray());
            tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(ts);
        }
        catch (Exception e) {
            System.out.println("initSSL exception: " + e.toString());
        }
}

servercert.p12 はサーバ証明書・秘密鍵の pkcs12 ファイル、パスフレーズは passphrase なので、ここは環境に合わせて修正が必要。
同じく clienttrust.jks と jkspass も要修正。

ServerSocketの生成

上で生成した kmf と tmf を使う。(クライアント証明書を使わない場合はkmfのみ)
手順としては、java.net.ssl.SSLContext を生成し、初期化時に kmf, tmf (から取得した鍵・証明書情報) を指定する。
続いて ctx.getServerSocketFactory() で ServerSocketFactory を取得、そこから ServerSocket を作る。

コードとしては以下の通り。

            SSLContext ctx = SSLContext.getInstance("TLS");
            // クライアント証明書を使用しない場合は tmf.getTrustManagers() のところをnullにする
            ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            SSLServerSocketFactory ssf = ctx.getServerSocketFactory();
            SSLServerSocket sslsvr = (SSLServerSocket)ssf.createServerSocket(443);
            // クライアント証明書を使用しない場合は、以下不要
            sslsvr.setNeedClientAuth(true);

あとは通常の ServerSocket と同様にクライアントからの connect を accept して Socket を得る。(SSLSocketにキャストできる)
443のところがポート番号なので、それ以外のポートを使う場合は変更する。

pkcs12 の作り方

サーバ証明書・クライアント証明書の pkcs12 は openssl で作る。(多分 keytool でも作れるが)

openssl pkcs12 -export -in <証明書ファイル名> -inkey <秘密鍵ファイル名> -certfile <CA局証明書ファイル名> -out <出力ファイル名> 

これを実行するとパスフレーズを設定するため、2回入力する必要がある。

JKSの作り方

Java付属の keytool で作る。

keytool import -file <クライアント証明書用CA局ファイル名> -alias clientca -keystore <出力ファイル名>

alias のところは任意で大丈夫そうだが、keytool を使うときに必要になる場合あり。

サーバプログラムの動作確認

クライアントを作る前に openssl を使って動作確認すると良い。
以下のように実行する。

openssl s_client -connect <サーバホスト名>:<ポート> -cert <クライアント証明書ファイル名(PEM)> -key <秘密鍵ファイル名(PEM)>

また、うまく動かない場合は、実行時の java オプションに -Djavax.net.debug=all とすると色々出力されるのでヒントになるかも。allのところにはsslと指定することもでき、sslにするとオプション指定ができる。
指定できるオプションはここ参照。

TLSのバージョンについて

OpenJDK 17 では、デフォルトで TLS 1.2, 1.3 のみ有効になっている。
SSLServerSocket.setEnabledProtocolsで指定可能。例えば、以下のようにすると TLS 1.3 のみしか受け付けなくなる。

    SSLServerSocket sslsvr = (SSLServerSocket)ssf.createServerSocket(443);
    sslsvr.setEnabledProtocols(new String[] { "TLSv1.3" });

使えるプロトコルの確認は getEnabledProtocols で可能。

同じように Cipher Suites の変更は setEnabledCipherSuites で可能、確認は getEnabledCipherSuites なので、getEnabledCipherSuites してから、使いたくないものを削って setEnabledCipherSuites に指定すれば良い。

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7