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

このドキュメントでは、Java Security APIクライアントでアルゴリズムや他のサービスを要求する際にそれらを検出できるよう、プロバイダをJava SEに統合するために必要なことを説明します。

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

既存の暗号化アルゴリズムと他のサービスにアクセスするためにのみJava Security API (Java暗号化アーキテクチャ(JCA)リファレンス・ガイドコア・クラスとインタフェースを参照)を使用するプログラマは、このドキュメントを参照する必要はありません

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

用語に関するノート

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

  • JDK 1.4より前は、JCEはバンドルされていない製品であり、そのため、JCAとJCEは常に別個のコンポーネントとして参照されていました。現在JCEはJDKにバンドルされているため、区別は明確でなくなってきています。JCEはJCAと同じアーキテクチャを使用するため、JCEはJCAのサブセットとして考えることができます。
  • JDKのJCAには、次の2つのソフトウェア・コンポーネントが含まれます。
    • プロバイダが実装を提供可能な暗号化サービスを定義およびサポートするフレームワーク。このフレームワークには、java.securityjavax.cryptojavax.crypto.specjavax.crypto.interfacesなどのパッケージが含まれます。
    • SunSunRsaSignSunJCEなどの実際のプロバイダ。実際の暗号化実装が含まれます。
  • JCEは、javax.crypto.*パッケージおよびSunJCEプロバイダで構成されます。

プロバイダ実装の概要

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

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

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

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

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

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

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

エンジン・クラスは、固定実装のない抽象的な方法で暗号化サービスを定義します。暗号化サービスは、常に特定のアルゴリズムまたはタイプに関連付けられています。

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

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

  • Signatureクラスは、デジタル署名アルゴリズムの機能へのアクセスを提供します。
  • DSA KeyFactoryクラスは、(エンコード形式または透明な仕様から) DSA非公開キーまたは公開キーを、それぞれDSA SignatureオブジェクトのinitSignまたはinitVerifyメソッドから利用可能な形式で提供します。
  • Cipherクラスは、AESなどの暗号化アルゴリズムの機能へのアクセスを提供します。
  • KeyAgreementクラスは、Diffie-Hellmanなどのキー協定プロトコルの機能へのアクセスを提供します。

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

  • MessageDigest - 指定データのメッセージ・ダイジェスト(ハッシュ)の計算に使います。
  • Signature - デジタル署名の署名および検証に使います。
  • KeyPairGenerator - 指定のアルゴリズムに適合した、公開キー、秘密キー・ペアの生成に使います。
  • KeyFactory - Key型の不透明な暗号化キーをキー仕様 (基本のキー・データの透明な表現)に変換したり、その逆の変換を行うために使用します。
  • KeyStore - キーストアの作成および管理に使用します。キーストアは、キーのデータベースです。キーストア内の秘密キーには、それらのキーに関連付けられた証明書チェーンがあります。証明書チェーンは、対応する公開キーを認証します。また、キーストアには、信頼できるエンティティからの証明書も格納されています。
  • CertificateFactory - 公開キーの証明書および証明書失効リスト(CRL)の作成に使用します。
  • AlgorithmParameters - パラメータのエンコードおよびデコードなど、特定のアルゴリズムのパラメータ管理に使います。
  • AlgorithmParameterGenerator - 特定のアルゴリズムに適したパラメータ・セットの生成に使います。
  • SecureRandom - 乱数または擬似乱数の生成に使用します。
  • Cipher - 指定されたデータの暗号化または復号化に使用します。
  • KeyAgreement - 複数のパーティ間のキー協定(キー交換)プロトコルの実行に使用します。
  • KeyGenerator - 指定のアルゴリズムに適合した、秘密(対称)キーの生成に使用します。
  • Mac - 指定されたデータのメッセージ認証コードの計算に使用します。
  • SecretKeyFactory - SecretKey型の不透明な暗号化キーをキー仕様(基本のキー・データの透明な表現)に変換したり、その逆の変換を行うために使用します。
  • CertPathBuilder - 公開キーの証明書および証明書失効リスト(CRL)の作成に使用します。
  • CertPathValidator - 証明書チェーンの検証に使用します。
  • CertStore - リポジトリから証明書とCRLを取得するために使用します。
  • ExemptionMechanism - キー復元キー弱化キー・エスクローなどの除外メカニズム、または他の(カスタム)除外メカニズムの機能を提供するために使用します。除外メカニズムを使用するアプリケーションまたはアプレットは、使用しないアプリケーションまたはアプレットに比べて強力な暗号化機能が付与されます。大半の国では暗号化制限はもはや適用されていないため、除外メカニズムが有用なのは政府により制限が要求されているいくつかの国だけです。

ノート:

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

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

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

図3-1 エンジン・クラス

図3-1の説明が続きます
「図3-1 エンジン・クラス」の説明

エンジン・クラスのインスタンスである「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で定義されていない暗号化サービス(たとえば、署名やメッセージ・ダイジェスト)については、エンジン・クラスとアルゴリズムを参照してください。

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

  • SignatureSpi
  • MessageDigestSpi
  • KeyPairGeneratorSpi
  • SecureRandomSpi
  • AlgorithmParameterGeneratorSpi
  • AlgorithmParametersSpi
  • KeyFactorySpi
  • CertificateFactorySpi
  • KeyStoreSpi
  • CipherSpi
  • KeyAgreementSpi
  • KeyGeneratorSpi
  • MacSpi
  • SecretKeyFactorySpi
  • ExemptionMechanismSpi

JCAとその他の暗号化クラスの詳細は、エンジン・クラスおよび対応するサービス・プロバイダ・インタフェース・クラスを参照してください。

このサブクラスでは、次のことが必要となります。

  1. 通常engineで始まる名前を持つ抽象メソッド用の実装を提供します。その他の実装詳細と要件を参照してください。
  2. どのようにプロバイダを記述しそのアルゴリズムを登録したかによって(StringオブジェクトまたはProvider.Serviceクラスを使用)、プロバイダは次のどちらかのことを行います。
    • 引数を持たないpublicコンストラクタの存在を確認します。これが必要な理由は、サービスの要求時に、Java Securityがマスター・クラス内のプロパティによる指定に従って、そのサービスを実装するサブクラスを参照するためです(ステップ3: プロバイダのサブクラスであるマスター・クラスの記述を参照)。その後、Java Securityは、サブクラスに関連付けられたClassオブジェクトを作成し、そのClassオブジェクトに対してnewInstanceメソッドを呼び出すことにより、サブクラスのインスタンスを作成します。newInstanceでは、サブクラスに、パラメータを持たないpublicコンストラクタがあることが必要となります。(サブクラスがコンストラクタを持たない場合、引数を持たないデフォルトのコンストラクタが自動的に生成されます。ただし、サブクラスでコンストラクタが定義されている場合は、引数を持たないpublicコンストラクタを明示的に定義する必要があります。)
    • 登録されているProvider.Service内のnewInstance()メソッドをオーバーライドします。これは、JDK 9以降で推奨されているメカニズムです。
ステップ1.1: 暗号化実装のためのJCAプロバイダのその他の要件および推奨事項の検討

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

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

  • プロバイダ・パッケージ内のすべてのSPI実装クラスをfinalとして宣言し(サブクラス化できないように)、SPI実装メソッドをprotectedとして宣言する必要があります。
  • プロバイダ・パッケージ内の暗号化関連ヘルパー・クラスはすべて、プロバイダ・パッケージ外からアクセスできないように、パッケージ独自のスコープを保持する必要があります。

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

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

  • オプション: CipherSpiengineWrapメソッドおよびengineUnwrapメソッド。キーをラッピングすると、ある場所から別の場所へ安全に転送できます。キーのラップおよびアンラップについての情報は、wrapで提供されます。
  • オプション: 1つ以上の除外メカニズム。除外メカニズムには、キー復元、キー・エスクロー、キー弱化などが含まれます。このメカニズムを実装および実行すると、これを利用するアプリケーション(またはアプレット)に対する暗号化の制限を緩めることができます。除外メカニズムを利用するアプリケーションの要件の詳細は、「暗号化制限からアプリケーションを除外する方法」を参照してください。

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

使用するプロバイダの一意の名前を決定します。これは、クライアント・アプリケーションがプロバイダを参照する際に使用する名前です。他のプロバイダ名と競合しないようにする必要があります。

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

java.security.Providerクラスのサブクラスを作成します。これは、基本的には、プロバイダが実装するアルゴリズムを通知する参照テーブルです。

Providerクラスをサブクラス化するには、次のコーディング・スタイルを使用します。

  • アルゴリズム名とそれらに関連する実装クラス名を格納するStringオブジェクトを使用してサービスを登録するプロバイダを作成します。これらは、java.security.ProviderHashtable<Object,Object>スーパークラスに格納されます。

  • Provider.Serviceクラスを使用するプロバイダを作成します。これは、別のメソッドを使用してアルゴリズム名を格納し、新しいオブジェクトを作成します。Provider.Serviceクラスでは、JCAフレームワークがプロバイダのサービスの新しいインスタンスをどのように作成するかなど、JCAフレームワークがプロバイダからサービスをどのように要求するかをカスタマイズできます。このコーディング・スタイルの使用をお薦めします(特にモジュール使用時)。

