Java暗号化アーキテクチャでのプロバイダの実装方法


はじめに

Javaプラットフォームでは、暗号化、公開鍵インフラストラクチャ、認証、安全な通信、アクセス制御など、主要なセキュリティ分野に渡る一連のAPIが定義されています。これらのAPIによって、開発者はアプリケーション・コードにセキュリティを簡単に統合できます。これらは、次の方針に基づいて設計されました。

  1. 実装の独立性

    アプリケーションでセキュリティを実装する必要はありません。Javaプラットフォームからセキュリティ・サービスを要求できます。セキュリティ・サービスはプロバイダ(次を参照)に実装されています。プロバイダは、標準インタフェースによってJavaプラットフォームにプラグインされます。アプリケーションは、複数の独立したプロバイダにセキュリティ機能を依存する場合があります。

  2. 実装の相互運用性

    プロバイダは、アプリケーション間で相互運用できます。具体的には、アプリケーションは特定のプロバイダにバインドされず、プロバイダは特定のアプリケーションにバインドされません。

  3. アルゴリズムの拡張性

    Javaプラットフォームには、現在広く使用されている基本的なセキュリティ・サービスを実装する多数の組込みプロバイダが含まれています。ただし、一部のアプリケーションは、まだ実装されていない普及しつつある規格や独自のサービスに依存している場合があります。Javaプラットフォームは、そのようなサービスを実装するカスタム・プロバイダのインストールをサポートします。

暗号化サービス・プロバイダ」(プロバイダ)は、JDK Security APIの暗号化に関するサブセットの固定実装を提供するパッケージ(またはパッケージ・セット)です。

java.security.Providerクラスは、セキュリティ・プロバイダの概念をJavaプラットフォームでカプセル化します。プロバイダの名前を指定し、実装するセキュリティ・サービスを一覧します。複数のプロバイダが同時に構成される場合があり、それらは優先順に一覧されます。セキュリティ・サービスが要求されると、そのサービスを実装する、優先順位がいちばん高いプロバイダが選択されます。

次の図は、SHA-256メッセージ・ダイジェスト実装を要求するための、これらのオプションを示しています。どちらの図も、メッセージ・ダイジェスト・アルゴリズムを実装する3つのプロバイダを示しています。プロバイダは、左から右への優先順位(1から3)で並べられています。左側の略図では、アプリケーションはプロバイダ名を指定せずにSHA-256アルゴリズム実装を要求しています。プロバイダが優先順位に従って検索され、その特定のアルゴリズムを提供する最初のプロバイダであるProviderBから実装が返されます。右側の略図では、アプリケーションは特定のプロバイダ(ProviderC)からSHA-256アルゴリズム実装を要求しています。この場合は、優先順位の高いプロバイダであるProviderBもSHA-256実装を提供していても、指定されたプロバイダから実装が返されます。

プロバイダ名を指定せずにSHA-256アルゴリズムを要求しているアプリケーションを示す図 特定のプロバイダからSHA-256アルゴリズムを要求しているアプリケーションを示す図
図1プロバイダ検索 図2特定のプロバイダの要求

各インストールでは1つ以上のプロバイダ・パッケージがインストールされます。クライアントは異なるプロバイダを用いて実行環境を構成し、各プロバイダの優先順位を指定できます。優先順位とは、特定プロバイダの指定がないときに、要求アルゴリズムについてプロバイダを検索する順序です。

オラクル社が提供するJava Runtime Environment(JRE)には、「SUN」という名前のデフォルトのプロバイダが標準で搭載されています。ほかのJava Runtime Environmentには、「SUN」プロバイダが含まれない場合があります。

このドキュメントの対象読者

Java Security APIを使って既存の暗号化アルゴリズムやほかのサービスにアクセスするだけであれば、このドキュメントを読む必要はありません

このドキュメントは、暗号化サービス実装を提供する独自のプロバイダ・パッケージを作成する上級プログラマを対象としています。ここでは、Java Security APIクライアントが、作成されたアルゴリズムやほかのサービスを要求する際にそれらを検出できるよう、プロバイダをJavaに統合するための方法がドキュメント化されています。

関連項目

このドキュメントは、読者がすでに「Java暗号化アーキテクチャ・リファレンス・ガイド」を読んでいることを前提としています。

Security APIのさまざまなクラスおよびインタフェースを含むパッケージについてドキュメント化されています。

用語に関する注記

このドキュメントでは、JCAという語は、JCAフレームワークを指して使用されています。このドキュメントで特定のJCAプロバイダを取り上げる場合は、常にプロバイダ名で明示的に指定されます。

エンジン・クラスおよび対応するService Provider Interfaceクラス

エンジン・クラスは、固定実装のない抽象的な方法で暗号化サービスを定義します。

暗号化サービスは、常に特定のアルゴリズムまたは型に関連付けられています。このサービスによって、暗号化の操作(デジタル署名またはメッセージ・ダイジェスト、暗号または鍵協定プロトコルなどのための操作)の提供、暗号化の操作に必要な暗号化データ(鍵またはパラメータ)の生成や提供、あるいは暗号化の操作で使用する暗号化鍵を安全にカプセル化するデータ・オブジェクト(キーストアまたは証明書)の生成が行われます。

たとえば、次の4つのエンジン・クラスがあります。

Java暗号化アーキテクチャには、エンジン・クラスなど、暗号化に関連するセキュリティ・パッケージを構成するクラスが含まれています。APIのユーザーは、エンジン・クラスを要求および利用して対応する処理を実行します。JDKは、次のエンジン・クラスを定義します。


注: ジェネレータ(generator)は、最新の内容でオブジェクトを作成しますが、ファクトリ(factory)は既存の構成要素(エンコードなど)からオブジェクトを作成します。


エンジン・クラスは、(特定の暗号化アルゴリズムに依存しない)特定の型の暗号化サービス機能へのインタフェースを提供します。これにより、Application Programming Interface (API)メソッドが定義され、APIが提供する特定の種類の暗号化サービスにアプリケーションがアクセスできるようになります。実際の実装(1つ以上のプロバイダから)は特定アルゴリズムのためのものです。たとえばSignatureエンジン・クラスは、デジタル署名アルゴリズムの機能へのアクセスを提供します。SignatureSpiサブクラスで提供される実際の実装(次の段落を参照)は、SHA256withDSAやSHA512withRSAなどの特定の種類の署名アルゴリズム用となります。

エンジン・クラスが提供するアプリケーション・インタフェースは、Service Provider Interface (SPI)として実装されます。つまり、各エンジン・クラスに対応する抽象SPIクラスが存在し、抽象SPIクラスによって暗号化サービス・プロバイダが実装しなければならないService Provider Interfaceのメソッドが定義されます。

サービス・プロバイダ・インタフェースのアーキテクチャ

サービス・プロバイダ・インタフェースのアーキテクチャの説明

エンジン・クラスのインスタンスである「APIオブジェクト」は、対応するSPIクラスのインスタンスである「SPIオブジェクト」をprivateフィールドとしてカプセル化します。APIオブジェクトのすべてのAPIメソッドは、「final」として宣言し、それらを実装することによって、カプセル化されるSPIオブジェクトの対応するSPIメソッドが呼び出されます。エンジン・クラス(およびそれに対応するSPIクラス)のインスタンスは、エンジン・クラスのgetInstanceファクトリ・メソッドへの呼出しによって作成されます。

各SPIクラスの名前は、対応するエンジン・クラス名のあとに「Spi」を追加した名前になります。たとえば、Signatureエンジン・クラスに対応するSPIクラスは、SignatureSpiクラスです。

各SPIクラスは、抽象クラスです。指定したアルゴリズムに対する特定の型のサービスの実装を提供するには、プロバイダは、対応するSPIクラスをサブクラス化して、すべての抽象メソッドの実装を提供する必要があります。

エンジン・クラスの別の例にMessageDigestクラスがあります。このクラスは、メッセージ・ダイジェスト・アルゴリズムへのアクセスを提供します。MessageDigestSpiサブクラスでのその実装は、SHA256やSHA384などの各種メッセージ・ダイジェスト・アルゴリズムにできます。

さらに別の例として、KeyFactoryエンジン・クラスは、不透明な鍵から透明な鍵仕様への変換、またはその逆の変換をサポートします。詳細は、「鍵ファクトリにより要求される鍵仕様のインタフェースおよびクラス」を参照してください。KeyFactorySpiサブクラスで提供される現実の実装は、DSA公開鍵または非公開鍵などの、特定の種類の鍵のための実装です。

プロバイダの実装および統合までのステップ

次の手順に従って、プロバイダを実装しJCAフレームワークに統合します。

ステップ1: サービス実装コードの記述

最初に、サポートする暗号化サービスのアルゴリズム固有の実装を提供するコードを記述する必要があります。

プロバイダは、JDKの1つ以上のセキュリティ・コンポーネントですでに使用可能になっている暗号化サービス実装を提供できます。

JCAには定義されていない暗号化サービス(署名やメッセージ・ダイジェストなど)に関しては、「Java暗号化アーキテクチャ・リファレンス・ガイド」を参照してください。

実装する暗号化サービスごとに、適切なSPIクラスのサブクラスを作成します。JCAは、次のエンジン・クラスを定義します。

JCAおよびその他の暗号化クラスについては、このドキュメントの「エンジン・クラスおよび対応するSPIクラス」を参照してください。

サブクラスで、次を実行する必要があります。

  1. 通常engineで始まる名前を持つ抽象メソッド用の実装を提供します。詳細は、「実装の詳細および要件」を参照してください。
  2. 引数を持たないpublicコンストラクタの存在を確認します。これが必要な理由は、サービスの要求時に、Java Securityがマスター・クラス内のプロパティによる指定に従って、そのサービスを実装するサブクラスを検索するためです(ステップ3を参照)。その後、Java Securityは、サブクラスに関連付けられたClassオブジェクトを作成し、そのClassオブジェクトに対してnewInstanceメソッドを呼び出すことにより、サブクラスのインスタンスを作成します。newInstanceはサブクラスがパラメータを持たないpublicコンストラクタを保持することを要求します。
  3. サブクラスがコンストラクタを持たない場合、引数を持たないデフォルトのコンストラクタが自動的に生成されます。ただし、サブクラスがコンストラクタを定義する場合、引数を持たないpublicコンストラクタを明示的に定義する必要があります。

ステップ1.1: 暗号化実装のためのJCAプロバイダの追加要件および推奨事項

プロバイダによるCipher、KeyAgreement、KeyGenerator、MACまたはSecretKeyファクトリの実装(クラス)をインスタンス化する際、フレームワークはプロバイダのコード・ベース(JARファイル)を判定し、その署名を検証します。このようにして、JCAはプロバイダを認証して、信頼されたエンティティにより署名されたプロバイダのみがJCAにプラグインできるようにします。このため、暗号化プロバイダは署名付きにする必要があります。詳細は後のステップで説明します。

また、各プロバイダは自己整合性チェックを実行して、プロバイダ・メソッドをJCAを介さずに直接呼び出そうとして、コードを含むJARファイルが操作されていないことを保証する必要があります。詳細は、「プロバイダによる自己整合性チェックの実行方法」を参照してください。

JCAを介さずに直接アプリケーションからインスタンス化が行われた場合に、プロバイダ・クラスを使用不可にするため、プロバイダは次の実装を行う必要があります。

プロバイダを米国以外に輸出する場合、CipherSpi実装に、Keyを指定すると鍵のサイズを返すengineGetKeySizeメソッドの実装を含める必要があります。管轄ポリシー・ファイルで指定された利用可能な暗号化強度に制限が設定されている場合、Cipher初期化メソッドはengineGetKeySizeを呼び出して、実行中のアプレットまたはアプリケーションの特定位置および状況での鍵の最大有効サイズと結果を比較します。鍵のサイズが大きすぎる場合には、初期化メソッドにより例外がスローされます。

プロバイダが実装可能なオプション機能を、次に示します。

ステップ2: プロバイダの命名

使用するプロバイダの名前を特定します。これは、クライアント・アプリケーションがプロバイダを参照する際に使用する名前です。

ステップ3: プロバイダのサブクラスであるマスター・クラスの記述

3番目のステップは、java.security.Providerクラスのサブクラスを作成することです。

このサブクラスはfinalにする必要があり、そのコンストラクタは次を実行する必要があります。

これらのどの場合にも、algName、certType、storeType、またはattrNameは、アルゴリズム、証明書の型、キーストアの型、または属性の「標準」名です。使用するべき標準名については、「Java暗号化アーキテクチャ・リファレンス・ガイド」の「付録A」を参照してください。

前述の形式のプロパティの場合、プロパティの値は、対応する属性に応じた値である必要があります。(各標準属性の定義については、「Java暗号化アーキテクチャAPI仕様& リファレンス」の「付録A」を参照してください。)

たとえば、「SUN」という名前のデフォルト・プロバイダは、SHA256withDSAデジタル署名アルゴリズムをソフトウェア内で実装します。プロバイダ「SUN」のマスター・クラスでは、次のようにして、Signature.SHA256withDSA ImplementedInが値Softwareを保持するように設定が行われます。

    put("Signature.SHA256withDSA ImplementedIn", "Software")

マスター・クラス・プロパティの設定例については、次のクラスのJDK 8ソース・コードを参照してください。

これらのファイルでは、SunおよびSunJCEプロバイダがプロパティをどのように設定しているかがわかります。

ステップ3.1: Cipher実装の追加手順

すでに説明したように、Cipherプロパティの場合、algNameは実際には変換を表します。変換は、指定された入力に対してCipherオブジェクトによって実行される操作(または操作のセット)を説明する文字列です。変換には、暗号化アルゴリズム(AESなど)の名前が必ず含まれます。これにモードおよびパディング・スキームが続く場合もあります。

変換は、次の書式で記述されます。

