keyStoreファイルをプログラムから操作

とりだんご

keytoolコマンドの説明をつらつらと読んでいたら、このコマンドによって行える操作は java.security.KeyStore クラスを叩く事によって実現されているようなことが書いてあった。ほへ〜、すると、Runtime.exec とかみっともない事しなくてもプログラムからキーストアファイルを操作できるわけか。そんなわけで子一時間ほど触ってみて、使い方がなんとなく把握できたのでラッパークラスを作ってみた。

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

public class KeyStoreWrapper
{
  private char[] storePass;

  private File storeFile;

  private KeyStore keyStore;

  ///////////////////////////////////////////////////////////////////////////

  public KeyStoreWrapper() throws IOException, KeyStoreException,
      CertificateException, NoSuchAlgorithmException
  {
    this(getDefaultKeyStorePath(), "changeit");
  }

  public KeyStoreWrapper(File storeFile, String storePass) throws IOException,
      KeyStoreException, CertificateException, NoSuchAlgorithmException
  {
    this.storeFile = storeFile;
    this.storePass = storePass.toCharArray();
    this.keyStore = createKeyStore(this.storeFile, this.storePass);
  }

  ///////////////////////////////////////////////////////////////////////////

  public void save() throws IOException, KeyStoreException,
      CertificateException, NoSuchAlgorithmException
  {
    this.save0(this.storeFile, this.storePass);
  }

  public void save(File storeFile, String storePass) throws IOException,
      KeyStoreException, CertificateException, NoSuchAlgorithmException
  {
    this.save0(this.storeFile, storePass.toCharArray());
  }

  private void save0(File storeFile, char[] storePass) throws IOException,
      KeyStoreException, CertificateException, NoSuchAlgorithmException
  {
    FileOutputStream fos = new FileOutputStream(storeFile);
    try {
      this.keyStore.store(fos, storePass);
    } finally {
      fos.close();
    }
  }

  public void importKey(String alias, File certFile) throws IOException,
      KeyStoreException, CertificateException
  {
    this.keyStore.setCertificateEntry(alias, createX509Certificate(certFile));
  }

  public void importKey(String alias, byte[] certificate) throws IOException,
      KeyStoreException, CertificateException
  {
    this.keyStore.setCertificateEntry(alias,
        createX509Certificate(new ByteArrayInputStream(certificate)));
  }

  public Certificate getKey(String alias) throws KeyStoreException
  {
    return this.keyStore.getCertificate(alias);
  }

  public void exportKey(String alias, File exportFile)
  throws IOException, KeyStoreException, CertificateEncodingException
  {
    FileOutputStream fos = new FileOutputStream(exportFile);
    try
    {
      exportKey(alias, fos);
    }
    finally
    {
      fos.close();
    }
  }

  public void exportKey(String alias, OutputStream out)
  throws IOException, KeyStoreException, CertificateEncodingException
  {
    Certificate cert = this.keyStore.getCertificate(alias);
    out.write(cert.getEncoded());
  }

  public void deleteKey(String alias) throws KeyStoreException
  {
    this.keyStore.deleteEntry(alias);
  }

  public Enumeration aliases() throws KeyStoreException
  {
    return this.keyStore.aliases();
  }

  ///////////////////////////////////////////////////////////////////////////

  private static File getDefaultKeyStorePath()
  {
    return new File(System.getProperty("java.home") + "/lib/security/cacerts");
  }

  private static KeyStore createKeyStore(File keyStorefile, char[] storePass)
      throws IOException, KeyStoreException, CertificateException,
      NoSuchAlgorithmException
  {
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    FileInputStream fis = new FileInputStream(keyStorefile);
    try {
      ks.load(fis, storePass);
      return ks;
    } finally {
      fis.close();
    }
  }

  private static X509Certificate createX509Certificate(File certFile)
      throws IOException, CertificateException
  {
    FileInputStream in = new FileInputStream(certFile);
    try {
      return createX509Certificate(in);
    } finally {
      in.close();
    }
  }

  private static X509Certificate createX509Certificate(InputStream in)
      throws IOException, CertificateException
  {
    CertificateFactory cf = CertificateFactory.getInstance("X509");
    return (X509Certificate) cf.generateCertificate(in);
  }
}

こんな風に使う

    // 読み込む
    KeyStoreWrapper ks = new KeyStoreWrapper();
    // ファイルからimportしてみる
    ks.importKey("tomcat", new File("c:/oreore.cer"));
    // エントリを列挙してみる
    Enumeration e = ks.aliases();
    while(e.hasMoreElements())
    {
      String alias = (String)e.nextElement();
      System.out.println( ks.getKey(alias) );
    }
    // exportしてみる
    ks.exportKey("tomcat",new File("c:/export.cer"));
    // 削除してみる
    ks.deleteKey("tomcat");
    // 保存してみる
    ks.save();

生成、保存、importの部分以外は、あんまり皮かぶせる意味なかったかな……。