プロバイダは、どちらかのスタイルを使用でき、両方のスタイルを同時に使用することもできます。選択したスタイルに関係なく、サブクラスは、finalである必要があります。

ステップ3.1: Stringオブジェクトを使用してサービスを登録するプロバイダの作成

次に、Stringオブジェクトを使用して実装済アルゴリズム名を格納する、プロバイダの例を示します。

package p;
public final class MyProvider extends Provider {
    public MyProvider() {
        super("MyProvider", "1.0",
            "Some info about my provider and which algorithms it supports");
        // com.my.crypto.provider.MyCipher extends CipherSPI
        put("Cipher.MyCipher", "com.my.crypto.provider.MyCipher");
    }
}

このコーディング・スタイルを使用してプロバイダを作成するには、次のことを行います。

  • superを呼び出して、プロバイダ名(ステップ2: プロバイダの命名を参照)、バージョン番号、およびプロバイダとそれがサポートするアルゴリズムに関する情報を指定します。
       super("MyProvider", "1.0",
            "Some info about my provider and which algorithms it supports");
    
  • プロバイダによって実装された暗号化サービスをJava Security APIで検索するために必要となる、様々なプロパティの値を設定します。

    プロバイダによって実装された各サービスには、サービスの型、ピリオド、およびサービスが適用されるアルゴリズムの名前で構成されている名前が付いたプロパティが必要です。プロパティの値には、サービスを実装するクラスの完全修飾名を指定する必要があります。

    たとえば、次に示す文では、値がcom.my.crypto.provider.MyCipher (CipherSPIを拡張するクラス)である、Cipher.MyCypherという名前のプロパティを設定します。

            put("Cipher.MyCipher", "com.my.crypto.provider.MyCipher");
    

    次のリストに、様々な型のJCAサービスを示します。ここでは、実際のアルゴリズム名のかわりにalgNameを使用します。

    • Signature.algName
    • MessageDigest.algName
    • KeyPairGenerator.algName
    • SecureRandom.algName
    • AlgorithmParameterGenerator.algName
    • AlgorithmParameters.algName
    • KeyFactory.algName
    • CertificateFactory.algName
    • KeyStore.algName
    • Cipher.algName: algNameは実際には変換を表します。これは、アルゴリズム名、指定されたモードおよびパディング・スキームで構成されます。Javaセキュリティ標準アルゴリズム名を参照してください
    • KeyAgreement.algName
    • KeyGenerator.algName
    • Mac.algName
    • SecretKeyFactory.algName
    • ExemptionMechanism.algName: algNameは除外メカニズムの名前を指し、KeyRecoveryKeyEscrowまたはKeyWeakeningのいずれかとなります。大文字小文字は区別されません

    ExemptionMechanismおよびCipherを除き、これらのどの場合にも、algNameは、アルゴリズム、証明書タイプまたはキーストア・タイプの標準名となります。使用する必要がある標準名については、Javaセキュリティ標準アルゴリズム名を参照してください。

    各プロパティの値は、指定されたアルゴリズム、証明書の型、またはキーストアの型を実装するクラスの完全修飾名である必要があります。つまり、クラス名のあとにピリオドとパッケージ名が記述されていなければなりません。

    たとえば、SUNという名前のデフォルト・プロバイダは、sun.security.providerパッケージのDSAという名前のクラス内のデジタル署名アルゴリズム(その標準名はSHA256withDSA)を実装します。Providerのサブクラス(sun.security.provider packageのSunクラス)は、次のようにして、Signature.SHA256withDSAプロパティの値をsun.security.provider.DSAに設定します。

    put("Signature.SHA256withDSA", "sun.security.provider.DSA")
    

    次に、その他のプロパティをリストしますが、これらのプロパティは様々な種類のサービスに定義可能で、実際には、algNameはアルゴリズム名に、certTypeは証明書タイプに、storeTypeはキーストア・タイプに、attrNameは属性名に置き換えられます:

    • Signature.algName [one or more spaces] attrName
    • MessageDigest.algName [one or more spaces] attrName
    • KeyPairGenerator.algName [one or more spaces] attrName
    • SecureRandom.algName [one or more spaces] attrName
    • KeyFactory.algName [one or more spaces] attrName
    • CertificateFactory.certType [one or more spaces] attrName
    • KeyStore.storeType [one or more spaces] attrName
    • AlgorithmParameterGenerator.algName [one or more spaces] attrName
    • AlgorithmParameters.algName [one or more spaces] attrName
    • Cipher.algName [one or more spaces] attrName
    • KeyAgreement.algName [one or more spaces] attrName
    • KeyGenerator.algName [one or more spaces] attrName
    • Mac.algName [one or more spaces] attrName
    • SecretKeyFactory.algName [one or more spaces] attrName
    • ExemptionMechanism.algName [one or more spaces] attrName

    これらのどの場合にも、attrNameは、アルゴリズム、証明書タイプ、キーストア・タイプまたは属性の標準名です。(使用する必要がある標準名については、Javaセキュリティ標準アルゴリズム名を参照。)

    この形式のプロパティの場合、プロパティの値は、対応する属性に応じた値にする必要があります。(各標準属性の定義については、Javaセキュリティ標準アルゴリズム名を参照。)

    マスター・クラス・プロパティ設定のその他の例は、sun.security.provider.Sunクラスとcom.sun.crypto.provider.SunJCEクラスのJDK ソース・コードを参照してください。それらでは、SunプロバイダおよびSunJCEプロバイダでプロパティがどのように設定されるかがわかります。

    たとえば、SUNという名前のデフォルト・プロバイダは、ソフトウェアにSHA256withDSAデジタル署名アルゴリズムを実装します。クラスsun.security.provider.Sunは、メソッドSunEntries.putEntriesを呼び出します。これは、プロパティSignature.SHA256withDSA ImplementedInの値をSoftwareに設定するなど、SUNプロバイダのプロパティを設定します。

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

    ノート:

    このコーディング・スタイルの例は、sun.security.provider.Sunクラスとsun.security.provider.SunEntriesクラスのソース・コードを参照してください。
ステップ3.2: Provider.Serviceを使用するプロバイダの作成

次に、Provider.Serviceクラスを使用するプロバイダの例を示します。

package p;
 
public final class MyProvider extends Provider {
     
    public MyProvider() {
        super("MyProvider", "1.0",
            "Some info about my provider and which algorithms it supports");
        putService(new ProviderService(this, "Cipher", "MyCipher", "p.MyCipher"));
    }
     
    private static final class ProviderService extends Provider.Service {
        ProviderService(Provider p, String type, String algo, String cn) {
            super(p, type, algo, cn, null, null);
        }
         
        @Override
        public Object newInstance(Object ctrParamObj)
            throws NoSuchAlgorithmException {
            String type = getType();
            String algo = getAlgorithm();
            try {
                if (type.equals("Cipher")) {
                    if (algo.equals("MyCipher")) {
                        return new MyCipher();
                    }
                }
            } catch (Exception ex) {
                throw new NoSuchAlgorithmException(
                    "Error constructing " + type + " for "
                    + algo + " using MyProvider", ex);
            }
            throw new ProviderException("No impl for " + algo + " " + type);
        }
    }
}

このコーディング・スタイルを使用してプロバイダを作成するには、次のことを行います。

  • プロバイダでサポートされているアルゴリズムごとに、Provider.Serviceのインスタンスを指定してputServiceを呼び出します。Provider.Serviceコンストラクタの引数は、サポートされているアルゴリズムを表します。

    次の文では、型がCipherの、MyCipherという名前のサービスが追加されます。このサービスを実装しているクラスの名前は、p.MyCipherです。putServiceの引数は、Provider.Serviceのサブクラスです。

            putService(new ProviderService(this, "Cipher", "MyCipher", "p.MyCipher"));

    この例では、ProviderServiceという名前の、Provider.Serviceのサブクラスを使用します(Provider.Service自体ではない)。これは、それによって、JCAフレームワークがサービスをどのようにインスタンス化するかをカスタマイズするためです。Provider.Serviceの動作をカスタマイズする必要がない場合は、Provider.Serviceコンストラクタを直接呼び出すことができます。

    public final class MyProvider extends Provider {   
        public MyProvider() {
            super("MyProvider", "1.0",
                "Some info about my provider and which algorithms it supports");
            putService(new Provider.Service(
                this, "Cipher", "MyCipher", "p.MyCipher", null, null));
        }
    }

    この例はステップ3.1: Stringオブジェクトを使用してサービスを登録するプロバイダの作成で示した例と基本的に同じであるということに注意してください。

  • newInstanceなど、Provider.Service内の任意のメソッドをオーバーライドして、JCAフレームワークがプロバイダのサービスをどのように処理するかをカスタマイズします。

    この項の初めにある例では、メソッドProvider.Service.newInstanceをオーバーライドします。このメソッドは、要求されたサービスがMyCipherである場合のみ、MyCipherのインスタンスを返します。そうでない場合は、NoSuchAlgorithmExceptionおよびProviderExceptionをスローします。

    オーバーライドできるその他のメソッドの詳細は、Provider.Serviceクラスを参照してください。

    ノート:

    このコーディング・スタイルの例は、sun.security.mscapiパッケージに含まれているJDK ソース・コードを参照してください。
