Javaのキーストア形式:JKSの操作と管理

JKS(Java KeyStore)は、Javaプラットフォーム専用に設計されたキーストア形式です。SSL/TLS通信で使用される秘密鍵と証明書を格納できますが、共通鍵(シークレットキー)の保存には利用できません。JKSファイルは通常「.jks」拡張子を持ち、標準のkeytoolでは内部に格納された秘密鍵を取り出すことはできません。

空のJKSキーストアを作成する

JKSキーストアを作成する最も簡単な方法は、空の状態から新規に生成することです。以下のコードでは、まずKeyStoreインスタンスを取得し、nullをロードして初期化します。その後、指定したパスワードとともにstoreメソッドを呼び出すことで、実際のファイルが作成されます。

import java.io.FileOutputStream;
import java.security.KeyStore;

public class CreateEmptyJKS {
    public static void main(String[] args) {
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(null, null);
            ks.store(new FileOutputStream("sampleKeystore.jks"), "changeit".toCharArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

秘密鍵と自己署名証明書を格納する

JDKの機能を使用して、秘密鍵とその関連する証明書チェーンをキーストアに追加することができます。ただし、証明書チェーンなしに単体の秘密鍵のみを登録することはできません。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import sun.security.x509.*;

public class StorePrivateKey {
    public static void main(String[] args) {
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("sampleKeystore.jks"), "changeit".toCharArray());

            CertAndKeyGen keyGen = new CertAndKeyGen("RSA", "SHA1WithRSA");
            keyGen.generate(1024);

            X509Certificate cert = keyGen.getSelfCertificate(
                new X500Name("CN=TestRoot"), 365L * 24 * 60 * 60);

            X509Certificate[] certChain = {cert};
            
            ks.setKeyEntry("testkeyalias", keyGen.getPrivateKey(), 
                           "keypass".toCharArray(), certChain);

            ks.store(new FileOutputStream("sampleKeystore.jks"), "changeit".toCharArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

証明書のみを格納する

証明書単体も、対応する秘密鍵なしにキーストアへ保存可能です。この場合もX.509形式である必要があります。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import sun.security.x509.*;

public class AddCertificateOnly {
    public static void main(String[] args) {
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("sampleKeystore.jks"), "changeit".toCharArray());

            CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA1WithRSA");
            certGen.generate(1024);

            X509Certificate cert = certGen.getSelfCertificate(
                new X500Name("CN=ExternalCert"), 365L * 24 * 60 * 60);

            ks.setCertificateEntry("externalcert", cert);
            
            ks.store(new FileOutputStream("sampleKeystore.jks"), "changeit".toCharArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

キーストアから証明書チェーンを読み込む

JKSからは秘密鍵そのものを直接抽出することはできませんが、証明書チェーンは取得可能です。

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;

public class LoadCertificateChain {
    public static void main(String[] args) {
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("sampleKeystore.jks"), "changeit".toCharArray());

            Certificate[] certs = ks.getCertificateChain("testkeyalias");
            for (Certificate c : certs) {
                System.out.println(c.toString());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

個別の証明書を読み込む

エイリアスを指定することで、特定の証明書情報を取得できます。

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;

public class LoadSingleCertificate {
    public static void main(String[] args) {
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("sampleKeystore.jks"), "changeit".toCharArray());

            Certificate cert = ks.getCertificate("externalcert");
            System.out.println(cert.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

他のキーストアからのインポート処理

PKCS#12などの形式から証明書を読み込み、JKS形式へ移行することが一般的です。秘密鍵そのものの移行は複雑ですが、公開証明書のコピーは容易に行えます。

import java.io.FileInputStream;
import java.security.KeyStore;

public class ImportFromOtherKeystore {
    public static void main(String[] args) {
        try {
            KeyStore srcKs = KeyStore.getInstance("PKCS12");
            srcKs.load(new FileInputStream("source.p12"), "srcpass".toCharArray());

            KeyStore destKs = KeyStore.getInstance("JKS");
            destKs.load(new FileInputStream("sampleKeystore.jks"), "changeit".toCharArray());

            String alias = "trustedcert";
            java.security.cert.Certificate cert = srcKs.getCertificate(alias);
            destKs.setCertificateEntry(alias, cert);

            // 更新後の保存処理...

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

大文字小文字の区別について

Oracle JDKでは、エイリアスの大文字・小文字を区別するJKS(CaseExactJKS)と、区別しない通常版(JKS)があります。一般的には後者が推奨されており、エイリアス名の一貫性により識別すべきです。

タグ: Java JKS Keystore SSL Cryptography

7月7日 17:40 投稿