後者の場合、モードおよびパディング・スキームには、プロバイダ固有のデフォルト値が使用されます。たとえば、次は有効な変換です。

    Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 

ストリーム暗号モードでブロック暗号を要求する(たとえばCFBまたはOFBモードでAESを要求する)場合、クライアントは、数値をモード名に追加することにより、一度に処理するビット数をオプションで指定できます。次に変換のサンプルを示します。

    Cipher c1 = Cipher.getInstance("AES/CFB8/NoPadding");
    Cipher c2 = Cipher.getInstance("AES/OFB32/PKCS5Padding");

数値がストリーム暗号モードに準拠していない場合、プロバイダ固有のデフォルト値が使用されます。たとえば、SunJCEプロバイダではデフォルトの64ビットが使用されます。

プロバイダは、algorithm/mode/paddingの組合せごとに別々のクラスを提供できます。または、algorithmalgorithm/modealgorithm//padding (ダブル・スラッシュを使用する点に注意)のいずれかに対応する下位変換を表すより汎用的なクラスを提供できます。この場合、要求されたモードまたはパディング(あるいはその両方)は、CiphergetInstanceメソッドによって自動的に設定されます。getInstanceメソッドは、プロバイダのCipherSpiのサブクラスからengineSetModeメソッドとengineSetPaddingメソッドを呼び出します。

つまり、プロバイダ・マスター・クラスのCipherプロパティは、次の表に示すいずれかの形式になります。

Cipherプロパティの形式 説明
Cipher.algName プロバイダのCipherSpiのサブクラスは、プラガブルなモードとパディングを利用してalgNameを実装します。
Cipher.algName/mode プロバイダのCipherSpiのサブクラスは、指定されたmodeとプラガブルなパディングを利用してalgNameを実装します。
Cipher.algName//padding プロバイダのCipherSpiのサブクラスは、指定されたpaddingとプラガブルなモードを利用してalgNameを実装します。
Cipher.algName/mode/padding プロバイダのCipherSpiのサブクラスは、指定されたmodepaddingを利用してalgNameを実装します。

(使用する必要がある標準アルゴリズム名、モード、およびパディング・スキームについては、「Java暗号化アーキテクチャ・リファレンス・ガイド」の「付録A」を参照してください。)

たとえば、プロバイダは、AES/ECB/PKCS5PaddingAES/CBC/PKCS5PaddingAES/CFB/PKCS5Padding、さらにはAES/OFB/PKCS5Paddingをそれぞれ実装するCipherSpiのサブクラスを提供できます。このプロバイダは、マスター・クラス内に次のCipherプロパティを保持します。

別のプロバイダは、上の各モードのためのクラス(つまり、ECBCBCCFBOFBのためにそれぞれ1つのクラス)、PKCS5Paddingのための1つのクラス、およびCipherSpiからサブクラス化された汎用AESクラスを実装できます。このプロバイダは、マスター・クラス内に次のCipherプロパティを保持します。

algorithm」形式の変換の場合、Cipherエンジン・クラスのgetInstanceファクトリ・メソッドは、次の規則に従ってプロバイダのCipherSpiの実装をインスタンス化します。

  1. プロバイダが、指定された「algorithm」に対応するCipherSpiのサブクラスを登録済みかどうかをチェックする。

    登録済みの場合、このクラスをインスタンス化して、このモードおよびパディング・スキームのデフォルト値(プロバイダにより提供)を使用可能にします。

    回答が「いいえ」の場合、例外NoSuchAlgorithmExceptionをスローします。

algorithm/mode/padding」形式の変換の場合、Cipherエンジン・クラスのgetInstanceファクトリ・メソッドは、次の規則に従ってプロバイダのCipherSpiの実装をインスタンス化します。

  1. プロバイダが、指定された「algorithm/mode/padding」変換に対応するCipherSpiのサブクラスを登録済みかどうかをチェックする。

    登録済みの場合、このクラスをインスタンス化します。

    回答が「いいえ」の場合、次のステップに進みます。
  2. プロバイダが、サブ変換「algorithm/mode」に対応するCipherSpiのサブクラスを登録済みかどうかをチェックする。

    回答が「はい」の場合、これをインスタンス化してから、新規インスタンスに対しengineSetPadding(padding)を呼び出します。

    回答が「いいえ」の場合、次のステップに進みます。
  3. プロバイダが、サブ変換「algorithm//padding」(ダブル・スラッシュに注意)に対応するCipherSpiのサブクラスを登録済みかどうかをチェックする。

    回答が「はい」の場合、これをインスタンス化してから、新規インスタンスに対しengineSetMode(mode)を呼び出します。

    回答が「いいえ」の場合、次のステップに進みます。
  4. プロバイダが、サブ変換「algorithm」に対応するCipherSpiのサブクラスを登録済みかどうかをチェックする。

    回答が「はい」の場合、これをインスタンス化してから、新規インスタンスに対しengineSetMode(mode)およびengineSetPadding(padding)を呼び出します。

    回答が「いいえ」の場合、例外NoSuchAlgorithmExceptionをスローします。

ステップ4: コードのコンパイル

実装コードの作成(ステップ1)、プロバイダの命名(ステップ2)、およびマスター・クラスの作成(ステップ3)が完了したら、Javaコンパイラを使ってファイルをコンパイルします。

ステップ5: JARファイルへのプロバイダの記述

次のステップ(JARファイルへの署名)の準備として、JARファイルにプロバイダ・コードを記載します。jarツールの詳細は、jar (Solaris、LinuxまたはMac OS X用Microsoft Windows用)を参照してください。

    jar cvf <JAR file name> <list of classes, separated by spaces>

このコマンドにより、指定されたクラスを含む、指定された名前のJARファイルが作成されます。

ステップ6: JARファイルの署名

プロバイダがCipher KeyAgreement、KeyGenerator、MacまたはSecretKeyFactoryクラスによって暗号化アルゴリズムを提供している場合、実行時にJCAがコードを認証できるように、JARファイルに署名する必要があります。詳細は、Step 1aを参照してください。この種類の実装を提供していない場合は、このステップはスキップできます。

ステップ6.1: コード署名証明書の取得

次のステップは、コード署名証明書の要求です。テストに先立ち、コード署名証明書を使用してプロバイダへの署名を行います。証明書は、テスト環境と本番環境の両方で利用できます。有効期間は5年間です。

次に、コード署名証明書の取得方法を示します。keytoolツールの詳細は、keytool (Solaris、LinuxまたはMac OS X用Microsoft Windows用)を参照してください。

  1. keytoolを使用して、DSA鍵ペアを生成します。例として、DSAアルゴリズムを使用します。
        keytool -genkeypair -alias <alias> \
            -keyalg DSA -keysize 1024 \
            -dname "cn=<Company Name>, \
            ou=Java Software Code Signing,\
            o=Sun Microsystems Inc" \
            -keystore <keystore file name>\
            -storepass <keystore password>
            
    

    これにより、DSA鍵ペア(公開鍵および関連する非公開鍵)が生成され、指定されたキーストアのエントリに格納されます。公開鍵は、自己署名証明書に格納されます。これ以降、キーストアのエントリには、指定された別名を使用してアクセスできます。


    角カッコ(「<」と「>」)内のオプション値には、実際の値を指定する必要があります。たとえば、<alias>は、新しく生成するキーストアのエントリを参照する際に使用する任意の別名で置き換え、<keystore file name>は、使用するキーストアの名前で置き換えます。注: 実際の値には、角カッコを付けないでください。たとえば、別名をmyTestAliasにする場合、-aliasオプションを次のように指定します。

        -alias myTestAlias
    
    まだ存在しないキーストアを指定すると、そのキーストアが作成されます。


    注: 入力するコマンド行を、実行するkeytool -genkeypairコマンドと同じ長さにできない場合(Microsoft WindowsのDOSプロンプトに入力する場合など)、コマンドを含むプレーン・テキストのバッチ・ファイルを作成して実行できます。つまり、keytool -genkeypairコマンドだけを含むテキスト・ファイルを新規作成します。なお、コマンドは、全体を1行に入力してください。拡張子.batを付けてファイルを保存します。DOSウィンドウで、ファイル名を(必要に応じてパスを付けて)入力します。これで、バッチ・ファイルに記述されたコマンドが実行されます。



  2. keytoolを使用して、証明書署名要求を生成します。
        keytool -certreq -alias <alias> \
            -file <csr file name> \
            -keystore <keystore file name> \
            -storepass <keystore password> 
    
    ここで、<alias>には、前のステップで作成したDSA鍵ペア・エントリの別名を指定します。このコマンドにより、証明書署名要求(CSR)がPKCS#10形式で生成されます。<csr file name>で指定した名前のファイルにCSRが格納されます。
  3. CSR、連絡先情報、およびその他の必須ドキュメントをJCAコード署名証明書発行局に送ります。連絡先情報については、「JCA Code Signing Certification Authority」を参照してください。
  4. 電子メール・メッセージを受信すると、JCAコード署名証明書発行局は、要求番号を電子メールで返信します。この要求番号を受信したら、Certification Form for CSPsを出力して記入し、送信します。連絡先情報については、「Sending Certification Form for CSPs」を参照してください。
  5. keytoolを使用してCAから受信した証明書をインポートします。

    JCAコード署名証明書発行局から2つの証明書を受信したら、keytoolを使用してキーストアにそれらをインポートできます。

    最初に、CAの証明書を「信頼できる証明書」としてインポートします。
        keytool -import -alias <alias for the CA cert> \
            -file <CA cert file name> \
            -keystore <keystore file name> \
            -storepass <keystore password>
    
    次に、コード署名証明書をインポートします。
        keytool -import -alias <alias> \
            -file <code-signing cert file name> \
            -keystore <keystore file name> \
            -storepass <keystore password>
    
    ここで、<alias>にはステップ1 (DSA鍵ペアの生成)で作成したのと同じ別名を指定します。このコマンドにより、<alias>で指定されたキーストア・エントリ内の自己署名証明書が、JCAコード署名証明書発行局が署名した証明書で置き換えられます。

これで、JCAにより信頼されたエンティティ(JCAコード署名証明書発行局)からの証明書がキーストア内に保存されたため、JARファイル内にプロバイダ・コードを記述し(ステップ5)、この証明書を使用してJARファイルに署名できます(ステップ6.2)。

ステップ6.2: プロバイダの署名

ステップ6で取得したコード署名証明書を使用して、ステップ5で作成したJARファイルに署名します。jarsignerツールの詳細は、jarsigner (Solaris、LinuxまたはMac OS X用Microsoft Windows用)を参照してください。

    jarsigner -keystore <keystore file name> \
        -storepass <keystore password> \
        <JAR file name> <alias>

ここで、<alias>には、JCAコード署名証明書発行局から受け取ったコード署名証明書を含むエントリ用キーストアの別名(ステップ6.1のコマンドで指定した別名)を指定します。

次の方法で、署名を検証できます。

    jarsigner -verify <JAR file name> 

検証が成功すると、「jar verified」というテキストが表示されます。


注意: ユーザー操作を最適化するため、署名されたJCEプロバイダをRIA (アプレットまたはWebstartアプリケーション)の一部としてバンドルする場合は、JCEプロバイダのJARに2番目の署名を適用するときに、アプレットまたはWebstartアプリケーションへの署名に使用したのと同じ証明書/鍵を使用するようにしてください。RIAの配備の詳細は、Java Platform, Standard Edition配備ガイドを参照し、JARファイルに複数の署名を適用する方法については、jarsigner (Solaris、LinuxまたはMac OS X用Windows用)のマニュアル・ページを参照してください。


ステップ7: テストの準備

次の手順では、新しいプロバイダをJCAで使用できるようにインストールおよび構成する方法について説明します。

ステップ7.1: プロバイダのインストール

プロバイダのテスト準備を行うために、プロバイダを使用するクライアントが行うのと同じ方法で、プロバイダをインストールする必要があります。インストールを実行すると、Java Securityはクライアントの要求に応じてアルゴリズムの実装を検出できるようになります。

プロバイダのインストールには、次の2つのステップが含まれます。プロバイダ・パッケージ・クラスのインストール、およびプロバイダの構成です。

プロバイダ・クラスのインストール

最初に行う事柄は、作成したクラスを利用可能にして、要求時に検出できるようにすることです。プロバイダ・クラスはJar (Java ARchive)ファイルの形式で提供します。

プロバイダ・クラスのインストール方法は2種類あります。

プロバイダJARファイルは、次に示すインストール型拡張機能JARファイルの標準位置に配置された場合、インストール型拡張機能と見なされます。

ここで、<java-home>は、実行ソフトウェアのインストール先ディレクトリ(Java Runtime Environment (JRE)のトップレベル・ディレクトリまたはJava SE (JDK)ソフトウェアのjreディレクトリ)を指します。たとえば、JDKを/home/user1/jdkディレクトリ(Solaris、LinuxまたはMac OS X)、あるいはC:\jdkディレクトリ(Microsoft Windows)にインストールした場合は、JARファイルを次のディレクトリにインストールする必要があります。

同様に、JREを/home/user1/jreディレクトリ(Solaris、LinuxまたはMac OS X)、あるいはC:\jreディレクトリ(Microsoft Windows)にインストールした場合は、JARファイルを次のディレクトリにインストールする必要があります。

インストール型」拡張機能の詳細は、「インストール型拡張機能」を参照してください。

バンドル型」拡張機能の詳細は、「バンドル型拡張機能」を参照してください。

プロバイダの構成

次の手順では、認可プロバイダのリストにこのプロバイダを追加します。これは、セキュリティ・プロパティ・ファイルを編集することにより、静的に行われます。

ここで、<java-home>はJREがインストールされているディレクトリを指します。たとえば、JDKを/home/user1/jdkディレクトリ(Solaris、LinuxまたはMac OS X)、あるいはC:\jdkディレクトリ(Microsoft Windows)にインストールした場合は、次のファイルを編集する必要があります。