ステップ3.3: Cipher実装のための追加情報の指定

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

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

  • algorithm/mode/paddingまたは
  • algorithm

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

    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プロバイダではデフォルトの128ビットが使用されます。

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

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

表3-1 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セキュリティ標準アルゴリズム名を参照。)

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

  • Cipher.AES/ECB/PKCS5Padding
  • Cipher.AES/CBC/PKCS5Padding
  • Cipher.AES/CFB/PKCS5Padding
  • Cipher.AES/OFB/PKCS5Padding

別のプロバイダは、これらの各モードのクラス(たとえば、ECBCBCCFBOFBそれぞれのクラスを1つずつ)、PKCS5Paddingのクラスを1つ、CipherSpiからサブクラス化された汎用AESクラスを1つ実装する場合もあります。このプロバイダは、マスター・クラス内に次のCipherプロパティを保持します。

  • Cipher.AES
  • Cipher.AES SupportedModes
    • 例: "ECB|CBC|CFB|OFB"

  • Cipher.AES SupportedPaddings
    • 例: "NOPADDING|PKCS5Padding"

algorithm」形式の変換の場合、Cipherエンジン・クラスのgetInstanceファクトリ・メソッドは、次の規則に従ってプロバイダのCipherSpiの実装をインスタンス化します。
  1. プロバイダが、指定された「algorithm」に対応するCipherSpiのサブクラスを登録済みかどうかをチェックする。
    • 登録済みの場合、このクラスをインスタンス化して、このモードおよびパディング方式のデフォルト値(プロバイダにより提供)を使用可能にします。
    • 回答が「いいえ」の場合、例外NoSuchAlgorithmExceptionをスローします。
  2. 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: プロバイダのモジュール宣言の作成

このステップは省略可能ですが、実行することをお薦めします。このステップでは、名前付きモジュールにプロバイダをパッケージ化できます。モジュラJDKでは、その後、クラス・パスの対極にあるものとしてモジュール・パスでプロバイダを見つけることができます。モジュール・システムでは、そのモジュール・パスにあるモジュール内の依存性をより詳細に確認できます。名前付きモジュールを非モジュラJDKで使用できるということを覚えておいてください。モジュール宣言は無視されます。また、その後も、名前のないモジュールまたは自動モジュールにプロバイダをパッケージ化できます。

プロバイダのモジュール宣言を作成し、module-info.javaという名前のファイルに保存します。このモジュール宣言には、次のものが含まれています。

  • モジュールの名前。

  • プロバイダが依存するモジュール。

  • モジュールがサービス実装を提供する場合は、providesディレクティブ。

次のモジュール宣言の例では、com.foo.MyProviderという名前のモジュールを定義します。p.MyProviderは、サービス実装の完全修飾クラス名です。この例では、p.MyProviderは、モジュールjava.security.jgss内にある、パッケージjavax.security.auth.kerberos内のAPIを使用すると仮定します。したがって、ディレクティブrequires java.security.jgssがモジュール宣言内に存在します。

module com.foo.MyProvider {
    provides java.security.Provider with p.MyProvider;
    requires java.security.jgss;
    }

プロバイダは、次の3種類のモジュールにパッケージ化できます。

  • 名前付きまたは明示的モジュール: モジュール・パスに存在し、module-info.classファイルにモジュール構成情報が含まれているモジュール。

    JCAフレームワークでは、ServiceLoaderクラス(プロバイダ構成を簡略化する)を使用して、モジュールに変更を加えずに明示的モジュール内のプロバイダを検索できます。ステップ8.1: プロバイダの構成ステップ10: テスト・プログラムの実行を参照してください。

  • 自動モジュール: モジュール・パスに存在するが、module-info.classファイル(基本的に標準JARファイル)にモジュール構成情報が含まれていないモジュール。

  • 名前のないモジュール: クラス・パスに存在するモジュール。module-info.classファイルがある場合もない場合もあります。このファイルは無視されます。

名前付きモジュールは他よりも優れたパフォーマンス、強力なカプセル化、簡単な構成、高い柔軟性を提供するため、プロバイダを名前付きモジュールにパッケージ化することをお薦めします。

プロバイダのパッケージ化と構成に関しては、柔軟性が非常に高くなります。ただし、これは、それらを使用するアプリケーションの起動方法に影響を与えます。たとえば、追加で--add-exportsオプションまたは--add-modulesオプションを指定する必要がある場合があります。名前付きモジュールでは、通常は、必要なオプションはこれらの追加オプションより少なくなります。また、名前付きモジュールは、より高い柔軟性を提供します。モジュラJDKのクラス・パスにあるそれらを指定することで、非モジュラJDKで、または名前のないモジュールとしてでも、それらを使用できます。モジュールの詳細は、モジュール・システムの状態JEP 261: モジュール・システムを参照してください。

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

実装コードの作成(ステップ 1: サービス実装コードの記述)、プロバイダの命名(ステップ2: プロバイダの命名)、マスター・クラスの作成(ステップ3: プロバイダのサブクラスであるマスター・クラスの記述)、およびモジュール宣言の作成(ステップ4: プロバイダのモジュール宣言の作成)が完了したら、Javaコンパイラを使用してファイルをコンパイルします。

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

ファイルjava.security.Providerを追加してServiceLoaderクラスを使用しプロバイダを検索する

プロバイダが自動モジュールまたは名前のないモジュールにパッケージ化されており(ステップ4: プロバイダのモジュール宣言の作成で説明されているようにモジュール宣言を作成していない)、java.util.ServiceLoaderを使用してプロバイダを検索する場合は、ファイルMETA-INF/services/java.security.ProviderをJARファイルに追加し、そのファイルにプロバイダ実装の完全修飾クラス名が必ず含まれるようにします。

セキュリティ・プロバイダのロード・メカニズムでは、クラス・パスを確認する前に、ServiceLoaderクラスを使用してプロバイダが検索されます。

たとえば、プロバイダの完全修飾クラス名がp.Providerであり、プロバイダのコンパイル済コードがすべてディレクトリclassesにある場合は、次の行を含むclasses/META-INF/services/java.security.Providerという名前のファイルを作成します。

p.MyProvider

jarコマンドを実行してJARファイルを作成する

次のコマンドでは、MyProvider.jarという名前のJARファイルが作成されます。モジュールJARファイルのすべてのコンパイル済コードは、ディレクトリclassesにあります。また、モジュール・ディスクリプタmodule-info.classは、ディレクトリclassesにあります。

jar --create --file MyProvider.jar --module-version 1.0 -C classes

ノート:

module-info.classファイルと--module-versionオプションは省略可能です。ただし、モジュラJARファイルを作成する場合は、module-info.classファイルが必要になります。(モジュラJARファイルは、最上位ディレクトリにmodule-info.classファイルがある標準JARファイルです。)

Java Platform, Standard Editionツール・リファレンスjarを参照してください。

ステップ7: JARファイルの署名(必要な場合)

プロバイダがCipherKeyAgreementKeyGeneratorMacまたはSecretKeyFactoryクラスによって暗号化アルゴリズムを提供している場合は、実行時にJCAがコードを認証できるように、JARファイルに署名する必要があります。ステップ1.1: 暗号化実装のためのJCAプロバイダのその他の要件および推奨事項の検討を参照してください。このタイプの実装を提供していない場合は、このステップをスキップしてかまいません。

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

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

