JavaキーストアAPI

Java KeyStore API

1. 概要

このチュートリアルでは、KeyStore APIを使用してJavaで暗号化キーと証明書を管理する方法について説明します。

2. キーストア

Javaでキーと証明書を管理する必要がある場合は、keystore, which is simply a secure collection of aliased entries of keys and certificates.が必要です。

通常、キーストアはファイルシステムに保存され、パスワードで保護できます。

デフォルトでは、JavaのキーストアファイルはJAVA_HOME/jre_ / lib / security / cacerts_にあります。 このキーストアには、デフォルトのキーストアパスワードchangeitを使用してアクセスできます。

さて、その少しの背景で、let’s get to creating our first one.

3. キーストアの作成

3.1. 建設

keytoolを使用してキーストアを簡単に作成することも、KeyStoreAPIを使用してプログラムで作成することもできます。

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

ここでは、デフォルトのタイプを使用しますが、jcekspcks12などのa few keystore typesを使用できます。

-Dkeystore.typeパラメータを使用して、デフォルトの「JKS」(Oracle独自のキーストアプロトコル)タイプをオーバーライドできます。

-Dkeystore.type=pkcs12

または、もちろん、サポートされている形式の1つをgetInstanceでリストすることもできます。

KeyStore ks = KeyStore.getInstance("pcks12");

3.2. 初期化

最初に、キーストアをloadする必要があります。

char[] pwdArray = "password".toCharArray();
ks.load(null, pwdArray);

新しいキーストアを作成する場合でも、既存のキーストアを開く場合でも、loadを使用します。

そして、最初のパラメータとしてnullを渡すことにより、KeyStoreに新しいパラメータを作成するように指示します。

また、パスワードを提供します。これは、将来キーストアにアクセスするために使用されます。 これをnullに設定することもできますが、そうすると秘密が公開されます。

3.3. ストレージ

最後に、新しいキーストアをファイルシステムに保存します。

try (FileOutputStream fos = new FileOutputStream("newKeyStoreFileName.jks")) {
    ks.store(fos, pwdArray);
}

上記に示されていないのは、getInstanceload, 、およびstoreがそれぞれスローするいくつかのチェック済み例外です。

4. キーストアのロード

キーストアをロードするには、前と同じように、最初にKeyStoreインスタンスを作成する必要があります。

ただし、今回は既存の形式を読み込んでいるため、形式を指定しましょう。

KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("newKeyStoreFileName.jks"), pwdArray);

JVMが渡したキーストアタイプをサポートしていない場合、または開いているファイルシステムのキーストアのタイプと一致しない場合は、KeyStoreExceptionを取得します。

java.security.KeyStoreException: KEYSTORE_TYPE not found

また、パスワードが間違っていると、UnrecoverableKeyException:が表示されます

java.security.UnrecoverableKeyException: Password verification failed

5. エントリの保存

キーストアには、各エントリがエイリアスの下にある3種類のエントリを保存できます。

  • 対称キー(JCEでは秘密キーと呼ばれます)

  • 非対称キー(JCEでは公開キーと秘密キーと呼ばれます)

  • 信頼できる証明書

それぞれを見てみましょう。

5.1. 対称鍵の保存

キーストアに保存できる最も簡単なものは、対称キーです。

対称鍵を保存するには、次の3つが必要です。

  1. an alias –彼は、エントリを参照するために将来使用する名前です。

  2. a key –これはKeyStore.SecretKeyEntryでラップされます。

  3. a password –これはいわゆるProtectionParamでラップされます。

KeyStore.SecretKeyEntry secret
 = new KeyStore.SecretKeyEntry(secretKey);
KeyStore.ProtectionParameter password
 = new KeyStore.PasswordProtection(pwdArray);
ks.setEntry("db-encryption-secret", secret, password);

Keep in mind that the password cannot be null, however, it can be an empty*String.* エントリにパスワードnull を残すと、KeyStoreException:が取得されます

java.security.KeyStoreException: non-null password required to create SecretKeyEntry

キーとパスワードをラッパークラスでラップする必要があるのは少し奇妙に思えるかもしれません。

setEntryは他のエントリタイプにも使用できる汎用メソッドであるため、キーをラップします。 エントリのタイプにより、KeyStoreAPIで異なる方法で処理できます。

KeyStore APIは、エンドユーザーからパスワードを収集するためのGUIおよびCLIへのコールバックをサポートしているため、パスワードをラップします。 詳細については、the KeyStore.CallbackHandlerProtection Javadocを確認してください。

このメソッドを使用して、既存のキーを更新することもできます。 同じエイリアスとパスワード、および新しいsecret.を使用して再度呼び出す必要があります。

5.2. 秘密鍵の保存

証明書チェーンを扱う必要があるため、非対称キーの保存はもう少し複雑です。

また、KeyStore APIは、一般的なsetEntry メソッドよりも便利なsetKeyEntry という専用メソッドを提供します。