同様に、JREを/home/user1/jreディレクトリ(Solaris、LinuxまたはMac OS X)、あるいはC:\jreディレクトリ(Windows)にインストールした場合は、このファイルを編集する必要があります。

プロバイダごとに、このファイルは次の形式の文を保持します。

    security.provider.n=masterClassName 

これはプロバイダを宣言し、その優先順位nを指定します。優先順位とは、特定プロバイダの指定がないときに、要求されたアルゴリズムについてプロバイダを検索する順序です。順位は1から始まり、1が最優先で次に2、3...と続きます。

masterClassNameには、ステップ3で実装したプロバイダの「マスター・クラス」の完全修飾名を指定する必要があります。このクラスは、常にProviderクラスのサブクラスです。

Javaには、SUNSunRsaSign、およびSunJCEという名前のプロバイダが標準で搭載されています。このプロバイダは、次に示すように、静的プロバイダとしてjava.securityプロパティ・ファイル内で自動的に構成されます。

    security.provider.2=sun.security.provider.Sun
    security.provider.3=sun.security.rsa.SunRsaSign
    security.provider.4=sun.security.provider.SunJCE

SUNプロバイダのマスター・クラスは、sun.security.providerパッケージ内のSunクラスです。

JCAプロバイダSunJCEおよびJavaプラットフォームに含まれるほかのセキュリティ関連プロバイダは、静的プロバイダとして自動的に構成されます。

ほかのJCAプロバイダを使用する場合は、行を追加してプロバイダを登録し、SUNおよびSunRsaSignプロバイダよりも低い優先順位を設定します。

例として、マスター・クラスがcom.cryptox.providerパッケージのCryptoXクラスで、プロバイダを4番目の優先順位とする場合を考えてみましょう。そのためには、java.securityファイルを次のように編集します。

    security.provider.2=sun.security.provider.Sun
    security.provider.3=sun.security.rsa.SunRsaSign
    security.provider.4=com.cryptox.provider.CryptoX
    security.provider.5=sun.security.provider.SunJCE

注意: プロバイダは動的に登録することもできます。その場合、プログラム(ステップ8で記述したテスト・プログラムなど)は、SecurityクラスのaddProviderメソッドまたはinsertProviderAtメソッドを呼び出します。こうした登録は持続的なものではありません。また、実行できるのは次の権限を付与されたコードのみです。

    java.security.SecurityPermission "insertProvider.{name}"

ここで、{name}には実際のプロバイダ名を指定します。

たとえば、プロバイダ名が「MyJCE」であり、プロバイダのコードが/localWorkディレクトリのmyjce_provider.jarファイル内に存在する場合、アクセス権を付与するサンプル・ポリシー・ファイルのgrant文は次のようになります。

    grant codeBase "file:/localWork/myjce_provider.jar" {
        permission java.security.SecurityPermission
            "insertProvider.MyJCE";
    };

ステップ7.2: プロバイダ・アクセス権の設定

プロバイダがインストール型拡張機能ではない場合、セキュリティ・マネージャがインストール済みの状態で、アプレットまたはアプリケーションを実行する際、常にプロバイダにアクセス権を付与する必要があります。通常、アプレットの実行時にはセキュリティ・マネージャが常にインストールされます。アプリケーションの場合でも、アプリケーション自体のコードまたはコマンド行引数で指定することにより、セキュリティ・マネージャをインストールできます。デフォルトのシステム・ポリシー・ファイルはインストール型拡張機能にすべてのアクセス権を付与するため、インストール型拡張機能にアクセス権を付与する必要はありません。

クライアントがプロバイダをインストール型拡張機能としてインストールしない場合、クライアント環境で次のアクセス権をプロバイダに常に付与する必要があります。

セキュリティ・マネージャがインストールされていて、プロバイダがインストール型拡張機能でない場合は、プロバイダが正しく機能するかどうかを確認するため、インストールおよび実行環境をテストする必要があります。なお、テストを実施する前に、プロバイダとこのプロバイダが使用するすべてのプロバイダに適切なアクセス権を付与しておく必要があります。たとえば、名前が「MyJCE」で、コードがmyjce_provider.jar内に存在するプロバイダにアクセス権を付与するサンプル・コードを次に示します。この種の文は、ポリシー・ファイルに記述されます。この例では、myjce_provider.jarファイルは/localWorkディレクトリに格納されるものとします。

    grant codeBase "file:/localWork/myjce_provider.jar" {
        permission java.lang.RuntimePermission "getProtectionDomain";
        permission java.security.SecurityPermission
            "putProviderProperty.MyJCE";
    };

ステップ8: テスト・プログラムの記述とコンパイル

Security APIへのプロバイダの統合、およびそのアルゴリズムをテストする1つ以上のテスト・プログラムの記述およびコンパイルを実行します。必要に応じて、暗号化の行われるテスト・データ用ファイルなどのサポート用ファイルを作成します。

プログラムが実行する最初のテストでは、プロバイダの検出、およびその名前、バージョン番号、追加情報が予期されたとおりかどうかを確認します。このために、次のようなコードを記述できます。MyPro部分は、独自のプロバイダ名に置き換えてください。

    import java.security.*;

    Provider p = Security.getProvider("MyPro");

    System.out.println("MyPro provider name is " + p.getName());
    System.out.println("MyPro provider version # is " + p.getVersion());
    System.out.println("MyPro provider info is " + p.getInfo());

次に、サービスが検出されることを確認します。たとえば、AES暗号化アルゴリズムを実装した場合には、要求時にこのアルゴリズムが確実に検出されるかどうかを、次のコードを使用して確認できます(ここでもMyProは、独自のプロバイダ名に置き換えてください)。

    Cipher c = Cipher.getInstance("AES", "MyPro");

    System.out.println("My Cipher algorithm name is " + c.getAlgorithm());

getInstanceへの呼出しでプロバイダ名を指定しない場合、そのアルゴリズムを実装するプロバイダが検出されるまで、登録されたすべてのプロバイダが優先順位に従って検索されます(「プロバイダの構成」を参照)。

プロバイダが免責メカニズムを実装している場合、免責メカニズムを使用するテスト・アプレットまたはアプリケーションを記述する必要があります。この種のアプレット/アプリケーションにも、署名、および「アクセス権ポリシー・ファイル」のバンドルが必要です。アプリケーションの作成およびテスト方法の詳細は、「Java暗号化アーキテクチャ・リファレンス・ガイド」の「アプリケーションの暗号化制限の「免責」を取得する方法」を参照してください。

ステップ9: テスト・プログラムの実行

テスト・プログラムを実行します。コードをデバッグし、必要に応じてテストを続行します。Java Security APIがアルゴリズムを検出できないようであれば、前述のステップを確認し、すべてのステップが完了していることを確認してください。

複数のインストール・オプション(プロバイダをインストール型拡張機能にする、またはクラス・パス内に配置するなど)および実行環境(セキュリティ管理を実行する、または実行しない)を使用して、プログラムを確実にテストしてください。インストール・オプションの詳細は、ステップ7.1を参照してください。特に、セキュリティ・マネージャをインストールし、かつプロバイダがインストール型拡張機能ではない(つまり、プロバイダにアクセス権を付与する必要がある)場合、プロバイダが正しく機能するかどうかを確認するため、ステップ7.2の手順に従ってプロバイダおよびそのプロバイダが使用するほかのプロバイダに必要なアクセス権を付与したあと、インストールおよび実行環境をテストする必要があります。

テストの結果、コードの修正が必要になった場合には、変更および再コンパイル(ステップ4)、JARファイルへの更新されたプロバイダ・コードの記載(ステップ6)、必要に応じたJARファイルへの署名(ステップ6.2)、プロバイダの再インストール(ステップ7.1)、必要に応じたアクセス権の修正または追加(Step 7.2)を実行してから、プログラムを再テストします。そのあと、必要に応じてこれらのステップを繰り返します。

ステップ10: 米国政府による輸出承認の申請(必要な場合)

プロバイダを米国外に輸出する可能性のある米国内のベンダーはすべて、米国商務省産業安全保障局に輸出承認申請を行う必要があります。詳細は、輸出問題を担当する顧問弁護士に確認してください。

注: プロバイダがCipher.getInstance()を呼び出し、返されるCipherオブジェクトで、ユーザーがダウンロードした管轄ポリシー・ファイルで許可されている暗号化の強度に関係なく強力な暗号化を実行する必要がある場合は、その暗号化強度に対応したアクセス権が指定されている、JARファイルにバンドルする予定のcryptoPermsアクセス権ポリシー・ファイルのコピーを含める必要があります。このファイルが必要な理由は、アプレットおよびアプリケーションが暗号化制限を「免責」されるために、JARファイルにcryptoPermsアクセス権ポリシー・ファイルを含める必要があるのと同じ理由です。このようなファイルの作成と組込みの詳細は、「Java暗号化アーキテクチャ・リファレンス・ガイド」の「アプリケーションの暗号化制限の「免責」を取得する方法」を参照してください。

役に立つと思われるURLを2つ紹介しておきます。

ステップ11: プロバイダおよびそのサポート対象サービスのドキュメント化

次のステップは、クライアント用のドキュメントを記述することです。少なくとも、次の指定が必要です。

さらに、ドキュメント内で、デフォルトのアルゴリズム・パラメータなどの、クライアントに関係するその他の指定も行う必要があります。

メッセージ・ダイジェストおよびMAC

メッセージ・ダイジェストおよびMACアルゴリズムごとに、実装が複製可能かどうかを指定します。これは、技術的には必須ではありませんが、複製による中間メッセージ・ダイジェストまたはMACが可能かどうかを指定することになるため、クライアントの費やす時間およびコードの記述にかかる手間が軽減されます。MessageDigestまたはMacの実装が複製可能かどうかがわからない場合、クライアントはオブジェクトの複製を試みて、発生する可能性のある例外をキャッチすることにより、複製可能かどうかを識別できます。次にその例を示します。

    try {
        // try and clone it
        /* compute the MAC for i1 */
        mac.update(i1);
        byte[] i1Mac = mac.clone().doFinal();

        /* compute the MAC for i1 and i2 */
        mac.update(i2);
        byte[] i12Mac = mac.clone().doFinal();

        /* compute the MAC for i1, i2 and i3 */
        mac.update(i3);
        byte[] i123Mac = mac.doFinal();
    } catch (CloneNotSupportedException cnse) {
        // have to use an approach not involving cloning
    } 

次にそれぞれの意味を示します。

鍵ペア・ジェネレータ

鍵ペア・ジェネレータ・アルゴリズムでは、クライアントが(initializeメソッドの呼出しを介して)明示的に鍵ペア・ジェネレータを初期化しない場合、各プロバイダはデフォルトの初期化を提供およびドキュメント化する必要があります。たとえば、SunJCEプロバイダにより提供されるDiffie-Hellman鍵ペア・ジェネレータは、1024ビットのデフォルト素数モジュラス・サイズ(keysize)を使用します。

鍵ファクトリ

プロバイダは、その(秘密)鍵ファクトリがサポートするすべての鍵仕様をドキュメント化する必要があります。

アルゴリズム・パラメータ・ジェネレータ

クライアントがAlgorithmParameterGeneratorエンジン・クラスのinitメソッドの呼出しを介してアルゴリズム・パラメータ・ジェネレータを明示的に初期化しない場合、各プロバイダはデフォルトの初期化を行い、これをドキュメント化する必要があります。たとえば、SunJCEプロバイダは、Diffie-Hellmanパラメータの生成に1024ビットのデフォルト素数モジュラス・サイズ(keysize)を使用し、SUNプロバイダは、DSAパラメータの生成に1024ビットのデフォルト・モジュラス素数サイズを使用します。

署名アルゴリズム

署名アルゴリズムを実装する場合、署名(signメソッドの1つを使って生成される)をエンコードする形式をドキュメント化する必要があります。たとえば、SUNプロバイダにより提供されるSHA256withDSA署名アルゴリズムでは、署名は、2つの整数rおよびsの標準ASN.1 SEQUENCEとしてエンコードされます。

乱数生成(SecureRandom)アルゴリズム

乱数生成アルゴリズムの場合、生成される数がどのように「ランダム」なのかを示す情報、および乱数ジェネレータの自己シード時のシードの質に関する情報を提供します。同時に、SecureRandomオブジェクト(およびそのカプセル化されたSecureRandomSpi実装オブジェクト)の直列化解除時に、何が発生するかにも留意してください。復元されたオブジェクトのnextBytesメソッド(カプセル化されたSecureRandomSpiオブジェクトのengineNextBytesメソッドを呼び出す)への続く呼出しにより、元のオブジェクトが生成するのと正確に同じ(ランダム)バイトが生成される場合、この動作が望ましくないのであれば、setSeedメソッドを呼び出して復元されたランダム・オブジェクトにシードをするよう、ユーザーに通知してください。

証明書ファクトリ

プロバイダは、ファクトリが作成可能な証明書の種類(および必要に応じてそのバージョン番号)をドキュメント化する必要があります。

キーストア

プロバイダは、キーストアの実装に関するすべての関連情報(ベースとなるデータ形式など)をドキュメント化する必要があります。

ステップ12: クラス・ファイルおよびドキュメントをクライアントから利用可能にする

プロバイダ・ソフトウェアの記述、構成、テスト、インストール、およびドキュメント化のあとで、ドキュメントをカスタマから利用可能にします。

プロバイダによる自己整合性チェックの実行方法