下記は、コード署名証明書の取得するのに使用するステップです。『Java Platform, Standard Editionツール・リファレンス』keytoolを参照してください。

  1. keytoolを使用して2048ビットのRSAキーペアを生成します:
    keytool -genkeypair -alias <alias> \
            -keyalg RSA -keysize 2048 \
            -dname "cn=<Company Name>, \
            ou=Java Software Code Signing, \
            o=Oracle Corporation" \
            -keystore <keystore file name> \  
            -storepass <keystore password>

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

    ノート:

    2048ビット以上のRSAまたはDSAを使用するキーペアを作成することをお薦めします。

    山カッコ(<および>)内のオプション値は、指定する必要がある実際の値を表します。たとえば、<alias>は、新しく生成するキーストアのエントリを参照する際に使用する任意の別名で置き換え、<keystore file name>は、使用するキーストアの名前で置き換えます。

    ヒント:

    実際の値には、角カッコを付けないでください。たとえば、別名をmyTestAliasにする場合、-aliasオプションを次のように指定します。
        -alias myTestAlias
    まだ存在しないキーストアを指定すると、そのキーストアが作成されます。

    ノート:

    入力するコマンド行を、実行するkeytool -genkeypairコマンドと同じ長さにできない場合(Microsoft WindowsのDOSプロンプトに入力する場合など)、コマンドを含むプレーン・テキストのバッチ・ファイルを作成して実行できます。つまり、keytool -genkeypairコマンドだけを含むテキスト・ファイルを新規作成します。なお、コマンドは、全体を1行に入力してください。拡張子.batを付けてファイルを保存します。DOSウィンドウで、ファイル名を(必要に応じてパスを付けて)入力します。これで、バッチ・ファイルに記述されたコマンドが実行されます。
  2. keytoolを使用して、証明書署名要求(CSR)を生成します:
        keytool -certreq -alias <alias> \
            -file <csr file name> \
            -keystore <keystore file name> \
            -storepass <keystore password> 
    ここで、<alias>には、前のステップで作成したRSAキー・ペア・エントリの別名を指定します。このコマンドにより、PKCS#10形式を使用してCSRが生成されます。<csr file name>で指定した名前のファイルにCSRが格納されます。
  3. CSR、連絡先情報、およびその他の必須ドキュメントをJCAコード署名証明書発行局に送信し、JCEコード署名証明書を要求します。詳細は、JCAコード署名証明書発行局を参照してください。
  4. JCEコード署名証明書発行局は要求を受け取ると、これを検証してバックグラウンド・チェックを実行します。このチェックにパスすると、5年間有効なJCEコード署名証明書が作成され、署名されます。コード署名証明書の公開キーを認証するコード署名証明書とJCE CA証明書の2つのテキスト証明書を含む電子メール・メッセージを受信します。
  5. JCAコード署名証明書発行局から受け取った証明書を、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 (RSAキーペアの生成)で作成したのと同じ別名を指定します。このコマンドにより、<alias>で指定されたキーストア・エントリ内の自己署名証明書が、JCAコード署名証明書発行局が署名した証明書で置き換えられます。

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

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

ステップ7.1: コード署名証明書の取得で取得したコード署名証明書を使用して、ステップ6: JARファイルへのプロバイダの記述で作成したJARファイルに署名します。Java Platform, Standard Editionツール・リファレンスjarsignerを参照してください。

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

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

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

jarsigner -verify <JAR file name>

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

ノート:

  • jdk.security.jarsigner APIを使用してJARファイルに署名することもできます。
  • 署名されたJCEプロバイダをアプリケーションに含める場合、他のコード署名ポリシーを実装するためにさらにJARファイルに署名する必要があるときは、適切な証明書/キーを使用して複数の署名をJCEプロバイダのJARに適用する必要があります。JCE署名はJCAフレームワークによってプロバイダJARを受け入れるためのもので、ポリシーの決定には他の署名を使用できます。JARファイルへの複数の署名の適用については、Java Platform, Standard Editionツール・リファレンスjarsignerに関する項を参照してください。
  • 署名済プロバイダをJMODファイルにパッケージ化することはできません。
  • Cipher、KeyAgreement、KeyGenerator、MacまたはSecretKFactorのインスタンスを提供するプロバイダのみに署名する必要があります。SecureRandom、MessageDigest、Signature、KeyStoreなどのインスタンスのみを提供するプロバイダに署名する必要はありません。
  • プロバイダにCipher、KeyAgreement、KeyGeneratorまたはMAC実装がない場合にかぎり、jlinkコマンドを使用してカスタム・ランタイム・イメージにプロバイダをリンクできます。

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

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

ステップ8.1: プロバイダの構成

JCAフレームワークがServiceLoaderクラスの使用によって、またはクラス・パスかモジュール・パスでプロバイダを見つけられるように、プロバイダを登録します。

  1. エディタでjava.securityファイルを開きます。
    • LinuxまたはmacOS: <java-home>/conf/security/java.security

    • Windows: <java-home>\conf\security\java.security

  2. java.securityファイル内で、次のような、SUN、SunRsaSignおよびSunJCEなどの標準プロバイダが静的プロバイダとして構成されているセクションを見つけます。
    security.provider.1=SUN
    security.provider.2=SunRsaSign
    security.provider.3=SunEC
    security.provider.4=SunJSSE
    security.provider.5=SunJCE
    security.provider.6=SunJGSS
    security.provider.7=SunSASL
    security.provider.8=XMLDSig
    security.provider.9=SunPCSC
    security.provider.10=JdkLDAP
    security.provider.11=JdkSASL
    security.provider.12=SunMSCAPI
    security.provider.13=SunPKCS11

    このセクションの各行の形式は、次のようになります。

    security.provider.n=provName|className 

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

    provNameはプロバイダの名前であり、classNameはプロバイダの完全修飾クラス名です。これら2つの名前のどちらかを使用できます。

  3. java.securityファイルにsecurity.provider.n=provName|classNameの形式の行を追加することで、プロバイダを登録します。

    プロバイダをServiceLoaderクラスで検索できるように構成してある場合は(ステップ4: プロバイダのモジュール宣言の作成で説明したようにプロバイダを名前付きモジュールにパッケージ化したか、ファイルjava.security.Providerを追加してServiceLoaderクラスを使用しプロバイダを検索するで説明したようにjava.security.Providerファイルを追加したため)、プロバイダの名前のみを指定します。

    プロバイダをServiceLoaderクラスで検索できるように構成していない場合(JCAフレームワークでクラス・パスかモジュール・パスでプロバイダが検索されることを意味する)、プロバイダの完全修飾クラス名を指定します。

    たとえば、強調表示された行では、プロバイダMyProvider (この完全修飾クラス名はp.MyProviderであり、ServiceLoaderクラスで検索できるように構成されている)が14番目の推奨プロバイダとして登録されます。

    # ...
    security.provider.11=JdkSASL
    security.provider.12=SunMSCAPI
    security.provider.13=SunPKCS11
    security.provider.14=MyProvider

    ServiceLoaderメカニズムが使用されるかどうかが不明な場合や、非モジュラ・システム上でデプロイする場合は、再度プロバイダを登録することもでき、この場合は完全なクラス名を使用します。

    security.provider.15=p.MyProvider

    ノート:

    通常、java.securityファイル内のプロパティは1回のみ解析されます。このファイル内のプロパティを変更した場合は、アプリケーションを再起動して、変更が正しく反映されていることを確認します。

別の方法として、プロバイダを動的に登録することもできます。その場合、プログラム( ステップ9: テスト・プログラムの記述とコンパイルで記述するテスト・プログラムなど)では、SecurityクラスのaddProviderメソッドまたはinsertProviderAtメソッドが呼び出されます。

ServiceLoader<Provider> sl = ServiceLoader.load(java.security.Provider.class);
for (Provider p : sl) {
    System.out.println(p);
    if (p.getName().equals("MyProvider")) {
        Security.addProvider(p);
    }
}

addProviderまたはinsertProviderAtメソッドをコールするプログラムに、次のいずれかの権限を付与します:

java.security.SecurityPermission "addProvider.<provider name>"
java.security.SecurityPermission "insertProvider.<provider name>"

たとえば、プロバイダ名がMyJCEで、プログラムが/localWorkディレクトリのmyapplication.jarファイル内にあり、プログラムがaddProviderメソッドをコールする場合、その権限を付与するgrant文を含むサンプル・ポリシー・ファイルを次に示します:

    grant codeBase "file:/localWork/myapplicaton.jar" {
        permission java.security.SecurityPermission
            "insertProvider.MyJCE";
    };
ステップ8.2: プロバイダ・アクセス権の設定

セキュリティ・マネージャをインストールすると同時に、アプリケーションを実行するときのためにアクセス権を付与する必要があります。セキュリティ・マネージャは、アプリケーションのために、アプリケーション自体のコードによって、またはコマンド行引数によってインストールできます。

  1. プロバイダは、クライアント環境で次のアクセス権が付与されている必要がある場合があります。
    • java.lang.RuntimePermission (クラス保護ドメインを取得するため)。プロバイダは、自己整合性チェックの実行過程で、独自の保護ドメインの取得が必要になる場合があります。
    • java.security.SecurityPermission (プロバイダ・プロパティの設定用)
  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";
        };
    

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

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

  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());
    
  2. サービスが検出されることを確認します。
    たとえば、AES暗号化アルゴリズムを実装した場合には、要求時にこのアルゴリズムが確実に検出されるかどうかを、次のコードを使用して確認できます(ここでもMyProは、独自のプロバイダ名に置き換えてください)。
    
        Cipher c = Cipher.getInstance("AES", "MyPro");
    
        System.out.println("My Cipher algorithm name is " + c.getAlgorithm());
    
  3. オプション: getInstanceへの呼出しでプロバイダ名を指定しない場合、そのアルゴリズムを実装するプロバイダが検出されるまで、登録されたすべてのプロバイダが優先順位に従って検索されます(ステップ8.1: プロバイダの構成を参照)。
  4. オプション: プロバイダが除外メカニズムを実装している場合、その除外メカニズムを使用するテスト・アプレットまたはアプリケーションを記述する必要があります。この種のアプレット/アプリケーションにも、署名、および「アクセス権ポリシー・ファイル」のバンドルが必要です。
    そのようなアプリケーションの作成およびテストの詳細は、「暗号化制限からアプリケーションを除外する方法」を参照してください。

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

