Javaプラットフォームでは、暗号化操作を実行するための一連のプログラミング・インタフェースを定義しています。これらのインタフェースは、総称してJava暗号化アーキテクチャ(JCA)およびJava暗号化拡張機能(JCE)と呼ばれています。Java暗号化アーキテクチャ(JCA)リファレンス・ガイドを参照してください。
暗号化インタフェースはプロバイダ・ベースです。具体的には、アプリケーションはアプリケーション・プログラミング・インタフェース(API)とやり取りをし、実際の暗号化操作は、一連のサービス・プロバイダ・インタフェース(SPI)に従った構成済みプロバイダ内で実行されます。このアーキテクチャでは、プロバイダのさまざまな実装をサポートしています。ソフトウェアで暗号化操作を行うプロバイダもあれば、スマートカード・デバイスやハードウェア暗号化アクセラレータなどのハードウェア・トークン上で暗号化操作を行うプロバイダもあります。
暗号化トークン・インタフェース標準であるPKCS#11は、RSA Securityが策定し、ハードウェア暗号化アクセラレータやスマートカードなどの暗号化トークンに対するネイティブ・プログラミング・インタフェースを定義しています。JCAおよびJCE APIを使用する既存のアプリケーションでは、PKCS#11プロバイダを使用してネイティブPKCS#11トークンにアクセスできます。アプリケーションへの変更は必要ありません。必要なのは、プロバイダを正しく構成することのみです。
アプリケーションは既存のAPIを使用してPKCS#11機能のほとんどを利用できますが、柔軟性や機能性をさらに必要とするアプリケーションもあります。たとえば、アプリケーションで、動的に抜き差しするスマートカードをより簡単に扱えるようにする必要がある場合があります。また、PKCS#11トークンで鍵とは関係ない一部の操作を認証する必要があるため、アプリケーションはキーストアを使用せずにトークンにログインできるようにする必要があります。JCAにより、アプリケーションが様々なプロバイダを扱う際の柔軟性が向上します。
このドキュメントでは、ネイティブPKCS#11トークンをJavaアプリケーションで使用できるように、Javaプラットフォームに構成する方法を説明します。また、JCAによってPKCS#11プロバイダを始めとする各種プロバイダがアプリケーションでどのように扱いやすくなるかについても説明します。
その他の多くのプロバイダとは異なり、SunPKCS11プロバイダ自体は暗号化アルゴリズムを実装していません。その代わりに、Java JCAおよびJCE APIとネイティブPKCS#11暗号化APIとの間のブリッジとして動作し、これらの間の呼び出しと規則を変換します。
つまり、標準JCAおよびJCE APIを呼び出すJavaアプリケーションなら、アプリケーションを変更しなくても、基盤となる次のようなPKCS#11実装で提供されるアルゴリズムを利用できるということです。
注意:
Java SEは、ネイティブPKCS#11実装へのアクセスを容易にするのみであり、それ自体にはネイティブPKCS#11実装は含まれていません。ただし、スマート・カードやハードウェア・アクセラレータなどの暗号化デバイスには、PKCS#11実装を含んだソフトウェアが付属していることが一般的で、製造元の指示に従ってインストールし、構成する必要があります。SunPKCS11プロバイダでは、PKCS#11 v2.20以降の実装がシステムにインストールされている必要があります。この実装は、共有オブジェクト・ライブラリ(SolarisおよびLinuxでの.so)またはダイナミック・リンク・ライブラリ(Windowsでの.dll)の形態である必要があります。使用する暗号化デバイスにこのようなPKCS#11実装が含まれているかどうかを調べる方法、実装を構成する方法、およびライブラリ・ファイルのファイル名については、ベンダーが提供するマニュアルを参照してください。
SunPKCS11プロバイダでは、基盤となるPKCS#11実装で提供されている場合は、多くのアルゴリズムがサポートされています。それらのアルゴリズムとその対応するPKCS#11メカニズムを、SunPKCS11プロバイダでサポートされているアルゴリズムで表に示します。
SunPKCS11プロバイダは、モジュールjdk.crypto.cryptokiにあります。このプロバイダを使用するには、まずそれを静的にまたはプログラムでインストールする必要があります。
このプロバイダを静的にインストールするには、Javaセキュリティのプロパティ・ファイル(java-home/conf/security/java.security)にプロバイダを追加します。
たとえば次は、SunPKCS11プロバイダを構成ファイル/opt/bar/cfg/pkcs11.cfgとともにインストールするjava.securityファイルの一部です。
# configuration for security providers 1-12 omitted security.provider.13=SunPKCS11 /opt/bar/cfg/pkcs11.cfg
このプロバイダを動的にインストールするには、適切な構成ファイル名を使用してプロバイダのインスタンスを作成し、インストールします。次に例を示します。
String configName = "/opt/bar/cfg/pkcs11.cfg";
Provider p = Security.getProvider("SunPKCS11");
p = p.configure(configName);
Security.addProvider(p);
注意:
この例で示しているように、configureメソッドから返されたプロバイダオブジェクトを保存した後に、そのオブジェクトを追加します。
p = p.configure(configName);
Security.addProvider(p);
configureメソッドの呼び出し元のプロバイダを追加しないでください。
p.configure(configName);
Security.addProvider(p);
このプロバイダがインプレースで構成できない場合は、新しいプロバイダが作成され、返されます。したがって、configureメソッドから返されるプロバイダを常に使用します。
PKCS#11実装あたり複数のスロットを使用する場合や、複数のPKCS#11実装を使用する場合は、適切な構成ファイルでそれぞれのインストールを繰り返すだけです。これにより、それぞれのPKCS#11実装の各スロットに対してSunPKCS11プロバイダのインスタンスが1つ作成されることになります。
構成ファイルはテキスト・ファイルであり、これには次の形式でエントリが含まれています。
attribute=valueattributeとvalueの有効な値については、この項内の表で説明しています。
2つある必須属性は、nameおよびlibraryです。次に、構成ファイルの例を示します。
name = FooAccelerator library = /opt/foo/lib/libpkcs11.so
コメントは、# (シャープ)記号で始まる行に記述します。
表5-1 PKCS#11プロバイダの構成ファイル内の属性
| 属性 | 値 | 説明 |
|---|---|---|
| library | PKCS#11実装のパス名 | PKCS#11実装のフル・パス名(拡張子を含む)。パス名の書式はプラットフォームに依存します。たとえば、SolarisおよびLinuxではPKCS#11実装のパス名が/opt/foo/lib/libpkcs11.soなどになりますが、Windowsでのパス名はC:\foo\mypkcs11.dllなどになります。 |
| name | このプロバイダ・インスタンスの名前接尾辞 | この文字列は、接頭辞SunPKCS11-と連結して、このプロバイダ・インスタンスの名前(つまり、プロバイダ・インスタンスのProvider.getName()メソッドで返される文字列)を生成します。たとえばname属性がFooAcceleratorの場合、プロバイダ・インスタンスの名前はSunPKCS11-FooAcceleratorになります。 |
| description | このプロバイダ・インスタンスの説明 | この文字列は、プロバイダ・インスタンスのProvider.getInfo()メソッドで返されます。何も指定されていない場合、デフォルトの説明が返されます。 |
| slot | スロットのID | このプロバイダ・インスタンスが関連付けされているスロットのID。たとえば、PKCS#11のID 1のスロットでは、1を使用します。指定できるのは、多くともslotかslotListIndexのどちらか1つです。どちらも指定しない場合のデフォルトは、値0のslotListIndexです。 |
| slotListIndex | スロットのインデックス | このプロバイダ・インスタンスが関連付けされているスロットのインデックス。これは、PKCS#11の関数C_GetSlotListで返されるすべてのスロットのリストに対するインデックスです。たとえば、0はリストの先頭のスロットを表します。指定できるのは、多くともslotかslotListIndexのどちらか1つです。どちらも指定しない場合のデフォルトは、値0のslotListIndexです。 |
| enabledMechanisms | 有効にするPKCS#11メカニズムのリスト。空白文字で区切り、全体を中カッコ()で囲む | これは、SunPKCS11プロバイダとPKCS#11トークンの両方でサポートされている場合に、このプロバイダ・インスタンスで使用する必要があるPKCS#11メカニズムのリストです。その他のメカニズムはすべて無視されます。リスト内の各エントリは、PKCS#11メカニズムの名前になります。2つのPKCS#11メカニズムから成るリストの例を次に示します。 enabledMechanisms = {
CKM_RSA_PKCS
CKM_RSA_PKCS_KEY_PAIR_GEN
}
指定できるのは、多くともenabledMechanismsかdisabledMechanismsのどちらか1つです。どちらも指定しない場合、有効なメカニズムは、SunPKCS11プロバイダ(SunPKCS11プロバイダでサポートされているアルゴリズムを参照)とPKCS#11トークンの両方でサポートされているメカニズムになります。 |
| disabledMechanisms | 無効にするPKCS#11メカニズムのリスト。空白文字で区切り、全体を中カッコ()で囲む | このプロバイダ・インスタンスが無視するPKCS#11メカニズムのリスト。リストされたあらゆるメカニズムは、トークンとSunPKCS11プロバイダでサポートされていても、プロバイダによって無視されます。これらのサービスを無効にするために文字列SecureRandomおよびKeyStoreを指定できます。 指定できるのは、多くとも |
| attributes | 後述の説明を参照してください | attributesオプションは、PKCS#11鍵オブジェクトの作成時に設定される追加のPKCS#11属性を指定するために使用します。これにより、特定の属性を必要とするトークンを使用できるようになります。詳細は、次のセクションを参照してください。 |
属性の構成
attributesオプションは、PKCS#11鍵オブジェクトの作成時に設定される追加のPKCS#11属性を指定するために使用できます。デフォルトでSun PKCS#11プロバイダでは、オブジェクトの作成時に必須のPKCS#11属性を指定するだけです。たとえばRSA公開鍵では、鍵タイプおよびアルゴリズム(CKA_CLASSおよびCKA_KEY_TYPE)と、RSA公開鍵の鍵の値(CKA_MODULUSおよびCKA_PUBLIC_EXPONENT)を指定します。使用しているPKCS#11ライブラリでは、実装固有のデフォルト値をRSA公開鍵のその他の属性に割り当てます。たとえば、メッセージの暗号化と検証に鍵を使用できます(CKA_ENCRYPTおよびCKA_VERIFY = true)。
attributesオプションは、PKCS#11実装で割り当てたデフォルト値を使用しない場合や、PKCS#11実装でデフォルト値をサポートしないために、明示的に値を指定する必要がある場合に使用します。使用しているPKCS#11実装でサポートしない属性や、該当する鍵タイプで無効な属性を指定すると、実行時に操作が失敗する原因となります。
オプションは0回または複数回指定できます。また、次に説明するように、構成ファイルで指定した順番でオプションが処理されます。attributesオプションは次の書式です。
attributes(operation, keytype, keyalgorithm) = {
name1 = value1
[...]
}
operationの有効な値は次のとおりです。
generate。KeyPairGeneratorまたはKeyGeneratorによって生成された鍵用import。KeyFactoryまたはSecretKeyFactoryによって作成された鍵用。暗号化操作の初期化メソッド(Signature.initSign()など)に渡されるときにPKCS#11鍵オブジェクトに自動的に変換されるJava Software鍵にも適用される。*。生成操作または作成操作のどちらかで作成された鍵用。 keytypeの有効な値は、CKO_PUBLIC_KEY、CKO_PRIVATE_KEY、およびCKO_SECRET_KEYで、それぞれ公開鍵、非公開鍵、および秘密鍵に対応しています。また、任意の鍵タイプに一致する*もあります。
keyalgorithmの有効な値は、PKCS#11仕様に定義されたCKK_xxx定数のいずれか1つ、または任意の鍵アルゴリズムに一致する*です。SunPKCS11プロバイダで現在サポートしているアルゴリズムは、CKK_RSA、CKK_DSA、CKK_DH、CKK_AES、CKK_DES、CKK_DES3、CKK_RC4、CKK_BLOWFISH、CKK_GENERIC_SECRETおよびCKK_ECです。
属性の名前と値は、1つまたは複数の名前と値のペアのリストとして指定されます。nameはPKCS#11仕様のCKA_xxx定数(CKA_SENSITIVEなど)でなければなりません。valueは、次のいずれかになります。
trueまたはfalse0xで始まる16進数表記。null。この属性はオブジェクトの作成時に指定してはならないことを示す。 attributesオプションを複数回指定すると、エントリは、まとめられた属性で指定された順序で、あとの属性が前の属性をオーバーライドして処理されます。たとえば、次の構成ファイルを考えてみます。
attributes(*,CKO_PRIVATE_KEY,*) = {
CKA_SIGN = true
}
attributes(*,CKO_PRIVATE_KEY,CKK_DH) = {
CKA_SIGN = null
}
attributes(*,CKO_PRIVATE_KEY,CKK_RSA) = {
CKA_DECRYPT = true
}
1番目のエントリでは、すべての非公開鍵に対してCKA_SIGN = trueを指定しています。2番目のオプションでは、Diffie-Hellman非公開鍵についてnullでオーバーライドするため、CKA_SIGN属性はDiffie-Hellman非公開鍵に対してまったく指定されません。最後に、3番目のオプションでは、RSA非公開鍵に対してCKA_DECRYPT = trueを指定しています。つまりRSA非公開鍵には、CKA_SIGN = trueとCKA_DECRYPT = true両方のセットがあることになります。
attributesオプションには特殊な形式もあります。構成ファイルにattributes = compatibilityと書くことができます。これは、属性ステートメントのセット全体に対するショートカットです。これはプロバイダが既存のJavaアプリケーションとの互換性を最大限に保つことを目的としています。これにより、Javaアプリケーションでは、たとえばすべての鍵コンポーネントにアクセス可能で、秘密鍵を暗号化および復号化の両方に使用可能であることが期待されます。compatibility属性の行は、他のattributes行とともに使用できます。この場合は先に説明したような、同様のアグリゲーションおよびオーバーライドの規則が適用されます。
ネットワーク・セキュリティ・サービス(NSS)は一連のオープン・ソース・セキュリティ・ライブラリであり、その暗号化APIは、PKCS#11に基づいていますが、PKCS#11標準ではない特別な機能を含んでいます。SunPKCS11プロバイダには、NSS固有の機能(複数のNSS固有の構成ディレクティブなど)と相互に作用するためのコードが含まれています。これらについては次で説明します。
最適な結果を得るために、使用可能な最新バージョンのNSSを使用することをお薦めします。少なくともバージョン3.12になります。
次に説明されているnss構成ディレクティブのいずれかが使用されている場合、SunPKCS11プロバイダはNSS固有のコードを使用します。この場合、通常の構成コマンドlibrary、slot、およびslotListIndexは使用できません。
表5-2 NSSの属性と値
| 属性 | 値 | 説明 |
|---|---|---|
| nssLibraryDirectory | NSSおよびNSPRライブラリを含むディレクトリ | NSSおよびNSPRライブラリを含むディレクトリのフル・パス名。Java VMとして同じプロセス内で実行されている別のコンポーネントによってNSSがすでにロードおよび初期化されていないかぎり、この属性を指定する必要があります。 プラットフォームに応じて、このディレクトリを含めるために |
| nssSecmodDirectory | NSS DBファイルを含むディレクトリ | NSS構成および鍵情報(secmod.db、key3.db、およびcert8.db)を含むディレクトリのフル・パス名。別のコンポーネントによってNSSがすでに初期化されていないか(上記参照)、または次に説明されているようにデータベース・ファイルなしでNSSが使用されないかぎり、この指示を指定する必要があります。 |
| nssDbMode | readWrite、readOnly、noDbのいずれか |
この指示は、NSSデータベースへのアクセス方法を決定します。読書きモードではフル・アクセスが可能ですが、データベースには一度に1つのプロセスのみがアクセスする必要があります。読取り専用モードでは、このファイルの変更は許可されません。 noDbモードを使用すると、データベース・ファイルなしで純粋に暗号化プロバイダとしてNSSを使用できます。PKCS11 KeyStoreを使用して持続的な鍵を作成することはできません。 |
| nssModule | keystore、crypto、fips、trustanchorsのいずれか |
さまざまなライブラリやスロットを使用してNSSの機能が使用できるようになっています。この指示は、SunPKCS11のインスタンスがアクセスするモジュールを決定します。
FIPS-140準拠モードに対してNSSの
信頼できるアンカー・ライブラリを含むように |
例5-1 NSS用SunPKCS11構成ファイル
純粋な暗号化プロバイダとしてのNSS
name = NSScrypto nssLibraryDirectory = /opt/tests/nss/lib nssDbMode = noDb attributes = compatibility
FIPS 140準拠の暗号化トークンとしてのNSS
name = NSSfips nssLibraryDirectory = /opt/tests/nss/lib nssSecmodDirectory = /opt/tests/nss/fipsdb nssModule = fips
PKCS#11に問題が発生し、デバッグが必要になることがあります。ライブラリ、スロット、トークンおよびメカニズムのデバッグ情報を表示するには、SunPKCS11プロバイダ構成ファイル(<java-home>/conf/security/sunpkcs11-solaris.cfg)でshowInfo=trueを追加するか、SunPKCS11構成で説明しているように静的または動的に指定した構成ファイルを追加します。
その他のデバッグ情報については、次のいずれかのオプションを指定してJavaプロセスを起動または再起動してください。
SunPKCS11プロバイダの一般的なデバッグ情報:
-Djava.security.debug=sunpkcs11
PKCS#11キーストア固有のデバッグ情報の場合:
-Djava.security.debug=pkcs11keystore
トラブルシューティング・プロセスの一環として、PKCS#11プロバイダ、または特定のプロバイダに固有のメカニズムを一時的に無効にすると便利な場合があります。
PKCS#11プロバイダの無効化
PKCS#11プロバイダは、次のいずれかの方法で無効にできます。
単一のJavaプロセスに対してPKCS#11を無効にします。次のJavaコマンド行フラグを使用してJavaプロセスを起動または再起動します。
-Dsun.security.pkcs11.enable-solaris=false
注意:
このステップは、デフォルトのSolaris PKCS#11プロバイダ・ファイル(<java_home>/conf/security/sunpkcs11-solaris.cfg)に基づくSunPKCS11プロバイダにのみ適用されます。特定のJavaインストールで実行されるすべてのJavaプロセスについてPKCS#11を無効にします。これは、APIを使用して動的に実行するか(この項には記載されていません)、次に示すように<java_home>/conf/security/java.securityファイルを編集してSunPKCS11セキュリティ・プロバイダをコメント・アウトすることにより静的に実行できます(必要な場合はプロバイダの順序の番号を変更することを忘れないでください)。
#
# List of providers and their preference orders (see above):
#
security.provider.1=SUN
security.provider.2=SunRsaSign
security.provider.3=SunEC
security.provider.4=SunJSSE
security.provider.5=SunJCE
security.provider.6=SunJGSS
security.provider.7=SunSASL
security.provider.8=XMLDSig
security.provider.9=SunPCSC
security.provider.10=JdkLDAP
security.provider.11=JdkSASL
security.provider.12=SunMSCAPI
#security.provider.13=SunPKCS11
このJavaのインストールで実行されているJavaプロセスを起動または再起動します。
特定のメカニズムの無効化
PKCS#11のメカニズムのいずれかで問題が発生した場合、PKCS#11プロバイダ全体ではなく、その特定のメカニズムのみを無効にすることにより、問題を解決できます(それまでにPKCS#11プロバイダを無効にしていた場合は、再度有効にすることを忘れないでください)。
注意:
PKCS#11 SecureRandom実装のみを無効にするには、<java-home>/conf/security/sunpkcs11-solaris.cfgファイル内の無効なメカニズムのリストにSecureRandomを追加します。
name = Solaris
description = SunPKCS11 accessing Solaris Cryptographic Framework
library = /usr/lib/$ISA/libpkcs11.so
handleStartupErrors = ignoreAll
# Use the X9.63 encoding for EC points (do not wrap in an ASN.1 OctetString).
useEcX963Encoding = true
attributes = compatibility
disabledMechanisms = {
CKM_DSA_KEY_PAIR_GEN
SecureRandom
}
Javaアプリケーションは、既存のJCAおよびJCE APIを使用して、SunPKCS11プロバイダ経由でPKCS#11トークンにアクセスできます。
個人識別番号を使用してキーストアにログインし、PKCS#11の操作を実行できます。
秘密鍵へのアクセスなど一部のPKCS#11操作では、その操作の実行前に、PIN (個人識別番号)を使用してログインする必要があります。ログインが必要となる操作でもっとも多いのはトークン上の鍵を扱う操作です。Javaアプリケーションではそのような操作で、最初にキーストアをロードするのが一般的です。java.security.KeyStoreクラス経由でキーストアとしてPKCS#11トークンにアクセスするときは、loadメソッドに対してパスワード入力パラメータでPINを指定できます。PINは、その後、トークンにログインするために、SunPKCS11プロバイダが使用します。次に例を示します。
char[] pin = ...;
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, pin);
この例は、静的なキーストアとしてPKCS#11トークンを扱うアプリケーションに適しています。抜き差しされるスマートカードなど、アプリケーションでPKCS#11トークンをより動的に利用する必要がある場合は、新しいKeyStore.Builderクラスを使用します。コールバック・ハンドラでPKCS#11キーストアのビルダーを初期化する方法を次の例に示します。
KeyStore.CallbackHandlerProtection chp =
new KeyStore.CallbackHandlerProtection(new MyGuiCallbackHandler());
KeyStore.Builder builder =
KeyStore.Builder.newInstance("PKCS11", null, chp);
SunPKCS11プロバイダの場合、コールバック・ハンドラがPasswordCallbackに対応できる必要があります。PasswordCallbackは、ユーザーにPINを要求するために使用されます。アプリケーションがキーストアにアクセスしなければならない場合は、ビルダーを次のように使用します。
KeyStore ks = builder.getKeyStore();
Key key = ks.getKey(alias, null);
ビルダーは、先に構成されたコールバック・ハンドラで使用するパスワードを必要に応じてユーザーに求めます。ビルダーがパスワードを要求するのは、初回のアクセス時のみです。アプリケーションのユーザーが同じスマート・カードを使用し続ける場合は、もう一度パスワードを要求されることはありません。ユーザーがスマートカードを抜いて、別のスマートカードを差した場合は、新しいカードに対するパスワードを要求されます。
PKCS#11トークンによっては、鍵とは関係ない操作でもトークン・ログインを必要とする場合があります。そのような操作を使用するアプリケーションでは、java.security.AuthProviderクラスを使用できます。AuthProviderクラスは、java.security.Providerを拡張し、プロバイダでログインおよびログアウト操作を行なったり、プロバイダが使用するコールバック・ハンドラを設定したりするためのメソッドを定義しています。
SunPKCS11プロバイダの場合、コールバック・ハンドラがPasswordCallbackに対応できる必要があります。PasswordCallbackは、ユーザーにPINを要求するために使用されます。
次に、アプリケーションでAuthProviderを使用してトークンにログインする方法の例を示します。(SunPKCS11プロバイダは使用する前に構成する必要があることに注意してください。)
Provider p = Security.getProvider("SunPKCS11");
AuthProvider aprov = (AuthProvider)p.configure(<provider configuration file>);
aprov.login(subject, new MyGuiCallbackHandler());
JavaのKeyオブジェクトには、実際の鍵データが含まれる場合も含まれない場合もあります。
アプリケーションおよびプロバイダでは、各種のKeyオブジェクトを表すために正しいインタフェースを使用する必要があります。ソフトウェアKeyオブジェクト(または実際の鍵データにアクセスできる任意のKeyオブジェクト)では、java.security.interfacesおよびjavax.crypto.interfacesパッケージにインタフェース(DSAPrivateKeyなど)を実装する必要があります。抽出不可能なトークン鍵を表すKeyオブジェクトでは、java.securityおよびjavax.cryptoパッケージに、関連するジェネリック・インタフェース(PrivateKey、PublicKeyまたはSecretKey)のみを実装する必要があります。鍵アルゴリズムの特定は、Key.getAlgorithm()メソッドを使用して実行されます。
抽出不可能なトークン鍵のKeyオブジェクトはそのトークンに関連付けられたプロバイダのみが使用できるということに注意してください。
Cipher.getInstance("AES")などのJava暗号化getInstance()メソッドは、要求されたアルゴリズムを実装している最初のプロバイダからの実装を返します。ただし、JDKは、関連する初期化メソッドが呼び出されるまでプロバイダの選択を遅らせます。初期化メソッドはKeyオブジェクトを受け入れ、その時点で、指定したKeyオブジェクトを受け入れられるプロバイダを判断できます。これにより、選択したプロバイダで、指定したKeyオブジェクトを使用できることが保証されます。(アプリケーションが、ソフトウェアKeyオブジェクトのみを使用できるプロバイダで、抽出不可能なトークン鍵のKeyオブジェクトを使用しようとした場合、プロバイダはInvalidKeyExceptionをスローします。これは、Cipher、KeyAgreement、Macおよび、Signatureクラスの問題です。次に、影響のある初期化メソッドを示します。
Cipher.init(..., Key key, ...)KeyAgreement.init(Key key, ...)Mac.init(Key key, ...)Signature.initSign(PrivateKey privateKey)また、アプリケーションが初期化メソッドを複数回呼び出すと(毎回異なる鍵を使用するなど)、その鍵に適切なプロバイダが毎回選択されます。つまり、初期化の呼び出しごとに異なるプロバイダが選択される可能性があります。
このプロバイダの遅延選択は、アプリケーションからは認識されませんが、Cipher、KeyAgreement、Mac、およびSignatureのgetProvider()メソッドの動作に影響します。getProvider()が初期化操作の発生する前 (したがって、プロバイダ選択が起こる前)に呼び出された場合は、要求されるアルゴリズムがサポートされている最初のプロバイダが返されます。このプロバイダは、初期化メソッドが呼び出されたあとに選択されたプロバイダと同じにならない場合があります。getProvider()が初期化操作のあとに呼び出された場合は、実際に選択されたプロバイダが返されます。アプリケーションでは関連する初期化メソッドを呼び出したあとにのみ、getProvider()を呼び出すようにしてください。
getProvider()だけでなく、次のメソッドにも同様の影響があります。
Cipher.getBlockSizeCipher.getExcemptionMechanismCipher.getIVCipher.getOutputSizeCipher.getParametersMac.getMacLengthSignature.getParametersSignature.setParameterJDKには、JAASキーストア・ログイン・モジュールであるKeyStoreLoginModuleが付属しています。このモジュールを使用すると、アプリケーションで、指定したキーストア内のIDを使用して認証を行うことができます。アプリケーションは認証後に、自身のプリンシパルおよびクレデンシャル情報(証明書および秘密鍵)をキーストアから取得します。このログイン・モジュールを使用し、PKCS#11トークンをキーストアとして使用するように構成すると、アプリケーションはこの情報をPKCS#11トークンから取得できるようになります。
次のオプションを使用してKeyStoreLoginModuleを構成すると、PKCS#11トークンをキーストアとして使用できます。
keyStoreURL="NONE"keyStoreType="PKCS11"keyStorePasswordURL=some_pin_url説明は次のとおりです。
keyStorePasswordURLオプションを省略すると、ログイン・モジュールはPINをアプリケーションのコールバック・ハンドラから取得し、PasswordCallback でそのPINを指定します。PKCS#11トークンをキーストアとして使用する構成ファイルの例を次に示します。 other {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="NONE"
keyStoreType="PKCS11"
keyStorePasswordURL="file:/home/joe/scpin";
};
複数のSunPKCS11プロバイダを動的に構成した場合、またはjava.securityセキュリティ・プロパティ・ファイル内で構成した場合は、keyStoreProviderオプションを使用して、特定のプロバイダ・インスタンスを対象にします。このオプションの引数は、プロバイダの名前です。SunPKCS11プロバイダの場合、プロバイダ名はSunPKCS11-TokenNameという形式になります。ここで、TokenNameはプロバイダ・インスタンスが構成された名前の接尾辞です。詳細は、表5-1を参照してください。たとえば、次の構成ファイルでは、PKCS#11プロバイダ・インスタンスに名前接尾辞SmartCardで名前を付けています。
other {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="NONE"
keyStoreType="PKCS11"
keyStorePasswordURL="file:/home/joe/scpin"
keyStoreProvider="SunPKCS11-SmartCard";
};
保護された認証パスを介してのログインをサポートしているPKCS#11トークンもあります。たとえばスマートカードには、PINを入力するための専用のPINパッドがある場合があります。生体測定機器にも、認証情報を取得する専用の方法が用意されています。PKCS#11トークンに保護された認証パスがある場合は、protected=trueオプションを使用し、keyStorePasswordURLオプションは省略します。そのようなトークン用の構成ファイルの例を次に示します。
other {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="NONE"
keyStoreType="PKCS11"
protected=true;
};
PKCS#11トークンをJSSEキーストアまたはトラスト・ストアとして使用できるように、JSSEアプリケーションでは、トークン・ログインで説明したAPIを使用してKeyStoreをインスタンス化します。このKeyStoreは、PKCS#11トークンによって戻され、キー・マネージャおよびトラスト・マネージャに渡されます。こうしてJSSEアプリケーションでは、トークン上の鍵にアクセスできるようになります。
JSSEでは、システム・プロパティ経由でキーストアおよびトラスト・ストアを使用するように構成できます(Java Secure Socket Extension (JSSE)リファレンス・ガイドを参照)。PKCS#11トークンをキーストアまたはトラスト・ストアとして使用するには、javax.net.ssl.keyStoreTypeおよびjavax.net.ssl.trustStoreTypeシステム・プロパティをそれぞれ「PKCS11」に設定し、javax.net.ssl.keyStoreおよびjavax.net.ssl.trustStoreシステム・プロパティをそれぞれ「NONE」に設定します。特定のプロバイダ・インスタンスを使用するように指定するには、javax.net.ssl.keyStoreProviderおよびjavax.net.ssl.trustStoreProviderシステム・プロパティを使用します(例: SunPKCS11-SmartCard)。
SunPKCS11プロバイダがjava.securityセキュリティ・プロパティ・ファイル(Javaランタイムの$JAVA_HOME/conf/securityディレクトリ内にある)で構成されている場合、次のオプションを使用することで、PKCS#11トークンの操作にkeytoolおよびjarsignerを使用できます。
-keystore NONE-storetype PKCS11keytool -keystore NONE -storetype PKCS11 -listPINは
-storepassオプションで指定できます。指定されない場合、keytoolおよびjarsignerは、トークンPINを要求します。トークンに保護された認証パス(専用のPINパッドや生体読取り機など)がある場合、-protectedオプションを指定する必要がありますが、パスワード・オプションを指定する必要はありません。java.securityセキュリティ・プロパティ・ファイル内で複数のSunPKCS11プロバイダが構成されている場合、-providerNameオプションを使用して特定のプロバイダ・インスタンスを選択できます。このオプションの引数は、プロバイダの名前です。
-providerName providerName SunPKCS11プロバイダの場合、providerNameの形式はSunPKCS11-TokenNameのようになります。この説明を次に示します。
SmartCardのPKCS#11キーストア・プロバイダ・インスタンスの内容をリストします。 keytool -keystore NONE -storetype PKCS11 \
-providerName SunPKCS11-SmartCard \
-list
SunPKCS11プロバイダをjava.securityセキュリティ・プロパティ・ファイル内で構成していない場合は、次のオプションを使用して、プロバイダを動的にインストールするようにkeytoolおよびjarsignerを設定します。
-providerClass sun.security.pkcs11.SunPKCS11-providerArg ConfigFilePathjava.securityファイルで構成されていないときにPKCS#11キーストアをリストするコマンドの例を示します。keytool -keystore NONE -storetype PKCS11 \
-providerClass sun.security.pkcs11.SunPKCS11 \
-providerArg /foo/bar/token.config \
-list
注意:
ポリシー・ツールは、JDK 9では推奨されていません。
デフォルト・ポリシー実装内のkeystoreエントリには、次の構文があります。これには、PINおよび複数のPKCS#11プロバイダ・インスタンスを指定できます。
keystore "some_keystore_url", "keystore_type", "keystore_provider"; keystorePasswordURL "some_password_url";
説明は次のとおりです。
"SunPKCS11-SmartCard")。keystorePasswordURLの行が指定されていない場合、パスワードは使用されません。 例5-2 PKCS#11トークンのためのキーストア・ポリシー・エントリ
PKCS#11トークンのキーストア・ポリシー・エントリの例を次に示します。
keystore "NONE", "PKCS11", "SunPKCS11-SmartCard"; keystorePasswordURL "file:/foo/bar/passwordFile";
java.security.Providerクラスを使用すると、プロバイダ開発者は、プロバイダ・サービスおよびパラメータ・サポートによって、より簡単にPKCS#11トークンおよび暗号化サービスをサポートできます。
プロバイダ・サービスおよびパラメータ・サポートを示すように設計された単純なプロバイダの例は、プロバイダの例を参照してください。
プロバイダによるサービス実装ごとに、サービスの型(Cipher、Signatureなど)、ピリオド、およびサービスが適用されるアルゴリズム名で構成される名前のプロパティが必要です。プロパティの値には、サービスを実装するクラスの完全修飾名を指定する必要があります。値com.sun.crypto.provider.DHKeyAgreementを持つようにKeyAgreement.DiffieHellmanプロパティを設定するプロバイダの例を次に示します。
put("KeyAgreement.DiffieHellman", "com.sun.crypto.provider.DHKeyAgreement")
public staticのネストされたクラスProvider.Serviceは、プロバイダ・サービスのプロパティ(型、属性、アルゴリズム名、アルゴリズムの別名など)をカプセル化します。プロバイダはProvider.putService()メソッドを呼び出すことで、Provider.Serviceオブジェクトをインスタンス化して登録できます。これは、Propertyエントリを作成してProvider.put()メソッドを呼び出すことと同じです。Provider.put経由で登録されたレガシーのPropertyエントリも引き続きサポートされています。
クラスcom.sun.crypto.provider.DHKeyAgreementによって実装され、DiffieHellmanアルゴリズムを使用する、KeyAgreement型のServiceオブジェクトを作成するプロバイダの例を次に示します。
Service s = new Service(this, "KeyAgreement", "DiffieHellman",
"com.sun.crypto.provider.DHKeyAgreement", null, null);
putService(s);
旧バージョンのPropertyエントリのかわりにProvider.Serviceオブジェクトを使用すると、大きな利点が2つあります。1つ目として、エンジン・クラスをインスタンス化するときに、プロバイダの柔軟性が向上します。2つ目として、プロバイダでパラメータ・サポートをテストできます。次に、これらの特徴について説明します。
エンジン・クラスのインスタンス化
デフォルトでは、Java暗号化フレームワークが特定のサービスのプロバイダ・プロパティを検索し、そのプロパティで登録されたエンジン・クラスを直接インスタンス化します。プロバイダはこの動作をオーバーライドし、要求されたサービス自体のためにエンジン・クラスをインスタンス化できます。
デフォルトの動作をオーバーライドするときは、プロバイダはカスタムな動作を追加するようにProvider.Service.newInstance()メソッドをオーバーライドします。たとえばプロバイダはカスタムのコンストラクタを呼び出したり、プロバイダの外部からはアクセスできない(プロバイダのみが知る)情報を使用して初期化を実行したりすることができます。
パラメータ・サポート
Java暗号化フレームワークでは、プロバイダのサービス実装がアプリケーション固有のパラメータを使用できるかどうかを判断するために、すばやいチェックを試みます。このチェックを実行するために、フレームワークではProvider.Service.supportsParameter()を呼び出します。
フレームワークは、プロバイダの遅延選択中にこのすばやいチェックを利用します(プロバイダの遅延選択を参照)。アプリケーションが初期化メソッドを呼び出してKeyオブジェクトを渡すと、フレームワークではService.supportsParameter()メソッドを呼び出すことで、配下のプロバイダに対してそのオブジェクトをサポートしているかどうかを確認します。supportsParameter()がfalseを返すと、フレームワークはただちにそのプロバイダを対象から取り除きます。supportsParameter()がtrueを返すと、フレームワークはKeyオブジェクトをそのプロバイダの初期化エンジン・クラス実装に渡します。ソフトウェアKeyオブジェクトを必要とするプロバイダでは、ソフトウェア以外の鍵を渡されたときにfalseを返すように、このメソッドをオーバーライドする必要があります。同様に、抽出不可能な鍵が含まれたPKCS#11トークンのプロバイダでは、このプロバイダが作成した、つまりそれぞれのトークン上の鍵に対応するKeyオブジェクトに対してtrueのみを返すようにする必要があります。
注意:
supportsParameter()のデフォルト実装ではtrueを返します。これにより、既存のプロバイダを変更しないで動作させることができます。しかし、この緩やかなデフォルト実装のために、フレームワークでは、初期化エンジン・クラス実装内部のKeyオブジェクトを拒否するプロバイダが例外をスローしたときに、その例外をキャッチできるようにしておく必要があります。フレームワークでは、これらのケースをsupportsParameter()がfalseを返すときと同じように扱います。Java暗号化フレームワークでは、プロバイダのサービス実装がアプリケーション固有のパラメータを使用できるかどうかを判断するために、すばやいチェックを試みます。このチェックを実行するために、フレームワークではProvider.Service.supportsParameter()を呼び出します。
フレームワークは、プロバイダの遅延選択中にこのすばやいチェックを利用します(プロバイダの遅延選択を参照)。アプリケーションが初期化メソッドを呼び出してKeyオブジェクトを渡すと、フレームワークではService.supportsParameter()メソッドを呼び出すことで、配下のプロバイダに対してそのオブジェクトをサポートしているかどうかを確認します。supportsParameter()がfalseを返すと、フレームワークはただちにそのプロバイダを対象から取り除きます。supportsParameter()がtrueを返すと、フレームワークはKeyオブジェクトをそのプロバイダの初期化エンジン・クラス実装に渡します。ソフトウェアKeyオブジェクトを必要とするプロバイダでは、ソフトウェア以外の鍵を渡されたときにfalseを返すように、このメソッドをオーバーライドする必要があります。同様に、抽出不可能な鍵が含まれたPKCS#11トークンのプロバイダでは、このプロバイダが作成した、つまりそれぞれのトークン上の鍵に対応するKeyオブジェクトに対してtrueのみを返すようにする必要があります。
注意:
supportsParameter()のデフォルト実装ではtrueを返します。これにより、既存のプロバイダを変更しないで動作させることができます。しかし、この緩やかなデフォルト実装のために、フレームワークでは、初期化エンジン・クラス実装内部のKeyオブジェクトを拒否するプロバイダが例外をスローしたときに、その例外をキャッチできるようにしておく必要があります。フレームワークでは、これらのケースをsupportsParameter()がfalseを返すときと同じように扱います。注意:
disabledMechanismsおよびenabledMechanisms構成ディレクティブを使用すると、メカニズムを無視するようにSunPKCS11に指示できます(SunPKCS11の構成を参照)。Elliptic Curveメカニズムでは、SunPKCS11プロバイダは、パラメータのエンコーディングとしてnamedCurveの選択を使用する鍵のみを使用し、圧縮されていない形式のみを許可します。SunPKCS11プロバイダでは、標準名が付けられたすべてのドメイン・パラメータをトークンがサポートしていることが前提となります。
表5-3 SunPKCS11プロバイダでサポートされているJavaアルゴリズム
| Javaアルゴリズム | PKCS#11メカニズム |
|---|---|
| Signature.MD2withRSA | CKM_MD2_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
| Signature.MD5withRSA | CKM_MD5_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
| Signature.SHA1withRSA | CKM_SHA1_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
| Signature.SHA224withRSA | CKM_SHA224_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
| Signature.SHA256withRSA | CKM_SHA256_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
| Signature.SHA384withRSA | CKM_SHA384_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
| Signature.SHA512withRSA | CKM_SHA512_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
| Signature.SHA1withDSA | CKM_DSA_SHA1、CKM_DSA |
| Signature.NONEwithDSA | CKM_DSA |
| Signature.SHA1withECDSA | CKM_ECDSA_SHA1、CKM_ECDSA |
| Signature.SHA224withECDSA | CKM_ECDSA |
| Signature.SHA256withECDSA | CKM_ECDSA |
| Signature.SHA384withECDSA | CKM_ECDSA |
| Signature.SHA512withECDSA | CKM_ECDSA |
| Signature.NONEwithECDSA | CKM_ECDSA |
| Cipher.RSA/ECB/NoPadding | CKM_RSA_X_509 |
| Cipher.RSA/ECB/PKCS1Padding | CKM_RSA_PKCS |
| Cipher.ARCFOUR | CKM_RC4 |
| Cipher.DES/CBC/NoPadding | CKM_DES_CBC |
| Cipher.DES/CBC/PKCS5Padding | CKM_DES_CBC_PAD、CKM_DES_CBC |
| Cipher.DES/ECB/NoPadding | CKM_DES_ECB |
| Cipher.DES/ECB/PKCS5Padding | CKM_DES_ECB |
| Cipher.DESede/CBC/NoPadding | CKM_DES3_CBC |
| Cipher.DESede/CBC/PKCS5Padding | CKM_DES3_CBC_PAD、CKM_DES3_CBC |
| Cipher.DESede/ECB/NoPadding | CKM_DES3_ECB |
| Cipher.DESede/ECB/PKCS5Padding | CKM_DES3_ECB |
| Cipher.AES/CBC/NoPadding | CKM_AES_CBC |
| Cipher.AES/CBC/PKCS5Padding | CKM_AES_CBC_PAD、CKM_AES_CBC |
| Cipher.Blowfish/CBC/NoPadding | CKM_BLOWFISH_CBC |
| Cipher.Blowfish/CBC/PKCS5Padding | CKM_BLOWFISH_CBC |
| Cipher.AES/CTR/NoPadding | CKM_AES_CTR |
| Cipher.AES/ECB/NoPadding | CKM_AES_ECB |
| Cipher.AES/ECB/PKCS5Padding | CKM_AES_ECB |
| Cipher.AES_128/CBC/NoPadding | CKM_AES_CBC |
| Cipher.AES_128/ECB/NoPadding | CKM_AES_ECB |
| Cipher.AES_192/CBC/NoPadding | CKM_AES_CBC |
| Cipher.AES_192/ECB/NoPadding | CKM_AES_ECB |
| Cipher.AES_256/CBC/NoPadding | CKM_AES_CBC |
| Cipher.AES_256/ECB/NoPadding | CKM_AES_ECB |
| KeyAgreement.ECDH | CKM_ECDH1_DERIVE |
| KeyAgreement.DiffieHellman | CKM_DH_PKCS_DERIVE |
| KeyPairGenerator.RSA | CKM_RSA_PKCS_KEY_PAIR_GEN |
| KeyPairGenerator.DSA | CKM_DSA_KEY_PAIR_GEN |
| KeyPairGenerator.EC | CKM_EC_KEY_PAIR_GEN |
| KeyPairGenerator.DiffieHellman | CKM_DH_PKCS_KEY_PAIR_GEN |
| KeyGenerator.ARCFOUR | CKM_RC4_KEY_GEN |
| KeyGenerator.DES | CKM_DES_KEY_GEN |
| KeyGenerator.DESede | CKM_DES3_KEY_GEN |
| KeyGenerator.AES | CKM_AES_KEY_GEN |
| KeyGenerator.Blowfish | CKM_BLOWFISH_KEY_GEN |
| Mac.HmacMD5 | CKM_MD5_HMAC |
| Mac.HmacSHA1 | CKM_SHA_1_HMAC |
| Mac.HmacSHA224 | CKM_SHA224_HMAC |
| Mac.HmacSHA256 | CKM_SHA256_HMAC |
| Mac.HmacSHA384 | CKM_SHA384_HMAC |
| Mac.HmacSHA512 | CKM_SHA512_HMAC |
| MessageDigest.MD2 | CKM_MD2 |
| MessageDigest.MD5 | CKM_MD5 |
| MessageDigest.SHA1 | CKM_SHA_1 |
| MessageDigest.SHA-224 | CKM_SHA224 |
| MessageDigest.SHA-256 | CKM_SHA256 |
| MessageDigest.SHA-384 | CKM_SHA384 |
| MessageDigest.SHA-512 | CKM_SHA512 |
| KeyFactory.RSA | サポートされるRSAメカニズムすべて |
| KeyFactory.DSA | サポートされるDSAメカニズムすべて |
| KeyFactory.EC | サポートされるECメカニズムすべて |
| KeyFactory.DiffieHellman | サポートされるDiffie-Hellmanメカニズムすべて |
| SecretKeyFactory.ARCFOUR | CKM_RC4 |
| SecretKeyFactory.DES | CKM_DES_CBC |
| SecretKeyFactory.DESede | CKM_DES3_CBC |
| SecretKeyFactory.AES | CKM_AES_CBC |
| SecretKeyFactory.Blowfish | CKM_BLOWFISH_CBC |
| SecureRandom.PKCS11 | CK_TOKEN_INFOにはCKF_RNGビット・セットがある |
| KeyStore.PKCS11 | 常に使用可能 |
ここでは、SunPKCS11プロバイダのKeyStoreを、ベースとなるネイティブのPKCS#11ライブラリへ実装する場合の要件を説明します。
注意:
より多くの既存のPKCS#11ライブラリとの相互運用性を実現するため、将来のリリースで変更が加えられる可能性があります。読取り専用アクセス
PKCS#11トークンに保存された既存のオブジェクトをKeyStoreエントリにマッピングするため、SunPKCS11プロバイダのKeyStore実装は次の操作を実行します。
C_FindObjects[Init|Final]を呼び出して、トークン上のすべての非公開鍵オブジェクトを検索します。検索テンプレートには、次の属性があります。 C_FindObjects[Init|Final]を呼び出して、トークン上のすべての証明書オブジェクトを検索します。検索テンプレートには、次の属性があります。 一致するペアごとに、発行者 ->サブジェクトのパスに従って証明書チェーンが作成されます。エンド・エンティティ証明書から、次の属性を持つ検索テンプレートを使用したC_FindObjects[Init|Final]が呼び出されます。
この検索は、発行者の証明書が見つからないか、自己署名証明書が見つかるまで継続します。複数の証明書が見つかった場合は、最初の証明書が使用されます。
非公開鍵と証明書が一致して証明書チェーンが作成されると、エンド・エンティティ証明書のCKA_LABELの値をKeyStoreの別名として、非公開鍵エントリに情報が保存されます。
エンド・エンティティ証明書にCKA_LABELがない場合は、別名はCKA_IDから作成されます。CKA_IDが出力可能文字からのみ構成される場合は、CKA_IDのバイトをUTF-8文字セットを使用してデコードして、Stringの別名を作成します。または、16進数のString別名を、CKA_IDバイトから作成します(例: 「0xFFFF」)。
複数の証明書が同一のCKA_LABELを共有する場合、別名はCKA_LABELに加え、エンド・エンティティ証明書の発行者およびシリアル番号(例: "MyCert/CN=foobar/1234")から作成されます。
CKA_TRUSTED属性がサポートされていない場合は、信頼できる証明書エントリは作成されません。
C_FindObjects[Init|Final]を呼び出して、トークン上のすべての秘密鍵オブジェクトを検索します。検索テンプレートには、次の属性があります。 各秘密鍵オブジェクトに対して、CKA_LABEL値をKeyStoreの別名とするKeyStore秘密鍵エントリが作成されます。各秘密鍵オブジェクトには、固有のCKA_LABELが必要です。
書込みアクセス
PKCS#11トークンにKeyStoreエントリに対する新しいKeyStoreエントリを作成するため、SunPKCS11プロバイダのKeyStore実装は次の操作を実行します。
CKA_TOKEN=trueとしてC_CreateObjectが呼び出され、エントリの内容それぞれに対してトークン・オブジェクトが作成されます。 非公開鍵オブジェクトは、CKA_PRIVATE=trueで保存されます。KeyStoreの別名(UTF-8エンコード)は、非公開鍵と対応するエンド・エンティティ証明書の両方でCKA_IDとして設定されます。KeyStoreの別名では、エンド・エンティティ証明書オブジェクトにCKA_LABELが設定されます。
非公開鍵エントリのチェーン内の各証明書も保存されます。CKA_LABELは、CA証明書には設定されません。CA証明書がトークン内にある場合、複製は保存されません。
秘密鍵オブジェクトは、CKA_PRIVATE=trueで保存されます。KeyStoreの別名は、CKA_LABELとして設定されます。
CKA_TOKEN=trueとして呼び出されます。CKA_TRUSTED=trueと設定することが許可されないため(トークン初期化アプリケーションのみが可能)、信頼できる証明書エントリを作成できません。その他
前述の検索の他、SunPKCS11プロバイダのKeyStore実装で次の検索を使用して内部関数を実行できます。具体的には、次のどの属性テンプレートを使用しても、C_FindObjects[Init|Final]を呼び出すことができます。
CKA_TOKEN true
CKA_CLASS CKO_CERTIFICATE
CKA_SUBJECT [subject DN]
CKA_TOKEN true
CKA_CLASS CKO_SECRET_KEY
CKA_LABEL [label]
CKA_TOKEN true
CKA_CLASS CKO_CERTIFICATE or CKO_PRIVATE_KEY
CKA_ID [cka_id]
次に、Providerクラスの機能を示す単純なプロバイダの例を示します。
package com.foo;
import java.io.*;
import java.lang.reflect.*;
import java.security.*;
import javax.crypto.*;
/**
* Example provider that demonstrates some Provider class features.
*
* . implement multiple different algorithms in a single class.
* Previously each algorithm needed to be implemented in a separate class
* (e.g. one for SHA-256, one for SHA-384, etc.)
*
* . multiple concurrent instances of the provider frontend class each
* associated with a different backend.
*
* . it uses "unextractable" keys and lets the framework know which key
* objects it can and cannot support
*
* Note that this is only a simple example provider designed to demonstrate
* several of the new features. It is not explicitly designed for efficiency.
*/
public final class ExampleProvider extends Provider {
// reference to the crypto backend that implements all the algorithms
final CryptoBackend cryptoBackend;
public ExampleProvider(String name, CryptoBackend cryptoBackend) {
super(name, 1.0, "JCA/JCE provider for " + name);
this.cryptoBackend = cryptoBackend;
// register the algorithms we support (SHA-256, SHA-384, DESede, and AES)
putService(new MyService
(this, "MessageDigest", "SHA-256", "com.foo.ExampleProvider$MyMessageDigest"));
putService(new MyService
(this, "MessageDigest", "SHA-384", "com.foo.ExampleProvider$MyMessageDigest"));
putService(new MyCipherService
(this, "Cipher", "DES", "com.foo.ExampleProvider$MyCipher"));
putService(new MyCipherService
(this, "Cipher", "AES", "com.foo.ExampleProvider$MyCipher"));
}
// the API of our fictitious crypto backend
static abstract class CryptoBackend {
abstract byte[] digest(String algorithm, byte[] data);
abstract byte[] encrypt(String algorithm, KeyHandle key, byte[] data);
abstract byte[] decrypt(String algorithm, KeyHandle key, byte[] data);
abstract KeyHandle createKey(String algorithm, byte[] keyData);
}
// the shell of the representation the crypto backend uses for keys
private static final class KeyHandle {
// fill in code
}
// we have our own ServiceDescription implementation that overrides newInstance()
// that calls the (Provider, String) constructor instead of the no-args constructor
private static class MyService extends Service {
private static final Class[] paramTypes = {Provider.class, String.class};
MyService(Provider provider, String type, String algorithm,
String className) {
super(provider, type, algorithm, className, null, null);
}
public Object newInstance(Object param) throws NoSuchAlgorithmException {
try {
// get the Class object for the implementation class
Class clazz;
Provider provider = getProvider();
ClassLoader loader = provider.getClass().getClassLoader();
if (loader == null) {
clazz = Class.forName(getClassName());
} else {
clazz = loader.loadClass(getClassName());
}
// fetch the (Provider, String) constructor
Constructor cons = clazz.getConstructor(paramTypes);
// invoke constructor and return the SPI object
Object obj = cons.newInstance(new Object[] {provider, getAlgorithm()});
return obj;
} catch (Exception e) {
throw new NoSuchAlgorithmException("Could not instantiate service", e);
}
}
}
// custom ServiceDescription class for Cipher objects. See supportsParameter() below
private static class MyCipherService extends MyService {
MyCipherService(Provider provider, String type, String algorithm,
String className) {
super(provider, type, algorithm, className);
}
// we override supportsParameter() to let the framework know which
// keys we can support. We support instances of MySecretKey, if they
// are stored in our provider backend, plus SecretKeys with a RAW encoding.
public boolean supportsParameter(Object obj) {
if (obj instanceof SecretKey == false) {
return false;
}
SecretKey key = (SecretKey)obj;
if (key.getAlgorithm().equals(getAlgorithm()) == false) {
return false;
}
if (key instanceof MySecretKey) {
MySecretKey myKey = (MySecretKey)key;
return myKey.provider == getProvider();
} else {
return "RAW".equals(key.getFormat());
}
}
}
// our generic MessageDigest implementation. It implements all digest
// algorithms in a single class. We only implement the bare minimum
// of MessageDigestSpi methods
private static final class MyMessageDigest extends MessageDigestSpi {
private final ExampleProvider provider;
private final String algorithm;
private ByteArrayOutputStream buffer;
MyMessageDigest(Provider provider, String algorithm) {
super();
this.provider = (ExampleProvider)provider;
this.algorithm = algorithm;
engineReset();
}
protected void engineReset() {
buffer = new ByteArrayOutputStream();
}
protected void engineUpdate(byte b) {
buffer.write(b);
}
protected void engineUpdate(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
}
protected byte[] engineDigest() {
byte[] data = buffer.toByteArray();
byte[] digest = provider.cryptoBackend.digest(algorithm, data);
engineReset();
return digest;
}
}
// our generic Cipher implementation, only partially complete. It implements
// all cipher algorithms in a single class. We implement only as many of the
// CipherSpi methods as required to show how it could work
private static abstract class MyCipher extends CipherSpi {
private final ExampleProvider provider;
private final String algorithm;
private int opmode;
private MySecretKey myKey;
private ByteArrayOutputStream buffer;
MyCipher(Provider provider, String algorithm) {
super();
this.provider = (ExampleProvider)provider;
this.algorithm = algorithm;
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
this.opmode = opmode;
myKey = MySecretKey.getKey(provider, algorithm, key);
if (myKey == null) {
throw new InvalidKeyException();
}
buffer = new ByteArrayOutputStream();
}
protected byte[] engineUpdate(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
return new byte[0];
}
protected int engineUpdate(byte[] b, int ofs, int len, byte[] out, int outOfs) {
buffer.write(b, ofs, len);
return 0;
}
protected byte[] engineDoFinal(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
byte[] in = buffer.toByteArray();
byte[] out;
if (opmode == Cipher.ENCRYPT_MODE) {
out = provider.cryptoBackend.encrypt(algorithm, myKey.handle, in);
} else {
out = provider.cryptoBackend.decrypt(algorithm, myKey.handle, in);
}
buffer = new ByteArrayOutputStream();
return out;
}
// code for remaining CipherSpi methods goes here
}
// our SecretKey implementation. All our keys are stored in our crypto
// backend, we only have an opaque handle available. There is no
// encoded form of these keys.
private static final class MySecretKey implements SecretKey {
final String algorithm;
final Provider provider;
final KeyHandle handle;
MySecretKey(Provider provider, String algorithm, KeyHandle handle) {
super();
this.provider = provider;
this.algorithm = algorithm;
this.handle = handle;
}
public String getAlgorithm() {
return algorithm;
}
public String getFormat() {
return null; // this key has no encoded form
}
public byte[] getEncoded() {
return null; // this key has no encoded form
}
// Convert the given key to a key of the specified provider, if possible
static MySecretKey getKey(ExampleProvider provider, String algorithm, Key key) {
if (key instanceof SecretKey == false) {
return null;
}
// algorithm name must match
if (!key.getAlgorithm().equals(algorithm)) {
return null;
}
// if key is already an instance of MySecretKey and is stored
// on this provider, return it right away
if (key instanceof MySecretKey) {
MySecretKey myKey = (MySecretKey)key;
if (myKey.provider == provider) {
return myKey;
}
}
// otherwise, if the input key has a RAW encoding, convert it
if (!"RAW".equals(key.getFormat())) {
return null;
}
byte[] encoded = key.getEncoded();
KeyHandle handle = provider.cryptoBackend.createKey(algorithm, encoded);
return new MySecretKey(provider, algorithm, handle);
}
}
}