各プロバイダは自己整合性チェックを実行して、プロバイダ・メソッドをJCAを介さずに直接呼び出すなどの操作により、コードを含むJARファイルが改変されていないことを保証する必要があります。暗号化サービス(Cipher、KeyAgreement、KeyGenerator、MAC、またはSecretKeyファクトリ)の実装を提供するプロバイダは、デジタル署名され、「信頼できる」証明書発行局が発行する証明書で署名されている必要があります。現状では、次の2種類の証明書発行局が「信頼できる」とされています。

Sun MicrosystemsのJCAコード署名CAからコード署名証明書を取得する方法については、ステップ6.2を参照してください。

署名済み証明書を前述の証明書発行局から入手した後、プロバイダ・パッケージに署名する証明書用のバイトを埋め込む必要があります。たとえば、後述する「個々の署名者の識別と信頼できる署名者の特定」の項で解説する、bytesOfProviderCert配列のような配列です。実行時には、埋め込まれた証明書を使用して、プロバイダ・コードが認証されたものかどうかが判断されます。

プロバイダが自らの整合性チェックに使用する基本的な方法を、次に示します。

  1. プロバイダ・コードを含むJARファイルのURLを特定します。
  2. JARファイルのデジタル署名を検証して、JARファイルの各エントリの少なくとも1つの署名者が信頼できることを確認します。

次のセクションでは、具体的な手順を示します。

サンプル・コードについて

注: サンプル・コードMyJCE.javaは、これらのステップを実装する完全なコード例です。このコードは参照用としてダウンロードできます。前述の概念がどのようにしてサンプル・コードに実装されているかについては、「サンプル・コードについて」のセクションを参照してください。


重要: JCE 1.2.x (JDK 1.2.xおよび1.3.xとともに使用される)のバンドルされていないバージョンでは、プラグインするJCAの整合性および信頼性を保証するため、JCAフレームワークの認証用コードをプロバイダに含める必要がありました。JDK 6以降では、これは必要なくなりました。

これにより、JCEフレームワーク・コードはプロバイダの期待する場所に存在しなくなるため、プロバイダのJCEフレームワーク認証チェックが機能しなくなります。このため、JCE 1.2.2専用に記述されたプロバイダは、JDK 6では動作しなくなります。プロバイダがJDK 6でのみ動作するようにするには、JCEフレームワークを認証するコードをプロバイダに含めないようにしてください。プロバイダをJCE 1.2.2とJDK 6の両方で動作させる場合は、条件文を追加します。こうしておけば、プロバイダをJCE 1.2.2で実行する場合にのみJCEフレームワークを認証するプロバイダ・コードが実行されるようになります。次に、サンプル・コードを示します。

    Class cipherCls = Class.forName("javax.crypto.Cipher");

    CodeSource cs = cipherCls.getProtectionDomain().getCodeSource();
    if (cs != null) {
        // Authenticate JCE framework
. . . }

プロバイダJARファイルの検索: 基本

プロバイダのJARファイルURLの確認

プロバイダのJARファイルのURLは、プロバイダのCodeSourceを確認し、CodeSourceに対してgetLocationメソッドを呼び出すことにより取得できます。

    URL providerURL = (URL) AccessController.doPrivileged(
        new PrivilegedAction) {
            public Object run() {
                CodeSource cs =
                    MyJCE.class.getProtectionDomain().getCodeSource();
                return cs.getLocation();
            }
        }); 

JARファイルを参照するJarFileの作成

プロバイダのJARファイルのURLを取得したら、JARファイルを参照するjava.util.jar.JarFileを作成できます。これは、「プロバイダJARファイルの検証」のステップで必要になります。