テスト・アプリケーションの実行時に必要なjavaコマンド・オプションは、プロバイダを名前付きモジュール、自動モジュールまたは名前のないモジュールとしてパッケージ化したかどうかや、それをServiceLoaderクラスで検索できるように構成したかどうかなどの要因によって異なります。

プロバイダを名前付きモジュールとしてパッケージ化し、それをServiceLoaderクラスで検索できるように構成してある場合(ステップ8.1: プロバイダの構成で説明したようにjava.securityでその名前を指定してそれを登録する)、次のコマンドを使用してテスト・プログラムを実行します。

java --module-path "jars" <other java options>

ディレクトリjarsにプロバイダが含まれます。

プロバイダを別の種類のモジュールにパッケージ化した場合や、それをServiceLoaderクラス用に構成していない場合は、プロバイダのコード・スタイルに応じてさらにオプションが必要な場合があります(ステップ3.1: Stringオブジェクトを使用してサービスを登録するプロバイダの作成およびステップ3.2: Provider.Serviceを使用するプロバイダの作成を参照)。次の表に、これらのオプションを示します。

このjavaコマンドの場合、プロバイダの名前はMyProviderで、その完全修飾クラス名はp.MyProviderであり、プロバイダはディレクトリjarsにあるファイルcom.foo.MyProvider.jarにパッケージ化されます。

表3-2 様々なプロバイダ実装スタイルでの予期されるJavaランタイム・オプション

モジュール・タイプ プロバイダのコーディング・スタイル ServiceLoaderクラス用に構成されているかどうか java.securityファイルで使用されるプロバイダ名 javaコマンド
名前なし StringオブジェクトまたはProvider.Service いいえ 完全修飾クラス名 java -cp "jars/com.foo.MyProvider.jar" <other java options>
名前なし StringオブジェクトまたはProvider.Service はい 完全修飾クラス名またはプロバイダ名 java -cp "jars/com.foo.MyProvider.jar" <other java options>
自動 StringオブジェクトまたはProvider.Service いいえ 完全修飾クラス名 java --module–path "jars/com.foo.MyProvider.jar" --add–modules=com.foo.MyProvider <other java options>
自動 StringオブジェクトまたはProvider.Service はい 完全修飾クラス名またはプロバイダ名 java --module–path "jars/com.foo.MyProvider.jar" <other java options>
名前付き StringオブジェクトまたはProvider.Service いいえ 完全修飾クラス名 java --module–path "jars" --add–modules=com.foo.MyProvider --add–exports=com.foo.MyProvider/p=java.base <other java options>

モジュール宣言にexports pを追加する場合は、--add-exportsオプションを削除できます。

名前付き Stringオブジェクト はい 完全修飾クラス名 java --module–path "jars" --add–exports=com.foo.MyProvider/p=java.base <other java options>

モジュール宣言にexports pを追加する場合は、--add-exportsオプションを削除できます。

名前付き Stringオブジェクト はい プロバイダ名 java --module–path "jars" --add–exports=com.foo.MyProvider/p=java.base <other java options>

モジュール宣言にexports pを追加する場合は、--add-exportsオプションを削除できます。

名前付き Provider.Service はい 完全修飾クラス名 java --module–path "jars" --add–exports=com.foo.MyProvider/p=java.base<other java options>

モジュール宣言にexports pを追加する場合は、--add-exportsオプションを削除できます。

名前付き Provider.Service はい プロバイダ名 java --module–path "jars" <other java options>

テスト・プログラムのための正しいjavaオプションを決定したら、それらを実行します。コードをデバッグし、必要に応じてテストを続行します。Javaランタイムでアルゴリズムのいずれかが見つからない場合は、前のステップを確認し、それらをすべて完了していることを確認します。

様々なインストール・オプション(たとえば、ServiceLoaderクラスを使用するように構成されているか、クラス・パスまたはモジュール・パスに見つかるように構成されている)および実行環境(セキュリティ管理を実行する、または実行しない)を使用して、プログラムを確実にテストしてください。

  1. オプション: テスト中に、コードの変更が必要だと判明した場合は、変更を加え、ステップ5: コードのコンパイルを再コンパイルします。
  2. 更新したプロバイダ・コードをJARファイルに配置します(ステップ6: JARファイルへのプロバイダの記述)。
  3. JARファイルに署名します(ステップ7: JARファイルの署名(必要な場合))。
  4. プロバイダを再構成します(ステップ8.1: プロバイダの構成)。
  5. オプション: 必要な場合は、アクセス権を修正するか追加します(ステップ8.2: プロバイダ・アクセス権の設定)。
  6. プログラムを実行します。
  7. オプション: 必要な場合は、ステップ1から6を繰り返します。

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

プロバイダを米国外に輸出する可能性のある米国内のベンダーはすべて、米国商務省産業安全保障局に輸出承認申請を行う必要があります。

詳細は、輸出問題を担当する顧問弁護士に確認してください。

ノート:

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

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

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

次のステップは、クライアント用のドキュメントを記述することです。少なくとも、次の指定が必要です。
  • プロバイダの参照に使用する名前プログラム。

    ノート:

    このドキュメントの執筆の時点では、プロバイダ名の検索では大文字と小文字が区別されます。つまり、マスター・クラスがプロバイダ名を「CryptoX」として指定した場合、ユーザーが「CRYPTOx」を要求しても、プロバイダは見つかりません。この動作は、将来変更される可能性がありますが、現時点ではクライアントに対して大文字小文字を正確に指定するように通知してください。
  • プロバイダによって実装されるアルゴリズムおよびその他のサービスのタイプ。
  • プロバイダのインストール方法に関する指示。これは、情報と例がプロバイダ固有のものになることを除き、ステップ8.1: プロバイダの構成で示した指示と同様です。
  • セキュリティ・マネージャが稼動している場合に、プロバイダに必要なアクセス権(ステップ8.2: プロバイダ・アクセス権の設定を参照)。
さらに、ドキュメント内で、デフォルトのアルゴリズム・パラメータなどの、クライアントに関係するその他の指定も行う必要があります。
ステップ12.1: 実装をメッセージ・ダイジェストと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
    } 

説明は次のとおりです。

mac
Mac.getInstanceへの呼出しを介して要求が行われた場合に、受け取られるMACオブジェクトを示します。
i1i2およびi3
入力バイト配列を示します。次のバイト配列のために別個のハッシュを計算する必要があります。
  • i1
  • i1およびi2
  • i1、i2およびi3

キー・ペア・ジェネレータ

キー・ペア・ジェネレータ・アルゴリズムでは、クライアントが(initializeメソッドの呼出しを介して)明示的にキー・ペア・ジェネレータを初期化しない場合、各プロバイダはデフォルトの初期化を提供およびドキュメント化する必要があります。

たとえば、SunJCEプロバイダにより提供されるDiffie-Hellmanキー・ペア・ジェネレータは、2048ビットのデフォルト素数モジュラス・サイズ(keysize)を使用します。

キー・ファクトリ

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

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

クライアントがAlgorithmParameterGeneratorエンジン・クラスのinitメソッドの呼出しを介してアルゴリズム・パラメータ・ジェネレータを明示的に初期化しない場合、各プロバイダはデフォルトの初期化を行い、これをドキュメント化する必要があります。

たとえば、SunJCEプロバイダでは、Diffie-Hellmanパラメータの生成に2048ビットのデフォルト素数モジュラス・サイズ(keysize)が使用され、Sunプロバイダでは、DSAパラメータの生成に2048ビットのデフォルト・モジュラス素数サイズが使用されます。

署名アルゴリズム

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

たとえば、SUNプロバイダにより提供されるSHA256withDSA署名アルゴリズムでは、署名は、2つの整数rおよびsの標準ASN.1 SEQUENCEとしてエンコードされます。

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

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

証明書ファクトリ

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

キーストア

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

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

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

実装の詳細および要件

この項では、別名、サービスの相互依存性、アルゴリズム・パラメータ・ジェネレータおよびアルゴリズム・パラメータについてさらに説明します。

別名

JDKでは、別名スキームにより、クライアントがアルゴリズムまたは型の参照時に標準名ではなく別名を使用できます。

多くの暗号化アルゴリズムおよび型には、Javaセキュリティ標準アルゴリズム名で定義されている公式な標準名が1つ存在します。

