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セキュリティのプロパティ・ファイル(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=value

attributevalueの有効な値については、この項内の表で説明しています。

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を指定します。

最大で、enabledMechanismsdisabledMechanismsのどちらか1つを指定できます。どちらも指定しない場合、有効なメカニズムは、SunPKCS11プロバイダ(「SunPKCS11プロバイダでサポートされているアルゴリズム」を参照)とPKCS#11トークンの両方でサポートされているメカニズムになります。

enabledMechanisms 有効にするPKCS#11メカニズムのリスト。空白文字で区切り、全体を中カッコ()で囲む SunPKCS11プロバイダとPKCS#11トークンの両方でサポートされている場合に、このプロバイダ・インスタンスで使用する必要があるPKCS#11メカニズムのリストを指定します。その他のすべてのメカニズムは無視されます。リスト内の各エントリは、PKCS#11メカニズムの名前になります。2つのPKCS#11メカニズムから成るリストの例を次に示します。
enabledMechanisms = {
  CKM_RSA_PKCS
  CKM_RSA_PKCS_KEY_PAIR_GEN
}
最大で、enabledMechanismsdisabledMechanismsのどちらか1つを指定できます。どちらも指定しない場合、有効なメカニズムは、SunPKCS11プロバイダ(「SunPKCS11プロバイダでサポートされているアルゴリズム」を参照)とPKCS#11トークンの両方でサポートされているメカニズムになります。
explicitCancel ブール値、デフォルト: true trueの場合、操作を明示的に取り消す必要があることを示します。
functionList PKCS#11関数リストを返すC関数の名前。デフォルト: C_GetFunctionList このオプションは、主に非推奨のSecmod.Module.getProvider()メソッドに対して存在します。
handleStartupErrors 使用できる値: ignoreAllignoreMissingLibrartまたはhalt。デフォルト: halt 起動時のエラー処理方法について説明します。
insertionCheckInterval 整数(ミリ秒単位)、デフォルトは2000です。この値は100msより大きくなければなりません。 トークンが存在しない場合に、トークン挿入をテストする頻度をミリ秒単位で指定します。
keyStoreCompatibilityMode ブール値、デフォルト: true trueの場合は、P11Keystoreが入力パラメータをより許容できることを示します。
library PKCS#11実装のパス名

PKCS#11実装のフル・パス名(拡張子を含む)を指定します。パス名の書式はプラットフォームに依存します。たとえば、LinuxではPKCS#11実装のパス名が/opt/foo/lib/libpkcs11.soなどになりますが、Windowsでのパス名はC:\foo\mypkcs11.dll、macOSでは/opt/local/lib/libpkcs11.dylibになります。

ノート:

  • Mozilla Network Security Services (NSS)をPKCS#11実装として構成するには、この属性をNSS softokn3ライブラリのフル・パス名に設定します。
  • プラットフォームによっては、含まれているディレクトリを含むように環境変数LD_LIBRARY_PATH (Linux)、PATH (Windows)またはDYLD_LIBRARY_PATH (macOS)に設定して、オペレーティング・システムが依存ライブラリを特定できるようにする必要があります。
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を使用します。指定できるのは、多くともslotslotListIndexのどちらか1つです。どちらも指定しない場合のデフォルトは、値0slotListIndexです。
slotListIndex スロットのインデックス このプロバイダ・インスタンスが関連付けられているスロットのインデックスを指定します。これは、PKCS#11の関数C_GetSlotListで返されるすべてのスロットのリストに対するインデックスです。たとえば、0はリストの先頭のスロットを表します。指定できるのは、多くともslotslotListIndexのどちらか1つです。どちらも指定しない場合のデフォルトは、値0slotListIndexです。
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の有効な値は次のとおりです。

  • generateKeyPairGeneratorまたはKeyGeneratorによって生成されたキー用
  • importKeyFactoryまたはSecretKeyFactoryによって作成されたキー用。暗号化操作の初期化メソッド(Signature.initSign()など)に渡されるときにPKCS#11キー・オブジェクトに自動的に変換されるJava Softwareキーにも適用される。
  • *。生成操作または作成操作のどちらかで作成されたキー用。

keytypeの有効な値は、CKO_PUBLIC_KEYCKO_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 = trueCKA_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固有のコードを使用します。この場合、通常の構成コマンドlibraryslot、およびslotListIndexは使用できません。

表5-2 NSSの属性と値

属性 説明
nssDbMode readWritereadOnlynoDbのいずれか。デフォルト: readWrite この指示は、NSSデータベースへのアクセス方法を決定します。読書きモードではフル・アクセスが可能ですが、データベースには一度に1つのプロセスのみがアクセスする必要があります。読取り専用モードでは、このファイルの変更は許可されません。

noDbモードを使用すると、データベース・ファイルなしで純粋に暗号化プロバイダとしてNSSを使用できます。PKCS11 KeyStoreを使用して持続的なキーを作成することはできません。

nssLibraryDirectory NSSおよびNetscape Portable Runtime (NSPR)ライブラリを含むディレクトリ(libnss3.soを含む)

NSSおよびNSPRライブラリを含むディレクトリのフル・パス名。Java VMとして同じプロセス内で実行されている別のコンポーネントによってNSSがすでにロードおよび初期化されていないかぎり、この属性を指定する必要があります。

この値が設定されている場合、nssUseSecmodtrueに設定されます。

ノート: プラットフォームによっては、このディレクトリを含むように環境変数LD_LIBRARY_PATH (Linux)、PATH (Windows)またはDYLD_LIBRARY_PATH (macOS)に設定して、オペレーティング・システムが依存ライブラリを特定できるようにする必要があります。

nssModule keystorecryptofipstrustanchorsのいずれか

さまざまなライブラリやスロットを使用してNSSの機能が使用できるようになっています。この指示は、SunPKCS11のインスタンスがアクセスするモジュールを決定します。

cryptoモジュールは、noDbモードでのデフォルトです。このモジュールはログインを必要としない暗号化操作をサポートしますが、持続的なキーはサポートしません。

FIPS-140準拠モードに対してNSSのsecmod.dbが設定されている場合、fipsモジュールがデフォルトになります。このモードでは、NSSによって、使用可能なアルゴリズムおよびキーを作成する場合に使用するPKCS#11属性が制限されます。

keystoreモジュールは、その他の構成でのデフォルトです。このモジュールは、PKCS11 KeyStoreを使用して持続的なキーをサポートします。このキーはNSS DBファイルに保存されます。このモジュールではログインが必要です。

信頼できるアンカー・ライブラリを含むようにsecmod.dbが構成されている場合、trustanchorsモジュールを使用すると、PKCS11 KeyStoreを使用してNSSの信頼できるアンカー証明書にアクセスできます。

この値が設定されている場合、nssUseSecmodtrueに設定されます。

nssNetscapeDbWorkaround ブール値、デフォルト: true

trueの場合、P11KeyStoreでは、秘密キーの作成時にCKA_NETSCAPE_DB属性を指定します。この設定は、nssUseSecmodtrueの場合にのみ有効です

nssOptimizeSpace ブール値、デフォルト: false

NSSがパフォーマンス(falseの場合)またはメモリー・フットプリント(trueの場合)を優先することを示します。

nssSecmodDirectory NSS構成およびキー情報(secmod.dbkey3.db、およびcert8.db)を含むディレクトリのフル・パス名

NSSがすでに別のコンポーネントにより初期化されている(nssLibraryDirectoryを参照)か、NSSがデータベース・ファイルなしで使用されている(nssDbMode、noDbモードを参照)のでないかぎり、このディレクティブを指定する必要があります。

この値が設定されている場合、nssUseSecmodtrueに設定されます。

nssUseSecmod ブール値 trueの場合、NSS secmodモードが使用されます。nssLibraryDirectorynssSecmodDirectoryまたは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プロバイダの無効化は一時的措置でしかないことに注意してください。PKCS#11プロバイダを無効にすると、そのプロバイダは使用できなくなるため、アプリケーションが破損したり、アプリケーションのパフォーマンスに影響が出たりする可能性があります。問題が特定されたら、その固有のメカニズムのみを無効のままにしてください。

PKCS#11プロバイダの無効化

特定のJavaインストールで実行されるすべてのJavaプロセスに対してPKCS#11プロバイダを無効にするには、これらのステップに従います:

  1. 「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
      ...
  2. この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
    ...
  3. この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パッケージに、関連するジェネリック・インタフェース(PrivateKeyPublicKeyまたはSecretKey)のみを実装する必要があります。キー・アルゴリズムの特定は、Key.getAlgorithm()メソッドを使用して実行されます。

抽出不可能なトークン・キーのKeyオブジェクトはそのトークンに関連付けられたプロバイダのみが使用できるということに注意してください。

プロバイダの遅延選択

Cipher.getInstance("AES")などのJava暗号化getInstance()メソッドは、要求されたアルゴリズムを実装している最初のプロバイダからの実装を返します。ただし、JDKは、関連する初期化メソッドが呼び出されるまでプロバイダの選択を遅らせます。初期化メソッドはKeyオブジェクトを受け入れ、その時点で、指定したKeyオブジェクトを受け入れられるプロバイダを判断できます。これにより、選択したプロバイダで、指定したKeyオブジェクトを使用できることが保証されます。(アプリケーションが、ソフトウェアKeyオブジェクトのみを使用できるプロバイダで、抽出不可能なトークン・キーのKeyオブジェクトを使用しようとした場合、プロバイダはInvalidKeyExceptionをスローします。これは、CipherKeyAgreementMacおよび、Signatureクラスの問題です。次に、影響のある初期化メソッドを示します。

また、アプリケーションが初期化メソッドを複数回呼び出すと(毎回異なるキーを使用するなど)、そのキーに適切なプロバイダが毎回選択されます。つまり、初期化の呼び出しごとに異なるプロバイダが選択される可能性があります。

このプロバイダの遅延選択は、アプリケーションからは認識されませんが、CipherKeyAgreementMac、およびSignaturegetProvider()メソッドの動作に影響します。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ファイルに署名するには、次のステップに従います:

  1. 「トークン・ログイン」の説明に従って、PKCS#11トークンのキーストアにアクセスします。

        char[] pin = ...; 
        KeyStore ks = KeyStore.getInstance("PKCS11");
        ks.load(null, pin); 
        KeyStore ks = builder.getKeyStore();
        Key key = ks.getKey(alias, null);
  2. JarSigner.Builder(Keystore.PrivateKeyEntry)コンストラクタを使用してJarSignerオブジェクトを作成します。

        JarSigner mySigner = JarSigner.Builder(key).build();
  3. 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_providerkeystorePasswordURLの行はどちらもオプションです。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トークンおよび暗号化サービスをサポートできます。

プロバイダ・サービスおよびパラメータ・サポートを示すように設計された単純なプロバイダの例は、プロバイダの例を参照してください。

プロバイダ・サービス

プロバイダによるサービス実装ごとに、サービスの型(CipherSignatureなど)、ピリオド、およびサービスが適用されるアルゴリズム名で構成される名前のプロパティが必要です。プロパティの値には、サービスを実装するクラスの完全修飾名を指定する必要があります。値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プロバイダでサポートされているアルゴリズム

表5-3では、SunPKCS11プロバイダでサポートされるJavaアルゴリズムと、それらのサポートに必要となる対応するPKCS#11メカニズムをリストします。複数のメカニズムがリストされている場合は、設定されている順番に記載しており、いずれか1つを指定すれば十分です。

ノート:

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.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.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
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実装は次の操作を実行します。

  1. C_FindObjects[Init|Final]を呼び出して、トークン上のすべての秘密キー・オブジェクトを検索します。検索テンプレートには、次の属性があります。
    • CKA_TOKEN = true
    • CKA_CLASS = CKO_PRIVATE_KEY
  2. C_FindObjects[Init|Final]を呼び出して、トークン上のすべての証明書オブジェクトを検索します。検索テンプレートには、次の属性があります。
    • CKA_TOKEN = true
    • CKA_CLASS = CKO_CERTIFICATE
  3. それぞれの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")から作成されます。

  4. (エンド・エンティティ証明書として)秘密キー・エントリの一部ではない各証明書は、信頼できるかどうかチェックされます。CKA_TRUSTED属性がtrueの場合、CKA_LABEL値をKeyStoreの別名とする、KeyStoreの信頼できる証明書エントリが作成されます。証明書にCKA_LABELがない場合、または複数の証明書が同じCKA_LABELを共有する場合、別名は上述のように作成されます。

    CKA_TRUSTED属性がサポートされていない場合は、信頼できる証明書エントリは作成されません。

  5. 秘密キー・エントリまたは信頼できる証明書エントリの一部ではない秘密キーまたは証明書オブジェクトは、すべて無視されます。
  6. C_FindObjects[Init|Final]を呼び出して、トークン上のすべての秘密キーオブジェクトを検索します。検索テンプレートには、次の属性があります。
    • CKA_TOKEN = true
    • CKA_CLASS = CKO_SECRET_KEY

    各秘密キーオブジェクトに対して、CKA_LABEL値をKeyStoreの別名とするKeyStore秘密キーエントリが作成されます。各秘密キーオブジェクトには、固有のCKA_LABELが必要です。

書込みアクセス

PKCS#11トークンにKeyStoreエントリに対する新しいKeyStoreエントリを作成するため、SunPKCS11プロバイダのKeyStore実装は次の操作を実行します。

  1. 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として設定されます。

  2. セッション・オブジェクトをトークン・オブジェクトに変換しようとする場合(KeyStore.setEntryが呼び出され、指定されたエントリの秘密キー・オブジェクトがセッション・オブジェクトである場合など)、C_CopyObjectがCKA_TOKEN=trueとして呼び出されます。
  3. トークン内に複数の証明書が見つかり、同じCKA_LABELを共有する場合は、トークンへの書込み機能は無効になります。
  4. 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);
        }
    }
}