JARファイルを作成するには、まず、openConnectionメソッドを呼び出し、指定されたURLへの接続を確立します。URLはJAR URLであるため、java.net.JarURLConnection型になります。標準的なコードは次のとおりです。

    // Prep the url with the appropriate protocol.
    jarURL =
        url.getProtocol().equalsIgnoreCase("jar") ? url :
            new URL("jar:" + url.toString() + "!/");

    // Retrieve the jar file using JarURLConnection
    JarFile jf = (JarFile) AccessController.doPrivileged(
        new PrivilegedExceptionAction() {
            public Object run() throws Exception {
                JarURLConnection conn =
                    (JarURLConnection) jarURL.openConnection();
        ...  

JarURLConnectionを取得できたら、getJarFileメソッドを呼び出してJARファイルを取得します。

    // Always get a fresh copy, so we don't have to
    // worry about the stale file handle when the
    // cached jar is closed by some other application.
    conn.setUseCaches(false);
    jf = conn.getJarFile(); 

プロバイダJARファイルの検証: 基本

前述のステップに従ってプロバイダJARファイルのURLを確認し、JARファイルを参照するJarFileを作成したら、ファイルの検証を行います。

基本的な方法は次のとおりです。

  1. 各エントリの署名者の証明書の少なくとも1つが、プロバイダ自身のコード署名証明書と等価であることを確認します。
  2. JARファイルのすべてのエントリを確認して、各エントリの署名が適切であることを検証します。
  3. 各エントリの署名者の証明書の少なくとも1つから、信頼できる証明書発行局までたどれることを確認します。

これらのステップで使用するサンプル・コードについては、次のセクションを参照してください。

検証の設定

ここでは、クラスJarVerifierを定義して指定されたURLからのJARファイル取得を処理し、JARファイルが指定された証明書で署名されているかどうかを検証します。

JarVerifierのコンストラクタはプロバイダURLをパラメータとして取ります。これを使用して、JARファイルが取得されます。

実際のJAR検証は、プロバイダ・コード署名証明書をパラメータとするverifyメソッドで実装されます。

    public void verify(X509Certificate targetCert) throws IOException {
        // variable 'jarFile' is a JarFile object created
        // from the provider's Jar URL.
        ...
        Vector entriesVec = new Vector(); 

verifyは、基本的にJARファイル・エントリを2回使用します。1回目は各エントリの署名をチェックし、2回目は署名者が信頼できることを検証します。

注: このコードでは、jarFile変数は、プロバイダのjarファイルのJarFileオブジェクトです。

JARファイル署名のチェック

認証されたプロバイダJARファイルが署名されます。このため、署名されないJARファイルは、改変されています。

    // Ensure the jar file is signed.
    Manifest man = jarFile.getManifest();
    if (man == null) {
        throw new SecurityException("The provider is not signed");
    } 

署名の検証

次のステップでは、JARファイルのすべてのエントリを確認して、各エントリの署名が適切であることを検証します。JARファイル・エントリの署名を検証する1つの方法は、単純なファイルの読取りです。JARファイルが署名されていると、readメソッドが自動的に署名の検証を実行します。サンプル・コードを次に示します。

    // Ensure all the entries' signatures verify correctly
    byte[] buffer = new byte[8192];
    Enumeration entries = jarFile.entries();

    while (entries.hasMoreElements()) {
        JarEntry je = (JarEntry) entries.nextElement();

        // Skip directories.
        if (je.isDirectory())
            continue;

        entriesVec.addElement(je);
        InputStream is = jarFile.getInputStream(je);

        // Read in each jar entry. A security exception will
        // be thrown if a signature/digest check fails.
        int n;
        while ((n = is.read(buffer, 0, buffer.length)) != -1) {
            // Don't care
        }
        is.close();
    }
    

署名者の信頼性の確認

前のセクションのコードでは、すべてのプロバイダJARファイル・エントリの署名を検証しました。すべてを適正に検証することは必須ですが、JARファイルの認証を確認するだけでは十分ではありません。最終的に、署名がこのプロバイダを構築したものと同じエントリから生成されたことを確認する必要があります。署名が信頼できることをテストするために、JARファイル内の各エントリをループ処理し(今回は前のステップで作成したentriesVecを使用)、署名の必要なエントリ(META-INFディレクトリ内に存在しないディレクトリ以外のエントリ)ごとに次の操作を実行します。

  1. エントリの署名者証明書のリストを取得します。
  2. 個々の証明書チェーンを特定し、信頼できる証明書チェーンがあるかどうかを判定します。信頼できる証明書チェーンが1つ以上存在しなければなりません。

ループの設定方法を、次に示します。

    Enumeration e = entriesVec.elements();
    while (e.hasMoreElements()) {
        JarEntry je = (JarEntry) e.nextElement();
        ...
    } 

証明書リストの取得

JARファイル・エントリJarEntryの署名者の証明書は、JarEntrygetCertificatesメソッドを呼び出すだけで取得できます。

    Certificate[] certs = je.getCertificates();

前のループ設定コードに、上記のコードおよびMETA-INFディレクトリ内のディレクトリおよびファイルを無視するコードを追加すると、次のようになります。

    while (e.hasMoreElements()) {
        JarEntry je = (JarEntry) e.nextElement();

        // Every file must be signed except files in META-INF.
        Certificate[] certs = je.getCertificates();
        if ((certs == null) || (certs.length == 0)) {
            if (!je.getName().startsWith("META-INF"))
                throw new SecurityException(
                    "The provider has unsigned class files.");
            } else {
                // Check whether the file is signed by the expected
                // signer. The jar may be signed by multiple signers.
                // See if one of the signers is 'targetCert'.
                ...
            }
        ...  

個々の署名者の識別と信頼できる署名者の特定

JarEntrygetCertificatesメソッドが返す証明書配列には、1つ以上の証明書チェーンが含まれます。エントリの署名者ごとに1つの連鎖が存在します。各連鎖には、1つ以上の証明書が含まれます。連鎖内の各証明書は、前の証明書の公開鍵を認証します。

連鎖内の最初の証明書は、エントリの署名に実際に使用される非公開鍵に対応する公開鍵を含む、署名者の証明書です。そのあとに続く証明書は、それぞれ前の証明書の発行者の証明書になります。自己整合性チェックは、JARファイルがプロバイダの署名証明書で署名されているかどうかを基準にしているため、信頼性の判断は最初の証明書である署名者の証明書のみで決定されます。

証明書チェーンの配列内で、「信頼できる」エンティティが見つかるまで各チェーンおよび関連する署名者をチェックします。JARファイル・エントリごとに、信頼できる署名者が少なくとも1人必要です。署名者が「信頼できる」と判断されるのは、証明書が、埋め込まれたプロバイダ署名証明書と等価である場合にかぎります。

次のサンプル・コードでは、すべての証明書チェーンでループ処理を行い、チェーンの最初の証明書と埋め込まれたプロバイダ署名証明書とを比較し、一致した場合にtrueだけを返します。

    int startIndex = 0;
    X509Certificate[] certChain;
    boolean signedAsExpected = false;

    while ((certChain = getAChain(certs, startIndex)) != null) {
        if (certChain[0].equals(targetCert)) {
            // Stop since one trusted signer is found.
            signedAsExpected = true;
            break;
        }

        // Proceed to the next chain.
        startIndex += certChain.length;
    }

    if (!signedAsExpected) {
        throw new SecurityException(
            "The provider is not signed by a trusted signer");
    }
    

getAChainメソッドは、次のように定義します。

    /**
     * Extracts ONE certificate chain from the specified certificate array
     * which may contain multiple certificate chains, starting from index
     * 'startIndex'.
     */
    private static X509Certificate[] getAChain(
            Certificate[] certs, int startIndex) {

        if (startIndex > certs.length - 1)
            return null;

        int i;
        // Keep going until the next certificate is not the
        // issuer of this certificate.
        for (i = startIndex; i < certs.length - 1; i++) {
            if (!((X509Certificate)certs[i + 1]).getSubjectDN().
                    equals(((X509Certificate)certs[i]).getIssuerDN())) {
                break;
            }
        }

        // Construct and return the found certificate chain.
        int certChainSize = (i-startIndex) + 1;
        X509Certificate[] ret = new X509Certificate[certChainSize];
        for (int j = 0; j < certChainSize; j++ ) {
            ret[j] = (X509Certificate) certs[startIndex + j];
        }
        return ret;
    }
    

myJCEコード・サンプルについて

サンプル・コードMyJCE.javaは、自動的に整合性チェックを実行するselfIntegrityCheckingメソッドを備えたサンプル・プロバイダです。このコードは、まず固有のプロバイダJARファイルのURLを特定します。次に、このプロバイダJARファイルに、組込み済みのコード署名証明書による署名があるかどうかを検証します。

注: selfIntegrityCheckingメソッドは、整合性を確保するため、その暗号化エンジン・クラスの全コンストラクタによって呼び出されます。

プロバイダMyJCEは、次のステップで自動的に整合性チェックを行います。

  1. 固有のクラスMyJCE.classを使ってプロバイダJARファイルにアクセスするためのURLを特定する。
  2. ステップ1で特定したプロバイダURLを使ってJarVerifierオブジェクトをインスタンス化する。
  3. 組込みバイト配列bytesOfProviderCertからX509Certificateオブジェクトを作成する。
  4. JarVerifier.verifyメソッドを呼び出し、プロバイダJARファイル内のすべてのエントリに、ステップ3でインスタンス化した同じ証明書による署名があることを検証する。

注: JarVerifierクラスは、所定のURLからJARファイルを取得し、そのJARファイルに署名があるか、すべてのエントリに有効な署名があるか、これらのエントリに指定されたX509Certificateによる署名があるかどうかを検証します。

場合によっては、JarVerifier.verifyによってセキュリティ例外がスローされます。

サンプル・コードMyJCE.javaは、前述のコードで構成されます。さらに、エラー処理、サンプル・コード署名証明書バイト、埋め込まれたサンプル・コード署名証明書バイトからX509Certificateオブジェクトをインスタンス化するコードが含まれています。

AccessController.doPrivilegedの使用については、「特権ブロックのためのAPI」でdoPrivilegedの使用に関する情報を参照してください。

実装の詳細および要件

別名

多くの暗号化のアルゴリズムおよび型には、「Java暗号化アーキテクチャ・リファレンス・ガイド」の「付録A」で定義された公式な「標準名」が1つ存在します。

たとえば、「MD5」は、RFC 1321のRSA DSIで定義されたRSA-MD5メッセージ・ダイジェスト・アルゴリズムの標準名です。DiffieHellmanは、PKCS3で定義されたDiffie-Hellman鍵協定アルゴリズムの標準です。

JDKでは、アルゴリズムまたは型への参照時に、クライアントが標準名ではなく別名を使用することを可能にするエイリアス化スキームが存在します。たとえば、「SUN」プロバイダのマスター・クラス(Sun.java)は、標準名が「SHA1withDSA」であるアルゴリズムの別名「SHA1/DSA」を定義します。このため、次の文は同じ意味になります。

    Signature sig = Signature.getInstance("SHA1withDSA", "SUN");

    Signature sig = Signature.getInstance("SHA1/DSA", "SUN");

別名は、「マスター・クラス」内で定義できます(ステップ3を参照)。別名を定義するには、次の名前のプロパティを作成します。

    Alg.Alias.engineClassName.aliasName

engineClassNameにはエンジン・クラス(Signatureなど)の名前が、aliasNameには設定する別名が当てはまります。プロパティのは、別名を設定するアルゴリズム(または型)の標準アルゴリズム(または型)名でなければなりません。

例として、「SUN」プロバイダが、Alg.Alias.Signature.SHA1/DSAという名前のプロパティに値SHA1withDSAを設定することにより、標準名がSHA1withDSAである署名アルゴリズムに別名「SHA1/DSA」を定義する場合を考えましょう。次にそのコードを示します。

    put("Alg.Alias.Signature.SHA1/DSA", "SHA1withDSA");

あるプロバイダによって定義された別名は、そのプロバイダのみが使用でき、その他のプロバイダは使用できません。したがって、SunJCEプロバイダによって定義された別名は、SunJCEプロバイダのみが使用できます。

サービスの相互依存性

アルゴリズムによっては、ほかの種類のアルゴリズムの使用を要求することがあります。たとえば、通常、PBEアルゴリズムは、メッセージ・ダイジェスト・アルゴリズムを使用して、パスワードを鍵に変換する必要があります。

別のアルゴリズムを要求するアルゴリズムを実装している場合、次のいずれかを実行できます。

  1. どちらのアルゴリズムにも対応した独自の実装の提供。
  2. すべてのJava SEプラットフォームのインストールに含まれるデフォルトの SUNプロバイダにより提供される場合のように、一方のアルゴリズムの実装が他方のアルゴリズムのインスタンスを使用するようにします。たとえば、実装しているPBEアルゴリズムがメッセージ・ダイジェスト・アルゴリズムを要求する場合、次の呼出しを行うことにより、SHA256メッセージ・ダイジェスト・アルゴリズムを実装するクラスのインスタンスを取得できます
        MessageDigest.getInstance("SHA256", "SUN")
  3. 別の特定のプロバイダにより提供される場合のように、一方のアルゴリズムの実装が他方のアルゴリズムのインスタンスを使用するようにします。これは、プロバイダを使用するすべてのクライアントが、インストールされた他方のプロバイダも保持する場合にだけ有効な方法です。
  4. 別の(無指定の)プロバイダにより提供される場合のように、一方のアルゴリズムの実装が他方のアルゴリズムのインスタンスを使用するようにします。つまり、アルゴリズムを名前で要求できます。ただし、特定のプロバイダは指定しません。次に例を示します。
        MessageDigest.getInstance("SHA256")
    これは、プロバイダが使用される各Javaプラットフォームにインストールされた、要求されたアルゴリズムの実装(この例ではSHA256)が少なくとも1つ存在することが確実な場合にだけ有効な方法です。

次に、アルゴリズムの相互依存の一般的な種類を示します。

署名アルゴリズムとメッセージ・ダイジェスト・アルゴリズム

署名アルゴリズムは、メッセージ・ダイジェスト・アルゴリズムを必要とすることがあります。たとえば、SHA256withDSA署名アルゴリズムは、SHA256メッセージ・ダイジェスト・アルゴリズムを必要とします。

署名アルゴリズムと(擬似)乱数生成アルゴリズム

署名アルゴリズムは、(擬似)乱数生成アルゴリズムの使用を必要とすることがあります。たとえば、DSA署名を生成するためには、対応する乱数生成アルゴリズムが必要です。

鍵のペア生成アルゴリズムとメッセージ・ダイジェスト・アルゴリズム

鍵のペア生成アルゴリズムは、メッセージ・ダイジェスト・アルゴリズムの使用を必要とすることがあります。たとえば、DSA鍵は、SHA-256メッセージ・ダイジェスト・アルゴリズムを使用して生成されます。

アルゴリズム・パラメータ生成とメッセージ・ダイジェスト・アルゴリズム

アルゴリズム・パラメータ・ジェネレータは、メッセージ・ダイジェスト・アルゴリズムの使用を必要とすることがあります。たとえば、DSAパラメータは、SHA-256メッセージ・ダイジェスト・アルゴリズムを使用して生成されます。

KeyStoresとメッセージ・ダイジェスト・アルゴリズム

キーストア実装は、メッセージ・ダイジェスト・アルゴリズムを利用して鍵ハッシュ(keyはユーザーが提供するパスワード)を計算し、キーストアの統合性検査、およびキーストアが改変されていないことを確認することがあります。

鍵のペア生成アルゴリズムとアルゴリズム・パラメータ・ジェネレータ

鍵のペア生成アルゴリズムは、新規アルゴリズム・パラメータ・セットの生成を必要とする場合があります。パラメータは、直接生成するか、アルゴリズム・パラメータ・ジェネレータを使用して生成できます。

鍵のペア生成、アルゴリズム・パラメータ生成、および(擬似)乱数生成アルゴリズム

鍵のペア生成アルゴリズムは、新しい鍵ペアおよび(場合によっては)鍵に関連付けられた新規パラメータ・セットの生成に、乱数の発生源を必要とする場合があります。乱数の発生源は、SecureRandomオブジェクトにより表されます。鍵ペア生成アルゴリズムの実装は、鍵パラメータ自体を生成する場合もあれば、アルゴリズム・パラメータ・ジェネレータを使って鍵パラメータを生成する場合もあります。これらの場合、乱数の発生源を使ってアルゴリズム・パラメータ・ジェネレータを初期化する場合もあれば、そうでない場合もあります。

アルゴリズム・パラメータ・ジェネレータおよびアルゴリズム・パラメータ

アルゴリズム・パラメータ・ジェネレータのengineGenerateParametersメソッドは、AlgorithmParametersインスタンスを返す必要があります。

署名および鍵ペア生成アルゴリズムまたは鍵ファクトリ

署名アルゴリズムを実装している場合、実装のengineInitSignメソッドおよびengineInitVerifyメソッドは、ベースとなるアルゴリズム(DSSアルゴリズム用のDSA鍵など)に対して有効な、引渡しの行われる鍵を必要とします。次のいずれかを実行できます。

  1. 適切なインタフェースを実装する独自のクラス(java.security.interfacesパッケージからDSAPrivateKeyおよびDSAPublicKeyインタフェースを実装するクラスなど)も作成し、独自の鍵ペアのジェネレータとこれらの型の鍵を返す鍵ファクトリのどちらかまたは両方を作成します。engineInitSignおよびengineInitVerifyに渡される鍵が、実装した鍵(つまり、鍵ペア・ジェネレータまたは鍵ファクトリから生成された鍵)と同じ型である必要があります。または、次の方法も可能です。
  2. 他の鍵ペア・ジェネレータまたは他の鍵ファクトリから鍵を受け取ります。これは、それらが署名の実装が必要な情報(非公開鍵、公開鍵および鍵パラメータなど)を取得することを可能にする適切なインタフェースのインスタンスである場合にかぎり有効です。たとえば、DSS Signatureクラス用のengineInitSignメソッドは、java.security.interfaces.DSAPrivateKeyのインスタンスである任意の非公開鍵を受け取ることができます。

キーストアと鍵および証明書ファクトリ

キーストアの実装は、鍵ファクトリを利用してキーストアに格納された鍵の構文解析を行うことがあります。また、証明書ファクトリを利用してキーストアに格納された証明書の構文解析を行います。

デフォルトの初期化

クライアントが明示的に鍵ペア・ジェネレータまたはアルゴリズム・パラメータ・ジェネレータを初期化しない場合、これらサービスの各プロバイダはデフォルトの初期化を提供(およびドキュメント化)する必要があります。たとえば、Sunプロバイダは、DSAパラメータの生成に1024ビットのデフォルト・モジュラス・サイズ(強度)を使用し、「SunJCE」プロバイダは、Diffie-Hellmanパラメータの生成に1024ビットのデフォルト・モジュラス・サイズ(キー・サイズ)を使用します。

デフォルトの鍵ペア・ジェネレータのパラメータの要件

鍵ペア・ジェネレータを実装する場合、クライアントがパラメータを指定しない場合は、実装がデフォルトのパラメータを提供する必要があります。提供するドキュメント(ステップ11)で、デフォルト・パラメータを指定する必要があります。

たとえば、SUNプロバイダのDSA鍵ペア・ジェネレータは、512、768、および1024ビットの鍵ペア生成用に、計算済みのpq、およびgデフォルト値のセットを提供します。次のpq、およびg値は、1024ビットDSA鍵ペア生成用のデフォルト値として使用されます。

p = fd7f5381 1d751229 52df4a9c 2eece4e7 f611b752 3cef4400 c31e3f80
    b6512669 455d4022 51fb593d 8d58fabf c5f5ba30 f6cb9b55 6cd7813b
    801d346f f26660b7 6b9950a5 a49f9fe8 047b1022 c24fbba9 d7feb7c6
    1bf83b57 e7c6a8a6 150f04fb 83f6d3c5 1ec30235 54135a16 9132f675
    f3ae2b61 d72aeff2 2203199d d14801c7

q = 9760508f 15230bcc b292b982 a2eb840b f0581cf5

g = f7e1a085 d69b3dde cbbcab5c 36b857b9 7994afbb fa3aea82 f9574c0b
    3d078267 5159578e bad4594f e6710710 8180b449 167123e8 4c281613
    b7cf0932 8cc8a6e1 3c167a8b 547c8d28 e0a3ae1e 2bb3a675 916ea37f
    0bfa2135 62f1fb62 7a01243b cca4f1be a8519089 a883dfe1 5ae59f06
    928b665e 807b5525 64014c3b fecf492a

(ここで指定されたpおよびq値は、素数生成標準により、次の160ビットのシードを使って生成されます。

SEED:  8d515589 4229d5e6 89ee01e6 018a237e 2cae64cd

このシードでは、カウンタが92の時にpおよびqを検出しました。)

Provider.Serviceクラス

導入されて以来、セキュリティ・プロバイダはHashtableエントリに入力された適切なフォーマットの鍵と値のStringペアを使用して、サービス情報を公開してきました。このメカニズムはシンプルで簡便ですが、カスタマイズの範囲がかぎられています。そのため、JDK 5.0では、2番目のオプションとして、Provider.Serviceクラスが導入されました。これを利用して、プロバイダはサービスおよびサポートに、次で説明するような追加機能を通知できます。この追加機能は、String値のHashTableエントリを使用する以前の方法と完全な互換性があることに留意してください。JDK 5.0では、プロバイダはどちらの方法も選択でき、また同時に両方を使用することもできます。

Provider.Serviceオブジェクトは、サービスに関するすべての情報をカプセル化します。このプロバイダは、サービス、型(MessageDigestSignatureなど)、アルゴリズム名およびサービスを実装するクラス名を提供します。また、オプションでこのサービス(別名)および属性に対する代替アルゴリズム名の一覧が含まれます。この一覧は、名前と値のStringペアのマップです。さらに、newInstance()およびsupportsParameter()メソッドを定義します。これらのメソッドはデフォルトで実装されていますが、プロバイダがハードウェア・セキュリティ・トークンとのインタフェースとなる場合は、必要に応じてプロバイダによるオーバーライドが可能です。

newInstance()メソッドは、新しい実装インスタンスを生成する必要がある場合に、セキュリティ・フレームワークによって使用されます。デフォルトの実装では、リフレクションを使用してサービス各種の標準的なコンストラクタが呼び出されます。CertStoreを除くすべての標準サービスで、このコンストラクタは引数を取りません。newInstance()constructorParameterは、この場合nullである必要があります。CertStore型のサービスでは、CertStoreParametersオブジェクトを使用するコンストラクタが呼び出されます。constructorParameterは、CertStoreParameters.のnull以外のインスタンスである必要があります。セキュリティ・プロバイダは、newInstance()メソッドをオーバーライドして、その実装に適したインスタンス化を実装できます。直接呼び出すことも、Providerインスタンスまたはトークンに固有の追加情報を渡すコンストラクタを呼び出すこともできます。たとえば、システムに複数のスマートカード・リーダーがある場合、新しく作成されたサービスをどのリーダーに関連付けるかという情報を渡すことができます。ただし、カスタマイズした場合でも、すべての実装は、前述のconstructorParameterに関する規約に従う必要があります。

supportsParameter()は、Serviceが指定されたパラメータを使用できるかどうかをテストします。このサービスがパラメータを使用できない場合、falseを返します。このサービスがパラメータを使用できる場合、すばやいチェックが実行できない場合、またはステータスが不明な場合、trueを返します。これは、ある種のサービスに対してセキュリティ・フレームワークにより使用され、一致しない実装が対象からすばやく除外されます。現状では、これはSignatureCipherMac、およびKeyAgreementの標準サービスでのみ定義されています。この場合、parameterKeyのインスタンスである必要があります。たとえば、Signatureサービスの場合、フレームワークはサービスをインスタンス化する前に、サービスが指定されたKeyを使用できるかどうかをテストします。デフォルトの実装では、前述のように、属性SupportedKeyFormatsおよびSupportedKeyClassesが検査されます。ここでも、プロバイダはこのメソッドをオーバーライドして、追加テストを実装できます。

SupportedKeyFormats属性は、エンコードされた鍵(key.getFormat()によって返される)でサポートされる形式の一覧で、「|」(パイプ)文字で区切られています。たとえば、X.509|PKCS#8です。SupportedKeyClasses属性は、インタフェースまたはクラス名の一覧で、「|」文字で区切られています。鍵オブジェクトは、指定されたクラスまたはインタフェースの1つ以上に割当て可能な場合に、受け入れ可能と認識されます。言い換えれば、鍵オブジェクトのクラスがリスト表示されたいずれかのクラスのサブクラス(またはクラス自体)である場合、またはリスト表示されたインタフェースを実装する場合です。たとえば、値"java.security.interfaces.RSAPrivateKey|java.security.interfaces.RSAPublicKey"がこれに該当します。

サービスの追加および検索用の4つのメソッドがProviderクラスに追加されました。前述のように、これらのメソッドおよび既存のPropertiesメソッドの実装は、既存のProviderサブクラスとの互換性を保つように特別に設計されています。次のようにして実現されます。

既存のPropertiesメソッドをエントリの追加に使用する場合、Providerクラスは、プロパティ文字列を解析して等価なServiceオブジェクトに変換してから、getService()を使用して検索するようにします。同様に、putService()メソッドを使用する場合、等価なプロパティ文字列がプロバイダのハッシュ表に同時に配置されます。プロバイダ実装がProviderクラス内のメソッドをオーバーライドする場合、実装がこの変換に干渉しないようにする必要があります。問題を防止するため、実装によってProviderクラスのメソッドがオーバーライドされないようにすることをお薦めします。

署名フォーマット

署名アルゴリズムを実装する場合、提供するドキュメント(ステップ11)で署名(signメソッドの1つを使って生成される)をエンコードする形式を指定する必要があります。

たとえば、Sunプロバイダにより提供されるSHA256withDSA署名アルゴリズムは、署名を、rおよびsという2つのASN.1 INTEGER値の標準ASN.1シーケンスとして、この順序でエンコードします。

SEQUENCE ::= {
        r INTEGER,
        s INTEGER }

DSAインタフェースおよびその実装要件

Java Security APIには、DSAサービスを実装するプログラマが利用しやすいように、(java.security.interfacesパッケージ内の)次のインタフェースが含まれます。

以降のセクションでは、これらのインタフェースの実装要件について取り上げます。

DSAKeyPairGenerator

インタフェースDSAKeyPairGeneratorは廃止されました。これは、クライアントに対し、実装で提供するデフォルト・パラメータの代わりにDSA固有のパラメータを使用することを可能にする上で必要でした。ただし、AlgorithmParameterSpecパラメータを取る新規KeyPairGenerator initializeメソッドにより、クライアントがアルゴリズム固有のパラメータを示すことが可能になったため、Javaではこれは必要ではなくなりました。

DSAParams実装

DSA鍵ペア・ジェネレータを実装する場合、pqおよびgパラメータを保持したり返したりするために、DSAParamsを実装するクラスが必要です。

DSAPrivateKeyおよびDSAPublicKeyインタフェースを実装する場合は、DSAParams実装も必要です。DSAPublicKeyおよびDSAPrivateKeyは、どちらもDSAParamsオブジェクトを返すgetParamsメソッドを含むDSAKeyインタフェースを拡張します。詳細は、「DSAPrivateKeyおよびDSAPublicKey実装」を参照してください。

注: JDKに組込み済みのDSAParams実装であるjava.security.spec.DSAParameterSpecクラスが存在します。

DSAPrivateKeyおよびDSAPublicKey実装

DSA鍵ペア・ジェネレータまたは鍵ファクトリを実装する場合、DSAPrivateKeyおよびDSAPublicKeyインタフェースを実装するクラスを作成する必要があります。

DSA鍵ペア・ジェネレータを実装する場合、(KeyPairGeneratorSpiサブクラスの) generateKeyPairメソッドはこれらのインタフェース実装のインスタンスを返します。

DSA鍵ファクトリを実装する場合、(KeyFactorySpiサブクラスの) engineGeneratePrivateメソッドはDSAPrivateKey実装のインスタンスを返し、engineGeneratePublicメソッドはDSAPublicKey実装のインスタンスを返します。

また、engineGetKeySpecおよびengineTranslateKeyメソッドは、引き渡される鍵がDSAPrivateKeyまたはDSAPublicKey実装のインスタンスであることを求めます。インタフェース実装により提供されるgetParamsメソッドは、鍵からパラメータを取得および抽出し、その後パラメータを使用する上で有用です。たとえば、DSAParameterSpecコンストラクタへのパラメータとして使用して、DSA用のKeyPairGeneratorオブジェクトの初期化に使用可能なパラメータ値からパラメータ仕様を作成できます。

DSA署名アルゴリズムを実装する場合、(SignatureSpiサブクラスの) engineInitSignメソッドはDSAPrivateKeyが渡されることを期待し、engineInitVerifyメソッドはDSAPublicKeyが渡されることを期待します。

注: DSAPublicKeyおよびDSAPrivateKeyインタフェースはそれぞれ、DSA公開鍵および非公開鍵に対するきわめて汎用的なプロバイダ非依存のインタフェースを定義します。KeyFactorySpiサブクラスのengineGetKeySpecメソッドとengineTranslateKeyメソッドは、プロバイダ固有の実装の詳細を利用するなどの目的で、引き渡される鍵が実際にプロバイダ独自のDSAPrivateKeyまたはDSAPublicKey実装のインスタンスであるかどうかをチェックすることもできます。SignatureSpiサブクラスのDSA署名アルゴリズムengineInitSignおよびengineInitVerifyメソッドについても、同様のことが当てはまります。

DSAPublicKeyおよびDSAPrivateKeyインタフェースを実装するクラスを使用してどのようなメソッドを実装する必要があるかについては、次のインタフェース署名に注目してください。

java.security.interfacesパッケージ内:

   public interface DSAPrivateKey extends DSAKey,
                java.security.PrivateKey

   public interface DSAPublicKey extends DSAKey,
                java.security.PublicKey

   public interface DSAKey 

java.securityパッケージ内:

   public interface PrivateKey extends Key

   public interface PublicKey extends Key

   public interface Key extends java.io.Serializable 

DSAPrivateKeyインタフェースとDSAPublicKeyインタフェースを実装するには、これらによって定義されるメソッドと、直接または間接的にこれらによって拡張されるインタフェースによって定義されたメソッドを実装する必要があります。

このため、非公開鍵の場合、次を実装するクラスを提供する必要があります

RSAインタフェースおよびその実装要件

Java Security APIには、RSAサービスを実装するプログラマが利用しやすいように、(java.security.interfacesパッケージ内の)次のインタフェースが含まれます。

以降のセクションでは、これらのインタフェースの実装要件について取り上げます。

RSAPrivateKeyRSAPrivateCrtKeyおよびRSAPublicKey実装

RSA鍵ペア・ジェネレータまたは鍵ファクトリを実装する場合、RSAPrivateKey (またはRSAPrivateCrtKey、あるいはその両方)およびRSAPublicKeyインタフェースを実装するクラスを作成する必要があります。(RSAPrivateCrtKeyは、中国剰余定理 (CRT)表現を使用した、RSA非公開鍵へのインタフェースです。)

RSA鍵ペア・ジェネレータを実装する場合、(KeyPairGeneratorSpiサブクラスの) generateKeyPairメソッドはこれらのインタフェース実装のインスタンスを返します。

RSA鍵ファクトリを実装する場合、(KeyFactorySpiサブクラスの) engineGeneratePrivateメソッドはRSAPrivateKey (またはRSAPrivateCrtKey)実装のインスタンスを返し、engineGeneratePublicメソッドはRSAPublicKey実装のインスタンスを返します。

また、engineGetKeySpecおよびengineTranslateKeyメソッドは、引き渡される鍵がRSAPrivateKeyRSAPrivateCrtKey、またはRSAPublicKey実装のインスタンスであることを求めます。

RSA署名アルゴリズムを実装する場合、(SignatureSpiサブクラスの) engineInitSignメソッドはRSAPrivateKeyまたはRSAPrivateCrtKeyが渡されることを期待し、engineInitVerifyメソッドはRSAPublicKeyが渡されることを期待します。

注: RSAPublicKeyRSAPrivateKey、およびRSAPrivateCrtKeyインタフェースは、RSA公開鍵および非公開鍵に対するきわめて汎用的なプロバイダ非依存のインタフェースを定義します。KeyFactorySpiサブクラスのengineGetKeySpecメソッドとengineTranslateKeyメソッドは、プロバイダ固有の実装の詳細を利用するなどの目的で、引き渡される鍵が実際にプロバイダ独自のRSAPrivateKeyRSAPrivateCrtKey、またはRSAPublicKey実装のインスタンスであるかどうかをチェックすることもできます。SignatureSpiサブクラスのRSA署名アルゴリズムengineInitSignおよびengineInitVerifyメソッドについても、同様のことが当てはまります。

RSAPublicKeyRSAPrivateKey、およびRSAPrivateCrtKeyインタフェースを実装するクラスを使用してどのようなメソッドを実装する必要があるかについては、次のインタフェース署名に注目してください。

java.security.interfacesパッケージ内:

    public interface RSAPrivateKey extends java.security.PrivateKey

    public interface RSAPrivateCrtKey extends RSAPrivateKey

    public interface RSAPublicKey extends java.security.PublicKey

java.securityパッケージ内:

    public interface PrivateKey extends Key

    public interface PublicKey extends Key

    public interface Key extends java.io.Serializable

RSAPrivateKeyRSAPrivateCrtKey、およびRSAPublicKeyインタフェースを実装するには、これらによって定義されるメソッドと、直接または間接的にこれらによって拡張されるインタフェースによって定義されたメソッドを実装する必要があります。

このため、RSA非公開鍵の場合、次のものを実装するクラスを提供する必要があります。

同様に、中国剰余定理 (CRT)表現を使用するRSA非公開鍵の場合、次のものを実装するクラスを提供する必要があります。

公開RSA鍵の場合、次のものを実装するクラスを提供する必要があります。

JCAには、よく使用される暗号化および鍵協定アルゴリズム・パラメータのAlgorithmParameterSpec実装が多数含まれています。JCAから提供されない、種類の異なるアルゴリズムに対応したアルゴリズム・パラメータを操作する場合は、その種類に適した独自のAlgorithmParameterSpec実装を提供する必要があります。

Diffie-Hellmanインタフェースおよびその実装要件

Diffie-Hellmanサービスを実装するプログラマのために、JCAには次のインタフェースが(javax.crypto.interfacesパッケージ内に)用意されています。

以降のセクションでは、これらのインタフェースの実装要件について取り上げます。

DHPrivateKeyおよびDHPublicKey実装

Diffie-Hellman鍵ペア・ジェネレータまたは鍵ファクトリを実装する場合、DHPrivateKeyおよびDHPublicKeyインタフェースを実装するクラスを作成する必要があります。

Diffie-Hellman鍵ペア・ジェネレータを実装する場合、(KeyPairGeneratorSpiサブクラスの) generateKeyPairメソッドはこれらのインタフェース実装のインスタンスを返します。

Diffie-Hellman鍵ファクトリを実装する場合、(KeyFactorySpiサブクラスの) engineGeneratePrivateメソッドはDHPrivateKey実装のインスタンスを返し、engineGeneratePublicメソッドはDHPublicKey実装のインスタンスを返します。

また、engineGetKeySpecおよびengineTranslateKeyメソッドは、引き渡される鍵がDHPrivateKeyまたはDHPublicKey実装のインスタンスであることを求めます。インタフェース実装によって提供されたgetParamsメソッドは、鍵からパラメータを取得および抽出する場合に便利です。そのあと、これらのパラメータを、パラメータ値からパラメータ仕様を作成するために呼び出されるDHParameterSpecコンストラクタのパラメータとして利用することにより、KeyPairGeneratorオブジェクトをDiffie-Hellman用として初期化できます。

Diffie-Hellman鍵協定アルゴリズムを実装する場合、KeyAgreementSpiサブクラスのengineInitメソッドはDHPrivateKeyが渡されることを求めます。また、engineDoPhaseメソッドはDHPublicKeyが渡されることを求めます。

注: DHPublicKeyおよびDHPrivateKeyインタフェースはそれぞれ、Diffie-Hellman公開鍵および非公開鍵に対するきわめて汎用的なプロバイダ非依存のインタフェースを定義します。KeyFactorySpiサブクラスのengineGetKeySpecメソッドとengineTranslateKeyメソッドは、プロバイダ固有の実装の詳細を利用するなどの目的で、引き渡される鍵が実際にプロバイダ独自のDHPrivateKeyまたはDHPublicKey実装のインスタンスであるかどうかをチェックすることもできます。KeyAgreementSpiサブクラスのDiffie-HellmanアルゴリズムengineInitおよびengineDoPhaseメソッドについても、同様のことが当てはまります。

DHPublicKeyおよびDHPrivateKeyインタフェースを実装するクラスを使用してどのようなメソッドを実装する必要があるかについては、次のインタフェース署名に注目してください。

javax.crypto.interfacesパッケージ内:

    public interface DHPrivateKey extends DHKey, java.security.PrivateKey

    public interface DHPublicKey extends DHKey, java.security.PublicKey

    public interface DHKey 

java.securityパッケージ内:

    public interface PrivateKey extends Key

    public interface PublicKey extends Key

    public interface Key extends java.io.Serializable 

DHPrivateKeyインタフェースとDHPublicKeyインタフェースを実装するには、これらによって定義されるメソッドと、直接または間接的にこれらによって拡張されるインタフェースによって定義されたメソッドを実装する必要があります。

このため、非公開鍵の場合、次を実装するクラスを提供する必要があります。

同様に、公開Diffie-Hellman鍵の場合、次を実装するクラスを提供する必要があります。

その他のアルゴリズム型用インタフェース

これまでに説明したとおり、Java Security APIには、DSA、RSA、ECCなどのサービスを実装するプログラマの利用に便利なインタフェースが含まれます。APIサポートがないサービスがある場合、独自のAPIを定義する必要があります。

異なるアルゴリズム用の鍵ペア・ジェネレータを実装する場合、実装が提供するデフォルト・パラメータではなく、アルゴリズム固有のパラメータをクライアントが提供して使用する場合にクライアントが呼び出すことのできる1つ以上のinitializeメソッドを使ってインタフェースを作成する必要があります。KeyPairGeneratorSpiのサブクラスは、このインタフェースを実装する必要があります。

直接のAPIサポートがないアルゴリズムの場合は、同様のインタフェースを作成して、実装クラスを提供することをお薦めします。公開鍵のインタフェースは、PublicKeyインタフェースを拡張する必要があります。同様に、非公開鍵のインタフェースは、PrivateKeyインタフェースを拡張する必要があります。

アルゴリズム・パラメータの仕様のインタフェースおよびクラス

アルゴリズム・パラメータの仕様は、アルゴリズムとともに使われるパラメータのセットの透明な表現です。

パラメータの「透明な」表現とは、対応する仕様クラスに定義されたgetメソッドの1つを使用して、各値に個々にアクセスできるということです。たとえば、DSAParameterSpecは、getPgetQgetGメソッドを定義して、パラメータp、q、gにアクセスします。

これと対照的なのが、AlgorithmParametersエンジン・クラスによって提供される場合のような「不透明な」表現です。この場合は、鍵データ値に直接アクセスすることはできません。パラメータ・セットに関連付けられたアルゴリズム名の取得(getAlgorithmによる)、およびそのパラメータ・セット用のある種のエンコードの取得(getEncodedによる)しかできません。

AlgorithmParametersSpiAlgorithmParameterGeneratorSpiKeyPairGeneratorSpiのいずれかの実装を提供する場合、AlgorithmParameterSpecインタフェースを利用する必要があります。理由は、これらの各クラスに、AlgorithmParameterSpecパラメータを取るメソッドが含まれるからです。この種のメソッドは、インタフェースのどの実装が実際に引き渡されるかを判定し、それに応じて動作する必要があります。

JCAには、よく使用される署名、暗号化、および鍵協定アルゴリズム・パラメータのAlgorithmParameterSpec実装が多数含まれています。JCAから提供されない、種類の異なるアルゴリズムに対応したアルゴリズム・パラメータを操作する場合は、その種類に適した独自のAlgorithmParameterSpec実装を提供する必要があります。

Javaは、次のアルゴリズム・パラメータ仕様インタフェースおよびクラスを、java.security.specおよびjavax.crypto.specパッケージ内で定義します。

AlgorithmParameterSpecインタフェース

AlgorithmParameterSpecは、暗号化パラメータの透明な仕様へのインタフェースです。

このインタフェースには、メソッドまたは定数が含まれていません。このインタフェースの唯一の目的は、すべてのパラメータの仕様をグループ化すること(およびそれらのパラメータに安全な型を提供すること)です。すべてのパラメータの仕様で、このインタフェースを実装する必要があります。

DSAParameterSpecクラス

AlgorithmParameterSpecおよびDSAParamsインタフェースを実装するこのクラスは、DSAアルゴリズムで使用されるパラメータのセットを指定します。このクラスには、次のメソッドがあります。

    public BigInteger getP()

    public BigInteger getQ()

    public BigInteger getG()

これらのメソッドは、DSAアルゴリズム・パラメータである素数のp、サブ素数のq、およびベースのgを返します。

多くの種類のDSAサービスは、このクラスを有用とみなします。たとえば、このクラスは、SUNプロバイダが実装するDSA署名、鍵ペア・ジェネレータ、アルゴリズム・パラメータ・ジェネレータおよびアルゴリズム・パラメータ・クラスにより利用されます。具体例をあげると、アルゴリズム・パラメータ実装は、AlgorithmParameterSpecを返すgetParameterSpecメソッド用の実装を含む必要があります。SUNにより提供されるDSAアルゴリズム・パラメータ実装は、DSAParameterSpecクラスのインスタンスを返します。

IvParameterSpecクラス

このクラス(AlgorithmParameterSpecインタフェースを実装)は、フィードバック・モードでの暗号化に使用される初期化ベクトル(IV)を指定します。

IvParameterSpecのメソッド
メソッド 説明
byte[] getIV() 初期化ベクトル(IV)を返します。

OAEPParameterSpecクラス

このクラスは、PKCS#1標準で定義されている、OAEPパディングで使用されるパラメータのセットを指定します。

OAEPParameterSpecのメソッド
メソッド 説明
String getDigestAlgorithm() メッセージ・ダイジェストのアルゴリズムの名前を返します。
String getMGFAlgorithm() マスク生成関数のアルゴリズムの名前を返します。
AlgorithmParameterSpec getMGFParameters() マスク生成関数のパラメータを返します。
PSource getPSource() エンコーディング入力Pのソースを返します。

PBEParameterSpecクラス

このクラス(AlgorithmParameterSpecインタフェースを実装)は、パスワードベースの暗号化(PBE)アルゴリズムで使用されるパラメータのセットを指定します。

PBEParameterSpecのメソッド
メソッド 説明
int getIterationCount() 繰返し処理の回数を返します。
byte[] getSalt() saltを返します。

RC2ParameterSpecクラス

このクラス(AlgorithmParameterSpecインタフェースを実装)は、RC2アルゴリズムで使われるパラメータのセットを指定します。

RC2ParameterSpecのメソッド
メソッド 説明
boolean equals(Object obj) 指定されたオブジェクトとこのオブジェクトが等価であるかどうかをテストします。
int getEffectiveKeyBits() 有効なキー・サイズをビット単位で返します。
byte[] getIV() IVを返します。このパラメータ・セットにIVが含まれない場合はnullを返します。
int hashCode() オブジェクトのハッシュ・コード値を計算します。

RC5ParameterSpecクラス

このクラス(AlgorithmParameterSpecインタフェースを実装)は、RC5アルゴリズムで使われるパラメータのセットを指定します。

RC5ParameterSpecのメソッド
メソッド 説明
boolean equals(Object obj) 指定されたオブジェクトとこのオブジェクトが等価であるかどうかをテストします。
byte[] getIV() IVを返します。このパラメータ・セットにIVが含まれない場合はnullを返します。
int getRounds() ラウンド回数を返します。
int getVersion() バージョンを返します。
int getWordSize() ワード・サイズをビット単位で返します。
int hashCode() オブジェクトのハッシュ・コード値を計算します。

DHParameterSpecクラス

このクラス(AlgorithmParameterSpecインタフェースを実装)は、Diffie-Hellmanアルゴリズムで使われるパラメータのセットを指定します。

DHParameterSpecのメソッド
メソッド 説明
BigInteger getG() ベース・ジェネレータgを返します。
int getL() ランダム指数(非公開値)のビット単位のサイズlを返します。
BigInteger getP() 素数モジュラスpを返します。

このクラスは、多くの種類のDiffie-Hellmanサービスにとって有用です。たとえば、このクラスは、「SunJCE」プロバイダが実装するDiffie-Hellman鍵協定、鍵ペア・ジェネレータ、アルゴリズム・パラメータ・ジェネレータおよびアルゴリズム・パラメータ・クラスにより利用されます。具体例をあげると、アルゴリズム・パラメータ実装は、AlgorithmParameterSpecを返すgetParameterSpecメソッド用の実装を含む必要があります。「SunJCE」により提供されるDiffie-Hellmanアルゴリズム・パラメータ実装は、DHParameterSpecクラスのインスタンスを返します。

鍵ファクトリにより要求される鍵仕様のインタフェースおよびクラス

鍵ファクトリは、不透明な鍵(Key型)と鍵仕様との間の双方向変換を提供します。このため、鍵ファクトリを実装する場合には、鍵仕様を理解して利用することが必要になります。場合によっては、独自の鍵仕様を実装する必要もあります。

鍵仕様、Javaで提供されるインタフェースとクラス、および仕様に関する鍵ファクトリの要件についての詳細は、後述します。

鍵仕様は、鍵を構成する鍵データの透明な表現です。鍵がハードウェア・デバイス上に格納されている場合は、その鍵仕様には、デバイス上の鍵の識別を助ける情報が含まれていることがあります。

鍵の「透明な」表現とは、対応する仕様クラスで定義されたgetメソッドの1つを使って、各鍵データ値に個々にアクセスできるということです。たとえば、java.security.spec.DSAPrivateKeySpecは、getXgetPgetQ、およびgetGメソッドを定義し、非公開鍵xおよび鍵の計算に使用するDSAアルゴリズムのパラメータ(素数のp、サブ素数のq、およびベースのg)にアクセスします。

これと対照的なのが、Keyインタフェースによって定義されるような、不透明な表現です。「不透明な」表現では、パラメータ・フィールドに直接アクセスできません。つまり、不透明な表現では、鍵へのアクセスが、Keyインタフェースによって定義されるgetAlgorithmgetFormatおよびgetEncodedの3つのメソッドに制限されます。

鍵は、アルゴリズムに固有の方法か、またはアルゴリズムに依存しないエンコーディング形式(ASN.1など)で指定できます。たとえば、DSA非公開鍵は、非公開鍵のコンポーネントxpq、およびgによって指定するか(DSAPrivateKeySpecを参照)、または、非公開鍵のDERエンコーディングを使って指定することが可能です(PKCS8EncodedKeySpecを参照)。

Javaは、次の鍵仕様インタフェースおよびクラスを、java.security.specおよびjavax.crypto.specパッケージ内で定義します。

KeySpecインタフェース

このインタフェースには、メソッドまたは定数が含まれていません。このインタフェースの唯一の目的は、すべての鍵仕様をグループ化すること(およびそれらのグループに安全な型を提供すること)です。すべての鍵仕様で、このインタフェースを実装する必要があります。

Javaは、KeySpecインタフェースであるDSAPrivateKeySpecDSAPublicKeySpecRSAPrivateKeySpecRSAPublicKeySpecEncodedKeySpecPKCS8EncodedKeySpec、およびX509EncodedKeySpecを実装するいくつかのクラスを提供します。

JDKが対応するKeySpecクラスを提供していない鍵型(Your_PublicKey_typeおよびYour_PrivateKey_typeなど)をプロバイダが使用する場合、2とおりの対処方法が考えられます。1つは、独自の鍵仕様を実装する方法です。

  1. ユーザーが、使用する鍵型の特定の鍵データ値にアクセスする必要が一切ない場合、その鍵型用のKeySpecクラスを提供する必要はありません。

    この方法では、ユーザーは常に、プロバイダが該当する鍵型用に提供する適切なKeyPairGeneratorを使用してYour_PublicKey_typeおよびYour_PrivateKey_type鍵を作成します。後で使用するために生成された鍵を格納する場合には、(KeyインタフェースのgetEncodedメソッドを使用して)鍵のエンコードを取得します。エンコードからYour_PublicKey_typeまたはYour_PrivateKey_type鍵を作成する場合(署名または検証目的でSignatureオブジェクトを初期化する場合など)は、エンコードからX509EncodedKeySpecまたはPKCS8EncodedKeySpecのインスタンスを作成して、そのアルゴリズム用にプロバイダが提供する適切なKeyFactoryに渡します。KeyFactoryのgeneratePublicおよびgeneratePrivateメソッドは、要求されたPublicKey (Your_PublicKey_typeのインスタンス)またはPrivateKey (Your_PrivateKey_typeのインスタンス)オブジェクトをそれぞれ返します。
  2. ユーザーが鍵型の特定の鍵データ値にアクセスするか、前述の場合のようにエンコーディングからではなく鍵データおよび関連するパラメータ値から自分の鍵型の鍵を構築する必要が予想される場合、適切なコンストラクタ・メソッドおよびgetメソッドを使用して、新しいKeySpecクラス(KeySpecインタフェースを実装するクラス)を指定して、自分の鍵型用の鍵データ・フィールドおよび関連するパラメータ値を返すようにする必要があります。これらのクラスは、DSAPrivateKeySpecおよびDSAPublicKeySpecクラスが行うのと同様の方法で指定します。これらのクラスは、プロバイダ・クラスとともに(たとえば、プロバイダJARファイルの一部として)提供する必要があります。

DSAPrivateKeySpecクラス

このクラス(KeySpecインタフェースを実装)は、関連付けられたパラメータを使ってDSA非公開鍵を指定します。このクラスには、次のメソッドがあります。

DSAPrivateKeySpecのメソッド 説明
public BigInteger getX() 非公開鍵xを返します。
public BigInteger getP() 素数pを返します。
public BigInteger getQ() サブ素数qを返します。
public BigInteger getG() ベースgを返します。

これらのメソッドは、非公開鍵x、および鍵の計算に使用されるDSAアルゴリズム・パラメータである素数のp、サブ素数のq、およびベースのgを返します。

DSAPublicKeySpecクラス

このクラス(KeySpecインタフェースを実装)は、関連付けられたパラメータを使ってDSA公開鍵を指定します。このクラスには、次のメソッドがあります。

DSAPublicKeySpecのメソッド 説明
public BigInteger getY() 公開鍵yを返します。
public BigInteger getP() 素数pを返します。
public BigInteger getQ() サブ素数qを返します。
public BigInteger getG() ベースgを返します。

これらのメソッドは、公開鍵y、および鍵の計算に使用されるDSAアルゴリズム・パラメータである素数のp、サブ素数のq、およびベースのgを返します。

RSAPrivateKeySpecクラス

このクラス(KeySpecインタフェースを実装)は、RSA非公開鍵を指定します。このクラスには、次のメソッドがあります。

RSAPrivateKeySpecのメソッド 説明
public BigInteger getModulus() モジュラスを返します。
public BigInteger getPrivateExponent() 非公開指数を返します。

これらのメソッドは、RSA非公開鍵を構成するRSAモジュラスnおよび非公開指数dの値を返します。

RSAPrivateCrtKeySpecクラス

このクラス(RSAPrivateKeySpecクラスを拡張)は、PKCS#1標準で定義されているように、中国剰余定理 (CRT)情報の値を使って、RSA非公開鍵を指定します。このクラスには、スーパー・クラスのRSAPrivateKeySpecから継承したメソッドのほかに、次のメソッドがあります。

RSAPrivateCrtKeySpecのメソッド 説明
public BigInteger getPublicExponent() 公開指数を返します。
public BigInteger getPrimeP() 素数Pを返します。
public BigInteger getPrimeQ() 素数Qを返します。
public BigInteger getPrimeExponentP() primeExponentPを返します。
public BigInteger getPrimeExponentQ() primeExponentQを返します。
public BigInteger getCrtCoefficient() crtCoefficientを返します。

これらのメソッドは、公開指数eおよびCRT情報の整数(モジュラスnの素数因数pnの素数因数q、指数d mod (p-1)、指数d mod (q-1)、および中国剰余定理係数(inverse of q) mod p)を返します。

RSA非公開鍵は、論理的にはモジュラスと非公開の指数だけで構成されます。CRT値は、効率を向上させる目的で存在します。

RSAPublicKeySpecクラス

このクラス(KeySpecインタフェースを実装)は、RSA公開鍵を指定します。このクラスには、次のメソッドがあります。

RSAPublicKeySpecのメソッド 説明
public BigInteger getModulus() モジュラスを返します。
public BigInteger getPublicExponent() 公開指数を返します。

これらのメソッドは、RSA公開鍵を構成するRSAモジュラスnおよび公開指数eの値を返します。

EncodedKeySpecクラス

この抽象クラス(KeySpecインタフェースを実装する)は、エンコードされた形式の公開鍵または非公開鍵を表します。

EncodedKeySpecのメソッド 説明
public abstract byte[] getEncoded() エンコードされた鍵を返します。
public abstract String getFormat() エンコード形式の名前を返します。

JDKは、EncodedKeySpecインタフェースであるPKCS8EncodedKeySpecおよびX509EncodedKeySpecを実装する2つのクラスを提供します。必要に応じて、これらの、またはほかの型の鍵エンコーディング用に独自のEncodedKeySpec実装を提供することもできます。

PKCS8EncodedKeySpecクラス

このクラスは、EncodedKeySpecのサブクラスで、PKCS#8標準で指定された形式に従って、非公開鍵のDERエンコードを表現します。

このクラスのgetEncodedメソッドは、PKCS#8標準に従ってエンコードされた鍵のバイトを返します。そのgetFormatメソッドは、文字列「PKCS#8」を返します。

X509EncodedKeySpecクラス

このクラスは、EncodedKeySpecのサブクラスで、X.509標準で指定された形式に従って、公開鍵または非公開鍵のDERエンコードを表現します。

そのgetEncodedメソッドは、X.509標準に従ってエンコードされた鍵のバイトを返します。そのgetFormatメソッドは、文字列「X.509」を返します。 DHPrivateKeySpecDHPublicKeySpecDESKeySpecDESedeKeySpecPBEKeySpec、およびSecretKeySpec

DHPrivateKeySpecクラス

このクラス(KeySpecインタフェースを実装)は、関連付けられたパラメータを使ってDiffie-Hellman非公開鍵を指定します。

DHPrivateKeySpecのメソッド 説明
BigInteger getG() ベース・ジェネレータgを返します。
BigInteger getP() 素数モジュラスpを返します。
BigInteger getX() 非公開値xを返します。

DHPublicKeySpecクラス

このクラス(KeySpecインタフェースを実装)は、関連付けられたパラメータを使ってDiffie-Hellman公開鍵を指定します。

DHPublicKeySpecのメソッド 説明
BigInteger getG() ベース・ジェネレータgを返します。
BigInteger getP() 素数モジュラスpを返します。
BigInteger getY() 公開値yを返します。

DESKeySpecクラス

このクラス(KeySpecインタフェースを実装)は、DES鍵を指定します。

DESKeySpecのメソッド 説明
byte[] getKey() DES鍵のバイト数を返します。
static boolean isParityAdjusted(byte[] key, int offset) 所定のDES鍵データがパリティ対応であるかどうかをチェックします。
static boolean isWeak(byte[] key, int offset) 所定のDES鍵データが脆弱(weak)または準脆弱(semi-weak)のどちらであるかをチェックします。

DESedeKeySpecクラス

このクラス(KeySpecインタフェースを実装)は、DES-EDE (トリプルDES)鍵を指定します。

DESedeKeySpecのメソッド 説明
byte[] getKey() DES-EDE鍵を返します。
static boolean isParityAdjusted(byte[] key, int offset) 所定のDES-EDE鍵がパリティ対応であるかどうかをチェックします。

PBEKeySpecクラス

このクラスは、KeySpecインタフェースを実装します。パスワードベースの暗号化(PBE)で使用するパスワードは、ユーザーが選択できます。このパスワードは、生の鍵データの型として参照されます。このクラスを使用する暗号化メカニズムは、生の鍵データから暗号鍵を引き出すことができます。

PBEKeySpecのメソッド 説明
void clearPassword パスワードの内部コピーを消去します。
int getIterationCount 繰返し処理の回数を返します。指定がない場合は0を返します。
int getKeyLength 派生される鍵の長さを返します。指定がない場合は0を返します。
char[] getPassword パスワードのコピーを返します。
byte[] getSalt saltのコピーを返します。指定がない場合はnullを返します。

SecretKeySpecクラス

このクラスは、KeySpecインタフェースを実装します。このクラスはSecretKeyインタフェースも実装するため、このクラスを使用するなら、SecretKeyオブジェクトをプロバイダに依存しない方法で(プロバイダ・ベースのSecretKeyFactoryを使用せずに)構築できます。

SecretKeySpecのメソッド 説明
boolean equals (Object obj) このオブジェクトとほかのオブジェクトが等しいかどうかを示します。
String getAlgorithm() この秘密鍵に関連付けられているアルゴリズム名を返します。
byte[] getEncoded() この秘密鍵の鍵データを返します。
String getFormat() この秘密鍵のエンコーディング形式の名前を返します。
int hashCode() オブジェクトのハッシュ・コード値を計算します。

秘密鍵の生成

特定の秘密鍵アルゴリズムに対して秘密鍵ジェネレータ(javax.crypto.KeyGeneratorSpiのサブクラス)を提供する場合、生成された秘密鍵オブジェクト(javax.crypto.SecretKeyのインスタンスであることが必要。engineGenerateKeyを参照)を次のいずれかの方法で返すことができます。

新規オブジェクト識別子の追加

次の情報は、「Java暗号化アーキテクチャ・リファレンス・ガイド」の「付録A」で標準アルゴリズムとして挙げられていないアルゴリズムを提供するプロバイダに当てはまります。

OIDから名前へのマッピング

JCAが、アルゴリズム識別子から(たとえば、証明書内でエンコードされたものとして)暗号化アルゴリズム実装のインスタンス化を行う必要がある場合があります。アルゴリズム識別子には、定義上、アルゴリズムのオブジェクト識別子(OID)が含まれます。たとえば、X.509証明書の署名を検証するために、JCAは証明書内でエンコードされた署名アルゴリズム識別子から署名アルゴリズムを判別し、そのアルゴリズムのSignatureオブジェクトについてインスタンスを生成してから、検証のために初期化します。

JCAでアルゴリズムを検索するには、プロバイダ・マスター・ファイルで、アルゴリズムの別名エントリとしてアルゴリズム識別子を指定します。

    put("Alg.Alias.<engine_type>.1.2.3.4.5.6.7.8",
        "<algorithm_alias_name>");

アルゴリズムが複数のオブジェクト識別子で知られている場合、オブジェクト識別子ごとに別名エントリを作成する必要があります。

JCAによるこの種のマッピングの例として、アルゴリズム(Foo)が署名アルゴリズムで、ユーザーがkeytoolコマンドを実行して(署名)アルゴリズムの別名を指定する場合を考えます。

    % keytool -genkeypair -sigalg 1.2.3.4.5.6.7.8

この場合、プロバイダのマスター・ファイルには、次のエントリを含める必要があります。

    put("Signature.Foo", "com.xyz.MyFooSignatureImpl");
    put("Alg.Alias.Signature.1.2.3.4.5.6.7.8", "Foo");

この種のマッピングの別の例として、(1)アルゴリズムがキー・タイプ・アルゴリズムで、プログラムが(SUNプロバイダのX.509実装を使って)証明書を構文解析し、証明書から公開鍵を抽出して、Signatureオブジェクトを初期化する場合、および(2) keytoolユーザーが、対応する鍵のペアの生成後に(デジタル署名を実行するために)キー・タイプの非公開鍵にアクセスしようとする場合、を挙げることができます。これらの場合、プロバイダのマスター・ファイルには、次のエントリを含める必要があります。

    put("KeyFactory.Foo", "com.xyz.MyFooKeyFactoryImpl");
    put("Alg.Alias.KeyFactory.1.2.3.4.5.6.7.8", "Foo");

名前からOIDへのマッピング

JCAが逆マッピング(つまり、アルゴリズム名からその関連付けられたOIDへの)を実行する必要がある場合、アルゴリズムと関連付けられたOIDの1つに対して、次の形式の別名エントリを提供する必要があります。

    put("Alg.Alias.Signature.OID.1.2.3.4.5.6.7.8", "MySigAlg");

アルゴリズムが複数のオブジェクト識別子で知られている場合、優先度の高いものに接頭辞「OID」を追加します。

JCAがこの種のマッピングを実行する一例として、ユーザーがkeytoolを、-sigalgオプションを取る任意のモードで実行する場合を挙げることができます。たとえば、-genkeypairおよび-certreqコマンドが呼び出されると、ユーザーは-sigalgオプションを使用して(署名)アルゴリズムを指定できます。

エクスポート機能の保証

JCAでは、特定の条件が満たされる場合に、JCAフレームワークおよびプロバイダ暗号化実装をエクスポート可能になります。これはJCAの重要な機能です。

輸入管理制限があるため、JDKに同梱された管轄ポリシー・ファイルは「強固」ですが、暗号化の使用には制限があります。適格国(大半の国が該当)の在住者は、暗号化機能に制限のない「無制限」のバージョンを利用できます。ただし、政府が制限を課しているこれらの国が輸入できるのは「強力な」バージョンだけです。JCAフレームワークでは、インストール済みの管轄ポリシー・ファイルで指定された制限が施行されます。

ほかの部分で説明したように、もっとも強力な暗号化を実装する、1つのバージョンのプロバイダ・ソフトウェアだけを実装できます。位置の異なるアプレット/アプリケーションから、利用可能な暗号化アルゴリズムおよび暗号化の最大強度に関して管轄ポリシー・ファイルに規定された制限を施行するのは、プロバイダではなく、JCAです。

JCAへのプラグインを可能にするために、プロバイダが満たす必要がある条件を次に示します。

付録A: java.security.propertiesファイル

次に示すのは、インストールされているプロバイダのデフォルトのリストを示すjava.securityファイルの一部です。これは、すべてのJREインストールで表示されます。ファイルにはその他のエントリも含まれていますが、簡潔にするために、ここでは一部のみを示します。完全なファイルは次の場所にあります。

ここで、<java-home>はJREがインストールされているディレクトリを指します。

このファイルにプロバイダに関する情報を追加する例については、ステップ5を参照してください。

#
# This is the "master security properties file".
#
# In this file, various security properties are set for use by
# java.security classes. This is where users can statically register
# Cryptography Package Providers ("providers" for short). The term
# "provider" refers to a package or set of packages that supply a
# concrete implementation of a subset of the cryptography aspects of
# the Java Security API. A provider may, for example, implement one or
# more digital signature algorithms or message digest algorithms.
#
# Each provider must implement a subclass of the Provider class.
# To register a provider in this master security properties file,
# specify the Provider subclass name and priority in the format
#
#    security.provider.<n>=<className>
#
# This declares a provider, and specifies its preference
# order n. The preference order is the order in which providers are
# searched for requested algorithms (when no specific provider is
# requested). The order is 1-based; 1 is the most preferred, followed
# by 2, and so on.
#
# <className> must specify the subclass of the Provider class whose
# constructor sets the values of various properties that are required
# for the Java Security API to look up the algorithms or other
# facilities implemented by the provider.
#
# There must be at least one provider specification in java.security.
# There is a default provider that comes standard with the JDK. It
# is called the "SUN" provider, and its Provider subclass
# named Sun appears in the sun.security.provider package. Thus, the
# "SUN" provider is registered via the following:
#
#    security.provider.1=sun.security.provider.Sun
#
# (The number 1 is used for the default provider.)
#
# Note: Providers can be dynamically registered instead by calls to
# either the addProvider or insertProviderAt method in the Security
# class.

#
# List of providers and their preference orders (see above):
#

security.provider.1=sun.security.pkcs11.SunPKCS11 \
    ${java.home}/lib/security/sunpkcs11-solaris.cfg
security.provider.2=sun.security.provider.Sun
security.provider.3=sun.security.rsa.SunRsaSign
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=com.sun.security.sasl.Provider
security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.9=sun.security.smartcardio.SunPCSC

# Rest of file deleted


Copyright © 1993, 2020, Oracle and/or its affiliates. All rights reserved.