たとえば、SHA-256は、RFC 1321で定義されたSHA-256メッセージ・ダイジェスト・アルゴリズムの標準名です。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アルゴリズムは、メッセージ・ダイジェスト・アルゴリズムを使用して、パスワードをキーに変換する必要があります。

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

  • どちらのアルゴリズムにも対応した独自の実装の提供。

  • すべてのJava SEプラットフォームのインストールに含まれるデフォルトの SUNプロバイダにより提供される場合のように、一方のアルゴリズムの実装が他方のアルゴリズムのインスタンスを使用するようにします。たとえば、実装しているPBEアルゴリズムがメッセージ・ダイジェスト・アルゴリズムを要求する場合、次の呼出しを行うことにより、SHA256メッセージ・ダイジェスト・アルゴリズムを実装するクラスのインスタンスを取得できます。

        MessageDigest.getInstance("SHA256", "SUN")
    
  • 別の特定のプロバイダにより提供される場合のように、一方のアルゴリズムの実装が他方のアルゴリズムのインスタンスを使用するようにします。これは、プロバイダを使用するすべてのクライアントが、インストールされた他方のプロバイダも保持する場合にだけ有効な方法です。

  • 別の(無指定の)プロバイダにより提供される場合のように、一方のアルゴリズムの実装が他方のアルゴリズムのインスタンスを使用するようにします。つまり、アルゴリズムを名前で要求できます。ただし、特定のプロバイダは指定しません。次に例を示します。:

        MessageDigest.getInstance("SHA256")
    

    これは、プロバイダが使用される各Javaプラットフォームにインストールされた、要求されたアルゴリズムの実装(この例ではSHA256)が少なくとも1つ存在することが確実な場合にだけ有効な方法です。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 適切なインタフェースを実装する独自のクラス(java.security.interfacesパッケージからDSAPrivateKeyおよびDSAPublicKeyインタフェースを実装するクラスなど)も作成し、独自のキー・ペアのジェネレータとこれらの型のキーを返すキー・ファクトリのどちらかまたは両方を作成します。engineInitSignおよびengineInitVerifyに渡されるキーが、実装したキー(つまり、キー・ペア・ジェネレータまたはキー・ファクトリから生成されたキー)と同じ型である必要があります。または、次の方法も可能です。

  • 他のキー・ペア・ジェネレータまたは他のキー・ファクトリからキーを受け取ります。これは、それらが署名の実装が必要な情報(非公開キー、公開キーおよびキー・パラメータなど)を取得することを可能にする適切なインタフェースのインスタンスである場合にかぎり有効です。たとえば、DSS Signatureクラス用のengineInitSignメソッドは、java.security.interfaces.DSAPrivateKeyのインスタンスである任意の秘密キーを受け取ることができます。

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

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

デフォルトの初期化

クライアントが明示的にキー・ペア・ジェネレータまたはアルゴリズム・パラメータ・ジェネレータを初期化しない場合、これらサービスの各プロバイダはデフォルトの初期化を提供(およびドキュメント化)する必要があります。

たとえば、SUNプロバイダは、DSAキー・ペアおよびパラメータの生成にデフォルト・キー・サイズの2048ビットを使用します。他のプロバイダのデフォルト・キー・サイズについては、JDKプロバイダのドキュメントを参照してください。

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

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

提供するドキュメント(ステップ12: プロバイダおよびそのサポート対象サービスのドキュメント化)で、デフォルト・パラメータを指定する必要があります。

たとえば、SUNプロバイダのDSAキー・ペア・ジェネレータは、サポートされているすべてのキー・サイズでキー・ペアを生成するために、計算済のp、qおよびgデフォルト値のセットを提供します。

次の例では、(java.securityファイルで指定されている)優先プロバイダのDSAキー・ペア・ジェネレータのp、qおよびg値と、プロバイダの名前およびデフォルトのキー・サイズを取得します:

    static void printDSA() throws Exception {
        KeyPairGenerator kpgen = KeyPairGenerator.getInstance("DSA");
        Provider prov = kpgen.getProvider();
        System.out.println("Current provider: " + prov.getName());
        
        KeyPair kp = kpgen.genKeyPair();
        DSAPublicKey pubKey = (DSAPublicKey) kp.getPublic();

        DSAParams params = pubKey.getParams();
        BigInteger p = params.getP();
        BigInteger q = params.getQ();
        BigInteger g = params.getG();
        
        System.out.println("Bit length of p: " + p.bitLength());
        System.out.printf("p: %x\n", p);
        System.out.printf("q: %x\n", q);
        System.out.printf("g: %x\n", g);
    }

この例では、次のような出力が表示されます(わかりやすくするために改行とスペースを追加しています):

Current provider: SUN
Bit length of p: 2048
p: 8f7935d9 b9aae9bf abed887a cf4951b6 f32ec59e 3baf3718 e8eac496 1f3efd36
   06e74351 a9c41833 39b809e7 c2ae1c53 9ba7475b 85d011ad b8b47987 75498469
   5cac0e8f 14b33608 28a22ffa 27110a3d 62a99345 3409a0fe 696c4658 f84bdd20
   819c3709 a01057b1 95adcd00 233dba54 84b6291f 9d648ef8 83448677 979cec04
   b434a6ac 2e75e998 5de23db0 292fc111 8c9ffa9d 8181e733 8db792b7 30d7b9e3
   49592f68 09987215 3915ea3d 6b8b4653 c633458f 803b32a4 c2e0f272 90256e4e
   3f8a3b08 38a1c450 e4e18c1a 29a37ddf 5ea143de 4b66ff04 903ed5cf 1623e158
   d487c608 e97f211c d81dca23 cb6e3807 65f822e3 42be484c 05763939 601cd667
q: baf696a6 8578f7df dee7fa67 c977c785 ef32b233 bae580c0 bcd5695d
g: 16a65c58 20485070 4e7502a3 9757040d 34da3a34 78c154d4 e4a5c02d 242ee04f
   96e61e4b d0904abd ac8f37ee b1e09f31 82d23c90 43cb642f 88004160 edf9ca09
   b32076a7 9c32a627 f2473e91 879ba2c4 e744bd20 81544cb5 5b802c36 8d1fa83e
   d489e94e 0fa0688e 32428a5c 78c478c6 8d0527b7 1c9a3abb 0b0be12c 44689639
   e7d3ce74 db101a65 aa2b87f6 4c6826db 3ec72f4b 5599834b b4edb02f 7c90e9a4
   96d3a55d 535bebfc 45d4f619 f63f3ded bb873925 c2f224e0 7731296d a887ec1e
   4748f87e fb5fdeb7 5484316b 2232dee5 53ddaf02 112b0d1f 02da3097 3224fe27
   aeda8b9d 4b2922d9 ba8be39e d9e103a6 3c52810b c688b7e2 ed4316e1 ef17dbde

Provider.Serviceクラス

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クラスのメソッドがオーバーライドされないようにすることをお薦めします。

署名フォーマット

署名アルゴリズムでは、署名をエンコードする形式を指定する必要があります。

署名アルゴリズムを実装する場合、提供するドキュメント(ステップ12: プロバイダおよびそのサポート対象サービスのドキュメント化)で署名(signメソッドのいずれかで生成される)をエンコードする形式を指定する必要があります。

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


SEQUENCE ::= {
        r INTEGER,
        s INTEGER }

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

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

Java Security APIには次のインタフェースがあります。

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

DSAKeyPairGenerator

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

DSAParams実装

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

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

ノート:

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

DSAPrivateKeyおよびDSAPublicKey実装

DSAキー・ペア・ジェネレータまたはキー・ファクトリを実装する場合、Interface DSAPrivateKeyおよびInterface 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インタフェースを実装するには、これらによって定義されるメソッドと、直接または間接的にこれらによって拡張されるインタフェースによって定義されたメソッドを実装する必要があります。

このため、秘密キーの場合、次を実装するクラスを提供する必要があります

  • Interface DSAPrivateKeyインタフェースのgetXメソッド。
  • Interface DSAKeyインタフェースのgetParamsメソッド(DSAPrivateKeyDSAKeyを拡張するため)。ノート: getParamsメソッドは、DSAParamsオブジェクトを返します。このため、DSAParams実装も保持する必要があります。
  • Interface KeyインタフェースのgetAlgorithmgetEncodedおよびgetFormatメソッド(DSAPrivateKeyjava.security.PrivateKeyを拡張し、PrivateKeyKeyを拡張するため)。

    同様に、公開DSAキーの場合、次のものを実装するクラスを提供する必要があります。

    • Interface DSAPublicKeyインタフェースのgetYメソッド。
    • Interface DSAKeyインタフェースのgetParamsメソッド(DSAPublicKeyがDSAKeyを拡張するため)。

      ノート:

      getParamsメソッドは、DSAParamsオブジェクトを返します。このため、DSAParams実装も保持する必要があります。
    • Interface KeygetAlgorithmgetEncodedおよびgetFormatメソッド(DSAPublicKeyjava.security.PublicKeyを拡張し、PublicKeyKeyを拡張するため)。

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

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

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

RSAPrivateKey、RSAPrivateCrtKeyおよびRSAPublicKey実装

