1概要

このチュートリアルでは、

__KeyStore

__APIを使用してJavaで暗号化キーと証明書を管理する方法を見ています。


2キーストア

Javaで鍵と証明書を管理する必要がある場合は、**

keystore

が必要です。これは、単に鍵と証明書の別名の

entries

の安全なコレクションです。

通常、キーストアをファイルシステムに保存します。パスワードで保護することができます。

デフォルトでは、Javaには


_JAVA

HOME/

_

jre

/lib/security/cacerts

にキーストアファイルがあります。デフォルトのキーストアパスワード

changeit__を使用してこのキーストアにアクセスできます。

さて、そのバックグラウンドを少し使って、最初のものを作成しよう


3キーストアの作成


3.1. 建設


keytool

を使用して簡単にキーストアを作成することも、

KeyStore

APIを使用してプログラムで作成することもできます。

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

ここではデフォルトタイプを使用しますが、

jceks



pcks12

のようなhttps://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyStore[いくつかのキーストアタイプ]があります。


-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);
}

上記に示されていないのは、

getInstance



__load、


および

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. エイリアス

    – 彼は単に私たちが将来使うために使う名前です

エントリーを参照


キー



KeyStore.SecretKeyEntry

にラップされています。


  1. パスワード

    – いわゆる

    ProtectionParam

    に囲まれています.

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

  • パスワードを


    nullにすることはできません、


    空の文字列にすることができます。

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

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


setEntry

は他のエントリタイプにも使用できる汎用メソッドなので、キーをラップします。エントリの種類によって、

KeyStore

APIはそれを別の方法で扱うことができます。


__KeyStore


APIはエンドユーザーからパスワードを収集するためにGUIおよびCLIへのコールバックをサポートしているため、パスワードをラップします。詳細については、https://docs.oracle.com/javase/7/docs/api/java/security/KeyStore.CallbackHandlerProtection.html[

KeyStore.CallbackHandlerProtection__Javadoc]を参照してください。

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


5.2. 秘密鍵を保存する

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

また、

__ KeyStore


APIは、一般的な


setEntry


methodよりも便利な


setKeyEntry

__という専用のメソッドを提供します。

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


  1. エイリアス

    、以前と同じ


  2. 秘密鍵

    一般的な方法を使用していないので、

包まれることはありません。また、私たちの場合、それはインスタンスであるべきです

PrivateKey


。エントリにアクセスするための

パスワード

。今回は、パスワードは

必須
。対応する公開鍵を証明する

証明書チェーン

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

これで、

pwdArray



null

の場合のように、ここで多くのことが間違ってしまうことがあります。

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

しかし、知っておくべき本当に奇妙な例外があります。それは

pwdArray

が空の配列の場合です

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

更新するには、同じエイリアスと新しい

privateKey

および__certificateChainを使用してメソッドをもう一度呼び出すだけです。

また、__https://www.mayrhofer.eu.org/create-x509-certs-in-java[証明書チェーンの生成方法]で簡単に更新してみると便利です。


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

  • 信頼できる証明書を保存するのはとても簡単です。エイリアスと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


simplyは

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"));
}

しかし、キーのパスワードが間違っていると、** 先ほど説明したのと同じ奇妙なエラーが発生します。

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


6.2. キーストアにエイリアス

が含まれているかどうかの確認


KeyStore



Map

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

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<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
    String alias = aliases.nextElement();
    keyStore.deleteEntry(alias);
}


9結論

この記事では、

KeyStore API

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

この例の完全な実装はhttps://github.com/eugenp/tutorials/tree/master/core-java[Githubで公開]にあります。