したがって、非対称キーを保存するには、次の4つが必要です。

  1. an alias、以前と同じ

  2. a private key。 ジェネリックメソッドを使用していないため、キーはラップされません。 また、この場合、PrivateKeyのインスタンスである必要があります

  3. エントリにアクセスするためのa password。 今回はパスワードが必須です

  4. 対応する公開鍵を証明するa certificate chain

X509Certificate[] certificateChain = new X509Certificate[2];
chain[0] = clientCert;
chain[1] = caCert;
ks.setKeyEntry("sso-signing-key", privateKey, pwdArray, certificateChain);

もちろん、pwdArraynullの場合のように、ここでは多くの問題が発生する可能性があります。

java.security.KeyStoreException: password can't be null

ただし、注意すべき非常に奇妙な例外があります。それは、pwdArrayが空の配列である場合です。

java.security.UnrecoverableKeyException: Given final block not properly padded

更新するには、同じエイリアスと新しいprivateKeyおよびcertificateChain.を使用してメソッドを再度呼び出すだけです。

また、how to generate a certificate chain.について簡単に復習することも価値があるかもしれません。

5.3. 信頼できる証明書の保存

Storing trusted certificates is quite simple. It only requires the alias and the certificate itself、これはタイプCertificateです:

ks.setCertificateEntry("google.com", trustedCertificate);

通常、証明書は私たちが生成したものではありませんが、サードパーティからのものです。

そのため、KeyStoreは実際にはこの証明書を検証しないことに注意してください。 保存する前に自分で確認する必要があります。

更新するには、同じエイリアスと新しいtrustedCertificateを使用してメソッドを再度呼び出すだけです。

6. エントリを読む

いくつかのエントリを作成したので、必ず読みたいと思います。

6.1. 単一のエントリを読む

最初に、キーと証明書をエイリアスでプルできます。

Key ssoSigningKey = ks.getKey("sso-signing-key", pwdArray);
Certificate google = ks.getCertificate("google.com");

その名前のエントリがない場合、または別のタイプのエントリがある場合、getKey sは単にnullを返します。

public void whenEntryIsMissingOrOfIncorrectType_thenReturnsNull() {
    // ... initialize keystore
    // ... add an entry called "widget-api-secret"

   Assert.assertNull(ks.getKey("some-other-api-secret"));
   Assert.assertNotNull(ks.getKey("widget-api-secret"));
   Assert.assertNull(ks.getCertificate("widget-api-secret"));
}

ただし、キーのパスワードが間違っている場合、we’ll get that same odd error we talked about earlier:

java.security.UnrecoverableKeyException: Given final block not properly padded

6.2. キーストアにエイリアスが含まれているかどうかの確認

KeyStoreMapを使用してエントリを格納するだけなので、エントリを取得せずに存在を確認する機能が公開されます。

public void whenAddingAlias_thenCanQueryWithoutSaving() {
    // ... initialize keystore
    // ... add an entry called "widget-api-secret"
    assertTrue(ks.containsAlias("widget-api-secret"));
    assertFalse(ks.containsAlias("some-other-api-secret"));
}

6.3. エントリーの種類を確認する

または、KeyStore#entryInstanceOfはもう少し強力です。

エントリタイプもチェックすることを除けば、containsAliasに似ています。

public void whenAddingAlias_thenCanQueryByType() {
    // ... initialize keystore
    // ... add a secret entry called "widget-api-secret"
    assertTrue(ks.containsAlias("widget-api-secret"));
    assertFalse(ks.entryInstanceOf(
      "widget-api-secret",
      KeyType.PrivateKeyEntry.class));
}

7. エントリの削除

もちろん、KeyStoreは、追加したエントリの削除をサポートしています。

public void whenDeletingAnAlias_thenIdempotent() {
    // ... initialize a keystore
    // ... add an entry called "widget-api-secret"
    assertEquals(ks.size(), 1);
    ks.deleteEntry("widget-api-secret");
    ks.deleteEntry("some-other-api-secret");
    assertFalse(ks.size(), 0);
}

幸い、deleteEntry はべき等であるため、エントリが存在するかどうかに関係なく、メソッドは同じように反応します。

8. キーストアの削除

キーストアを削除したい場合、APIは役に立ちませんが、Javaを使用してそれを行うことができます。

Files.delete(Paths.get(keystorePath));

または、別の方法として、キーストアを保持して、エントリを削除することもできます。

Enumeration aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
    String alias = aliases.nextElement();
    keyStore.deleteEntry(alias);
}

9. 結論

この記事では、KeyStore APIを使用した証明書とキーの管理について説明しました。 キーストアとは何か、キーストアを作成、ロード、削除する方法、キーストアにキーまたは証明書を保存する方法、既存のエントリを新しい値でロードおよび更新する方法について説明しました。

例の完全な実装はover on Githubにあります。