RSAキー・ペア・ジェネレータまたはキー・ファクトリを実装する場合、Interface RSAPublicKey (またはInterface RSAPrivateCrtKey、あるいはその両方)およびInterface 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秘密キーの場合、次のものを実装するクラスを提供する必要があります。

  • Interface RSAPrivateKeyインタフェースのgetModulusおよびgetPrivateExponentメソッド。
  • Interface KeyインタフェースのgetAlgorithmgetEncodedおよびgetFormatメソッド(RSAPrivateKeyjava.security.PrivateKeyを拡張し、PrivateKeyKeyを拡張するため)。

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

  • RSA秘密キーを対象として前にリストしたすべてのメソッド(RSAPrivateCrtKeyjava.security.interfaces.RSAPrivateKeyを拡張するため)。
  • Interface RSAPrivateKeyインタフェースのgetPublicExponentgetPrimePgetPrimeQgetPrimeExponentPgetPrimeExponentQおよびgetCrtCoefficientメソッド。

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

  • Interface RSAPublicKeyインタフェースのgetModulusおよびgetPublicExponentメソッド。
  • Interface KeyインタフェースのgetAlgorithmgetEncodedおよびgetFormatメソッド(RSAPublicKeyjava.security.PublicKeyを拡張し、PublicKeyKeyを拡張するため)。

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

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

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

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

DHPrivateKeyおよびDHPublicKey実装

Diffie-Hellmanキー・ペア・ジェネレータまたはキー・ファクトリを実装する場合、Interface DHPrivateKeyおよびInterface 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インタフェースを実装するには、これらによって定義されるメソッドと、直接または間接的にこれらによって拡張されるインタフェースによって定義されたメソッドを実装する必要があります。

このため、秘密キーの場合、次を実装するクラスを提供する必要があります。

  • Interface DHPrivateKeyインタフェースのgetXメソッド。
  • Interface DHKeyインタフェースのgetParamsメソッド(DHPrivateKeyDHKeyを拡張するため)。
  • Interface KeyインタフェースのgetAlgorithmgetEncodedおよびgetFormatメソッド(DHPrivateKeyjava.security.PrivateKeyを拡張し、PrivateKeyKeyを拡張するため)。

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

  • Interface DHPublicKeyインタフェースのgetYメソッド。
  • Interface DHKeyインタフェースのgetParamsメソッド(DHPublicKeyDHKeyを拡張するため)。
  • Interface KeyインタフェースのgetAlgorithmgetEncodedおよびgetFormatメソッド(DHPublicKeyjava.security.PublicKeyを拡張し、PublicKeyKeyを拡張するため)。

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

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

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

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

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

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

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

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

AlgorithmParametersSpiAlgorithmParameterGeneratorSpiまたはKeyPairGeneratorSpi実装のいずれかを提供する場合、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)を指定します。

表3-3 IvParameterSpecのメソッド

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

OAEPParameterSpecクラス

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

表3-4 OAEPParameterSpecのメソッド

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

PBEParameterSpecクラス

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

表3-5 PBEParameterSpecのメソッド

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

RC2ParameterSpecクラス

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

表3-6 RC2ParameterSpecのメソッド

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

RC5ParameterSpecクラス

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

表3-7 RC5ParameterSpecのメソッド

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

DHParameterSpecクラス

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

表3-8 DHParameterSpecのメソッド

メソッド 説明
BigInteger getG() ベース・ジェネレータgを返します。
int getL() ランダム指数(非公開値)のビット単位のサイズlを返します。
BigInteger getP() 素数モジュラスpを返します。
このクラスは、多くの種類のDiffie-Hellmanサービスにとって有用です。たとえば、このクラスは、「SunJCE」プロバイダが実装するDiffie-Hellmanキー協定、キー・ペア・ジェネレータ、アルゴリズム・パラメータ・ジェネレータおよびアルゴリズム・パラメータ・クラスにより利用されます。具体例をあげると、アルゴリズム・パラメータ実装は、AlgorithmParameterSpecを返すgetParameterSpecメソッド用の実装を含む必要があります。「SunJCE」により提供されるDiffie-Hellmanアルゴリズム・パラメータ実装は、DHParameterSpecクラスのインスタンスを返します。

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

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

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

キーの透明な表現とは、対応する仕様クラスで定義された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インタフェースを実装する、次の複数のクラスが提供されます。

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秘密キーを指定します。このクラスには、次のメソッドがあります。

表3-9 DSAPrivateKeySpecのメソッド

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公開キーを指定します。このクラスには、次のメソッドがあります。

表3-10 DSAPublicKeySpecのメソッド

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

RSAPrivateKeySpecクラス

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

表3-11 RSAPrivateKeySpecのメソッド

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

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

RSAPrivateCrtKeySpecクラス

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

表3-12 RSAPrivateCrtKeySpecのメソッド

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公開キーを指定します。このクラスには、次のメソッドがあります。

表3-13 RSAPublicKeySpecのメソッド

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

EncodedKeySpecクラス

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

表3-14 EncodedKeySpecのメソッド

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秘密キーを指定します。

表3-15 DHPrviateKeySpecのメソッド

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

DHPublicKeySpecクラス

表3-16 DHPublicKeySpecのメソッド

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

DESKeySpecクラス

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

表3-17 DESKeySpecのメソッド

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

表3-18 DESedeKeySpecのメソッド

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

PBEKeySpecクラス

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

表3-19 PBEKeySpecのメソッド

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

SecretKeySpecクラス

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

表3-20 SecretKeySpecのメソッド

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

秘密キーの生成

特定の秘密キーアルゴリズム用の秘密キージェネレータ(javax.crypto.KeyGeneratorSpiのサブクラス)を提供する場合は、生成された秘密キーオブジェクトを返すことができます。

次のいずれかの方法で、生成された秘密キーオブジェクト(javax.crypto.SecretKeyのインスタンスである必要があります。engineGenerateKeyを参照)を返すことができます。

  • キー・ジェネレータと関連付けられたアルゴリズムの秘密キーを表すインスタンスを保持するクラスを実装します。キー・ジェネレータ実装により、そのクラスのインスタンスが返されます。キー・ジェネレータにより生成されたキーがプロバイダ固有のプロパティを保持する場合、この方法は有用です。
  • キー・ジェネレータは、javax.crypto.SecretKeyインタフェースをすでに実装しているSecretKeySpecのインスタンスを返します。未加工のキーのバイトおよびキー・ジェネレータに関連付けられた秘密キー・アルゴリズムの名前を、SecretKeySpecコンストラクタに渡します。基盤となる未加工のキーのバイトをバイト配列で表すことができ、関連付けられたキー・パラメータが存在しない場合、この方法は有用です。

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

次の情報は、Javaセキュリティ標準アルゴリズム名内の標準アルゴリズムの1つとして示されていないアルゴリズムを提供するプロバイダに当てはまります。

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の重要な機能です。

デフォルトでは、アプリケーションは、どの強度の暗号化アルゴリズムでも使用できます。ただし、一部の国での輸入規制のため、それらのアルゴリズムの強度を制限する必要がある場合があります。これは、管轄ポリシー・ファイルを使用して行います。暗号化強度の構成を参照してください。JCAフレームワークでは、インストール済みの管轄ポリシー・ファイルで指定された制限が施行されます。

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

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

MyProviderのサンプル・コード

次に、プロバイダ例MyProviderの全ソース・コードを示します。これは移植可能なプロバイダです。クラス・パスまたはモジュール・パスで指定できます。これは、次の2つのモジュールで構成されています。

  • com.example.MyProvider: Provider.Serviceメカニズムの使用によるプロバイダの記述方法を示すプロバイダ例が含まれています。プロバイダの実装および統合までのステップで示されているように、このプロバイダをコンパイル、パッケージ化および署名してから、クラス・パスまたはモジュール・パスで指定する必要があります。

  • com.example.MyApp: MyProviderプロバイダを使用するサンプル・アプリケーションが含まれています。これは、このプロバイダをServiceLoaderメカニズムを使用して検出しロードしてから、Security.addProvider()メソッドを使用して動的に登録します。

この例は、次のファイルで構成されています。

src/com.example.MyProvider/module-info.java

module-info.java内で指定されるモジュール宣言の詳細は、ステップ4: プロバイダのモジュール宣言の作成を参照してください。

module com.example.MyProvider {
    provides java.security.Provider with com.example.MyProvider.MyProvider;
}

src/com.example.MyProvider/com/example/MyProvider/MyProvider.java

MyProviderクラスは、Provider.Serviceクラスを使用するプロバイダの例です。ステップ3.2: Provider.Serviceを使用するプロバイダの作成を参照してください。

package com.example.MyProvider;

import java.security.*;
import java.util.*;

/**
 * Test JCE provider.
 *
 * Registers services using Provider.Service and overrides newInstance().
 */
public final class MyProvider extends Provider {

