読者です 読者をやめる 読者になる 読者になる

JavaでSSL通信したいのに、手元にはPEMファイルしか無い時の解決方法

PHPでは適当にPEMファイルをcontextに含めてやりゃー、それでSSL通信ができます。お手軽!

ですが、JavaではPEMファイルだけでは、SSL通信ができません!!(ババーン)

そうは言っても、手元にはPEMファイルしかねーよ!! という方もいるでしょう。そう、私です!!(ババーン)

色々、調べてみた結果、PEMファイルからPKCS#12形式の証明書を作成して、SSL通信をするしか無いようだ。

PEMファイルは200個以上あるんだけど……大丈夫かな。

OpenSSLを使って、PEMからPKCS#12を生成する

この方法が一番メジャーな方法のようだ。SSL接続に使用する証明書が少ない時は、この方法でも良いのだろう。

# certificateとprivate keyが一つのファイルにある場合
openssl pkcs12 -export -in certkey.pem -out certkey.p12 -passsout pas:newpassword -name "newname"

# certificateとprivate keyが別々のファイルにある場合
openssl pkcs12 -export -in cert.pem -inkey key.pem -out certkey.p12 -passsout pas:newpassword -name "newname"

この方法をとる場合は、事前にPEMファイルから、全てPKCS#12形式のファイルに変換しておく必要がある。(もしくは、JavaからRuntimeのexecを使って起動するか。)

PEMファイルは多いし、これからも増えるだろうから、動的にPKCS#12を生成したいにゃー。

外部プログラムの起動には色々問題があるしのー。(そもそも外部プログラム叩いていいなら、PHP叩くわ)

BouncyCastleを使って、PEMからPKCS#12を生成する

BoucyCastleオープンソースの暗号化ライブラリです。

The Legion of the Bouncy Castle
http://www.bouncycastle.org/

これを使って、PEMファイルを読み込んでPKCS#12を生成できる。

PEMReader pr = new PEMReader(new FileReader("cert.pem"));
X509Certificate cert = (X509Certificate) pr.readObject();
PEMReader kr = new PEMReader(new FileReader("privkey.pem"),
        new PasswordFinder() {
    public char[] getPassword() {
        return "passphase".toCharArray();
    }
});
KeyPair key = (KeyPair) kr.readObject();
KeyStore ksKeys = KeyStore.getInstance("JKS");
ksKeys.load(null, "passphase".toCharArray());
ksKeys.setCertificateEntry("MyCert", cert);
ksKeys.setKeyEntry("Mykey", key.getPrivate(),
        "passphase".toCharArray(), new Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
        KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ksKeys, "passphase".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
        TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ksKeys);

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

Socket socket = sslContext.getSocketFactory().createSocket(
        "localhost", 4433);
BufferedReader in = new BufferedReader(new InputStreamReader(
        socket.getInputStream()));
PrintWriter out = new PrintWriter(new OutputStreamWriter(
        socket.getOutputStream()));
out.println("Hello World");
System.out.println(in.readLine());
out.close();
in.close();

とまぁ、こんな感じらしい。

結論

めんどい。

参考

術/Security/PKI,SSL,TLS/メモ01_拡張子の迷宮(pem,der,crt,cer,csr,...)
http://www.glamenv-septzen.net/view/1058

PKCS12 形式のキーストアの作成
http://docs.oracle.com/cd/E19416-01/820-5959/ggfhb/

SSL socket php code needs to be converted to Java
http://stackoverflow.com/questions/722931/ssl-socket-php-code-needs-to-be-converted-to-java

Convert SSL .pem to .p12 with or without OpenSSL
http://stackoverflow.com/questions/9711173/convert-ssl-pem-to-p12-with-or-without-openssl