Javaプログラムにとって、証明書がインストールされた(信頼済みの)状態にする方法

ブラウザならGUI操作で簡単にインストールできるが、Javaのプログラムの場合はどうするか。Javaプログラム(というかJavaSSL通信クラス)は、SSL通信の際、下記のPATHにあるキーストアファイルを参照し、受信したサーバ証明書がストアに登録されているエントリに含まれるかどうかを持って、信頼済みかどうかを判断する。従って、このキーストアファイルに対して、keytool コマンドを使ってサーバ証明書をimportしてやれば「信頼済み」の状態とすることができる。


デフォルトのキーストアファイルのPATH:
${JAVA_HOME}/jre/lib/security/cacerts


import時のコマンドイメージは以下のとおり。

keytool -import -storepass changeit \
    -keystore ${JAVA_HOME}/jre/lib/security/cacerts \
    -alias [ALIAS] \
    -file [importする証明書ファイル]

登録されているものの一覧を表示したい場合 (-v を加えると詳細表示、-rfcでRFC1421形式で表示)

keytool -list -storepass changeit \
    -keystore ${JAVA_HOME}/jre/lib/security/cacerts

削除する場合

keytool -delete -storepass changeit \
    -keystore ${JAVA_HOME}/jre/lib/security/cacerts \
    -alias [削除するALIAS]

ファイルの形でexportする場合

keytool -export -storepass changeit \
    -keystore ${JAVA_HOME}/jre/lib/security/cacerts \
    -alias [exportするALIAS] \
    -file [出力ファイル名]

自分以外の第三者が立てたサーバの場合

下記のようなプログラムを用いて受信したサーバ証明書をファイルの形で出力し、それをデフォルトキーストアに import すればOK。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509TrustManager;

public class CertificateDownloader
{
  public static SSLContext initSSLContext(final File file)
      throws NoSuchAlgorithmException, KeyManagementException
  {
    SSLContext ssl = SSLContext.getInstance("SSL");
    X509TrustManager[] tm = new X509TrustManager[] { new X509TrustManager() {
      public X509Certificate[] getAcceptedIssuers()
      {
        return null;
      }

      public void checkClientTrusted(X509Certificate[] arg0, String arg)
      {
      }

      public void checkServerTrusted(X509Certificate[] certs, String arg)
      {
        for (int i = 0, length = certs.length; i < length; ++i) {
          try {
            File out = new File(file.getAbsolutePath() + "." + i + ".cer");

            System.out.println("---------------------------------------");
            System.out.println(out.getCanonicalPath());
            System.out.println(certs[i]);
            byte[] b = certs[0].getEncoded();
            FileOutputStream fos = new FileOutputStream(out);
            try {
              fos.write(b);
              fos.flush();
            } finally {
              fos.close();
            }
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    } };
    ssl.init(null, tm, null);
    return ssl;
  }

  public static void exec(String host, File file) throws IOException,
      NoSuchAlgorithmException, KeyManagementException
  {
    exec(host, 443, file);
  }

  public static void exec(String host, int port, final File file)
      throws IOException, NoSuchAlgorithmException, KeyManagementException
  {
    SSLContext ssl = initSSLContext(file);
    SSLSocket s = (SSLSocket) ssl.getSocketFactory().createSocket(host, port);
    try {
      s.startHandshake();
    } finally {
      s.close();
    }
  }

  public static void main(String[] args) throws Exception
  {
    exec("www.oreore.co.jp", new File("f:/tmp/certs/server"));
  }
}