    public MyProvider() {
        super("MyProvider", "1.0", "My JCE provider");

        final Provider p = this;

        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
            putService(new ProviderService(p, "Cipher",
                    "MyCipher", "com.example.MyProvider.MyCipher"));
            return null;
        });
    }

    private static final class ProviderService extends Provider.Service {

        ProviderService(Provider p, String type, String algo, String cn) {
            super(p, type, algo, cn, null, null);
        }

        ProviderService(Provider p, String type, String algo, String cn,
                String[] aliases, HashMap<String, String> attrs) {
            super(p, type, algo, cn,
                    (aliases == null ? null : Arrays.asList(aliases)), attrs);
        }

        @Override
        public Object newInstance(Object ctrParamObj)
                throws NoSuchAlgorithmException {

            String type = getType();
            if (ctrParamObj != null) {
                throw new InvalidParameterException(
                        "constructorParameter not used with " + type
                        + " engines");
            }
            String algo = getAlgorithm();
            try {
                if (type.equals("Cipher")) {
                    if (algo.equals("MyCipher")) {
                        return new MyCipher();
                    }
                }
            } catch (Exception ex) {
                throw new NoSuchAlgorithmException(
                        "Error constructing " + type + " for "
                        + algo + " using SunMSCAPI", ex);
            }
            throw new ProviderException("No impl for " + algo
                    + " " + type);
        }
    }

    @Override
    public String toString() {
        return "MyProvider [getName()=" + getName()
                + ", getVersionStr()=" + getVersionStr() + ", getInfo()="
                + getInfo() + "]";
    }
}

src/com.example.MyProvider/com/example/MyProvider/MyCipher.java

MyCipherクラスは、Server Provider Interface (SPI)である、CipherSPIを拡張します。プロバイダが実装する各暗号化サービスに、適切なSPIのサブクラスがあります。ステップ1: サービス実装コードの記述を参照してください。

ノート:

このコードは、プロバイダの記述方法を示す単なるスタブ・プロバイダです。実際の暗号化アルゴリズム実装は含まれていません。MyProviderが実在するセキュリティ・プロバイダである場合は、MyCipherクラスに実際の暗号化アルゴリズム実装が含まれます。
package com.example.MyProvider;

import java.security.*;
import java.security.spec.*;
import javax.crypto.*;

/**
 * Implementation represents a test Cipher.
 *
 * All are stubs.
 */
public class MyCipher extends CipherSpi {

    @Override
    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
            throws IllegalBlockSizeException, BadPaddingException {
        return null;
    }

    @Override
    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
            byte[] output, int outputOffset) throws ShortBufferException,
            IllegalBlockSizeException, BadPaddingException {
        return 0;
    }

    @Override
    protected int engineGetBlockSize() {
        return 0;
    }

    @Override
    protected byte[] engineGetIV() {
        return null;
    }

    @Override
    protected int engineGetOutputSize(int inputLen) {
        return 0;
    }

    @Override
    protected AlgorithmParameters engineGetParameters() {
        return null;
    }

    @Override
    protected void engineInit(int opmode, Key key, SecureRandom random)
            throws InvalidKeyException {
    }

    @Override
    protected void engineInit(int opmode, Key key,
            AlgorithmParameterSpec params, SecureRandom random)
            throws InvalidKeyException, InvalidAlgorithmParameterException {
    }

    @Override
    protected void engineInit(int opmode, Key key, AlgorithmParameters params,
            SecureRandom random) throws InvalidKeyException,
            InvalidAlgorithmParameterException {
    }

    @Override
    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
    }

    @Override
    protected void engineSetPadding(String padding)
            throws NoSuchPaddingException {
    }

    @Override
    protected int engineGetKeySize(Key key)
            throws InvalidKeyException {
        return 0;
    }

    @Override
    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
        return null;
    }

    @Override
    protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
            byte[] output, int outputOffset) throws ShortBufferException {
        return 0;
    }
}

src/com.example.MyProvider/META-INF/services/java.security.Provider

java.security.Providerファイルにより、自動モジュールまたは名前のないモジュールで、ServiceLoaderクラスを使用してプロバイダを検索できるようになります。ステップ6: JARファイルへのプロバイダの記述を参照してください。

com.example.MyProvider.MyProvider

src/com.example.MyApp/module-info.java

このファイルには、usesディレクティブが含まれています。これは、モジュールに必要なサービスを指定します。このディレクティブは、モジュール・システムでプロバイダを検出しそれらを確実に動作させるために役立ちます。これにより、MyProviderモジュール定義内のprovidesディレクティブを補完します。

module com.example.MyApp {
    uses java.security.Provider;
}

src/com.example.MyApp/com/example/MyApp/MyApp.java

package com.example.MyApp;

import java.util.*;
import java.security.*;
import javax.crypto.*;

/**
 * A simple JCE test client to access a simple test Provider/Cipher
 * implementation in a signed modular jar.
 */
public class MyApp {

    private static final String PROVIDER = "MyProvider";
    private static final String CIPHER = "MyCipher";

    public static void main(String[] args) throws Exception {

        /*
         * Registers MyProvider dynamically.
         *
         * Could do statically by editing the java.security file.
         * Use the first form if using ServiceLoader ("uses" or
         * META-INF/service), the second if using the traditional class
         * lookup method.  Both if provider could be deployed to either.
         *
         * security.provider.14=MyProvider
         * security.provider.15=com.example.MyProvider.MyProvider
         */
        ServiceLoader<Provider> sl =
            ServiceLoader.load(java.security.Provider.class);
        for (Provider p : sl) {
            if (p.getName().equals(PROVIDER)) {
                System.out.println("Registering the Provider");
                Security.addProvider(p);
            }
        }

        /*
         * Get a MyCipher from MyProvider and initialize it.
         */
        Cipher cipher = Cipher.getInstance(CIPHER, PROVIDER);
        cipher.init(Cipher.ENCRYPT_MODE, (Key) null);

        /*
         * What Provider did we get?
         */
        Provider p = cipher.getProvider();
        Class c = p.getClass();
        Module m = c.getModule();
        System.out.println(p.getName() + ": version "
            + p.getVersionStr() + "\n"
            + p.getInfo() + "\n    "
            + ((m.getName() == null) ? "<UNNAMED>" : m.getName())
            + "/" + c.getName());
    }
}

RunTest.sh

#!/bin/sh

#
# A simple example to show how a JCE provider could be developed in a
# modular JDK, for deployment as either Named/Unnamed modules.
#

#
# Edit as appropriate
#
JDK_DIR=d:/java/jdk9
KEYSTORE=YourKeyStore
STOREPASS=YourStorePass
SIGNER=YourAlias

echo "-----------"
echo "Clean/Init"
echo "-----------"
rm -rf mods jars
mkdir mods jars

echo "--------------------"
echo "Compiling MyProvider"
echo "--------------------"
${JDK_DIR}/bin/javac.exe \
    --module-source-path src \
    -d mods \
    $(find src/com.example.MyProvider -name '*.java' -print)

echo "------------------------------------"
echo "Packaging com.example.MyProvider.jar"
echo "------------------------------------"
${JDK_DIR}/bin/jar.exe --create \
    --file jars/com.example.MyProvider.jar \
    --verbose \
    --module-version=1.0 \
    -C mods/com.example.MyProvider . \
    -C src/com.example.MyProvider META-INF/services

echo "----------------------------------"
echo "Signing com.example.MyProvider.jar"
echo "----------------------------------"
${JDK_DIR}/bin/jarsigner.exe \
    -keystore ${KEYSTORE} \
    -storepass ${STOREPASS} \
    jars/com.example.MyProvider.jar ${SIGNER}

echo "---------------"
echo "Compiling MyApp"
echo "---------------"
${JDK_DIR}/bin/javac.exe \
    --module-source-path src \
    -d mods \
    $(find src/com.example.MyApp -name '*.java' -print)

echo "-------------------------------"
echo "Packaging com.example.MyApp.jar"
echo "-------------------------------"
${JDK_DIR}/bin/jar.exe --create \
    --file jars/com.example.MyApp.jar \
    --verbose \
    --module-version=1.0 \
    -C mods/com.example.MyApp .

echo "------------------------"
echo "Test1                   "
echo "Named Provider/Named App"
echo "------------------------"
${JDK_DIR}/bin/java.exe \
    --module-path 'jars' \
    -m com.example.MyApp/com.example.MyApp.MyApp

echo "--------------------------"
echo "Test2                     "
echo "Named Provider/Unnamed App"
echo "--------------------------"
${JDK_DIR}/bin/java.exe \
    --module-path 'jars/com.example.MyProvider.jar' \
    --class-path 'jars/com.example.MyApp.jar' \
    com.example.MyApp.MyApp

echo "--------------------------"
echo "Test3                     "
echo "Unnamed Provider/Named App"
echo "--------------------------"
${JDK_DIR}/bin/java.exe \
    --module-path 'jars/com.example.MyApp.jar' \
    --class-path 'jars/com.example.MyProvider.jar' \
    -m com.example.MyApp/com.example.MyApp.MyApp

echo "----------------------------"
echo "Test4                       "
echo "Unnamed Provider/Unnamed App"
echo "----------------------------"
${JDK_DIR}/bin/java.exe \
    --class-path \
        'jars/com.example.MyProvider.jar;jars/com.example.MyApp.jar' \
    com.example.MyApp.MyApp