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)があります。一般的には後者が推奨されており、エイリアス名の一貫性により識別すべきです。