5 PKCS#11リファレンス・ガイド
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プロバイダ
その他の多くのプロバイダとは異なり、SunPKCS11プロバイダ自体は暗号化アルゴリズムを実装していません。その代わりに、Java JCAおよびJCE APIとネイティブPKCS#11暗号化APIとの間のブリッジとして動作し、これらの間の呼び出しと規則を変換します。
つまり、標準JCAおよびJCE APIを呼び出すJavaアプリケーションなら、アプリケーションを変更しなくても、基盤となる次のようなPKCS#11実装で提供されるアルゴリズムを利用できるということです。
- 暗号化スマートカード、
- ハードウェア暗号化アクセラレータ
- ハイ・パフォーマンスのソフトウェア実装。
ノート:
Java SEは、ネイティブPKCS#11実装へのアクセスを容易にするのみであり、それ自体にはネイティブPKCS#11実装は含まれていません。ただし、スマート・カードやハードウェア・アクセラレータなどの暗号化デバイスには、PKCS#11実装を含んだソフトウェアが付属していることが一般的で、製造元の指示に従ってインストールし、構成する必要があります。SunPKCS11の要件
SunPKCS11プロバイダでは、PKCS#11 v2.20以降の実装がシステムにインストールされている必要があります。この実装は、共有オブジェクト・ライブラリ(Linuxでの.so
)またはダイナミック・リンク・ライブラリ(Windowsでの.dll
またはmacOSでの.dylib
)の形態である必要があります。使用する暗号化デバイスにこのようなPKCS#11実装が含まれているかどうかを調べる方法、実装を構成する方法、およびライブラリ・ファイルのファイル名については、ベンダーが提供するマニュアルを参照してください。
SunPKCS11プロバイダでは、基盤となるPKCS#11実装で提供されている場合は、多くのアルゴリズムがサポートされています。それらのアルゴリズムとその対応するPKCS#11メカニズムを、SunPKCS11プロバイダでサポートされているアルゴリズムで表に示します。
SunPKCS11の構成
SunPKCS11プロバイダは、モジュールjdk.crypto.cryptokiにあります。このプロバイダを使用するには、まずそれを静的にまたはプログラムでインストールする必要があります。
java-home/conf/security/java.security
)にプロバイダを追加します。
ノート:
通常、java.security
ファイル内のプロパティは1回のみ解析されます。このファイル内のプロパティを変更した場合は、アプリケーションを再起動して、変更が正しく反映されていることを確認します。たとえば次は、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=value
attributeとvalueの有効な値については、この項内の表で説明しています。
2つある必須属性は、name
およびlibrary
です。
次に、構成ファイルの例を示します。
name = FooAccelerator
library = /opt/foo/lib/libpkcs11.so
コメントは、#
(シャープ)記号で始まる行に記述します。
表5-1 PKCS#11プロバイダの構成ファイル内の属性
属性 | 値 | 説明 |
---|---|---|
allowSingleThreadedModules |
ブール値、デフォルト: true |
true の場合、シングル・スレッド・アクセスのみをサポートするモジュールが許可されます。シングル・スレッド・モジュールは、たとえばSunPKCS11でNetwork Security Services (NSS)を使用する場合に、同じプロセス内で複数のPKCS#11コンシューマから安全に使用できません。
|
attributes |
「属性の構成」を参照してください | PKCS#11キー・オブジェクトの作成時に設定される追加のPKCS#11属性を指定します。これにより、特定の属性を必要とするトークンを使用できるようになります。 |
cleaner.longInterval |
整数(ミリ秒単位)、デフォルトは60000 です。この値は1000msより大きくなければなりません。
|
クリーナ・スレッドがビジーでない期間中にネイティブ参照をチェックする頻度(つまり、クリーナ・スレッドがネイティブ参照のためにキューをチェックする頻度)をミリ秒単位で指定します。
ノート: クリーニング用のネイティブPKCS11参照が検出されると、クリーナ・スレッドはcleaner.shortInterval の頻度に切り替わります。
|
cleaner.shortInterval |
整数(ミリ秒単位)、デフォルトは2000 です。この値は1000msより大きくなければなりません。
|
ビジー期間中にネイティブ参照のクリアを実行する頻度(つまり、クリーナ・スレッドが、ネイティブ・メモリーを解放するためにキュー内の不要になったネイティブ参照を処理する頻度)をミリ秒単位で指定します。
ノート: クリーナ・スレッドは、200回の試行に失敗した後(つまり、キュー内に参照が見つからなかった場合に)、cleaner.longInterval の頻度に切り替わります。
|
description |
このプロバイダ・インスタンスの説明 | プロバイダ・インスタンスのProvider.getInfo() メソッドが返す文字列を指定します。文字列が指定されていない場合は、デフォルトの説明が返されます。
|
destroyTokenAfterLogout |
ブール値、デフォルト: false |
true の場合、java.security.AuthProvider.logout()がSunPKCS11プロバイダ・インスタンスから呼び出されると、基盤となるトークン・オブジェクトが破棄され、リソースが解放されます。これにより、基本的に、logout()の呼出し後にSunPKCS11プロバイダ・インスタンスは使用できなくなります。
ノート: この属性をtrueに設定してSunPKCS11プロバイダをシステム・プロバイダ・リストに追加しないでください。これは、logout()が呼び出された後にプロバイダ・オブジェクトが使用できなくなるためです。 |
disabledMechanisms |
無効にするPKCS#11メカニズムのリスト。空白文字で区切り、全体を中カッコ()で囲む | このプロバイダ・インスタンスが無視するPKCS#11メカニズムのリストを指定します。リストされたあらゆるメカニズムは、トークンとSunPKCS11プロバイダでサポートされていても、プロバイダによって無視されます。これらのサービスを無効にするために文字列SecureRandom およびKeyStore を指定します。
最大で、 |
enabledMechanisms |
有効にするPKCS#11メカニズムのリスト。空白文字で区切り、全体を中カッコ()で囲む | SunPKCS11プロバイダとPKCS#11トークンの両方でサポートされている場合に、このプロバイダ・インスタンスで使用する必要があるPKCS#11メカニズムのリストを指定します。その他のすべてのメカニズムは無視されます。リスト内の各エントリは、PKCS#11メカニズムの名前になります。2つのPKCS#11メカニズムから成るリストの例を次に示します。
最大で、enabledMechanisms かdisabledMechanisms のどちらか1つを指定できます。どちらも指定しない場合、有効なメカニズムは、SunPKCS11プロバイダ(「SunPKCS11プロバイダでサポートされているアルゴリズム」を参照)とPKCS#11トークンの両方でサポートされているメカニズムになります。
|
explicitCancel |
ブール値、デフォルト: true |
true の場合、操作を明示的に取り消す必要があることを示します。
|
functionList |
PKCS#11関数リストを返すC関数の名前。デフォルト: C_GetFunctionList
|
このオプションは、主に非推奨のSecmod.Module.getProvider()メソッドに対して存在します。 |
handleStartupErrors |
使用できる値: ignoreAll 、ignoreMissingLibrart またはhalt 。デフォルト: halt |
起動時のエラー処理方法について説明します。 |
insertionCheckInterval |
整数(ミリ秒単位)、デフォルトは2000 です。この値は100msより大きくなければなりません。
|
トークンが存在しない場合に、トークン挿入をテストする頻度をミリ秒単位で指定します。 |
keyStoreCompatibilityMode |
ブール値、デフォルト: true |
true の場合は、P11Keystoreが入力パラメータをより許容できることを示します。
|
library |
PKCS#11実装のパス名 |
PKCS#11実装のフル・パス名(拡張子を含む)を指定します。パス名の書式はプラットフォームに依存します。たとえば、LinuxではPKCS#11実装のパス名が ノート:
|
name |
このプロバイダ・インスタンスの名前接尾辞 | 接頭辞SunPKCS11- と連結されて、このプロバイダ・インスタンスの名前(つまり、プロバイダ・インスタンスのProvider.getName() メソッドで返される文字列)を生成する文字列を指定します。たとえばname 属性がFooAccelerator の場合、プロバイダ・インスタンスの名前はSunPKCS11-FooAccelerator になります。
|
nssArgs |
引用符付き文字列 | NSSソフト・トークンの特殊な初期化引数文字列を指定します。これは、secmodモードを使用せずにNSSソフト・トークンを直接使用するときに使用されます。 |
nssDbMode |
表5-2を参照してください | 表5-2を参照してください |
nssLibraryDirectory |
表5-2を参照してください | 表5-2を参照してください |
nssModule |
表5-2を参照してください | 表5-2を参照してください |
nssNetscapeDbWorkaround |
表5-2を参照してください | 表5-2を参照してください |
nssOptimizeSpace |
表5-2を参照してください | 表5-2を参照してください |
nssSecmodDirectory |
表5-2を参照してください | 表5-2を参照してください |
nssUseSecmod |
表5-2を参照してください | 表5-2を参照してください |
omitInitialize |
ブール値、デフォルト: false |
true の場合、C_Initialize()関数の呼出しを省略します。PKCS#11実装が以前にC_Initialize()呼出しで初期化されている場合にのみ使用します。
|
showInfo |
ブール値、デフォルト: false |
true の場合は、起動時にプロバイダ情報を表示します。プロバイダ情報には、プロバイダの名前とサポートされているPKCS#11メカニズムが含まれます。
|
slot |
スロットID | このプロバイダ・インスタンスが関連付けられているスロットのIDを指定します。たとえば、PKCS#11のID 1 のスロットでは、1 を使用します。指定できるのは、多くともslot かslotListIndex のどちらか1つです。どちらも指定しない場合のデフォルトは、値0 のslotListIndex です。
|
slotListIndex |
スロットのインデックス | このプロバイダ・インスタンスが関連付けられているスロットのインデックスを指定します。これは、PKCS#11の関数C_GetSlotList で返されるすべてのスロットのリストに対するインデックスです。たとえば、0 はリストの先頭のスロットを表します。指定できるのは、多くともslot かslotListIndex のどちらか1つです。どちらも指定しない場合のデフォルトは、値0 のslotListIndex です。
|
useEcX963Encoding |
ブール値、デフォルト: false |
ECポイントのX9.63エンコーディングが使用されているか(true )、またはエンコーディングがASN.1 OctetStringでラップされている(false )ことを示します。
|
属性の構成
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実装でサポートしない属性や、該当するキー・タイプで無効な属性を指定すると、実行時に操作が失敗する原因となります。
このオプションはゼロ回以上指定できます。これらのオプションは、構成ファイルで指定されている順序で処理されます。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プロバイダでサポートされているアルゴリズム」を参照してください。
属性の名前と値は、1つまたは複数の名前と値のペアのリストとして指定されます。name
はPKCS#11仕様のCKA_xxx
定数(CKA_SENSITIVE
など)でなければなりません。value
は、次のいずれかになります。
- ブール値。
true
またはfalse
- 整数値。10進数表記(デフォルト)または
0x
で始まる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
行とともに使用できます。この場合は先に説明したような、同様のアグリゲーションおよびオーバーライドの規則が適用されます。
Network Security Services (NSS)へのアクセス
ネットワーク・セキュリティ・サービス(NSS)は一連のオープン・ソース・セキュリティ・ライブラリであり、その暗号化APIは、PKCS#11に基づいていますが、PKCS#11標準ではない特別な機能を含んでいます。SunPKCS11プロバイダには、NSS固有の機能(複数のNSS固有の構成ディレクティブなど)と連携するコードが含まれています。
最適な結果を得るために、使用可能な最新バージョンのNSSを使用することをお薦めします。少なくともバージョン3.12になります。
表5-2で説明されているnss
構成ディレクティブのいずれかが使用されている場合、SunPKCS11プロバイダはNSS固有のコードを使用します。この場合、通常の構成コマンドlibrary
、slot
、およびslotListIndex
は使用できません。
表5-2 NSSの属性と値
属性 | 値 | 説明 |
---|---|---|
nssDbMode |
readWrite 、readOnly 、noDb のいずれか。デフォルト: readWrite |
この指示は、NSSデータベースへのアクセス方法を決定します。読書きモードではフル・アクセスが可能ですが、データベースには一度に1つのプロセスのみがアクセスする必要があります。読取り専用モードでは、このファイルの変更は許可されません。
noDbモードを使用すると、データベース・ファイルなしで純粋に暗号化プロバイダとしてNSSを使用できます。PKCS11 KeyStoreを使用して持続的なキーを作成することはできません。 |
nssLibraryDirectory |
NSSおよびNetscape Portable Runtime (NSPR)ライブラリを含むディレクトリ(libnss3.so を含む)
|
NSSおよびNSPRライブラリを含むディレクトリのフル・パス名。Java VMとして同じプロセス内で実行されている別のコンポーネントによってNSSがすでにロードおよび初期化されていないかぎり、この属性を指定する必要があります。 この値が設定されている場合、 ノート: プラットフォームによっては、このディレクトリを含むように環境変数 |
nssModule |
keystore 、crypto 、fips 、trustanchors のいずれか |
さまざまなライブラリやスロットを使用してNSSの機能が使用できるようになっています。この指示は、SunPKCS11のインスタンスがアクセスするモジュールを決定します。
FIPS-140準拠モードに対してNSSの
信頼できるアンカー・ライブラリを含むように この値が設定されている場合、 |
nssNetscapeDbWorkaround |
ブール値、デフォルト: true |
trueの場合、P11KeyStoreでは、秘密キーの作成時に |
nssOptimizeSpace |
ブール値、デフォルト: false |
NSSがパフォーマンス( |
nssSecmodDirectory |
NSS構成およびキー情報(secmod.db 、key3.db 、およびcert8.db )を含むディレクトリのフル・パス名
|
NSSがすでに別のコンポーネントにより初期化されている( この値が設定されている場合、 |
nssUseSecmod |
ブール値 | trueの場合、NSS secmodモードが使用されます。nssLibraryDirectory 、nssSecmodDirectory またはnssModule が指定されている場合は、暗黙的にtrue に設定されます。
|
例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のトラブルシューティング
PKCS#11に問題が発生し、デバッグが必要になることがあります。ライブラリ、スロット、トークンおよびメカニズムのデバッグ情報を表示するには、「SunPKCS11の構成」で説明しているように静的または動的に指定したSunPKCS11プロバイダ構成ファイルにshowInfo=true
を追加します。
その他のデバッグ情報については、次のいずれかのオプションを指定してJavaプロセスを起動または再起動してください。
-
SunPKCS11プロバイダの一般的なデバッグ情報:
-Djava.security.debug=sunpkcs11
-
PKCS#11キーストア固有のデバッグ情報の場合:
-Djava.security.debug=pkcs11keystore
PKCS#11プロバイダまたは個々のPKCS#11メカニズム(あるいはその両方)の無効化
トラブルシューティング・プロセスの一環として、PKCS#11プロバイダ、または特定のプロバイダに固有のメカニズムを一時的に無効にすると便利な場合があります。
PKCS#11プロバイダの無効化
特定のJavaインストールで実行されるすべてのJavaプロセスに対してPKCS#11プロバイダを無効にするには、これらのステップに従います:
-
「SunPKCS11の構成」の説明に従って、PKCS#11プロバイダをインストールしたことを確認します。これらのステップでは、次のことが前提です:
-
PKCS#11プロバイダの名前は
MyOwn
です。 -
構成ファイルの名前は
java-home/conf/security/myown.cfg
で、次の内容が含まれています。name = MyOwn description = A PKCS11 provider accessing a specific PKCS11 binary implementation library = Pathname of MyOwn PKCS11 implementation
-
次のようにJavaセキュリティ・プロパティ・ファイル(
java-home/conf/security/java.security
)を変更しました。# # List of providers and their preference orders: # security.provider.1=SUN security.provider.2=SunRsaSign security.provider.3=SunPKCS11 myOwn.cfg security.provider.4=SunEC security.provider.5=SunJSSE ...
-
-
このJavaインストールで
MyOwn
PKCS#11プロバイダを静的に無効化するには、Javaセキュリティ・プロパティ・ファイルを編集し、プロバイダが登録されている行をコメント・アウトしてから、それに続くプロバイダの優先順位を再番号付けします。# # List of providers and their preference orders: # security.provider.1=SUN security.provider.2=SunRsaSign #security.provider.3=SunPKCS11 myOwn.cfg security.provider.3=SunEC security.provider.4=SunJSSE ...
-
このJavaインストールで
MyOwn
PKCS#11プロバイダを動的に無効化するには、アプリケーションで次を呼び出します。Security.removeProvider("SunPKCS11-MyOwn");
特定のメカニズムの無効化
PKCS#11のメカニズムのいずれかで問題が発生した場合、PKCS#11プロバイダ全体ではなく、その特定のメカニズムのみを無効にすることにより、問題を解決できます(それまでにPKCS#11プロバイダを無効にしていた場合は、再度有効にすることを忘れないでください)。
ノート:
PKCS#11 SecureRandom実装のみを無効化するには、(「SunPKCS11の構成」で説明したように、静的または動的に指定した) SunPKCS11プロバイダ構成ファイル内の無効化されたメカニズムのリストにSecureRandomを追加します。
disabledMechanisms = {
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オブジェクトには、実際のキー・データが含まれており、そのデータにアクセスできる。
- スマートカードなどの保護されたトークン上にある抽出不可能なキーは、実際のキー・データを含まないJava Keyオブジェクトによって表される。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)
ノート:
いったんプロバイダが選択されると、たとえば、最初の初期化コールの後は、JDKがそれ以降の初期化コールのために別のプロバイダに切り替えることはありません。特定のKey
オブジェクトに基づいてプロバイダを再選択するには、getInstance()をコールして新しいインスタンスを取得してから、すでに初期化されている古いインスタンスを再利用するのではなく、Key
オブジェクトを使用してこのインスタンスの初期化メソッドをコールします。
このプロバイダの遅延選択は、アプリケーションからは認識されませんが、Cipher
、KeyAgreement
、Mac
、およびSignature
のgetProvider()
メソッドの動作に影響します。getProvider()
が初期化操作の発生する前 (したがって、プロバイダ選択が起こる前)に呼び出された場合は、要求されるアルゴリズムがサポートされている最初のプロバイダが返されます。このプロバイダは、初期化メソッドが呼び出されたあとに選択されたプロバイダと同じにならない場合があります。getProvider()
が初期化操作のあとに呼び出された場合は、実際に選択されたプロバイダが返されます。アプリケーションでは関連する初期化メソッドを呼び出したあとにのみ、getProvider()
を呼び出すようにしてください。
getProvider()
だけでなく、次のメソッドにも同様の影響があります。
Cipher.getBlockSize
Cipher.getExcemptionMechanism
Cipher.getIV
Cipher.getOutputSize
Cipher.getParameters
Mac.getMacLength
Signature.getParameters
Signature.setParameter
JAAS KeyStoreLoginModule
JDKには、JAASキーストア・ログイン・モジュールであるKeyStoreLoginModuleが付属しています。このモジュールを使用すると、アプリケーションで、指定したキーストア内のIDを使用して認証を行うことができます。アプリケーションは認証後に、自身のプリンシパルおよびクレデンシャル情報(証明書および秘密キー)をキーストアから取得します。このログイン・モジュールを使用し、PKCS#11トークンをキーストアとして使用するように構成すると、アプリケーションはこの情報をPKCS#11トークンから取得できるようになります。
次のオプションを使用してKeyStoreLoginModule
を構成すると、PKCS#11トークンをキーストアとして使用できます。
keyStoreURL="NONE"
keyStoreType="PKCS11"
keyStorePasswordURL=some_pin_url
説明は次のとおりです。
- some_pin_url
- PINの場所です。
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;
};
JSSEキーストアおよびトラスト・ストアとしてのトークン
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)。
PKCS#11トークンとのkeytoolおよびjarsignerの使用
SunPKCS11プロバイダがjava.security
セキュリティ・プロパティ・ファイル(Javaランタイムの$JAVA_HOME/conf/security
ディレクトリ内にある)で構成されている場合、次のオプションを使用することで、PKCS#11トークンの操作にkeytoolおよびjarsignerを使用できます。
-keystore NONE
-storetype PKCS11
構成済みPKCS#11トークンの内容をリストするコマンドの例を次に示します。
keytool -keystore NONE -storetype PKCS11 -list
PINは-storepass
オプションで指定できます。指定されない場合、keytool
およびjarsigner
は、トークンPINを要求します。トークンに保護された認証パス(専用のPINパッドや生体読取り機など)がある場合、-protected
オプションを指定する必要がありますが、パスワード・オプションを指定する必要はありません。
java.security
セキュリティ・プロパティ・ファイル内で複数のSunPKCS11プロバイダが構成されている場合、-providerName
オプションを使用して特定のプロバイダ・インスタンスを選択できます。このオプションの引数は、プロバイダの名前です。
-providerName providerName
SunPKCS11プロバイダの場合、providerName
の形式はSunPKCS11-TokenName
のようになります。この説明を次に示します。
- TokenName
- プロバイダ・インスタンスの構成時に付けられた名前の接尾辞です。詳細は、表5-1を参照してください。たとえば、次のコマンドでは、名前接尾辞
SmartCard
のPKCS#11キーストア・プロバイダ・インスタンスの内容をリストします。keytool -keystore NONE -storetype PKCS11 \ -providerName SunPKCS11-SmartCard \ -list
SunPKCS11プロバイダを
java.security
セキュリティ・プロパティ・ファイル内で構成していない場合は、次のオプションを使用して、プロバイダを動的にインストールするようにkeytool
およびjarsigner
を設定します。-providerClass sun.security.pkcs11.SunPKCS11
-providerArg ConfigFilePath
- ConfigFilePath
-
トークン構成ファイルのパスです。次に、SunPKCS11プロバイダが
java.security
ファイルで構成されていないときにPKCS#11キーストアをリストするコマンドの例を示します。keytool -keystore NONE -storetype PKCS11 \ -providerClass sun.security.pkcs11.SunPKCS11 \ -providerArg /foo/bar/token.config \ -list
ノート:
ハードウェア・トークンが小さすぎて証明書を格納できない場合があります。jarsigner
ツールの-certchain
オプションを使用すると、外部ファイルからロードできます。
jdk.security.jarsigner APIおよびPKCS#11トークンでのJARファイルの署名
jdk.security.jarsigner APIおよびPKCS#11トークンをキーストアとしてJARファイルに署名するには、次のステップに従います:
-
「トークン・ログイン」の説明に従って、PKCS#11トークンのキーストアにアクセスします。
char[] pin = ...; KeyStore ks = KeyStore.getInstance("PKCS11"); ks.load(null, pin); KeyStore ks = builder.getKeyStore(); Key key = ks.getKey(alias, null);
-
JarSigner.Builder(Keystore.PrivateKeyEntry)コンストラクタを使用してJarSignerオブジェクトを作成します。
JarSigner mySigner = JarSigner.Builder(key).build();
-
JARファイルに署名します。
try (ZipFile in = new ZipFile(inputFile); FileOutputStream out = new FileOutputStream(outputFile)) { mySigner.sign(in, out); }
ポリシー・ファイルのキーストア・エントリ構文
デフォルト・ポリシー実装内のkeystore
エントリには、次の構文があります。これには、PINおよび複数のPKCS#11プロバイダ・インスタンスを指定できます。
keystore "some_keystore_url", "keystore_type", "keystore_provider";
keystorePasswordURL "some_password_url";
説明は次のとおりです。
- keystore_provider
- キーストア・プロバイダ名です(たとえば、
"SunPKCS11-SmartCard"
)。 - some_password_url
- トークンPINの場所を指すURLです。keystore_providerとkeystorePasswordURLの行はどちらもオプションです。keystore_providerが指定されていない場合、特定のキーストア・タイプをサポートする最初の構成済みプロバイダが使用されます。
keystorePasswordURL
の行が指定されていない場合、パスワードは使用されません。
「デフォルトのPolicyの実装とポリシー・ファイルの構文」を参照してください。
例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
を返すときと同じように扱います。
SunPKCS11プロバイダでサポートされているアルゴリズム
ノート:
disabledMechanisms
およびenabledMechanisms
構成ディレクティブを使用すると、メカニズムを無視するようにSunPKCS11に指示できます(SunPKCS11の構成を参照)。
Elliptic Curveメカニズムでは、SunPKCS11プロバイダは、パラメータのエンコーディングとしてnamedCurve
の選択を使用するキーのみを使用し、圧縮されていない形式のみを許可します。SunPKCS11プロバイダでは、標準名が付けられたすべてのドメイン・パラメータをトークンがサポートしていることが前提となります。
ノート:
楕円曲線(EC)名について、SunPKCS11プロバイダは、SunECプロバイダでサポートされる任意のEC名をサポートします(ただし、トークンでサポートされている場合)。「SunECプロバイダ」の「サポートされる楕円曲線名」を参照してください。次の表のPKCS#11メカニズムの詳細は、「PKCS #11 Cryptographic Token Interface Current Mechanisms Specification Version 3.0」を参照してください。
表5-3 SunPKCS11プロバイダでサポートされているJavaアルゴリズム
Javaアルゴリズム | PKCS#11メカニズム |
---|---|
Cipher.AES_128/CBC/NoPadding | CKM_AES_CBC |
Cipher.AES_128/ECB/NoPadding | CKM_AES_ECB |
Cipher.AES_128/GCM/NoPadding | CKM_AES_GCM |
Cipher.AES_192/CBC/NoPadding | CKM_AES_CBC |
Cipher.AES_192/ECB/NoPadding | CKM_AES_ECB |
Cipher.AES_192/GCM/NoPadding | CKM_AES_GCM |
Cipher.AES_256/CBC/NoPadding | CKM_AES_CBC |
Cipher.AES_256/ECB/NoPadding | CKM_AES_ECB |
Cipher.AES_256/GCM/NoPadding | CKM_AES_GCM |
Cipher.AES/CBC/NoPadding | CKM_AES_CBC |
Cipher.AES/CBC/PKCS5Padding | CKM_AES_CBC_PAD、CKM_AES_CBC |
Cipher.AES/CTR/NoPadding | CKM_AES_CTR |
Cipher.AES/ECB/NoPadding | CKM_AES_ECB |
Cipher.AES/ECB/PKCS5Padding | CKM_AES_ECB |
Cipher.AES/GCM/NoPadding | CKM_AES_GCM |
Cipher.ARCFOUR | CKM_RC4 |
Cipher.Blowfish/CBC/NoPadding | CKM_BLOWFISH_CBC |
Cipher.Blowfish/CBC/PKCS5Padding | CKM_BLOWFISH_CBC |
Cipher.ChaCha20-Poly1305 | CKM_CHACHA20_POLY1305 |
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.PBEWithHmacSHA1AndAES_128 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA_1_HMACが必要) |
Cipher.PBEWithHmacSHA224AndAES_128 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA224_HMACが必要) |
Cipher.PBEWithHmacSHA256AndAES_128 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA256_HMACが必要) |
Cipher.PBEWithHmacSHA384AndAES_128 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA384_HMACが必要) |
Cipher.PBEWithHmacSHA512AndAES_128 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA512_HMACが必要) |
Cipher.PBEWithHmacSHA1AndAES_256 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA_1_HMACが必要) |
Cipher.PBEWithHmacSHA224AndAES_256 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA224_HMACが必要) |
Cipher.PBEWithHmacSHA256AndAES_256 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA256_HMACが必要) |
Cipher.PBEWithHmacSHA384AndAES_256 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA384_HMACが必要) |
Cipher.PBEWithHmacSHA512AndAES_256 | CKM_AES_CBC_PAD、CKM_AES_CBC (CKM_PKCS5_PBKD2およびCKM_SHA512_HMACが必要) |
Cipher.RSA/ECB/NoPadding | CKM_RSA_X_509 |
Cipher.RSA/ECB/PKCS1Padding | CKM_RSA_PKCS |
KeyAgreement.DiffieHellman | CKM_DH_PKCS_DERIVE |
KeyAgreement.ECDH | CKM_ECDH1_DERIVE |
KeyFactory.DiffieHellman | サポートされるDiffie-Hellmanメカニズムすべて |
KeyFactory.DSA | サポートされるDSAメカニズムすべて |
KeyFactory.EC | サポートされるECメカニズムすべて |
KeyFactory.RSA | サポートされるRSAメカニズムすべて |
KeyGenerator.AES | CKM_AES_KEY_GEN |
KeyGenerator.ARCFOUR | CKM_RC4_KEY_GEN |
KeyGenerator.Blowfish | CKM_BLOWFISH_KEY_GEN |
KeyGenerator.ChaCha20 | CKM_CHACHA20_KEY_GEN |
KeyGenerator.DES | CKM_DES_KEY_GEN |
KeyGenerator.DESede | CKM_DES3_KEY_GEN |
KeyGenerator.HmacMD5 | CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA1 | CKM_SHA_1_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA224 | CKM_SHA224_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA256 | CKM_SHA256_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA3-224 | CKM_SHA3_224_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA3-256 | CKM_SHA3_256_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA3-384 | CKM_SHA3_384_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA3-512 | CKM_SHA3_512_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA384 | CKM_SHA384_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA512 | CKM_SHA512_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA512/224 | CKM_SHA512_224_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyGenerator.HmacSHA512/256 | CKM_SHA512_256_KEY_GEN、CKM_GENERIC_SECRET_KEY_GEN |
KeyPairGenerator.DiffieHellman | CKM_DH_PKCS_KEY_PAIR_GEN |
KeyPairGenerator.DSA | CKM_DSA_KEY_PAIR_GEN |
KeyPairGenerator.EC | CKM_EC_KEY_PAIR_GEN |
KeyPairGenerator.RSA | CKM_RSA_PKCS_KEY_PAIR_GEN |
KeyStore.PKCS11 | 常に使用可能 |
Mac.HmacMD5 | CKM_MD5_HMAC |
Mac.HmacPBESHA1 | CKM_PBA_SHA1_WITH_SHA1_HMAC (CKM_SHA_1_HMACが必要) |
Mac.HmacPBESHA224 | CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN (CKM_SHA224_HMACが必要) |
Mac.HmacPBESHA256 | CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN (CKM_SHA256_HMACが必要) |
Mac.HmacPBESHA384 | CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN (CKM_SHA384_HMACが必要) |
Mac.HmacPBESHA512 | CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN (CKM_SHA512_HMACが必要) |
Mac.HmacSHA1 | CKM_SHA_1_HMAC |
Mac.HmacSHA224 | CKM_SHA224_HMAC |
Mac.HmacSHA256 | CKM_SHA256_HMAC |
Mac.HmacSHA3-224 | CKM_SHA3_224_HMAC |
Mac.HmacSHA3-256 | CKM_SHA3_256_HMAC |
Mac.HmacSHA3-384 | CKM_SHA3_384_HMAC |
Mac.HmacSHA3-512 | CKM_SHA3_512_HMAC |
Mac.HmacSHA384 | CKM_SHA384_HMAC |
Mac.HmacSHA512 | CKM_SHA512_HMAC |
Mac.HmacSHA512/224 | CKM_SHA512_224_HMAC |
Mac.HmacSHA512/256 | CKM_SHA512_256_HMAC |
MessageDigest.MD2 | CKM_MD2 |
MessageDigest.MD5 | CKM_MD5 |
MessageDigest.SHA-224 | CKM_SHA224 |
MessageDigest.SHA-256 | CKM_SHA256 |
MessageDigest.SHA-384 | CKM_SHA384 |
MessageDigest.SHA-512 | CKM_SHA512 |
MessageDigest.SHA-512/224 | CKM_SHA512_224 |
MessageDigest.SHA-512/256 | CKM_SHA512_256 |
MessageDigest.SHA1 | CKM_SHA_1 |
MessageDigest.SHA3-224 | CKM_SHA3_224 |
MessageDigest.SHA3-256 | CKM_SHA3_256 |
MessageDigest.SHA3-384 | CKM_SHA3_384 |
MessageDigest.SHA3-512 | CKM_SHA3_512 |
SecretKeyFactory.AES | CKM_AES_CBC |
SecretKeyFactory.ARCFOUR | CKM_RC4 |
SecretKeyFactory.Blowfish | CKM_BLOWFISH_CBC |
SecretKeyFactory.ChaCha20 | CKM_CHACHA20_POLY1305 |
SecretKeyFactory.DES | CKM_DES_CBC |
SecretKeyFactory.DESede | CKM_DES3_CBC |
SecretKeyFactory.PBEWithHmacSHA1AndAES_128 | CKM_PKCS5_PBKD2 (CKM_SHA_1_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA224AndAES_128 | CKM_PKCS5_PBKD2 (CKM_SHA224_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA256AndAES_128 | CKM_PKCS5_PBKD2 (CKM_SHA256_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA384AndAES_128 | CKM_PKCS5_PBKD2 (CKM_SHA384_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA512AndAES_128 | CKM_PKCS5_PBKD2 (CKM_SHA512_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA1AndAES_256 | CKM_PKCS5_PBKD2 (CKM_SHA_1_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA224AndAES_256 | CKM_PKCS5_PBKD2 (CKM_SHA224_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA256AndAES_256 | CKM_PKCS5_PBKD2 (CKM_SHA256_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA384AndAES_256 | CKM_PKCS5_PBKD2 (CKM_SHA384_HMACが必要) |
SecretKeyFactory.PBEWithHmacSHA512AndAES_256 | CKM_PKCS5_PBKD2 (CKM_SHA512_HMACが必要) |
SecretKeyFactory.HmacPBESHA1 | CKM_PBA_SHA1_WITH_SHA1_HMAC |
SecretKeyFactory.HmacPBESHA224 | CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN |
SecretKeyFactory.HmacPBESHA256 | CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN |
SecretKeyFactory.HmacPBESHA384 | CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN |
SecretKeyFactory.HmacPBESHA512 | CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN |
SecretKeyFactory.PBKDF2WithHmacSHA1 | CKM_PKCS5_PBKD2 (CKM_SHA_1_HMACが必要) |
SecretKeyFactory.PBKDF2WithHmacSHA224 | CKM_PKCS5_PBKD2 (CKM_SHA224_HMACが必要) |
SecretKeyFactory.PBKDF2WithHmacSHA256 | CKM_PKCS5_PBKD2 (CKM_SHA256_HMACが必要) |
SecretKeyFactory.PBKDF2WithHmacSHA384 | CKM_PKCS5_PBKD2 (CKM_SHA384_HMACが必要) |
SecretKeyFactory.PBKDF2WithHmacSHA512 | CKM_PKCS5_PBKD2 (CKM_SHA512_HMACが必要) |
SecureRandom.PKCS11 | CK_TOKEN_INFOにはCKF_RNGビット・セットがある |
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.NONEwithDSA | CKM_DSA |
Signature.NONEwithECDSA | CKM_ECDSA |
Signature.RSASSA-PSS | CKM_RSA_PKCS_PSS |
Signature.SHA1withDSA | CKM_DSA_SHA1、CKM_DSA |
Signature.SHA1withECDSA | CKM_ECDSA_SHA1、CKM_ECDSA |
Signature.SHA1withRSA | CKM_SHA1_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA1withRSASSA-PSS | CKM_SHA1_RSA_PKCS_PSS |
Signature.SHA224withDSA | CKM_DSA_SHA224 |
Signature.SHA224withDSAinP1363Format | CKM_DSA_SHA224 |
Signature.SHA224withECDSA | CKM_ECDSA_SHA224、CKM_ECDSA |
Signature.SHA224withECDSAinP1363Format | CKM_ECDSA_SHA224、CKM_ECDSA |
Signature.SHA224withRSA | CKM_SHA224_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA224withRSASSA-PSS | CKM_SHA224_RSA_PKCS_PSS |
Signature.SHA256withDSA | CKM_DSA_SHA256 |
Signature.SHA256withDSAinP1363Format | CKM_DSA_SHA256 |
Signature.SHA256withECDSA | CKM_ECDSA_SHA256、CKM_ECDSA |
Signature.SHA256withECDSAinP1363Format | CKM_ECDSA_SHA256、CKM_ECDSA |
Signature.SHA256withRSA | CKM_SHA256_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA256withRSASSA-PSS | CKM_SHA256_RSA_PKCS_PSS |
Signature.SHA3-224withDSA | CKM_DSA_SHA3_224 |
Signature.SHA3-224withDSAinP1363Format | CKM_DSA_SHA3_224 |
Signature.SHA3-224withECDSA | CKM_ECDSA_SHA3_224、CKM_ECDSA |
Signature.SHA3-224withECDSAinP1363Format | CKM_ECDSA_SHA3_224、CKM_ECDSA |
Signature.SHA3-224withRSA | CKM_SHA3_224_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA3-224withRSASSA-PSS | CKM_SHA3_224_RSA_PKCS_PSS |
Signature.SHA3-256withDSA | CKM_DSA_SHA3_256 |
Signature.SHA3-256withDSAinP1363Format | CKM_DSA_SHA3_256 |
Signature.SHA3-256withECDSA | CKM_ECDSA_SHA3_256、CKM_ECDSA |
Signature.SHA3-256withECDSAinP1363Format | CKM_ECDSA_SHA3_256、CKM_ECDSA |
Signature.SHA3-256withRSA | CKM_SHA3_256_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA3-256withRSASSA-PSS | CKM_SHA3_256_RSA_PKCS_PSS |
Signature.SHA3-384withDSA | CKM_DSA_SHA3_384 |
Signature.SHA3-384withDSAinP1363Format | CKM_DSA_SHA3_384 |
Signature.SHA3-384withECDSA | CKM_ECDSA_SHA3_384、CKM_ECDSA |
Signature.SHA3-384withECDSAinP1363Format | CKM_ECDSA_SHA3_384、CKM_ECDSA |
Signature.SHA3-384withRSA | CKM_SHA3_384_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA3-384withRSASSA-PSS | CKM_SHA3_384_RSA_PKCS_PSS |
Signature.SHA3-512withDSA | CKM_DSA_SHA3_512 |
Signature.SHA3-512withDSAinP1363Format | CKM_DSA_SHA3_512 |
Signature.SHA3-512withECDSA | CKM_ECDSA_SHA3_512、CKM_ECDSA |
Signature.SHA3-512withECDSAinP1363Format | CKM_ECDSA_SHA3_512、CKM_ECDSA |
Signature.SHA3-512withRSA | CKM_SHA3_512_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA3-512withRSASSA-PSS | CKM_SHA3_512_RSA_PKCS_PSS |
Signature.SHA384withDSA | CKM_DSA_SHA384 |
Signature.SHA384withDSAinP1363Format | CKM_DSA_SHA384 |
Signature.SHA384withECDSA | CKM_ECDSA_SHA384、CKM_ECDSA |
Signature.SHA384withECDSAinP1363Format | CKM_ECDSA_SHA384、CKM_ECDSA |
Signature.SHA384withRSA | CKM_SHA384_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA384withRSASSA-PSS | CKM_SHA384_RSA_PKCS_PSS |
Signature.SHA512withDSA | CKM_DSA_SHA512 |
Signature.SHA512withDSAinP1363Format | CKM_DSA_SHA512 |
Signature.SHA512withECDSA | CKM_ECDSA_SHA512、CKM_ECDSA |
Signature.SHA512withECDSAinP1363Format | CKM_ECDSA_SHA512、CKM_ECDSA |
Signature.SHA512withRSA | CKM_SHA512_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509 |
Signature.SHA512withRSASSA-PSS | CKM_SHA512_RSA_PKCS_PSS |
SunPKCS11プロバイダのキーストアの要件
ここでは、SunPKCS11プロバイダのKeyStoreを、ベースとなるネイティブのPKCS#11ライブラリへ実装する場合の要件を説明します。
ノート:
より多くの既存のPKCS#11ライブラリとの相互運用性を実現するため、将来のリリースで変更が加えられる可能性があります。読取り専用アクセス
PKCS#11トークンに保存された既存のオブジェクトをKeyStoreエントリにマッピングするため、SunPKCS11プロバイダのKeyStore実装は次の操作を実行します。
C_FindObjects[Init|Final]
を呼び出して、トークン上のすべての秘密キー・オブジェクトを検索します。検索テンプレートには、次の属性があります。- CKA_TOKEN = true
- CKA_CLASS = CKO_PRIVATE_KEY
C_FindObjects[Init|Final]
を呼び出して、トークン上のすべての証明書オブジェクトを検索します。検索テンプレートには、次の属性があります。- CKA_TOKEN = true
- CKA_CLASS = CKO_CERTIFICATE
- それぞれのCKA_ID属性を取得することで、各秘密キー・オブジェクトを対応する証明書と一致させます。一致するペアは、同じ一意のCKA_IDを共有する必要があります。
一致するペアごとに、発行者 ->サブジェクトのパスに従って証明書チェーンが作成されます。エンド・エンティティ証明書から、次の属性を持つ検索テンプレートを使用した
C_FindObjects[Init|Final]
が呼び出されます。- CKA_TOKEN = true
- CKA_CLASS = CKO_CERTIFICATE
- CKA_SUBJECT = [DN of certificate issuer]
この検索は、発行者の証明書が見つからないか、自己署名証明書が見つかるまで継続します。複数の証明書が見つかった場合は、最初の証明書が使用されます。
秘密キーと証明書が一致して証明書チェーンが作成されると、エンド・エンティティ証明書の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属性がtrueの場合、CKA_LABEL値をKeyStoreの別名とする、KeyStoreの信頼できる証明書エントリが作成されます。証明書にCKA_LABELがない場合、または複数の証明書が同じCKA_LABELを共有する場合、別名は上述のように作成されます。
CKA_TRUSTED属性がサポートされていない場合は、信頼できる証明書エントリは作成されません。
- 秘密キー・エントリまたは信頼できる証明書エントリの一部ではない秘密キーまたは証明書オブジェクトは、すべて無視されます。
C_FindObjects[Init|Final]
を呼び出して、トークン上のすべての秘密キー・オブジェクトを検索します。検索テンプレートには、次の属性があります。- CKA_TOKEN = true
- CKA_CLASS = CKO_SECRET_KEY
各秘密キー・オブジェクトに対して、CKA_LABEL値をKeyStoreの別名とするKeyStore秘密キー・エントリが作成されます。各秘密キー・オブジェクトには、固有のCKA_LABELが必要です。
書込みアクセス
PKCS#11トークンにKeyStoreエントリに対する新しいKeyStoreエントリを作成するため、SunPKCS11プロバイダのKeyStore実装は次の操作を実行します。
- KeyStoreエントリを作成するときには(KeyStore.setEntryなどの使用中)、
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として設定されます。 - セッション・オブジェクトをトークン・オブジェクトに変換しようとする場合(KeyStore.setEntryが呼び出され、指定されたエントリの秘密キー・オブジェクトがセッション・オブジェクトである場合など)、C_CopyObjectが
CKA_TOKEN=true
として呼び出されます。 - トークン内に複数の証明書が見つかり、同じCKA_LABELを共有する場合は、トークンへの書込み機能は無効になります。
- PKCS#11仕様では一般のアプリケーションで
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().
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);
}
}
}