Java Platform, Standard Edition

セキュリティ開発者ガイド

リリース9

E91919-01(原本部品番号:E68624-04)

2017年10月

このガイドは、Javaセキュリティのテクノロジ、ツール、およびJava Platform, Standard Edition (Java SE)上のよく使用されるセキュリティ・アルゴリズム、メカニズムおよびプロトコルの実装を理解するために役立ちます。

Java Platform, Standard Editionセキュリティ開発者ガイド, リリース9

E91919-01

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

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクル社までご連絡ください。

このソフトウェアまたは関連ドキュメントを、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供する場合は、次の通知が適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs. No other rights are granted to the U.S. Government.

このソフトウェアもしくはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアもしくはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアもしくはハードウェアを危険が伴うアプリケーションで使用する際、安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアもしくはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、オラクル社およびその関連会社は一切の責任を負いかねます。

OracleおよびJavaはOracle Corporationおよびその関連企業の登録商標です。その他の名称は、それぞれの所有者の商標または登録商標です。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

このソフトウェアまたはハードウェア、そしてドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。適用されるお客様とOracle Corporationとの間の契約に別段の定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。適用されるお客様とOracle Corporationとの間の契約に定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても一切の責任を負いかねます。

目次

タイトルおよび著作権情報

はじめに

- 対象読者

- ドキュメントのアクセシビリティについて

- 関連ドキュメント

- 表記規則

1 一般的なセキュリティ

- Javaセキュリティの概要

- Javaセキュリティの紹介

- Java言語セキュリティおよびバイト・コード検証

- 基本的なセキュリティ・アーキテクチャ

- セキュリティ・プロバイダ

- ファイルの場所

- Javaの暗号化

- 公開鍵インフラストラクチャ

- 鍵および証明書ストレージ

- 公開鍵インフラストラクチャのツール

- 認証

- セキュアな通信

- SSL、TLSおよびDTLSプロトコル

- Simple Authentication and Security Layer(SASL)

- Generic Security Service APIとKerberos

- アクセス制御

- アクセス権

- セキュリティ・ポリシー

- アクセス制御の実施

- XML署名

- Javaセキュリティに関する追加情報

- Javaセキュリティ・クラスのサマリー

- 削除対象としてマークされている推奨されていないセキュリティAPI

- セキュリティ・ツールのサマリー

- 組込みのプロバイダ

- セキュリティ・アーキテクチャ

- 標準アルゴリズム名

- Java Development Kit (JDK)でのアクセス権

- アクセス権の説明とリスク

- NIO-Related Targets

- メソッドおよび必要なアクセス権

- java.lang.SecurityManagerメソッドのアクセス権チェック

- デフォルトのPolicyの実装とポリシー・ファイルの構文

- デフォルトのPolicyの実装

- ポリシー・ファイルのデフォルトの場所

- Policyの実装の変更

- ポリシー・ファイルの構文

- キーストア・エントリ

- 付与エントリ

- SignedBy、Principal、およびCodeBaseフィールド

- キーストア別名の置換

- アクセス権エントリ

- Windowsシステムでのファイル・パスの指定

- ポリシー・ファイルの例

- ポリシー・ファイルでのプロパティの展開

- Windowsシステム、ファイル・パス、およびプロパティの展開

- パス名正規化

- ポリシー・ファイルでの一般的な展開

- 特権ブロックのためのAPI

- doPrivileged APIの使用

- 戻り値がなく、例外がスローされない場合

- 値を返す場合

- ローカル変数にアクセスする場合

- 例外の処理

- 特権のサブセットのアサーション

- 最小特権

- 追加特権

- 特権コードを使う意味

- リフレクション

- セキュリティのトラブルシューティング

2 Java暗号化アーキテクチャ(JCA)リファレンス・ガイド

- Java暗号化アーキテクチャの概要

- JCAの設計の原則

- プロバイダ・アーキテクチャ

- 暗号化サービス・プロバイダ

- プロバイダの実際の実装方法

- キーストア

- エンジン・クラスとアルゴリズム

- コア・クラスとインタフェース

- Providerクラス

- プロバイダ実装の要求および獲得方法

- プロバイダのインストール

- Providerクラス・メソッド

- Securityクラス

- プロバイダの管理

- セキュリティ・プロパティ

- SecureRandomクラス

- SecureRandomオブジェクトの作成

- SecureRandomオブジェクトのシードまたは再シード

- SecureRandomオブジェクトの使用

- シード・バイトの生成

- MessageDigestクラス

- MessageDigestオブジェクトの作成

- メッセージ・ダイジェスト・オブジェクトのアップデート

- ダイジェストの計算

- Signatureクラス

- Signatureオブジェクトの状態

- Signatureオブジェクトの作成

- Signatureオブジェクトの初期化

- Signatureオブジェクトによる署名

- Signatureオブジェクトによる検証

- Cipherクラス

- Cipherベースのその他のクラス

- Cipher Streamクラス

- SealedObjectクラス

- Macクラス

- Keyインタフェース

- KeyPairクラス

- 鍵仕様のインタフェースおよびクラス

- KeySpecインタフェース

- KeySpecサブインタフェース

- EncodedKeySpecクラス

- PKCS8EncodedKeySpecクラス

- X509EncodedKeySpecクラス

- ジェネレータとファクトリ

- KeyFactoryクラス

- SecretKeyFactoryクラス

- KeyPairGeneratorクラス

- KeyGeneratorクラス

- KeyAgreementクラス

- 鍵の管理

- KeyStoreクラス

- アルゴリズム・パラメータのクラス

- AlgorithmParameterSpecインタフェース

- AlgorithmParametersクラス

- AlgorithmParameterGeneratorクラス

- CertificateFactoryクラス

- SSL/TLS実装でのJCAの使用方法

- 暗号強度の構成

- 管轄ポリシー・ファイル形式

- アプリケーションの暗号化制限の免責を取得する方法

- 標準名

- アプリケーションのパッケージ化

- その他のJCAコード・サンプル

- MessageDigestオブジェクトの計算

- 鍵のペアの生成

- 生成された鍵を使った署名の生成および検証

- 鍵仕様およびKeyFactoryを使った署名の生成と検証

- 乱数の生成

- 2つの鍵の同一性の判定

- Base64でエンコードされた証明書の読み取り

- 認証応答の解析

- 暗号化の使用

- パスワードベース暗号化の使用

- Diffie-Hellman鍵交換、AES/GCMおよびHMAC-SHA256のサンプル・プログラム

- 2つのパーティ間のDiffie-Hellman鍵交換

- 3つのパーティ間のDiffie-Hellman鍵交換

- AES/GCMの例

- HMAC-SHA256の例

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

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

- 用語に関する注記

- プロバイダ実装の概要

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

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

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

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

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

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

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

- ステップ3.2: Provider.Serviceを使用するプロバイダの作成

- ステップ3.3: Cipher実装のための追加情報の指定

- ステップ4: プロバイダのモジュール宣言の作成

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

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

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

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

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

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

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

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

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

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

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

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

- ステップ12.1: 実装をメッセージ・ダイジェストとMAC用に複製可能かどうかの指定

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

- 実装の詳細および要件

- 別名

- サービスの相互依存性

- デフォルトの初期化

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

- Provider.Serviceクラス

- 署名フォーマット

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

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

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

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

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

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

- 秘密鍵の生成

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

- エクスポート機能の保証

- MyProviderのサンプル・コード

4 JDKプロバイダ・ドキュメント

- JDKプロバイダの概要

- 暗号化アルゴリズムの輸入制限

- 暗号の変換

- SecureRandom実装

- SunPKCS11プロバイダ

- SUNプロバイダ

- SunRsaSignプロバイダ

- SunJSSEプロバイダ

- SunJCEプロバイダ

- SunJGSSプロバイダ

- SunSASLプロバイダ

- XMLDSigプロバイダ

- SunPCSCプロバイダ

- SunMSCAPIプロバイダ

- SunECプロバイダ

- OracleUcryptoプロバイダ

- Appleプロバイダ

- JdkLDAPプロバイダ

- JdkSASLプロバイダ

5 PKCS#11リファレンス・ガイド

- SunPKCS11プロバイダ

- SunPKCS11の要件

- SunPKCS11の構成

- Network Security Services (NSS)へのアクセス

- PKCS#11のトラブルシューティング

- PKCS#11プロバイダまたは個々のPKCS#11メカニズム(あるいはその両方)の無効化

- アプリケーション開発者

- トークン・ログイン

- トークン鍵

- プロバイダの遅延選択

- JAAS KeyStoreLoginModule

- JSSEキーストアおよびトラスト・ストアとしてのトークン

- PKCS#11トークンとのkeytoolおよびjarsignerの使用

- ポリシー・ツール

- プロバイダ開発者

- プロバイダ・サービス

- パラメータ・サポート

- SunPKCS11プロバイダでサポートされているアルゴリズム

- SunPKCS11プロバイダのキーストアの要件

- プロバイダの例

6 JAAS (Java Authentication and Authorization Service)

- JAASリファレンス・ガイド

- JAASチュートリアル

- Java Authentication and Authorization Service (JAAS): LoginModule開発者ガイド

- LoginModuleの概要

- LoginModuleの実装手順

- ステップ1: 認証技術の理解

- ステップ2: LoginModule実装への命名

- ステップ3: 抽象LoginModuleメソッドの実装

- ステップ4: サンプル・アプリケーションの選択または記述

- ステップ5: LoginModuleおよびアプリケーションのコンパイル

- ステップ6: テストの準備

- ステップ7: LoginModuleの試用

- ステップ8: LoginModule実装のドキュメント化

- ステップ9: LoginModule JARファイルおよびドキュメントの有効化

7 Java Generic Security Services (Java GSS-API)

- Kerberosを使ったJava GSS-APIおよびJAASのチュートリアル

- Kerberosを使ったJavaのシングル・サインオン

- Java GSSの高度なセキュリティ・プログラミング

- Kerberos 5 GSS-APIメカニズム

8 Java Secure Socket Extension (JSSE)リファレンス・ガイド

- JSSEの概要

- JSSEの特長と利点

- JSSE標準API

- SunJSSEプロバイダ

- JSSE関連ドキュメント

- 用語と定義

- Secure Sockets Layer (SSL)プロトコルの概要

- SSLを使用することの利点

- SSLの仕組み

- 暗号化処理

- 秘密鍵暗号化

- 公開鍵暗号化

- 秘密鍵暗号化と公開鍵暗号化の比較

- 公開鍵証明書

- 暗号化ハッシュ関数

- メッセージ認証コード

- デジタル署名

- SSLハンドシェーク

- SSLプロトコル

- ハンドシェークの再実行(再ネゴシエーション)

- 符号化方式の選択とリモート・エンティティの検証

- クライアント主導型OCSPとOCSPステープリング

- クライアント主導型OCSPと証明書失効

- クライアント主導型OCSPを使用するためのJavaクライアントの設定

- OCSPステープリングと証明書失効

- OCSPステープリングを使用するためのJavaクライアントの設定

- OCSPステープリングを使用するためのJavaサーバーの設定

- OCSPステープリングの構成プロパティ

- JSSEクラスとインタフェース

- JSSEのコア・クラスとインタフェース

- SocketFactoryおよびServerSocketFactoryクラス

- SSLSocketFactoryおよびSSLServerSocketFactoryクラス

- SSLSocketFactoryの取得

- SSLSocketおよびSSLServerSocketクラス

- SSLSocketの取得

- SSLEngineクラス

- SSLEngineオブジェクトの作成

- SSL/TLSデータの生成と処理

- Datagram Transport Layer Security (DTLS)プロトコル

- DTLSハンドシェーク

- DTLS接続での再送信の処理

- DTLSのためのSSLEngineオブジェクトの作成

- DTLSデータの生成と処理

- SSLEngineの操作のステータスについて

- ブロック・タスクの処理

- SSL/TLS/DTLS接続の停止

- SSLSessionとExtendedSSLSession

- HttpsURLConnectionクラス

- 割当て済のSSLSocketFactoryの設定

- 割当て済のHostnameVerifierの設定

- サポート・クラスとインタフェース

- SSLContextクラス

- SSLContextクラスの取得と初期化

- SSLContextオブジェクトの作成

- TrustManagerインタフェース

- TrustManagerFactoryクラス

- TrustManagerFactoryの作成

- PKIX TrustManagerのサポート

- X509TrustManagerインタフェース

- X509TrustManagerの作成

- 独自のX509TrustManagerの作成

- keyStoreの動的更新

- X509ExtendedTrustManagerクラス

- X509ExtendedTrustManagerの作成

- 独自のX509ExtendedTrustManagerの作成

- KeyManagerインタフェース

- KeyManagerFactoryクラス

- KeyManagerFactoryの作成

- X509KeyManagerインタフェース

- X509KeyManagerの作成

- 独自のX509KeyManagerの作成

- X509ExtendedKeyManagerクラス

- TrustManagerとKeyManagerの関係

- 二次サポート・クラスおよびインタフェース

- SSLParametersクラス

- 暗号化方式群の優先順位

- SSLSessionContextインタフェース

- SSLSessionBindingListenerインタフェース

- SSLSessionBindingEventクラス

- HandShakeCompletedListenerインタフェース

- HandShakeCompletedEventクラス

- HostnameVerifierインタフェース

- X509Certificateクラス

- AlgorithmConstraintsインタフェース

- StandardConstantsクラス

- SNIServerNameクラス

- SNIMatcherクラス

- SNIHostNameクラス

- JSSEのカスタマイズ

- java.lang.Systemプロパティの指定方法

- java.security.Securityプロパティの指定方法

- X509証明書実装のカスタマイズ

- HTTPSプロトコルの代替実装の指定

- プロバイダ実装のカスタマイズ

- 暗号化プロバイダの静的な登録

- 暗号化サービス・プロバイダを動的に登録する

- プロバイダ構成

- 特定の複数のアルゴリズムのための推奨プロバイダの構成

- デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ

- デフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズ

- 無効化された制限付き暗号化アルゴリズム

- 暗号化アルゴリズム・プロバイダのカスタマイズ

- エフェメラルDiffie-Hellman鍵のサイズのカスタマイズ

- 最大断片長ネゴシエーション(MFLN)拡張のカスタマイズ

- 最大および最小パケット・サイズの構成

- Transport Layer Security (TLS)再ネゴシエーションの問題

- この問題を解決するためのフェーズ別アプローチ

- フェーズ2修正の説明

- SSL/TLS再ネゴシエーションに対する回避方法と代替方法

- TLS実装の詳細

- フェーズ1修正の説明

- SSL/TLS再ネゴシエーションにおける安全でないサーバー証明書変更の許可

- ハードウェア高速化およびスマート・カードのサポート

- スマートカードをキーストアおよびトラストストアとして使用するためのJSSEの構成

- 複数の動的キーストア

- Kerberos暗号化方式群

- Kerberos要件

- ピアのアイデンティティ情報

- セキュリティ・マネージャ

- その他のキーストア形式(PKCS12)

- Server Name Indication (SNI)拡張

- TLSのアプリケーション層プロトコル・ネゴシエーション

- クライアントでのALPNの設定

- サーバーでのデフォルトALPNの設定

- サーバーでのカスタムALPNの設定

- ハンドシェーク中のネゴシエーション済ALPN値の特定

- ALPN関連のクラスとメソッド

- JSSEのトラブルシューティング

- 構成の問題

- ハンドシェーク時のCertificateException

- 実行時例外: SSL Service Not Available

- 実行時例外: "No available certificate corresponding to the SSL cipher suites which are enabled"

- 実行時例外: No Cipher Suites in Common

- ClientHelloメッセージの送信後ソケットが切断される

- 必要なアルゴリズムをサポートするJCAプロバイダをSunJSSEが見つけられず、NoSuchAlgorithmExceptionが発生する

- WebサーバーからSSLでアプリケーション・リソースを取得しようとするとFailedDownloadExceptionがスローされる

- RC4暗号化方式群がDTLS用に構成されている場合のIllegalArgumentException

- デバッグ・ユーティリティ

- SSL/TLS接続のデバッグ

- コード例

- セキュアでないソケットからセキュアなソケットへの変換

- JSSEサンプル・コードの実行

- JSSEで使用するキーストアの作成

- Server Name Indication (SNI)拡張の使用

- 一般的なクライアント側使用例

- 一般的なサーバー側使用例

- 仮想インフラストラクチャの操作

- 標準名

- プロバイダのプラグイン可能性

- JSSE暗号化方式群のパラメータ

9 Java PKIプログラマーズ・ガイド

- PKIプログラマーズ・ガイドの概要

- 公開鍵証明書の概要

- X.509証明書と証明書失効リスト(CRL)

- コア・クラスとインタフェース

- 基本のCertification Pathクラス

- CertPathクラス

- CertificateFactoryクラス

- CertPathParametersインタフェース

- Certification Path検証クラス

- CertPathValidatorクラス

- CertPathValidatorResultインタフェース

- Certification Path構築クラス

- CertPathBuilderクラス

- CertPathBuilderResultインタフェース

- 証明書/CRLストレージ・クラス

- CertStoreクラス

- CertStoreParametersインタフェース

- CertSelectorおよびCRLSelectorインタフェース

- X509CertSelectorクラス

- X509CRLSelectorクラス

- PKIXクラス

- TrustAnchorクラス

- PKIXParametersクラス

- PKIXCertPathValidatorResultクラス

- PolicyNodeインタフェースおよびPolicyQualifierInfoクラス

- PKIXBuilderParametersクラス

- PKIXCertPathBuilderResultクラス

- PKIXCertPathCheckerクラス

- 証明書パス検証でのPKIXCertPathCheckerの使用

- PKIXRevocationCheckerクラスを使った証明書の失効ステータスのチェック

- サービス・プロバイダの実装

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

- サービスの相互依存性

- 証明書パス・パラメータ仕様のインタフェース

- 証明書パスの結果の仕様インタフェース

- 証明書パスの例外クラス

- 付録A: 標準名

- 付録B: SUNプロバイダでのCertPath実装

- 付録C: OCSPサポート

- 付録D: JdkLDAPプロバイダでのCertPath実装

- 付録E: 暗号化アルゴリズムの無効化

10 Java SASL APIプログラミングおよび配備ガイド

- Java SASL APIの概要

- メカニズムの作成

- メカニズムに入力を渡す

- メカニズムの使用

- ネゴシエーション済みのセキュリティ層の使用

- SASLメカニズムをインストールおよび選択する方法

- SunSASLプロバイダ

- SunSASLプロバイダのクライアント・メカニズム

- SunSASLプロバイダのサーバー・メカニズム

- デバッグおよびモニタリング

- JdkSASLプロバイダ

- JdkSASLプロバイダのクライアント・メカニズム

- JdkSASLプロバイダのサーバー・メカニズム

- SASLセキュリティ・プロバイダの実装

11 XMLデジタル署名

- Java XMLデジタル署名API仕様

- 謝辞

- 要件

- APIの依存性

- 目標にしない事項

- パッケージの概要

- サービス・プロバイダ

- DOMメカニズムの要件

- オープンAPIの問題

- プログラミング例

- XMLデジタル署名APIの概要とチュートリアル

- パッケージ階層

- サービス・プロバイダ

- XML署名について

- XML署名の例

- XMLデジタル署名APIの例

- 検証の例

- XML署名の検証

- 署名を含むドキュメントのインスタンス化

- 検証する署名要素の指定

- 検証コンテキストの作成

- XML署名のアンマーシャリング

- XML署名の検証

- KeySelectorの使用

- GenEnvelopedの例

- XML署名の生成

- 署名するドキュメントのインスタンス化

- 公開鍵のペアの作成

- 署名コンテキストの作成

- XML署名の構築

- XML署名の生成

- 生成されるドキュメントの印刷または表示

12 セキュリティAPIの仕様

13 削除対象としてマークされている推奨されていないセキュリティAPI

14 セキュリティ・ツール

15 セキュリティのチュートリアル

はじめに

このガイドでは、Javaセキュリティのテクノロジ、ツール、およびJava Platform, Standard Edition (Java SE)上のよく使用されるセキュリティ・アルゴリズム、メカニズムおよびプロトコルの実装について説明します。

対象読者

このドキュメントは、包括的なJavaセキュリティ・フレームワークを使用してアプリケーションを構築する、経験のある開発者が対象となっています。また、アプリケーションをセキュアに管理するための一連のツールを使用するユーザーまたは管理者が対象となっています。

ドキュメントのアクセシビリティについて

Oracleのアクセシビリティへの取り組みについては、Oracle Accessibility ProgramのWebサイト(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc)にアクセスしてください。

Oracle Supportへのアクセス

サポートをご購入のOracleのお客様は、My Oracle Supportにアクセスして電子サポートを受けることができます。詳細は、http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info、聴覚に障害があるお客様はhttp://www.oracle.com/pls/topic/lookup?ctx=acc&id=trsを参照してください。

関連ドキュメント

他のJDK 9ガイドについては、Oracle JDK 9のドキュメントを参照してください。

表記規則

このマニュアルでは次の表記規則を使用します。

規則

意味

太字

太字は、操作に関連するGraphical User Interface要素、または本文中で定義されている用語および用語集に記載されている用語を示します。

イタリック体

イタリックは、ユーザーが特定の値を指定するプレースホルダ変数を示します。

固定幅フォント

固定幅フォントは、段落内のコマンド、URL、サンプル内のコード、画面に表示されるテキスト、または入力するテキストを示します。

1 一般的なセキュリティ

Javaセキュリティの概要では、暗号化、公開鍵インフラストラクチャ、認証、セキュアな通信、アクセス制御およびXML署名について説明します。

JDK 8ドキュメント内のセキュリティ・アーキテクチャでは、主要なセキュリティ機能の目的の概要、セキュリティ・クラスとそれらの使用方法の概要、セキュリティ・アーキテクチャがコードに与える影響についての考察、およびセキュリティの影響を受けるコードの記述についての見解を示します。

Javaセキュリティ標準アルゴリズム名仕様では、Java SEで必要となり使用されるアルゴリズム、証明書およびキーストア・タイプの一連の標準名について説明します。

Java Development Kit (JDK)におけるアクセス権では、組込みのJDKアクセス権タイプについてと、各アクセス権の付与のリスクについて説明します。

セキュリティのトラブルシューティングでは、セキュリティ・アクセスを監視できるようになる、java.security.debugシステム・プロパティのオプションをリストします。

Javaセキュリティの概要

Javaセキュリティには、API、ツール、およびよく使用されるセキュリティ・アルゴリズム、メカニズムおよびプロトコルの実装が大規模なセットとして含まれています。JavaセキュリティAPIは、暗号化、公開鍵インフラストラクチャ、安全な通信、認証、アクセス制御など、広範な領域に及んでいます。Javaセキュリティ・テクノロジでは、開発者に、アプリケーションを記述するための包括的なセキュリティ・フレームワークが提供され、ユーザーまたは管理者に、アプリケーションを安全に管理するための一連のツールが提供されます。

Javaセキュリティの紹介

JDKは、セキュリティを重視して設計されました。その中核では、Java言語自体が型保証されており、自動ガベージ・コレクションを提供してアプリケーション・コードの堅牢性を強化しています。安全なクラス・ロードおよび検証メカニズムによって、正当なJavaコードのみが実行されます。Javaセキュリティ・アーキテクチャには、アプリケーション・プログラミング・インタフェース(API)、ツール、および一般的に使用されるセキュリティ・アルゴリズム、メカニズムおよびプロトコルの実装の、大規模なセットが含まれています。

JavaセキュリティAPIは、広範な領域に及びます。暗号化および公開鍵インフラストラクチャ(PKI)インタフェースは、安全なアプリケーションを開発するための基盤となります。認証およびアクセス制御を実行するためのインタフェースにより、アプリケーションは、保護されたリソースへの承認されていないアクセスから保護できます。

APIでは、アルゴリズムおよびその他のセキュリティ・サービスについて、複数の相互運用可能な実装が可能です。サービスはプロバイダに実装され、プロバイダは、アプリケーションがセキュリティ・サービスの実装を意識せずに簡単に取得できるようにする標準インタフェースによって、JDKにプラグインされます。これにより、開発者は、複雑なセキュリティ・メカニズムを実際に実装する方法ではなく、セキュリティをアプリケーションに統合する方法に重点を置くことができます。

JDKには、基本的なセキュリティ・サービスを実装する多数のプロバイダが含まれています。追加のカスタム・プロバイダをインストールすることも可能です。これにより、開発者は新しいセキュリティ・メカニズムでプラットフォームを拡張できます。

JDKは、複数のモジュールに分けられています。セキュリティAPIを含むモジュールには、次のものがあります。

表1-1 セキュリティAPIを含むモジュール

モジュール

説明

java.base

Java SEの基礎を成すAPIを定義します。含まれているパッケージには、java.security、javax.crypto、javax.net.sslおよびjavax.security.authなどがあります。

java.security.jgss

IETF Generic Security Services API (GSS-API)のJavaバインディングを定義します。このモジュールには、Kerberos v5やSPNEGOなどのGSS-APIメカニズムも含まれています。

java.security.sasl

IETF Simple Authentication and Security Layer (SASL)のJavaサポートを定義します。このモジュールには、DIGEST-MD5、CRAM-MD5およびNTLMなどのSASLメカニズムも含まれています。

java.smartcardio

Javaスマートカード入出力APIを定義します。

java.xml.crypto

XML暗号化のAPIを定義します。

jdk.security.auth

javax.security.auth.*インタフェースおよび様々な認証モジュールの実装を提供します。

jdk.security.jgss

GSS-APIのJava拡張、およびSASL GSS-APIメカニズムの実装を定義します。

Java言語セキュリティおよびバイト・コード検証

Java言語は、型保証されるように設計されており、簡単に使用できます。自動メモリー管理、ガベージ・コレクション、および配列の範囲チェックを備えています。これにより、開発者の全体的なプログラミングの負荷を軽減し、見つかりにくいプログラミング・エラーの減少および安全で堅牢なコードを実現します。

コンパイラは、Javaプログラムをマシンに依存しないバイト・コード表現に変換します。正当なバイト・コードのみがJava Runtimeで実行されるように、バイトコード・ベリファイアが呼び出されます。これは、バイト・コードがJava言語仕様に準拠しており、Javaの言語規則または名前空間制限に違反していないことをチェックします。ベリファイアは、メモリー管理違反、スタック・アンダーフローまたはオーバーフロー、および不正な型キャストもチェックします。バイト・コードが検証されると、Java Runtimeはそれらの実行を準備します。

また、Java言語は、異なるアクセス修飾子を定義してJavaクラス、メソッド、およびフィールドに割り当てることができるため、開発者はクラス実装へのアクセスを必要に応じて制限できます。この言語は、4つの異なるアクセス・レベルを定義します。

- private: 最も制限された修飾子。アクセスは、privateメンバー(メソッドなど)が定義された特定のクラス以外では許可されません。

- protected: 任意のサブクラス、または同じパッケージ内の他のクラスへのアクセスを許可します。

- Package-private: 指定されない場合、これがデフォルトのアクセス・レベルとなります。同じパッケージ内のクラスへのアクセスを許可します。

- public: 要素にどこからでもアクセスできることは保証されなくなります。アクセス可能性は、その要素を含むパッケージがその定義モジュールによってエクスポートされるかどうか、およびそのモジュールをそれにアクセスしようとしているコードを含むモジュールで読み取れるかどうかによって異なります。

基本的なセキュリティ・アーキテクチャ

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

これらのAPIは、次の方針に基づいて設計されました。

実装の独立性

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

実装の相互運用性

プロバイダは、アプリケーション間で相互運用できます。具体的に述べると、アプリケーションは、プロバイダからのデフォルト値に依存しない場合は、特定のプロバイダにバインドされません。

アルゴリズムの拡張性

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

セキュリティ・プロバイダ

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

アプリケーションは、関連するgetInstanceメソッドによって、基盤となるプロバイダからセキュリティ・サービスを要求します。

たとえば、メッセージ・ダイジェストの作成は、プロバイダから利用可能なサービスの1つのタイプを表します。特定のメッセージ・ダイジェスト・アルゴリズムの実装を要求するには、メソッドjava.security.MessageDigest.getInstanceを呼び出します。次の文は、プロバイダ名を指定せずにSHA-256メッセージ・ダイジェスト実装を要求します。

MessageDigest md = MessageDigest.getInstance("SHA-256");

次の図では、この文でSHA-256メッセージ・ダイジェスト実装がどのように取得されるかを示します。プロバイダが優先順位に従って検索され、その特定のアルゴリズムを提供する最初のプロバイダであるProviderBからの実装が返されます。

図1-1 プロバイダ指定なしでのSHA-256メッセージ・ダイジェスト実装の要求

オプションで、プロバイダ名を指定することで特定のプロバイダからの実装を要求できます。次の文は、特定のプロバイダProviderCからのSHA-256メッセージ・ダイジェスト実装を要求します。

MessageDigest md = MessageDigest.getInstance("SHA-256", "ProviderC");

次の図では、この文で特定のプロバイダProviderCからのSHA-256メッセージ・ダイジェスト実装がどのように要求されるかを示します。この場合は、優先順位の高いプロバイダであるProviderBもSHA-256実装を提供していても、指定されたプロバイダからの実装が返されます。

図1-2 特定のプロバイダからのSHA-256メッセージ・ダイジェスト実装の要求

メッセージ・ダイジェスト・アルゴリズムなどの暗号化サービスの詳細は、Javaの暗号化の項を参照してください。

OracleのJavaプラットフォーム実装には、アプリケーションによって使用できる一連の基本的なセキュリティ・サービスを実装する、組込みの多数のデフォルト・プロバイダが含まれています。Javaプラットフォームのほかのベンダーの実装には、ベンダー固有のセキュリティ・サービスのセットをカプセル化した、異なるプロバイダのセットが含まれている場合があります。組込みのデフォルト・プロバイダという用語は、Oracleの実装で利用可能なプロバイダのことを指しています。

ファイルの場所

次の表では、セキュリティ関連のいくつかのファイルおよびツールの場所をリストします。

表1-2 Javaセキュリティ・ファイルおよびツール

ファイル名またはツール名

場所

説明

java.security

<java-home>/conf/security

プロバイダの構成など、Javaセキュリティの特定の側面は、セキュリティ・プロパティを設定することでカスタマイズできます。java.securityファイルでセキュリティ・プロパティを静的に設定できます。セキュリティ・プロパティは、java.securityパッケージ内のSecurityクラスの適切なメソッドを呼び出すことによって、動的に設定することもできます。

java.policy

<java-home>/conf/security

これはデフォルトのシステム・ポリシー・ファイルです。セキュリティ・ポリシーを参照してください。

暗号化ポリシー・ディレクトリ

<java-home>/conf/security/policy

このディレクトリには、管轄ポリシー・ファイルのセットが含まれています。暗号化強度の構成を参照してください。

cacerts

<java-home>/lib/security

cacertsファイルは、証明書発行局(CA)およびその他の信頼できる証明書を含む、システム全体のキーストアを表します。このファイルの構成と管理の詳細は、Java Platform、Standard Editionツール・リファレンスのkeytoolを参照してください。

keytool、jarsigner、policytool

Windowsのみ: kinit、klist、ktab

<java-home>/bin

セキュリティ関連ツールの詳細は、Java Platform, Standard Editionツール・リファレンスでセキュリティのツールおよびコマンドを参照してください。

Javaの暗号化

Java暗号化アーキテクチャは、Javaプラットフォームの暗号化機能へのアクセスおよび開発のためのフレームワークです。

これには、各種暗号化サービスのためのAPIが含まれています。それらを次に示します。

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

- デジタル署名アルゴリズム

- 対称バルクおよびストリーム暗号化

- 非対称暗号化

- パスワードベース暗号化(PBE)

- 楕円曲線暗号(ECC)

- 鍵協定アルゴリズム

- 鍵ジェネレータ

- メッセージ認証コード(MAC)

- 安全な乱数ジェネレータ

歴史的な理由(輸出規制)により、暗号化APIは2つの別個のパッケージに編成されています。

- java.securityおよびjava.security.*パッケージには、輸出規制の対象ではないクラス(SignatureやMessageDigestなど)が含まれています。

- javax.cryptoパッケージには、輸出規制の対象のクラス(CipherやKeyAgreementなど)が含まれています。

暗号化インタフェースはプロバイダ・ベースであり、複数の相互運用性のある暗号化実装が可能です。ソフトウェアで暗号化操作を行うプロバイダもあれば、スマートカード・デバイスやハードウェア暗号化アクセラレータなどのハードウェア・トークン上で暗号化操作を行うプロバイダもあります。輸出規制の対象のサービスを実装するプロバイダには、Oracle JCE証明書発行局によって発行された証明書でデジタル署名を行う必要があります。

Javaプラットフォームには、最も一般的に使用される暗号化アルゴリズムの多くのための組込みプロバイダが含まれています。それらは、RSA、DSAおよびECDSA署名アルゴリズム、AES暗号化アルゴリズム、SHA-2メッセージ・ダイジェスト・アルゴリズム、Diffie-Hellman (DH)およびElliptic Curve Diffie-Hellman (ECDH)鍵協定アルゴリズムなどです。組込みプロバイダの大部分は、Javaコードで暗号化アルゴリズムを実装しています。

Javaプラットフォームには、ネイティブPKCS#11 (v2.x)トークンへのブリッジとして機能する組込みプロバイダも含まれています。このプロバイダはSunPKCS11という名前であり、JavaアプリケーションはPKCS#11に準拠するトークンに存在する暗号化サービスにシームレスにアクセスできます。

Windowsでは、JavaプラットフォームにネイティブMicrosoft CryptoAPIへのブリッジとして機能する組込みプロバイダが含まれています。このプロバイダはSunMSCAPIという名前であり、JavaアプリケーションはCryptoAPIを介してWindows上の暗号化サービスにシームレスにアクセスできます。

公開鍵インフラストラクチャ

公開鍵インフラストラクチャ(PKI)は、公開鍵暗号方式に基づいて情報のセキュアな交換を可能にするフレームワークに対して使用される用語です。(人や組織などの)識別情報のデジタル証明書へのバインドを可能にし、証明書の信頼性を検証する手段を提供します。PKIには、鍵、証明書、公開鍵暗号化および信頼できる証明書発行局(CA)が含まれます。CAは、証明書を生成してデジタル署名を行います。

Javaプラットフォームには、X.509デジタル証明書と証明書失効リスト(CRL)のAPIおよびプロバイダ・サポート、PKIXに準拠した証明書パスの構築および検証が含まれています。PKIに関係するクラスは、java.securityおよびjava.security.certパッケージ内にあります。

鍵および証明書ストレージ

Javaプラットフォームは、キーストアおよび証明書ストアを使用して、暗号化鍵および証明書の長期にわたる永続的なストレージを提供します。具体的には、java.security.KeyStoreクラスがキーストアを表し、java.security.cert.CertStoreクラスが証明書ストアを表します。キーストアは、(たとえば、証明書パス検証中に使用される)暗号化鍵または信頼できる証明書、あるいはその両方のセキュアなリポジトリです。証明書ストアは、関連がなく通常は信頼されない証明書が含まれるため非常に大きくなる可能性がある公開リポジトリです。CertStoreはCRLを格納する場合もあります。

KeyStoreとCertStoreの実装は、タイプによって区別されます。Javaプラットフォームには、標準のPKCS11とPKCS12キーストア・タイプ(その実装は、RSA Securityの対応するPKCS仕様に準拠する)が含まれています。さらに、JKS (Java Key Storeを表す)と呼ばれる独自のファイルベースのキーストア・タイプと、DKS (Domain Key Store)と呼ばれるタイプも含まれます。DKSは単一の論理キーストアとして存在するキーストアのコレクションです。

Javaプラットフォームには、特別な組込みキーストアcacertsが含まれています。このキーストアには、既知の信頼できるCAの多数の証明書が含まれています。keytoolユーティリティでは、cacertsに含まれている証明書をリストできます。Java Platform, Standard Editionツール・リファレンスでkeytoolを参照してください。

Javaの暗号化の項で説明されているSunPKCS11プロバイダには、PKCS11 KeyStore実装が含まれています。つまり、安全なハードウェア(スマートカードなど)に存在する鍵や証明書は、KeyStore APIによってJavaアプリケーションからアクセスおよび使用できます。スマートカードの鍵がデバイスを離れることは許可されない場合があります。このような場合、KeyStore APIから返されるjava.security.Keyオブジェクトは、単純に鍵の参照になる(つまり、実際の鍵データは含まれない)ことがあります。このようなKeyオブジェクトは、実際の鍵が存在するデバイスでの暗号化操作の実行にのみ使用できます。

Javaプラットフォームには、LDAP証明書ストア・タイプ(LDAPディレクトリに格納されている証明書へのアクセス用)、およびメモリー内のCollection証明書ストア・タイプ(java.util.Collectionオブジェクトで管理されている証明書へのアクセス用)も含まれています。

公開鍵インフラストラクチャのツール

鍵、証明書、およびキーストアを操作するための2つの組込みツールがあります。

- keytoolでは、キーストアを作成および管理します。これは、次のタスクを実行するために使用します。

- 公開鍵と非公開鍵のペアを作成する

- ファイルとして格納されたX.509 v1、v2、およびv3証明書を表示、インポート、エクスポートする

- X.509証明書の作成

- CAに送信される証明書(PKCS#10)要求を発行する

- 証明書要求に基づいて証明書を作成する

- 証明書応答をインポートする(証明書要求の送信先のCAから取得する)

- 公開鍵証明書を信頼できるものとして指定する

- パスワードを受け付け、それを秘密鍵としてセキュアに保存する

- jarsignerは、JARファイルに署名し、署名付きJARファイルの署名を検証します。Java ARchive (JAR)ファイル形式を使用すると、複数のファイルを1つのファイルに統合できます。JARファイルには、主にアプレットおよびアプリケーションに関連したクラス・ファイルおよび補助リソースが含まれています。

コードにデジタル署名を行うには、次のことを実行します。

1. keytoolを使用して、適切な鍵および証明書を生成するかキーストアにインポートします(それらがまだキーストアにない場合)。

2. jarツールを使用して、コードをJARファイルにパッケージ化します。

3. jarsignerツールを使用して、JARファイルに署名します。jarsignerツールは、キーストアにアクセスし、JARファイルへの署名、または署名付きJARファイルの署名の検証に必要な鍵および証明書を見つけます。

注意:
jarsignerは、オプションで、タイムスタンプを含む署名を生成できます。JARファイルの署名を検証するシステム(Java Plug-inなど)は、タイムスタンプをチェックして、証明書が最新であることを要求するかわりに、証明書が有効な間に署名されたJARファイルを受け入れることができます。証明書は、通常1年で期限切れになります。配備されたJARファイルにJARファイルの作成者が毎年再署名すると考えるのは妥当ではありません。

Java Platform, Standard Editionツール・リファレンスでkeytoolおよびjarsignerを参照してください。

認証

認証とは、ユーザーのアイデンティティを判別するプロセスのことです。Java Runtime Environmentのコンテキストでは、実行中のJavaプログラムのユーザーを識別するプロセスのことです。このプロセスは、Javaの暗号化の項で説明したサービスに依存する場合があります。

Javaプラットフォームは、アプリケーションがプラガブルなログイン・モジュールによってユーザー認証を実行できるようにするAPIを提供します。アプリケーションはLoginContextクラス(javax.security.auth.loginパッケージ内)を呼び出し、このクラスは構成を参照します。構成は、実際の認証を実行するために使用するログイン・モジュール(javax.security.auth.spi.LoginModuleインタフェースの実装)を指定します。

アプリケーションは、標準のLoginContext APIと通信するだけなので、基盤となるプラグイン・モジュールから独立したままの状態を維持できます。アプリケーションでは、新規または更新されたモジュールをプラグインとして使用でき、アプリケーションを変更する必要はありません。次の図では、アプリケーションと基盤となるログイン・モジュールとの間の独立性を示します。

図1-3 認証フレームワークにプラグインする認証ログイン・モジュール

ログイン・モジュールはJavaプラットフォームで構成できるプラガブルなコンポーネントですが、セキュリティ・プロバイダを介してプラグインされません。したがって、セキュリティ・プロバイダの項で説明されているプロバイダ検索モデルには従いません。かわりに、図1-3で示したように、ログイン・モジュールは固有の構成によって管理されます。

Javaプラットフォームは、次の組込みログイン・モジュールを提供します。これらはすべて、com.sun.security.auth.moduleパッケージ内にあります。

- Krb5LoginModule (Kerberosプロトコルを使用した認証用)

- JndiLoginModule (LDAPまたはNISデータベースを使用したユーザー名/パスワード認証用)

- KeyStoreLoginModule (PKCS#11トークン・キーストアなど、任意のタイプのキーストアへのログイン用)

認証は、2つのピア間のセキュアな通信チャネルの確立プロセス中に実現することもできます。Javaプラットフォームは、多数の標準通信プロトコルの実装を提供します。これについては、セキュアな通信の項で説明します。

セキュアな通信

ネットワークを通じてやり取りされるデータには、意図された受信者以外の人もアクセスできます。データにパスワードやクレジット・カード番号などの個人情報が含まれる場合、権限のない者がデータを理解できないよう、手段を講じる必要があります。また、適切な相手にデータを送信し、意図的であるかどうかにかかわらず、通信中にデータが変更されないようにすることも重要です。

暗号化はセキュアな通信に必要な基盤を形成します。Javaの暗号化の項を参照してください。Javaプラットフォームは、多くの標準のセキュアな通信プロトコルにAPIのサポートとプロバイダ実装も提供します。

SSL、TLSおよびDTLSプロトコル

JDKは、SSL、TLSおよびDTLSプロトコルのAPIおよび実装を提供しています。これには、データの暗号化、メッセージの整合性、およびサーバーとクライアントの認証の機能が含まれています。アプリケーションは、SSL/TLS/DTLSを使用して、TCP/IP上のHTTPなど、任意のアプリケーション・プロトコル上の2つのピア間でセキュアなデータのやり取りを実現できます。

javax.net.ssl.SSLSocketクラスは、通常のストリーム・ソケット(java.net.Socket)上でSSL/TLSサポートをカプセル化するネットワーク・ソケットを表します。一部のアプリケーションは、別のデータ・トランスポート抽象化(New-I/Oなど)を使用する場合があります。javax.net.ssl.SSLEngineクラスを使用して、SSL/TLS/DTLSパケットを生成および消費できます。

JDKには、プラガブルな(プロバイダ・ベースの)キー・マネージャおよびトラスト・マネージャの概念をサポートするAPIも含まれています。キー・マネージャはjavax.net.ssl.KeyManagerクラスによってカプセル化され、認証の実行に使用される鍵を管理します。トラスト・マネージャはTrustManagerクラス(同じパッケージ内)によってカプセル化され、管理するキーストア内の証明書に基づいて信頼できる人を決定します。

JDKには、次のSSL/TLS/DTLSプロトコルを実装する組込みプロバイダが含まれています。

- SSLv3

- TLSv1

- TLSv1.1

- TLSv1.2

- DTLSv1.0

- DTLSv1.2

Simple Authentication and Security Layer(SASL)

Simple Authentication and Security Layer (SASL)は、認証およびオプションでクライアント・アプリケーションとサーバー・アプリケーション間のセキュリティ層の確立を行うプロトコルを指定するインターネット標準です。SASLは、認証データの交換方法を定義しますが、そのデータの内容は指定しません。認証データの内容およびセマンティックスを指定する特定の認証メカニズムが適合できるフレームワークです。さまざまなセキュリティ・レベルおよび配備シナリオ用にインターネット・コミュニティによって定義された、多数の標準SASLメカニズムがあります。

Java SASL APIは、java.security.saslモジュール内にあり、SASLメカニズムを使用するアプリケーション用のクラスおよびインタフェースを定義します。これは、メカニズムに依存しないように定義されています。APIを使用するアプリケーションを、特定のSASLメカニズムの使用に固定する必要はありません。アプリケーションは、必要なセキュリティ機能に基づいて、使用するメカニズムを選択できます。このAPIは、クライアントとサーバーの両方のアプリケーションをサポートしています。javax.security.sasl.Saslクラスを使用して、SaslClientおよびSaslServerオブジェクトを作成します。

SASLメカニズムの実装は、プロバイダ・パッケージで提供されます。各プロバイダは、1つ以上のSASLメカニズムをサポートする場合があり、標準プロバイダ・アーキテクチャによって登録されて呼び出されます。

Javaプラットフォームには、次のSASLメカニズムを実装する組込みプロバイダが含まれています。

- CRAM-MD5、DIGEST-MD5、EXTERNAL、GSSAPI、NTLM、およびPLAINクライアント・メカニズム

- CRAM-MD5、DIGEST-MD5、GSSAPI、およびNTLMサーバー・メカニズム

Generic Security Service APIとKerberos

Javaプラットフォームには、Generic Security Service Application Programming Interface (GSS-API)用のJava言語バインディングを持つAPIが含まれています。これは、java.security.jgssモジュール内にあります。GSS-APIにより、アプリケーション・プログラマは、様々な基盤となるセキュリティ・メカニズム上のセキュリティ・サービスに統一されたアクセスができます。Java GSS-APIでは現在Kerberos v5メカニズムを使用する必要があり、Javaプラットフォームにはこのメカニズムの組込み実装が含まれています。現時点では、追加のメカニズムをプラグインすることはできません。

注意:
認証の項で説明されているKrb5LoginModuleを、GSS Kerberosメカニズムとともに使用できます。

Javaプラットフォームには、Simple and Protected GSS-API Negotiation Mechanism (SPNEGO) GSS-APIメカニズムの組込み実装も含まれています。

2つのアプリケーションがGSS-APIを利用して安全にメッセージを交換するには、事前にジョイント・セキュリティ・コンテキストを確立しておく必要があります。コンテキストは、共有状態の情報(暗号化鍵などを含む)をカプセル化します。両方のアプリケーションが、org.ietf.jgss.GSSContextオブジェクトを作成および使用して、セキュリティ・コンテキストを構成する共有情報の確立および保守を行います。セキュリティ・コンテキストが確立されると、安全なメッセージ交換を準備するために使用できます。

Java GSS APIはorg.ietf.jgssパッケージ内にあります。Javaプラットフォームは、KerberosPrincipal、KerberosTicket、KerberosKey、KeyTabなど、基本的なKerberosクラスも定義します。これらはjavax.security.auth.kerberosパッケージ内にあります。

アクセス制御

Javaプラットフォームのアクセス制御アーキテクチャは、機密リソース(ローカル・ファイルなど)または機密アプリケーション・コード(クラス内のメソッドなど)へのアクセスを保護します。すべてのアクセス制御の決定は、java.lang.SecurityManagerクラスによって表されるセキュリティ・マネージャが仲介します。アクセス制御チェックを有効にするには、SecurityManagerをJava Runtimeにインストールする必要があります。

SecurityManagerがインストールされていると、JavaアプレットおよびJava Web Startアプリケーションは自動的に実行されます。ただし、javaコマンドによって実行されるローカル・アプリケーションは、SecurityManagerがインストールされていてもデフォルトでは実行されません。ローカル・アプリケーションをSecurityManagerで実行するには、アプリケーション自体がsetSecurityManagerメソッド(java.lang.Systemクラス内)によってプログラムでそれを設定するか、またはコマンド行で-Djava.security.manager引数を使用してjavaを呼び出す必要があります。

アクセス権

アクセス権は、システム・リソースへのアクセスを表します。アプレット(またはセキュリティ・マネージャとともに実行するアプリケーション)が、リソースへのアクセスを許可されるためには、アクセスを試みるコードに対し、該当するアクセス権を明示的に与える必要があります。

Javaコードがクラス・ローダーによってJava Runtimeにロードされると、クラス・ローダーは次の情報をそのコードに自動的に関連付けます。

- コードのロード元の場所

- コードに署名した人(存在する場合)

- コードに付与されたデフォルトのアクセス権

この情報は、コードが信頼されないネットワークからダウンロードされたか(アプレットなど)、ファイル・システムからロードされたか(ローカル・アプリケーションなど)に関係なく、コードに関連付けられます。コードのロード元の場所はURLで表され、コード署名者は署名者の証明書チェーンによって表され、デフォルトのアクセス権はjava.security.Permissionオブジェクトによって表されます。

ダウンロードされたコードに自動的に付与されるデフォルトのアクセス権には、コードの出所であるホストへのネットワーク接続を行う機能が含まれています。ローカル・ファイル・システムからロードされたコードに自動的に付与されるデフォルトのアクセス権には、コードの出所であるディレクトリおよびそのディレクトリのサブディレクトリからファイルを読み取る機能が含まれています。

コードを実行しているユーザーのアイデンティティは、クラス・ローディング時に利用できません。アプリケーション・コードで、必要に応じてエンド・ユーザーを認証する必要があります(認証の項を参照)。ユーザーが認証されると、javax.security.auth.SubjectクラスのdoAsメソッドを呼び出すことによって、アプリケーションはそのユーザーを実行コードに動的に関連付けることができます。

セキュリティ・ポリシー

クラスローダによって、限られた一連のデフォルト・アクセス権がコードに付与されます。管理者は、セキュリティ・ポリシーによって、追加のコード・アクセス権を柔軟に管理できます。

Java SEは、セキュリティ・ポリシーの概念をjava.security.Policyクラスにカプセル化します。Java RuntimeにインストールされるPolicyオブジェクトはいつでも1つのみです。Policyオブジェクトの基本的な役割は、保護されたリソースへのアクセスがコード(ロード元の場所、署名者、および実行者によって特徴付けられる)に許可されるかどうかを決定することです。Policyオブジェクトがこの決定を行う方法は、実装によって異なります。たとえば、承認データを含むデータベースに問い合わせたり、別のサービスに問い合わせたりする場合があります。

Java SEには、セキュリティ・プロパティ・ファイル内に構成された1つ以上のASCII (UTF-8)ファイルから承認データを読み込む、デフォルトのPolicy実装が含まれています。これらのポリシー・ファイルには、コードに付与されるアクセス権の厳密なセットが含まれています。具体的には、特定の場所からロードされ、特定のエンティティによって署名され、特定のユーザーとして実行されるコードに付与されるアクセス権の厳密なセットです。各ファイル内のポリシー・エントリは、ドキュメント化された独自の構文に準拠する必要があり、単純なテキスト・エディタまたはグラフィカルなpolicytoolユーティリティを使用して作成できます。

注意:
policytoolは推奨されておらず、次のJDKメジャー・リリースでの削除対象としてマークされています。

アクセス制御の実施

Java Runtimeは、プログラムの実行時に行われた一連のJava呼出しを追跡します。保護されたリソースへのアクセスが要求された場合、デフォルトでは、要求されたアクセスを許可するかどうかを決定するために呼出しスタック全体が評価されます。

前述したように、リソースはSecurityManagerによって保護されます。JDK内およびアプリケーション内の、セキュリティの影響を受けるコードは、次のようなコードでリソースへのアクセスを保護します。

SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(perm);
}

Permissionオブジェクトpermは、要求されたアクセスに対応します。たとえば、ファイル/tmp/abcを読み取ろうとすると、アクセス権は次のように構成される場合があります。

Permission perm = new java.io.FilePermission("/tmp/abc", "read");

SecurityManagerのデフォルトの実装は、その決定をjava.security.AccessController実装に委譲します。AccessControllerは、呼出しスタックをトラバースして、スタック内の各コード要素を、要求されたアクセス権(たとえば、前述の例ではFilePermission)とともに、インストールされているセキュリティPolicyに渡します。Policyは、管理者によって構成されたアクセス権に基づいて、要求されたアクセスを付与するかどうかを決定します。アクセスが付与されない場合、AccessControllerはjava.lang.SecurityException.をスローします。

図1-4は、アクセス制御の適用を示しています。この特定の例では、最初に2つの要素ClassAおよびClassBが呼出しスタックにあります。ClassAは、ClassB内のメソッドを呼び出します。これは次に、java.io.FileInputStreamのインスタンスを作成することで、ファイル/tmp/abcへのアクセスを試みます。FileInputStreamコンストラクタは、FilePermission、permを作成してから、前述のように、permをSecurityManagerクラスのcheckPermissionメソッドに渡します。この特定の例では、ClassAおよびClassBのアクセス権のみをチェックする必要があります。これは、FileInputStream、SecurityManagerおよびAccessControllerなど、java.baseモジュール内のすべてのクラスは、すべてのアクセス権を自動的に受け取るためです。

この例では、ClassAとClassBには異なるコード特性があります(出所および署名者が異なります)。それぞれに異なるアクセス権が付与されている場合があります。要求されたFilePermissionが両方のクラスに付与されていることをPolicyが示した場合にのみ、AccessControllerは要求されたファイルへのアクセスを付与します。

図1-4 リソースへのアクセスの制御

XML署名

Java XMLデジタル署名APIは、XML署名を生成および検証するための標準のJava APIです。

XML署名は、どのタイプ(XMLまたはバイナリ)のデータにも適用できます(XML署名の構文と処理を参照)。結果の署名は、XMLで表されます。XML署名は、データを保護するために使用でき、データの整合性、メッセージ認証、および署名者認証を提供します。

APIは、「XML-Signature Syntax and Processing」でのW3C勧告の必須機能または推奨機能をすべてサポートするように設計されています。APIは拡張可能およびプラガブルであり、Java暗号化サービス・プロバイダ・アーキテクチャに基づいてます。

Java XMLデジタル署名APIは、java.xml.cryptoモジュール内にあり、次の6つのパッケージで構成されます。

- javax.xml.crypto

- javax.xml.crypto.dsig

- javax.xml.crypto.dsig.keyinfo

- javax.xml.crypto.dsig.spec

- javax.xml.crypto.dom

- javax.xml.crypto.dsig.dom

Javaセキュリティに関する追加情報

その他のJavaセキュリティ・ドキュメントは、Java SEセキュリティにあります。

注意:歴史的に、新しいタイプのセキュリティ・サービスがJava SEに追加されると(最初は拡張機能としての場合もある)、それらを表すために様々な略語が使用されました。これらの略語は、現在もJavaセキュリティ・ドキュメント内で使用されています。次に、それらが何を表すかを説明します。

- JSSE (Java Secure Socket Extension)とは、SSL、TLSおよびDTLSプロトコルの項で示されているように、SSL関連のサービスを表します。

- JCE (Java Cryptography Extension)とは、Javaの暗号化の項で示されているように、暗号化サービスを表します。

- JAAS (Java Authentication and Authorization Service)とは、認証とアクセス制御の項でそれぞれ示されているように、認証およびユーザーベースのアクセス制御のサービスを表します。

Javaセキュリティ・クラスのサマリー

次の表では、Javaセキュリティのクラスおよびインタフェースの名前、パッケージおよび使用方法をいくつか示します。

表1-3 Javaセキュリティのパッケージおよびクラス

パッケージ

クラス名またはインタフェース名

使用方法

モジュール

java.lang

SecurityException

セキュリティ違反を示します

java.base

java.lang

SecurityManager

すべてのアクセス制御の決定を仲介します

java.base

java.lang

System

SecurityManagerをインストールします

java.base

java.security

AccessController

アクセス制御の決定を行うために、SecurityManagerのデフォルトの実装によって呼び出されます

java.base

java.security

DomainLoadStoreParameter

ドメイン・キーストア(DKS)のパラメータを保存します

java.base

java.security

Key

暗号化鍵を表します

java.base

java.security

KeyStore

鍵および信頼できる証明書のリポジトリを表します

java.base

java.security

MessageDigest

メッセージ・ダイジェストを表します

java.base

java.security

Permission

特定のリソースへのアクセスを表します

java.base

java.security

PKCS12Attribute

PKCS12キーストアの属性をサポートします

java.base

java.security

Policy

セキュリティ・ポリシーをカプセル化します

java.base

java.security

Provider

セキュリティ・サービスの実装をカプセル化します

java.base

java.security

Security

セキュリティ・プロバイダおよびセキュリティ・プロパティを管理します

java.base

java.security

Signature

デジタル署名を作成および検証します

java.base

java.security.cert

Certificate

公開鍵証明書を表します

java.base

java.security.cert

CertStore

関連性がなく通常は信頼されない証明書のリポジトリを表します

java.base

java.security.cert

CRL

CRLを表します

java.base

javax.crypto

Cipher

暗号化および復号化を実行します

java.base

javax.crypto

KeyAgreement

鍵交換を実行します

java.base

javax.net.ssl

KeyManager

SSL/TLS認証を実行するために使用される鍵を管理します

java.base

javax.net.ssl

SSLEngine

アプリケーションがトランスポート・メカニズムを自由に選択できるように、SSL/TLSパケットを生成および消費します

java.base

javax.net.ssl

SSLSocket

通常のストリーム・ソケット上でSSL/TLSサポートをカプセル化するネットワーク・ケットを表します

java.base

javax.net.ssl

TrustManager

SSL/TLSインタラクションで信頼できる人に関する決定を行います(たとえば、キーストア内の信頼できる証明書に基づく)

java.base

javax.security.auth

Subject

ユーザーを表します

java.base

javax.security.auth.kerberos

KerberosPrincipal

Kerberosプリンシパルを表します

java.base

javax.security.auth.kerberos

KerberosTicket

Kerberosチケットを表します

java.base

javax.security.auth.kerberos

KerberosKey

Kerberos鍵を表します

java.base

javax.security.auth.kerberos

KerberosTab

Kerberosキータブ・ファイルを表します

java.base

javax.security.auth.login

LoginContext

プラガブルな認証をサポートします

java.base

javax.security.auth.spi

LoginModule

特定の認証メカニズムを実装します

java.base

javax.security.sasl

Sasl

SaslClientおよびSaslServerオブジェクトを作成します

java.security.sasl

javax.security.sasl

SaslClient

SASL認証をクライアントとして実行します

java.security.sasl

javax.security.sasl

SaslServer

SASL認証をサーバーとして実行します

java.security.sasl

org.ietf.jgss

GSSContext

GSS-APIセキュリティ・コンテキストをカプセル化し、コンテキストによって使用可能なセキュリティ・サービスを提供します

java.security.jgss

com.sun.security.auth.module

JndiLoginModule

LDAPまたはNISを使用して、ユーザー名/パスワード認証を実行します

jdk.security.auth

com.sun.security.auth.module

KeyStoreLoginModule

キーストア・ログインに基づいて認証を実行します

jdk.security.auth

com.sun.security.auth.module

Krb5LoginModule

Kerberosプロトコルを使用して認証を実行します

jdk.security.auth

削除対象としてマークされている推奨されていないセキュリティAPI

次のAPIは推奨されておらず、将来のリリースでの削除対象となっています。

jdeprscanツールを使用してこのAPIの依存性を確認できます。Java Platform, Standard Editionツール・リファレンスでjdeprscanを参照してください。

次のクラスは推奨されておらず、削除対象としてマークされています。

- com.sun.security.auth.PolicyFile

- com.sun.security.auth.SolarisNumericGroupPrincipal

- com.sun.security.auth.SolarisNumericUserPrincipal

- com.sun.security.auth.SolarisPrincipal

- com.sun.security.auth.X500Principal

- com.sun.security.auth.module.SolarisLoginModule

- com.sun.security.auth.module.SolarisSystem

次のメソッドは推奨されておらず、削除対象としてマークされています。

- java.lang.SecurityManager.getInCheck

- java.lang.SecurityManager.checkMemberAccess

- java.lang.SecurityManager.classDepth

- java.lang.SecurityManager.currentClassLoader

- java.lang.SecurityManager.currentLoadedClass

- java.lang.SecurityManager.inClass

- java.lang.SecurityManager.inClassLoader

- java.lang.SecurityManager.checkAwtEventQueueAccess

- java.lang.SecurityManager.checkTopLevelWindow

- java.lang.SecurityManager.checkSystemClipboardAccess

次のフィールドは推奨されておらず、削除対象としてマークされています。

- java.lang.SecurityManager.incheck

セキュリティ・ツールのサマリー

次の表では、JavaセキュリティおよびKerberos関連のツールを示します。

Java Platform, Standard Editionツール・リファレンスでセキュリティのツールおよびコマンドを参照してください。

表1-4 Javaセキュリティのツール

ツール

使用方法

jar

Java Archive (JAR)ファイルを作成します

jarsigner

JARファイルの署名および署名の検証を行います

keytool

キーストアを作成および管理します

policytool

デフォルトのPolicyの実装とともに使用するポリシー・ファイルを作成および編集します

注意:
policytoolは推奨されておらず、削除対象としてマークされています。

Windows用のJDKに同梱される3つのKerberos関連ツールもあります。同等の機能が、自動的にSolarisおよびLinuxオペレーティング環境の一部になる同じ名前のツールで提供されています。

表1-5 Kerberos関連のツール

ツール

使用方法

kinit

Kerberosチケット認可チケットを取得およびキャッシュします

klist

ローカルのKerberos資格キャッシュおよびキー表のエントリを一覧します

ktab

ローカルのKerberosキー表に格納された名前およびサービス鍵を管理します

組込みのプロバイダ

OracleのJava SE実装には、多数の組込みプロバイダ・パッケージが含まれています。JDKプロバイダ・ドキュメントを参照してください。

セキュリティ・アーキテクチャ

主要なセキュリティ機能の目的の概要、セキュリティ・クラスとそれらの使用方法の概要、セキュリティ・アーキテクチャがコードに与える影響についての考察、およびセキュリティの影響を受けるコードの記述についての見解は、JDK 8ドキュメント内のセキュリティ・アーキテクチャを参照してください。

標準アルゴリズム名

Java SEで必要となり使用されるアルゴリズム、証明書およびキーストア・タイプの一連の標準名については、Javaセキュリティ標準アルゴリズム名仕様を参照してください。

Java Development Kit (JDK)でのアクセス権

組込みのJDKアクセス権タイプについてと、各アクセス権の付与の関連リスクについて情報を示します。正常実行のためにアクセス権を有効にする必要があるメソッドについての情報、およびメソッドごとの必要なアクセス権のリストです。

アクセス権は、システム・リソースへのアクセスを表します。アプレット(またはセキュリティ・マネージャとともに実行するアプリケーション)が、リソースへのアクセスを許可されるためには、アクセスを試みるコードに対し、該当するアクセス権を明示的に与える必要があります。

一般に、アクセス権には名前(「ターゲット名」とも呼ばれる)があり、場合によっては、カンマで区切られた1つ以上のアクションのリストを持つこともあります。

たとえば、次のコードでは、/tmpディレクトリのabcという名前のファイルへの読取りアクセスを表すFilePermissionオブジェクトを作成します。

perm = new java.io.FilePermission("/tmp/abc", "read");

ここでは、ターゲット名が/tmp/abcで、アクション文字列がreadになります。

重要:
前述の文によって、アクセス権オブジェクトが作成されます。アクセス権オブジェクトは、システム・リソースを表しますが、システム・リソースへのアクセスを許可することはありません。アクセス権オブジェクトは、実効性のあるポリシーに基づいて作成され、コードへ割り当てられ(「付与」され)ます。アクセス権オブジェクトが、あるコードに割り当てられると、そのコードはアクセス権オブジェクトで指定されたシステム・リソースへ、指定された方法でアクセスするアクセス権が付与されます。現在のセキュリティ・マネージャが、アクセス判断を行う際に、アクセス権オブジェクトを作成することもあります。この場合、ターゲットのアクセス権オブジェクトは、要求されたアクセスに基づいて作成されます。さらに、ターゲットのアクセス権オブジェクトは、要求を行うコードに付与され、かつ要求を行うコードが保持する、アクセス権オブジェクトに照らしてチェックされます。

Javaアプリケーション環境のポリシーは、Policyオブジェクトにより表現されます。"JavaPolicy" Policy実装では、1つまたは複数のポリシー構成ファイルからポリシーを指定できます。ポリシー・ファイルは、指定したコード・ソースのコードにどのアクセス権を許可するかを指定します。たとえば、/home/sysadminディレクトリのコードに対して/tmp/abcファイルへの読取りアクセスを許可する場合、ポリシー・ファイルのエントリは次のようになります。

grant codeBase "file:/home/sysadmin/" {
permission java.io.FilePermission "/tmp/abc", "read";
};

ポリシー・ファイルの場所、およびポリシー・ファイルにより付与するアクセス権の詳細は、デフォルトのPolicyの実装とポリシー・ファイルの構文を参照してください。

ポリシー・ツールを使用すると、入力や、ポリシー・ファイルの構文規則を記憶する手間を減らすことができます。ポリシー・ツールの使用によるアクセス権の指定の詳細は、ポリシー・ツールを参照してください。ポリシー・ツールを使用すると、入力や、ポリシー・ファイルの構文規則を記憶する手間を減らすことができます。

技術的な観点から言うと、リソースへのアクセスを試みる場合は、実行スレッド上のコードに「特権設定」のマークが付けられていないかぎり、実行スレッドが利用するコードはすべて、そのリソースへのアクセス権を保持する必要があります。特権ブロックのためのAPIを参照してください。

アクセス権の説明とリスク

組込みのJDKアクセス権タイプ、および各アクセス権の付与のリスクのリストを示します。

- AWTPermission

- FilePermission

- SerializablePermission

- ManagementPermission

- ReflectPermission

- RuntimePermission

- NIO-Related Targets

- NetPermission

- SocketPermission

- LinkPermission

- URLPermission

- AllPermission

- SecurityPermission

- UnresolvedPermission

- SQLPermission

- LoggingPermission

- PropertyPermission

- MBeanPermission

- MBeanServerPermission

- MBeanTrustPermission

- SubjectDelegationPermission

- SSLPermission

- AuthPermission

- DelegationPermission

- ServicePermission

- PrivateCredentialPermission

- AudioPermission

- JAXBPermission

- WebServicePermission

- メソッドおよび必要なアクセス権

- java.lang.SecurityManagerメソッドのアクセス権チェック

NIO関連のターゲット

NIO関連のターゲット名です。

JavaSE JDKのリリース1.4では、次の2つのNIOに関するRuntimePermissionターゲットが追加されました。

selectorProvider
charsetProvider

これらのRuntimePermissionは、java.nio.channel.spi.SelectorProviderまたはjava.nio.charset.spi.CharsetProviderをサブクラス化して実装するクラスに付与する必要があります。アクセス権は、抽象基底クラス・コンストラクタの呼出し時にチェックされます。これらのアクセス権は、重要なプロバイダ・メカニズムを実装するクラスの信頼性を保証します。詳細は、java.nio.channels.spi.SelectorProviderjava.nio.channels.spi.CharsetProviderを参照してください。

メソッドおよび必要なアクセス権

アクセス権が必要なすべてのメソッドと、各メソッドが呼び出す対応するSecurityManagerメソッドのリストを示します。

注意:
このドキュメントで示されるすべてのメソッドのリストは完全なものではなく、アクセス権が必要な一部のメソッドは含まれていません。SecurityExceptionをスローするメソッドと必要なアクセス権の追加情報は、APIドキュメントを参照してください。

SecurityManagerメソッドのデフォルトの実装では、SecurityManagerメソッド列の対応するエントリで示されているアクセス権が現在有効なポリシーで与えられている場合だけ、メソッド列で示したメソッドを呼び出すことができます。

例1-1 SecurityManager checkPermissionメソッド

java.awt.ToolkitクラスのgetSystemEventQueueメソッドは、結果的にSecurityManagerメソッドcheckPermissionを呼び出します。これは、呼出しスタック上のコードに次のアクセス権が与えられている場合にのみ成功します。

java.awt.AWTPermission "accessEventQueue";

メソッド

SecurityManagerメソッド

アクセス権

java.awt.Toolkit
getSystemEventQueue();

checkPermission

java.awt.AWTPermission "accessEventQueue";

次の表記規則は、アクセス権名中の文字列{foo}はfooの実行時の値で置き換えられるという意味です。

メソッド

SecurityManagerメソッド

アクセス権

some.package.class
public static void someMethod(String foo);

checkXXX

SomePermission "{foo}";

例1-2 SecurityManager checkReadメソッド

java.io.FileInputStream クラスのFileInputStreamメソッドは、結果的にSecurityManagerメソッドcheckReadを呼び出します。これは、呼出しスタック上のコードに次のアクセス権が与えられている場合にのみ成功します。

メソッド

SecurityManagerメソッド

アクセス権

java.io.FileInputStream
FileInputStream(String name)

checkRead(String)

java.io.FilePermission "{name}", "read";

FileInputStreamメソッド(この場合はコンストラクタ)を、次のように引数nameに/test/MyTestFileを指定して呼び出したとします。

FileInputStream("/test/MyTestFile");

この呼出しは、現在のポリシーで次のアクセス権が設定され、/test/MyTestFileファイルへの読取りアクセスが許可されていないと行えません。

java.io.FilePermission "/test/MyTestFile", "read";

より正確には、アクセス権は、このように明示的に設定されているか、次のように、別のアクセス権により暗黙に設定されている必要があります。

java.io.FilePermission "/test/*", "read";

「/test」ディレクトリに含まれる任意のファイルへの読取りアクセスが許可されています。

例1-3 SecurityManager checkAcceptメソッド

中カッコで囲まれた項目が、特定のメソッド引数の名前と同一ではなく、関係のある値を表す場合もあります。

メソッド

SecurityManagerメソッド

アクセス権

java.net.DatagramSocket
public synchronized void
receive(DatagramPacket p);

checkAccept({host}, {port})

java.net.SocketPermission "{host}:{port}", "accept";

ここでは、適切なホストおよびポートの値がreceiveメソッドにより計算されて、checkAcceptに渡されます。

ほとんどの場合、呼び出されるSecurityManagerメソッドの名前のみが一覧表示されます。メソッドは、複数ある同名のメソッドのいずれかであり、引数の型も一覧表示されます。たとえば、checkRead(String)やcheckRead(FileDescriptor)などがあります。引数が関係するその他の場合にも、引数は一覧表示されます。

メソッドおよびそのアクセス権

次の表は、パッケージ名順になっています。最初にjava.awtパッケージ内のクラスのメソッド、次にjava.ioパッケージ内のクラスのメソッド、という順番です。

表1-6 メソッドおよびそのアクセス権

メソッド

SecurityManagerメソッド

アクセス権

java.awt.Graphics2d
public abstract void
setComposite(Composite comp)

checkPermission

このGraphics2Dコンテキストが表示画面上のComponentに描画中であり、かつCompositeがAlphaCompositeクラスのインスタンスではなく、カスタム・オブジェクトの場合、java.awt.AWTPermission "readDisplayPixels"。注: setCompositeメソッドは、実際には抽象であるため、セキュリティ・チェックを呼び出すことはできない。こうした条件下では、メソッドの実際の実装ごとに、java.awt.AWTPermission("readDisplayPixels")アクセス権でjava.lang.SecurityManager checkPermissionメソッドを呼び出すべきである。

java.awt.Robot
public Robot()
public Robot(GraphicsDevice screen)

checkPermission

java.awt.AWTPermission "createRobot"

java.awt.Toolkit
public void addAWTEventListener(
AWTEventListener listener,
long eventMask)
public void removeAWTEventListener(
AWTEventListener listener)

checkPermission

java.awt.AWTPermission "listenToAllAWTEvents"

java.awt.Toolkit
public abstract PrintJob getPrintJob(
Frame frame, String jobtitle,
Properties props)

checkPrintJobAccess

java.lang.RuntimePermission "queuePrintJob"

注: getPrintJobメソッドは、実際には抽象であるため、セキュリティ・チェックを呼び出すことはできない。このメソッドの実際の各実装部分では、java.lang.SecurityManagerのcheckPrintJobAccessメソッドを呼び出すべきであり、これは、アクセス権java.lang.RuntimePermission "queuePrintJob"が現在許可されている場合にのみ成功する。

java.awt.Toolkit
public abstract Clipboard
getSystemClipboard()

checkPermission

java.awt.AWTPermission "accessClipboard"

注: getSystemClipboardメソッドは、実際には抽象であるため、セキュリティ・チェックを呼び出すことはできない。このメソッドの実際の各実装部分では、checkPermissionメソッドを呼び出すべきであり、これは、アクセス権java.awt.AWTPermission "accessClipboard"が現在許可されている場合にのみ成功する。

java.awt.Toolkit
public final EventQueue
getSystemEventQueue()

checkPermission

java.awt.AWTPermission "accessEventQueue"

java.awt.Window
Window()

checkPermission

java.awt.AWTPermission "showWindowWithoutWarningBanner"が設定されていると、ウィンドウが表示されるときに、そのウィンドウがアプレットによって作成されていることを警告するバナーは表示されない。設定されていない場合は、バナーが表示される。

java.beans.Beans
public static void setDesignTime(
boolean isDesignTime)
public static void setGuiAvailable(
boolean isGuiAvailable)

java.beans.Introspector
public static synchronized void
setBeanInfoSearchPath(String path[])

java.beans.PropertyEditorManager
public static void registerEditor(
Class targetType,
Class editorClass)
public static synchronized void
setEditorSearchPath(String path[])

checkPropertiesAccess

java.util.PropertyPermission "*", "read,write"

java.io.File
public boolean delete()
public void deleteOnExit()

checkDelete(String)

java.io.FilePermission "{name}", "delete"

java.io.FileInputStream
FileInputStream(FileDescriptor fdObj)

checkRead(FileDescriptor)

java.lang.RuntimePermission "readFileDescriptor"

java.io.FileInputStream
FileInputStream(String name)
FileInputStream(File file)

java.io.File
public boolean exists()
public boolean canRead()
public boolean isFile()
public boolean isDirectory()
public boolean isHidden()
public long lastModified()
public long length()
public String[] list()
public String[] list(
FilenameFilter filter)
public File[] listFiles()
public File[] listFiles(
FilenameFilter filter)
public File[] listFiles(
FileFilter filter)

java.io.RandomAccessFile
RandomAccessFile(String name, String mode)
RandomAccessFile(File file, String mode)
(where mode is "r" in both of these)

checkRead(String)

java.io.FilePermission "{name}", "read"

java.io.FileOutputStream
FileOutputStream(FileDescriptor fdObj)

checkWrite(FileDescriptor)

java.lang.RuntimePermission "writeFileDescriptor"

java.io.FileOutputStream
FileOutputStream(File file)
FileOutputStream(String name)
FileOutputStream(String name,
boolean append)

java.io.File
public boolean canWrite()
public boolean createNewFile()
public static File createTempFile(
String prefix, String suffix)
public static File createTempFile(
String prefix,  String suffix,
File directory)
public boolean mkdir()
public boolean mkdirs()
public boolean renameTo(File dest)
public boolean setLastModified(long time)
public boolean setReadOnly()

checkWrite(String)

java.io.FilePermission "{name}", "write"

java.io.ObjectInputStream
protected final boolean
enableResolveObject(boolean enable);

java.io.ObjectOutputStream
protected final boolean
enableReplaceObject(boolean enable)

checkPermission

java.io.SerializablePermission "enableSubstitution"

java.io.ObjectInputStream
protected ObjectInputStream()

java.io.ObjectOutputStream
protected ObjectOutputStream()

checkPermission

java.io.SerializablePermission "enableSubclassImplementation"

java.io.RandomAccessFile
RandomAccessFile(String name, String mode)
(where mode is "rw")

checkRead(String) and checkWrite(String)

java.io.FilePermission "{name}", "read,write"

java.lang.Class
public static Class forName(
String name, boolean initialize,
ClassLoader loader)

checkPermission

loaderがnullで、呼出し側のクラス・ローダーはnullでない場合、java.lang.RuntimePermission("getClassLoader")になる。

java.lang.Class
public ClassLoader getClassLoader()

checkPermission

呼出し側のクラス・ローダーがnullの場合、あるいは呼出し側のクラス・ローダーが、クラス・ローダーが要求されているクラスのクラス・ローダーと同じかその上位クラスの場合は、アクセス権は必要ない。それ以外の場合は、java.lang.RuntimePermission "getClassLoader"が必要。

java.lang.Class
public Class[] getDeclaredClasses()
public Field[] getDeclaredFields()
public Method[] getDeclaredMethods()
public Constructor[]
getDeclaredConstructors()
public Field getDeclaredField(
String name)
public Method getDeclaredMethod(...)
public Constructor
getDeclaredConstructor(...)

checkMemberAccess(this, Member.DECLARED)、およびこのクラスがパッケージ内にある場合はcheckPackageAccess({pkgName})

「この」クラスのクラス・ローダーが呼出し側のクラス・ローダーと同一である場合は、デフォルトのcheckMemberAccessはどのようなアクセス権も必要としない。同一でない場合は、java.lang.RuntimePermission "accessDeclaredMembers"が必要。このクラスがパッケージ内にある場合、java.lang.RuntimePermission "accessClassInPackage.{pkgName}"も必要。

java.lang.Class
public Class[] getClasses()
public Field[] getFields()
public Method[] getMethods()
public Constructor[] getConstructors()
public Field getField(String name)
public Method getMethod(...)
public Constructor getConstructor(...)

checkMemberAccess(this, Member.PUBLIC)、およびこのクラスがパッケージ内にある場合はcheckPackageAccess({pkgName})

アクセス・タイプがMember.PUBLICの場合、デフォルトのcheckMemberAccessはどのようなアクセス権も必要としない。このクラスがパッケージ内にある場合、java.lang.RuntimePermission "accessClassInPackage.{pkgName}"が必要。

java.lang.Class
public ProtectionDomain
getProtectionDomain()

checkPermission

java.lang.RuntimePermission "getProtectionDomain"

java.lang.ClassLoader
ClassLoader()
ClassLoader(ClassLoader parent)

checkCreateClassLoader

java.lang.RuntimePermission "createClassLoader"

java.lang.ClassLoader
public static ClassLoader
getSystemClassLoader()
public ClassLoader getParent()

checkPermission

呼出し側のクラス・ローダーがnullの場合、あるいは呼出し側のクラス・ローダーが、クラス・ローダーが要求されているクラスのクラス・ローダーと同じかその上位クラスの場合は、アクセス権は必要ない。それ以外の場合は、java.lang.RuntimePermission "getClassLoader"が必要。

java.lang.Runtime
public Process exec(String command)
public Process exec(String command,
String envp[])
public Process exec(String cmdarray[])
public Process exec(String cmdarray[],
String envp[])

checkExec

java.io.FilePermission "{command}", "execute"

java.lang.Runtime
public void exit(int status)
public static void
runFinalizersOnExit(boolean value)
java.lang.System
public static void exit(int status)
public static void
runFinalizersOnExit(boolean value)

checkExit(status)。このとき、runFinalizersOnExitに対してstatusは0

java.lang.RuntimePermission "exitVM.{status}"

java.lang.Runtime
public void addShutdownHook(Thread hook)
public boolean removeShutdownHook(Thread hook)

checkPermission

java.lang.RuntimePermission "shutdownHooks"

java.lang.Runtime
public void load(String lib)
public void loadLibrary(String lib)
java.lang.System
public static void load(String filename)
public static void loadLibrary(
String libname)

checkLink({libName})ただし、{libName}はlib、filenameまたはlibname引数

java.lang.RuntimePermission "loadLibrary.{libName}"

java.lang.SecurityManager methods

checkPermission

表1-7を参照してください。

java.lang.System
public static Properties
getProperties()
public static void
setProperties(Properties props)

checkPropertiesAccess

java.util.PropertyPermission "*", "read,write"

java.lang.System
public static String
getProperty(String key)
public static String
getProperty(String key, String def)

checkPropertyAccess

java.util.PropertyPermission "{key}", "read"

java.lang.System
public static void setIn(InputStream in)
public static void setOut(PrintStream out)
public static void setErr(PrintStream err)

checkPermission

java.lang.RuntimePermission "setIO"

java.lang.System
public static String
setProperty(String key, String value)

checkPermission

java.util.PropertyPermission "{key}", "write"

java.lang.System
public static synchronized void
setSecurityManager(SecurityManager s)

checkPermission

java.lang.RuntimePermission "setSecurityManager"

java.lang.Thread
public ClassLoader getContextClassLoader()

checkPermission

呼出し側のクラス・ローダーがnullの場合、あるいは呼出し側のクラス・ローダーが、コンテキスト・クラス・ローダーが要求されているスレッドのコンテキスト・クラス・ローダーと同じかその上位クラスの場合は、アクセス権は必要ない。それ以外の場合は、java.lang.RuntimePermission "getClassLoader"が必要。

java.lang.Thread
public void setContextClassLoader
(ClassLoader cl)

checkPermission

java.lang.RuntimePermission "setContextClassLoader"

java.lang.Thread
public final void checkAccess()
public void interrupt()
public final void suspend()
public final void resume()
public final void setPriority
(int newPriority)
public final void setName(String name)
public final void setDaemon(boolean on)

checkAccess(this)

java.lang.RuntimePermission "modifyThread"

java.lang.Thread
public static int
enumerate(Thread tarray[])

checkAccess({threadGroup})

java.lang.RuntimePermission "modifyThreadGroup"

java.lang.Thread
public final void stop()

checkAccess(this)。現在のスレッドが自分以外のスレッドを停止させようとしている場合は、checkPermissionも呼び出される。

java.lang.RuntimePermission "modifyThread"。
現在のスレッドが自分以外のスレッドを停止させようとしている場合、java.lang.RuntimePermission "stopThread"も必要。

java.lang.Thread
public final synchronized void
stop(Throwable obj)

checkAccess(this)。現在のスレッドが自分以外のスレッドを停止させようとしている場合、またはobjがThreadDeathのインスタンスでない場合は、checkPermissionも呼び出される。

java.lang.RuntimePermission "modifyThread"。
現在のスレッドが自分以外のスレッドを停止させようとしている場合、またはobjがThreadDeathのインスタンスではない場合は、java.lang.RuntimePermission "stopThread"も必要。

java.lang.Thread
Thread()
Thread(Runnable target)
Thread(String name)
Thread(Runnable target, String name)

java.lang.ThreadGroup
ThreadGroup(String name)
ThreadGroup(ThreadGroup parent,
String name)

checkAccess({parentThreadGroup})

java.lang.RuntimePermission "modifyThreadGroup"

java.lang.Thread
Thread(ThreadGroup group, ...)

java.lang.ThreadGroup
public final void checkAccess()
public int enumerate(Thread list[])
public int enumerate(Thread list[],
boolean recurse)
public int enumerate(ThreadGroup list[])
public int enumerate(ThreadGroup list[],
boolean recurse)
public final ThreadGroup getParent()
public final void
setDaemon(boolean daemon)
public final void setMaxPriority(int pri)
public final void suspend()
public final void resume()
public final void destroy()

ThreadGroupメソッドの場合はcheckAccess(this)、Threadメソッドの場合はcheckAccess(group)

java.lang.RuntimePermission "modifyThreadGroup"

java.lang.ThreadGroup
public final void interrupt()

checkAccess(this)

java.lang.RuntimePermission "modifyThreadGroup"が必要。スレッド・グループ内とそのすべてのサブグループ内のスレッドごとにjava.lang.Thread interrupt()メソッドが呼び出されるため、java.lang.RuntimePermission "modifyThread"も必要。Thread interrupt()メソッドを参照。

java.lang.ThreadGroup
public final void stop()

checkAccess(this)

java.lang.RuntimePermission "modifyThreadGroup"が必要。java.lang.Threadのstop()メソッドは、そのスレッド・グループ内およびそのすべてのサブグループ内のスレッドごとに呼び出されるので、java.lang.RuntimePermission "modifyThread"および場合によってはjava.lang.RuntimePermission "stopThread"も必要。Threadのstop()メソッドを参照。

java.lang.reflect.AccessibleObject
public static void setAccessible(...)
public void setAccessible(...)

checkPermission

java.lang.reflect.ReflectPermission "suppressAccessChecks"

java.net.Authenticator
public static PasswordAuthentication
requestPasswordAuthentication(
InetAddress addr,
int port,
String protocol,
String prompt,
String scheme)

checkPermission

java.net.NetPermission "requestPasswordAuthentication"

java.net.Authenticator
public static void
setDefault(Authenticator a)

checkPermission

java.net.NetPermission "setDefaultAuthenticator"

java.net.MulticastSocket
public void
joinGroup(InetAddress mcastaddr)
public void
leaveGroup(InetAddress mcastaddr)

checkMulticast(InetAddress)

java.net.SocketPermission( mcastaddr.getHostAddress(), "accept,connect")

java.net.DatagramSocket
public void send(DatagramPacket p)

checkMulticast(p.getAddress())または
checkConnect( p.getAddress().getHostAddress(), p.getPort())

if (p.getAddress().isMulticastAddress())
{ java.net.SocketPermission( (p.getAddress()).getHostAddress(), "accept,connect") }
else {port = p.getPort();
host = p.getAddress().getHostAddress();
if (port == -1)
java.net.SocketPermission "{host}","resolve";
else
java.net.SocketPermission "{host}:{port}","connect"

java.net.MulticastSocket
public synchronized void
send(DatagramPacket p, byte ttl)

checkMulticast(p.getAddress(), ttl)または
checkConnect( p.getAddress().getHostAddress(), p.getPort())

if (p.getAddress().isMulticastAddress()) { java.net.SocketPermission( (p.getAddress()).getHostAddress(), "accept,connect") } else { port = p.getPort(); host = p.getAddress().getHostAddress(); if (port == -1) java.net.SocketPermission "{host}","resolve"; else java.net.SocketPermission "{host}:{port}","connect" }

java.net.InetAddress
public String getHostName()
public static InetAddress[]
getAllByName(String host)
public static InetAddress getLocalHost()

java.net.DatagramSocket
public InetAddress getLocalAddress()

checkConnect({host}, -1)

java.net.SocketPermission "{host}", "resolve"

java.net.ServerSocket
ServerSocket(...)

java.net.DatagramSocket
DatagramSocket(...)

java.net.MulticastSocket
MulticastSocket(...)

checkListen({port})

java.net.SocketPermission "localhost:{port}","listen";

java.net.ServerSocket
public Socket accept()
protected final void implAccept(Socket s)

checkAccept({host}, {port})

java.net.SocketPermission "{host}:{port}", "accept"

java.net.ServerSocket
public static synchronized void
setSocketFactory(...)

java.net.Socket
public static synchronized void
setSocketImplFactory(...)

java.net.URL
public static synchronized void
setURLStreamHandlerFactory(...)

java.net.URLConnection
public static synchronized void
setContentHandlerFactory(...)
public static void
setFileNameMap(FileNameMap map)

java.net.HttpURLConnection
public static void
setFollowRedirects(boolean set)

java.rmi.activation.ActivationGroup
public static synchronized
ActivationGroup createGroup(...)
public static synchronized void
setSystem(ActivationSystem system)

java.rmi.server.RMISocketFactory
public synchronized static void
setSocketFactory(...)

checkSetFactory

java.lang.RuntimePermission "setFactory"

java.net.Socket
Socket(...)

checkConnect({host}, {port})

java.net.SocketPermission "{host}:{port}", "connect"

java.net.DatagramSocket
public synchronized void
receive(DatagramPacket p)

checkAccept({host}, {port})

java.net.SocketPermission "{host}:{port}", "accept"

java.net.URL
URL(...)

checkPermission

java.net.NetPermission "specifyStreamHandler"

java.net.URLClassLoader
URLClassLoader(...)

checkCreateClassLoader

java.lang.RuntimePermission "createClassLoader"

java.security.AccessControlContext
public AccessControlContext(AccessControlContext acc,
DomainCombiner combiner)
public DomainCombiner getDomainCombiner()

checkPermission

java.security.SecurityPermission "createAccessControlContext"

java.security.Identity
public void addCertificate(...)

checkSecurityAccess( "addIdentityCertificate")

java.security.SecurityPermission "addIdentityCertificate"

java.security.Identity
public void removeCertificate(...)

checkSecurityAccess( "removeIdentityCertificate")

java.security.SecurityPermission "removeIdentityCertificate"

java.security.Identity
public void setInfo(String info)

checkSecurityAccess( "setIdentityInfo")

java.security.SecurityPermission "setIdentityInfo"

java.security.Identity
public void setPublicKey(PublicKey key)

checkSecurityAccess( "setIdentityPublicKey")

java.security.SecurityPermission "setIdentityPublicKey"

java.security.Identity
public String toString(...)

checkSecurityAccess( "printIdentity")

java.security.SecurityPermission "printIdentity"

java.security.IdentityScope
protected static void setSystemScope()

checkSecurityAccess( "setSystemScope")

java.security.SecurityPermission "setSystemScope"

java.security.Permission
public void checkGuard(Object object)

checkPermission(this)

このアクセス権オブジェクトはチェックされたアクセス権である

java.security.Policy
public static Policy getPolicy()

checkPermission

java.security.SecurityPermission "getPolicy"

java.security.Policy
public static void
setPolicy(Policy policy)

checkPermission

java.security.SecurityPermission "setPolicy"

java.security.Policy
public static Policy
getInstance(String type, SpiParameter params)
getInstance(String type, SpiParameter params, String provider)
getInstance(String type, SpiParameter params, Provider provider)

checkPermission

java.security.SecurityPermission "createPolicy.{type}"

java.security.Provider
public synchronized void clear()

checkSecurityAccess( "clearProviderProperties."+{name})

java.security.SecurityPermission "clearProviderProperties.{name}" (nameはプロバイダ名)。

java.security.Provider
public synchronized Object
put(Object key, Object value)

checkSecurityAccess( "putProviderProperty."+{name})

java.security.SecurityPermission "putProviderProperty.{name}" (nameはプロバイダ名)。

java.security.Provider
public synchronized Object
remove(Object key)

checkSecurityAccess( "removeProviderProperty."+{name})

java.security.SecurityPermission "removeProviderProperty.{name}" (nameはプロバイダ名)。

java.security.SecureClassLoader
SecureClassLoader(...)

checkCreateClassLoader

java.lang.RuntimePermission "createClassLoader"

java.security.Security
public static void getProperty(String key)

checkPermission

java.security.SecurityPermission "getProperty.{key}"

java.security.Security
public static int
addProvider(Provider provider)
public static int
insertProviderAt(Provider provider,
int position);

checkSecurityAccess( "insertProvider."+provider.getName())

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

java.security.Security
public static void
removeProvider(String name)

checkSecurityAccess( "removeProvider."+name)

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

java.security.Security
public static void
setProperty(String key, String datum)

checkSecurityAccess( "setProperty."+key)

java.security.SecurityPermission "setProperty.{key}"

java.security.Signer
public PrivateKey getPrivateKey()

checkSecurityAccess( "getSignerPrivateKey")

java.security.SecurityPermission "getSignerPrivateKey"

java.security.Signer
public final void
setKeyPair(KeyPair pair)

checkSecurityAccess( "setSignerKeypair")

java.security.SecurityPermission "setSignerKeypair"

java.sql.DriverManager
public static synchronized void
setLogWriter(PrintWriter out)

checkPermission

java.sql.SQLPermission "setLog"

java.sql.DriverManager
public static synchronized void
setLogStream(PrintWriter out)

checkPermission

java.sql.SQLPermission "setLog"

java.util.Locale
public static synchronized void
setDefault(Locale newLocale)

checkPermission

java.util.PropertyPermission "user.language","write"

java.util.zip.ZipFile
ZipFile(String name)

checkRead

java.io.FilePermission "{name}","read"

javax.security.auth.Subject
public static Subject getSubject(final AccessControlContext acc)

checkPermission

javax.security.auth.AuthPermission "getSubject"

javax.security.auth.Subject
public void setReadOnly()

checkPermission

javax.security.auth.AuthPermission "setReadOnly"

javax.security.auth.Subject
public static Object doAs(final Subject subject,
final PrivilegedAction action)

checkPermission

javax.security.auth.AuthPermission "doAs"

javax.security.auth.Subject
public static Object doAs(final Subject subject,
final PrivilegedExceptionAction action)
throws java.security.PrivilegedActionException

checkPermission

javax.security.auth.AuthPermission "doAs"

javax.security.auth.Subject
public static Object doAsPrivileged(final Subject subject,
final PrivilegedAction action,
final AccessControlContext acc)

checkPermission

javax.security.auth.AuthPermission "doAsPrivileged"

javax.security.auth.Subject
public static Object doAsPrivileged(final Subject subject,
final PrivilegedExceptionAction action,
final AccessControlContext acc)
throws java.security.PrivilegedActionException

checkPermission

javax.security.auth.AuthPermission "doAsPrivileged"

javax.security.auth.SubjectDomainCombiner
public Subject getSubject()

checkPermission

javax.security.auth.AuthPermission "getSubjectFromDomainCombiner"

javax.security.auth.SubjectDomainCombiner
public Subject getSubject()

checkPermission

javax.security.auth.AuthPermission "getSubjectFromDomainCombiner"

javax.security.auth.login.LoginContext
public LoginContext(String name)
throws LoginException

checkPermission

javax.security.auth.AuthPermission "createLoginContext.{name}"

javax.security.auth.login.LoginContext
public LoginContext(String name,
Subject subject)
throws LoginException

checkPermission

javax.security.auth.AuthPermission "createLoginContext.{name}"

javax.security.auth.login.LoginContext
public LoginContext(String name,
CallbackHandler callbackHandler)
throws LoginException

checkPermission

javax.security.auth.AuthPermission "createLoginContext.{name}"

javax.security.auth.login.LoginContext
public LoginContext(String name,
Subject subject,
CallbackHandler callbackHandler)
throws LoginException

checkPermission

javax.security.auth.AuthPermission "createLoginContext.{name}"

javax.security.auth.login.Configuration
public static Configuration getConfiguration()

checkPermission

javax.security.auth.AuthPermission "getLoginConfiguration"

javax.security.auth.login.Configuration
public static void setConfiguration(Configuration configuration)

checkPermission

javax.security.auth.AuthPermission "setLoginConfiguration"

javax.security.auth.login.Configuration
public static void refresh()

checkPermission

javax.security.auth.AuthPermission "refreshLoginConfiguration"

javax.security.auth.login.Configuration
public static Configuration
getInstance(String type, SpiParameter params)
getInstance(String type, SpiParameter params, String provider)
getInstance(String type, SpiParameter params, Provider provider)

checkPermission

javax.security.auth.AuthPermission "createLoginConfiguration.{type}"

java.lang.SecurityManagerメソッドのアクセス権チェック

java.lang.SecurityManagerメソッドのデフォルトの実装によってチェックされるアクセス権のリストを示します。

指定された各checkメソッドはSecurityManager checkPermissionメソッドを指定されたアクセス権で呼び出します(コンテキスト引数を取るcheckConnectおよびcheckReadメソッドを除く)。これらのメソッドは、AccessControlContextコンテキストを想定し、コンテキストのcheckPermissionメソッドを指定されたアクセス権で呼び出します。

表1-7 java.lang.SecurityManagerのメソッドおよびアクセス権

メソッド

アクセス権

public void checkAccept(String host, int port);

java.net.SocketPermission "{host}:{port}", "accept";

public void checkAccess(Thread t);

java.lang.RuntimePermission "modifyThread";

public void checkAccess(ThreadGroup g);

java.lang.RuntimePermission "modifyThreadGroup";

public void checkAwtEventQueueAccess();

注意:
このメソッドは推奨されていません。かわりに次を使用してください。
public void checkPermission(Permission perm);

java.awt.AWTPermission "accessEventQueue";

public void checkConnect(String host, int port);

if (port == -1) java.net.SocketPermission "{host}","resolve"; else java.net.SocketPermission "{host}:{port}","connect";

public void checkConnect(String host, int port, Object context);

if (port == -1) java.net.SocketPermission "{host}","resolve"; else java.net.SocketPermission "{host}:{port}","connect";

public void checkCreateClassLoader();

java.lang.RuntimePermission "createClassLoader";

public void checkDelete(String file);

java.io.FilePermission "{file}", "delete";

public void checkExec(String cmd);

if cmd is an absolute path:java.io.FilePermission "{cmd}", "execute"; else java.io.FilePermission "<<ALL_FILES>>", "execute";

public void checkExit(int status);

java.lang.RuntimePermission "exitVM.{status}";

public void checkLink(String lib);

java.lang.RuntimePermission "loadLibrary.{lib}";

public void checkListen(int port);

java.net.SocketPermission "localhost:{port}","listen";

public void checkMemberAccess(Class clazz, int which);

注意:
このメソッドは推奨されていません。かわりに次を使用してください。
public void checkPermission(Permission perm);

if (which != Member.PUBLIC) {
if (currentClassLoader() != clazz.getClassLoader()) {
checkPermission(
new java.lang.RuntimePermission("accessDeclaredMembers"));
}
}

public void checkMulticast(InetAddress maddr);

java.net.SocketPermission(maddr.getHostAddress(),"accept,connect");

public void checkMulticast(InetAddress maddr, byte ttl);

注意:
このメソッドは推奨されていません。かわりに次を使用してください。
public void checkPermission(Permission perm);

java.net.SocketPermission(maddr.getHostAddress(),"accept,connect");

public void checkPackageAccess(String pkg);

java.lang.RuntimePermission "accessClassInPackage.{pkg}";

public void checkPackageDefinition(String pkg);

java.lang.RuntimePermission "defineClassInPackage.{pkg}";

public void checkPrintJobAccess();

java.lang.RuntimePermission "queuePrintJob";

public void checkPropertiesAccess();

java.util.PropertyPermission "*", "read,write";

public void checkPropertyAccess(String key);

java.util.PropertyPermission "{key}", "read,write";

public void checkRead(FileDescriptor fd);

java.lang.RuntimePermission "readFileDescriptor";

public void checkRead(String file);

java.io.FilePermission "{file}", "read";

public void checkRead(String file, Object context);

java.io.FilePermission "{file}", "read";

public void checkSecurityAccess(String target);

java.security.SecurityPermission "{target}";

public void checkSetFactory();

java.lang.RuntimePermission "setFactory";

public void checkSystemClipboardAccess();

注意:
このメソッドは推奨されていません。かわりに次を使用してください。
public void checkPermission(Permission perm);

java.awt.AWTPermission "accessClipboard";

public boolean checkTopLevelWindow(Object window);

注意:
このメソッドは推奨されていません。かわりに次を使用してください。
public void checkPermission(Permission perm);

java.awt.AWTPermission "showWindowWithoutWarningBanner";

public void checkWrite(FileDescriptor fd);

java.lang.RuntimePermission "writeFileDescriptor";

public void checkWrite(String file);

java.io.FilePermission "{file}", "write";

public SecurityManager();

java.lang.RuntimePermission "createSecurityManager";

デフォルトのPolicyの実装とポリシー・ファイルの構文

Javaプログラミング言語のアプリケーション環境のポリシーは、様々なソースを出所とするコードがどのようなアクセス権を使用できるかを指定し、様々なプリンシパルとして実行するもので、Policyオブジェクトによって表されます。具体的には、Policyクラス(java.securityパッケージ内)内のabstractメソッドの実装を提供するPolicyサブクラスによって表されます。

Policyオブジェクトが使用するポリシー情報がどこに置かれるかは、Policyの実装によります。Policyのリファレンス実装では、ポリシー情報を静的なポリシー構成ファイルから得ます。

このドキュメントの残りの部分では、Policyのリファレンス実装と、それによって読み取られるポリシー・ファイルで使用する構文について説明します。

- デフォルトのPolicyの実装

- ポリシー・ファイルのデフォルトの場所

- Policyの実装の変更

- ポリシー・ファイルの構文

- ポリシー・ファイルの例

- ポリシー・ファイルでのプロパティの展開

- Windowsシステム、ファイル・パス、およびプロパティの展開

- ポリシー・ファイルでの一般的な展開

デフォルトのPolicyの実装

任意のテキスト・エディタでポリシー・ファイルを作成します。

Policyのリファレンス実装では、1つまたは複数のポリシー構成ファイルからポリシーを指定できます。ポリシー構成ファイルでは、指定されたコード・ソースからのコードに対してどのようなアクセス権を与え、指定されたプリンシパルとして実行するかを指定します。各構成ファイルは、UTF-8方式でエンコードする必要があります。

デフォルトでは、システム全体のポリシー・ファイルが1つと、ユーザー・ポリシー・ファイル(オプション)が1つ存在します。デフォルトでは、常に、プラットフォーム・クラス・ローダーによってロードされるJDKモジュールまたはその祖先で必要なアクセス権が付与されます。

Policyのリファレンス実装は、そのgetPermissionsメソッドがはじめて呼び出されたとき、あるいは、そのrefreshメソッドが呼び出されたときに初期化されます。初期化時には、ポリシー構成ファイル(ポリシー・ファイルの構文を参照)の解析が行われた後、その情報がPolicyオブジェクトに読み込まれます。

ポリシー・ファイルのデフォルトの場所

デフォルトでは、システム全体のポリシー・ファイルが1つと、ユーザー・ポリシー・ファイル(オプション)が1つ存在します。Policyが初期化されると、まずシステム・ポリシーがロードされ、次に、ロードされたシステム・ポリシーにユーザー・ポリシーが追加されます。どちらのポリシーも存在しない場合は、組込みポリシーが使われます。この組込みポリシーは、JREとともにインストールしたjava.policyファイルと同じです。

システム・ポリシー・ファイルの場所

デフォルトでは、システム・ポリシー・ファイルは<java-home>/conf/security/java.policyです。

システムのポリシー・ファイルは、システム全体にわたってコードにアクセス権を与えます。JDKに含まれているjava.policyファイルは、すべてのユーザーに動的ポートでの待機を許可し、すべてのコードに、セキュリティにかかわらない特定の標準プロパティ(os.nameプロパティやfile.separatorプロパティなど)の読取りを許可します。

ユーザー・ポリシー・ファイルの場所

デフォルトでは、ユーザー・ポリシー・ファイルは<user-home>/.java.policyです。

ポリシー・ファイルの場所と形式の構成

ポリシー・ファイルの場所は、セキュリティ・プロパティ・ファイル<java-home>/conf/security/java.securityで指定されています。

ポリシー・ファイルの場所は、次のような形式の名前を持つプロパティの値として指定されています。

policy.url.n

ここで、nは数値です。次に示す形式の行で、それぞれのプロパティの値を指定します。

policy.url.n=URL

ここで、URLはURLの指定を表します。たとえば、デフォルトのシステムおよびユーザー・ポリシー・ファイルは、セキュリティ・プロパティ・ファイルに次のように指定されています。:

policy.url.1=file:${java.home}/conf/security/java.policy
policy.url.2=file:${user.home}/.java.policy

(${java.home}を使用してjava.homeプロパティの値を指定するなど、特別な構文でプロパティの値を指定する方法については、プロパティの展開を参照。)

URLをいくつも指定して(http://形式のものを含む)、該当するポリシー・ファイルをすべてロードすることもできます。また、上に示したポリシー・ファイルの指定のうち、2番目のポリシー・ファイルの指定をコメント・アウトするか、あるいは修正すれば、デフォルト・ユーザー・ポリシー・ファイルの読込みを無効にすることができます。

アルゴリズムは、policy.url.1から処理を開始して、番号を1つずつ増やしながら、URLが見つからなくなるまで処理を続けます。したがって、policy.url.1とpolicy.url.3がある場合、policy.url.3は読み込まれません。

例1-4 実行時に新しいポリシー・ファイルを指定する

アプリケーションを実行するときに、追加のポリシー・ファイルや別のポリシー・ファイルを指定することもできます。これは、-Djava.security.policyコマンド行引数を使用してjava.security.policyプロパティの値を設定することで実行できます。たとえば、次のコマンドを入力した場合(ここで、someURLはポリシー・ファイルの場所を指定するURL)、指定したポリシー・ファイルが、セキュリティ・プロパティ・ファイルに指定されているすべてのポリシー・ファイルに加えてロードされます。

java -Djava.security.manager -Djava.security.policy=someURL SomeApp

次に示すとおり、URLは通常のURLでも、単純に現在のディレクトリのポリシー・ファイルの名前でもかまいません。:

java -Djava.security.manager -Djava.security.policy=mypolicy SomeApp

-Djava.security.managerオプションによってデフォルト・セキュリティ・マネージャがインストールされるので、このアプリケーションに対してはポリシーのチェックが行われます。アプリケーションSomeAppがセキュリティ・マネージャをインストールする場合は、この引数は必要ありません。

次のコマンドを使用した場合(等号が2つ使用されていることに注意)、指定されたポリシー・ファイルのみが使用され、セキュリティ・プロパティ・ファイルに示されたポリシー・ファイルはすべて無視されます。

java -Djava.security.manager -Djava.security.policy==someURL SomeApp

注意:
セキュリティ・プロパティ・ファイル内のpolicy.allowSystemPropertyプロパティがfalseに設定されている場合、-Djava.security.policyオプションのポリシー・ファイル値は無視されます。デフォルトは、trueです。

Policyの実装の変更

セキュリティ・プロパティ・ファイル(JDKのconf/securityディレクトリのjava.securityファイル)を編集すれば、Policyのリファレンス実装を変更できます。

新しいポリシー・クラスを作成し、Policyのリファレンス実装クラスと置き換えることもできます。この場合は、Policy抽象クラスのサブクラスを作成し、getPermissionsメソッド(必要に応じてその他のメソッドも)を実装します。

java.securityで設定できるプロパティの例を次に示します。

policy.provider=PolicyClassName

PolicyClassNameには、目的のPolicy実装クラスを完全修飾名で指定します。

デフォルトのセキュリティ・プロパティ・ファイルでは、このプロパティは次のように指定されています。

policy.provider=sun.security.provider.PolicyFile

カスタマイズするには、次のように、このプロパティに別のクラスを指定します。

policy.provider=com.mycom.MyPolicy

ポリシー・ファイルの構文

インストールしたJDKのポリシー構成ファイルでは、指定されたコード・ソースからのコードに対し、どのようなアクセス権(システム・リソースへのアクセスの種類)を与え、指定したプリンシパルとして実行するかを指定します。

アプレット(またはセキュリティ・マネージャの下で動作しているアプリケーション)が、ファイルの読み書きなど、セキュリティ保護された操作を行うためには、その操作を行うためのアクセス権が与えられている必要があります。Policyのリファレンス実装では、ポリシー構成ファイルの付与エントリによって、そのアクセス権を与える必要があります。詳細は、以降の説明とJavaセキュリティ・アーキテクチャ仕様を参照してください。(ただし、同じ(URLの)場所にあるファイルと、その場所のサブディレクトリにあるファイルの読取りアクセス権は、常に自動的に与えられます。したがって、そのようなアクセス権については、明示的に指定する必要はありません。)

ポリシー構成ファイルは、エントリのリストで構成されます。1つのkeystoreエントリと、0以上のgrantエントリを持たせることができます。

キーストア・エントリ

キーストアの作成と管理には、keytoolユーティリティを使います。

キーストアは、非公開鍵と、対応する公開鍵を認証するX.509証明書チェーンなどのデジタル証明書が格納されたデータベースです。キーストアの作成と管理には、keytoolユーティリティを使用します。ポリシー構成ファイルで指定されているキーストアは、そのファイルのgrantエントリで指定されている署名者の公開鍵を照合するために使用されます。署名者の別名を指定しているgrantエントリがある場合、またはプリンシパルの別名を指定しているgrantエントリがある場合は、ポリシー構成ファイルに必ずkeystoreエントリを置きます。

このとき、ポリシー・ファイル内で有効なkeystore/keystorePasswordURLエントリは1つのみです。最初のエントリ以外の後続エントリは無視されます。このエントリは、ファイルのgrantエントリ外部の任意の場所に配置できます。構文は次のとおりです。

keystore "some_keystore_url", "keystore_type", "keystore_provider";
keystorePasswordURL "some_password_url";

各構成要素は次のとおりです。

some_keystore_url

キーストアのURLの場所を指定します。

some_password_url

キーストア・パスワードのURLの場所を指定します。

keystore_type

キーストアのタイプを指定します。

keystore_provider

キーストア・プロバイダを指定します。

注意:

- some_keystore_urlからの入力ストリームはKeyStore.loadメソッドに渡されます。

- URLとしてNONEが指定されている場合は、nullのストリームがKeyStore.loadメソッドに渡されます。NONEは、キーストアがファイルベースでない場合にURLで指定してください。たとえば、ハードウェア・トークン・デバイスに置かれている場合などです。

- URLは、ポリシー・ファイルがある場所からの相対位置を表します。たとえば、セキュリティ・プロパティ・ファイルの中でポリシー・ファイルが次のように指定されているとします。

policy.url.1=http://foo.example.com/fum/some.policy

また、このポリシー・ファイルには、次のエントリがあるとします。

keystore ".keystore";

この場合、キーストアは次の場所からロードされます。

http://foo.example.com/fum/.keystore

- URLに絶対位置を指定することもできます。

キーストアのタイプは、キーストア情報の格納形式とデータ形式を定義するとともに、キーストア内の非公開鍵とキーストア自体の整合性を保護するために使われるアルゴリズムを定義します。デフォルトのタイプは、PKCS12という独自のキーストア・タイプです。したがって、キーストアのタイプがPKCS12であれば、キーストア・エントリを指定する必要はありません。

付与エントリ

付与エントリは、アクセス権を付与するコードを指定するために使用されます。

実行されるコードは、常に、特定の「コード・ソース」(CodeSource型のオブジェクトによって表される)から来ると考えられます。コード・ソースは、コードの出所を表す場所(URL)だけでなく、コードの署名に使われた非公開鍵に対応する公開鍵を含んだ証明書への参照も含みます。コード・ソース内の証明書は、ユーザーのキーストアからのシンボリックな別名によって参照されます。コードはさらに、特定のプリンシパル(Principal型のオブジェクトによって表される)またはプリンシパルのグループとして実行されると考えられます。

各付与エントリには、オプションのcodeBaseとsignedBy、およびプリンシパルの名前と値のペアのあとに、アクセス権を付与するコードを指定する1つ以上の「アクセス権エントリ」が含まれます。付与エントリの基本形式は、次のとおりです。

grant signedBy "signer_names", codeBase "URL",
principal principal_class_name "principal_name",
principal principal_class_name "principal_name",
... {

permission permission_class_name "target_name", "action",
signedBy "signer_names";
permission permission_class_name "target_name", "action",
signedBy "signer_names";
...
};

上の形式でイタリック体になっていない項目は、示されているとおりに指定します。ただし、大文字と小文字は区別されず、また、以降で説明するように、いくつかの項目はオプションです。イタリックで示されている項目は、可変の項目です。

付与エントリは、grantで始まります。

SignedBy、Principal、およびCodeBaseフィールド

signedBy、codeBase、およびprincipalの値はオプションです。また、これらのフィールドの順序は問われません。

signedByの値は、キーストアに格納された証明書の別名を示します。証明書内の公開鍵は、コードのデジタル署名の検証に使われます。別名によって指定されたキーストア・エントリ内の公開鍵に対応する、非公開鍵で署名されたコードに、アクセス権を付与します。

signedByの値には、複数の別名をカンマで区切って指定できます。たとえば、"Adam,Eve,Charles"のように指定できます。この場合は、各要素がORではなくANDで結ばれ、「Adam、EveおよびCharlesによって署名された」という意味になります。より厳密には、「Adamによって署名されたコード」とは、「Adamという別名が付けられたエントリを持つキーストアにある公開鍵証明書に対応する、非公開鍵を使って署名されたJARファイルに含まれている、クラス・ファイル内のコード」という意味です。

signedByフィールドはオプションです。省略した場合は、「任意の署名者」という意味になります。コードに署名が付いているかどうか、だれが署名しているかは問われなくなります。

プリンシパルの値はclass_nameとprincipal_nameのペアを指定します。このペアは、実行中のスレッドのプリンシパル・セット内にある必要があります。プリンシパル・セットは、Subjectによって実行するコードに関連付けられます。

principal_class_nameにワイルドカード値(*)を設定することで、任意のPrincipalクラスに一致させることができます。さらに、principal_nameにワイルドカード値(*)を設定することで、任意のPrincipal名に一致させることができます。principal_class_nameまたはprincipal_nameに*を設定するときは、*を引用符で囲まないでください。また、ワイルドカードのプリンシパル・クラスを指定する場合は、ワイルドカードのプリンシパル名も指定する必要があります。

プリンシパル・フィールドは省略可能です。省略した場合は、「任意のプリンシパル」という意味になります。

キーストア別名の置換

プリンシパルのclass_name/principal_nameペアは単一引用符で囲まれた文字列として指定され、キーストアの別名として扱われます。

キーストアは別名を経由してX509証明書を調査し、問い合わせます。キーストアがある場合、プリンシパルclass_nameは自動的にjavax.security.auth.x500.X500Principalとして扱われ、principal_nameは自動的に証明書のサブジェクト識別名として扱われます。X509証明書のマッピングが見つからない場合は、grantエントリはすべて無視されます。

codeBaseの値は、コードが置かれる場所を示します。この場所からコードにアクセス権を付与します。codeBaseエントリを省略した場合は、「任意のコード」という意味になり、コードの出所は問われません。

注意:
codeBaseの値はURLであるため、コード・ソースがWindowsシステム上にある場合であっても、ディレクトリのセパレータには、バックスラッシュではなく、必ずスラッシュを使用します。したがって、Windowsシステム上でコードの場所がC:\somepath\api\の場合、codeBaseポリシー・エントリは次のように指定します。

grant codeBase "file:/C:/somepath/api/" {
...
};

codeBase値の正確な意味は、末尾の文字によって異なります。末尾が/のcodeBaseは、指定されたディレクトリ内のすべてのクラス・ファイル(JARファイルを除く)を示します。末尾が/*のcodeBaseは、そのディレクトリ内にあるすべてのファイル(クラス・ファイルとJARファイルの両方)を示します。末尾が/-のcodeBaseは、指定されたディレクトリとその下の全サブディレクトリ内のすべてのファイル(クラス・ファイルとJARファイルの両方)を示します。次の表では、様々なケースを示します。

表1-8 キーストア別名

ダウンロードされたコードのcodebase URL

ポリシーのcodebase URL

一致するかどうか

www.example.com/people/gong/

www.example.com/people/gong

はい

www.example.com/people/gong/

www.example.com/people/gong/

はい

www.example.com/people/gong/

www.example.com/people/gong/*

はい

www.example.com/people/gong/

www.example.com/people/gong/-

はい

www.example.com/people/gong/appl.jar

www.example.com/people/gong/

いいえ

www.example.com/people/gong/appl.jar

www.example.com/people/gong/-

はい

www.example.com/people/gong/appl.jar

www.example.com/people/gong/*

はい

www.example.com/people/gong/appl.jar

www.example.com/people/-

はい

www.example.com/people/gong/appl.jar

www.example.com/people/*

いいえ

www.example.com/people/gong/

www.example.com/people/-

はい

www.example.com/people/gong/

www.example.com/people/*

いいえ

アクセス権エントリ

アクセス権エントリは、順序どおり(permission、permission_class_name、"target_name"、"action"およびsignedBy "signer_names)に指定されます。

アクセス権エントリは、単語permissionで始まります。前述のテンプレートにある単語permission_class_nameは、実際にはjava.io.FilePermissionやjava.lang.RuntimePermissionなどの特定のアクセス権タイプになります。

java.io.FilePermission (どのような種類のファイル・アクセスを許可するかを指定)など、多くのアクセス権タイプでは"action"が必須です。java.lang.RuntimePermissionなどのカテゴリでは不要なため、必須ではありません。permission_class_nameの後の"target_name"値で指定されたアクセス権が与えられるか、アクセス権が与えられないかのどちらかになります。

アクセス権エントリのsignedByの名前と値のペアは省略可能です。この値が存在する場合は、署名付きアクセス権であることを示します。つまり、そのアクセス権を与えられるようにするためには、アクセス権クラスそれ自体が、指定された別名によって署名されていなければなりません。たとえば、次のような付与エントリがあるとします。

grant {
permission Foo "foobar", signedBy "FooSoft";
};

この場合、このアクセス権タイプFooが与えられるのは、あるJARファイルにアクセス権Foo.classが置かれていて、そのJARファイルが、別名FooSoftにより指定された証明書内の公開鍵に対応する非公開鍵によって署名されているときか、Foo.classがシステム・クラスであるときです(システム・クラスはポリシーによる制限を受けないため)。

アクセス権エントリの各項目は、指定された順序(permission、permission_class_name、"target_name"、"action"、およびsignedBy "signer_names")で並んでいる必要があります。各エントリはセミコロンで終わります。

識別子(permission、signedBy、codeBaseなど)では大文字と小文字は区別されませんが、permission_class_nameと、値として引き渡される文字列については大文字と小文字が区別されます。

Windowsシステムでのファイル・パスの指定

Windowsシステムでのファイル・パス仕様には、実際の1つバックスラッシュごとに、2つのバックスラッシュを含める必要があります。

注意:

java.io.FilePermissionを指定する場合、"target_name"はファイル・パスになります。Windowsシステムでは、ファイル・パスをcodeBase URLではなく直接文字列で指定する場合は、パス中のバックスラッシュは、次のように2つ重ねて指定する必要があります。

grant {
permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read";
};

これは、文字列はトークナイザ(java.io.StreamTokenizer)によって処理され、トークナイザは\をエスケープ文字列と解釈するため(たとえば、\nは改行を表す)、バックスラッシュそのものを表すには、バックスラッシュを2つ重ねる必要があるからです。トークナイザは、上記のファイル・パス文字列の処理を終えると、二重のバックスラッシュを単一のバックスラッシュに変換し、最終的には次のようになります。

"C:\users\cathy\foo.bat"

ポリシー・ファイルの例

codeBase値およびsignedBy値の異なる構成を含むポリシー構成ファイルの例を示します。様々なプリンシパル・ベースのエントリおよびKeyStore値を使用するgrant文の例です。

例1-5 ポリシー構成ファイルの例

ポリシー構成ファイルから取り出した2つのエントリの例を次に示します。

// If the code is signed by "Duke", grant it read/write access to all
// files in /tmp:
grant signedBy "Duke" {
permission java.io.FilePermission "/tmp/*", "read,write";
};

// Grant everyone the following permission:
grant {
permission java.util.PropertyPermission "java.vendor", "read";
};

例1-6 ポリシー構成ファイルの例

次のコードは、次の条件を満たすコードのみがSecurityクラス内のメソッドを呼び出して、プロバイダの追加または削除を行ったり、Securityプロパティを設定したりできることを示しています。

- ローカル・ファイル・システムの"/home/sysadmin/"ディレクトリにある、署名されたJARファイルからコードがロードされた。

- 署名が、キーストア内の別名sysadminによって参照される公開鍵で認証される。

grant signedBy "sysadmin", codeBase "file:/home/sysadmin/*" {
permission java.security.SecurityPermission "Security.insertProvider.*";
permission java.security.SecurityPermission "Security.removeProvider.*";
permission java.security.SecurityPermission "Security.setProperty.*";
};

例1-7 codeBaseがない場合の例

コード・ソースを指定するコンポーネントは、どちらも(または両方を)省略可能です。codeBaseを省略した例を次に示します。

grant signedBy "sysadmin" {
permission java.security.SecurityPermission "Security.insertProvider.*";
permission java.security.SecurityPermission "Security.removeProvider.*";
};

このポリシーが有効な場合、sysadminによって署名されたJARファイルに含まれるコードは、JARファイルの出所に関係なく、プロバイダの追加と削除を行えます。

例1-8 signedByを使用しない例

grant codeBase "file:/home/sysadmin/-" {
permission java.security.SecurityPermission "Security.insertProvider.*";
permission java.security.SecurityPermission "Security.removeProvider.*";
};

この場合、ローカル・ファイル・システムの"/home/sysadmin/"ディレクトリに置かれたコードは、プロバイダの追加と削除を行うことができます。コードに署名は必要ありません。

例1-9 codeBaseまたはsignedByを使用しない例

grant {
permission java.security.SecurityPermission "Security.insertProvider.*";
permission java.security.SecurityPermission "Security.removeProvider.*";
};

この例では、コード・ソースを指定する要素がどちらも省略されています。したがって、出所がどこか、署名が付いているか、だれの署名が付いているかに関係なく、どのコードでもプロバイダの追加と削除が行えます。

例1-10 X500Principalとして実行する例

grant principal javax.security.auth.x500.X500Principal "cn=Alice" {
permission java.io.FilePermission "/home/Alice", "read, write";
};

これにより、X500Principalのアクセス権cn=Aliceとして実行するコードは/home/Aliceを読み取ることも書き込むことも可能になります。

例1-11 識別名を使用せずにX500Principalとして実行する例

grant principal javax.security.auth.x500.X500Principal * {
permission java.io.FilePermission "/tmp", "read, write";
};

これにより、X500Principalのアクセス権(識別名に関係なく)として実行するコードは/tmpを読み取ることも書き込むことも可能になります。

例1-12 CodeBaseおよびX500Principal情報を使用するGrant文

grant codebase "http://www.games.example.com",
signedBy "Duke",
principal javax.security.auth.x500.X500Principal "cn=Alice" {
permission java.io.FilePermission "/tmp/games", "read, write";
};

これにより、"Duke"によって署名され、"www.games.example.com"からダウンロードされたコードが"cn=Alice"によって実行されると、"/tmp/games"ディレクトリへの読取り権と書出し権が与えられます。

例1-13 キーストア別名を使用するGrant文

keystore "http://foo.example.com/blah/.keystore";

grant principal "alice" {
permission java.io.FilePermission "/tmp/games", "read, write";
};

"alice"は次のアクセス権に置き換えられます。

javax.security.auth.x500.X500Principal "cn=Alice"

ただし、キーストア別名aliceに関連付けられたX.509証明書に、サブジェクトの識別名cn=Aliceがあることが前提となります。これにより、X500Principalのアクセス権"cn=Alice"によって実行されるコードに、"/tmp/games"ディレクトリへの読取り権と書出し権が与えられます。

ポリシー・ファイル内のプロパティの展開

ポリシー・ファイルとセキュリティ・プロパティ・ファイルでは、プロパティの展開が可能です。

プロパティの展開は、シェルでの変数の展開に似ています。ポリシー・ファイルまたはセキュリティ・プロパティ・ファイルに次のような文字列がある場合、

${some.property}

この文字列はシステム・プロパティの値に展開されます。次に例を示します。

permission java.io.FilePermission "${user.home}", "read";

この場合、"${user.home}"は、システム・プロパティ"user.home"の値に展開されます。そのプロパティの値が"/home/cathy"である場合、上の記述は下の記述と同じになります

permission java.io.FilePermission "/home/cathy", "read";

プラットフォームにより異なるポリシー・ファイルを使いやすくするために、${/}という特殊な表記(${file.separator}の簡略形)も使用できます。この表現を使用して、次のような指定が可能です。

permission java.io.FilePermission "${user.home}${/}*", "read";

user.homeプロパティの値が/home/cathyで、プラットフォームがSolaris、LinuxまたはmacOSである場合、これは次のように変換されます。

permission java.io.FilePermission "/home/cathy/*", "read";

Windowsシステムで、"user.home"の値がC:\users\cathyであれば、次のように変換されます。

permission java.io.FilePermission "C:\users\cathy\*", "read";

特殊なケースとして、コード・ベースのプロパティを次のように記述すると、

grant codeBase "file:${java.home}/lib/ext/"

file.separator文字は自動的に/ に変換されます。したがって、Windowsシステムでは次のように変換されます。

grant codeBase "file:C:/jdk1.4/lib/ext/"

java.homeがC:\jdk1.4\に設定されている場合でも、コードベース文字列で${/}を使用する必要はありません(使用しないでください)。プロパティの展開は、ポリシー・ファイル内で、二重引用符で囲まれた文字列が使用できる場所であればどこでも行われます。これには、"signer_names"、"URL"、"target_name"、"action"の各フィールドが含まれます。プロパティの展開が許可されるかどうかは、セキュリティ・プロパティ・ファイルの"policy.expandProperties"プロパティの値によって決まります。このプロパティの値がtrue (デフォルト)の場合は、展開が許可されます。

注意:
ネストされたプロパティは使用できません。次に例を示します。

"${user.${foo}}"

この例では、"foo"プロパティが"home"に設定されている場合であっても、エラーになります。これは、プロパティ構文解析プログラムはネストされたプロパティを認識しないためです。プロパティ構文解析プログラムは、最初の${を見つけると、次に最初の}を探し、その結果(この場合は${user.$foo})をプロパティと解釈しようとします。しかし、そのようなプロパティがない場合はエラーになります。

注意:
付与エントリ、アクセス権エントリまたはキーストア・エントリで展開できないプロパティがある場合、そのエントリは無視されます。たとえば、次のようにシステム・プロパティ"foo"が定義されていない場合、

grant codeBase "${foo}" {
permission ...;
permission ...;
};

このgrantエントリ内のpermissionはすべて無視されます。また、次のような場合、

grant {
permission Foo "${foo}";
permission Bar "barTarget";
};

"permission Foo..."エントリだけが無視されます。また、次のように指定されている場合、

keystore "${foo}";

この場合は、keystoreエントリが無視されます。

Windowsシステム、ファイル・パス、およびプロパティの展開

Windowsシステムでのファイル・パス仕様には、実際の1つバックスラッシュごとに、2つのバックスラッシュを含める必要があります。

Windowsシステムでは、ファイル・パスをcodeBase URLではなく直接文字列で指定する場合は、パス中のバックスラッシュは、次のように2つ重ねて指定する必要があります。

grant {
permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read";
};

これは、文字列はトークナイザ(java.io.StreamTokenizer)によって処理され、トークナイザは\をエスケープ文字列と解釈するため(たとえば、\nは改行を表す)、バックスラッシュそのものを表すには、バックスラッシュを2つ重ねる必要があるからです。トークナイザは、上記のファイル・パス文字列の処理を終えると、二重のバックスラッシュを単一のバックスラッシュに変換し、最終的には次のようになります。

"C:\users\cathy\foo.bat"

文字列中のプロパティの展開は、トークナイザがその文字列の処理を完了したあとに行われます。たとえば、次のような文字列があるとします。

"${user.home}\\foo.bat"

トークナイザは、この文字列中の二重のバックスラッシュを単一のバックスラッシュに変換し、結果は次のようになります。

"${user.home}\foo.bat"

次に、${user.home}プロパティが展開されて次のようになります。

"C:\users\cathy\foo.bat"

ここでは、"user.home"の値をC:\users\cathyとしています。もちろん、プラットフォームに依存しないために、明示的にスラッシュを使うのではなく、${/}プロパティを使って次のように指定する方が望ましいと言えます。

"${user.home}${/}foo.bat"

パス名正規化

正規パスは、リンクやショートカットを含まないパスです。FilePermissionオブジェクトでパス名正規化を実行すると、パフォーマンスに悪影響を与える可能性があります。

JDK 9より前は、2つのFilePermissionオブジェクトが比較されるときに、パス名が正規化されました。これにより、プログラムで、ポリシー・ファイル内でFilePermissionオブジェクトに付与されている名前とは異なる名前を使用してファイルにアクセスできました(そのオブジェクトが同じファイルを指している場合)。正規化では、基にあるファイル・システムにアクセスする必要があったため、非常に低速になる場合がありました。

JDK 9では、パス名正規化はデフォルトでは無効になっています。これは、一方が絶対パスを使用し他方が相対パスを使用する場合、一方がシンボリック・リンクを使用し他方がターゲットを使用する場合、または一方がWindowsロング・ネームを使用し他方がDOS形式の8.3ネームを使用する場合は、2つのFilePermissionオブジェクトが互いに等しくないということです。これは、それらすべてがファイル・システム内の同じファイルを指している場合でも当てはまります。

したがって、ポリシー・ファイル内のFilePermissionオブジェクトにパス名が付与されている場合、プログラムでも同じパス名形式を使用してそのファイルにアクセスする必要があります。たとえば、ポリシー・ファイル内のパス名でシンボリック・リンクが使用されている場合、プログラムでもそのシンボリック・リンクを使用する必要があります。ターゲット・パス名でファイルにアクセスすると、アクセス権チェックに失敗します。

互換性レイヤー

相対パスのFilePermissionオブジェクトの付与により(逆に)アプリケーションに絶対パスでのファイルへのアクセスが許可されるように、互換性レイヤーが追加されています。これは、デフォルトのPolicyプロバイダおよび制限付きdoPrivileged呼出しで機能します。

たとえば、相対パス名がaのファイル上のFilePermissionオブジェクトは、絶対パス名が/pwd/a (pwdは現在の作業ディレクトリ)の同一ファイル上のFilePermissionオブジェクトを意味するわけではなくなりました。セキュリティ・マネージャが有効になっている場合は、aを読み取るFilePermissionオブジェクトをコードに付与すると、そのコードで/pwd/aも読み取ることができます。

互換性レイヤーは、シンボリック・リンクとターゲット、Windowsロング・ネームとDOS形式8.3ネーム、または同じ名前に正規化できる他の様々な名前形式の間の変換には対応していません。

パス名正規化のカスタマイズ

表1-9のシステム・プロパティは、FilePermissionパス名正規化のカスタマイズに使用できます。java.lang.Systemプロパティの指定方法を参照してください。

表1-9 パス名正規化をカスタマイズするシステム・プロパティ

システム・プロパティ

デフォルト値

説明

jdk.io.permissionsUseCanonicalPath

false

このシステム・プロパティは、FilePermissionオブジェクトでのパス名正規化を有効または無効にするために使用できます。

- FilePermissionパス名正規化を無効にするには、jdk.io.permissionsUseCanonicalPath=falseと設定します。

- FilePermissionパス名正規化を有効にするには、jdk.io.permissionsUseCanonicalPath=trueと設定します。

jdk.security.filePermCompat

false

このシステム・プロパティは、サードパーティのPolicyの実装をサポートするように互換性レイヤーを拡張するために使用できます。

- このシステム・プロパティを無効にするには、jdk.security.filePermCompat=falseと設定します。
相対パスのFilePermissionで、アプリケーションが、デフォルトPolicyプロバイダおよび制限付きdoPrivilegedメソッドの絶対パスを使用してファイルにアクセスできるようになります。

- サードパーティのPolicyの実装をサポートするように互換性レイヤーを拡張するには、jdk.security.filePermCompat=trueと設定します。
相対パスのFilePermissionで、アプリケーションが、デフォルトPolicyプロバイダ、制限付きdoPrivilegedメソッド、およびサードパーティのPolicyの実装の絶対パスを使用してファイルにアクセスできるようになります。

ポリシー・ファイルにおける一般的な展開

ポリシー・ファイルは、2つのプロトコル(ポリシー・ファイル内の拡張形式selfとalias)を使用して拡張できます。

アクセス権名に次の形式の文字列が含まれているとします。

${{protocol:protocol_data}}

このような文字列がアクセス権名に含まれる場合、protocolの値で、実行される展開のタイプが決まり、protocol_dataは空にすることもできます。空の場合は、前述の文字列は次のような単純な形式になります。

${{protocol}}

デフォルトのポリシー・ファイルの実装では、次の2つのプロトコルがサポートされます。

1. ${{self}}
プロトコルselfは、${{self}}文字列全体を1つ以上のプリンシパル・クラスとプリンシパル名のペアに置き換えることを示します。実際に実行される置換えは、permissionを含むgrant節の内容によって決まります。

grant節にプリンシパルの情報が含まれていない場合は、permissionは無視されます。プリンシパルベースのgrant節のコンテキストでは、ターゲット名に${{self}}を含むpermissionのみが有効です。たとえば、次のgrant節内のBarPermissionは常に無視されます。

grant codebase "www.example.com", signedby "duke" {
permission BarPermission "... ${{self}} ...";
};

grant句にプリンシパル情報が含まれている場合は、${{self}}がそのプリンシパル情報に置き換えられます。たとえば、次のgrant句のBarPermission内の${{self}}は、javax.security.auth.x500.X500Principal "cn=Duke"に置き換えられます。

grant principal javax.security.auth.x500.X500Principal "cn=Duke" {
permission BarPermission "... ${{self}} ...";
};

grant句内にカンマで区切られたプリンシパルのリストがある場合、${{self}}は、そのカンマで区切られたプリンシパルのリストに置き換えられます。grant句内のプリンシパル・クラスとプリンシパル名がどちらもワイルドカードになっている場合、${{self}}は、現在のAccessControlContext内のSubjectに関連付けられたすべてのプリンシパルに置き換えられます。
次の例は、selfとキーストア別名の置換の両方を含むシナリオを示しています。

keystore "http://foo.example.com/blah/.keystore";

grant principal "duke" {
permission BarPermission "... ${{self}} ...";
};

上の例では、最初にdukeがjavax.security.auth.x500.X500Principal "cn=Duke"に展開されます。ただし、KeyStore別名dukeに関連付けられたX.509証明書にサブジェクト識別名cn=Dukeがあることが前提となります。次に、${{self}}が、grant節内で展開されたその同じプリンシパル情報に置き換えられます。javax.security.auth.x500.X500Principal "cn=Duke"

2. ${{alias:alias_name}}
プロトコルaliasは、java.security.KeyStore別名の置換を示します。KeyStoreエントリに指定されたKeyStoreが使用されます。alias_nameは、KeyStoreの別名を表します。${{alias:alias_name}}は、javax.security.auth.x500.X500Principal "DN"に置き換えられます。このDNは、alias_nameに属する証明書のサブジェクト識別名を表します。次に例を示します。

keystore "http://foo.example.com/blah/.keystore";

grant codebase "www.example.com" {
permission BarPermission "... ${{alias:duke}} ...";
};

上の例では、別名dukeに関連付けられているX.509証明書は、KeyStoreのfoo.example.com/blah/.keystoreから取得されます。dukeの証明書がサブジェクト識別名としてo=dukeOrg, cn=dukeを指定し、${{alias:duke}}がjavax.security.auth.x500.X500Principal "o=dukeOrg, cn=duke"で置き換えられるものとします。
次のエラー条件に該当する場合、アクセス権エントリは無視されます。

- keystoreエントリが指定されていない

- alias_nameが指定されていない

- alias_nameの証明書を取得できない

- 取得される証明書がX.509証明書ではない

特権ブロックのためのAPI

特権コードの意味とその使用目的について説明した後、APIの使用方法を示します。内容は次のとおりです。

doPrivileged APIの使用

特権コードを使う意味

リフレクション

doPrivileged APIの使用

doPrivileged API、および特権機能の使用方法を説明します。

戻り値がなく、例外がスローされない場合

ローカル変数にアクセスする場合

例外の処理

特権のサブセットのアサーション

最小特権

追加特権

戻り値がなく、例外がスローされない場合

特権ブロック内から値を返す必要がない場合は、doPrivilegedの呼出しは例1-14のようにできます。

ラムダ式を使ってdoPrivilegedを呼び出す場合は、ラムダ式をPrivilegedAction<Void>型として明示的にキャストします。doPrivilegedメソッドには、PrivilegedExceptionAction型のオブジェクトを取るバージョンも存在します。例外の処理を参照してください。

PrivilegedActionは、型パラメータで指定された型の値を返すrunという名前の単一抽象メソッドを持つ関数型インタフェースです。

この例では、runメソッドの戻り値は無視されています。特権コードを構成する実際の要素によっては、内部クラスの処理のために若干の変更が必要になる場合があります。たとえば、特権コードが例外をスローしたりローカル変数にアクセスしたりする場合は、後で説明する変更が必要になります。

特権構造の使用には細心の注意を払って、特権コード・セクションをできるだけ小さくしてください。つまり、runメソッドの中のコードは、特権付きで実行する必要のあるコードのみに制限し、より一般的な処理はrunメソッドの外側で行うようにします。また、doPrivilegedの呼出しは、特権を有効にする必要があるコードの中で行うようにします。それ自体がdoPrivilegedを呼び出すようなユーティリティ・クラスは、セキュリティ・ホールとなる危険があるため、作成しないでください。前の例で示したように、直接呼び出さなくても、PrivilegedActionクラスのためのユーティリティ・クラスを記述できます。Javaプログラミング言語のセキュア・コーディング・ガイドラインのガイドライン9-3: java.security.AccessController.doPrivilegedの安全な呼出しを参照してください。

例1-14 特権ブロックのサンプル・コード次のコードは、次の3つの方法で特権コードを指定します。

- インタフェースPrivilegedActionを実装するクラスで。

- 匿名クラスで。

- ラムダ式で。

import java.security.*;

public class NoReturnNoException {

class MyAction implements PrivilegedAction<Void> {
public Void run() {
// Privileged code goes here, for example:
System.loadLibrary("awt");
return null; // nothing to return
}
}

public void somemethod() {

MyAction mya = new MyAction();

// Become privileged:
AccessController.doPrivileged(mya);

// Anonymous class
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// Privileged code goes here, for example:
System.loadLibrary("awt");
return null; // nothing to return
}
});

// Lambda expression
AccessController.doPrivileged((PrivilegedAction<Void>)
() -> {
// Privileged code goes here, for example:
System.loadLibrary("awt");
return null; // nothing to return
}
);
}

public static void main(String... args) {
NoReturnNoException myApplication = new NoReturnNoException();
myApplication.somemethod();
}
}

値を返す場合

値を返すサンプル・コードを示します。

次に、値を返す必要がある場合の例を示します。

System.out.println(
AccessController.doPrivileged((PrivilegedAction<String>)
() -> System.getProperty("user.name")
)
);

ローカル変数にアクセスする場合

ラムダ式または匿名の内部クラスを使う場合は、アクセスするローカル変数がすべてfinalまたは事実上のfinalである必要があります。

次に例を示します。

String lib = "awt";
AccessController.doPrivileged((PrivilegedAction<Void>)
() -> {
System.loadLibrary(lib);
return null; // nothing to return
}
);

AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Object run() {
System.loadLibrary(lib);
return null; // nothing to return
}
});

変数libは、その値が変更されていないため、事実上のfinalです。たとえば、変数libの宣言の後に次の代入文を追加するとします。

lib = "swing";

コンパイラは、ラムダ式と匿名クラスの両方にあるSystem.loadLibraryの呼出しを検出したときに、次のエラーを生成します。

- error: local variables referenced from a lambda expression must be final or effectively final

- error: local variables referenced from an inner class must be final or effectively final

詳細は、「ローカル・クラス」の「包含クラスのメンバーへのアクセス」を参照してください。

繰返し設定されるなどの理由で、既存の変数を事実上のfinalとして宣言できない場合は、doPrivilegedメソッドを呼び出す直前に新しいfinal変数を作成し、その変数を他方の変数と等しいものとして設定します。次に例を示します。

String lib;

// The lib variable gets set multiple times so you can't make it
// effectively final.

// Create a final String that you can use inside of the run method
final String fLib = lib;

AccessController.doPrivileged((PrivilegedAction<Void>)
() -> {
System.loadLibrary(fLib);
return null; // nothing to return
}
);

例外の処理

runメソッド内で実行された処理によってチェック例外(メソッドのthrows節にリストする必要がある例外)がスローされた場合は、PrivilegedActionインタフェースではなく、PrivilegedExceptionActionインタフェースを使用する必要があります。

例1-15 例外処理の例

チェック例外がrunメソッドの実行中にスローされる場合は、この例で示したようにPrivilegedActionExceptionのラッパー例外に置いてからスローし、記述したコードを使用してキャッチします。

public void processSomefile() throws IOException {

try {
Path path = FileSystems.getDefault().getPath("somefile");
BufferedReader br = AccessController.doPrivileged(
(PrivilegedExceptionAction<BufferedReader>)
() -> Files.newBufferedReader(path)
);
// ... read from file and do something
} catch (PrivilegedActionException e) {

// e.getException() should be an instance of IOException
// as only checked exceptions will be wrapped in a
// PrivilegedActionException.
throw (IOException) e.getException();
}
}

特権のサブセットのアサーション

doPrivilegedのバリアントには3つのパラメータがあり、そのうちの1つは特権のサブセットを指定するために使用します。

JDK 8では、他のアクセス権をチェックするためのスタックの完全なトラバースを禁止せずにコード内でコードの特権のサブセットを表明できる、doPrivilegedのバリアントを使用できます。このdoPrivilegedのバリアントには3つのパラメータがあり、そのうちの1つはこの特権のサブセットを指定するために使用します。たとえば、次のコード例はシステム・プロパティを取得するための特権を表明しています。

// Returns the value of the specified property. All code
// is allowed to read the app.version and app.vendor
// properties.

public String getProperty(final String prop) {
return AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty(prop),
null,
new java.util.PropertyPermission("app.version", "read"),
new java.util.PropertyPermission("app.vendor", "read")
);
}

このバージョンのdoPrivilegedの1つ目のパラメータは、java.security.PrivilegedAction型です。この例では、1つ目のパラメータは関数型インタフェースPrivilegedActionを実装するラムダ式です。このインタフェースのrunメソッドは、パラメータpropで指定されたシステム・プロパティの値を返します。

このバージョンのdoPrivilegedの2つ目のパラメータは、AccessControlContext型です。場合によっては、異なるコンテキスト(ワーカー・スレッドなど)で追加のセキュリティ・チェックを実行する必要があります。AccessControlContext.getContextメソッドを使用して、特定の呼出しコンテキストからAccessControlContextインスタンスを取得できます。このパラメータに(この例のように)nullを指定すると、doPrivilegedの呼出し時に追加のセキュリティ・チェックが実行されません。

このバージョンのdoPrivilegedの3つ目のパラメータは、可変引数パラメータであるPermission...型です。つまり、1つ以上のPermissionパラメータを指定するか、またはPermission[]のようにしてPermissionオブジェクトの配列を指定できます。この例では、doPrivilegedを呼び出すことでapp.versionプロパティとapp.vendorプロパティを取得できます。

このdoPrivilegedの3つのパラメータ・バリアントは、最小特権のモードまたは追加特権のモードで使用できます。

最小特権

doPrivilegedメソッドの典型的な使用例は、現在のメソッドの呼出し側に必要なすべてのアクセス権がなくても、これを呼び出すことで、そのメソッドでアクセス権のチェックを必要とする1つ以上のアクションを実行できるようにする場合です。

たとえば、現在のメソッドが内部実装に固有の目的でファイルを開いたり、ネットワーク要求を実行したりする必要がある場合があります。

JDK 8より前のバージョンでは、doPrivilegedメソッドの呼出しには2つのパラメータしかありませんでした。これらは、呼出し側のメソッドに一時的な特権を付与し、アクセスをチェックするための通常の完全なスタックのトラバーサルを、必要なアクセス権がないクラスで定義されたメソッドに到達する可能性がある呼出しスタックまで継続せずに、そのクラスに到達したときに停止することで機能しました。通常、doPrivilegedを呼び出しているクラスは、そのコード・パスでは不要な追加のアクセス権を持っている可能性があり、それらは一部の呼出し側クラスにもない可能性があります。

通常、これらの追加権限は実行時に使用されません。doPrivilegedを使ってこれらを昇格させないことで、意図しないアクションを実行する可能性がある不正なコードの利用を防ぐことができます。これは特に、PrivilegedActionが通常より複雑な場合や、時間の経過とともに独立して発展する可能性があるクラスやパッケージの境界の外側にあるコードを呼び出す場合に当てはまります。

3つのパラメータを持つdoPrivilegedのバリアントは、必要とされる予定がないアクセス権を不必要に昇格しないため、一般により安全に使用できます。ただし、実行時の効率が低いため、単純なコード・パスやパフォーマンス重視のコード・パスでは使用されない場合があります。

追加特権

現在のメソッドをコーディングするときには、アクションを実行するために呼出し側のメソッドのアクセス権を一時的に拡張できます。

たとえば、フレームワークのI/O APIに、特定のデータ形式のファイルを開くための汎用メソッドが含まれる場合があります。このAPIは、通常のファイル・パス・パラメータを取り、それを使って基盤となるFileInputStreamを開くときに、呼出し側のコードのアクセス権を使用します。ただし、これによってすべての呼出し側が標準のデモ用サンプルを含む特別なディレクトリ内のデータ・ファイルを開くこともできるようになる可能性があります。

このAPIの呼出し側には、読取りアクセス用のFilePermissionを直接付与できます。ただし、呼出し側のコードのセキュリティ・ポリシーの更新が不便または不可能になる可能性があります。たとえば、呼出し側のコードがサンドボックス内で実行されるアプレットである可能性があります。

これを実装する1つの方法は、コード内で受け取ったパスをチェックし、それが特別なディレクトリ内のファイルを参照しているかどうかを判定することです。参照している場合は、doPrivilegedを呼び出してすべてのアクセス権を有効にしてから、PrivilegedActionの内部でファイルを開きます。ファイルが特別なディレクトリに含まれていない場合は、コード内でdoPrivilegedを使用せずにファイルを開きます。

この手法では、実装は要求されたファイル・パスを慎重に扱い、それが特別な共有ディレクトリを参照しているかどうかを判定する必要があります。doPrivilegedを呼び出す前にファイル・パスを正規化して、パスが特別なディレクトリ内のファイルを参照しているかどうかを判定する前に、すべての相対パスが処理されるように(およびuser.dirシステム・プロパティを読み取るためのアクセス権がチェックされるように)する必要があります。また、特別なディレクトリの外側に抜ける意図を持つ悪質な"../"パス要素を防止する必要もあります。

より簡単かつ適切な実装では、3つ目のパラメータを持つdoPrivilegedのバリアントを使用します。特別なディレクトリへの読取りアクセスを含むFilePermissionを3つ目のパラメータとして渡します。その後、PrivilegedActionの内部でファイルの操作を行います。この実装はより簡単で、セキュリティ上の欠陥を含む可能性がかなり低くなります。

特権コードを使う意味

コードを特権付きとしてマークすると、信頼できるコードは、それを呼び出しているコードが直接利用できるものより多くのリソースに、一時的にアクセスできるようになります。

JDKのインストール・ポリシーにより、特定のコード・ソースに基づくコードに対して許可されるアクセス権、つまりシステム・リソースに対するアクセスの種類が決まります。CodeSource型のコード・ソースは、基本的に、コードの場所(URL)と、コードへの署名に使われている非公開鍵に対応する公開鍵を含む証明書の参照(コードが署名されている場合)で、構成されています。

ポリシーは、Policyオブジェクトによって表されます。具体的には、Policyクラス(java.securityパッケージ内)内のabstractメソッドの実装を提供するPolicyサブクラスによって表されます。

Policyオブジェクトが使用するポリシー情報がどこに置かれるかは、Policyの実装によります。Policyのリファレンス実装では、ポリシー情報をポリシー構成ファイルから得ます。デフォルトのPolicyのリファレンス実装とそこで読み取られるポリシー・ファイルで使用する構文については、「Policyのデフォルト実装とポリシー・ファイルの構文」を参照してください。ポリシー・ツールを使用した(必須の構文を知る必要のない)ポリシー・ファイルの作成方法の詳細は、ポリシー・ツールを参照してください。

そのとき有効になっているセキュリティ・ポリシーでの決定に従って、CodeSourceインスタンスと、そのCodeSourceに基づくコードに対して付与されているアクセス権は、保護ドメインに包含されます。したがって、同じ鍵で署名された同じURLにあるクラスは一般に同じドメインに置かれ、クラスは1つの保護ドメインのみに所属します。(ただし、同じ鍵で署名され、同じURLにあっても、別のクラス・ローダー・インスタンスでロードされたクラスは、一般に別のドメインに置かれます。)同じアクセス権を与えられていても、別のコード・ソースが基になっているクラスは、別のドメインに属します。

現在、JDKとともに提供されるすべてのクラスには、ロード時にすべてのアクセス権が与えられます(これは将来のリリースで変更される可能性があります)。これらのクラスのほとんどは、固有のシステム・ドメインに置かれます。さらに、拡張クラス・ローダーは<java_home>/jre/lib/extディレクトリに含まれているJARファイルのコードを別々のドメインにロードしますが(これらのJARファイル内のコードには固有のURLがあるため)、これらのドメインはJDKとともに提供されるクラスのために予約された固有のシステム・ドメインとは独立しています。

アプレットまたはアプリケーションは、それぞれのコード・ソースによって決められるドメイン内で動作します。アプレット(またはセキュリティ・マネージャの下で動作しているアプリケーション)がセキュリティ保護された操作(ファイルの読み書きなど)を行うためには、その操作を行うためのアクセス権がアプレットまたはアプリケーションに与えられている必要があります。

より具体的には、リソースにアクセスしようとする場合は常に、その地点に至るまでに実行スレッドがたどってきたすべてのコードが、そのリソース・アクセスのためのアクセス権を持っている必要があります。ただし、スレッドの中に特権付きのマークが付けられたコードがある場合は除きます。つまり、複数の呼出し側のチェーンが存在する実行のスレッドで、アクセス制御のチェックが行われる場合を考えます。(保護ドメインの境界を横断する可能性のある複数のメソッドの呼出しのような場合です。)最後の呼出し側がAccessControllerのcheckPermissionメソッドを呼び出すとき、要求されたアクセスの許可または拒否を決定する基本的なアルゴリズムは、次のようになります。呼出しチェーンのいずれかの呼出し側のコードに、要求されたアクセス権がない場合、AccessControlExceptionがスローされます。ただし、このアクセス権を与えられているコードの呼出し側に特権付きのマークが付けられていて、この呼出し側からその後(直接または間接的に)呼び出されるすべての部分がこのアクセス権を持っている場合、この例外はスローされません。

注意:
AccessController.checkPermissionメソッドは、通常、checkという語で始まる特定のSecurityManagerメソッド(checkConnectなど)の呼出しによって、またはSecurityManager.checkPermissionメソッドによって間接的に呼び出されます。通常、これらのチェックはSecurityManagerがインストールされている場合にのみ行われます。AccessController.checkPermissionメソッドによってチェックされるコードは、最初にSystem.getSecurityManagerメソッドがnullを返すかどうかをチェックします。

コードを特権付きとしてマークすると、信頼できるコードは、それを呼び出しているコードが直接利用できるものより多くのリソースに、一時的にアクセスできるようになります。これは、次のような場合に必要になります。たとえば、あるアプリケーションがフォントが入ったファイルへの直接アクセスを許可されていないときに、ドキュメントを表示するシステム・ユーティリティがユーザーに代わってそのフォントを取得する必要がある場合などです。このシステム・ユーティリティは、フォントを取得するために特権付きになる必要があります。

リフレクション

doPrivilegedメソッドは、java.lang.reflect.Method.invokeを使用して反射的に呼び出すことができます。

このAPIとリフレクションの相互作用は、注意の必要な微妙な点です。doPrivilegedメソッドは、java.lang.reflect.Method.invokeメソッドを使ってリフレクションとして呼び出すことができます。この場合、特権モードで与えられる特権は、Method.invokeの特権ではなく、それによって呼び出されるリフレクションでないコードの特権です。このようにしないと、システム特権が誤って(または悪意を持って)ユーザー・コードに与えられてしまう危険があります。既存のAPIでリフレクションを使う場合にも、同様の要件が適用されます。

セキュリティのトラブルシューティング

セキュリティ・アクセスを監視するには、java.security.debugシステム・プロパティを設定します。これにより、実行中にどのトレース・メッセージを出力するかを決定します。

すべてのデバッグ・オプションのリストを表示するには、helpという設定値を使用します。

java -Djava.security.debug=help

注意:

複数のオプションを使用するには、オプションをカンマで区切ります。

JSSEでは、SSL/TLS/DTLSのトラブルシューティングのために動的なデバッグ・トレースもサポートされています。デバッグ・ユーティリティを参照してください。

次の表に、java.security.debugのオプションと各オプションの関連情報へのリンクを示します。

表1-10 java.security.debugのオプション

オプション

説明

関連情報

all

すべてのデバッグ・オプションを有効にします。

なし

access

AccessController.checkPermissionメソッドの結果をすべて出力します。

accessオプションとともに、次のオプションを使用できます。

1. stack: スタック・トレースを含む

2. domain: コンテキスト内のすべてのドメインをダンプする

3. failure: 例外をスローする前に、アクセス権を保持しないスタックおよびドメインをダンプする

stackおよびdomainオプションとともに、次のオプションを使用できます。

1. permission=<classname>: 指定されたアクセス権がチェックされている場合にのみ、出力をダンプする

2. codebase=<URL>: 指定されたコード・ベースがチェックされている場合にのみ、出力をダンプする

Java Development Kit (JDK)でのアクセス権

certpath

PKIX CertPathValidatorおよびCertPathBuilder実装のデバッグを有効にします。OCSPプロトコルのトレースを行う場合は、ocspオプションをcertpathオプションとともに使用します。OCSPリクエストおよびレスポンスのバイトの16進ダンプが表示されます。

PKIプログラマーズ・ガイドの概要

combiner

SubjectDomainCombinerのデバッグ

Java Development Kit (JDK)でのアクセス権

configfile

JAAS (Java Authentication and Authorization Service)構成ファイルのロード

Java認証・承認サービス(JAAS)リファレンス・ガイド

JAAS LoginユーティリティおよびJava GSS-APIを使用したセキュアなメッセージ交換

configparser

JAAS構成ファイルの解析

Java認証・承認サービス(JAAS)リファレンス・ガイド

JAAS LoginユーティリティおよびJava GSS-APIを使用したセキュアなメッセージ交換

gssloginconfig

Java GSS (Generic Security Services)ログイン構成ファイルのデバッグ

Java Generic Security Services: (Java GSS)およびKerberos

JAASおよびJava GSS-APIチュートリアル

javax.security.auth.login.Configuration: Configurationオブジェクトは、特定のアプリケーションで使用されるjavax.net.ssl.SSLEngineと、LoginModulesが呼び出される順番を指定します。

JAASログイン構成ファイル

Java SE認証、セキュアな通信、およびシングル・サインオンでの高度なセキュリティ・プログラミング

jar

JARファイルの検証

Javaチュートリアルの「Verifying Signed JAR Files」

jca

JCAエンジン・クラスのデバッグ

エンジン・クラスとアルゴリズム

keystore

Keystoreのデバッグ

キーストア

KeyStore

logincontext

LoginContextの結果

Java認証・承認サービス(JAAS)リファレンス・ガイド

JAAS LoginユーティリティおよびJava GSS-APIを使用したセキュアなメッセージ交換

pkcs11

PKCS11セッション・マネージャのデバッグ

PKCS#11リファレンス・ガイド

pkcs11keystore

PKCS11 KeyStoreのデバッグ

PKCS#11リファレンス・ガイド

pkcs12

PKCS12 KeyStoreのデバッグ

なし

policy

ポリシー・ファイルによるアクセス権のロードと付与

Javaチュートリアルの「Set up the Policy File to Grant the Required Permissions」(Controlling Applications)

デフォルトのPolicyの実装とポリシー・ファイルの構文

provider

セキュリティ・プロバイダのデバッグ
次のオプションは、プロバイダ・オプションとともに使用できます。

engine=<engines>: この出力は、指定されたJCAエンジン・リストのためにのみ表示されます。

<engines>に対してサポートされている値は次のとおりです。

- 暗号

- KeyAgreement

- KeyGenerator

- KeyPairGenerator

- KeyStore

- Mac

- MessageDigest

- SecureRandom

- Signature

Java暗号化アーキテクチャ(JCA)リファレンス・ガイド

scl

SecureClassLoaderによって割り当てられるアクセス権

Java Development Kit (JDK)でのアクセス権

securerandom

SecureRandomのデバッグ

SecureRandomクラス

sunpkcs11

SunPKCS11プロバイダのデバッグ

PKCS#11リファレンス・ガイド

ts

Timestampingのデバッグ

なし

2 Java暗号化アーキテクチャ(JCA)リファレンス・ガイド

Java暗号化アーキテクチャ(JCA)はプラットフォームの主要な部分であり、プロバイダ・アーキテクチャ、およびデジタル署名、メッセージ・ダイジェスト(ハッシュ)、証明書と証明書検証、暗号化(対称/非対称ブロック/ストリーム暗号)、鍵の生成と管理、安全な乱数生成などのための一連のAPIを含んでいます。

Java暗号化アーキテクチャの概要

Javaプラットフォームは、言語の安全性、暗号化、公開鍵インフラストラクチャ、認証、安全な通信、アクセス制御など、セキュリティに重点を置いています。

JCAはプラットフォームの主要な部分であり、「プロバイダ」アーキテクチャ、およびデジタル署名、メッセージ・ダイジェスト(ハッシュ)、証明書と証明書検証、暗号化(対称/非対称ブロック/ストリーム暗号)、鍵の生成と管理、安全な乱数生成などのための一連のAPIが含まれています。これらのAPIによって、開発者はアプリケーション・コードにセキュリティを簡単に統合できます。アーキテクチャは、次の方針に基づいて設計されました。

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

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

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

JDKで使用できるその他の暗号化通信ライブラリはJCAプロバイダ・アーキテクチャを使用しますが、これらについては別途説明します。JSSEコンポーネントは、Secure Socket Layer (SSL)、Transport Layer Security (TLS)およびDatagram Transport Layer Security (DTLS)実装へのアクセスを提供します。Java Secure Socket Extension (JSSE)リファレンス・ガイドを参照してください。Java Generic Security Services (JGSS) (Kerberosを使用)のAPI、およびSimple Authentication and Security Layer (SASL)を使用して、通信するアプリケーション間でメッセージを安全に交換できます。Kerberosを使ったJava GSS-APIおよびJAASのチュートリアルおよびJava SASL APIプログラミングおよびデプロイメント・ガイドを参照してください。

用語に関する注記

- JDK 1.4より前は、JCEはバンドルされていない製品であり、そのため、JCAとJCEは常に別個のコンポーネントとして参照されていました。現在JCEはJDKにバンドルされているため、区別は明確でなくなっています。JCEはJCAと同じアーキテクチャを使用するため、JCEはJCAの一部として考えることができます。

- JDKのJCAには、次の2つのソフトウェア・コンポーネントが含まれます。

- プロバイダが実装を提供可能な暗号化サービスを定義およびサポートするフレームワーク。このフレームワークには、java.security、javax.crypto、javax.crypto.spec、javax.crypto.interfacesなどのパッケージが含まれます。

- Sun、SunRsaSign、SunJCEなどの実際のプロバイダ。実際の暗号化実装が含まれます。

特定のJCAプロバイダを取り上げる場合は、常にプロバイダの名前で明示的に指定されます。

警告:
JCAを使用すると、アプリケーションへのセキュリティ機能の組込みが簡単になります。ただし、このドキュメントでは、APIの説明に必要な概念の基本的な紹介以外、セキュリティ/暗号化の理論については説明しません。このドキュメントでは、特定のアルゴリズムの長所と短所やプロトコルの設計についても説明しません。暗号化は高度なトピックであり、これらのツールを最適に使用するには、信頼できる、可能なかぎり最近のリファレンスを参照するようにしてください。
実行している内容とその理由を常に理解している必要があります。ランダム・コードを単純にコピーし、それが用途を満たす筋書きを完全に解決してくれるとは考えないでください。間違ったツールまたはアルゴリズムが選択されたため、セキュリティまたはパフォーマンスに重大な問題があるアプリケーションが数多く配備されています。

JCAの設計の原則

JCAは、次の方針に基づいて設計されました。

- 実装の独立性と相互操作性

- アルゴリズムの独立性と拡張性

実装の独立性とアルゴリズムの独立性は相補関係にあります。そのため、デジタル署名やメッセージ・ダイジェストなどの暗号化サービスを、実装の詳細やこれらの概念の基になるアルゴリズムを意識しないで使用できます。完全なアルゴリズムの独立性は不可能ですが、JCAは標準化された、アルゴリズム固有のAPIを提供します。実装の独立性が望ましくない場合、JCAでは、開発者が特定の実装を指定できます。

アルゴリズムの独立性は、暗号化「エンジン」(サービス)の型を定義し、これらの暗号化エンジンの機能を提供するクラスを定義することにより実現しています。これらのクラスは、エンジン・クラスと呼ばれ、例としては、MessageDigest、Signature、KeyFactory、KeyPairGenerator、およびCipherクラスがあります。

実装の独立性は、「プロバイダ」ベースのアーキテクチャを使って実現されます。暗号化サービス・プロバイダ(CSP) (プロバイダとも呼ばれる)とは(暗号化サービス・プロバイダを参照)、デジタル署名アルゴリズム、メッセージ・ダイジェスト・アルゴリズム、鍵変換サービスなどの1つ以上の暗号化サービスを実装するパッケージやパッケージ・セットを意味します。プログラムは単に、特定のサービス(たとえばDSA署名アルゴリズム)を実行する特定の型のオブジェクト(たとえばSignatureオブジェクト)を要求するだけで、インストールされているプロバイダの1つから実装を獲得できます。必要に応じて、プログラムは特定プロバイダから実装を要求することもできます。たとえば、より高速で安全性の高いバージョンが利用できるときは、プロバイダをアプリケーションに対して透過的に更新できます。

実装の相互操作性とは、さまざまな実装が互いに機能でき、互いの鍵を使ったり、互いの署名を検証できることを意味します。つまり、1つのプロバイダが生成した同じアルゴリズムや鍵を別のプロバイダが使えたり、あるプロバイダが生成した署名を別のプロバイダが検証できるということです。

アルゴリズムの拡張性とは、サポートされるエンジン・クラスの1つに当てはまる新規アルゴリズムを容易に追加できることを意味します。

プロバイダ・アーキテクチャ

プロバイダは、通知されている暗号化アルゴリズムの固定実装を提供するパッケージ(または、パッケージのセット)を含みます。

暗号化サービス・プロバイダ

java.security.Providerは、すべてのセキュリティ・プロバイダの基底クラスです。各CSPはこのクラスのインスタンスを含みます。インスタンスはプロバイダの名前を含み、実装するセキュリティ・サービス/アルゴリズムをすべて一覧表示します。特定のアルゴリズムのインスタンスが必要な場合、JCAフレームワークはプロバイダのデータベースに問い合わせ、一致するものが見つかった場合はそのインスタンスが作成されます。

プロバイダは、通知されている暗号化アルゴリズムの固定実装を提供するパッケージ(または、パッケージのセット)を含みます。各JDKのインストールでは、1つ以上のプロバイダがインストールされ、デフォルトで構成されます。追加のプロバイダは、静的または動的に追加できます。クライアントは実行環境を構成し、プロバイダの優先順位を指定できます。優先順位とは、特定プロバイダの指定がない場合に、要求されたサービスに関して検索されるプロバイダの順位です。

JCAを使用するには、アプリケーションは単に特定の型のオブジェクト(たとえばMessageDigest)と特定のアルゴリズムまたはサービス(たとえばSHA-256アルゴリズム)を要求し、インストールされているプロバイダのうちの1つから実装を獲得します。たとえば、次の文は、インストールされているプロバイダからのSHA-256メッセージ・ダイジェストを要求します。

md = MessageDigest.getInstance("SHA-256");

あるいは、特定プロバイダのオブジェクトを要求することもできます。各プロバイダは、それぞれの参照名を持ちます。たとえば、次の文は、ProviderCという名前のプロバイダからのSHA-256メッセージ・ダイジェストを要求します。

md = MessageDigest.getInstance("SHA-256", "ProviderC");

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

図2-1 プロバイダ指定なしでのSHA-256メッセージ・ダイジェスト実装の要求

図2-2 ProviderCによるSHA-256メッセージ・ダイジェストの要求

JDKでの暗号化実装は、様々なプロバイダ(Sun、SunJSSE、SunJCE、SunRsaSign)によって配布されます。これは主に歴史的な理由によりますが、それらが提供する機能およびアルゴリズムの型によるところもあります。その他のJava実行環境は、必ずしもこれらのプロバイダを含みません。したがって、特定のプロバイダが使用可能になることがわかっている場合を除き、アプリケーションはプロバイダ固有の実装を要求しないようにする必要があります。

JCAには、インストールされているプロバイダおよびそれらがサポートするサービスについて、ユーザーが問い合わせることができるAPIセットがあります。

このアーキテクチャを使用すると、エンド・ユーザーがプロバイダを追加することも簡単になります。多くのサード・パーティのプロバイダ実装がすでに使用可能です。プロバイダの記述、インストール、および登録の方法の詳細は、Providerクラスを参照してください。

プロバイダの実際の実装方法

アルゴリズムの独立性は、サービスの型にアクセスするためにすべてのアプリケーションが使用する、汎用で高レベルのAPI (Application Programming Interface)を定義することによって実現されます。実装の独立性は、すべてのプロバイダ実装を、明確に定義されたインタフェースに準拠させることによって実現されます。したがって、エンジン・クラスのインスタンスは、同じメソッド署名を持つ実装クラスに基づいています。アプリケーション呼出しはエンジン・クラスによって渡され、基盤となる実装に伝送されます。実装は要求を処理し、正しい結果を返します。

各エンジン・クラス内のアプリケーションAPIメソッドは、対応するSPI (Service Provider Interface)を実装するクラスによってプロバイダの実装に渡されます。つまり、各エンジン・クラスに対応する抽象SPIクラスが存在し、この抽象SPIクラスによって各暗号化サービス・プロバイダのアルゴリズムが実装する必要があるメソッドが定義されます。SPIクラスの名前は、対応するエンジン・クラス名のあとにSpiを追加した名前になります。たとえばSignatureエンジン・クラスは、デジタル署名アルゴリズムの機能へのアクセスを提供します。実際のプロバイダ実装は、SignatureSpiのサブクラス内で提供されます。アプリケーションは各エンジン・クラスのAPIメソッドを呼び出し、APIメソッドは実際の実装内のSPIメソッドを呼び出します。

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

APIの各エンジン・クラスについては、エンジン・クラス特有のgetInstance() ファクトリ・メソッドを呼び出すことによって実装インスタンスが要求され、インスタンスが生成されます。ファクトリ・メソッドは、クラスのインスタンスを返すstaticメソッドです。エンジン・クラスは、前述のフレームワーク・プロバイダ選択メカニズムを使用して実際の基盤となる実装(SPI)を取得し、実際のエンジン・オブジェクトを作成します。エンジン・クラスの各インスタンスは、SPIオブジェクトと呼ばれる対応するSPIクラスのインスタンスを、privateフィールドとしてカプセル化します。APIオブジェクトのすべてのAPIメソッドは、finalとして宣言し、それらを実装することによって、カプセル化されるSPIオブジェクトの対応するSPIメソッドが呼び出されます。

これをより理解するために、例2-1および図2-3を確認してください。

例2-1 エンジン・クラスのインスタンスの取得のサンプル・コード

import javax.crypto.*;

Cipher c = Cipher.getInstance("AES");
c.init(ENCRYPT_MODE, key);

図2-3 アプリケーションがAES Cipherインスタンスを取得する

ここで、アプリケーションは「AES」javax.crypto.Cipherインスタンスを必要とし、使用するプロバイダは問題となりません。アプリケーションはCipherエンジン・クラスのgetInstance()ファクトリ・メソッドを呼び出し、ファクトリ・メソッドはJCAフレームワークに「AES」をサポートする最初のプロバイダ・インスタンスを見つけることを要求します。フレームワークはインストールされている各プロバイダに問い合わせ、Providerクラスのプロバイダのインスタンスを取得します。Providerクラスは、使用可能なアルゴリズムのデータベースです。フレームワークは各プロバイダを検索し、最終的にCSP3で適切なエントリを見つけます。このデータベース・エントリは、CipherSpiを拡張する実装クラスcom.foo.AESCipherを指定するため、Cipherエンジン・クラスによる使用に適しています。com.foo.AESCipherのインスタンスが作成され、javax.crypto.Cipherの新しく作成されるインスタンスにカプセル化され、それがアプリケーションに返されます。アプリケーションがCipherインスタンス上でinit()操作を実行すると、Cipherエンジン・クラスはその要求をcom.foo.AESCipherクラス内の対応するengineInit()バッキング・メソッドに渡します。

Javaセキュリティ標準アルゴリズム名仕様には、Java環境用に定義されている標準名がリストされています。ほかのサード・パーティ・プロバイダが、これらのサービスまたは追加サービスの独自の実装を定義している場合があります。

キーストア

「キーストア」と呼ばれるデータベースは、鍵および証明書のリポジトリを管理するために使用できます。キーストアは、認証、暗号化、または署名のためにデータが必要なアプリケーションで利用できます。

アプリケーションは、KeyStoreクラスの実装を経由してキーストアにアクセスできます。キーストア・クラスの実装は、java.securityパッケージ内にあります。JDK 9の時点では、推奨されるデフォルト・キーストア・タイプ(形式)はpkcs12であり、これは、RSA PKCS12 Personal Information Exchange Syntax Standardに基づいています。以前は、デフォルトのキーストア・タイプは、独自仕様の形式であるjksでした。独自の代替キーストア形式であるjceksや、RSA PKCS11 Standardに基づいた、ハードウェア・セキュリティ・モジュールやスマートカードなどの暗号化トークンへのアクセスがサポートされているpkcs11など、その他のキーストア形式も使用できます。

アプリケーションは、前述の同じプロバイダ・メカニズムを使用して、様々なプロバイダから様々なキーストア実装を選択できます。鍵の管理を参照してください。

エンジン・クラスとアルゴリズム

エンジン・クラスは、特定の暗号化アルゴリズムまたはプロバイダに依存しない、特定の型の暗号化サービスへのインタフェースを提供します。

エンジンでは、次のいずれかが提供されます。

- 暗号化操作(暗号化、デジタル署名、メッセージ・ダイジェストなど)

- 暗号化データ(鍵およびアルゴリズム・パラメータ)のジェネレータまたはコンバータ

- 暗号化データをカプセル化し、上位の抽象レイヤーで使用できるオブジェクト(キーストアまたは証明書)。

次のエンジン・クラスが使用可能です。

- SecureRandom: 乱数または擬似乱数の生成に使用します。

- MessageDigest: 指定データのメッセージ・ダイジェスト(ハッシュ)の計算に使います。

- Signature: 鍵によって初期化されます。データの署名およびデジタル署名の検証に使用します。

- Cipher: 鍵によって初期化されます。データの暗号化/復号化に使用します。対称バルク暗号化(例: AES)、非対称暗号化(例: RSA)、パスワードベース暗号化(例: PBE)など、様々なタイプのアルゴリズムがあります。

- メッセージ認証コード(MAC): MessageDigestと同様にこれらもハッシュ値を生成しますが、メッセージの整合性を保護するために最初に鍵によって初期化されます。

- KeyFactory: Key型の既存の不透明な暗号化鍵を「鍵仕様」(基本の鍵データの透明な表現)に変換したり、その逆の変換を行うために使用します。

- SecretKeyFactory: SecretKey型の既存の不透明な暗号化鍵を「鍵仕様」(基本の鍵データの透明な表現)に変換したり、その逆の変換を行うために使用します。SecretKeyFactoryは、秘密(対称)鍵のみを作成する特殊なKeyFactoryです。

- KeyPairGenerator: 指定のアルゴリズムでの使用に適合した、新しい公開鍵、非公開鍵ペアの生成に使用します。

- KeyGenerator: 指定のアルゴリズムで使用するための、新しい秘密鍵の生成に使用します。

- KeyAgreement: 特定の暗号化操作に使用する特定の鍵の合意および確立のために、複数のパーティによって使用されます。

- AlgorithmParameters: パラメータのエンコードおよびデコードなど、特定のアルゴリズムのパラメータの格納に使用します。

- AlgorithmParameterGenerator: 特定のアルゴリズムに適したアルゴリズム・パラメータ・セットの生成に使用します。

- KeyStore: キーストアの作成および管理に使用します。キーストアは、鍵のデータベースです。キーストア内の非公開鍵には、それらの鍵に関連付けられた証明書チェーンがあります。証明書チェーンは、対応する公開鍵を認証します。また、キーストアには、信頼できるエンティティからの証明書も格納されています。

- CertificateFactory: 公開鍵の証明書および証明書失効リスト(CRL)の作成に使用します。

- CertPathBuilder: 証明書チェーン(証明書パスとも呼ばれる)の構築に使用します。

- CertPathValidator: 証明書チェーンの検証に使います。

- CertStore: リポジトリからCertificateとCRLを取得するために使用します。

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

コア・クラスとインタフェース

次に、JCAで提供されるコア・クラスとインタフェースを示します。

- ProviderおよびSecurity

- SecureRandom、MessageDigest、Signature、Cipher、Mac、KeyFactory、SecretKeyFactory、KeyPairGenerator、KeyGenerator、KeyAgreement、AlgorithmParameter、AlgorithmParameterGenerator、KeyStore、CertificateFactoryおよびエンジン

- Key Interface、KeyPair

- AlgorithmParameterSpec Interface、AlgorithmParameters、AlgorithmParameterGenerator、およびjava.security.specパッケージとjavax.crypto.specパッケージ内のアルゴリズム・パラメータ仕様インタフェースおよびクラス

- KeySpec Interface、EncodedKeySpec、PKCS8EncodedKeySpecおよびX509EncodedKeySpec

- SecretKeyFactory、KeyFactory、KeyPairGenerator、KeyGenerator、KeyAgreementおよびKeyStore

注意:
Java PKIプログラマーズ・ガイドでCertPathBuilder、CertPathValidatorおよびCertStoreエンジン・クラスを参照してください。

このガイドでは、もっとも有用な高レベルのクラスについて最初に説明し(Provider、Security、SecureRandom、MessageDigest、Signature、Cipher、およびMac)、次にさまざまなサポート・クラスについて説明します。現時点では、鍵(公開、非公開、および秘密)はさまざまなJCAクラスによって生成および表現され、高レベルのクラスによってその操作の一部として使用されるという説明だけで十分です。

このセクションでは、各クラスおよびインタフェースのメイン・メソッドの署名を示します。対応するコード例の項に、これらのクラス(MessageDigest、Signature、KeyPairGenerator、SecureRandom、KeyFactoryおよび鍵仕様クラス)の例があります。

関連するSecurity APIパッケージの完全なリファレンス・ドキュメントは、次のパッケージのサマリーにあります。

- java.security

- javax.crypto

- java.security.cert

- java.security.spec

- javax.crypto.spec

- java.security.interfaces

- javax.crypto.interfaces

Providerクラス

「暗号化サービス・プロバイダ」(このドキュメントでは「プロバイダ」とも呼ばれる)とは、JDK Security APIの暗号化機能のサブセットの固定実装を提供するパッケージまたはパッケージ・セットです。Providerクラスは、このようなパッケージやパッケージ・セットへのインタフェースです。プロバイダ名、バージョン番号、その他の情報にアクセスするためのメソッドを備えています。Providerクラスを使用すると、暗号化サービスの実装を登録するだけでなく、その他のセキュリティ・サービスの実装を登録することもできます。その他のセキュリティ・サービスは、JDK Security APIまたはその拡張機能の1つの一部として定義されている場合があります。

暗号化サービスの実装を提供するには、エンティティ(開発グループなど)は実装コードを作成し、Providerクラスのサブクラスを生成します。Providerサブクラスのコンストラクタは、プロバイダが実装したサービスを検索するためにJDK Security APIが使用する各種プロパティの値を設定します。つまり、サブクラスは、サービスを実装するクラス名を指定します。

図2-4 Providerクラス

プロバイダ・パッケージが実装できるサービスには、様々な種類があります。エンジン・クラスとアルゴリズムを参照してください。

実装が違うと、特徴も違ってくる場合があります。ソフトウェア・ベースのものもあれば、ハードウェア・ベースのものもあります。プラットフォーム独立のものもあれば、プラットフォーム固有のものもあります。また、レビューや評価用に使えるプロバイダ・コードもあれば、使えないものもあります。JCAでは、エンド・ユーザーと開発者の双方が独自のニーズを決定できます。

エンド・ユーザーのニーズに合った暗号化実装のインストール方法、および開発者のニーズに合った実装の要求方法について説明されています。

注意:
プロバイダを実装するには、プロバイダの実装および統合までのステップを参照してください。

プロバイダ実装の要求および獲得方法

このAPIのエンジン・クラス(エンジン・クラスとアルゴリズムを参照)ごとに、実装インスタンスが要求され、そのエンジン・クラスのgetInstanceメソッドの1つを呼び出してインスタンスが生成されます。このときに、希望するアルゴリズムの名前、および希望する実装を含むプロバイダ(またはProviderクラス)の名前(オプション)を指定します。

static EngineClassName getInstance(String algorithm)
throws NoSuchAlgorithmException

static EngineClassName getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException

static EngineClassName getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException

説明は次のとおりです。

EngineClassName

は、希望するエンジン・タイプ(MessageDigest/Cipher/など)です。次に例を示します。

MessageDigest md = MessageDigest.getInstance("SHA-256");
KeyAgreement ka = KeyAgreement.getInstance("DH", "SunJCE");

これらはそれぞれ、SHA-256 MessageDigestオブジェクトおよびDH KeyAgreementオブジェクトのインスタンスを返します。

Javaセキュリティ標準アルゴリズム名には、Java環境で使用するために標準化されている名前のリストがあります。一部のプロバイダには、同じアルゴリズムを参照する別名がある場合もあります。たとえば、SHA256アルゴリズムは、SHA-256と呼ばれる場合があります。必ずしもすべてのプロバイダが同じようにアルゴリズム名の別名を持つわけではないため、アプリケーションは別名ではなく標準名を使用する必要があります。

注意:
アルゴリズム名の大文字と小文字は区別されません。たとえば、次の呼出しはすべて等価とみなされます。

MessageDigest.getInstance("SHA256")
MessageDigest.getInstance("sha256")
MessageDigest.getInstance("sHa256")

プロバイダを指定しない場合、getInstanceは、指定されたアルゴリズムに関連する、要求された暗号化サービスの実装に対応する登録プロバイダを検索します。どのJava仮想マシン(JVM)でも、プロバイダは指定の「優先順位」でインストールされます。優先順位とは、特定のプロバイダの指定がない場合に、プロバイダ・リストが検索される順位です。(プロバイダのインストールを参照。)たとえば、JVMに、PROVIDER_1とPROVIDER_2という2つのプロバイダがインストールされているとします。次のようになっていると仮定します。

- PROVIDER_1はSHA-256およびDESedeを実装。PROVIDER_1の優先順位が1 (最高の優先順位)。

- PROVIDER_2はSHA256withDSA、SHA-256、RC5およびRSAを実装。PROVIDER_2の優先順位は2。

ここで、3つのシナリオについて見てみます。

1. SHA-256実装を検索するとする。どちらのプロバイダもこの実装を供給する。PROVIDER_1が最高の優先順位を持ち最初に検索されるので、PROVIDER_1実装が返される。

2. SHA256withDSA署名アルゴリズムを検索する場合、最初にPROVIDER_1が検索される。実装が見つからないので次にPROVIDER_2が検索される。実装が見つかるので、このプロバイダが返される。

3. SHA256withRSA署名アルゴリズムを検索するとする。これを実装するプロバイダはインストールされていないので、NoSuchAlgorithmExceptionがスローされる。

プロバイダ引数を持つgetInstanceメソッドは、必要なアルゴリズムを提供するプロバイダを指定したい開発者のためのものです。たとえば、連邦証明書を受け取ったプロバイダ実装を連邦局が使いたいとします。PROVIDER_1のSHA256withDSA実装はこの証明書を受け取っていないのに対し、PROVIDER_2のDSA実装がこれを受け取っているとします。

連邦局のプログラムは次の呼出しを実行します。PROVIDER_2が認証された実装を持つため、これを指定します。

Signature dsa = Signature.getInstance("SHA256withDSA", "PROVIDER_2");

この場合、PROVIDER_2がインストールされていないと、インストールされている別のプロバイダが要求アルゴリズムを実装している場合であっても、NoSuchProviderExceptionがスローされます。

プログラムは、(SecurityクラスのクラスのgetProvidersメソッドを使用して)インストールされているすべてのプロバイダのリストを獲得し、リストからいずれかを選択することもできます。

注意:
汎用アプリケーションは、特定のプロバイダから暗号化サービスを要求しないようにする必要があります。そうでない場合、アプリケーションは特定のプロバイダに結び付けられることになり、そのプロバイダはほかのJava実装で使用できない可能性があります。特定の要求されたプロバイダよりも優先順位が高い、使用可能な最適化されたプロバイダ(たとえば、PKCS11によるハードウェア・アクセラレータまたはMicrosoftのMSCAPIなどのネイティブOS実装)を利用できない場合もあります。

プロバイダのインストール

暗号化プロバイダを使用するには、最初にインストールし、次に静的または動的に登録する必要があります。さまざまなSunプロバイダがこのリリースに同梱されており(SUN、SunJCE、SunJSSE、SunRsaSignなど)、すでにインストールおよび登録されています。以降のセクションでは、追加プロバイダのインストールおよび登録方法について説明します。

すべてのJDKプロバイダは、すでにインストールされ登録されています。ただし、サードパーティ製プロバイダが必要な場合は、クラス・パスまたはモジュール・パスへのプロバイダの追加、プロバイダの登録(静的または動的)、必要なアクセス権の追加の方法の詳細を、プロバイダの実装および統合までのステップのステップ8: テストの準備で参照してください。

Providerクラス・メソッド

各Providerクラス・インタフェースは、名前(現時点では大文字小文字を区別する)、バージョン番号、およびプロバイダとそのサービスの文字列記述を持ちます。

次のメソッドを呼び出して、この情報についてProviderのインスタンスを照会できます。

public String getName()
public double getVersion()
public String getInfo()

Securityクラス

Securityクラスは、インストールされているプロバイダおよびセキュリティに関するプロパティを管理します。このクラスに含まれるのは、インスタンスが生成されないstaticメソッドだけです。プロバイダの追加または削除、およびSecurityプロパティの設定用メソッドは、信頼できるプログラムによってしか実行できません。現時点での「信頼できるプログラム」は、次のどれかです。

- セキュリティ・マネージャの下で動作していないローカル・アプリケーション。

- 指定されたメソッドを実行する権限のあるアプレットまたはアプリケーション(次を参照)。

あるコードが、実行しようとしているアクション(プロバイダの追加など)を行うことができるような、信頼できるコードであると判断するには、その特定のアクションを行う適切な権限がそのアプレットに与えられている必要があります。インストールしたJDKのポリシー構成ファイルでは、指定されたコード・ソースからのコードに対し、どのようなアクセス権(システム・リソースへのアクセスの種類)を与えるかを指定します。(以降の説明、およびデフォルトのPolicyの実装とポリシー・ファイルの構文とJavaセキュリティ・アーキテクチャ仕様を参照。)

実行されるコードは、常に、特定の「コード・ソース」から来ると考えられます。コード・ソースには、そのコードの元の場所(URL)だけでなく、コードの署名に使用された非公開鍵に対応する任意の公開鍵への参照も含まれています。コード・ソース内の公開鍵は、ユーザーのものから別名(記号名)で参照します。

ポリシー構成ファイル内では、コード・ソースは、コード・ベース(URL)、および(signedByで始まる)別名の2つのコンポーネントで表されます。別名は、コードの署名の検証に使う必要のある公開鍵を含んだキーストアのエントリを識別します。

このようなファイル内の各「許可」文は、指定されたコード・ソースにアクセス権のセットを与え、許可されるアクションを指定します。

次に、ポリシー構成ファイルの例を示します。

grant codeBase "file:/home/sysadmin/", signedBy "sysadmin" {
permission java.security.SecurityPermission "insertProvider";
permission java.security.SecurityPermission "removeProvider";
permission java.security.SecurityPermission "putProviderProperty.*";
};

この構成ファイルは、ローカル・ファイル・システム上の/home/sysadmin/ディレクトリ内の署名されたJARファイルからロードされたコードが、プロバイダの追加または削除、あるいはプロバイダのプロパティを設定できることを指定します。(JARファイルの署名は、ユーザーのキーストア内の別名sysadminで参照する公開鍵を使用して検証できます)。

コード・ソースを指定するコンポーネントは、どちらも(または両方を)省略可能です。次に、codeBaseを省略した構成ファイルの例を示します。

grant signedBy "sysadmin" {
permission java.security.SecurityPermission "insertProvider.*";
permission java.security.SecurityPermission "removeProvider.*";
};

このポリシーが有効になっている場合は、ローカル・ファイル・システム上の/home/sysadmin/ディレクトリによって署名されたJARファイル内のコードで、プロバイダを追加または削除できます。コードに署名は必要ありません。

codeBaseとsignedByを両方とも省略した例を次に示します。

grant {
permission java.security.SecurityPermission "insertProvider.*";
permission java.security.SecurityPermission "removeProvider.*";
};

この例では、コード・ソースを指定するコンポーネントがどちらも省略されています。したがって、出所がどこか、署名が付いているか、だれの署名が付いているかに関係なく、どのコードでもプロバイダの追加と削除が行えます。この付与はセキュリティ・ホールを生む可能性があるため、絶対にお薦めしません。信頼されていないコードによってプロバイダがインストールされ、それにより適切に機能する実装に依存する後続のコードに影響する可能性があります。(たとえば、破壊行為を行うCipherオブジェクトが、受信した機密情報を取り込んで格納する場合があります。)

プロバイダの管理

次の表に、Securityクラス内のメソッドのうち、インストールされているProviderを照会するために使用できるメソッドと、実行時にプロバイダをインストールまたは削除するためのメソッドの要約を示します。

プロバイダの照会

メソッド

説明

static Provider[] getProviders()

インストールされているすべてのプロバイダを含む配列(つまり、各パッケージ・プロバイダのProviderサブクラス)を返します。配列内のProviderの順序は、優先順位に従います。

static Provider getProvider (String providerName)

providerNameというProviderを返します。このProviderが見つからないとnullを返します。

プロバイダの追加

メソッド

説明

static int addProvider(Provider provider)

インストールされたProviderのリストの末尾にProviderを追加します。Providerが追加された優先順位を返すか、すでにインストールされているためにProviderが追加されなかった場合は-1を返します。

static int insertProviderAt (Provider provider, int position)

指定された位置に新規のProviderを追加します。あるプロバイダが要求された場所にインストールされた場合は、それまでその場所にあったプロバイダ、およびpositionより後の位置のすべてのプロバイダの順位は、1つ後になり、一覧の最後尾に向かって1つ移動します。このメソッドは、Providerが追加された優先順位を返すか、すでにインストールされているためにProviderが追加されなかった場合は-1を返します。

プロバイダの削除

メソッド

説明

static void removeProvider(String name)

指定された名前のProviderを削除します。プロバイダがインストールされていない場合は、何もせず復帰します。指定されたプロバイダが削除されると、そのプロバイダよりもあとの位置にあるすべてのプロバイダの位置が1つ前になり、インストールされたプロバイダ一覧の先頭に向かって1つ移動します。

注意:
プロバイダの優先順位を変更する場合は、まず目的のプロバイダを削除してから、希望する優先順位の位置に挿入しなおす必要があります。

セキュリティ・プロパティ

Securityクラスは、システムに関するセキュリティ・プロパティのリストを保持します。これらのプロパティはSystemプロパティに似ていますが、セキュリティ関連です。これらのプロパティは、静的(<java-home>/conf/security/java.securityファイルを使用)または動的(APIを使用)に設定できます。security.provider.nセキュリティ・プロパティで静的にプロバイダを登録する例は、プロバイダの実装および統合までのステップのステップ8.1: プロバイダの構成を参照してください。プロパティを動的に設定する場合、信頼できるプログラムは次のメソッドを使用できます。

static String getProperty(String key)
static void setProperty(String key, String datum)

注意:
セキュリティ・プロバイダのリストは、VMの起動中に確立されます。したがって、前述のメソッドは、プロバイダ・リストを変更する場合に使用してください。

SecureRandomクラス

SecureRandomクラスは、初期シード値から疑似乱数列を生成する決定性アルゴリズムである、疑似乱数ジェネレータ(PRNG)にアクセスするか、ランダム性の発生源(たとえば、/dev/randomまたは実際の乱数ジェネレータ)を読み取ることで、暗号として強力な乱数を提供するエンジン・クラス(エンジン・クラスとアルゴリズムを参照)です。PRNGの一例は、NIST SP 800-90Ar1で示されているDeterministic Random Bits Generator (DRBG)です。実装の中には、真の乱数を生成するものもあれば、両方の技術の組み合わせを使用するものもあります。暗号として強力な乱数は、少なくとも、FIPS 140-2、暗号化モジュールのセキュリティ要件の第4.9.1項で示されている統計的乱数生成テストに適合しています。

すべてのJava SE実装は、java.security.Securityクラスのsecurerandom.strongAlgorithmsプロパティで提供される、もっとも強力な(もっともランダム性の高い)SecureRandomの実装を示す必要があります。この実装は、特に強力な乱数値が必要な場合に使用できます。

securerandom.drbg.configプロパティは、SUNプロバイダでDRBGのSecureRandom構成および実装を指定するために使用されます。securerandom.drbg.configは、java.security.Securityクラスのプロパティです。その他のDRBG実装でも、securerandom.drbg.configプロパティを使用できます。

図2-5 SecureRandomクラス

SecureRandomオブジェクトの作成

SecureRandomのインスタンスは、いくつかの方法で取得できます。

- すべてのJava SE実装は、引数なしのコンストラクタnew SecureRandom()を使用してデフォルトのSecureRandomを提供します。このコンストラクタは、登録されているセキュリティ・プロバイダのリスト内を、最も推奨されるプロバイダから順に確認し、その後、SecureRandom乱数ジェネレータ(RNG)アルゴリズムをサポートする最初のプロバイダから新しいSecureRandomオブジェクトを返します。どのプロバイダもRNGアルゴリズムをサポートしていない場合は、SUNプロバイダからSHA1PRNGを使用するSecureRandomオブジェクトを返します。

- SecureRandomの特定の実装を獲得するには、プロバイダ実装の要求および獲得方法のいずれかを使用します。

- java.security.Securityクラスのsecurerandom.strongAlgorithmsプロパティで定義される強力なSecureRandom実装を取得するには、getInstanceStrong()メソッドを使用します。このプロパティは、重要な値を生成するのに適したプラットフォーム実装をリストします。

SecureRandomオブジェクトのシードまたは再シード

SecureRandomオブジェクトは、getInstance()の呼出し後に次のsetSeedメソッドのいずれかを呼び出さないかぎり、ランダム・シードで初期化されます。

void setSeed(byte[] seed)
void setSeed(long seed)

最初のnextBytes呼出しの前にsetSeedを呼び出して、環境のランダム性を防ぐ必要があります。

SecureRandomオブジェクトによってもたらされるビットのランダム性は、シード・ビットのランダム性によって異なります。

SecureRandomオブジェクトは、setSeedメソッドまたはreseedメソッドのいずれかを使用していつでも再シードされる可能性があります。setSeedで指定されたシードは、既存のシードと置き換えられるのではなく、既存のシードに追加されます。したがって、呼出しを繰り返しても、ランダム性が減少しないことが保証されます。

SecureRandomオブジェクトの使用

ランダム・バイトを得るには、呼出し側は単純に任意の長さの配列を渡します。すると、この配列にランダム・バイトが入ります。

void nextBytes(byte[] bytes)

シード・バイトの生成

ほかの乱数ジェネレータにシードを入れる場合など、必要な場合は、generateSeedメソッドを呼び出して、与えられた数のシード・バイトを生成できます。

byte[] generateSeed(int numBytes)

MessageDigestクラス

MessageDigestクラスは、暗号として安全なメッセージ・ダイジェスト(SHA-256やSHA-512など)の機能を提供するように設計されているエンジン・クラス(エンジン・クラスとアルゴリズムを参照)です。安全な暗号化メッセージ・ダイジェストは、任意サイズの入力(バイト配列)を取り、固定サイズ出力を生成します。これをダイジェストまたはハッシュと言います。

図2-6 MessageDigestクラス

たとえば、SHA-256のアルゴリズムは32バイトのダイジェストを生成し、SHA-512のアルゴリズムは64バイトのダイジェストを生成します。

ダイジェストには次の2つの特徴があります。

- 計算によって、同じ値にハッシュされた2つのメッセージを検索できない。

- ダイジェストの生成に使った入力情報を明らかにしない。

メッセージ・ダイジェストを使い、一意で、信頼できるデータ識別子を生成します。データ識別子をデータの「チェックサム」または「デジタル指紋」と呼ぶこともあります。メッセージのうちの1ビットを変更するだけで、異なるダイジェスト値が生成されます。

メッセージ・ダイジェストには多くの用途があり、意図的かどうかにかかわらず、いつデータが変更されたかを判断できます。近年、一般的なアルゴリズムに弱点があるかどうかを判断するために大きな努力が払われていますが、結果はさまざまです。ダイジェスト・アルゴリズムを選択する場合は、最近のリファレンスを調べて、そのステータスと当面のタスクでの妥当性を判断する必要があります。

MessageDigestオブジェクトの作成

MessageDigestオブジェクトを作成する手順を示します。

- ダイジェストを計算するには、メッセージ・ダイジェスト・インスタンスを作成します。MessageDigestオブジェクトは、MessageDigestクラスのgetInstance()メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。
ファクトリ・メソッドは、初期化されたメッセージ・ダイジェスト・オブジェクトを返します。したがって、このあとで初期化を行う必要はありません。

メッセージ・ダイジェスト・オブジェクトのアップデート

Message Digestオブジェクトを更新する手順を示します。

- データのダイジェストを計算するには、初期化されたメッセージ・ダイジェスト・オブジェクトにそのデータを提供する必要があります。すべてを一度に、またはチャンクで入れることができます。次のいずれかのupdateメソッドを呼び出すことによって、メッセージ・ダイジェストに渡すことができます。

void update(byte input)
void update(byte[] input)
void update(byte[] input, int offset, int len)

ダイジェストの計算

様々なタイプのdigest()メソッドを使用してダイジェストを計算する手順を示します。

データ・チャンクは、updateメソッドを呼び出して提供する必要があります。メッセージ・ダイジェスト・オブジェクトのアップデートを参照してください。

- ダイジェストは、digestメソッドのいずれかを呼び出して計算されます。

byte[] digest()
byte[] digest(byte[] input)
int digest(byte[] buf, int offset, int len)

1. byte[] digest()メソッドは、計算されたダイジェストを返します。

2. byte[] digest(byte[] input)メソッドは、ダイジェストのバイト配列を返すdigest()を呼び出す前に、入力バイト配列で最後のupdate(input)を行います。

3. int digest(byte[] buf, int offset, int len)メソッドは、offsetから始まる提供バッファbuf内に算出ダイジェストを格納します。lenは、ダイジェストに割り当てられたbuf内のバイト数です。このメソッドは、実際にbuf内に格納されたバイト数を返します。バッファ内に十分な領域がない場合、メソッドは例外をスローします。

MessageDigestオブジェクトの計算を参照してください。

Signatureクラス

Signatureクラスは、SHA256withDSAやSHA512withRSAなどの暗号化デジタル署名アルゴリズムの機能を提供するように設計されているエンジン・クラス(エンジン・クラスとアルゴリズムを参照)です。安全な暗号化署名アルゴリズムは、任意サイズの入力と非公開鍵を取り、署名と呼ばれる比較的短い(固定サイズの場合もよくある)バイト文字列を生成します。このプロパティは次のとおりです。

- 公開鍵と非公開鍵のペアの所有者のみが署名を作成できる。計算によって、公開鍵およびいくつかの署名のみを保持するユーザーが非公開鍵を回復することはできない。

- 署名の生成に使う非公開鍵に対応する公開鍵が与えられる。これは入力の認証性および整合性を検証できる。

図2-7 Signatureクラス

Signatureオブジェクトは、非公開鍵での署名に対して初期化され、署名するデータを与えられます。結果の署名バイトは、通常は署名されたデータとともに保持されます。検証が必要な場合、別のSignatureオブジェクトが作成され、検証に対して初期化されて対応する公開鍵を与えられます。データおよび署名バイトはSignature (署名)オブジェクトに渡され、データと署名が一致した場合に、Signatureオブジェクトは成功を報告します。

署名はメッセージ・ダイジェストに似ていますが、提供する保護の種類の点で目的が大きく異なっています。実際、SHA256WithRSAなどのアルゴリズムは、メッセージ・ダイジェストSHA256を使用して最初に大きなデータ・セットを管理しやすい形式に圧縮し、次にRSAアルゴリズムで結果の32バイトのメッセージ・ダイジェストに署名します。

データの署名と検証の例は、生成された鍵を使った署名の生成および検証を参照してください。

Signatureオブジェクトの状態

Signatureオブジェクトはモデル・オブジェクトです。つまり、Signatureオブジェクトは、常に指定の状態にあり、この状態で1つの型の操作だけを実行できます。

状態は、個々のクラスで定義したfinal (ファイナル)整数の定数で表されます。

Signatureオブジェクトに可能な状態は、次の3つです。

- UNINITIALIZED

- SIGN

- VERIFY

Signatureオブジェクトがはじめて生成されるときは、UNINITIALIZEDの状態です。Signatureクラスは、状態をSIGNに変更するinitSignと、状態をVERIFYに変更するinitVerifyという2つの初期化メソッドを定義します。

Signatureオブジェクトの作成

署名を付けたり、検証したりするには、最初にSignatureのインスタンスを生成します。

Signatureオブジェクトは、Signature getInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。

Signatureオブジェクトの初期化

Signatureオブジェクトを使うには、最初に必ずこれを初期化します。初期化メソッドは、オブジェクトを署名用に使うか検証用に使うかに応じて異なります。

署名で使う場合、オブジェクトは最初に、署名を生成するエンティティの非公開鍵を使って初期化する必要があります。この初期化は、次のメソッドを呼び出して実行します。

final void initSign(PrivateKey privateKey)

このメソッドでは、SignatureオブジェクトはSIGN状態になります。Signatureオブジェクトを検証で使う場合は、最初に、署名を検証するエンティティの公開鍵を使って初期化する必要があります。この初期化は、次のいずれかのメソッドを呼び出して実行します。

final void initVerify(PublicKey publicKey)

final void initVerify(Certificate certificate)

このメソッドでは、SignatureオブジェクトはVERIFY状態になります。

Signatureオブジェクトによる署名

Signatureオブジェクトを署名用に初期化した場合(このオブジェクトがSIGN状態の場合)、署名を付けるデータをオブジェクトに入れることができます。これは、次のupdateメソッドのどれか1つを1回または複数回呼び出して実行します。

final void update(byte b)
final void update(byte[] data)
final void update(byte[] data, int off, int len)

署名を付けるデータがすべてSignatureオブジェクトに入るまで、updateメソッドを呼び出します。

署名を生成するには、signメソッドの1つを呼び出すだけです。

final byte[] sign()
final int sign(byte[] outbuf, int offset, int len)

最初のメソッドは、署名の結果をバイト配列で返します。2番目のメソッドは、オフセット位置offsetから開始する提供バッファoutbuf内に署名の結果を格納します。lenは、署名に割り当てられたoutbuf内のバイト数です。このメソッドは、実際に格納されたバイト数を返します。

署名のエンコードは、アルゴリズムに依存します。Java暗号化アーキテクチャでのASN.1エンコードの使用の詳細は、Javaセキュリティ標準アルゴリズム名仕様を参照してください。

signメソッドを呼び出すと、signature (署名)オブジェクトは、initSignを呼び出して最初に署名用に初期化されたときの状態にリセットされます。つまり、オブジェクトをリセットするので、必要であればupdateとsignを新たに呼び出して、同じ非公開鍵を使って別の署名を生成できます。

あるいは異なる非公開鍵を指定して、initSignへの新規呼出しを作成したり、(署名を検証するためにSignatureオブジェクトを初期化する目的で) initVerifyへの新規呼出しを作成したりできます。

Signatureオブジェクトによる検証

Signatureオブジェクトを検証用に初期化した場合(VERIFY状態にある場合)は、指定の署名が、それに関連したデータの実際の認証署名であるかどうかを検証できます。この処理を開始するには、検証データ(署名自体ではない)をオブジェクトに入れます。データは、次のいずれかのupdateメソッドを呼び出すことによってオブジェクトに渡されます。

final void update(byte b)
final void update(byte[] data)
final void update(byte[] data, int off, int len)

検証するデータがすべてSignatureオブジェクトに入るまで、updateメソッドを呼び出します。これで、次のいずれかのverifyメソッドを呼び出して署名を検証できます。

final boolean verify(byte[] signature)

final boolean verify(byte[] signature, int offset, int length)

引数は、署名を含むバイト配列である必要があります。このバイト配列は、前にいずれかのsignメソッドの呼出しによって返された署名バイトを保持します。

verifyメソッドは、エンコードされた署名がupdateメソッドに入れたデータの認証署名かどうかを示すbooleanを返します。

verifyメソッドを呼び出すと、signature (署名)オブジェクトは、initVerifyを呼び出して検証用に初期化されたときの状態にリセットされます。つまり、オブジェクトをリセットするので、initVerifyへの呼出しで指定した公開鍵を持つ識別の別の署名を検証できます。

あるいは異なる非公開鍵を指定し、(異なるエンティティの署名を検証するためにSignatureオブジェクトを初期化する目的で)initVerifyへの新規呼出しを作成したり、(署名を生成するためにSignatureオブジェクトを初期化する目的で) initSignへの新規呼出しを作成したりできます。

Cipherクラス

Cipherクラスは、暗号化および復号化で使用される暗号機能を提供します。暗号化とは、データ(クリアテキストと呼ばれる)および鍵を受け取って、鍵を知らないサードパーティにとって無意味なデータ(暗号テキスト)を生成する処理のことです。復号化はその逆で、暗号テキストおよび鍵を受け取り、クリアテキストを生成する処理です。

図2-8 Cipherクラス

対称暗号化方式と非対称暗号化方式

暗号化には、対称(秘密鍵とも呼ばれる)と非対称(または公開鍵暗号化方式)という2つの主要な種類があります。対称暗号化方式では、同じ秘密鍵がデータの暗号化と復号化の両方を行います。データの機密性を保持するには、鍵を非公開にすることが重要となります。一方、非対称暗号化方式は、公開鍵と非公開鍵のペアを使用してデータを暗号化します。一方の鍵で暗号化されたデータは、他方の鍵で復号化されます。ユーザーは最初に公開鍵と非公開鍵のペアを生成し、だれでもアクセスできる信頼できるデータベースに公開鍵を発行します。そのユーザーと安全に通信することを希望するユーザーは、取得した公開鍵を使用してデータを暗号化します。非公開鍵の保有者のみが復号化できます。このスキームでは、非公開鍵を機密にすることが重要となります。

RSAなどの非対称アルゴリズムは、通常、対称アルゴリズムよりも大幅に遅くなります。これらのアルゴリズムは、大量データを効率的に保護するように設計されていません。実際には、非対称アルゴリズムは、対称アルゴリズムを初期化するために使用される少量の秘密鍵を交換する場合に使用されます。

ストリーム暗号とブロック暗号

ブロックとストリームという2つの主要な種類の暗号があります。ブロック暗号は、ブロック全体を一度に処理します。通常は長いバイト長になります。完全な入力ブロックを作成するのにデータが不足する場合は、データのパディングが必要です。つまり、暗号化の前に、ダミー・バイトを追加して暗号のブロック・サイズの倍数にします。これらのバイトは、復号化の段階で削除されます。パディングは、アプリケーションにより、または「PKCS5PADDING」などのパディング型を使用するように暗号を初期化することによって実行できます。これに対して、ストリーム暗号は入力データを一度に1つの小さい単位(通常はバイト、またはビットの場合もある)で処理します。これにより、暗号は任意の量のデータをパディングを行わずに処理できます。

操作モード

単純なブロック暗号を使用して暗号化する場合、2つの同じプレーン・テキストのブロックは、常に同じ暗号テキストのブロックを生成します。暗号テキストを破ろうとする暗号解読者が繰返しのテキストのブロックに気付くと、簡単に解読されてしまいます。暗号操作モードは、ブロック位置、または他の暗号テキスト・ブロックの値に基づく出力ブロック変更により、暗号テキストを予想しにくくします。最初のブロックには初期値が必要です。この値は、初期化ベクトル(IV)と呼ばれます。IVは暗号化の前に単純にデータを変更するため、ランダムである必要がありますが、必ずしも秘密である必要はありません。CBC (Cipher Block Chaining)、CFB (Cipher Feedback Mode)、OFB (Output Feedback Mode)など、様々なモードがあります。ECB (Electronic Codebook Mode)は、ブロック位置や他の暗号テキスト・ブロックの影響を受けないモードです。ECB暗号テキストは同じプレーン・テキスト/鍵を使用する場合は同一になるため、このモードは、通常は暗号化アプリケーションに適していません。使用しないようにしてください。

AESやRSAなどの一部のアルゴリズムでは、異なる長さの鍵を使用可能ですが、それ以外(3DESなど)では固定です。長い鍵を使用する暗号化は、一般にメッセージ復元に対して抵抗力が強いことを意味します。いつものように、セキュリティと時間という相反するものの間で折合いを付けるため、適切な鍵の長さを選択します。

ほとんどのアルゴリズムは、バイナリ鍵を使用します。ほとんどの人は、16進数で表現されていても、長いバイナリの数値を記憶できません。文字のパスワードの方が記憶するのは簡単です。通常、文字のパスワードは少数の文字([a-zA-Z0-9]など)から選択されるため、「パスワードベースの暗号化」(PBE)などのプロトコルは、文字のパスワードを取得して強力なバイナリ鍵を生成するように定義されています。パスワードから暗号化鍵を取得しようとする攻撃者のタスク(一般の辞書の単語->値のマッピングが事前に計算されている、いわゆるレインボー・テーブル攻撃または計算済ディクショナリ攻撃による)を非常に時間のかかるものにするために、大半のPBE実装では、乱数への混入(saltと呼ばれる)を行い、計算済テーブルの有用性を低下させます。

関連データによる認証付き暗号化方式(AEAD)などの新しい暗号化モード(Galois/Counter Mode (GCM)など)では、データを暗号化し、同時に結果のメッセージを認証します。結果のAEADタグ(MAC)の計算にAAD (Additional Associated Data)を使用できますが、このAADデータは暗号テキストとして出力されません。たとえば、一部のデータは機密にする必要がありませんが、変更を検出するため、タグ計算に含める必要がある場合があります。タグ計算にAADを含めるには、Cipher.updateAAD()メソッドを使用できます。

GCMモードでのAES暗号の使用

GCMでのAES暗号はAEAD暗号で、非AEAD暗号とは使用パターンが異なります。通常のデータ以外にAADも取ります(AADは暗号化/復号化用のオプションですが、暗号化/復号化を行うデータの前に必ず指定する必要があります)。また、GCMを安全に使用するため、呼出し側では鍵とIVの組合せを再利用して暗号化を行わないでください。つまり、暗号オブジェクトは、暗号化操作のたびに異なるパラメータ・セットで明示的に初期化しなおす必要があります。

例2-2 GCMモードでAES Cipherを使用するサンプル・コード

SecretKey myKey = ...
byte[] myAAD = ...
byte[] plainText = ...
int myTLen = ...
byte[] myIv = ...

GCMParameterSpec myParams = new GCMParameterSpec(myTLen, myIv);
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, myKey, myParams);

// AAD is optional, if present, it must be supplied before any update/doFinal calls.
c.updateAAD(myAAD);  // if AAD is non-null
byte[] cipherText = new byte[c.getOutputSize(plainText.length)];
// conclusion of encryption operation
int actualOutputLen = c.doFinal(plainText, 0, plainText.length, cipherText);

// To decrypt, same AAD and GCM parameters must be supplied
c.init(Cipher.DECRYPT_MODE, myKey, myParams);
c.updateAAD(myAAD);
byte[] recoveredText = c.doFinal(cipherText, 0, actualOutputLen);

// MUST CHANGE IV VALUE if the same key were to be used again for encryption
byte[] newIv = ...;
myParams = new GCMParameterSpec(myTLen, newIv);

Cipherオブジェクトの作成

Cipherオブジェクトは、Cipher getInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。ここでのアルゴリズム名は、アルゴリズム名だけでなく「変換」を指定する点で、ほかのエンジン・クラスの場合と少し異なります。変換は、指定された入力に対して実行し、なんらかの出力を生成する操作(または操作のセット)を説明する文字列です。変換には、暗号化アルゴリズム(AESなど)の名前が必ず含まれます。これにモードおよびパディング・スキームが続く場合もあります。

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

- "algorithm/mode/padding"または

- "algorithm"

たとえば、次は有効な変換です。

"AES/CBC/PKCS5Padding"

"AES"

変換名だけを指定すると、要求された変換の実装がその環境で使用可能かどうかをシステムが判別し、複数の実装が存在する場合には優先度の高い実装が存在することを返します。

変換名とパッケージ・プロバイダの両方を指定すると、システムは要求されたパッケージ内に要求された変換の実装が存在するかどうかを判別し、存在しない場合には例外をスローします。

アルゴリズム、モードおよびパディングを完全に指定した変換を使用することをお薦めします。そうしないと、プロバイダはデフォルトを使用します。たとえば、SunJCEプロバイダとSunPKCS11プロバイダは、多くの対称暗号でECBをデフォルト・モードとして、PKCS5Paddingをデフォルト・パディングとして使用します。

このため、SunJCEプロバイダでは、

Cipher c1 = Cipher.getInstance("AES/ECB/PKCS5Padding");

および

Cipher c1 = Cipher.getInstance("AES");

という文は、等価になります。

注意:
ECBモードは最も簡単に使用できるブロック暗号モードであり、JDKおよびJREではデフォルトになっています。ECBは、異なる鍵を使用する場合の単一のデータ・ブロックに対しては正常に機能しますが、複数のデータ・ブロックには絶対に使用しないでください。Cipher Block Chaining (CBC)やGalois/Counter Mode (GCM)などのその他の暗号モードのほうが適しています。

CFBやOFBなどのモードを使用すると、ブロック暗号は、暗号の実際のブロック・サイズよりも小さい単位でデータを暗号化できます。このようなモードを要求する場合、AES/CFB8/NoPaddingおよびAES/OFB32/PKCS5Padding変換で示されるように、一度に処理するビット数を、モード名に追加することで指定することもできます。数値を指定しない場合、プロバイダ固有のデフォルトが使用されます。(たとえば、SunJCEプロバイダではAESにデフォルトの128ビットが使用されます。)したがって、CFB8やOFB8などの8ビット・モードを使用することで、ブロック暗号をバイト指向のストリーム暗号に変換できます。

Javaセキュリティ標準アルゴリズム名には、変換のアルゴリズム名、モードおよびパディング・スキーム・コンポーネントの指定に使用可能な標準名のリストが掲載されています。

ファクトリ・メソッドにより返されるオブジェクトは初期化されていないため、使用する前に初期化する必要があります。

Cipherオブジェクトの初期化

getInstanceを介して取得されたCipherオブジェクトは、次の4つのモードのいずれかで初期化する必要があります。これらのモードはCipherクラスのfinal (ファイナル)整数の定数として定義されます。モードは、シンボリック名で参照できます。次に、各モードのシンボリック名および目的の説明を示します。

ENCRYPT_MODE

データの暗号化。

DECRYPT_MODE

データの復号化。

WRAP_MODE

安全に転送するためにjava.security.Keyをラッピングする。

UNWRAP_MODE

ラッピングされた鍵をjava.security.Keyオブジェクトにアンラッピングする。

Cipher初期化メソッドは、それぞれ操作モード・パラメータ(opmode)を取り、そのモード用のCipherオブジェクトを初期化します。その他のパラメータには、鍵(key)または鍵を含む証明書(certificate)、アルゴリズム・パラメータ(params)、および乱数の発生源(random)などがあります。

Cipherオブジェクトを初期化する場合、次のいずれかのinitメソッドを呼び出します。

public void init(int opmode, Key key);

public void init(int opmode, Certificate certificate);

public void init(int opmode, Key key, SecureRandom random);

public void init(int opmode, Certificate certificate,
SecureRandom random);

public void init(int opmode, Key key,
AlgorithmParameterSpec params);

public void init(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random);

public void init(int opmode, Key key,
AlgorithmParameters params);

public void init(int opmode, Key key,
AlgorithmParameters params, SecureRandom random);

初期化ベクトルなどのパラメータを必要とするCipherオブジェクトを暗号化用に初期化する場合、initメソッドにパラメータを何も指定しないと、ランダム・パラメータを生成するか、プロバイダ固有のパラメータ・セット(デフォルト)を使用することにより、基盤となる暗号実装が必須パラメータを提供すると見なされます。

ただし、パラメータを必要とするCipherオブジェクトを復号化用に初期化する場合、initメソッドにパラメータを何も指定しないと、使用したinitメソッドに応じてInvalidKeyExceptionまたはInvalidAlgorithmParameterException例外が発生します。

アルゴリズム・パラメータの管理を参照してください。

復号化には、暗号化に使用したのと同じパラメータを使用する必要があります。

Cipherオブジェクトを初期化すると、それまでに獲得した状態がすべて失われることに留意してください。つまり、Cipherを初期化することは、そのCipherの新規インスタンスを作成して初期化することと等価です。たとえば、指定された鍵で復号化を行うためにCipherを初期化してから、暗号化を行うために初期化すると、復号化モードで獲得した状態はすべて失われます。

データの暗号化および復号化

データの暗号化または復号化は、1つのステップで実行(単一部分操作)することも、複数のステップで実行(複数部分操作)することもできます。データの長さが不明な場合、またはデータが長すぎて一度にメモリーに格納できない場合に、複数部分操作は有用です。

1つのステップでデータの暗号化または復号化を行う場合、次のいずれかのdoFinalメソッドを呼び出します。

public byte[] doFinal(byte[] input);

public byte[] doFinal(byte[] input, int inputOffset, int inputLen);

public int doFinal(byte[] input, int inputOffset,
int inputLen, byte[] output);

public int doFinal(byte[] input, int inputOffset,
int inputLen, byte[] output, int outputOffset)

複数のステップでデータの暗号化または復号化を行うには、次のいずれかのupdateメソッドを呼び出します。

public byte[] update(byte[] input);

public byte[] update(byte[] input, int inputOffset, int inputLen);

public int update(byte[] input, int inputOffset, int inputLen,
byte[] output);

public int update(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)

複数部分操作は、前述のdoFinalメソッドのいずれか(最後のステップで入力データが残されている場合)、または次のdoFinalメソッドのいずれか(最後のステップで入力データが残っていない場合)を使用して終了させます。

public byte[] doFinal();

public int doFinal(byte[] output, int outputOffset);

指定された変換の一部としてパディング(またはアンパディング)が要求された場合、すべてのdoFinalメソッドで、必要なパディング(またはアンパディング)操作がすべて処理されます。

doFinalを呼び出すと、Cipherオブジェクトはinitを呼び出して初期化した時の状態にリセットされます。つまり、Cipherオブジェクトはリセットされて、さらにデータを暗号化または復号化するために使用できます(initの呼出しで指定された操作モードにより異なる)。

鍵のラッピングとアンラッピング

鍵をラッピングすると、ある場所から別の場所へ安全に転送できます。

wrap/unwrap APIは鍵オブジェクトに対して直接機能するため、このAPIを使用するとコードの記述が容易になります。次のメソッドを使用すると、ハードウェア・ベースの鍵の安全な転送も可能になります。

Keyをwrapする場合、まずWRAP_MODEのCipherオブジェクトを初期化し、次のメソッドを呼び出します。

public final byte[] wrap(Key key);

ラップされた鍵のバイト(wrapを呼び出した結果)を、そのラップを解除する他のユーザーに提供する場合、受信者がunwrapを実行するのに必要な、次の追加情報も送信する必要があります。

- 鍵アルゴリズムの名前

- ラップされた鍵の型(Cipher.SECRET_KEY、Cipher.PRIVATE_KEYまたはCipher.PUBLIC_KEYのいずれか)

鍵アルゴリズム名は、次に示すようにKeyインタフェースからgetAlgorithmメソッドを呼び出すことにより確認できます。

public String getAlgorithm();

wrapへの呼出しにより返されたバイトのラップを解除するには、UNWRAP_MODEのCipherオブジェクトを初期化してから、次を呼び出します。

public final Key unwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType));

ここで、wrappedKeyはラップへの呼出しにより返されたバイトを、wrappedKeyAlgorithmはラップされた鍵に関連付けられたアルゴリズムを、wrappedKeyTypeはラップされた鍵の型をそれぞれ指します。これは、Cipher.SECRET_KEY、Cipher.PRIVATE_KEY、Cipher.PUBLIC_KEYのいずれかである必要があります。

アルゴリズム・パラメータの管理

基盤となるCipher実装により使用されるパラメータ(アプリケーションによりinitメソッドに明示的に渡されたか、基盤となる実装自体により生成されたパラメータ)は、getParametersメソッドを呼び出すことによりCipherオブジェクトから取得できます。このメソッドは、パラメータをjava.security.AlgorithmParametersオブジェクト(または、パラメータが使用されない場合はnull)として返します。パラメータが初期化ベクトル(IV)の場合には、getIVメソッドを呼び出すことによってもパラメータを取得できます。

次の例では、パスワードベース暗号化(PBE)を実装するCipherオブジェクトを、パラメータを使用せずに鍵のみを使用して初期化します。ただし、選択されたパスワードベース暗号化用に選択されたアルゴリズムは、saltおよびiteration countという2つのパラメータを必要とします。これらは、基盤となるアルゴリズム実装自体により生成されます。アプリケーションは、生成されたパラメータをCipherオブジェクトから取得できます。例2-3を参照してください。

復号化には、暗号化に使用したのと同じパラメータを使用する必要があります。これらは、それらのエンコーディングからインスタンス化でき、対応するCipherオブジェクトを復号化用に初期化する際に使用できます。例2-4を参照してください。

Cipherオブジェクトの初期化時にパラメータを一切指定せず、基盤となる実装がいずれかのパラメータを使用するかどうか不明な場合、CipherオブジェクトのgetParametersメソッドを呼び出して、返される値をチェックするだけで確認できます。戻り値がnullの場合、パラメータが使用されなかったことを示します。

SunJCEプロバイダにより実装される次の暗号アルゴリズムは、パラメータを使用します。

- AES、DES-EDEおよびBlowfishは、フィードバック(つまりCBC、CFB、OFBまたはPCBC)モードでの使用時に、初期化ベクトル(IV)を使用します。javax.crypto.spec.IvParameterSpecクラスは、指定されたIVでのCipherオブジェクトの初期化に使用できます。また、CTRモードとGCMモードでは、IVが必要になります。

- PBE Cipherアルゴリズムは、saltと繰返し処理の回数を含む、一連のパラメータを使用します。javax.crypto.spec.PBEParameterSpecクラスを使用して、saltおよび繰返し処理回数を指定してPBEアルゴリズムを実装するCipherオブジェクトを初期化できます(例: PBEWithHmacSHA256AndAES_256)。

SealedObjectクラスのクラスを使用する場合、復号化操作で使用するアルゴリズム・パラメータの格納または転送について心配する必要はありません。このクラスは、シール(暗号化)に使用されるパラメータを暗号化されたオブジェクト・コンテンツに添付します。また、アンシール(復号化)でも同じパラメータを使用します。

例2-3 Cipherオブジェクトからパラメータを取得するサンプル・コード

アプリケーションは、暗号化用の生成されたパラメータを次の方法でCipherオブジェクトから取得できます。

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

// get cipher object for password-based encryption
Cipher c = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");

// initialize cipher for encryption, without supplying
// any parameters. Here, "myKey" is assumed to refer
// to an already-generated key.
c.init(Cipher.ENCRYPT_MODE, myKey);

// encrypt some data and store away ciphertext
// for later decryption
byte[] cipherText = c.doFinal("This is just an example".getBytes());

// retrieve parameters generated by underlying cipher
// implementation
AlgorithmParameters algParams = c.getParameters();

// get parameter encoding and store it away
byte[] encodedAlgParams = algParams.getEncoded();

例2-4 複合化用にCipherオブジェクトを初期化するサンプル・コード

復号化には、暗号化に使用したのと同じパラメータを使用する必要があります。これらは、それらのエンコーディングからインスタンス化でき、次に示すように、対応するCipherオブジェクトを復号化用に初期化する際に使用できます。

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

// get parameter object for password-based encryption
AlgorithmParameters algParams;
algParams = AlgorithmParameters.getInstance("PBEWithHmacSHA256AndAES_256");

// initialize with parameter encoding from above
algParams.init(encodedAlgParams);

// get cipher object for password-based encryption
Cipher c = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");

// initialize cipher for decryption, using one of the
// init() methods that takes an AlgorithmParameters
// object, and pass it the algParams object from above
c.init(Cipher.DECRYPT_MODE, myKey, algParams);

暗号出力時の考慮事項

CipherのupdateおよびdoFinalメソッドの中には、呼出し側による出力バッファの指定が可能なものがあります。暗号化または復号化されたデータは、このバッファ内に出力されます。この場合、暗号化または復号化操作の結果を保持できるだけの大きさのバッファを渡すことは重要です。

Cipher内の次のメソッドを使用して、設定すべき出力バッファのサイズを確認できます。

public int getOutputSize(int inputLen)

Cipherベースのその他のクラス

Cipherを内部的に使用して共通の暗号使用への簡単なアクセスを提供する、いくつかのヘルパー・クラスがあります。

トピック

Cipher Streamクラス

SealedObjectクラス

Cipher Streamクラス

CipherInputStreamクラスとCipherOutputStreamクラスは、Cipherストリーム・クラスです。

CipherInputStreamクラス

このクラスは、通過するデータの暗号化または復号化を行うFilterInputStreamです。これは、InputStreamで構成されています。CipherInputStreamは、Cipherオブジェクトの挿入先の、セキュリティ保護された入力ストリームを表します。CipherInputStreamのreadメソッドは、基盤となるInputStreamから読み取られ、埋め込まれたCipherオブジェクトにより追加処理されたデータを返します。Cipherオブジェクトは、CipherInputStreamで使用する前に完全に初期化する必要があります。

たとえば、埋め込まれたCipherが復号化用に初期化されている場合、CipherInputStreamは基盤となるInputStreamから読み込んだデータの復号化を試みてから、データをアプリケーションに返します。

このクラスは、上位クラスjava.io.FilterInputStreamおよびjava.io.InputStreamのセマンティックス(特にエラーに関するセマンティックス)に厳密に準拠します。このクラスは、上位クラスで指定されたメソッドを正確に保持し、それらすべてをオーバーライドします。このため、埋め込まれた暗号によるデータの追加処理が可能になります。さらに、このクラスは、上位クラスがスローしない例外をすべてキャッチします。特に、skip(long)メソッドは、Cipherに処理されたデータのみを無視します。

このクラスを使用するプログラマにとって、このクラスで定義またはオーバーライドされていないメソッド(上位クラスのいずれかに後で追加された新規メソッドまたはコンストラクタ)を使用しないようにすることは重要です。これらのメソッドの設計と実装は、CipherInputStreamへのセキュリティ面の影響を考慮に入れていないためです。例2-5でその使用方法を参照してください。cipher1が暗号化用に初期化されている場合を考えてみましょう。このプログラムは、ファイル/tmp/a.txtから内容を読み取って暗号化し、結果(暗号化されたバイト)を/tmp/b.txtに格納します。

例2-6では、CipherInputStreamおよびFileInputStreamの複数のインスタンスを簡単に接続する方法を示します。この例では、cipher1およびcipher2が、それぞれ暗号化および復号化用に(対応する鍵を使用して)初期化されているものとします。このプログラムは、ファイル/tmp/a.txtの内容を/tmp/b.txtにコピーします。ただし、/tmp/a.txtからの読取り時に、最初に内容の暗号化、次に復号化が行われます。実際のところ、このプログラムはテキストを暗号化した後、すぐに復号化を行うため、CipherInputStreamsのチェーンをわかりやすく示す以外は特に有用なものではありません。

CipherInputStreamの読取りメソッドは、基盤となる暗号からデータが返されるまでブロックします。ブロック暗号が使用される場合、基盤となるInputStreamから暗号テキストの完全なブロックが取得される必要があります。

例2-5 CipherInputStreamとFileInputStreamを使用するサンプル・コード

次のコードは、暗号およびFileInputStreamを含むCipherInputStreamを使用して、入力ストリーム・データを暗号化する方法を示します。

try (FileInputStream fis = new FileInputStream("/tmp/a.txt");
CipherInputStream cis = new CipherInputStream(fis, cipher1);
FileOutputStream fos = new FileOutputStream("/tmp/b.txt")) {
byte[] b = new byte[8];
int i = cis.read(b);
while (i != -1) {
fos.write(b, 0, i);
i = cis.read(b);
}
}

例2-6 CipherInputStreamとFileInputStreamを接続するサンプル・コード

次の例では、CipherInputStreamおよびFileInputStreamの複数のインスタンスを簡単に接続する方法を示します。この例では、cipher1およびcipher2が、それぞれ暗号化および復号化用に(対応する鍵を使用して)初期化されているものとします。

try (FileInputStream fis = new FileInputStream("/tmp/a.txt");
CipherInputStream cis1 = new CipherInputStream(fis, cipher1);
CipherInputStream cis2 = new CipherInputStream(cis1, cipher2);
FileOutputStream fos = new FileOutputStream("/tmp/b.txt")) {
byte[] b = new byte[8];
int i = cis2.read(b);
while (i != -1) {
fos.write(b, 0, i);
i = cis2.read(b);
}
}

CipherOutputStreamクラス

このクラスは、通過するデータの暗号化または復号化を行うFilterOutputStreamです。これは、OutputStreamまたはそのサブクラスのいずれか、およびCipherで構成されます。CipherOutputStreamは、Cipherオブジェクトの挿入先の、セキュリティ保護された出力ストリームを表します。CipherOutputStreamのwriteメソッドは、埋め込まれたCipherオブジェクトを使用してデータを処理してから、基盤となるOutputStreamにデータを書き出します。Cipherオブジェクトは、CipherOutputStreamで使用する前に完全に初期化する必要があります。

たとえば、埋め込まれたCipherが暗号化用に初期化されている場合、CipherOutputStreamはデータを暗号化してから、基盤となる出力ストリームに書き出します。

このクラスは、上位クラスjava.io.OutputStreamおよびjava.io.FilterOutputStreamのセマンティックス(特にエラーに関するセマンティックス)に厳密に準拠します。このクラスは、上位クラスで指定されたメソッドを正確に保持し、それらすべてをオーバーライドします。このため、埋め込まれた暗号によるすべてのデータの追加処理が可能になります。さらに、このクラスは、上位クラスがスローしない例外をすべてキャッチします。

このクラスを使用するプログラマにとって、このクラスで定義またはオーバーライドされていないメソッド(上位クラスのいずれかに後で追加された新規メソッドまたはコンストラクタ)を使用しないようにすることは重要です。これらのメソッドの設計と実装は、CipherOutputStreamへのセキュリティ面の影響を考慮に入れていないためです。

例2-7でその使用方法を参照してください。cipher1が暗号化用に初期化されている場合を考えてみましょう。このプログラムは、ファイル/tmp/a.txtから内容を読み取って暗号化し、結果(暗号化されたバイト)を/tmp/b.txtに格納します。

例2-7では、CipherOutputStreamおよびFileOutputStreamの複数のインスタンスを簡単に接続する方法を示します。この例では、cipher1およびcipher2が、それぞれ復号化および暗号化用に(対応する鍵を使用して)初期化されているものとします。このプログラムは、ファイル/tmp/a.txtの内容を/tmp/b.txtにコピーします。ただし、/tmp/b.txtに書き込む前に、内容の暗号化および復号化が行われます。

ブロック暗号アルゴリズムを使用する場合は、データが暗号化されて基盤となる出力ストリームに送信される前に、プレーン・テキスト・データの完全なブロックをCipherOutputStreamに与える必要があります。

このクラスのflushメソッドとcloseメソッドには、他に1つの重要な相違点があります。カプセル化されたCipherオブジェクトがパディングを有効にしてブロック暗号アルゴリズムを実装する場合、この相違点に特に留意する必要があります。

- flushは、カプセル化されたCipherオブジェクトにより処理済のバッファリングされた出力バイトを、すべて強制的に書き出すことにより、基盤となるOutputStreamをフラッシュします。カプセル化されたCipherオブジェクトによりバッファリングされ、処理待ち状態にあるバイトは、書き出されません。

- closeは、基盤となるOutputStreamを閉じて、関連付けられたすべてのシステム・リソースを解放します。カプセル化されたCipherオブジェクトのdoFinalメソッドを呼び出して、このオブジェクトによりバッファリングされたすべてのバイトを処理します。さらにflushメソッドを呼び出して、処理したバイトを基盤となるストリームに書き込みます。

例2-7 CipherOutputStreamとFileOutputStreamを使用するサンプル・コード

このコードは、暗号およびFileOutputStreamを含むCipherOutputStreamを使用して、暗号化されたデータを出力ストリームに書き出す方法を示します。

try (FileInputStream fis = new FileInputStream("/tmp/a.txt");
FileOutputStream fos = new FileOutputStream("/tmp/b.txt");
CipherOutputStream cos = new CipherOutputStream(fos, cipher1)) {
byte[] b = new byte[8];
int i = fis.read(b);
while (i != -1) {
cos.write(b, 0, i);
i = fis.read(b);
}
cos.flush();
}

例2-8 CipherOutputStreamとFileOutputStreamを接続するサンプル・コード

このコードでは、CipherOutputStreamおよびFileOutputStreamの複数のインスタンスを簡単に接続する方法を示します。この例では、cipher1およびcipher2が、それぞれ復号化および暗号化用に(対応する鍵を使用して)初期化されているものとします。

try (FileInputStream fis = new FileInputStream("/tmp/a.txt");
FileOutputStream fos = new FileOutputStream("/tmp/b.txt");
CipherOutputStream cos1 = new CipherOutputStream(fos, cipher1);
CipherOutputStream cos2 = new CipherOutputStream(cos1, cipher2)) {
byte[] b = new byte[8];
int i = fis.read(b);
while (i != -1) {
cos2.write(b, 0, i);
i = fis.read(b);
}
cos2.flush();
}

SealedObjectクラス

プログラマは、このクラスを使用してオブジェクトを生成し、暗号化アルゴリズムを利用してその機密性を保護することができます。

java.io.Serializableインタフェースを実装するオブジェクトが指定された場合、元のオブジェクトを直列化形式(ディープ・コピー)でカプセル化するSealedObjectを作成し、AESなどの暗号化アルゴリズムを使用して、直列化された内容をシール(暗号化)することにより、機密性を保護できます。そのあと、暗号化された内容の暗号解読(適正な暗号解読鍵を使用)、および直列化解除を行うことにより、元のオブジェクトを復元できます。

一般的な使用法を、次のコード例に示します。オブジェクトをシールする場合、シール対象のオブジェクトからSealedObjectを作成し、直列化されたオブジェクト内容を暗号化する、完全に初期化されたCipherオブジェクトを作成します。この例では、文字列This is a secretがAESアルゴリズムを使用してシールされます。シール操作に使用されるすべてのアルゴリズム・パラメータは、SealedObjectの内部に格納されることに留意してください。

// create Cipher object
// NOTE: sKey is assumed to refer to an already-generated
// secret AES key.
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, sKey);

// do the sealing
SealedObject so = new SealedObject("This is a secret", c);

シールされた元のオブジェクトは、次の異なる2つの方法で復元可能です。

- 厳密に同一のアルゴリズム、鍵、パディング・スキームなどで初期化され、オブジェクトのシールに使用されたCipherオブジェクトを使用する方法。

c.init(Cipher.DECRYPT_MODE, sKey);
try {
String s = (String)so.getObject(c);
} catch (Exception e) {
// do something
};

この方法には、暗号解読鍵に関する知識がなくても、シールされたオブジェクトのアンシールを実行できるという利点があります。たとえば、あるパーティが暗号オブジェクトを必須の暗号解読鍵を使って初期化したあとで、別のパーティに渡すと、そのパーティはシールされたオブジェクトをアンシールできます。

- 適切な復号化鍵を使用する方法(AESは対称暗号化アルゴリズムであるため、シールとアンシールに同じ鍵を使用できる)

try {
String s = (String)so.getObject(sKey);
} catch (Exception e) {
// do something
};

この方法では、getObjectメソッドは、適切な暗号解読アルゴリズム用の暗号オブジェクトを作成し、シール済みのオブジェクトに格納された特定の暗号解読鍵およびアルゴリズム・パラメータ(存在する場合)を使用してそれを初期化します。この方法の利点は、オブジェクトをアンシールするパーティが、オブジェクトのシールに使用したパラメータ(IVなど)を追跡する必要がないことです。

Macクラス

MessageDigestと同様に、メッセージ認証コード(MAC)は、送信された情報や信頼できないメディアに保存されている情報の整合性をチェックする方法を提供しますが、秘密鍵が計算に含まれます。

適切な鍵を持つ人だけが、受信したメッセージを検証できます。一般に、メッセージ認証コードは、秘密鍵を共有する2つのパーティ間で送信される情報の有効性を検証する場合に使用されます。

図2-9 Macクラス

暗号化ハッシュ機能に基づくMACメカニズムは、HMACと呼ばれます。HMACは、秘密共有鍵と組み合せて、SHA-256などの任意の暗号化ハッシュ機能で使用できます。

Macクラスは、メッセージ認証コード(MAC)の機能を提供します。HMAC-SHA256の例を参照してください。

Macオブジェクトの作成

Macオブジェクトは、Macの getInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。

Macオブジェクトの初期化

Macオブジェクトは、常に(秘密)鍵を使って初期化されます。またオプションとして、基盤となるMACアルゴリズムによっては、パラメータ・セットを使って初期化することもできます。

Macオブジェクトを初期化する場合、次のいずれかのinitメソッドを呼び出します。

public void init(Key key);

public void init(Key key, AlgorithmParameterSpec params);

javax.crypto.SecretKeyインタフェースを実装する任意の(秘密)鍵オブジェクトを使って、Macオブジェクトを初期化できます。これは、javax.crypto.KeyGenerator.generateKey()が返すオブジェクト、またはjavax.crypto.KeyAgreement.generateSecret()などが返す鍵協定プロトコルの結果生成されるオブジェクト、またはjavax.crypto.spec.SecretKeySpecのインスタンスです。

MACアルゴリズムの中には、Macオブジェクトの初期化に使用される(秘密)鍵オブジェクトに関連付けられた(秘密)鍵アルゴリズムが重要ではないものがあります(SunJCEプロバイダのHMAC-MD5およびHMAC-SHA1実装の場合)。ただし、それ以外の場合、(秘密)鍵アルゴリズムは重要であり、(秘密)鍵オブジェクトが不適切な(秘密)鍵アルゴリズムで使用されると、InvalidKeyExceptionがスローされます。

MACの計算

MACは、1つのステップで計算(単一部分操作)することも、複数のステップで計算(複数部分操作)することもできます。データの長さが不明な場合、またはデータが長すぎて一度にメモリーに格納できない場合に、複数部分操作は有用です。

あるデータのMACを1回のステップで計算するには、次のdoFinalメソッドを呼び出します。

public byte[] doFinal(byte[] input);

複数のステップでデータのMACを計算するには、次のいずれかのupdateメソッドを呼び出します。

public void update(byte input);

public void update(byte[] input);

public void update(byte[] input, int inputOffset, int inputLen);

複数部分操作は、前述のdoFinalメソッド(最後のステップで入力データが残されている場合)、または次のdoFinalメソッドのいずれか(最後のステップで入力データが残っていない場合)を使用して終了させます。

public byte[] doFinal();

public void doFinal(byte[] output, int outOffset);

Keyインタフェース

java.security.Keyインタフェースは、すべての不透明な鍵に関するトップ・レベルのインタフェースです。すべての不透明な鍵オブジェクトが共有する機能を定義します。

ここまでは、鍵とは何か、鍵はどのように生成および表現されるかについて詳細は説明せずに、JCAの高レベルの使用に重点を置いてきました。ここでは鍵に注意を向けます。

「不透明な」鍵の表現では、鍵を構成する鍵データに直接アクセスできません。つまり、「不透明」さにより、鍵へのアクセスが、Keyインタフェースによって定義されるgetAlgorithm、getFormat、およびgetEncoded。

これは、対応するKeySpecインタフェース(KeySpecインタフェースを参照)で定義されているgetメソッドのいずれかによって各鍵データ値に個別にアクセスできる、透明な表現とは対照的です。

不透明な鍵はすべて、次の3つの特性を持ちます。

アルゴリズム

その鍵の鍵アルゴリズムです。鍵アルゴリズムとは、通常の場合、暗号化アルゴリズムか非対称操作アルゴリズム(AES、DSAまたはRSAなど)のことであり、そのようなアルゴリズムや関連するアルゴリズム(SHA256withRSAなど)と協調動作します。鍵のアルゴリズムの名前は、次のメソッドを使用して取得されます。

String getAlgorithm()

エンコードされた形式

鍵の外部エンコード形式は、鍵を別の組織に転送する場合など、鍵の標準表示がJava仮想マシンの外部で必要なときに使います。鍵は標準形式(X.509やPKCS8など)に従ってエンコードされ、次のメソッドを使用して返されます。

byte[] getEncoded()

フォーマット

エンコードされた鍵のフォーマット名です。次のメソッドから返されます。

String getFormat()

鍵は一般的に、KeyGeneratorクラスやKeyPairGeneratorクラスなどの鍵ジェネレータ、証明書、KeyFactoryを使用する鍵仕様(KeySpecインタフェースを参照)、または鍵管理で使用するキーストア・データベースにアクセスするKeystoreの実装から獲得します。KeyFactoryを使って、アルゴリズム依存型の方法でエンコードされた鍵を解析することが可能です。

また、CertificateFactoryを使って、認証を解析することも可能です。

次に、java.security.interfacesおよびjavax.crypto.interfacesパッケージ内のKeyインタフェースを拡張するインタフェースのリストを示します。

- SecretKey

- PBEKey

- PrivateKey

- DHPrivateKey

- DSAPrivateKey

- ECPrivateKey

- RSAMultiPrimePrivateCrtKey

- RSAPrivateCrtKey

- RSAPrivateKey

- PublicKey

- DHPublicKey

- DSAPublicKey

- ECPublicKey

- RSAPublicKey

PublicKeyインタフェースとPrivateKeyインタフェース

PublicKeyおよびPrivateKeyインタフェースはどちらもKeyインタフェースを拡張しますが、これらはメソッドを使わないインタフェースで、型の安全性および型の識別に使用します。

KeyPairクラス

KeyPairクラスは鍵のペア(公開鍵と非公開鍵)の簡単なホルダーです。

これには2つのpublicメソッドがあります。1つは非公開鍵を返し、もう1つは公開鍵を返します。

PrivateKey getPrivate()
PublicKey getPublic()

鍵仕様のインタフェースおよびクラス

Keyオブジェクトと鍵仕様(KeySpec)は、鍵データの異なる2つの表現です。CipherはKeyオブジェクトを使用して暗号化アルゴリズムを初期化しますが、伝送またはストレージのために鍵を移植性の高い形式に変換する必要がある場合があります。

鍵の透明な表現とは、対応する仕様クラスで定義されたgetメソッドの1つを使って、各鍵データ値に個々にアクセスできるということです。たとえば、DSAPrivateKeySpecは、getX、getP、getQ、およびgetGメソッドを定義し、非公開鍵xおよび鍵の計算に使用するDSAアルゴリズムのパラメータ(素数のp、サブ素数のq、およびベースのg)にアクセスします。鍵がハードウェア・デバイス上に格納されている場合は、その鍵仕様には、デバイス上の鍵の識別を助ける情報が含まれていることがあります。

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

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

KeyFactoryクラスおよびSecretKeyFactoryクラスのクラスは、不透明な鍵の表現と透明な鍵の表現の間(つまり、KeyとKeySpecの間。操作が可能であると想定)で変換を行うために使用できます。(たとえば、スマート・カード上の非公開鍵は、カードから取り出せない場合があります。そのようなKeyは変換不可能です。)

次のセクションでは、java.security.specパッケージ内に含まれる鍵仕様のインタフェースおよびクラスについて説明します。

KeySpecインタフェース

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

KeySpecサブインタフェース

Keyインタフェースと同じように、KeySpecインタフェースの同様のセットがあります。

- SecretKeySpec

- EncodedKeySpec

- PKCS8EncodedKeySpec

- X509EncodedKeySpec

- DESKeySpec

- DESedeKeySpec

- PBEKeySpec

- DHPrivateKeySpec

- DSAPrivateKeySpec

- ECPrivateKeySpec

- RSAPrivateKeySpec

- RSAMultiPrimePrivateCrtKeySpec

- RSAPrivateCrtKeySpec

- DHPublicKeySpec

- DSAPublicKeySpec

- ECPublicKeySpec

- RSAPublicKeySpec

EncodedKeySpecクラス

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

abstract byte[] getEncoded();

このクラスのgetFormatメソッドは、次のエンコード形式の名前を返します。

abstract String getFormat();

固定実装PKCS8EncodedKeySpecおよびX509EncodedKeySpecについては、次のセクションを参照してください。

PKCS8EncodedKeySpecクラス

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

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

X509EncodedKeySpecクラス

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

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

ジェネレータとファクトリ

JavaおよびJCA APIをはじめて使用する人は、ジェネレータとファクトリの区別がつかない場合があります。

図2-10 ジェネレータとファクトリ

ジェネレータは、新しいオブジェクトを生成する場合に使用されます。ジェネレータは、アルゴリズム依存またはアルゴリズム非依存で初期化できます。たとえば、Diffie-Hellman (DH)鍵ペアを作成するために、アプリケーションは必要なPおよびG値を指定できます。または、ジェネレータを適切な鍵の長さで単純に初期化できます。ジェネレータは適切なPおよびG値を選択します。どちらの場合も、ジェネレータはパラメータに基づいて新しい鍵を生成します。

一方、ファクトリは、既存のオブジェクト型から別のオブジェクト型へデータを変換する場合に使用されます。たとえば、アプリケーションで、DH非公開鍵のコンポーネントが使用可能になっている場合があります。アプリケーションは、それらをKeySpecインタフェースとしてパッケージ化できますが、KeyAgreementオブジェクトで使用できるようPrivateKeyオブジェクトに変換する必要があります。または逆の変換が必要な場合もあります。または、それらに証明書のバイト配列があるが、CertificateFactoryを使用して、X509Certificateオブジェクトに変換する必要がある場合があります。アプリケーションは、変換を行うためにファクトリ・オブジェクトを使用します。

KeyFactoryクラス

KeyFactoryクラスは、不透明な暗号化Keyインタフェースと鍵仕様のインタフェースおよびクラス(背後の鍵データの透明な表現)との間の変換を実行するために設計されたエンジン・クラスとアルゴリズムです。

図2-11 KeyFactoryクラス

鍵ファクトリは、双方向性があります。つまり、これによって、与えられた鍵仕様(鍵のデータ)から不透明な鍵オブジェクトを構築することも、鍵オブジェクトの背後の鍵データを適切な形式で取得することもできます。

同一の鍵に対して、複数の互換性のある鍵仕様を存在させることもできます。たとえば、DSA公開鍵は、コンポーネントy、p、qおよびgによって指定することも(java.security.spec.DSAPublicKeySpecを参照)、X.509標準に従ってDERエンコードを使用して指定することも(X509EncodedKeySpecクラスを参照)できます。

鍵ファクトリは、互換性のある鍵仕様間の変換に使用できます。互換性のある鍵仕様間の変換では、鍵の構文解析が行われます。たとえば、X509EncodedKeySpecをDSAPublicKeySpecに変換する場合は、基本的にエンコードされた鍵をコンポーネント単位に解析します。例については、「鍵仕様およびKeyFactoryを使った署名の生成と検証」のセクションの最後を参照してください。

KeyFactoryオブジェクトの作成

KeyFactoryオブジェクトは、KeyFactoryのgetInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。

鍵仕様とKeyオブジェクト間の変換

公開鍵用の鍵仕様がある場合は、generatePublicメソッドを使って、その仕様から不透明なPublicKeyオブジェクトを取得できます。

PublicKey generatePublic(KeySpec keySpec)

同様に、非公開鍵用の鍵仕様がある場合は、generatePrivateメソッドを使って、その仕様から不透明なPrivateKeyオブジェクトを取得できます。

PrivateKey generatePrivate(KeySpec keySpec)

Keyオブジェクトと鍵仕様間の変換

Keyオブジェクトがある場合は、getKeySpecメソッドの呼出しによって、対応する鍵仕様オブジェクトを取得できます。

KeySpec getKeySpec(Key key, Class keySpec)

keySpecは、鍵のデータが返されるべき仕様クラスを識別します。たとえば、DSAPublicKeySpec.classは、鍵のデータがDSAPublicKeySpecクラスのインスタンスに返される必要があることを指示します。鍵仕様およびKeyFactoryを使った署名の生成と検証を参照してください。

SecretKeyFactoryクラス

SecretKeyFactoryクラスは、秘密鍵のファクトリを表します。KeyFactoryクラス(KeyFactoryクラスを参照)とは異なり、javax.crypto.SecretKeyFactoryオブジェクトは秘密(対称)鍵のみを処理し、一方、java.security.KeyFactoryオブジェクトは鍵ペアの公開鍵および非公開鍵コンポーネントを処理します。

図2-12 SecretKeyFactoryクラス

鍵ファクトリは、java.security.Key型の不透明な暗号鍵であるKeyインタフェースと、適切な形式の基本の鍵データの透明な表現である鍵仕様のインタフェースおよびクラスとの間の変換を行うために使用します。

java.security.Key型のオブジェクト(java.security.PublicKey、java.security.PrivateKey、およびjavax.crypto.SecretKeyはそのサブクラス)は、その実装方法が不明であるため、不透明な鍵オブジェクトになります。基盤となる実装はプロバイダ依存であるため、ソフトウェア・ベースにもハードウェア・ベースにもできます。鍵ファクトリを使用すると、プロバイダは独自の暗号化鍵実装を提供できるようになります。

たとえば、公開値y、素数モジュラスp、ベースgで構成されるDiffie-Hellman公開鍵の鍵仕様を保持しており、同じ仕様を別のプロバイダのDiffie-Hellman鍵ファクトリに送る場合、生成されるPublicKeyオブジェクトは大概、異なる基盤実装を保持するようになります。

プロバイダは、秘密鍵ファクトリがサポートする鍵仕様をドキュメント化する必要があります。たとえば、SunJCEプロバイダにより提供されるDES鍵のSecretKeyFactoryは、DESKeySpecをDES鍵の透明表現としてサポートします。また、DES-EDE鍵のSecretKeyFactoryはDESedeKeySpecをDES-EDE鍵の透明表現として、PBEのSecretKeyFactoryはPBEKeySpecを基盤となるパスワードの透明表現として、それぞれサポートします。

次の例は、SecretKeyFactoryを使用して秘密鍵データをSecretKeyオブジェクトに変換する方法を示します。これは、以降のCipher操作で使用できます。

// Note the following bytes are not realistic secret key data
// bytes but are simply supplied as an illustration of using data
// bytes (key material) you already have to build a DESedeKeySpec.

byte[] desEdeKeyData = getKeyData();
DESedeKeySpec desEdeKeySpec = new DESedeKeySpec(desEdeKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey secretKey = keyFactory.generateSecret(desEdeKeySpec);

この場合、SecretKeyの基盤実装は、KeyFactoryのプロバイダに基づきます。

別の方法として、プロバイダに依存せずに、同じ鍵データから等価な機能を持つSecretKeyオブジェクトを作成することも可能です。その場合、javax.crypto.SecretKeyインタフェースを実装するjavax.crypto.spec.SecretKeySpecクラスを使用します。

byte[] aesKeyData = getKeyData();
SecretKeySpec secretKey = new SecretKeySpec(aesKeyData, "AES");

SecretKeyFactoryオブジェクトの作成

SecretKeyFactoryオブジェクトは、SecretKeyFactoryのgetInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。

鍵仕様とSecret Keyオブジェクト間の変換

秘密鍵用の鍵仕様がある場合は、generateSecretメソッドを使って、その仕様から不透明なSecretKeyオブジェクトを取得できます。

SecretKey generateSecret(KeySpec keySpec)

Secret Keyオブジェクトと鍵仕様間の変換

SecretKeyオブジェクトがある場合は、getKeySpecメソッドの呼出しによって、対応する鍵仕様オブジェクトを取得できます。

KeySpec getKeySpec(Key key, Class keySpec)

keySpecは、鍵のデータが返されるべき仕様クラスを識別します。たとえば、DESKeySpec.classは、鍵のデータがDESKeySpecクラスのインスタンスに返される必要があることを指示します。

KeyPairGeneratorクラス

KeyPairGeneratorクラスは、エンジン・クラス(エンジン・クラスとアルゴリズムを参照)であり、公開鍵と非公開鍵のペアの生成に使用します。

図2-13 KeyPairGeneratorクラス

鍵のペアの生成方法には、アルゴリズム独立型とアルゴリズム固有型の2つがあります。この2つの唯一の相違点は、オブジェクトの初期化にあります。

次に記述するメソッドの呼出し例は、鍵のペアの生成を参照してください。

KeyPairGeneratorの作成

すべての鍵のペアは、最初にKeyPairGeneratorを使って生成します。KeyPairGeneratorオブジェクトは、KeyPairGeneratorのgetInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。

KeyPairGeneratorの初期化

特定のアルゴリズム用の鍵のペアのジェネレータは、そのアルゴリズムで使うことができる公開鍵または非公開鍵を作成します。また、アルゴリズム固有型のパラメータを生成された各鍵に関連付けます。

まず、鍵のペアを初期化してからでなければ、鍵のペアは鍵を生成できません。ほとんどの場合、アルゴリズム独立型の初期化で十分です。ただし、その他の場合は、アルゴリズム固有型の初期化を使用できます。

アルゴリズムに依存しない初期化

すべての鍵ジェネレータは、キー・サイズおよび乱数発生の元の概念を共有します。キー・サイズは、アルゴリズムごとに解釈が異なります。たとえば、DSAアルゴリズムの場合、キー・サイズはモジュラスの長さと一致します。(特定のアルゴリズムのキー・サイズについては、Javaセキュリティ標準アルゴリズム名仕様を参照。)

普遍的に共有されるこれら2つの引数の型をとるinitializeメソッドがあります。

void initialize(int keysize, SecureRandom random)

また、keysize引数だけをとり、システムが提供する乱数の発生源を使用するinitializeメソッドもあります。

void initialize(int keysize)

上記のアルゴリズムに依存しないinitializeメソッドを呼び出した場合、パラメータが指定されないため、それぞれの鍵に関連したアルゴリズム固有のパラメータが存在する場合、これをどのように扱うかはプロバイダに任されます。

アルゴリズムがDSAアルゴリズムで、モジュラスのサイズ(キー・サイズ)が512、768、1024、2048または3072の場合は、SUNプロバイダはp、q、およびgパラメータに事前計算済の一連の値を使用します。モジュラスのサイズが前述の値の1つでない場合は、SUNプロバイダは、新しいパラメータのセットを作成します。これらの3つのモジュラス・サイズ以外の、事前に計算されたパラメータ・セットを持つプロバイダが存在する可能性もあります。また、事前に計算されたパラメータがなく、常に新しいパラメータ・セットを作成するプロバイダが存在する可能性もあります。

アルゴリズム固有の初期化

アルゴリズム固有のパラメータのセットがすでに存在する状況では(DSAのコミュニティ・パラメータなど)、AlgorithmParameterSpecインタフェース引数を取るinitializeメソッドが2つあります。このうちの一方はSecureRandom引数も取りますが、他方では、乱数の発生源はシステムによって提供されます。

void initialize(AlgorithmParameterSpec params,
SecureRandom random)

void initialize(AlgorithmParameterSpec params)

鍵のペアの生成を参照してください。

鍵のペアの生成

鍵のペアの生成手順は、初期化(およびアルゴリズム)に関係なく、常に同じです。必ずKeyPairGeneratorから次のメソッドを呼び出します。

KeyPair generateKeyPair()

generateKeyPairを呼び出すたびに、異なる鍵のペアが作られます。

KeyGeneratorクラス

鍵ジェネレータは、対称アルゴリズム用の秘密鍵を生成します。

図2-14 KeyGeneratorクラス

KeyGeneratorの作成

KeyGeneratorオブジェクトは、KeyGeneratorのgetInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。

KeyGeneratorオブジェクトの初期化

特定の対称鍵アルゴリズムの鍵ジェネレータは、そのアルゴリズムで使用可能な対称鍵を作成します。また、生成された鍵に、アルゴリズム固有のパラメータ(存在する場合)を関連付けます。

鍵の生成方法には、アルゴリズム独立型とアルゴリズム固有型の2つがあります。この2つの唯一の相違点は、オブジェクトの初期化にあります。

- アルゴリズムに依存しない初期化
すべての鍵ジェネレータが、鍵サイズおよび乱数発生の元の概念を共有します。普遍的に共有されるこれら2つの引数の型をとるinitメソッドが存在します。また、keysize引数のみをとり、システムにより提供される乱数の発生源を使用するものや、乱数の発生源だけをとるものも存在します。

public void init(SecureRandom random);

public void init(int keysize);

public void init(int keysize, SecureRandom random);

前述のアルゴリズム独立型initメソッドを呼び出すときは、その他のパラメータは指定しないため、生成された鍵に関連付けられるアルゴリズム固有のパラメータが存在する場合、そのパラメータの処理は、プロバイダによって異なります。

- アルゴリズム固有の初期化
アルゴリズム固有のパラメータのセットがすでに存在する状況では、AlgorithmParameterSpec引数を取る2つのinitメソッドが存在します。このうちの一方はSecureRandom引数も取りますが、他方では、乱数の発生源はシステムによって提供されます。

public void init(AlgorithmParameterSpec params);

public void init(AlgorithmParameterSpec params, SecureRandom random);

クライアントが(initメソッドの呼出しによって) KeyGeneratorを明示的に初期化しない場合は、各プロバイダがデフォルトの初期化を提供(および文書化)する必要があります。

鍵の作成

次のメソッドにより、秘密鍵が生成されます。

public SecretKey generateKey();

KeyAgreementクラス

鍵協定とは、複数のパーティが秘密情報を交換しなくても同じ暗号化鍵を確立可能なプロトコルを指します。

図2-15 KeyAgreementクラス

各パーティは、非公開鍵によって鍵協定オブジェクトを初期化し、通信に参加する各パーティの公開鍵を入力します。ほとんどの場合は2つのパーティのみですが、Diffie-Hellmanなどのアルゴリズムでは3つ以上のパーティの参加が可能です。公開鍵がすべて入力されると、各KeyAgreementオブジェクトは同じ鍵を生成(合意)します。

KeyAgreementクラスは、鍵協定プロトコルの機能を提供します。共有の秘密の確立に関係する鍵は、鍵ジェネレータのいずれか(KeyPairGeneratorまたはKeyGenerator)かKeyFactoryによって、または鍵協定プロトコルの中間フェーズの結果として作成されます。

KeyAgreementオブジェクトの作成

鍵協定に関係する各パーティは、KeyAgreementオブジェクトを作成する必要があります。KeyAgreementオブジェクトは、KeyAgreementのgetInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。

KeyAgreementオブジェクトの初期化

非公開情報を使用してKeyAgreementオブジェクトを初期化できます。Diffie-Hellmanの場合、Diffie-Hellman非公開鍵を使用して初期化します。補足的な初期化情報には、乱数の発生源、アルゴリズム・パラメータのセットが含まれます。要求された鍵協定アルゴリズムで、アルゴリズム・パラメータを指定する必要があり、またKeyAgreementオブジェクトの初期化にパラメータではなく鍵だけが提供される場合、必須のアルゴリズム・パラメータを鍵に含める必要があります。たとえば、Diffie-Hellmanアルゴリズムは、素数モジュラスpおよびベース・ジェネレータgをパラメータとして使用します。

KeyAgreementオブジェクトを初期化する場合、次のいずれかのinitメソッドを呼び出します。

public void init(Key key);

public void init(Key key, SecureRandom random);

public void init(Key key, AlgorithmParameterSpec params);

public void init(Key key, AlgorithmParameterSpec params,
SecureRandom random);

KeyAgreementフェーズの実行

各協定プロトコルは、鍵協定に関係する各パーティが実行する必要のある多数のフェーズで構成されます。

鍵協定の次のフェーズを実行するには、doPhaseメソッドを呼び出します。

public Key doPhase(Key key, boolean lastPhase);

keyパラメータには、そのフェーズで処理する鍵が含まれます。ほとんどの場合、これは、鍵協定に関係する他のいずれかのパーティの公開鍵、または前のフェーズで生成された中間鍵です。doPhaseは、この鍵協定の他のパーティに送信する必要のある中間鍵を返すため、他のパーティは続くフェーズでその鍵を処理できます。

lastPhaseパラメータには、実行するフェーズが鍵協定の最後のフェーズかどうかを指定します。値FALSEは、これが鍵協定の最後のフェーズではない(このあとにフェーズが続く)ことを示します。値TRUEは、これが鍵協定の最後のフェーズであり、鍵協定が完了する(次にgenerateSecretが呼び出される)ことを示します。

2つのパーティ間のDiffie-Hellman鍵交換の例では、lastPhaseをTRUEに設定してdoPhaseを呼び出します。Diffie-Hellmanで3つのパーティが存在する場合、doPhaseを2度呼び出します。最初はlastPhaseをFALSEに設定し、2度目にはlastPhaseをTRUEに設定します。

共有される秘密の生成

各パーティがすべての必須鍵協定フェーズを実行したあとで、generateSecretメソッドのいずれかを呼び出して共有される秘密を計算できます。

public byte[] generateSecret();

public int generateSecret(byte[] sharedSecret, int offset);

public SecretKey generateSecret(String algorithm);

鍵の管理

「キーストア」と呼ばれるデータベースは、鍵および証明書のリポジトリを管理するために使用できます。証明書とは、あるエンティティが発行したデジタル署名付きの文書で、別なエンティティの公開鍵が特定の値であることを証明しています。

キーストアの場所

ユーザー・キーストアは、デフォルトではユーザーのホーム・ディレクトリの.keystoreという名前のファイルに格納されます。ユーザーのホーム・ディレクトリは、user.homeシステム・プロパティによって決まります。このデフォルト値は、オペレーティング・システムによって異なります。

- Solaris、LinuxおよびMacOS: /home/username/

- Windows: C:\Users\username\

キーストア・ファイルは必要な場所に配置できます。環境によっては、複数のキーストアが存在することに意味がある場合があります。たとえば、1つのキーストアがユーザーの非公開鍵を保持し、別のキーストアが、信頼関係を確立するために使用される証明書を保持する場合があります。

ユーザーのキーストア以外に、JDKは、さまざまな証明書発行局(CA)からの信頼できる証明書を格納するために使用されるシステム全体のキーストアも保持します。これらのCA証明書は、信頼性の判定を行う場合に使用できます。たとえば、SSL/TLS/DTLSでSunJSSEプロバイダがリモート・ピアから証明書を提示された場合、デフォルトのトラスト・マネージャは、次のいずれかのファイルを調べて、その接続を信頼できるかどうかを判断します。

- Solaris、LinuxおよびMacOS: <java-home>/lib/security/cacerts

- Windows: <java-home>\lib\security\cacerts

システム全体のcacertsキーストアを使用する代わりに、アプリケーションは独自のキーストアを設定して使用するか、前述のユーザー・キーストアを使用できます。

キーストアの実装

KeyStoreクラスは、キーストア内の情報へのアクセスおよび情報の変更を行うための、明確に定義されたインタフェースを提供します。複数の異なる固定実装を生成できます(それぞれ特定の型のキーストアを実装)。

現在、KeyStoreを利用するコマンド行ツールには、keytoolとjarsignerの2つがあります。また、GUIベースのpolicytoolというツールもあります。様々なソースのコードに対して(システム・リソースへの)アクセス権を指定するポリシー・ファイルを処理する場合、このツールはPolicyのリファレンス実装によっても使われます。KeyStoreはpublicとして使用可能なので、JDKユーザーはKeyStoreを使った他のセキュリティ・アプリケーションも作成できます。

アプリケーションでは、KeyStoreクラスのgetInstanceファクトリ・メソッドを使うことで、さまざまなプロバイダから異なる型のキーストア実装を選択できます。キーストアの型は、キーストア情報のストレージ形式とデータ形式を定義するとともに、キーストア内の非公開鍵とキーストア自体の整合性を保護するために使われるアルゴリズムを定義します。異なる型のキーストアの実装には、互換性はありません。

デフォルトのキーストア実装はpkcs12です。これは、RSA PKCS12 Personal Information Exchange Syntax Standardに基づくクロス・プラットフォーム・キーストアです。この規格は、ユーザーの非公開鍵、証明書、その他の秘密を格納および転送することを主な目的としています。PKCS12キーストア内の個々のエントリに任意の属性を関連付けることができます。

keystore.type=pkcs12

ツールおよび他のアプリケーションで異なるデフォルト・キーストア実装を使用するには、この行を変更して様々な型を指定します。

keytoolなどの一部のアプリケーションでも、-storetypeコマンド行パラメータによって、デフォルトのキーストアの型をオーバーライドできます。

注意:
キーストアの型の指定では、大文字と小文字は区別されません。たとえば、「jks」と「JKS」は同じものとして扱われます。

PKCS12は、推奨されている、デフォルトのキーストア型です。ただし、他に3つの型のキーストアが、JDK実装に付属しています。

1. jceksは、jksに対する独自の代替キーストア形式であり、トリプルDESによるパスワードベース暗号化を使用します。
jceks実装は、jksキーストア・ファイルを解析してjceks形式に変換できます。キーストア内の非公開鍵エントリのパスワードを変更し、キーストアの型として"-storetype jceks"を指定することによって、「jks」型のキーストアを「jceks」型のキーストアにアップグレードできます。提供される(より)強力な暗号化による鍵保護を、デフォルト・キーストア内の「signkey」という名前の非公開鍵に適用する場合は、次のコマンドを入力します。このコマンドにより、旧パスワードおよび新規パスワードの指定が求められます。

keytool -keypass -alias signkey -storetype jceks

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

2. jksがもうひとつのオプションです。これは、独自のキーストアの型(形式)を利用するもので、キーストアをファイルとして実装しています。この実装では、個々の非公開鍵は独自の個別のパスワードによって保護され、キーストア全体の整合性も(非公開鍵とは別の)パスワードによって保護されます。

3. "dks"は、ドメイン・キーストアです。これは、単一の論理的なキーストアとして表されるキーストアの集合です。特定のドメインを構成するキーストアは、DomainLoadStoreParameterで説明されている構文を持つ構成データによって指定されます。

キーストアの実装は、プロバイダ・ベースです。独自のKeyStore実装を記述する必要がある場合は、Java暗号化アーキテクチャでのプロバイダの実装方法を参照してください。

KeyStoreクラス

KeyStoreクラスは、キーストア内の情報へのアクセスおよび情報の変更を行うための、明確に定義されたインタフェースを提供します。

KeyStoreクラスは、エンジン・クラスおよびアルゴリズムです。

図2-16 KeyStoreクラス

このクラスは、メモリー内の鍵および証明書のコレクションを表します。KeyStoreは次の2種類のエントリを管理します。

- 鍵のエントリ: このタイプのキーストア・エントリには、非常に重要な暗号化鍵情報が保持されます。この情報は、権限のないアクセスから保護する必要があります。通常、この種類のエントリに格納される鍵は非公開鍵で、対応する公開鍵を証明する証明書チェーンが伴います。
非公開鍵および証明書チェーンは、デジタル署名を使った自己認証用に特定のエンティティが使用します。たとえば、ソフトウェア配布団体は、リリースまたはライセンスするソフトウェアの一部としてJARファイルにデジタル署名を付けます。

- 信頼できる証明書のエントリ: このタイプのエントリには、別の相手に属する単一の公開鍵証明書が含まれます。これは、信頼できる証明書と呼ばれます。それは、証明書内の公開鍵が、証明書のsubject(所有者)によって特定されるアイデンティティに由来するものであることを、キーストアの所有者が信頼するからです。
この種類のエントリは、ほかの組織の認証に使うことができます。

キーストア内の各エントリは、「別名」文字列によって識別されます。非公開鍵とそれに関連付けられた証明書チェーンの場合は、これらの文字列はエンティティ自体が認証するというように、方法別に区別されます。たとえば、エンティティが異なる証明書発行局を使ったり、異なる公開鍵アルゴリズムを使ったりして、エンティティ自体を認証することも可能です。

キーストアが持続性があるかどうか、および持続性がある場合に使われるメカニズムは、ここでは指定されません。この規則により、重要な(秘密または非公開)鍵を保護するための様々な技術を使うことができます。スマート・カードまたはその他の統合暗号化エンジン(SafeKeyper)を使うことも1つの方法です。また、ファイルなどのより単純なメカニズムを様々な形式で使うこともできます。

主要なKeyStoreメソッドを次で説明します。

KeyStoreオブジェクトの作成

KeyStoreオブジェクトは、KeyStoreのgetInstance()メソッドのいずれかを使用して取得されます。プロバイダ実装の要求および獲得方法を参照してください。

特定キーストアのメモリーへのロード

KeyStoreオブジェクトを使う前に、loadメソッドによってメモリー内に実際のキーストア・データをロードする必要があります。

final void load(InputStream stream, char[] password)

オプションのパスワードを使って、キーストア・データの整合性をチェックします。パスワードが提供されない場合は、整合性のチェックは行われません。

空のキーストアを作成するには、InputStream引数としてnullをloadメソッドに渡します。

DKSキーストアは、DomainLoadStoreParameterを代替のloadメソッドに渡すことによってロードされます。

final void load(KeyStore.LoadStoreParameter param)

キーストアの別名一覧の取得

キーストアのすべてのエントリには、一意の別名を介してアクセスします。aliasesメソッドは、キーストア内の別名の列挙を返します。

final Enumeration aliases()

キーストア・エントリの種類の決定

「KeyStoreクラス」で説明したように、キーストアのエントリには、2種類あります。次のメソッドは、与えられた別名によって指定されたエントリが、それぞれ鍵または証明書か、信頼できる証明書エントリであることを決定します。

final boolean isKeyEntry(String alias)
final boolean isCertificateEntry(String alias)

キーストア・エントリの追加、設定、および削除

setCertificateEntryメソッドは、証明書を指定された別名に割り当てます。

final void setCertificateEntry(String alias, Certificate cert)

aliasが存在しない場合は、その別名のついた信頼できる証明書のエントリが作成されます。aliasが存在し、信頼できる証明書のエントリが識別された場合は、それに関連付けられた証明書がcertによって置き換えられます。

setKeyEntryメソッドは、aliasがまだ存在しない場合に、鍵のエントリの追加または設定を行います。

final void setKeyEntry(String alias,
Key key,
char[] password,
Certificate[] chain)

final void setKeyEntry(String alias,
byte[] key,
Certificate[] chain)

バイト配列としてkeyを取るメソッドでは、この引数は、保護された形式の鍵のバイトです。たとえば、SUNプロバイダによって提供されるキーストアの実装では、keyバイト配列は、PKCS8標準の定義に従ってEncryptedPrivateKeyInfoとしてエンコードされた、保護された非公開鍵を格納します。もう一方のメソッドのpasswordは、鍵の保護に使うパスワードです。

deleteEntryメソッドは、エントリを削除します。

final void deleteEntry(String alias)

PKCS #12キーストアは、任意の属性を含むエントリをサポートします。属性を作成するには、PKCS12Attributeクラスを使用します。新しいキーストア・エントリを作成するときは、属性を受け入れるコンストラクタを使用します。最後に、次のメソッドを使用してエントリをキーストアに追加します。

final void setEntry(String alias, Entry entry,
ProtectionParameter protParam)

キーストアからの情報の取得

getKeyメソッドは、与えられた別名に関連付けられた鍵を返します。鍵は、与えられたパスワードを使って復元されます。

final Key getKey(String alias, char[] password)

次のメソッドは、与えられた別名に関連付けられた証明書、または証明書チェーンをそれぞれ返します。

final Certificate getCertificate(String alias)
final Certificate[] getCertificateChain(String alias)

次の文を使って、与えられた証明書と一致した最初のエントリの名前(alias)を決定できます。

final String getCertificateAlias(Certificate cert)

PKCS #12キーストアは、任意の属性を含むエントリをサポートします。次のメソッドを使用して、属性を含む可能性があるエントリを取得します。

final Entry getEntry(String alias, ProtectionParameter protParam)

次に、KeyStore.Entry.getAttributesメソッドを使用してそのような属性を抽出し、KeyStore.Entry.Attributeインタフェースのメソッドを使用してそれらを調べます。

キーストアの保存

メモリー内のキーストアを、storeメソッドを使って保存できます。

final void store(OutputStream stream, char[] password)

パスワードは、キーストア・データの統合チェックサムの計算に使われます。統合チェックサムは、キーストア・データのあとに追加されます。

DKSキーストアは、DomainLoadStoreParameterを代替のstoreメソッドに渡すことによって格納されます。

final void store(KeyStore.LoadStoreParameter param)

アルゴリズム・パラメータのクラス

KeyおよびKeyspecと同様に、アルゴリズムの初期化パラメータはAlgorithmParameterまたはAlgorithmParameterSpecによって表されます。

使用状況によっては、アルゴリズムでパラメータを直接使用できますが、転送や格納のためにパラメータを移植性の高い形式に変換する必要がある場合もあります。

AlgorithmParameterSpecによるパラメータのセットの透明な表現とは、セットの各パラメータの値に個別にアクセスできることを意味します。これらの値には、対応する仕様クラスに定義されたgetメソッドの1つを使ってアクセスできます(DSAParameterSpecでは、getP、getQ、およびgetGメソッドを定義して、それぞれp、q、およびgにアクセスするなど)。

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

AlgorithmParameterSpecインタフェース

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

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

- DHParameterSpec

- DHGenParameterSpec

- DSAParameterSpec

- ECGenParameterSpec

- ECParameterSpec

- GCMParameterSpec

- IvParameterSpec

- MGF1ParameterSpec

- OAEPParameterSpec

- PBEParameterSpec

- PSSParameterSpec

- RC2ParameterSpec

- RC5ParameterSpec

- RSAKeyGenParameterSpec

次のアルゴリズム・パラメータ仕様は、特にXMLデジタル署名用に使用されます。

- Interface C14NMethodParameterSpec

- Interface DigestMethodParameterSpec

- Interface SignatureMethodParameterSpec

- Interface TransformParameterSpec

- Interface ExcC14NParameterSpec

- Interface HMACParameterSpec

- Interface XPathFilter2ParameterSpec

- Interface XPathFilterParameterSpec

- XSLTTransformParameterSpec

AlgorithmParametersクラス

AlgorithmParametersクラスは、暗号化パラメータの不透明な表現を提供します。

AlgorithmParametersクラス

AlgorithmParametersクラスはエンジン・クラスおよびアルゴリズムです。特定のAlgorithmParameterSpecオブジェクトを使用するか、または認識される書式でパラメータをエンコードすることによって、AlgorithmParametersクラスを初期化できます。結果の仕様をgetParameterSpecメソッドで取得できます(次のセクションを参照)。

AlgorithmParametersオブジェクトの作成

AlgorithmParametersオブジェクトは、AlgorithmParametersのgetInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。詳細は、プロバイダ実装の要求および獲得方法を参照してください。

AlgorithmParametersオブジェクトの初期化

AlgorithmParametersオブジェクトのインスタンスが生成されたら、適切なパラメータ仕様またはパラメータのエンコードにより、initを呼び出すことで初期化する必要があります。

void init(AlgorithmParameterSpec paramSpec)
void init(byte[] params)
void init(byte[] params, String format)

これらのinitメソッドでは、paramsは、エンコードされたパラメータを含む配列であり、formatは、デコード形式の名前です。params引数を指定し、format引数を指定しないinitメソッドでは、パラメータのプライマリ・デコード形式が使用されます。パラメータのASN.1仕様が存在する場合は、プライマリ・デコード形式は、ASN.1です。

エンコードされたパラメータの取得

AlgorithmParametersオブジェクトで表現されるパラメータのバイト・エンコードは、getEncodedへの呼出しによって取得できます。

byte[] getEncoded()

このメソッドは、パラメータをプライマリ・エンコード形式で返します。この種のパラメータのASN.1仕様が存在する場合は、プライマリ復号化形式は、ASN.1です。

特定のエンコード形式でパラメータが返されるようにするには、次のように記述します。

byte[] getEncoded(String format)

formatがnullの場合は、他のgetEncodedメソッドと同様に、プライマリ・エンコード形式が使用されます。

AlgorithmParametersオブジェクトから透明な仕様への変換

アルゴリズム・パラメータの透明なパラメータの仕様は、getParameterSpecへの呼出しにより、AlgorithmParametersオブジェクトから取得できます。

AlgorithmParameterSpec getParameterSpec(Class paramSpec)

paramSpecは、パラメータが返されるべき仕様クラスを識別します。たとえば、仕様クラスDSAParameterSpec.classが識別された場合、パラメータがDSAParameterSpecクラスのインスタンスに返されるべきであることを示します。このクラスはjava.security.specパッケージにあります。

AlgorithmParameterGeneratorクラス

AlgorithmParameterGeneratorクラスは、特定のアルゴリズム(このアルゴリズムは、AlgorithmParameterGeneratorインスタンスの作成時に指定される)に適した新しいパラメータのセットの生成に使用されるエンジン・クラスおよびアルゴリズムです。このオブジェクトは、既存のアルゴリズム・パラメータのセットがなく、最初から生成する場合に使用されます。

AlgorithmParameterGeneratorオブジェクトの作成

AlgorithmParameterGeneratorオブジェクトは、AlgorithmParameterGeneratorのgetInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。プロバイダ実装の要求および獲得方法を参照してください。

AlgorithmParameterGeneratorオブジェクトの初期化

AlgorithmParameterGeneratorオブジェクトは、アルゴリズム独立型、またはアルゴリズム固有型の2種類の方法で初期化できます。

アルゴリズム独立型の方法では、すべてのパラメータ・ジェネレータが「サイズ」および乱数発生の元という概念を共有するという特性を利用します。サイズの単位は、すべてアルゴリズム・パラメータで普遍的に共通していますが、その解釈はアルゴリズムにより異なります。たとえば、DSAアルゴリズムのパラメータの場合、「サイズ」は素数モジュラスのビット数のサイズに一致します。(特定のアルゴリズムのサイズの詳細は、Javaセキュリティ標準アルゴリズム名仕様を参照。)この方法を使うと、アルゴリズム固有型のパラメータの生成値が(ある場合は)、デフォルト基準値になります。普遍的に共有されるこれら2つの引数の型をとるinitメソッドが1つあります。

void init(int size, SecureRandom random);

また、size引数だけをとり、システムが提供する乱数の発生源を使用するinitメソッドもあります。

void init(int size)

3番目の方法では、パラメータ・ジェネレータ・オブジェクトの初期化にアルゴリズム固有型のセマンティックスを使います。アルゴリズム固有型のセマンティックスは、AlgorithmParameterSpecオブジェクト内に提供されるアルゴリズム固有型のパラメータ生成値のセットによって表されます。

void init(AlgorithmParameterSpec genParamSpec,
SecureRandom random)

void init(AlgorithmParameterSpec genParamSpec)

たとえば、Diffie-Hellmanシステム・パラメータを生成するには、通常、パラメータ生成値は、プライム・モジュラスおよびランダム指数のサイズで構成されます。どちらのサイズもビット数で指定します。

アルゴリズム・パラメータの生成

AlgorithmParameterGeneratorオブジェクトを作成および初期化したら、generateParametersメソッドを使ってアルゴリズム・パラメータを生成できます。

AlgorithmParameters generateParameters()

CertificateFactoryクラス

CertificateFactoryクラスは、証明書ファクトリの機能を定義します。証明書ファクトリは、エンコーディングから証明書のオブジェクトおよび証明書失効リスト(CRL)のオブジェクトを生成するために使用されます。

CertificateFactoryクラスは、エンジン・クラスおよびアルゴリズムです。

X.509の証明書ファクトリは、java.security.cert.X509Certificateのインスタンスである証明書、およびjava.security.cert.X509CRLのインスタンスであるCRLを返します。

CertificateFactoryオブジェクトの作成

CertificateFactoryオブジェクトは、getInstance() staticファクトリ・メソッドの1つを使用することによって取得されます。詳細は、プロバイダ実装の要求および獲得方法を参照してください。

証明書オブジェクトの生成

証明書オブジェクトを生成し、入力ストリームから読み込まれたデータを使って初期化するには、generateCertificateメソッドを使います。

final Certificate generateCertificate(InputStream inStream)

特定の入力ストリームから読み込まれた証明書のコレクション・ビュー(空の可能性もある)を返すには、generateCertificatesメソッドを使います。

final Collection generateCertificates(InputStream inStream)

CRLオブジェクトの生成

証明書失効リスト(CRL)オブジェクトを生成し、入力ストリームから読み込まれたデータを使って初期化するには、generateCRLメソッドを使います。

final CRL generateCRL(InputStream inStream)

特定の入力ストリームから読み込まれたCRLのコレクション・ビュー(空の可能性もある)を返すには、generateCRLsメソッドを使います。

final Collection generateCRLs(InputStream inStream)

CertPathオブジェクトの生成

PKIX用の証明書パス・ビルダーおよびバリデータは、RFC 5280「Internet X.509 Public Key Infrastructure Certificate and CRL Profile」によって定義されます。

PKIX LDAP V2スキーマを使用して証明書とCRLをCollectionディレクトリおよびLDAPディレクトリから取得するための証明書ストアの実装も、RFC 2587としてIETFから使用可能です。

CertPathオブジェクトを生成し、そのオブジェクトを入力ストリームから読み込まれたデータを使って初期化するには、次のいずれかのgenerateCertPathメソッドを使用します(必要に応じて、データに対して使用するエンコーディングを指定します)。

final CertPath generateCertPath(InputStream inStream)

final CertPath generateCertPath(InputStream inStream,
String encoding)

CertPathオブジェクトを生成し、証明書のリストを使用して初期化するには、次のメソッドを使用します。

final CertPath generateCertPath(List certificates)

この証明書ファクトリでサポートされているCertPathエンコーディングのリストを取得するには、getCertPathEncodingsメソッドを呼び出します。

final Iterator getCertPathEncodings()

初めにデフォルトのエンコーディングが一覧表示されます。

SSL/TLS実装でのJCAの使用方法

JCAのクラスについて理解したため、次に、これらのクラスがどのように結合されてSSL/TLSのような高度なネットワーク・プロトコルを実装するかについて考えます。

SSL、TLSおよびDTLSプロトコルの「SSL/TLSの概要」の項に、プロトコルの機能のおおまかな説明があります。非対称(公開鍵)暗号操作は対称操作(秘密鍵)よりも大幅に遅いため、公開鍵暗号方式は、実際のアプリケーション・データを保護するために使用される秘密鍵を確立する場合に使用されます。非常に単純に言うと、SSL/TLSハンドシェークには、初期化データの交換、いくつかの公開鍵操作の実行による秘密鍵への到達、およびその鍵を使用したそのあとのトラフィックの暗号化が含まれます。

注意:
ここで説明する詳細では、単純に、前述のクラスのいくつかをどのように使用できるかを示します。このセクションでは、SSL/TLS実装を構築するための十分な情報は提供しません。詳細は、Java Secure Socket Extension (JSSE)リファレンス・ガイドおよびRFC 5246: Transport Layer Security (TLS)プロトコル、バージョン1.2を参照してください。

このSSL/TLS実装がJSSEプロバイダとして使用可能になると仮定します。Providerクラスの固定実装が最初に記述され、最終的にプロバイダのSecurityクラスのリストに登録されます。このプロバイダは、主に、アルゴリズム名から実際の実装クラスへのマッピングを提供します(たとえば、SSLContext.TLS->com.foo.TLSImpl)。アプリケーションが(SSLContext.getInstance("TLS")によって)TLSインスタンスを要求すると、プロバイダのリストに、要求されたアルゴリズムが問合せられ、適切なインスタンスが作成されます。

実際のハンドシェークの詳細を説明する前に、JSSEのアーキテクチャのいくつかを簡単に確認する必要があります。JSSEアーキテクチャの中心は、SSLContextです。コンテキストは、最終的にSSL/TLSプロトコルを実際に実装する最終オブジェクト(SSLSocketおよびSSLEngine)を作成します。SSLContextは、2つのコールバック・クラスKeyManagerおよびTrustManagerによって初期化されます。これらを使用すると、アプリケーションは送信する認証データを最初に選択し、次にピアによって送信された資格を検証できます。

JSSE KeyManagerは、ピアに提示する資格の選択を行います。多くのアルゴリズムが可能ですが、一般的な方法は、RSAまたはDSA公開鍵と非公開鍵のペアを、ディスク・ファイルに基づくKeyStore内のX509Certificateとともに保持することです。KeyStoreオブジェクトが初期化されてファイルからロードされると、ファイルの未変換のバイトはKeyFactoryを使用してPublicKeyおよびPrivateKeyオブジェクトに変換され、証明書チェーンのバイトはCertificateFactoryを使用して変換されます。資格が必要な場合、KeyManagerは単純にこのKeyStoreオブジェクトに問い合わせ、どの資格を提示するかを判断します。

KeyStoreの内容は、最初にkeytoolなどのユーティリティを使用して作成されている場合があります。keytoolは、RSAまたはDSAのKeyPairGeneratorを作成し、適切な鍵サイズで初期化します。このジェネレータは、次に、KeyPair (最終的にディスクに書き込まれる)を作成する場合に使用されます。これはkeytoolにより、新しく作成された証明書とともにKeyStoreに格納されます。

JSSE TrustManagerは、ピアから受信した資格の検証を行います。資格の検証には多数の方法があります。その1つは、CertPathオブジェクトを作成し、JDKの組込み公開鍵インフラストラクチャ(PKI)フレームワークで検証を処理する方法です。内部的に、CertPath実装はSignatureオブジェクトを作成し、それを使用して証明書チェーン内のそれぞれの署名を検証する場合があります。

アーキテクチャの基本について理解することによって、SSL/TLSハンドシェークの手順のいくつかを調べることができます。クライアントは、ClientHelloメッセージをサーバーに送信することによって開始します。サーバーは使用する暗号群を選択し、ServerHelloメッセージ内でそれを返信し、暗号群の選択に基づいてJCAオブジェクトの作成を開始します。次の例では、サーバーのみの認証を使用します。

図2-17 SSL/TLSメッセージ

サーバーのみの認証を、次の例で説明します。この例は非常に単純化されていますが、より高レベルのプロトコルを作成するためにJSSEクラスをどのように組み合せることができるかを示しています。

例2-9 SSL/TLSサーバーでTLS_RSA_WITH_AES_128_CBC_SHAなどのRSAベースの暗号群を使用する

サーバーのKeyManagerがクエリーされ、適切なRSAエントリを返します。サーバーの資格(つまり、証明書/公開鍵)がサーバーのCertificateメッセージ内で送信されます。クライアントのTrustManagerはサーバーの証明書を検証し、受け入れた場合、クライアントはSecureRandomオブジェクトを使用していくつかのランダム・バイトを生成します。次に、サーバーの証明書内で見つかったPublicKeyによって初期化された暗号化非対称RSA Cipherオブジェクトを使用して暗号化されます。この暗号化されたデータは、Client Key Exchangeメッセージ内で送信されます。サーバーは、対応するPrivateKeyを使用し、同様のCipherを復号モードで使用してバイトを回復します。これらのバイトは、実際の暗号化鍵を確立する場合に使用されます。

例2-10 一時的なDiffie-Hellman鍵協定アルゴリズムとともにTLS_DHE_DSS_WITH_AES_128_CBC_SHAなどのDSA署名アルゴリズムを選択する

両方の側でそれぞれ、KeyPairGeneratorを使用して、新しい一時的なDH公開鍵と非公開鍵のペアを確立します。各ジェネレータは、KeyFactoryおよびDHPublicKeySpecクラスを使用して、さらに細かく変換できるDH鍵を作成します。それぞれの側は、次にKeyAgreementオブジェクトを作成し、それぞれのDH PrivateKeyによって初期化します。サーバーはその公開鍵をServerKeyExchangeメッセージで送信し(DSA署名アルゴリズムによって保護される)、クライアントはその公開鍵をClientKeyExchangeメッセージで送信します。公開鍵が別のKeyFactoryを使用して再度組み立てられると、これらは協定オブジェクトに渡されます。KeyAgreementオブジェクトは、実際の暗号化鍵を確立する場合に使用される合意済みのバイトを生成します。
実際の暗号化鍵が確立されると、対称Cipherオブジェクトを初期化するために秘密鍵が使用され、この暗号が送信中のすべてのデータを保護する場合に使用されます。データが変更されたかどうかを判断できるようにMessageDigestが作成され、ネットワークに送信されるデータのコピーを受信します。パケットが完全な場合、ダイジェスト(ハッシュ)がデータのあとに付加され、パケット全体がCipherによって暗号化されます。AESなどのブロック暗号が使用される場合は、データにパディングを行なって完全なブロックにします。リモート側では、手順は単純に逆になります。

暗号強度の構成

管轄ポリシー・ファイル(管轄ポリシー・ファイル形式を参照)とセキュリティ・プロパティ・ファイルを使用してJava Cryptography Extension (JCE)アーキテクチャの暗号強度を構成できます。

Oracle Java JDK 9より前は、Oracle実装によって許可されているデフォルト暗号強度は、強力だが制限付きでした(たとえば、128ビットに制限されたAES鍵)。この制限をなくすには、管理者が、無制限強度の管轄ポリシー・ファイルのバンドルを別にダウンロードしてインストールします。管轄ポリシー・ファイルのメカニズムは、JDK 9用に改変されました。現在は、より柔軟な構成が可能になっています。Oracle JDKでは、デフォルト値が、limitedではなくunlimitedになりました。これまでどおり、管理者およびユーザーは、自分の地理的な場所に応じたすべての輸入/輸出ガイドラインに従う必要があります。アクティブな暗号強度は、構成ディレクトリにある管轄ポリシー・ファイルと組み合せてセキュリティ・プロパティ(通常は、java.securityプロパティ・ファイル内で設定される)を使用して決定されるようになっています。

無制限の暗号強度または強力だが制限付きの暗号強度を提供するために必要なJCEポリシー・ファイルはすべてJDKに含まれています。

暗号強度の設定

<java_home>/conf/security/policyの下の各ディレクトリは、それらに含まれている管轄ポリシー・ファイルで定義された一連のポリシー構成を表します。ディレクトリ内のポリシー・ファイルによって表される特定の暗号強度設定をアクティブ化するには、crypto.policyセキュリティ・プロパティ(ファイル<java_home>/conf/security/java.security内で構成される)をそのディレクトリを指すように設定します。

JDKには、limitedとunlimitedという2つのディレクトリがセットになっており、それぞれにいくつかのポリシー・ファイルが含まれています。デフォルトでは、crypto.policyセキュリティ・プロパティは、次のように設定されています。

crypto.policy = unlimited

この値全体は、このディレクトリに含まれているファイル間で共通の部分です。これらのポリシー・ファイル設定は、VM規模で適用され、このVM上で実行されるすべてのアプリケーションに影響します。アプリケーション・レベルで暗号強度をオーバーライドする必要がある場合は、アプリケーションの暗号化制限の免責を取得する方法を参照してください。

Unlimitedディレクトリの内容

unlimitedディレクトリには、次のポリシー・ファイルが含まれています。

- <java_home>/conf/security/unlimited/default_US_export.policy

// Default US Export policy file.
grant {
// There is no restriction to any algorithms.
permission javax.crypto.CryptoAllPermission;
};

注意:
現在は米国からの暗号化の輸出に関する制限はないため、default_US_export.policyファイルは制限なしで設定されます。

- <java_home>/conf/security/unlimited/default_local.policy

// Country specific policy file for countries with no limits on crypto strength.
grant {
// There is no restriction to any algorithms.
permission javax.crypto.CryptoAllPermission;
};

注意:
国によっては、現地の制限事項がある場合がありますが、このポリシー・ファイルはunlimitedディレクトリにあるため、ここでは制限は示されません。

これら2つのファイルで定義されているように無制限暗号強度を選択するには、ファイル<java_home>/conf/security/java.security内でcrypto.policy = unlimitedと設定します。

Limitedディレクトリの内容

limitedディレクトリには、現在、次のポリシー・ファイルが含まれています。

- <java_home>/conf/security/limited/default_US_export.policy

// Default US Export policy file.
grant {
// There is no restriction to any algorithms.
permission javax.crypto.CryptoAllPermission;
};

注意:
たとえこれがlimitedディレクトリ内にあるとしても、現在は米国からの暗号化の輸出に関する制限はないため、default_US_export.policyファイルは制限なしで設定されます。

- <java_home>/conf/security/limited/default_local.policy

// Some countries have import limits on crypto strength. This policy file
// is worldwide importable.

grant {
permission javax.crypto.CryptoPermission "DES", 64;
permission javax.crypto.CryptoPermission "DESede", *;
permission javax.crypto.CryptoPermission "RC2", 128,
"javax.crypto.spec.RC2ParameterSpec", 128;
permission javax.crypto.CryptoPermission "RC4", 128;
permission javax.crypto.CryptoPermission "RC5", 128,
"javax.crypto.spec.RC5ParameterSpec", *, 12, *;
permission javax.crypto.CryptoPermission "RSA", *;
permission javax.crypto.CryptoPermission *, 128;
};

注意:
このローカル・ポリシー・ファイルでは、デフォルトの制限が示されます。輸入制限があるものを含め、これは国によって許可される必要があります。ただし、法的ガイダンスを入手してください。

- <java_home>/conf/security/limited/exempt_local.policy

// Some countries have import limits on crypto strength, but may allow for
// these exemptions if the exemption mechanism is used.

grant {
// There is no restriction to any algorithms if KeyRecovery is enforced.
permission javax.crypto.CryptoPermission *, "KeyRecovery";

// There is no restriction to any algorithms if KeyEscrow is enforced.
permission javax.crypto.CryptoPermission *, "KeyEscrow";

// There is no restriction to any algorithms if KeyWeakening is enforced.
permission javax.crypto.CryptoPermission *, "KeyWeakening";
};

注意:
輸入制限がある国は、limitedを使用する必要がありますが、免責メカニズムを採用できる場合は、これらの制限を緩和できます。アプリケーションの暗号化制限の免責を取得する方法を参照してください。ご自分の状況に応じた法的ガイダンスを入手してください。

カスタム暗号強度の設定

制限をlimitedまたはunlimitedディレクトリにあるポリシー・ファイル内の設定とは異なる暗号強度に設定するには、limitedおよびunlimitedと同列に新しいディレクトリを作成し、そこにポリシー・ファイルを配置します。たとえば、customというディレクトリを作成するとします。このcustomディレクトリに、ファイルdefault_*export.policyまたはexempt_*local.policy、あるいはその両方を含めます。

customディレクトリ内のファイルで定義されているように暗号強度を選択するには、ファイル<java_home>/conf/security/java.security内でcrypto.policy = customと設定します。

管轄ポリシー・ファイル形式

JCAの管轄ポリシー・ファイルは、Java形式のポリシー・ファイル(対応するアクセス権を指定する文を含む)で表されます。暗号強度の構成で説明したように、Javaポリシー・ファイルは、指定されたコード・ソースからのコードに許可するアクセス権を指定します。アクセス権は、システム・リソースへのアクセスを表します。JCAの場合、「リソース」は暗号化アルゴリズムです。また、暗号化制限はすべてのコードに適用されるため、コード・ソースを指定する必要はありません。

管轄ポリシー・ファイルは、1つ以上の「アクセス権エントリ」を含む、非常に基本的な「付与エントリ」で構成されます。

grant {
<permission entries>;
};

次に、管轄ポリシー・ファイルのアクセス権エントリの書式を示します。

permission <crypto permission class name>
[<alg_name>
[
[, <exemption mechanism name>]
[, <maxKeySize>
[, <AlgorithmParameterSpec class name>,
<parameters for constructing an AlgorithmParameterSpec object>
]
]
]
];

次に、AESアルゴリズムを128ビットの最大鍵サイズに制限する、管轄ポリシー・ファイルのサンプルを示します。

grant {
permission javax.crypto.CryptoPermission "AES", 128;
// ...
};

アクセス権エントリは、単語permissionで始まります。アクセス権エントリの各項目は、指定された順序で記述する必要があります。各エントリはセミコロンで終わります。識別子(grant、permission)では大文字と小文字は区別されませんが、<crypto permission class name>と、値として引き渡される文字列については大文字と小文字が区別されます。アスタリスク(*)は、すべてのアクセス権エントリ・オプションでワイルドカードとして使用できます。たとえば、<alg_name>オプションにアスタリスクを指定すると、すべてのアルゴリズムという意味になります。

次の表に、アクセス権エントリのオプションを示します。

表2-1 アクセス権エントリのオプション

オプション

説明

<crypto permission class name>

javax.crypto.CryptoPermissionなど、特定のアクセス権クラス名。必須です。

暗号化アクセス権クラスは、特定の環境下で特定の鍵サイズで、特定のアルゴリズムを使用するアプリケーションの機能に対応します。暗号化アクセス権クラスには、CryptoPermissionおよびCryptoAllPermissionの2つが存在します。特別なCryptoAllPermissionクラスは、暗号化関連のアクセス権すべてを表します。つまり、暗号化関連の制限はないことを示します。

<alg_name>

"AES"や"RSA"など、暗号化アルゴリズムの標準名を指定する引用符付き文字列。オプションです。

<exemption mechanism name>

免責メカニズムを示す引用符付き文字列。免責メカニズムを実施すると、暗号化制限が緩和されます。オプションです。

使用可能な免責メカニズム名には、「KeyRecovery」、「KeyEscrow」および「KeyWeakening」が含まれます。

<maxKeySize>

指定したアルゴリズムに許可する最大鍵サイズ(ビット)を示す整数値。オプションです。

<AlgorithmParameterSpec class name>

アルゴリズムの強度を指定するクラス名。オプションです。

アルゴリズムによっては、鍵サイズでアルゴリズムの強度を指定するだけでは不十分な場合があります。たとえば、「RC5」アルゴリズムの場合には、ラウンド数も考慮する必要があります。アルゴリズムの強度を鍵サイズだけで表現するのでは不十分な場合、このオプションを使用して、これを行うAlgorithmParameterSpecクラス名(RC5アルゴリズムの場合はjavax.crypto.spec.RC5ParameterSpecなど)を指定します。

<parameters for constructing an AlgorithmParameterSpec object>

指定されたAlgorithmParameterSpecオブジェクトを構成するためのパラメータのリスト。<AlgorithmParameterSpec class name>が指定されており、パラメータが必要な場合は必須です。

アプリケーションの暗号化制限の免責を取得する方法

注意:
大半のアプリケーション開発者には、この項で説明する内容は関係ありません。関係があるのは、作成するアプリケーションが、政府により暗号化制限の課された国に輸出される可能性があり、アプリケーションをその制限に適合させる必要がある場合だけです。

デフォルトでは、アプリケーションは、どの強度の暗号化アルゴリズムでも使用できます。ただし、一部の国の政府による輸入管理制限のため、それらのアルゴリズムの強度を制限する必要がある場合があります。JCAフレームワークには、様々な管轄コンテキスト(場所)でアプリケーションに使用可能な暗号化アルゴリズムの最大強度に関して、制限を強制する機能が含まれます。管轄ポリシー・ファイルでこれらの制限を指定します。管轄ポリシー・ファイルとそれらを作成および構成する方法の詳細は、暗号強度の構成を参照してください。

これらの国の一部またはすべてで、特定のアプリケーションに対し、暗号化制限の一部またはすべての免責が許可されています。たとえば、特定の種類のアプリケーションは「特別」と見なされ、免責されます。また、鍵復元などの「免責メカニズム」を利用するアプリケーションは、免責可能です。この種の国では、免責されたと見なされるアプリケーションは、免責されていないアプリケーションに許可されるよりも強力な暗号化にアクセスできます。

実行時にアプリケーションが「免責されている」と認識されるようにするには、次の条件を満たす必要があります。

- JARファイル内にアクセス権ポリシー・ファイルをバンドルする必要があります。アクセス権ポリシー・ファイルには、アプリケーションが保持する暗号化関連のアクセス権、およびそれを保持する条件(存在する場合)を指定します。

- アプリケーションおよびアクセス権ポリシー・ファイルを含むJARファイルには、アプリケーションが免責を受け取ったあとで発行されたコード署名証明書を使用して署名する必要があります。

次に、アプリケーションにいくつかの暗号化制限が適用されないようにするために必要な手順のサンプルを示します。これは、免責されたものとしてアプリケーションを認識および処理するため、JCAにより要求される情報を含む、基本情報です。実際には、アプリケーションを実行可能にする(政府が暗号化制限を課している)特定の国の免責要件を知る必要があります。また、免責されたアプリケーションの処理プロセスを保持するJCAフレームワーク・ベンダーの要件も理解しておく必要があります。詳細は、ベンダーにお尋ね下さい。

注意:
SunJCEプロバイダは、ExemptionMechanismSpiクラスの実装を提供しません

1. アプリケーション・コードの記述およびコンパイル

2. 適切な暗号化アクセス権を付与するアクセス権ポリシー・ファイルの作成

3. テストの準備

a. 政府の課す制限に関する政府承認の申請

b. コード署名証明書の取得

c. JARファイルへのアプリケーションおよびアクセス権ポリシー・ファイルのバンドル

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

e. 制限国のユーザーと同じ環境の設定

f. アクセス権ポリシー・ファイルのエントリによって指定された免責メカニズムを実装するプロバイダのインストール(免責メカニズムを使用するアプリケーションのみ)

4. アプリケーションのテスト

5. 米国政府による輸出承認の申請(必要な場合)

6. アプリケーションのデプロイ

免責メカニズムを使用するアプリケーションに対する特殊コード要件

アプリケーションに、関連付けられたアクセス権ポリシー・ファイルがあり(同じJARファイル内)、そのアクセス権ポリシー・ファイルで免責メカニズムが指定されている場合、CipherのgetInstanceメソッドが呼び出されてCipherがインスタンス化されると、JCAコードは、インストール済プロバイダの中から、指定された免責メカニズムを実装するプロバイダを検索します。目的のプロバイダが見つかると、JCAは、そのプロバイダの実装に関連付けられたExemptionMechanism APIオブジェクトをインスタンス化してから、ExemptionMechanismオブジェクトを、getInstanceによって返されたCipherと関連付けます。

Cipherをインスタンス化した後、初期化する前に(Cipherのinitメソッドを呼び出して)、コードから次のCipherメソッドを呼び出す必要があります。

public ExemptionMechanism getExemptionMechanism()

この呼出しにより、Cipherに関連付けられたExemptionMechanismオブジェクトが返されます。次に、返されたExemptionMechanismに対して次のメソッドを実行して、免責メカニズムの実装を初期化する必要があります。

public final void init(Key key)

ここで指定する引数の型は、このあとでCipher initメソッドに指定する引数の型と同じにする必要があります。

ExemptionMechanismの初期化が完了したら、通常と同じ方法でCipherを初期化して使用できます。

アクセス権ポリシー・ファイル

実行時にアプリケーションが暗号化制限の一部またはすべてを「免責」されていると認識されるには、JARファイル内にアクセス権ポリシー・ファイルをバンドルする必要があります。アクセス権ポリシー・ファイルには、アプリケーションが保持する暗号化関連のアクセス権、およびそれを保持する条件(存在する場合)を指定します。

免責されるアプリケーションにバンドルされるアクセス権ポリシー・ファイル内のアクセス権エントリの書式は、JDKとともにダウンロードされる管轄ポリシー・ファイルの書式と同じです。次にその書式を示します。

permission <crypto permission class name>
[<alg_name>
[
[, <exemption mechanism name>]
[, <maxKeySize>
[, <AlgorithmParameterSpec class name>,
<parameters for constructing an AlgorithmParameterSpec object>
]
]
]
];

管轄ポリシー・ファイル形式を参照してください。

免責されるアプリケーションのアクセス権ポリシー・ファイル

アプリケーションの中には、制限を完全に解除可能なものもあります。通常、その種のアプリケーションにバンドルするアクセス権ポリシー・ファイルには、次を含めるだけで十分です。

grant {
// There are no restrictions to any algorithms.
permission javax.crypto.CryptoAllPermission;
};

アプリケーションが1つの(またはいくつかの特定の)アルゴリズムだけを使用する場合、アクセス権ポリシー・ファイルには、CryptoAllPermissionを付与するのではなく、そのアルゴリズムを明示的に記述します。

たとえば、アプリケーションがBlowfishアルゴリズムだけを使用する場合、アクセス権ポリシー・ファイルですべてのアルゴリズムにCryptoAllPermissionを付与する必要はありません。Blowfishアルゴリズムが使用される場合、暗号化制限が存在しないことを指定するだけで十分です。この場合、アクセス権ポリシー・ファイルは、次のようになります。

grant {
permission javax.crypto.CryptoPermission "Blowfish";
};

免責メカニズムにより免責されるアプリケーションのアクセス権ポリシー・ファイル

免責メカニズムが導入されるためにアプリケーションが「免責される」と見なされる場合、アプリケーションにバンドルされるアクセス権ポリシー・ファイルに1つ以上の免責メカニズムを指定する必要があります。実行時に、これらの免責メカニズムのいずれかが機能していると、アプリケーションは免責されたものと見なされます。次のようなアクセス権エントリ内に、各免責メカニズムを指定する必要があります。

// No algorithm restrictions if specified
// exemption mechanism is enforced.
permission javax.crypto.CryptoPermission *,
"<ExemptionMechanismName>";

ここで、<ExemptionMechanismName>には免責メカニズムの名前を指定します。指定可能な免責メカニズムの名前には、次が含まれます。

- KeyRecovery

- KeyEscrow

- KeyWeakening

例として、鍵復元または鍵エスクローのいずれかが機能すると、アプリケーションが免責される場合を考えましょう。その場合、アクセス権ポリシー・ファイルには、次が含まれます。

grant {
// No algorithm restrictions if KeyRecovery is enforced.
permission javax.crypto.CryptoPermission *, "KeyRecovery";

// No algorithm restrictions if KeyEscrow is enforced.
permission javax.crypto.CryptoPermission *, "KeyEscrow";
};

注意:
免責メカニズムを指定するアクセス権エントリには、最大鍵サイズを指定しないでください。許可される鍵のサイズは、実際にはインストールされた免責管轄ポリシー・ファイルにより決定されます。詳細は、次のセクションを参照してください。

バンドルされたアクセス権ポリシー・ファイルによる暗号化アクセス権への影響

実行時にアプリケーションが(getInstanceメソッドを呼び出すことで)Cipherをインスタンス化し、かつそのアプリケーションに、関連するアクセス権ポリシー・ファイルがある場合、JCAはそのアクセス権ポリシー・ファイルに、getInstance呼出しで指定されたアルゴリズムに適用されるエントリが含まれるかどうかをチェックします。該当するエントリが含まれ、そのエントリがCryptoAllPermissionを付与するか免責メカニズムの実施を指定しない場合、このアルゴリズムには暗号化制限が存在しないことを意味します。

アクセス権ポリシー・ファイルにgetInstanceの呼出しで指定されたアルゴリズムに適用されるエントリが含まれ、かつエントリで免責メカニズムの実施が指定されている場合、免責管轄ポリシー・ファイルがチェックされます。免責されるアクセス権に、関連するアルゴリズムおよび免責メカニズムのエントリが含まれており、そのエントリがアプリケーションにバンドルされたアクセス権ポリシー・ファイル内のアクセス権により暗黙的に設定されている場合、および指定された免責メカニズムの実装がいずれかの登録済プロバイダから利用可能な場合、Cipherの最大鍵サイズおよびアルゴリズム・パラメータ値は、免責アクセス権エントリにより決定されます。

アプリケーションにバンドルされるアクセス権ポリシー・ファイル内の関連するエントリに、暗黙的に設定された免責アクセス権が存在しない場合、またはいずれかの登録済みプロバイダから利用可能な、指定された免責メカニズムの実装が存在しない場合、デフォルトの標準暗号化アクセス権のみがアプリケーションに付与されます。

標準名

「標準名」ドキュメントには、アルゴリズム仕様に関する情報が含まれています。

Javaセキュリティ標準アルゴリズム名では、JDK Security APIで必要となり使用されるアルゴリズム、証明書およびキーストア・タイプの標準名について説明します。これには、アルゴリズムの仕様に関する詳細も含まれています。特定のプロバイダの情報は、JDKプロバイダにあります。

JDKでの暗号化実装は、主に歴史的な理由により、さまざまなプロバイダによって配布されます(Sun、SunJSSE、SunJCE、SunRsaSign)。これらのプロバイダは、すべてのJDK実装で使用可能ではない場合があるため、真に移殖可能なアプリケーションは、特定のプロバイダを指定せずにgetInstance()を呼び出す必要があります。特定のプロバイダを指定するアプリケーションは、基盤となるオペレーティング環境(PKCSやMicrosoftのCAPIなど)用に調整されたネイティブ・プロバイダを使用できない場合があります。

SunPKCS11プロバイダ自体には暗号化アルゴリズムは含まれていませんが、代わりに要求を基盤となるPKCS11実装に送ります。PKCS#11リファレンス・ガイドおよび基盤となるPKCS11実装を参照して、必要なアルゴリズムがPKCS11プロバイダで使用可能かどうかを判断するようにしてください。同様に、Windowsシステムでは、SunMSCAPIプロバイダは暗号化機能を提供しませんが、代わりに基盤となるオペレーティング・システムに要求を処理するよう渡します。

アプリケーションのパッケージ化

アプリケーションは、次の3種類のモジュールにパッケージ化できます。

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

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

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

名前付きモジュールは他よりも優れたパフォーマンス、強力なカプセル化、および簡単な構成を提供するため、アプリケーションを名前付きモジュールにパッケージ化することをお薦めします。それらは、高い柔軟性も提供します。モジュラJDKのクラス・パスにあるそれらを指定することで、非モジュラJDKで、または名前のないモジュールとしてでも、それらを使用できます。

モジュールの詳細は、モジュール・システムの状態とJEP 261: モジュール・システムを参照してください。

その他のJCAコード・サンプル

これらの例では、いくつかのJCAメカニズムの使用を説明します。Diffie-Hellman鍵交換、AES/GCMおよびHMAC-SHA256のサンプル・プログラムも参照してください。

トピック

MessageDigestオブジェクトの計算

鍵のペアの生成

生成された鍵を使った署名の生成および検証

鍵仕様およびKeyFactoryを使った署名の生成と検証

2つの鍵の同一性の判定

Base64でエンコードされた証明書の読み取り

認証応答の解析

暗号化の使用

パスワードベース暗号化の使用

MessageDigestオブジェクトの計算

MessageDigestオブジェクトを計算する手順を示す例。

1. 次の例で示すように、MessageDigestオブジェクトを作成します。

MessageDigest sha = MessageDigest.getInstance("SHA-256");

この呼出しは、正しく初期化されたメッセージ・ダイジェスト・オブジェクトを変数shaに代入します。この実装は、National Institute for Standards and Technology (NIST)のFIPS 180-2ドキュメントの定義に従って、Secure Hashアルゴリズム(SHA-256)を実装します。

2. 3つのバイト配列i1、i2およびi3があるとします。この3つの配列から形成される合計入力のメッセージ・ダイジェストを計算します。このダイジェスト(または「ハッシュ」)は、次の呼出しを使って計算できます。

sha.update(i1);
sha.update(i2);
sha.update(i3);
byte[] hash = sha.digest();

3. オプション: 次の一連の呼出しを使用しても同じです。

sha.update(i1);
sha.update(i2);
byte[] hash = sha.digest(i3);

メッセージ・ダイジェストが計算されると、メッセージ・ダイジェスト・オブジェクトは自動的にリセットされ、新しいデータを受信してそのダイジェストを計算できる状態になります。以前の状態(たとえばupdate呼出しで入れたデータ)はすべて失われます。

例2-11 複製によるハッシュ実装

ハッシュ実装によっては、複製(コピー)を介して中間ハッシュをサポートするものもあります。次について、別々のハッシュを計算すると仮定します。

- i1

- i1およびi2

- i1、i2およびi3

次に、これらのハッシュを計算する1つの方法を示します。ただし、このコードは、SHA-256実装が複製可能である場合のみ機能します。

/* compute the hash for i1 */
sha.update(i1);
byte[] i1Hash = sha.clone().digest();

/* compute the hash for i1 and i2 */
sha.update(i2);
byte[] i12Hash = sha.clone().digest();

/* compute the hash for i1, i2 and i3 */
sha.update(i3);
byte[] i123hash = sha.digest();

例2-12 ハッシュ実装が複製可能か複製不可能かを判断する

メッセージ・ダイジェストの実装の中には、複製可能なものもあれば、不可能なものもあります。複製(コピー)が可能かどうかを判断するには、次のようにMessageDigestオブジェクトを複製し、発生する可能性のある例外をキャッチします。

try {
// try and clone it
/* compute the hash for i1 */
sha.update(i1);
byte[] i1Hash = sha.clone().digest();
// ...
byte[] i123hash = sha.digest();
} catch (CloneNotSupportedException cnse) {
// do something else, such as the code shown below
}

例2-13 ハッシュ実装が複製可能ではない場合に中間ダイジェストを計算する

メッセージ・ダイジェストが複製不可能な場合は、別の多少複雑な方法を使います。つまり、複数のダイジェストを生成して中間ダイジェストを計算します。この場合、計算する中間ダイジェストの数をあらかじめ知っておく必要があります。

MessageDigest md1 = MessageDigest.getInstance("SHA-256");
MessageDigest md2 = MessageDigest.getInstance("SHA-256");
MessageDigest md3 = MessageDigest.getInstance("SHA-256");

byte[] i1Hash = md1.digest(i1);

md2.update(i1);
byte[] i12Hash = md2.digest(i2);

md3.update(i1);
md3.update(i2);
byte[] i123Hash = md3.digest(i3);

鍵のペアの生成

この例では、「DSA」(デジタル署名アルゴリズム)という名前のアルゴリズムについて、「公開 - 非公開」の鍵のペアを生成し、この鍵のペアをそのあとの例で使用します。鍵は、2048ビット・モジュラスで生成します。ここでは、アルゴリズム実装を提供するプロバイダは考慮しません。

鍵ペア・ジェネレータの生成

最初の手順は、DSAアルゴリズムの鍵を生成するための鍵のペア・ジェネレータ・オブジェクトの獲得です。

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");

鍵ペア・ジェネレータの初期化

次の手順は、鍵のペア・ジェネレータの初期化です。ほとんどの場合、アルゴリズム独立型の初期化で十分ですが、アルゴリズム固有型の初期化を使う必要がある場合もあります。

アルゴリズムに依存しない初期化

すべての鍵ジェネレータは、キー・サイズおよび乱数発生の元の概念を共有します。KeyPairGeneratorクラス初期化メソッドには、最低限キー・サイズが必要です。乱数の発生源が明示的に提供されない場合、もっとも高い優先順位でインストールされているプロバイダのSecureRandom実装が使用されます。したがって、キー・サイズ2048で鍵を生成するには、次のコードを呼び出すだけです。

keyGen.initialize(2048);

次のコードは、特定の、追加でシードされるSecureRandom オブジェクトを使用する方法を示しています。

SecureRandom random = SecureRandom.getInstance("DRBG", "SUN");
random.setSeed(userSeed);
keyGen.initialize(2048, random);

前述のアルゴリズム独立型のinitializeメソッドを呼び出した場合、パラメータが指定されないため、それぞれの鍵に関連したアルゴリズム固有のパラメータが存在する場合、これをどのように扱うかはプロバイダに任されます。プロバイダは、事前に計算されたパラメータ値を使うことも、新しい値を生成することもできます。

アルゴリズム固有の初期化

アルゴリズム固有のパラメータのセットがすでに存在する状況では(DSAの「コミュニティ・パラメータ」など)、AlgorithmParameterSpec引数を取るinitializeメソッドが2つあります。鍵のペアのジェネレータがDSAアルゴリズムのジェネレータで、DSA固有のパラメータ・セットp、qおよびgがあり、これを使って鍵のペアを生成するとします。次のコードを実行して鍵ペア・ジェネレータを初期化できます。DSAParameterSpecはAlgorithmParameterSpecです。

DSAParameterSpec dsaSpec = new DSAParameterSpec(p, q, g);
keyGen.initialize(dsaSpec);

鍵のペアの生成

最終手順は、鍵のペアの実際の生成です。使用した初期化の型(アルゴリズム独立型またはアルゴリズム固有型)に関係なく、同じコードを使用してKeyPairを生成します。

KeyPair pair = keyGen.generateKeyPair();

生成された鍵を使った署名の生成および検証

生成された鍵の使用による署名の生成と検証の例。

署名の生成および検証に関する次の例では、鍵のペアの生成で生成されたKeyPairを利用しています。

署名の生成

最初にSignature Classオブジェクトを作成します。

Signature dsa = Signature.getInstance("SHA256withDSA");

鍵のペアの例で生成した鍵のペアを使い、非公開鍵を指定してオブジェクトを初期化します。このあとでdataというバイト配列に署名を付けます。

/* Initializing the object with a private key */
PrivateKey priv = pair.getPrivate();
dsa.initSign(priv);

/* Update and sign the data */
dsa.update(data);
byte[] sig = dsa.sign();

署名の検証

署名の検証は簡単です。ここでも、鍵のペアの例で生成した鍵のペアを使います。

/* Initializing the object with the public key */
PublicKey pub = pair.getPublic();
dsa.initVerify(pub);

/* Update and verify the data */
dsa.update(data);
boolean verifies = dsa.verify(sig);
System.out.println("signature verifies: " + verifies);

鍵仕様およびKeyFactoryを使った署名の生成と検証

鍵仕様とKeyFactoryを使用して署名を生成および検証するために使用するサンプル・コードを示します。

前述の鍵のペアの生成で生成したような公開鍵と非公開鍵のペアを使用するのではなく、単にDSA非公開鍵のコンポーネントx (非公開鍵)、p (素数)、q (サブ素数)、およびg (ベース)を使用するとします。

さらに、非公開鍵を使ってsomeDataというバイト配列内のあるデータにデジタル署名を付けたいとします。次の手順を実行します。この手順は、鍵仕様の作成方法、および鍵ファクトリを使用して鍵仕様からPrivateKeyを取得する方法も示しています(initSignにはPrivateKeyが必要)。

DSAPrivateKeySpec dsaPrivKeySpec = new DSAPrivateKeySpec(x, p, q, g);

KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PrivateKey privKey = keyFactory.generatePrivate(dsaPrivKeySpec);

Signature sig = Signature.getInstance("SHA256withDSA");
sig.initSign(privKey);
sig.update(someData);
byte[] signature = sig.sign();

Aliceが署名したデータを使いたいとします。彼女がそのデータを使うため、および署名を検証するためには、彼女に次の3つのものを送る必要があります。

1. データ

2. 署名

3. データの署名に使用した非公開鍵に対応する公開鍵

someDataバイトをあるファイルに格納し、signatureバイトを別のファイルに格納し、両方をAliceに送ることができます。

公開鍵に関しては、上の署名の例のように、データの署名に使ったDSA非公開鍵に対応するDSA公開鍵のコンポーネントを持っていると想定します。この場合は、コンポーネントからDSAPublicKeySpecを作成できます。

DSAPublicKeySpec dsaPubKeySpec = new DSAPublicKeySpec(y, p, q, g);

ただし、鍵バイトをファイルに記述できるように、鍵バイトを抽出する必要があります。このためには、まず、前の例ですでに作成したDSA鍵ファクトリのgeneratePublicメソッドを呼び出します。

PublicKey pubKey = keyFactory.generatePublic(dsaPubKeySpec);

次に、次のコードを使って(エンコードされた)鍵バイトを抽出します。

byte[] encKey = pubKey.getEncoded();

この時点でバイトをファイルに保存し、データおよび署名を保存したファイルと一緒にAliceに送ることができます。

ここで、Aliceがこれらのファイルを受け取り、データ・ファイルのデータ・バイトをdataというバイト配列に、署名ファイルの署名バイトをsignatureというバイト配列に、公開鍵ファイルのエンコード済公開鍵バイトをencodedPubKeyというバイト配列にコピーしたとします。

Aliceは、次のコードを実行して署名を検証できます。このコードは、エンコードされた状態からDSA公開鍵のインスタンスを生成するために、鍵ファクトリを使う方法も示しています。initVerifyにはPublicKeyが必要です。

X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedPubKey);

KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);

Signature sig = Signature.getInstance("SHA256withDSA");
sig.initVerify(pubKey);
sig.update(data);
sig.verify(signature);

注意:
前述のコードでは、initVerifyがPublicKeyを必要とするため、Aliceはエンコードされた鍵ビットからPublicKeyを生成する必要がありました。PublicKeyを取得したら、KeyFactorygetKeySpecメソッドを使って、PublicKeyをDSAPublicKeySpecに変換して、必要な場合にコンポーネントにアクセスすることが可能です。

DSAPublicKeySpec dsaPubKeySpec =
(DSAPublicKeySpec)keyFactory.getKeySpec(pubKey, DSAPublicKeySpec.class);

これで、DSA公開鍵コンポーネントy、p、q、およびgに、DSAPublicKeySpecクラスの対応するgetメソッド(getY、getP、getQ、およびgetG)を使用してアクセスできます。

乱数の生成

次のコード例では、SecureRandomクラスのDRBG実装の使用による、様々なセキュリティ強度で構成された乱数の生成を説明します。

SecureRandom drbg;
byte[] buffer = new byte[32];

// Any DRBG can be provided
drbg = SecureRandom.getInstance("DRBG");
drbg.nextBytes(buffer);

SecureRandomParameters params = drbg.getParameters();
if (params instanceof DrbgParameters.Instantiation) {
DrbgParameters.Instantiation ins = (DrbgParameters.Instantiation) params;
if (ins.getCapability().supportsReseeding()) {
drbg.reseed();
}
}

// The following call requests a weak DRBG instance. It is only
// guaranteed to support 112 bits of security strength.
drbg = SecureRandom.getInstance("DRBG",
DrbgParameters.instantiation(112, NONE, null));

// Both the next two calls will likely fail, because drbg could be
// instantiated with a smaller strength with no prediction resistance
// support.
drbg.nextBytes(buffer,
DrbgParameters.nextBytes(256, false, "more".getBytes()));
drbg.nextBytes(buffer,
DrbgParameters.nextBytes(112, true, "more".getBytes()));

// The following call requests a strong DRBG instance, with a
// personalization string. If it successfully returns an instance,
// that instance is guaranteed to support 256 bits of security strength
// with prediction resistance available.
drbg = SecureRandom.getInstance("DRBG", DrbgParameters.instantiation(
256, PR_AND_RESEED, "hello".getBytes()));

// Prediction resistance is not requested in this single call,
// but an additional input is used.
drbg.nextBytes(buffer,
DrbgParameters.nextBytes(-1, false, "more".getBytes()));

// Same for this call.
drbg.reseed(DrbgParameters.reseed(false, "extra".getBytes()));

2つの鍵の同一性の判定

2つの鍵が等しいかどうかを判断するサンプル・コードです。

しばしば、2つの鍵の同一性の判定が必要になることがありますが、デフォルトのjava.lang.Object.equalsメソッドでは、必要な結果が得られない場合があります。プロバイダに対する依存度がもっとも少ない方法は、エンコードされた鍵を比較する方法です。この方法で適切に比較できない場合(たとえば、RSAPrivateKeyとRSAPrivateCrtKeyを比較する場合)、各コンポーネントを比較する必要があります。

その方法を示すコードを次に示します。

static boolean keysEqual(Key key1, Key key2) {
if (key1.equals(key2)) {
return true;
}

if (Arrays.equals(key1.getEncoded(), key2.getEncoded())) {
return true;
}

// More code for different types of keys here.
// For example, the following code can check if
// an RSAPrivateKey and an RSAPrivateCrtKey are equal:
// if ((key1 instanceof RSAPrivateKey) &&
//     (key2 instanceof RSAPrivateKey)) {
//     if ((key1.getModulus().equals(key2.getModulus())) &&
//         (key1.getPrivateExponent().equals(
//                                      key2.getPrivateExponent()))) {
//         return true;
//     }
// }

return false;
}

Base64でエンコードされた証明書の読み取り

次の例は、Base64でエンコードされた証明書を読み取ります。認証の開始は、次のコードで指定されます。

-----BEGIN CERTIFICATE-----

最後は、次の行で終わります。

-----END CERTIFICATE-----

generateCertificateの各呼出しが1つの証明書だけを使用し、入力ストリームの読出し位置がファイル内の次の証明書に置かれるように、FileInputStream (markおよびresetをサポートしていない)をByteArrayInputStream (これらのメソッドをサポート)に変換します。

try (FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
Certificate cert = cf.generateCertificate(bis);
System.out.println(cert.toString());
}
}

認証応答の解析

次の例は、ファイル内に保存されたPKCS7形式の認証応答を解析し、そこから証明書をすべて抽出します。

try (FileInputStream fis = new FileInputStream(filename)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");

Collection<? extends Certificate> c = cf.generateCertificates(fis);
for (Certificate cert : c) {
System.out.println(cert);
}

// Or use the aggregate operations below for the above for-loop
// c.stream().forEach(e -> System.out.println(e));
}

暗号化の使用

このセクションでは、鍵の生成、Cipherオブジェクトの作成と初期化、およびファイルの暗号化と復号化という一連の処理について説明します。この例全体で、Advanced Encryption Standard (AES)を使用します。

鍵の生成

AES鍵を作成するには、AES用のKeyGeneratorをインスタンス化する必要があります。特定のAES鍵生成実装について考慮する必要はないため、プロバイダは指定しません。KeyGeneratorを初期化しないため、AES鍵の作成にはシステム提供の乱数発生源およびデフォルトの鍵サイズが使用されます。

KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
SecretKey aesKey = keygen.generateKey();

鍵を生成したあと、同じKeyGeneratorを使用してほかの鍵を再度作成できます。

Cipherの作成

次のステップは、Cipherインスタンスの作成です。これには、CipherクラスのいずれかのgetInstanceファクトリ・メソッドを使用します。次のコンポーネントを含む必須の変換名を、スラッシュ(/)で区切って指定する必要があります。

- アルゴリズム名

- モード(オプション)

- パディング・スキーム(オプション)

この例では、Cipher Block ChainingモードおよびPKCS5パディングでAES暗号を作成します。特定の必須変換の実装について考慮する必要はないため、プロバイダは指定しません。

AESの標準アルゴリズム名はAES、Cipher Block Chainingモードの標準名はCBC、PKCS5パディングの標準名はPKCS5Paddingです。

Cipher aesCipher;

// Create the cipher
aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

前で生成されたaesKeyを使用して、Cipherオブジェクトを暗号化用に初期化します。

// Initialize the cipher for encryption
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey);

// Our cleartext
byte[] cleartext = "This is just an example".getBytes();

// Encrypt the cleartext
byte[] ciphertext = aesCipher.doFinal(cleartext);

// Retrieve the parameters used during encryption to properly
// initialize the cipher for decryption
AlgorithmParameters params = aesCipher.getParameters();

// Initialize the same cipher for decryption
aesCipher.init(Cipher.DECRYPT_MODE, aesKey, params);

// Decrypt the ciphertext
byte[] cleartext1 = aesCipher.doFinal(ciphertext);

cleartextとcleartext1は、同一です。

パスワードベース暗号化の使用

この例では、ユーザーにパスワードを要求し、暗号化鍵をそのパスワードから導き出します。

パスワードを収集し、java.lang.String型のオブジェクトに格納するのは、適切と考えられます。ただし、注意が必要な点があります。それは、String型のオブジェクトは不変であるということです。このため、使用後にStringの内容を変更(上書き)またはゼロにするようなメソッドは存在しません。この機能のために、Stringオブジェクトは、ユーザー・パスワードなどセキュリティ上重要な情報の格納には適しません。セキュリティ関連の情報は、常にchar型の配列に収集および格納するようにしてください。この理由で、javax.crypto.spec.PBEKeySpecクラスは、パスワードをchar型の配列として受け取り、返します。

PKCS5で定義されたパスワードベース暗号化(PBE)を使用するには、saltおよびiteration countを指定する必要があります。復号化時にも、暗号化時と同じsaltおよび繰返し処理の回数を使用する必要があります。より新しいPBEアルゴリズムは、1000以上の繰返し処理回数を使用します。

PBEKeySpec pbeKeySpec;
PBEParameterSpec pbeParamSpec;
SecretKeyFactory keyFac;

// Salt
byte[] salt = new SecureRandom().nextBytes(salt);

// Iteration count
int count = 1000;

// Create PBE parameter set
pbeParamSpec = new PBEParameterSpec(salt, count);

// Prompt user for encryption password.
// Collect user password as char array, and convert
// it into a SecretKey object, using a PBE key
// factory.
char[] password = System.console.readPassword("Enter encryption password: ");
pbeKeySpec = new PBEKeySpec(password);
keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

// Create PBE Cipher
Cipher pbeCipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");

// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

// Our cleartext
byte[] cleartext = "This is another example".getBytes();

// Encrypt the cleartext
byte[] ciphertext = pbeCipher.doFinal(cleartext);

Diffie-Hellman鍵交換、AES/GCMおよびHMAC-SHA256のサンプル・プログラム

次に、Diffie-Hellman鍵交換、AES/GCMおよびHMAC-SHA256のサンプル・プログラムを示します。

トピック

2つのパーティ間のDiffie-Hellman鍵交換

3つのパーティ間のDiffie-Hellman鍵交換

AES/GCMの例

HMAC-SHA256の例

2つのパーティ間のDiffie-Hellman鍵交換

このプログラムでは、2つのパーティ間でDiffie-Hellman鍵協定プロトコルが実行されます。

/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Oracle nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import java.security.interfaces.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
import com.sun.crypto.provider.SunJCE;

public class DHKeyAgreement2 {
private DHKeyAgreement2() {}
public static void main(String argv[]) throws Exception {

/*
* Alice creates her own DH key pair with 2048-bit key size
*/
System.out.println("ALICE: Generate DH keypair ...");
KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
aliceKpairGen.initialize(2048);
KeyPair aliceKpair = aliceKpairGen.generateKeyPair();

// Alice creates and initializes her DH KeyAgreement object
System.out.println("ALICE: Initialization ...");
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKpair.getPrivate());

// Alice encodes her public key, and sends it over to Bob.
byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

/*
* Let's turn over to Bob. Bob has received Alice's public key
* in encoded format.
* He instantiates a DH public key from the encoded key material.
*/
KeyFactory bobKeyFac = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEnc);

PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);

/*
* Bob gets the DH parameters associated with Alice's public key.
* He must use the same parameters when he generates his own key
* pair.
*/
DHParameterSpec dhParamFromAlicePubKey = ((DHPublicKey)alicePubKey).getParams();

// Bob creates his own DH key pair
System.out.println("BOB: Generate DH keypair ...");
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(dhParamFromAlicePubKey);
KeyPair bobKpair = bobKpairGen.generateKeyPair();

// Bob creates and initializes his DH KeyAgreement object
System.out.println("BOB: Initialization ...");
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKpair.getPrivate());

// Bob encodes his public key, and sends it over to Alice.
byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();

/*
* Alice uses Bob's public key for the first (and only) phase
* of her version of the DH
* protocol.
* Before she can do so, she has to instantiate a DH public key
* from Bob's encoded key material.
*/
KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");
x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
System.out.println("ALICE: Execute PHASE1 ...");
aliceKeyAgree.doPhase(bobPubKey, true);

/*
* Bob uses Alice's public key for the first (and only) phase
* of his version of the DH
* protocol.
*/
System.out.println("BOB: Execute PHASE1 ...");
bobKeyAgree.doPhase(alicePubKey, true);

/*
* At this stage, both Alice and Bob have completed the DH key
* agreement protocol.
* Both generate the (same) shared secret.
*/
try {
byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
int aliceLen = aliceSharedSecret.length;
byte[] bobSharedSecret = new byte[aliceLen];
int bobLen;
} catch (ShortBufferException e) {
System.out.println(e.getMessage());
}        // provide output buffer of required size
bobLen = bobKeyAgree.generateSecret(bobSharedSecret, 0);
System.out.println("Alice secret: " +
toHexString(aliceSharedSecret));
System.out.println("Bob secret: " +
toHexString(bobSharedSecret));
if (!java.util.Arrays.equals(aliceSharedSecret, bobSharedSecret))
throw new Exception("Shared secrets differ");
System.out.println("Shared secrets are the same");

/*
* Now let's create a SecretKey object using the shared secret
* and use it for encryption. First, we generate SecretKeys for the
* "AES" algorithm (based on the raw shared secret data) and
* Then we use AES in CBC mode, which requires an initialization
* vector (IV) parameter. Note that you have to use the same IV
* for encryption and decryption: If you use a different IV for
* decryption than you used for encryption, decryption will fail.
*
* If you do not specify an IV when you initialize the Cipher
* object for encryption, the underlying implementation will generate
* a random one, which you have to retrieve using the
* javax.crypto.Cipher.getParameters() method, which returns an
* instance of java.security.AlgorithmParameters. You need to transfer
* the contents of that object (e.g., in encoded format, obtained via
* the AlgorithmParameters.getEncoded() method) to the party who will
* do the decryption. When initializing the Cipher for decryption,
* the (reinstantiated) AlgorithmParameters object must be explicitly
* passed to the Cipher.init() method.
*/
System.out.println("Use shared secret as SecretKey object ...");
SecretKeySpec bobAesKey = new SecretKeySpec(bobSharedSecret, 0, 16, "AES");
SecretKeySpec aliceAesKey = new SecretKeySpec(aliceSharedSecret, 0, 16, "AES");

/*
* Bob encrypts, using AES in CBC mode
*/
Cipher bobCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
bobCipher.init(Cipher.ENCRYPT_MODE, bobAesKey);
byte[] cleartext = "This is just an example".getBytes();
byte[] ciphertext = bobCipher.doFinal(cleartext);

// Retrieve the parameter that was used, and transfer it to Alice in
// encoded format
byte[] encodedParams = bobCipher.getParameters().getEncoded();

/*
* Alice decrypts, using AES in CBC mode
*/

// Instantiate AlgorithmParameters object from parameter encoding
// obtained from Bob
AlgorithmParameters aesParams = AlgorithmParameters.getInstance("AES");
aesParams.init(encodedParams);
Cipher aliceCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aliceCipher.init(Cipher.DECRYPT_MODE, aliceAesKey, aesParams);
byte[] recovered = aliceCipher.doFinal(ciphertext);
if (!java.util.Arrays.equals(cleartext, recovered))
throw new Exception("AES in CBC mode recovered text is " +
"different from cleartext");
System.out.println("AES in CBC mode recovered text is "
"same as cleartext");
}

/*
* Converts a byte to hex digit and writes to the supplied buffer
*/
private static void byte2hex(byte b, StringBuffer buf) {
char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F' };
int high = ((b & 0xf0) >> 4);
int low = (b & 0x0f);
buf.append(hexChars[high]);
buf.append(hexChars[low]);
}

/*
* Converts a byte array to hex string
*/
private static String toHexString(byte[] block) {
StringBuffer buf = new StringBuffer();
int len = block.length;
for (int i = 0; i < len; i++) {
byte2hex(block[i], buf);
if (i < len-1) {
buf.append(":");
}
}
return buf.toString();
}
}

3つのパーティ間のDiffie-Hellman鍵交換

このプログラムでは、3つのパーティ間でDiffie-Hellman鍵協定プロトコルが実行されます。

/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Oracle nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
/*
* This program executes the Diffie-Hellman key agreement protocol between
* 3 parties: Alice, Bob, and Carol using a shared 2048-bit DH parameter.
*/
public class DHKeyAgreement3 {
private DHKeyAgreement3() {}
public static void main(String argv[]) throws Exception {
// Alice creates her own DH key pair with 2048-bit key size
System.out.println("ALICE: Generate DH keypair ...");
KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
aliceKpairGen.initialize(2048);
KeyPair aliceKpair = aliceKpairGen.generateKeyPair();
// This DH parameters can also be constructed by creating a
// DHParameterSpec object using agreed-upon values
DHParameterSpec dhParamShared = ((DHPublicKey)aliceKpair.getPublic()).getParams();
// Bob creates his own DH key pair using the same params
System.out.println("BOB: Generate DH keypair ...");
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(dhParamShared);
KeyPair bobKpair = bobKpairGen.generateKeyPair();
// Carol creates her own DH key pair using the same params
System.out.println("CAROL: Generate DH keypair ...");
KeyPairGenerator carolKpairGen = KeyPairGenerator.getInstance("DH");
carolKpairGen.initialize(dhParamShared);
KeyPair carolKpair = carolKpairGen.generateKeyPair();
// Alice initialize
System.out.println("ALICE: Initialize ...");
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKpair.getPrivate());
// Bob initialize
System.out.println("BOB: Initialize ...");
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKpair.getPrivate());
// Carol initialize
System.out.println("CAROL: Initialize ...");
KeyAgreement carolKeyAgree = KeyAgreement.getInstance("DH");
carolKeyAgree.init(carolKpair.getPrivate());
// Alice uses Carol's public key
Key ac = aliceKeyAgree.doPhase(carolKpair.getPublic(), false);
// Bob uses Alice's public key
Key ba = bobKeyAgree.doPhase(aliceKpair.getPublic(), false);
// Carol uses Bob's public key
Key cb = carolKeyAgree.doPhase(bobKpair.getPublic(), false);
// Alice uses Carol's result from above
aliceKeyAgree.doPhase(cb, true);
// Bob uses Alice's result from above
bobKeyAgree.doPhase(ac, true);
// Carol uses Bob's result from above
carolKeyAgree.doPhase(ba, true);
// Alice, Bob and Carol compute their secrets
byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
System.out.println("Alice secret: " + toHexString(aliceSharedSecret));
byte[] bobSharedSecret = bobKeyAgree.generateSecret();
System.out.println("Bob secret: " + toHexString(bobSharedSecret));
byte[] carolSharedSecret = carolKeyAgree.generateSecret();
System.out.println("Carol secret: " + toHexString(carolSharedSecret));
// Compare Alice and Bob
if (!java.util.Arrays.equals(aliceSharedSecret, bobSharedSecret))
throw new Exception("Alice and Bob differ");
System.out.println("Alice and Bob are the same");
// Compare Bob and Carol
if (!java.util.Arrays.equals(bobSharedSecret, carolSharedSecret))
throw new Exception("Bob and Carol differ");
System.out.println("Bob and Carol are the same");
}
/*
* Converts a byte to hex digit and writes to the supplied buffer
*/
private static void byte2hex(byte b, StringBuffer buf) {
char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F' };
int high = ((b & 0xf0) >> 4);
int low = (b & 0x0f);
buf.append(hexChars[high]);
buf.append(hexChars[low]);
}
/*
* Converts a byte array to hex string
*/
private static String toHexString(byte[] block) {
StringBuffer buf = new StringBuffer();
int len = block.length;
for (int i = 0; i < len; i++) {
byte2hex(block[i], buf);
if (i < len-1) {
buf.append(":");
}
}
return buf.toString();
}
}

AES/GCMの例

次に、AES/GCMを使用してデータを暗号化/複合化するサンプル・プログラムを示します。

/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Oracle nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

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

public class AESGCMTest {

public static void main(String[] args) throws Exception {
// Slightly longer than 1 AES block (128 bits) to show PADDING
// is "handled" by GCM.
byte[] data = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10};

// Create a 128-bit AES key.
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
SecretKey key = kg.generateKey();

// Obtain a AES/GCM cipher to do the enciphering. Must obtain
// and use the Parameters for successful decryption.
Cipher encCipher = Cipher.getInstance("AES/GCM/NOPADDING");
encCipher.init(Cipher.ENCRYPT_MODE, key);
byte[] enc = encCipher.doFinal(data);
AlgorithmParameters ap = encCipher.getParameters();

// Obtain a similar cipher, and use the parameters.
Cipher decCipher = Cipher.getInstance("AES/GCM/NOPADDING");
decCipher.init(Cipher.DECRYPT_MODE, key, ap);
byte[] dec = decCipher.doFinal(enc);

if (Arrays.compare(data, dec) != 0) {
throw new Exception("Original data != decrypted data");
}
}
}

HMAC-SHA256の例

次のサンプル・プログラムでは、HMAC-SHA256の秘密鍵オブジェクトを生成し、それを使用してHMAC-SHA256オブジェクトを初期化する方法を示します。

例2-14 HMAC-SHA256の秘密鍵オブジェクトを生成する

/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Oracle nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

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

/**
* This program demonstrates how to generate a secret-key object for
* HMACSHA256, and initialize an HMACSHA256 object with it.
*/

public class initMac {

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

// Generate secret key for HmacSHA256
KeyGenerator kg = KeyGenerator.getInstance("HmacSHA256");
SecretKey sk = kg.generateKey();

// Get instance of Mac object implementing HmacSHA256, and
// initialize it with the above secret key
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(sk);
byte[] result = mac.doFinal("Hi There".getBytes());
}
}

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.security、javax.crypto、javax.crypto.spec、javax.crypto.interfacesなどのパッケージが含まれます。

- Sun、SunRsaSign、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 エンジン・クラス

エンジン・クラスのインスタンスである「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: サービス実装コードの記述

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

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

- ステップ4: プロバイダのモジュール宣言の作成

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

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

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

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

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

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

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

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

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

ステップ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プロバイダのその他の要件および推奨事項の検討

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

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

- プロバイダ・パッケージ内のすべてのSPI実装クラスをfinalとして宣言し(サブクラス化できないように)、SPI実装メソッドをprotectedとして宣言する必要があります。

- プロバイダ・パッケージ内の暗号化関連ヘルパー・クラスはすべて、プロバイダ・パッケージ外からアクセスできないように、パッケージ独自のスコープを保持する必要があります。

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

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

- オプション: CipherSpiのengineWrapメソッドおよびengineUnwrapメソッド。鍵をラッピングすると、ある場所から別の場所へ安全に転送できます。鍵のラップおよびアンラップについての情報は、wrapで提供されます。

- オプション: 1つ以上の免責メカニズム。免責メカニズムには、鍵復元、鍵エスクロー、鍵弱化などが含まれます。このメカニズムを実装および実行すると、これを利用するアプリケーション(またはアプレット)に対する暗号化の制限を緩めることができます。免責メカニズムを利用するアプリケーションの要件の詳細は、アプリケーションの暗号化制限の免責を取得する方法を参照してください。

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

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

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

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

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

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

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

プロバイダは、どちらかのスタイルを使用でき、両方のスタイルを同時に使用することもできます。選択したスタイルに関係なく、サブクラスは、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は免責メカニズムの名前を指し、KeyRecovery、KeyEscrowまたは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 9ソース・コードを参照してください。それらでは、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 SunMSCAPI", 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自体ではない)。これは、それによって、JCEフレームワークがサービスをどのようにインスタンス化するかをカスタマイズするためです。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内の任意のメソッドをオーバーライドして、JCEフレームワークがプロバイダのサービスをどのように処理するかをカスタマイズします。

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

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

注意:
このコーディング・スタイルの例は、sun.security.mscapiパッケージに含まれているJDK 9ソース・コードを参照してください。

ステップ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の組合せごとに別々のクラスを提供できます。または、algorithm、algorithm/mode、algorithm//padding (ダブル・スラッシュを使用する点に注意)のいずれかに対応する下位変換を表す、より汎用的なクラスを提供できます。この場合、要求されたモードまたはパディング(あるいはその両方)は、CipherのgetInstanceメソッドによって自動的に設定されます。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のサブクラスは、指定されたmodeとpaddingを利用してalgNameを実装します。

(使用する必要がある標準アルゴリズム名、モードおよびパディング・スキームについては、Javaセキュリティ標準アルゴリズム名仕様を参照。)

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

- Cipher.AES/ECB/PKCS5Padding

- Cipher.AES/CBC/PKCS5Padding

- Cipher.AES/CFB/PKCS5Padding

- Cipher.AES/OFB/PKCS5Padding

別のプロバイダは、上の各モードのためのクラス(つまり、ECB、CBC、CFB、OFBのためにそれぞれ1つのクラス)、PKCS5Paddingのための1つのクラス、およびCipherSpiからサブクラス化された汎用AESクラスを実装できます。このプロバイダは、マスター・クラス内に次の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の実装をインスタンス化します。

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

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

- 未登録の場合、次のステップに進みます。

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

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

- 未登録の場合、次のステップに進みます。

c. プロバイダが、サブ変換algorithm//padding (ダブル・スラッシュに注意)に対応するCipherSpiのサブクラスを登録済かどうかをチェックする。

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

- 未登録の場合、次のステップに進みます。

d. プロバイダが、サブ変換「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ファイルにモジュール構成情報が含まれているモジュール。
JCEフレームワークでは、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ファイルの署名(必要な場合)

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

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

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

次に、コード署名証明書の取得方法を示します。Java Platform, Standard Editionツール・リファレンスでkeytoolを参照してください。

1. keytoolを使用して、RSA鍵ペアを生成します。例として、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>

これにより、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コード署名証明書発行局を参照してください。

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ファイル内にプロバイダ・コードを記述し(ステップ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」というテキストが表示されます。

注意:

- ユーザー操作を最適化するため、署名されたJCEプロバイダをRIA (アプレットまたはWebstartアプリケーション)の一部としてバンドルする場合は、JCEプロバイダのJARに2番目の署名を適用するときに、アプレットまたはWebstartアプリケーションへの署名に使用したのと同じ証明書/鍵を使用するようにしてください。RIAのデプロイについては、デプロイメント構成ファイルおよびプロパティを参照してください。また、JARファイルへの複数の署名の適用については、Java Platform, Standard Editionツール・リファレンスのjarsignerを参照してください。

- 署名済プロバイダをJMODファイルにパッケージ化することはできません。

- プロバイダに署名する必要はありません。

- プロバイダにCipher、KeyAgreementまたはMAC実装がない場合にかぎり、jlinkコマンドを使用してカスタム・ランタイム・イメージにプロバイダをリンクできます。

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

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

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

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

1. エディタでjava.securityファイルを開きます。

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

たとえば、強調表示された行では、プロバイダ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

別の方法として、プロバイダを動的に登録することもできます。その場合、プログラム( ステップ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);
}
}

こうした登録は持続的なものではありません。また、実行できるのは次の権限を付与されたコードのみです。

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

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

grant codeBase "file:/localWork/myjce_provider.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オブジェクトを示します。

i1、i2および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パラメータの生成に1024ビットのデフォルト・モジュラス・サイズ(強度)が使用され、SunJCEプロバイダでは、Diffie-Hellmanパラメータの生成に2048ビットのデフォルト・モジュラス・サイズ(鍵サイズ)が使用されます。

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

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

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

たとえば、SunプロバイダのDSA鍵ペア・ジェネレータは、512、768、1024および2048ビットの鍵ペアの生成用に、計算済のp、qおよびgデフォルト値のセットを提供します。次のp、q、および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クラス

Provider.Serviceクラスは、プロバイダがそれらのサービスを通知し追加機能をサポートするための代替方法を提供します。

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

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

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

supportsParameter()は、Serviceが指定されたパラメータを使用できるかどうかをテストします。このサービスがパラメータを使用できない場合、falseを返します。このサービスがパラメータを使用できる場合、すばやいチェックが実行できない場合、またはステータスが不明な場合、trueを返します。これは、ある種のサービスに対してセキュリティ・フレームワークにより使用され、一致しない実装が対象からすばやく除外されます。現状では、これはSignature、Cipher、Mac、およびKeyAgreementの標準サービスでのみ定義されています。この場合、parameterはKeyのインスタンスである必要があります。たとえば、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には次のインタフェースがあります。

- Interface DSAKey

- Interface DSAKeyPairGenerator

- Interface DSAParams

- Interface DSAPrivateKey

- Interface DSAPublicKey

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

DSAKeyPairGenerator

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

DSAParams実装

DSA鍵ペア・ジェネレータを実装する場合、p、qおよび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メソッド(DSAPrivateKeyがDSAKeyを拡張するため)。注意: getParamsメソッドは、DSAParamsオブジェクトを返します。このため、DSAParams実装も保持する必要があります。

- Interface KeyインタフェースのgetAlgorithm、getEncodedおよびgetFormatメソッド(DSAPrivateKeyがjava.security.PrivateKeyを拡張し、PrivateKeyがKeyを拡張するため)。
同様に、公開DSA鍵の場合、次のものを実装するクラスを提供する必要があります。

- Interface DSAPublicKeyインタフェースのgetYメソッド。

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

注意:
getParamsメソッドは、DSAParamsオブジェクトを返します。このため、DSAParams実装も保持する必要があります。

- Interface KeyのgetAlgorithm、getEncodedおよびgetFormatメソッド(DSAPublicKeyがjava.security.PublicKeyを拡張し、PublicKeyがKeyを拡張するため)。

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

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

- Interface RSAPrivateKey

- Interface RSAPrivateCrtKey

- Interface RSAPublicKey

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

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メソッドは、引き渡される鍵がRSAPrivateKey、RSAPrivateCrtKey、またはRSAPublicKey実装のインスタンスであることを求めます。

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

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

RSAPublicKey、RSAPrivateKey、および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

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

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

- Interface RSAPrivateKeyインタフェースのgetModulusおよびgetPrivateExponentメソッド。

- Interface KeyインタフェースのgetAlgorithm、getEncodedおよびgetFormatメソッド(RSAPrivateKeyがjava.security.PrivateKeyを拡張し、PrivateKeyがKeyを拡張するため)。

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

- RSA非公開鍵用にリスト・アップした前述のメソッドすべて(RSAPrivateCrtKeyがjava.security.interfaces.RSAPrivateKeyを拡張するため)。

- Interface RSAPrivateKeyインタフェースのgetPublicExponent、getPrimeP、getPrimeQ、getPrimeExponentP、getPrimeExponentQおよびgetCrtCoefficientメソッド。

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

- Interface RSAPublicKeyインタフェースのgetModulusおよびgetPublicExponentメソッド。

- Interface KeyインタフェースのgetAlgorithm、getEncodedおよびgetFormatメソッド(RSAPublicKeyがjava.security.PublicKeyを拡張し、PublicKeyがKeyを拡張するため)。

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

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

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

- Interface DHPublicKey

- Interface DHKey

- Interface DHPrivateKey

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

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メソッド(DHPrivateKeyがDHKeyを拡張するため)。

- Interface KeyインタフェースのgetAlgorithm、getEncodedおよびgetFormatメソッド(DHPrivateKeyがjava.security.PrivateKeyを拡張し、PrivateKeyがKeyを拡張するため)。

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

- Interface DHPublicKeyインタフェースのgetYメソッド。

- Interface DHKeyインタフェースのgetParamsメソッド(DHPublicKeyがDHKeyを拡張するため)。

- Interface KeyインタフェースのgetAlgorithm、getEncodedおよびgetFormatメソッド(DHPublicKeyがjava.security.PublicKeyを拡張し、PublicKeyがKeyを拡張するため)。

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

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

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

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

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

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

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

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

AlgorithmParametersSpi、AlgorithmParameterGeneratorSpiまたは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は、getX、getP、getQおよびgetGメソッドを定義し、非公開鍵x、および鍵の計算に使用するDSAアルゴリズム・パラメータ(素数のp、サブ素数のq、およびベースのg)にアクセスします。

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

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

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

KeySpecインタフェース

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

Javaでは、KeySpecインタフェースを実装する、次の複数のクラスが提供されます。

- DSAPrivateKeySpec

- DSAPublicKeySpec

- RSAPrivateKeySpec

- RSAPublicKeySpec

- EncodedKeySpec

- PKCS8EncodedKeySpec

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

表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の素数因数p、nの素数因数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".DHPrivateKeySpec、DHPublicKeySpec、DESKeySpec、DESedeKeySpec、PBEKeySpecおよび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へのプラグインを可能にするために、プロバイダが満たす必要がある条件を次に示します。

- プロバイダ・コードを、JCAをバイパスして、アプリケーションから直接インスタンス化する場合には、プロバイダ・クラスが無効になるようにプロバイダ・コードを記述する必要があります。プロバイダの実装および統合までのステップでステップ1: サービス実装コードの記述を参照してください。

- プロバイダ・パッケージは、JCAフレームワークにより信頼されたエンティティを使用して署名する必要があります。(ステップ7.1: コード署名証明書の取得からステップ7.2: プロバイダの署名までを参照。)プロバイダを米国外に輸出する可能性のある米国内のベンダーは、輸出承認を米国政府に申請する必要があります。(ステップ11: 米国政府による輸出承認の申請(必要な場合)を参照。)

MyProviderのサンプル・コード

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

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

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

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

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

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

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

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

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

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

- RunTest.sh

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

4 JDKプロバイダ・ドキュメント

このドキュメントには、JDKに含まれるプロバイダの技術的詳細が含まれています。読者がJava暗号化アーキテクチャとプロバイダ・アーキテクチャを詳しく理解していることが前提となります。

注意:
Javaセキュリティ標準アルゴリズム名仕様には、このドキュメントで使用されている標準名に関する詳細な情報が含まれています。

トピック

JDKプロバイダの概要

暗号化アルゴリズムの輸入制限

暗号の変換

SecureRandom実装

SunPKCS11プロバイダ

SUNプロバイダ

SunRsaSignプロバイダ

SunJSSEプロバイダ

SunJCEプロバイダ

SunJGSSプロバイダ

SunSASLプロバイダ

XMLDSigプロバイダ

SunPCSCプロバイダ

SunMSCAPIプロバイダ

SunECプロバイダ

OracleUcryptoプロバイダ

Appleプロバイダ

JdkLDAPプロバイダ

JdkSASLプロバイダ

JDKプロバイダの概要

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

Java暗号化アーキテクチャ(JCA)およびそのプロバイダ・アーキテクチャは、Java Development Kit (JDK)のコア概念です。このアーキテクチャについて十分理解していることが前提となります。

注意: JDKでの暗号化実装は、歴史的な理由および提供するサービスの種類により、様々なプロバイダによって配布されています(Sun、SunJSSE、SunJCE、SunRsaSign)。汎用アプリケーションは、特定のプロバイダから暗号化サービスを要求しないようにする必要があります。つまり、次のようになります。

getInstance("...", "SunJCE");  // not recommended

これに対して

getInstance("...");            // recommended

後者のようにしないと、アプリケーションは特定のプロバイダに結び付けられることになり、そのプロバイダはほかのJava実装で使用できない可能性があります。特定の要求されたプロバイダよりも優先順位が高い、使用可能な最適化されたプロバイダ(たとえば、PKCS11によるハードウェア・アクセラレータまたはMicrosoftのMSCAPIなどのネイティブOS実装)を利用できない場合もあります。

次の表では、モジュール、およびサポートされているJava暗号化サービス・プロバイダをリストします。

表4-1 モジュールおよびJava暗号化サービス・プロバイダ

モジュール

プロバイダ

java.base

SUN、SunRsaSign、SunJSSE、SunJCE [1]、Apple

java.naming

JdkLDAP

java.security.jgss

SunJGSS

java.security.sasl

SunSASL

java.smartcardio

SunPCSC

java.xml.crypto

XMLDSig

jdk.crypto.cryptoki

SunPKCS11 [1]

jdk.crypto.ec

SunEC [1]

jdk.crypto.mscapi

SunMSCAPI [1]

jdk.crypto.ucrypto

OracleUcrypto [1]

jdk.security.jgss

JdkSASL

脚注1: 署名付きJARファイルとして前に説明した、JCE暗号プロバイダを示します(JCEプロバイダにはCipher/KeyAgreement/KeyGenerator/Mac/SecretKeyFactory実装が含まれます)。

暗号化アルゴリズムの輸入制限

デフォルトでは、アプリケーションは、どの強度の暗号化アルゴリズムでも使用できます。ただし、一部の場所での輸入規制のため、それらのアルゴリズムの強度を制限する必要がある場合があります。JDKでは、ディレクトリ<java_home>/conf/security/policyに、暗号化アルゴリズムの強度を決定する管轄ポリシー・ファイルのセットが2つ用意されています。管轄ポリシー・ファイル、およびそれらのアクティブ化の方法については、暗号化強度の構成を参照してください。

自分の場所のための正確な要件を決定する際は、輸出入管理コンサルタントまたは弁護士に相談してください。

制限付きの構成のために、次の表では、制限付きの一連の管轄ポリシー・ファイルで許可されている最大鍵サイズをリストします。

表4-2 暗号化アルゴリズムの最大鍵サイズ

アルゴリズム

最大鍵サイズ

DES

64

DESede

*

RC2

128

RC4

128

RC5

128

RSA

*

その他すべて

128

暗号の変換

javax.crypto.Cipher.getInstance(String transformation)ファクトリ・メソッドは、algorithm/mode/padding形式の変換を使用してCipherオブジェクトを生成します。モード/パディングを省略すると、SunJCEプロバイダとSunPKCS11プロバイダは、多くの対称暗号でECBをデフォルト・モードとして、PKCS5Paddingをデフォルト・パディングとして使用します。

デフォルトを使用するのではなく、アルゴリズム、モードおよびパディングを完全に指定した変換を使用することをお薦めします。デフォルトは、プロバイダ固有であり、プロバイダによって異なる可能性があります。

注意:
ECBは、単一のデータ・ブロックに対しては正常に機能し、パラレル化可能ですが、複数のデータ・ブロックには絶対に使用しないでください。

SecureRandom実装

次の表に、利用可能なSecureRandom実装のデフォルトの優先順位を示します。

表4-3 デフォルトのSecureRandom実装

OS

アルゴリズム名

プロバイダ名

Solaris

1. PKCS11 [1] [4]

SunPKCS11

2. NativePRNG [2]

SUN

3. DRBG

SUN

4. SHA1PRNG [2]

SUN

5. NativePRNGBlocking

SUN

6. NativePRNGNonBlocking

SUN

Linux

1. NativePRNG [2]

SUN

2. DRGB

SUN

3. SHA1PRNG [2]

SUN

4. NativePRNGBlocking

SUN

5. NativePRNGNonBlocking

SUN

macOS

1. NativePRNG [2]

SUN

2. DRGB

SUN

3. SHA1PRNG [2]

SUN

4. NativePRNGBlocking

SUN

5. NativePRNGNonBlocking

SUN

Windows

1. DRGB

SUN

2. SHA1PRNG

SUN

3. Windows-PRNG [3]

SunMSCAPI

脚注1: SunPKCS11プロバイダはすべてのプラットフォームで利用可能ですが、これがデフォルトで有効になっているのは、ネイティブのPKCS11実装が自動的にインストールおよび構成される唯一のOSであるSolarisのみです。他のプラットフォームでは、アプリケーションまたはデプロイヤが特にネイティブPKCS11ライブラリをインストールして構成し、それを使用できるようにSunPKCS11を構成して有効にする必要があります。

脚注2: Solaris、LinuxおよびOS Xでは、java.securityのエントロピー収集デバイスをfile:/dev/urandomまたはfile:/dev/randomに設定すると、SHA1PRNGよりNativePRNGが優先されます。それ以外の場合は、SHA1PRNGが優先されます。

脚注3: Windowsには現在NativePRNGはありません。同等の機能を利用するには、SunMSCAPIプロバイダを使用します。

脚注4: SolarisのPKCS11 SecureRandom実装は、小さいサイズの要求のパフォーマンス・オーバーヘッドが原因で、無効になっています。再度有効にするには、sunpkcs11-solaris.cfgを編集します。

SecureRandom実装がJCAフレームワークに登録されていない場合は、java.security.SecureRandomで、ハードコード化されたSHA1PRNGが使用されます。

SunPKCS11プロバイダ

暗号化トークン・インタフェース標準(PKCS#11)は、ハードウェア暗号化アクセラレータやスマート・カードなどの暗号化メカニズムに対するネイティブ・プログラミング・インタフェースを提供しています。SunPKCS11プロバイダを適切に構成すると、アプリケーションは標準のJCA/JCE APIを使用してネイティブPKCS#11ライブラリにアクセスできるようになります。SunPKCS11プロバイダ自体には暗号化機能は含まれていません。このプロバイダは、Java環境とネイティブPKCS11プロバイダ間のコンジットとして機能します。このプロバイダの詳細は、『Java PKCS#11リファレンス・ガイド』を参照してください。

SUNプロバイダ

JDK 1.1では、Providerアーキテクチャが導入されました。最初のJDKプロバイダにはSUNという名前が付けられ、2種類の暗号化サービス(MessageDigestおよびSignature)が含まれていました。以降のリリースでは、他のメカニズムが追加されました(SecureRandom、KeyPairGenerator、KeyFactoryなど)。

当時有効だった米国の輸出制限事項によって、JDKの内部で使用可能な暗号化機能に重大な制限が課されました。このため、SUNプロバイダには、データを直接暗号化または復号化しない暗号化エンジンが含まれています。

SUNプロバイダでは、次のアルゴリズムを使用できます。

表4-4 SUNプロバイダでのアルゴリズム

エンジン

アルゴリズム名

AlgorithmParameterGenerator

DSA

AlgorithmParameters

DSA

CertificateFactory

X.509

CertPathBuilder

PKIX

CertPathValidator

PKIX

CertStore

Collection

Configuration

JavaLoginConfig

KeyFactory

DSA

KeyPairGenerator

DSA

KeyStore

PKCS12

JKS

DKS

CaseExactJKS

MessageDigest

MD2

MD5

SHA-1

SHA-224

SHA-256

SHA-384

SHA-512

SHA-512/224

SHA-512/256

SHA3-224

SHA3-256

SHA3-384

SHA3-512

Policy

JavaPolicy

SecureRandom

DRBG

(次のメカニズムとアルゴリズムがサポートされています。SHA-224、SHA-512/224、SHA-256、SHA-512/256、SHA-384およびSHA-512を使用するHash_DRBGおよびHMAC_DRBG。AES-128、AES-192およびAES-256を使用するCTR_DRBG (導出関数を使用する場合と使用しない場合がある)。各組合せでサポートされている予測耐性と再シード、およびセキュリティ強度は、112から、それがサポートする最大強度まで要求できます。)

SHA1PRNG

(初期シードは、現在、システム属性とjava.securityエントロピー収集デバイスの組合せによって実行されます。)

NativePRNG

(nextBytes()では/dev/urandomが使用され、generateSeed()では/dev/randomが使用される)

NativePRNGBlocking

(nextBytes()およびgenerateSeed()では/dev/randomが使用される)

NativePRNGNonBlocking

(nextBytes()およびgenerateSeed()では/dev/urandomが使用される)

Signature

NONEwithDSA

SHA1withDSA

SHA224withDSA

SHA256withDSA

NONEwithDSAinP1363Format

SHA1withDSAinP1363Format

SHA224withDSAinP1363Format

SHA256withDSAinP1363Format

次の表に、SHAメッセージ・ダイジェストに関連付けられたOIDを示します。

表4-5 SHAメッセージ・ダイジェストに関連付けられたOID

SHAメッセージ・ダイジェスト

OID

SHA-224

2.16.840.1.101.3.4.2.4

SHA-256

2.16.840.1.101.3.4.2.1

SHA-384

2.16.840.1.101.3.4.2.2

SHA-512

2.16.840.1.101.3.4.2.3

SHA-512/224

2.16.840.1.101.3.4.2.5

SHA-512/256

2.16.840.1.101.3.4.2.6

SHA3-224

2.16.840.1.101.3.4.2.7

SHA3-256

2.16.840.1.101.3.4.2.8

SHA3-384

2.16.840.1.101.3.4.2.9

SHA3-512

2.16.840.1.101.3.4.2.10

次の表に、DSA署名に関連付けられたOIDを示します。

表4-6 DSA署名に関連付けられたOID

DSA署名

OID

SHA1withDSA

1.2.840.10040.4.3

1.3.14.3.2.13

1.3.14.3.2.27

SHA224withDSA

2.16.840.1.101.3.4.3.1

SHA256withDSA

2.16.840.1.101.3.4.3.2

鍵サイズの制限

SUNプロバイダでは、次のデフォルト・キーサイズ(ビット単位)を使用し、次の制限を強制します。

KeyPairGenerator

アルゴリズム名前

デフォルトの鍵サイズ

制限/コメント

DSA

1024

鍵サイズは、512から1024の範囲で64の倍数であるか、2048および3072である必要があります。

AlgorithmParameterGenerator

アルゴリズム名前

デフォルトの鍵サイズ

制限/コメント

DSA

1024

鍵サイズは、512から1024の範囲で64の倍数であるか、2048および3072である必要があります。

CertificateFactory/CertPathBuilder/CertPathValidator/CertStore実装

CertificateFactory、CertPathBuilder、CertPathValidatorおよびCertStoreのSUNプロバイダ実装の詳細は、PKIプログラマーズ・ガイドの付録B: SUNプロバイダでのCertPath実装を参照してください。

SunRsaSignプロバイダ

SunRsaSignプロバイダは、SunJSSEプロバイダのRSA署名の機能が拡張された代替機能として、JDK 1.3で導入されました。

SunRsaSignプロバイダでは、次のアルゴリズムを使用できます。

表4-7 SunRsaSignプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

KeyFactory

RSA

KeyPairGenerator

RSA

Signature

MD2withRSA

MD5withRSA

SHA1withRSA

SHA224withRSA

SHA256withRSA

SHA384withRSA

SHA512withRSA

鍵サイズの制限

SunRsaSignプロバイダでは、次のデフォルト・キーサイズ(ビット単位)を使用し、次の制限を強制します。

KeyPairGenerator

表4-8 SunRsaSignプロバイダの鍵サイズの制限

アルゴリズム名前

デフォルトの鍵サイズ

制限/コメント

RSA

2048

鍵サイズは、512から65536ビットの範囲内である必要があります。

SunJSSEプロバイダ

アルゴリズム

Java Secure Socket Extension (JSSE)は、個別のオプション・パッケージ(標準拡張機能とも呼ばれる)としてリリースされ、JDK 1.2.nおよび1.3.nで使用できました。SunJSSEプロバイダはこのリリースの一部として導入されました。

以前のJDKリリースでは、JDKでRSA署名プロバイダは使用できませんでした。このため、SunJSSEでは、共通で使用できるRSAベースの証明書を使用するために独自のRSA実装を提供する必要がありました。JDK 5では、SunRsaSignプロバイダが導入されました。このプロバイダは、SunJSSEプロバイダのすべての機能(およびそれ以上の機能)を提供します。JDK 5.0以降をターゲットにしているアプリケーションは、かわりにSunRsaSignプロバイダのインスタンスを要求する必要があります。RSAアルゴリズムは、下位互換性のためにこのプロバイダでも使用可能ですが、実際にはSunRsaSignプロバイダに実装されています。

SunJSSEプロバイダでは、次のアルゴリズムを使用できます。

エンジン

アルゴリズム名

KeyFactory

RSA

注意: SunJSSEプロバイダは、以前のリリースとの下位互換性のためにあるため、KeyFactoryには使用しなでください。

KeyManagerFactory

PKIX: X509ExtendedKeyManagerインスタンスのファクトリであり、IETF PKIXワーキング・グループによってRFC 5280で定義された規則に従って、ローカル側の認証に使用するX.509証明書ベースの鍵ペアを管理します。現在、このKeyManagerFactoryは、KeyStoreオブジェクトまたはjavax.net.ssl.KeyStoreBuilderParametersを使用した初期化をサポートしています。

SunX509: X509ExtendedKeyManagerインスタンスのファクトリであり、ローカル側の認証に使用するX.509証明書ベースの鍵ペアを管理しますが、証明書の使用方法/有効性およびチェーン検証のチェックはあまり厳しくありません。このKeyManagerFactoryは、Keystoreオブジェクトを使用した初期化をサポートしますが、現在のところjavax.net.ssl.ManagerFactoryParametersクラスを使用した初期化はサポートしていません。

注意: SunX509ファクトリは、以前のリリースとの下位互換性のためにあるため、使用しなでください。

KeyPairGenerator

RSA

注意: SunJSSEプロバイダは、以前のリリースとの下位互換性のためにあるため、KeyPairGeneratorには使用しなでください。

KeyStore

PKCS12

注意: SunJSSEプロバイダは、以前のリリースとの下位互換性のためにあるため、KeyStoreには使用しなでください。

Signature

MD2withRSA

MD5withRSA

SHA1withRSA

注意: SunJSSEプロバイダは、以前のリリースとの下位互換性のためにあるため、Signatureには使用しなでください。

SSLContext

SSLv3

TLSv1

TLSv1.1

TLSv1.2

DTLSv1.0

DTLSv1.2

TrustManagerFactory

PKIX: X509ExtendedTrustManagerインスタンスのファクトリであり、IETF PKIXワーキング・グループによってRFC 5280で定義された規則に従って、証明書チェーンを検証します。現在、このTrustManagerFactoryは、KeyStoreオブジェクトまたはjavax.net.ssl.CertPathTrustManagerParametersを使用した初期化をサポートしています。

SunX509: X509ExtendedTrustManagerインスタンスのファクトリであり、証明書チェーンを検証しますが、証明書の使用方法/有効性およびチェーン検証のチェックはあまり厳しくありません。このTrustManagerFactoryは、Keystoreオブジェクトを使用した初期化をサポートしますが、現在のところjavax.net.ssl.ManagerFactoryParametersクラスを使用した初期化はサポートしていません。

注意: SunX509ファクトリは、以前のリリースとの下位互換性のためにあるため、使用しなでください。

SunJSSEプロバイダのプロトコルのパラメータ

SunJSSEプロバイダは、次のprotocolパラメータをサポートしています。

表4-9 SunJSSEプロバイダのプロトコルのパラメータ

プロトコル

クライアントではデフォルトで有効

サーバーではデフォルトで有効

SSLv3

なし(使用不可[3])

なし(使用不可[3])

TLSv1

はい

はい

TLSv1.1

はい

はい

TLSv1.2

はい

はい

SSLv2Hello [1]

なし

はい

DTLSv1.0

はい

はい

DTLSv1.2 [2]

はい

はい

脚注1 : SSLv3、TLSv1、TLSv1.1およびTLSv1.2プロトコルを使用すると、SSLv2Hello疑似プロトコルを使用して、SSLv3、TLSv1、TLSv1.1およびTLSv1.2のClientHellosをSSLv2形式のhelloにカプセル化して送信できます。

脚注2: DTLSv1.0とDTLSv1.2の両方を使用可能です。

脚注3: SSLv3を使用可能です。

- JDK 8u31リリース以降、SSLv3プロトコル(Secure Socket Layer)は無効になっており、デフォルトで使用できません。<java_home>/conf/security/java.securityファイルのjava.security.Securityプロパティjdk.tls.disabledAlgorithmsを参照してください。

- SSLv3が絶対に必要な場合は、java.securityファイルのjdk.tls.disabledAlgorithmsプロパティからSSLv3を削除するか、JSSEが初期化される前にこのセキュリティ・プロパティを動的に設定すれば、プロトコルを再度アクティブにできます。

- SSLv3プロトコルをデプロイ・レベルで有効化するには、前述のステップに従った後で、deployment.propertiesファイルを編集してdeployment.security.SSLv3=trueを追加します。

次の表は、SSLv2Hellosの使用時に可能な接続の組合せを示しています。

表4-10 SSLv2Hellosを使用して利用可能な接続

クライアント

サーバー

接続を使用できるかどうか

使用可能

使用可能

はい

使用不可

使用可能

はい(最も相互運用可能度が高い: SunJSSEデフォルト)

使用可能

使用不可

いいえ

使用不可

使用不可

はい

暗号化方式群

SunJSSEは、数多くの暗号化方式群をサポートしています。表4-11および表4-12は、SunJSSEでサポートされている暗号化方式群(優先順)、およびそれらが導入されたリリースを示しています。

表4-11では、デフォルトで使用可能になっている暗号化方式群をリストします。表4-12では、SunJSSEでサポートされているがデフォルトでは使用可能になっていない暗号化方式群をリストします。

注意:
DTLSバージョン1.0とDTLSバージョン1.2によると、RC4暗号化方式群はDTLS接続とは使用できません。

デフォルトで使用可能になっている暗号化方式群

表4-11 使用可能になっている暗号化方式群

暗号化方式群

JDK 6

JDK 7

JDK 8

JDK 9

TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384

X [1]

X

X

TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384

X [1]

X

X

TLS_RSA_WITH_AES_256_CBC_SHA256

X [1]

X

X

TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384

X [1]

X

X

TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384

X [1]

X

X

TLS_DHE_RSA_WITH_AES_256_CBC_SHA256

X [1]

X

X

TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_RSA_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_ECDH_RSA_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_DHE_RSA_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_DHE_DSS_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256

X [1]

X

X

TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256

X [1]

X

X

TLS_RSA_WITH_AES_128_CBC_SHA256

X [1]

X

X

TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256

X [1]

X

X

TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256

X [1]

X

X

TLS_DHE_RSA_WITH_AES_128_CBC_SHA256

X [1]

X

X

TLS_DHE_DSS_WITH_AES_128_CBC_SHA256

X [1]

X

X

TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_RSA_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_ECDH_RSA_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_DHE_RSA_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_DHE_DSS_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_ECDHE_ECDSA_WITH_RC4_128_SHA

X

X

X

TLS_ECDHE_RSA_WITH_RC4_128_SHA

X

X

X

X

SSL_RSA_WITH_RC4_128_SHA

X

X

X

TLS_ECDH_ECDSA_WITH_RC4_128_SHA

X

X

X

X

TLS_ECDH_RSA_WITH_RC4_128_SHA

X

X

X

TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384

X

X

TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256

X

X

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

X

TLS_RSA_WITH_AES_256_GCM_SHA384

X

X

TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384

X

X

TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384

X

X

TLS_DHE_RSA_WITH_AES_256_GCM_SHA384

X

X

TLS_DHE_DSS_WITH_AES_256_GCM_SHA384

X

X

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

X

X

TLS_RSA_WITH_AES_128_GCM_SHA256

X

X

TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256

X

X

TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256

X

X

TLS_DHE_RSA_WITH_AES_128_GCM_SHA256

X

X

TLS_DHE_DSS_WITH_AES_128_GCM_SHA256

X

X

TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA

X

X

X

X

TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA

X

X

X

X

SSL_RSA_WITH_3DES_EDE_CBC_SHA

X

X

X

X

TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA

X

X

X

X

TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA

X

X

X

X

SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA

X

X

X

X

SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA

X

X

X

X

TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384

X

TLS_DHE_DSS_WITH_AES_256_CBC_SHA256

X

TLS_DHE_DSS_WITH_AES_256_CBC_SHA256

X

SSL_RSA_WITH_RC4_128_MD5

X

X

X

TLS_EMPTY_RENEGOTIATION_INFO_SCSV[2]

u22+

X

X

TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384

X

TLS_DHE_DSS_WITH_AES_256_CBC_SHA256

X

TLS_DHE_DSS_WITH_AES_256_CBC_SHA256

X

脚注1: SHA384およびSHA256を使用する暗号化方式群は、TLS 1.2以降でのみ使用可能です。

脚注2: TLS_EMPTY_RENEGOTIATION_INFO_SCSVは、RFC 5746をサポートするための新しい擬似暗号化方式群です。Java Secure Socket Extension (JSSE)リファレンス・ガイドのTransport Layer Security (TLS)再ネゴシエーションの問題を参照してください。

デフォルトで使用可能になっていない暗号化方式群

表4-12 使用可能になっていない暗号化方式群

暗号化方式群

JDK 6

JDK 7

JDK 8

JDK 9

TLS_DH_anon_WITH_AES_256_GCM_SHA384

X

X

TLS_DH_anon_WITH_AES_128_GCM_SHA256

X

X

TLS_DH_anon_WITH_AES_256_CBC_SHA256

X

X

X

TLS_ECDH_anon_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_DH_anon_WITH_AES_256_CBC_SHA

X

X

X

X

TLS_DH_anon_WITH_AES_128_CBC_SHA256

X

X

X

TLS_ECDH_anon_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_DH_anon_WITH_AES_128_CBC_SHA

X

X

X

X

TLS_ECDH_anon_WITH_RC4_128_SHA

X

X

X

X

SSL_DH_anon_WITH_RC4_128_MD5

X

X

X

X

TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA

X

X

X

X

SSL_DH_anon_WITH_3DES_EDE_CBC_SHA

X

X

X

X

TLS_RSA_WITH_NULL_SHA256

X

X

X

TLS_ECDHE_ECDSA_WITH_NULL_SHA

X

X

X

X

TLS_ECDHE_RSA_WITH_NULL_SHA

X

X

X

X

SSL_RSA_WITH_NULL_SHA

X

X

X

X

TLS_ECDH_ECDSA_WITH_NULL_SHA

X

X

X

X

TLS_ECDH_RSA_WITH_NULL_SHA

X

X

X

X

TLS_ECDH_anon_WITH_NULL_SHA

X

X

X

X

SSL_RSA_WITH_NULL_MD5

X

X

X

X

SSL_RSA_WITH_DES_CBC_SHA

X

X [1]

X

X

SSL_DHE_RSA_WITH_DES_CBC_SHA

X

X [1]

X

X

SSL_DHE_DSS_WITH_DES_CBC_SHA

X

X [1]

X

X

SSL_DH_anon_WITH_DES_CBC_SHA

X

X [1]

X

X

SSL_RSA_EXPORT_WITH_RC4_40_MD5

X

X [2]

X

X

SSL_DH_anon_EXPORT_WITH_RC4_40_MD5

X

X [2]

X

X

SSL_RSA_EXPORT_WITH_DES40_CBC_SHA

X

X [2]

X

X

SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA

X

X [2]

X

X

SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA

X

X [2]

X

X

SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA

X

X [2]

X

X

TLS_KRB5_WITH_RC4_128_SHA

X

X

X

X

TLS_KRB5_WITH_RC4_128_MD5

X

X

X

X

TLS_KRB5_WITH_3DES_EDE_CBC_SHA

X

X

X

X

TLS_KRB5_WITH_3DES_EDE_CBC_MD5

X

X

X

X

TLS_KRB5_WITH_DES_CBC_SHA

X

X [1]

X

X

TLS_KRB5_WITH_DES_CBC_MD5

X

X [1]

X

X

TLS_KRB5_EXPORT_WITH_RC4_40_SHA

X

X [2]

X

X

TLS_KRB5_EXPORT_WITH_RC4_40_MD5

X

X [2]

X

X

TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA

X

X [2]

X

X

TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5

X

X [2]

X

X

TLS_ECDHE_ECDSA_WITH_RC4_128_SHA

X

TLS_ECDHE_RSA_WITH_RC4_128_SHA

X

SSL_RSA_WITH_RC4_128_MD5

X

脚注1: RFC 5246 TLS 1.2では、これらの群の使用は禁止されています。これらは、SSLv3/TLS1.0/TLS1.1プロトコルでは使用できますが、TLS 1.2以降では使用できません。

脚注2: RFC 4346 TLS 1.1では、これらの群の使用は禁止されています。これらは、SSLv3/TLS1.0プロトコルでは使用できますが、TLS 1.1以降では使用できません。

AES_256を使用する暗号群では、Java Cryptography Extension (JCE)の無制限強度の適切な管轄ポリシー・ファイル・セットが必要となります。これは、JDKに含まれています。デフォルトでは、アクティブな暗号化ポリシーは、無制限です。「暗号化アルゴリズムの輸入制限」を参照してください。

楕円曲線暗号(ECDSA、ECDH、ECDHE、ECDH_anon)を使用する暗号化方式群では、次の要件を満たすJCE暗号化プロバイダが必要です。

- プロバイダは、パッケージjava.security.specおよびjava.security.interfaces内のクラスとインタフェースで定義されているようにECCを実装する必要があります。Elliptic Curve鍵オブジェクトのgetAlgorithm()メソッドは、文字列「EC」を返す必要があります。

- プロバイダは、SignatureアルゴリズムのSHA1withECDSAとNONEwithECDSA、KeyAgreementアルゴリズムのECDH、およびアルゴリズムECのKeyPairGeneratorとKeyFactoryをサポートする必要があります。これらのアルゴリズムのいずれかが欠落している場合、SunJSSEではEC暗号化方式群の使用が許可されません。

- プロバイダは、RFC 4492仕様のセクション5.1.1 (「付録A」も参照)で説明されているすべてのSECG曲線をサポートする必要があります。証明書では、圧縮されていない形式を使用してポイントをエンコードし、namedCurve選択を使用して(つまりオブジェクト識別子を使用して)曲線をエンコードします。

これらの要件が満たされていない場合、EC暗号化方式群が正しくネゴシエーションされないことがあります。

EncryptedPreMasterSecretバージョン番号チェックの強化

JDK 7リリースより前のSSL/TLS実装では、PreMasterSecret内のバージョン番号がチェックされず、SSL/TLSクライアントからデフォルトで正しいバージョン番号が送信されませんでした。システム・プロパティcom.sun.net.ssl.rsaPreMasterSecretFixがtrueに設定されないかぎり、TLSクライアントはアクティブなネゴシエートされたバージョンを送信しますが、クライアントでサポートされる予想最大バージョンは送信しません。

SSLバージョン3.0およびTLSバージョン1.0では、互換性のため、この動作は保持されます。しかし、TLSバージョン1.1以降の実装では、RFC 5246の要求に従って、PreMasterSecretバージョン番号のチェックが強化されています。クライアントは常に正しいバージョン番号を送信し、サーバーはバージョン番号を厳密にチェックします。TLS 1.1以降では、システム・プロパティcom.sun.net.ssl.rsaPreMasterSecretFixは使用されません。

SunJCEプロバイダ

「SUNプロバイダ」で簡単に説明されているように、当時の米国の輸出制限事項によって、JDK内で使用できる暗号化機能が制限されていました。このため、アプリケーションでデータの暗号化または復号化を可能にするために、個別のAPIおよびリファレンス実装が開発されました。Java Cryptographic Extension (JCE)は、別個のオプション・パッケージとしてリリースされ(簡潔に標準拡張機能とも呼ばれる)、JDK 1.2xおよび1.3xで利用可能でした。JDK 1.4の開発中に、規制は、JCE (およびSunJSSE)をJDKの一部としてバンドルできるくらいには緩和されました。

SunJCEプロバイダでは、次のアルゴリズムを使用できます。

表4-13 SunJCEプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

AlgorithmParameterGenerator

DiffieHellman

AlgorithmParameters

AES

Blowfish

DES

DESede

DiffieHellman

GCM

OAEP

PBE

PBES2

PBEWithHmacSHA1AndAES_128

PBEWithHmacSHA224AndAES_128

PBEWithHmacSHA256AndAES_128

PBEWithHmacSHA384AndAES_128

PBEWithHmacSHA512AndAES_128

PBEWithHmacSHA1AndAES_256

PBEWithHmacSHA224AndAES_256

PBEWithHmacSHA256AndAES_256

PBEWithHmacSHA384AndAES_256

PBEWithHmacSHA512AndAES_256

PBEWithMD5AndDES

PBEWithMD5AndTripleDES

PBEWithSHA1AndDESede

PBEWithSHA1AndRC2_40

PBEWithSHA1AndRC2_128

PBEWithSHA1AndRC4_40

PBEWithSHA1AndPC4_128

RC2

Cipher

表4-14を参照してください

KeyAgreement

DiffieHellman

KeyFactory

DiffieHellman

KeyGenerator

AES

ARCFOUR

Blowfish

DES

DESede

HmacMD5

HmacSHA1

HmacSHA224

HmacSHA256

HmacSHA384

HmacSHA512

RC2

KeyPairGenerator

DiffieHellman

KeyStore

JCEKS

Mac

HmacMD5

HmacSHA1

HmacSHA224

HmacSHA384

HmacSHA512

HmacSHA256

HmacSHA512/224

HmacSHA512/256

HmacPBESHA1

PBEWithHmacSHA1

PBEWithHmacSHA224

PBEWithHmacSHA256

PBEWithHmacSHA384

PBEWithHmacSHA512

SecretKeyFactory

DES

DESede

PBEWithMD5AndDES

PBEWithMD5AndTripleDES

PBEWithSHA1AndDESede

PBEWithSHA1AndRC2_40

PBEWithSHA1AndRC2_128

PBEWithSHA1AndRC4_40

PBEWithSHA1AndRC4_128

PBKDF2WithHmacSHA1

PBKDF2WithHmacSHA224

PBKDF2WithHmacSHA256

PBKDF2WithHmacSHA384

PBKDF2WithHmacSHA512

PBEWithHmacSHA1AndAES_128

PBEWithHmacSHA224AndAES_128

PBEWithHmacSHA256AndAES_128

PBEWithHmacSHA384AndAES_128

PBEWithHmacSHA512AndAES_128

PBEWithHmacSHA1AndAES_256

PBEWithHmacSHA224AndAES_256

PBEWithHmacSHA256AndAES_256

PBEWithHmacSHA384AndAES_256

PBEWithHmacSHA512AndAES_256

次の表では、SunJCEプロバイダで使用できる暗号変換を示します。

表4-14 SunJCEプロバイダの暗号変換

アルゴリズム名

モード

パディング

AES

ECB、CBC、PCBC、CTR、CTS、CFB[1]、CFB8..CFB128、OFB[1]、OFB8..OFB128

NoPadding、PKCS5Padding、ISO10126Padding

AES

GCM

NoPadding

AESWrap

ECB

NoPadding

AESWrap_128

ECB

NoPadding

AESWrap_192

ECB

NoPadding

AESWrap_256

ECB

NoPadding

ARCFOUR

ECB

NoPadding

Blowfish、DES、DESede、RC2

ECB、CBC、PCBC、CTR、CTS、CFB[1]、CFB8..CFB64、OFB[1]、OFB8..OFB64

NoPadding、PKCS5Padding、ISO10126Padding

DESedeWrap

CBC

NoPadding

PBEWithMD5AndDES、

PBEWithMD5AndTripleDES [2]、

PBEWithSHA1AndDESede、

PBEWithSHA1AndRC2_40、

PBEWithSHA1AndRC2_128、

PBEWithSHA1AndRC4_40、

PBEWithSHA1AndRC4_128、

PBEWithHmacSHA1AndAES_128、

PBEWithHmacSHA224AndAES_128、

PBEWithHmacSHA256AndAES_128、

PBEWithHmacSHA384AndAES_128、

PBEWithHmacSHA512AndAES_128、

PBEWithHmacSHA1AndAES_256、

PBEWithHmacSHA224AndAES_256、

PBEWithHmacSHA256AndAES_256、

PBEWithHmacSHA384AndAES_256、

PBEWithHmacSHA512AndAES_256

CBC

PKCS5Padding

RSA

ECB

NoPadding、

PKCS1Padding、

OAEPPadding

OAEPWithMD5AndMGF1Padding、

OAEPWithSHA1AndMGF1Padding、

OAEPWithSHA-1AndMGF1Padding、

OAEPWithSHA-224AndMGF1Padding、

OAEPWithSHA-256AndMGF1Padding、

OAEPWithSHA-384AndMGF1Padding、

OAEPWithSHA-512AndMGF1Padding

脚注1: 値指定なしのCFB/OFBは、デフォルトでアルゴリズムのブロック・サイズになります(つまり、AESは128で、Blowfish、DES、DESedeおよびRC2は64です。)

脚注2: PBEWithMD5AndTripleDESは、標準化されていない独自のアルゴリズムです。

鍵サイズの制限

SunJCEプロバイダでは、次のデフォルト鍵サイズ(ビット単位)が使用され、次の制限が強制されます。

KeyGenerator

表4-15 SunJCEプロバイダの鍵サイズの制限

アルゴリズム名

デフォルトの鍵サイズ

制限/コメント

AES

128

鍵サイズは、128、192または256と等しいサイズである必要があります。

ARCFOUR (RC4)

128

鍵サイズは、40から1024の範囲内である必要があります。

Blowfish

128

鍵サイズは、32から448の範囲内で、8の倍数である必要があります。

DES

56

鍵サイズは、56と等しいサイズである必要があります。

DESede (トリプルDES)

168

鍵サイズは、112または168と等しいサイズである必要があります。

鍵サイズが112の場合には2つの中間鍵を保持するトリプルDES鍵が、168の場合には3つの中間鍵を保持するトリプルDES鍵が、それぞれ生成されます。

Meet-In-The-Middle問題のため、112ビットまたは168ビットの鍵データが使用されますが、有効な鍵サイズはそれぞれ80ビットまたは112ビットです。

HmacMD5

512

鍵サイズの制限はありません。

HmacSHA1

512

鍵サイズの制限はありません。

HmacSHA224

224

鍵サイズの制限はありません。

HmacSHA256

256

鍵サイズの制限はありません。

HmacSHA384

384

鍵サイズの制限はありません。

HmacSHA512

512

鍵サイズの制限はありません。

RC2

128

鍵サイズは、40から1024の範囲内である必要があります。

注意:
各種のパスワードベース暗号化(PBE)アルゴリズムは、様々なアルゴリズムを使用して鍵データを生成し、最終的にターゲットのCipherアルゴリズムに依存します。次に例を示します。
PBEWithMD5AndDESは常に56ビットの鍵を生成します。

表4-16 KeyPairGenerator

アルゴリズム名

デフォルトの鍵サイズ

制限/コメント

Diffie-Hellman (DH)

2048

鍵サイズは、512から1024の範囲で64の倍数であるか、1536、2048、3072、4096、6144、8192である必要があります。

表4-17 AlgorithmParameterGenerator

アルゴリズム名

デフォルトの鍵サイズ

制限/コメント

Diffie-Hellman (DH)

2048

鍵サイズは、512から1024の範囲で64の倍数であるか、2048および3072である必要があります。

SunJGSSプロバイダ

SunJGSSプロバイダでは、次のアルゴリズムを使用できます。

表4-18 SunJGSSプロバイダ

OID

名前

1.2.840.113554.1.2.2

Kerberos v5

1.3.6.1.5.5.2

SPNEGO

SunSASLプロバイダ

SunSASLプロバイダでは、次のアルゴリズムを使用できます。

表4-19 SunSASLプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

SaslClient

CRAM-MD5

DIGEST-MD5

EXTERNAL

NTLM

PLAIN

SaslServer

CRAM-MD5

DIGEST-MD5

NTLM

XMLDSigプロバイダ

XMLDSigプロバイダでは、次のアルゴリズムを使用できます。

表4-20 XMLDSigプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

KeyInfoFactory

DOM

TransformService

http://www.w3.org/TR/2001/REC-xml-c14n-20010315 - (CanonicalizationMethod.INCLUSIVE)

http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments - (CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS)

http://www.w3.org/2001/10/xml-exc-c14n# - (CanonicalizationMethod.EXCLUSIVE)

http://www.w3.org/2001/10/xml-exc-c14n#WithComments - (CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS)

http://www.w3.org/2000/09/xmldsig#base64 - (Transform.BASE64)

http://www.w3.org/2000/09/xmldsig#enveloped-signature - (Transform.ENVELOPED)

http://www.w3.org/TR/1999/REC-xpath-19991116 - (Transform.XPATH)

http://www.w3.org/2002/06/xmldsig-filter2 - (Transform.XPATH2)

http://www.w3.org/TR/1999/REC-xslt-19991116 - (Transform.XSLT)

XMLSignatureFactory

DOM

SunPCSCプロバイダ

SunPCSCプロバイダを使用すると、アプリケーションはJavaスマート・カードI/O APIを使用して、ベースとなるオペレーティング・システムのPC/SCスマート・カード・スタックと相互に作用することができます。詳細は、オペレーティング・システムのドキュメントを参照してください。

SolarisおよびLinuxプラットフォームでは、SunPCSCはlibpcsclite.soライブラリを使用してPC/SCスタックにアクセスします。これは、ディレクトリ/usr/$LIBISAおよび/usr/local/$LIBISA内でこのライブラリを検索します。$LIBISAは、64ビットのSolarisプラットフォームではlib/64、64ビットのLinuxプラットフォームではlib64にそれぞれ展開されます。システム・プロパティsun.security.smartcardio.libraryが、代替のlibpcsclite.so実装の完全なファイル名に設定される場合もあります。Windowsプラットフォームでは、SunPCSCは常にwinscard.dllを呼び出すため、Javaレベルの構成は不要であり、行うことができません。

ホスト・プラットフォームでPC/SCが使用可能な場合、SunPCSC実装はTerminalFactory.getDefault()およびTerminalFactory.getInstance("PC/SC")を介して取得できます。PC/SCが使用可能でないか、または正しく構成されていない場合、getInstance()の呼出しはNoSuchAlgorithmExceptionで失敗し、getDefault()は端末をサポートしないJRE組込み実装を返します。

SunPCSCプロバイダでは、次のアルゴリズムを使用できます。

表4-21 SunPCSCプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

TerminalFactory

PC/SC

SunMSCAPIプロバイダ

SunMSCAPIプロバイダを使用すると、アプリケーションは標準のJCA/JCE APIを使用して、Microsoft Windowsプラットフォームのネイティブ暗号化ライブラリ、証明書ストアおよび鍵コンテナにアクセスできます。SunMSCAPIプロバイダ自体には暗号化機能は含まれていません。このプロバイダは、Java環境とWindowsのネイティブ暗号化サービス間のコンジットとして機能します。

SunMSCAPIプロバイダでは、次のアルゴリズムを使用できます。

表4-22 SunMSCAPIのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

Cipher

RSA RSA/ECB/PKCS1Paddingのみ

KeyPairGenerator

RSA

KeyStore

Windows-MY: ネイティブMicrosoft Windows MYキーストアを識別するキーストア・タイプ。ユーザーの個人証明書および関連する非公開鍵が含まれています。

Windows-ROOT: ネイティブMicrosoft Windows ROOTキーストアを識別するキーストア・タイプ。ルート認証局の証明書およびほかの信頼できる自己署名証明書が含まれています。

SecureRandom

Windows-PRNG: ネイティブ擬似乱数生成(PRNG)アルゴリズムの名前。

Signature

MD5withRSA

MD2withRSA

NONEwithRSA

SHA1withRSA

SHA256withRSA

SHA384withRSA

SHA512withRSA

鍵サイズの制限

SunMSCAPIプロバイダでは、次のデフォルト・キーサイズ(ビット単位)を使用し、次の制限を強制します。

KeyGenerator

表4-23 SunMSCAPIプロバイダの鍵サイズの制限

アルゴリズム名前

デフォルトの鍵サイズ

制限/コメント

RSA

2048

キー・サイズの範囲は512ビットから16,384ビットです。ベースとなるMicrosoft Windows暗号化サービス・プロバイダによって異なります。

SunECプロバイダ

SunECプロバイダは、楕円曲線暗号(ECC)を実装します。RSAのような従来の暗号システムと比べて、ECCではより小さい鍵サイズで同等のセキュリティが提供されるため、計算の高速化、消費電力の削減、メモリーと帯域幅の節約を実現できます。

アプリケーションは、JDK 6リリースのように(SunPKCS11を介して)外部のECCライブラリに依存せずに、標準のJCA/JCE APIを使用してECCの機能にアクセスできるようになりました。

SunECプロバイダでは、次のアルゴリズムを使用できます。

表4-24 エンジン・クラスのSunECプロバイダ名

エンジン

アルゴリズム名

AlgorithmParameters

EC

KeyAgreement

ECDH

KeyFactory

EC

KeyPairGenerator

EC

Signature

NONEwithECDSA

SHA1withECDSA

SHA224withECDSA

SHA256withECDSA

SHA384withECDSA

SHA512withECDSA

NONEwithECDSAinP1363Format

SHA1withECDSAinP1363Format

SHA224withECDSAinP1363Format

SHA256withECDSAinP1363Format

SHA384withECDSAinP1363Format

SHA512withECDSAinP1363Format

鍵サイズの制限

SunECプロバイダでは、次のデフォルト・キーサイズ(ビット単位)を使用し、次の制限を強制します。

KeyPairGenerator

表4-25 SunECプロバイダの鍵サイズの制限

アルゴリズム名前

デフォルトの鍵サイズ

制限/コメント

EC

256

キー・サイズは、112から571の範囲内である必要があります。

OracleUcryptoプロバイダ

Solaris専用のセキュリティ・プロバイダであるOracleUcryptoは、Solaris Ucryptoライブラリを利用して、Oracle SPARC T4ベースのオンコア暗号化命令でサポートされる暗号化操作のオフロードと委譲を行います。OracleUcryptoプロバイダ自体には暗号化機能は含まれていません。このプロバイダは、Java環境とSolaris Ucryptoライブラリ間のコンジットとして機能します。

基盤となるSolaris Ucryptoライブラリが特定のアルゴリズムをサポートしない場合は、OracleUcryptoプロバイダもそれをサポートしません。その結果、実行時にサポートされるアルゴリズムは、Solaris UcryptoライブラリがサポートするアルゴリズムとOracleUcryptoプロバイダが認識するアルゴリズムの共通部分になります。

OracleUcryptoプロバイダでは、次のアルゴリズムを使用できます。

表4-26 OracleUcryptoプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

Cipher

AES

RSA

AES/ECB/NoPadding

AES/ECB/PKCS5Padding

AES/CBC/NoPadding

AES/CBC/PKCS5Padding

AES/CTR/NoPadding

AES/GCM/NoPadding

AES/CFB128/NoPadding

AES/CFB128/PKCS5Padding

AES_128/ECB/NoPadding

AES_192/ECB/NoPadding

AES_256/ECB/NoPadding

AES_128/CBC/NoPadding

AES_192/CBC/NoPadding

AES_256/CBC/NoPadding

AES_128/GCM/NoPadding

AES_192/GCM/NoPadding

AES_256/GCM/NoPadding

RSA/ECB/PKCS1Padding

RSA/ECB/NoPadding

Signature

MD5withRSA

SHA1withRSA

SHA256withRSA

SHA384withRSA

SHA512withRSA

MessageDigest

MD5

SHA

SHA-224

SHA-256

SHA-384

SHA-512

SHA3–224

SHA3–256

SHA3–384

SHA3–512

鍵サイズの制限

OracleUcryptoプロバイダは、デフォルトのキー・サイズまたはキー・サイズ制限を指定しません。これらは、基盤となるSolaris Ucryptoライブラリで指定されます。

OracleUcryptoプロバイダの構成ファイル

OracleUcryptoプロバイダには、$JAVA_HOME/conf/securityディレクトリに配置されたucrypto-solaris.cfgという名前の構成ファイルがあります。デフォルトで無効にするアルゴリズムを指定するには、この構成ファイルを変更します。たとえば、次の構成ファイルは、CFB128モードでのAESをデフォルトで無効にします。

#
# Configuration file for the OracleUcrypto provider
#
disabledServices = {
Cipher.AES/CFB128/PKCS5Padding
Cipher.AES/CFB128/NoPadding
}

Appleプロバイダ

Appleプロバイダは、macOSキー・チェーンへのアクセスを提供するjava.security.KeyStoreを実装します。

Appleプロバイダでは、次のアルゴリズムを使用できます。

表4-27 Appleプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

KeyStore

KeychainStore

JdkLDAPプロバイダ

JdkLDAPプロバイダは、SUNプロバイダでのLDAP CertStore実装の代替として、JDK 9で導入されました。

JdkLDAPプロバイダでは、次のアルゴリズムを使用できます。

表4-28 JdkLDAPプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

CertStore

LDAP

JdkSASLプロバイダ

JdkSASLプロバイダでは、次のアルゴリズムを使用できます。

表4-29 JdkSASLプロバイダのエンジン・クラス・アルゴリズム名

エンジン

アルゴリズム名

SaslClient

GSSAPI

SaslServer

GSSAPI

5 PKCS#11リファレンス・ガイド

Javaプラットフォームでは、暗号化操作を実行するための一連のプログラミング・インタフェースを定義しています。これらのインタフェースは、総称してJava暗号化アーキテクチャ(JCA)およびJava暗号化拡張機能(JCE)と呼ばれています。Java暗号化アーキテクチャ(JCA)リファレンス・ガイドを参照してください。

暗号化インタフェースはプロバイダ・ベースです。具体的には、アプリケーションはアプリケーション・プログラミング・インタフェース(API)とやり取りをし、実際の暗号化操作は、一連のサービス・プロバイダ・インタフェース(SPI)に従った構成済みプロバイダ内で実行されます。このアーキテクチャでは、プロバイダのさまざまな実装をサポートしています。ソフトウェアで暗号化操作を行うプロバイダもあれば、スマートカード・デバイスやハードウェア暗号化アクセラレータなどのハードウェア・トークン上で暗号化操作を行うプロバイダもあります。

暗号化トークン・インタフェース標準であるPKCS#11は、RSA Securityが策定し、ハードウェア暗号化アクセラレータやスマートカードなどの暗号化トークンに対するネイティブ・プログラミング・インタフェースを定義しています。JCAおよびJCE APIを使用する既存のアプリケーションでは、PKCS#11プロバイダを使用してネイティブPKCS#11トークンにアクセスできます。アプリケーションへの変更は必要ありません。必要なのは、プロバイダを正しく構成することのみです。

アプリケーションは既存のAPIを使用してPKCS#11機能のほとんどを利用できますが、柔軟性や機能性をさらに必要とするアプリケーションもあります。たとえば、アプリケーションで、動的に抜き差しするスマートカードをより簡単に扱えるようにする必要がある場合があります。また、PKCS#11トークンで鍵とは関係ない一部の操作を認証する必要があるため、アプリケーションはキーストアを使用せずにトークンにログインできるようにする必要があります。JCAにより、アプリケーションが様々なプロバイダを扱う際の柔軟性が向上します。

このドキュメントでは、ネイティブPKCS#11トークンをJavaアプリケーションで使用できるように、Javaプラットフォームに構成する方法を説明します。また、JCAによってPKCS#11プロバイダを始めとする各種プロバイダがアプリケーションでどのように扱いやすくなるかについても説明します。

SunPKCS11プロバイダ

その他の多くのプロバイダとは異なり、SunPKCS11プロバイダ自体は暗号化アルゴリズムを実装していません。その代わりに、Java JCAおよびJCE APIとネイティブPKCS#11暗号化APIとの間のブリッジとして動作し、これらの間の呼び出しと規則を変換します。

つまり、標準JCAおよびJCE APIを呼び出すJavaアプリケーションなら、アプリケーションを変更しなくても、基盤となる次のようなPKCS#11実装で提供されるアルゴリズムを利用できるということです。

- 暗号化スマートカード、

- ハードウェア暗号化アクセラレータ

- ハイ・パフォーマンスのソフトウェア実装。

注意:
Java SEは、ネイティブPKCS#11実装へのアクセスを容易にするのみであり、それ自体にはネイティブPKCS#11実装は含まれていません。ただし、スマート・カードやハードウェア・アクセラレータなどの暗号化デバイスには、PKCS#11実装を含んだソフトウェアが付属していることが一般的で、製造元の指示に従ってインストールし、構成する必要があります。

SunPKCS11の要件

SunPKCS11プロバイダでは、PKCS#11 v2.0以降の実装がシステムにインストールされている必要があります。この実装は、共有オブジェクト・ライブラリ(SolarisおよびLinuxでの.so)またはダイナミック・リンク・ライブラリ(Windowsでの.dll)の形態である必要があります。使用する暗号化デバイスにこのようなPKCS#11実装が含まれているかどうかを調べる方法、実装を構成する方法、およびライブラリ・ファイルのファイル名については、ベンダーが提供するマニュアルを参照してください。

SunPKCS11プロバイダでは、基盤となるPKCS#11実装で提供されている場合は、多くのアルゴリズムがサポートされています。それらのアルゴリズムとその対応するPKCS#11メカニズムを、SunPKCS11プロバイダでサポートされているアルゴリズムで表に示します。

SunPKCS11の構成

SunPKCS11プロバイダは、モジュールjdk.crypto.cryptokiにあります。このプロバイダを使用するには、まずそれを静的にまたはプログラムでインストールする必要があります。

このプロバイダを静的にインストールするには、Javaセキュリティのプロパティ・ファイル(java-home/conf/security/java.security)にプロバイダを追加します。

たとえば次は、SunPKCS11プロバイダを構成ファイル/opt/bar/cfg/pkcs11.cfgとともにインストールするjava.securityファイルの一部です。

# configuration for security providers 1-12 ommitted
security.provider.13=SunPKCS11 /opt/bar/cfg/pkcs11.cfg

このプロバイダを動的にインストールするには、適切な構成ファイル名を使用してプロバイダのインスタンスを作成し、インストールします。次に例を示します。

String configName = "/opt/bar/cfg/pkcs11.cfg";
Provider p = Security.getProvider("SunPKCS11");
p = p.configure(configName);
Security.addProvider(p);

PKCS#11実装あたり複数のスロットを使用する場合や、複数のPKCS#11実装を使用する場合は、適切な構成ファイルでそれぞれのインストールを繰り返すだけです。これにより、それぞれのPKCS#11実装の各スロットに対してSunPKCS11プロバイダのインスタンスが1つ作成されることになります。

構成ファイルはテキスト・ファイルであり、これには次の形式でエントリが含まれています。

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

2つある必須属性は、nameおよびlibraryです。
次に、構成ファイルの例を示します。

name = FooAccelerator
library = /opt/foo/lib/libpkcs11.so

コメントは、# (シャープ)記号で始まる行に記述します。

表5-1 PKCS#11プロバイダの構成ファイル内の属性

属性

値

説明

library

PKCS#11実装のパス名

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

name

このプロバイダ・インスタンスの名前接尾辞

この文字列は、接頭辞SunPKCS11-と連結して、このプロバイダ・インスタンスの名前(つまり、プロバイダ・インスタンスのProvider.getName()メソッドで返される文字列)を生成します。たとえばname属性がFooAcceleratorの場合、プロバイダ・インスタンスの名前はSunPKCS11-FooAcceleratorになります。

description

このプロバイダ・インスタンスの説明

この文字列は、プロバイダ・インスタンスのProvider.getInfo()メソッドで返されます。何も指定されていない場合、デフォルトの説明が返されます。

slot

スロットのID

このプロバイダ・インスタンスが関連付けされているスロットのID。たとえば、PKCS#11のID 1のスロットでは、1を使用します。指定できるのは、多くともslotかslotListIndexのどちらか1つです。どちらも指定しない場合のデフォルトは、値0のslotListIndexです。

slotListIndex

スロットのインデックス

このプロバイダ・インスタンスが関連付けされているスロットのインデックス。これは、PKCS#11の関数C_GetSlotListで返されるすべてのスロットのリストに対するインデックスです。たとえば、0はリストの先頭のスロットを表します。指定できるのは、多くともslotかslotListIndexのどちらか1つです。どちらも指定しない場合のデフォルトは、値0のslotListIndexです。

enabledMechanisms

有効にするPKCS#11メカニズムのリスト。空白文字で区切り、全体を中カッコ()で囲む

これは、SunPKCS11プロバイダとPKCS#11トークンの両方でサポートされている場合に、このプロバイダ・インスタンスで使用する必要があるPKCS#11メカニズムのリストです。その他のメカニズムはすべて無視されます。リスト内の各エントリは、PKCS#11メカニズムの名前になります。2つのPKCS#11メカニズムから成るリストの例を次に示します。

enabledMechanisms = {
CKM_RSA_PKCS
CKM_RSA_PKCS_KEY_PAIR_GEN
}

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

disabledMechanisms

無効にするPKCS#11メカニズムのリスト。空白文字で区切り、全体を中カッコ()で囲む

このプロバイダ・インスタンスが無視するPKCS#11メカニズムのリスト。リストされたあらゆるメカニズムは、トークンとSunPKCS11プロバイダでサポートされていても、プロバイダによって無視されます。これらのサービスを無効にするために文字列SecureRandomおよびKeyStoreを指定できます。
指定できるのは、多くともenabledMechanismsかdisabledMechanismsのどちらか1つです。どちらも指定しない場合、有効なメカニズムは、SunPKCS11プロバイダ(SunPKCS11プロバイダでサポートされているアルゴリズムを参照)とPKCS#11トークンの両方でサポートされているメカニズムになります。

attributes

下記参照

attributesオプションは、PKCS#11鍵オブジェクトの作成時に設定される追加のPKCS#11属性を指定するために使用します。これにより、特定の属性を必要とするトークンを使用できるようになります。詳細は、次のセクションを参照してください。

属性の構成

attributesオプションは、PKCS#11鍵オブジェクトの作成時に設定される追加のPKCS#11属性を指定するために使用できます。デフォルトでSun PKCS#11プロバイダでは、オブジェクトの作成時に必須のPKCS#11属性を指定するだけです。たとえばRSA公開鍵では、鍵タイプおよびアルゴリズム(CKA_CLASSおよびCKA_KEY_TYPE)と、RSA公開鍵の鍵の値(CKA_MODULUSおよびCKA_PUBLIC_EXPONENT)を指定します。使用しているPKCS#11ライブラリでは、実装固有のデフォルト値をRSA公開鍵のその他の属性に割り当てます。たとえば、メッセージの暗号化と検証に鍵を使用できます(CKA_ENCRYPTおよびCKA_VERIFY = true)。

attributesオプションは、PKCS#11実装で割り当てたデフォルト値を使用しない場合や、PKCS#11実装でデフォルト値をサポートしないために、明示的に値を指定する必要がある場合に使用します。使用しているPKCS#11実装でサポートしない属性や、該当する鍵タイプで無効な属性を指定すると、実行時に操作が失敗する原因となります。

オプションは0回または複数回指定できます。また、次に説明するように、構成ファイルで指定した順番でオプションが処理されます。attributesオプションは次の書式です。

attributes(operation, keytype, keyalgorithm) = {
name1 = value1
[...]
}

operationの有効な値は次のとおりです。

- generate。KeyPairGeneratorまたはKeyGeneratorによって生成された鍵用

- import。KeyFactoryまたはSecretKeyFactoryによって作成された鍵用。暗号化操作の初期化メソッド(Signature.initSign()など)に渡されるときにPKCS#11鍵オブジェクトに自動的に変換されるJava Software鍵にも適用される。

- *。生成操作または作成操作のどちらかで作成された鍵用。

keytypeの有効な値は、CKO_PUBLIC_KEY、CKO_PRIVATE_KEY、およびCKO_SECRET_KEYで、それぞれ公開鍵、非公開鍵、および秘密鍵に対応しています。また、任意の鍵タイプに一致する*もあります。

keyalgorithmの有効な値は、PKCS#11仕様に定義されたCKK_xxx定数のいずれか1つ、または任意の鍵アルゴリズムに一致する*です。Sun PKCS11プロバイダで現在サポートしているアルゴリズムは、CKK_RSA、CKK_DSA、CKK_DH、CKK_AES、CKK_DES、CKK_DES3、CKK_RC4、CKK_BLOWFISH、およびCKK_GENERICです。

属性の名前と値は、1つまたは複数の名前と値のペアのリストとして指定されます。nameはPKCS#11仕様のCKA_xxx定数(CKA_SENSITIVEなど)でなければなりません。valueは、次のいずれかになります。

- ブール値。trueまたはfalse

- 整数値。10進数表記(デフォルト)または0xで始まる16進数表記。

- null。この属性はオブジェクトの作成時に指定してはならないことを示す。

attributesオプションを複数回指定すると、エントリは、まとめられた属性で指定された順序で、あとの属性が前の属性をオーバーライドして処理されます。たとえば、次の構成ファイルを考えてみます。

attributes(*,CKO_PRIVATE_KEY,*) = {
CKA_SIGN = true
}

attributes(*,CKO_PRIVATE_KEY,CKK_DH) = {
CKA_SIGN = null
}

attributes(*,CKO_PRIVATE_KEY,CKK_RSA) = {
CKA_DECRYPT = true
}

1番目のエントリでは、すべての非公開鍵に対してCKA_SIGN = trueを指定しています。2番目のオプションでは、Diffie-Hellman非公開鍵についてnullでオーバーライドするため、CKA_SIGN属性はDiffie-Hellman非公開鍵に対してまったく指定されません。最後に、3番目のオプションでは、RSA非公開鍵に対してCKA_DECRYPT = trueを指定しています。つまりRSA非公開鍵には、CKA_SIGN = trueとCKA_DECRYPT = true両方のセットがあることになります。

attributesオプションには特殊な形式もあります。構成ファイルにattributes = compatibilityと書くことができます。これは、属性ステートメントのセット全体に対するショートカットです。これはプロバイダが既存のJavaアプリケーションとの互換性を最大限に保つことを目的としています。これにより、Javaアプリケーションでは、たとえばすべての鍵コンポーネントにアクセス可能で、秘密鍵を暗号化および復号化の両方に使用可能であることが期待されます。compatibility属性の行は、他のattributes行とともに使用できます。この場合は先に説明したような、同様のアグリゲーションおよびオーバーライドの規則が適用されます。

Network Security Services (NSS)へのアクセス

ネットワーク・セキュリティ・サービス(NSS)は一連のオープン・ソース・セキュリティ・ライブラリであり、その暗号化APIは、PKCS#11に基づいていますが、PKCS#11標準ではない特別な機能を含んでいます。SunPKCS11プロバイダには、NSS固有の機能(複数のNSS固有の構成ディレクティブなど)と相互に作用するためのコードが含まれています。これらについては次で説明します。

最適な結果を得るために、使用可能な最新バージョンのNSSを使用することをお薦めします。少なくともバージョン3.12になります。

次に説明されているnss構成ディレクティブのいずれかが使用されている場合、SunPKCS11プロバイダはNSS固有のコードを使用します。この場合、通常の構成コマンドlibrary、slot、およびslotListIndexは使用できません。

表5-2 NSSの属性と値

属性

値

説明

nssLibraryDirectory

NSSおよびNSPRライブラリを含むディレクトリ

NSSおよびNSPRライブラリを含むディレクトリのフル・パス名。Java VMとして同じプロセス内で実行されている別のコンポーネントによってNSSがすでにロードおよび初期化されていないかぎり、この属性を指定する必要があります。
プラットフォームに応じて、このディレクトリを含めるためにLD_LIBRARY_PATHまたはPATH (Windows)を設定して、オペレーティング・システムで依存ライブラリを検索できるようにする必要がある場合があります。

nssSecmodDirectory

NSS DBファイルを含むディレクトリ

NSS構成および鍵情報(secmod.db、key3.db、およびcert8.db)を含むディレクトリのフル・パス名。別のコンポーネントによってNSSがすでに初期化されていないか(上記参照)、または次に説明されているようにデータベース・ファイルなしでNSSが使用されないかぎり、この指示を指定する必要があります。

nssDbMode

readWrite、readOnly、noDbのいずれか

この指示は、NSSデータベースへのアクセス方法を決定します。読書きモードではフル・アクセスが可能ですが、データベースには一度に1つのプロセスのみがアクセスする必要があります。読取り専用モードでは、このファイルの変更は許可されません。
noDbモードを使用すると、データベース・ファイルなしで純粋に暗号化プロバイダとしてNSSを使用できます。PKCS11 KeyStoreを使用して持続的な鍵を作成することはできません。NSSには、Oracleのバンドル済Javaベース暗号化プロバイダ(Elliptic Curve Cryptography (ECC)など)では現在使用できない高度に最適化された実装およびアルゴリズムが含まれているため、このモードは有用です。

nssModule

keystore、crypto、fips、trustanchorsのいずれか

さまざまなライブラリやスロットを使用してNSSの機能が使用できるようになっています。この指示は、SunPKCS11のインスタンスがアクセスするモジュールを決定します。
cryptoモジュールは、noDbモードでのデフォルトです。このモジュールはログインを必要としない暗号化操作をサポートしますが、持続的な鍵はサポートしません。

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

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

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

例5-1 NSS用SunPKCS11構成ファイル

純粋な暗号化プロバイダとしてのNSS

name = NSScrypto
nssLibraryDirectory = /opt/tests/nss/lib
nssDbMode = noDb
attributes = compatibility

FIPS 140準拠の暗号化トークンとしてのNSS

name = NSSfips
nssLibraryDirectory = /opt/tests/nss/lib
nssSecmodDirectory = /opt/tests/nss/fipsdb
nssModule = fips

PKCS#11のトラブルシューティング

PKCS#11に問題が発生し、デバッグが必要になることがあります。ライブラリ、スロット、トークンおよびメカニズムのデバッグ情報を表示するには、<java-home>/conf/security/sunpkcs11-solaris.cfgファイルにshowInfo=trueを追加します。

その他のデバッグ情報については、次のいずれかのオプションを指定してJavaプロセスを起動または再起動してください。

- SunPKCS11プロバイダの一般的なデバッグ情報:
-Djava.security.debug=sunpkcs11

- PKCS#11キーストア固有のデバッグ情報の場合:
-Djava.security.debug=pkcs11keystore

PKCS#11プロバイダまたは個々のPKCS#11メカニズム(あるいはその両方)の無効化

トラブルシューティング・プロセスの一環として、PKCS#11プロバイダ、または特定のプロバイダに固有のメカニズムを一時的に無効にすると便利な場合があります。

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

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

PKCS#11プロバイダは、次のいずれかの方法で無効にできます。

1. 単一のJavaプロセスに対してPKCS#11を無効にします。次のJavaコマンド行フラグを使用してJavaプロセスを起動または再起動します。
-Dsun.security.pkcs11.enable-solaris=false

注意:
このステップは、デフォルトのSolaris PKCS11プロバイダ・ファイル(sun.security.pkcs11.SunPKCS11、/conf/security/sunpkcs11-solaris.cfgおよび/conf/security/sunpkcs11-solaris.cfg)に基づくSunPKCS11プロバイダにのみ適用されます。

2. 特定のJavaインストールで実行されるすべてのJavaプロセスについてPKCS#11を無効にします。これは、APIを使用して動的に実行するか(この項には記載されていません)、次に示すように<java_home>/conf/security/java.securityファイルを編集してSunPKCS11セキュリティ・プロバイダをコメント・アウトすることにより静的に実行できます(必要な場合はプロバイダの順序の番号を変更することを忘れないでください)。

#
# List of providers and their preference orders (see above):
#
security.provider.1=SUN
security.provider.2=SunRsaSign
security.provider.3=SunEC
security.provider.4=SunJSSE
security.provider.5=SunJCE
security.provider.6=SunJGSS
security.provider.7=SunSASL
security.provider.8=XMLDSig
security.provider.9=SunPCSC
security.provider.10=JdkLDAP
security.provider.11=JdkSASL
security.provider.12=SunMSCAPI
#security.provider.13=SunPKCS11

このJavaのインストールで実行されているJavaプロセスを起動または再起動します。

特定のメカニズムの無効化

PKCS#11のメカニズムのいずれかで問題が発生した場合、PKCS#11プロバイダ全体ではなく、その特定のメカニズムのみを無効にすることにより、問題を解決できます(それまでにPKCS#11プロバイダを無効にしていた場合は、再度有効にすることを忘れないでください)。

たとえば、SecureRandomメカニズムのみを無効にするには、<java-home>/conf/security/sunpkcs11-solaris.cfgファイル内の無効なメカニズムのリストにSecureRandomを追加します。

name = Solaris

description = SunPKCS11 accessing Solaris Cryptographic Framework

library = /usr/lib/$ISA/libpkcs11.so

handleStartupErrors = ignoreAll

# Use the X9.63 encoding for EC points (do not wrap in an ASN.1 OctetString).
useEcX963Encoding = true

attributes = compatibility

disabledMechanisms = {
CKM_DSA_KEY_PAIR_GEN
SecureRandom
}

アプリケーション開発者

Javaアプリケーションは、既存のJCAおよびJCE APIを使用して、SunPKCS11プロバイダ経由でPKCS#11トークンにアクセスできます。

トークン・ログイン

個人識別番号を使用してキーストアにログインし、PKCS#11の操作を実行できます。

秘密鍵へのアクセスなど一部のPKCS#11操作では、その操作の実行前に、PIN (個人識別番号)を使用してログインする必要があります。ログインが必要となる操作でもっとも多いのはトークン上の鍵を扱う操作です。Javaアプリケーションではそのような操作で、最初にキーストアをロードするのが一般的です。java.security.KeyStoreクラス経由でキーストアとしてPKCS#11トークンにアクセスするときは、loadメソッドに対してパスワード入力パラメータでPINを指定できます。PINは、その後、トークンにログインするために、SunPKCS11プロバイダが使用します。次に例を示します。

char[] pin = ...;
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, pin);

この例は、静的なキーストアとしてPKCS#11トークンを扱うアプリケーションに適しています。抜き差しされるスマートカードなど、アプリケーションでPKCS#11トークンをより動的に利用する必要がある場合は、新しいKeyStore.Builderクラスを使用します。コールバック・ハンドラでPKCS#11キーストアのビルダーを初期化する方法を次の例に示します。

KeyStore.CallbackHandlerProtection chp =
new KeyStore.CallbackHandlerProtection(new MyGuiCallbackHandler());
KeyStore.Builder builder =
KeyStore.Builder.newInstance("PKCS11", null, chp);

SunPKCS11プロバイダの場合、コールバック・ハンドラがPasswordCallbackに対応できる必要があります。PasswordCallbackは、ユーザーにPINを要求するために使用されます。アプリケーションがキーストアにアクセスしなければならない場合は、ビルダーを次のように使用します。

KeyStore ks = builder.getKeyStore();
Key key = ks.getKey(alias, null);

ビルダーは、先に構成されたコールバック・ハンドラで使用するパスワードを必要に応じてユーザーに求めます。ビルダーがパスワードを要求するのは、初回のアクセス時のみです。アプリケーションのユーザーが同じスマート・カードを使用し続ける場合は、もう一度パスワードを要求されることはありません。ユーザーがスマートカードを抜いて、別のスマートカードを差した場合は、新しいカードに対するパスワードを要求されます。

PKCS#11トークンによっては、鍵とは関係ない操作でもトークン・ログインを必要とする場合があります。そのような操作を使用するアプリケーションでは、java.security.AuthProviderクラスを使用できます。AuthProviderクラスは、java.security.Providerを拡張し、プロバイダでログインおよびログアウト操作を行なったり、プロバイダが使用するコールバック・ハンドラを設定したりするためのメソッドを定義しています。

SunPKCS11プロバイダの場合、コールバック・ハンドラがPasswordCallbackに対応できる必要があります。PasswordCallbackは、ユーザーにPINを要求するために使用されます。

次に、アプリケーションでAuthProviderを使用してトークンにログインする方法の例を示します。

AuthProvider aprov = (AuthProvider)Security.getProvider("SunPKCS11");
aprov.login(subject, new MyGuiCallbackHandler());

トークン鍵

JavaのKeyオブジェクトには、実際の鍵データが含まれる場合も含まれない場合もあります。

- ソフトウェアKeyオブジェクトには、実際の鍵データが含まれており、そのデータにアクセスできる。

- スマートカードなどの保護されたトークン上にある抽出不可能な鍵は、実際の鍵データを含まないJava Keyオブジェクトによって表される。Keyオブジェクトには、実際の鍵への参照のみが含まれる。

アプリケーションおよびプロバイダでは、各種のKeyオブジェクトを表すために正しいインタフェースを使用する必要があります。ソフトウェアKeyオブジェクト(または実際の鍵データにアクセスできる任意のKeyオブジェクト)では、java.security.interfacesおよびjavax.crypto.interfacesパッケージにインタフェース(DSAPrivateKeyなど)を実装する必要があります。抽出不可能なトークン鍵を表すKeyオブジェクトでは、java.securityおよびjavax.cryptoパッケージに、関連するジェネリック・インタフェース(PrivateKey、PublicKeyまたはSecretKey)のみを実装する必要があります。鍵アルゴリズムの特定は、Key.getAlgorithm()メソッドを使用して実行されます。

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

プロバイダの遅延選択

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

- Cipher.init(..., Key key, ...)

- KeyAgreement.init(Key key, ...)

- Mac.init(Key key, ...)

- Signature.initSign(PrivateKey privateKey)

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

このプロバイダの遅延選択は、アプリケーションからは認識されませんが、Cipher、KeyAgreement、Mac、およびSignatureのgetProvider()メソッドの動作に影響します。getProvider()が初期化操作の発生する前 (したがって、プロバイダ選択が起こる前)に呼び出された場合は、要求されるアルゴリズムがサポートされている最初のプロバイダが返されます。このプロバイダは、初期化メソッドが呼び出されたあとに選択されたプロバイダと同じにならない場合があります。getProvider()が初期化操作のあとに呼び出された場合は、実際に選択されたプロバイダが返されます。アプリケーションでは関連する初期化メソッドを呼び出したあとにのみ、getProvider()を呼び出すようにしてください。

getProvider()だけでなく、次のメソッドにも同様の影響があります。

- Cipher.getBlockSize

- Cipher.getExcemptionMechanism

- Cipher.getIV

- Cipher.getOutputSize

- Cipher.getParameters

- Mac.getMacLength

- Signature.getParameters

- Signature.setParameter

JAAS KeyStoreLoginModule

JDKには、JAASキーストア・ログイン・モジュールであるKeyStoreLoginModuleが付属しています。このモジュールを使用すると、アプリケーションで、指定したキーストア内のIDを使用して認証を行うことができます。アプリケーションは認証後に、自身のプリンシパルおよびクレデンシャル情報(証明書および秘密鍵)をキーストアから取得します。このログイン・モジュールを使用し、PKCS#11トークンをキーストアとして使用するように構成すると、アプリケーションはこの情報をPKCS#11トークンから取得できるようになります。

次のオプションを使用してKeyStoreLoginModuleを構成すると、PKCS#11トークンをキーストアとして使用できます。

- keyStoreURL="NONE"

- keyStoreType="PKCS11"

- keyStorePasswordURL=some_pin_url

説明は次のとおりです。

some_pin_url

PINの場所です。keyStorePasswordURLオプションを省略すると、ログイン・モジュールはPINをアプリケーションのコールバック・ハンドラから取得し、PasswordCallback でそのPINを指定します。PKCS#11トークンをキーストアとして使用する構成ファイルの例を次に示します。

other {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="NONE"
keyStoreType="PKCS11"
keyStorePasswordURL="file:/home/joe/scpin";
};

複数のSunPKCS11プロバイダを動的に構成した場合、またはjava.securityセキュリティ・プロパティ・ファイル内で構成した場合は、keyStoreProviderオプションを使用して、特定のプロバイダ・インスタンスを対象にします。このオプションの引数は、プロバイダの名前です。SunPKCS11プロバイダの場合、プロバイダ名はSunPKCS11-TokenNameという形式になります。ここで、TokenNameはプロバイダ・インスタンスが構成された名前の接尾辞です。詳細は、表5-1を参照してください。たとえば、次の構成ファイルでは、PKCS#11プロバイダ・インスタンスに名前接尾辞SmartCardで名前を付けています。

other {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="NONE"
keyStoreType="PKCS11"
keyStorePasswordURL="file:/home/joe/scpin"
keyStoreProvider="SunPKCS11-SmartCard";
};

保護された認証パスを介してのログインをサポートしているPKCS#11トークンもあります。たとえばスマートカードには、PINを入力するための専用のPINパッドがある場合があります。生体測定機器にも、認証情報を取得する専用の方法が用意されています。PKCS#11トークンに保護された認証パスがある場合は、protected=trueオプションを使用し、keyStorePasswordURLオプションは省略します。そのようなトークン用の構成ファイルの例を次に示します。

other {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="NONE"
keyStoreType="PKCS11"
protected=true;
};

JSSEキーストアおよびトラスト・ストアとしてのトークン

PKCS#11トークンをJSSEキーストアまたはトラスト・ストアとして使用できるように、JSSEアプリケーションでは、トークン・ログインで説明したAPIを使用してKeyStoreをインスタンス化します。このKeyStoreは、PKCS#11トークンによって戻され、キー・マネージャおよびトラスト・マネージャに渡されます。こうしてJSSEアプリケーションでは、トークン上の鍵にアクセスできるようになります。

JSSEでは、システム・プロパティ経由でキーストアおよびトラスト・ストアを使用するように構成できます(Java Secure Socket Extension (JSSE)リファレンス・ガイドを参照)。PKCS#11トークンをキーストアまたはトラスト・ストアとして使用するには、javax.net.ssl.keyStoreTypeおよびjavax.net.ssl.trustStoreTypeシステム・プロパティをそれぞれ「PKCS11」に設定し、javax.net.ssl.keyStoreおよびjavax.net.ssl.trustStoreシステム・プロパティをそれぞれ「NONE」に設定します。特定のプロバイダ・インスタンスを使用するように指定するには、javax.net.ssl.keyStoreProviderおよびjavax.net.ssl.trustStoreProviderシステム・プロパティを使用します(例: SunPKCS11-SmartCard)。

PKCS#11トークンとのkeytoolおよびjarsignerの使用

SunPKCS11プロバイダがjava.securityセキュリティ・プロパティ・ファイル(Javaランタイムの$JAVA_HOME/conf/securityディレクトリ内にある)で構成されている場合、次のオプションを使用することで、PKCS#11トークンの操作にkeytoolおよびjarsignerを使用できます。

- -keystore NONE

- -storetype PKCS11

構成済みPKCS#11トークンの内容をリストするコマンドの例を次に示します。

keytool -keystore NONE -storetype PKCS11 -list

PINは-storepassオプションで指定できます。指定されない場合、keytoolおよびjarsignerは、トークンPINを要求します。トークンに保護された認証パス(専用のPINパッドや生体読取り機など)がある場合、-protectedオプションを指定する必要がありますが、パスワード・オプションを指定する必要はありません。

java.securityセキュリティ・プロパティ・ファイル内で複数のSunPKCS11プロバイダが構成されている場合、-providerNameオプションを使用して特定のプロバイダ・インスタンスを選択できます。このオプションの引数は、プロバイダの名前です。

- -providerName providerName

SunPKCS11プロバイダの場合、providerNameの形式はSunPKCS11-TokenNameのようになります。この説明を次に示します。

TokenName

プロバイダ・インスタンスの構成時に付けられた名前の接尾辞です。詳細は、表5-1を参照してください。たとえば、次のコマンドでは、名前接尾辞SmartCardのPKCS#11キーストア・プロバイダ・インスタンスの内容をリストします。

keytool -keystore NONE -storetype PKCS11 \
-providerName SunPKCS11-SmartCard \
-list

SunPKCS11プロバイダをjava.securityセキュリティ・プロパティ・ファイル内で構成していない場合は、次のオプションを使用して、プロバイダを動的にインストールするようにkeytoolおよびjarsignerを設定します。

- -providerClass sun.security.pkcs11.SunPKCS11

- -providerArg ConfigFilePath

ConfigFilePath

トークン構成ファイルのパスです。次に、SunPKCS11プロバイダがjava.securityファイルで構成されていないときにPKCS#11キーストアをリストするコマンドの例を示します。

keytool -keystore NONE -storetype PKCS11 \
-providerClass sun.security.pkcs11.SunPKCS11 \
-providerArg /foo/bar/token.config \
-list

ポリシー・ツール

注意:

ポリシー・ツールは、JDK 9では推奨されていません。

デフォルト・ポリシー実装内のkeystoreエントリには、次の構文があります。これには、PINおよび複数のPKCS#11プロバイダ・インスタンスを指定できます。

keystore "some_keystore_url", "keystore_type", "keystore_provider";
keystorePasswordURL "some_password_url";

説明は次のとおりです。

keystore_provider

キーストア・プロバイダ名です(たとえば、"SunPKCS11-SmartCard")。

some_password_url

トークンPINの場所を指すURLです。keystore_providerとkeystorePasswordURLの行はどちらもオプションです。keystore_providerが指定されていない場合、特定のキーストア・タイプをサポートする最初の構成済みプロバイダが使用されます。keystorePasswordURLの行が指定されていない場合、パスワードは使用されません。

例5-2 PKCS#11トークンのためのキーストア・ポリシー・エントリ

PKCS#11トークンのキーストア・ポリシー・エントリの例を次に示します。

keystore "NONE", "PKCS11", "SunPKCS11-SmartCard";
keystorePasswordURL "file:/foo/bar/passwordFile";

プロバイダ開発者

java.security.Providerクラスを使用すると、プロバイダ開発者は、プロバイダ・サービスおよびパラメータ・サポートによって、より簡単にPKCS#11トークンおよび暗号化サービスをサポートできます。

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

プロバイダ・サービス

プロバイダによるサービス実装ごとに、サービスの型(Cipher、Signatureなど)、ピリオド、およびサービスが適用されるアルゴリズム名で構成される名前のプロパティが必要です。プロパティの値には、サービスを実装するクラスの完全修飾名を指定する必要があります。値com.sun.crypto.provider.DHKeyAgreementを持つようにKeyAgreement.DiffieHellmanプロパティを設定するプロバイダの例を次に示します。

put("KeyAgreement.DiffieHellman", "com.sun.crypto.provider.DHKeyAgreement")

public staticのネストされたクラスProvider.Serviceは、プロバイダ・サービスのプロパティ(型、属性、アルゴリズム名、アルゴリズムの別名など)をカプセル化します。プロバイダはProvider.putService()メソッドを呼び出すことで、Provider.Serviceオブジェクトをインスタンス化して登録できます。これは、Propertyエントリを作成してProvider.put()メソッドを呼び出すことと同じです。Provider.put経由で登録されたレガシーのPropertyエントリも引き続きサポートされています。

クラスcom.sun.crypto.provider.DHKeyAgreementによって実装され、DiffieHellmanアルゴリズムを使用する、KeyAgreement型のServiceオブジェクトを作成するプロバイダの例を次に示します。

Service s = new Service(this, "KeyAgreement", "DiffieHellman",
"com.sun.crypto.provider.DHKeyAgreement", null, null);
putService(s);

旧バージョンのPropertyエントリの代わりにProvider.Serviceeオブジェクトを使用すると、大きな利点が2つあります。1つ目として、エンジン・クラスをインスタンス化するときに、プロバイダの柔軟性が向上します。2つ目として、プロバイダでパラメータ・サポートをテストできます。次に、これらの特徴について説明します。

エンジン・クラスのインスタンス化

デフォルトでは、Java暗号化フレームワークが特定のサービスのプロバイダ・プロパティを検索し、そのプロパティで登録されたエンジン・クラスを直接インスタンス化します。プロバイダはこの動作をオーバーライドし、要求されたサービス自体のためにエンジン・クラスをインスタンス化できます。

デフォルトの動作をオーバーライドするときは、プロバイダはカスタムな動作を追加するようにProvider.Service.newInstance()メソッドをオーバーライドします。たとえばプロバイダはカスタムのコンストラクタを呼び出したり、プロバイダの外部からはアクセスできない(プロバイダのみが知る)情報を使用して初期化を実行したりすることができます。

パラメータ・サポート

Java暗号化フレームワークでは、プロバイダのサービス実装がアプリケーション固有のパラメータを使用できるかどうかを判断するために、すばやいチェックを試みます。このチェックを実行するために、フレームワークではProvider.Service.supportsParameter()を呼び出します。

フレームワークは、プロバイダの遅延選択中にこのすばやいチェックを利用します(プロバイダの遅延選択を参照)。アプリケーションが初期化メソッドを呼び出してKeyオブジェクトを渡すと、フレームワークではService.supportsParameter()メソッドを呼び出すことで、配下のプロバイダに対してそのオブジェクトをサポートしているかどうかを確認します。supportsParameter()がfalseを返すと、フレームワークはただちにそのプロバイダを対象から取り除きます。supportsParameter()がtrueを返すと、フレームワークはKeyオブジェクトをそのプロバイダの初期化エンジン・クラス実装に渡します。ソフトウェアKeyオブジェクトを必要とするプロバイダでは、ソフトウェア以外の鍵を渡されたときにfalseを返すように、このメソッドをオーバーライドする必要があります。同様に、抽出不可能な鍵が含まれたPKCS#11トークンのプロバイダでは、このプロバイダが作成した、つまりそれぞれのトークン上の鍵に対応するKeyオブジェクトに対してtrueのみを返すようにする必要があります。

注意:
supportsParameter()のデフォルト実装ではtrueを返します。これにより、既存のプロバイダを変更しないで動作させることができます。しかし、この緩やかなデフォルト実装のために、フレームワークでは、初期化エンジン・クラス実装内部のKeyオブジェクトを拒否するプロバイダが例外をスローしたときに、その例外をキャッチできるようにしておく必要があります。フレームワークでは、これらのケースをsupportsParameter()がfalseを返すときと同じように扱います。

SunPKCS11プロバイダでサポートされているアルゴリズム

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

注意:
disabledMechanismsおよびenabledMechanisms構成ディレクティブを使用すると、メカニズムを無視するようにSunPKCS11に指示できます(SunPKCS11の構成を参照)。

Elliptic Curveメカニズムでは、SunPKCS11プロバイダは、パラメータのエンコーディングとしてnamedCurveの選択を使用する鍵のみを使用し、圧縮されていない形式のみを許可します。SunPKCS11プロバイダでは、標準名が付けられたすべてのドメイン・パラメータをトークンがサポートしていることが前提となります。

表5-3 SunPKCS11プロバイダでサポートされているJavaアルゴリズム

Javaアルゴリズム

PKCS#11メカニズム

Signature.MD2withRSA

CKM_MD2_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509

Signature.MD5withRSA

CKM_MD5_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509

Signature.SHA1withRSA

CKM_SHA1_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509

Signature.SHA224withRSA

CKM_SHA224_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509

Signature.SHA256withRSA

CKM_SHA256_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509

Signature.SHA384withRSA

CKM_SHA384_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509

Signature.SHA512withRSA

CKM_SHA512_RSA_PKCS、CKM_RSA_PKCS、CKM_RSA_X_509

Signature.SHA1withDSA

CKM_DSA_SHA1、CKM_DSA

Signature.NONEwithDSA

CKM_DSA

Signature.SHA1withECDSA

CKM_ECDSA_SHA1、CKM_ECDSA

Signature.SHA224withECDSA

CKM_ECDSA

Signature.SHA256withECDSA

CKM_ECDSA

Signature.SHA384withECDSA

CKM_ECDSA

Signature.SHA512withECDSA

CKM_ECDSA

Signature.NONEwithECDSA

CKM_ECDSA

Cipher.RSA/ECB/PKCS1Padding

CKM_RSA_PKCS

Cipher.ARCFOUR

CKM_RC4

Cipher.DES/CBC/NoPadding

CKM_DES_CBC

Cipher.DESede/CBC/NoPadding

CKM_DES3_CBC

Cipher.AES/CBC/NoPadding

CKM_AES_CBC

Cipher.Blowfish/CBC/NoPadding

CKM_BLOWFISH_CBC

Cipher.RSA/ECB/NoPadding

CKM_RSA_X_509

Cipher.AES/CTR/NoPadding

CKM_AES_CTR

KeyAgreement.ECDH

CKM_ECDH1_DERIVE

KeyAgreement.DiffieHellman

CKM_DH_PKCS_DERIVE

KeyPairGenerator.RSA

CKM_RSA_PKCS_KEY_PAIR_GEN

KeyPairGenerator.DSA

CKM_DSA_KEY_PAIR_GEN

KeyPairGenerator.EC

CKM_EC_KEY_PAIR_GEN

KeyPairGenerator.DiffieHellman

CKM_DH_PKCS_KEY_PAIR_GEN

KeyGenerator.ARCFOUR

CKM_RC4_KEY_GEN

KeyGenerator.DES

CKM_DES_KEY_GEN

KeyGenerator.DESede

CKM_DES3_KEY_GEN

KeyGenerator.AES

CKM_AES_KEY_GEN

KeyGenerator.Blowfish

CKM_BLOWFISH_KEY_GEN

Mac.HmacMD5

CKM_MD5_HMAC

Mac.HmacSHA1

CKM_SHA_1_HMAC

Mac.HmacSHA224

CKM_SHA224_HMAC

Mac.HmacSHA256

CKM_SHA256_HMAC

Mac.HmacSHA384

CKM_SHA384_HMAC

Mac.HmacSHA512

CKM_SHA512_HMAC

MessageDigest.MD2

CKM_MD2

MessageDigest.MD5

CKM_MD5

MessageDigest.SHA1

CKM_SHA_1

MessageDigest.SHA-224

CKM_SHA224

MessageDigest.SHA-256

CKM_SHA256

MessageDigest.SHA-384

CKM_SHA384

MessageDigest.SHA-512

CKM_SHA512

KeyFactory.RSA

サポートされるRSAメカニズムすべて

KeyFactory.DSA

サポートされるDSAメカニズムすべて

KeyFactory.EC

サポートされるECメカニズムすべて

KeyFactory.DiffieHellman

サポートされるDiffie-Hellmanメカニズムすべて

SecretKeyFactory.ARCFOUR

CKM_RC4

SecretKeyFactory.DES

CKM_DES_CBC

SecretKeyFactory.DESede

CKM_DES3_CBC

SecretKeyFactory.AES

CKM_AES_CBC

SecretKeyFactory.Blowfish

CKM_BLOWFISH_CBC

SecureRandom.PKCS11

CK_TOKEN_INFOにはCKF_RNGビット・セットがある

KeyStore.PKCS11

常に使用可能

SunPKCS11プロバイダのキーストアの要件

ここでは、SunPKCS11プロバイダのKeyStoreを、ベースとなるネイティブのPKCS#11ライブラリへ実装する場合の要件を説明します。

注意:
より多くの既存のPKCS#11ライブラリとの相互運用性を実現するため、将来のリリースで変更が加えられる可能性があります。

読取り専用アクセス

PKCS#11トークンに保存された既存のオブジェクトをKeyStoreエントリにマッピングするため、SunPKCS11プロバイダのKeyStore実装は次の操作を実行します。

1. C_FindObjects[Init|Final]を呼び出して、トークン上のすべての非公開鍵オブジェクトを検索します。検索テンプレートには、次の属性があります。

- CKA_TOKEN = true

- CKA_CLASS = CKO_PRIVATE_KEY

2. C_FindObjects[Init|Final]を呼び出して、トークン上のすべての証明書オブジェクトを検索します。検索テンプレートには、次の属性があります。

- CKA_TOKEN = true

- CKA_CLASS = CKO_CERTIFICATE

3. それぞれのCKA_ID属性を取得することで、各非公開鍵オブジェクトを対応する証明書と一致させます。一致するペアは、同じ一意のCKA_IDを共有する必要があります。
一致するペアごとに、発行者 ->サブジェクトのパスに従って証明書チェーンが作成されます。エンド・エンティティ証明書から、次の属性を持つ検索テンプレートを使用したC_FindObjects[Init|Final]が呼び出されます。

- CKA_TOKEN = true

- CKA_CLASS = CKO_CERTIFICATE

- CKA_SUBJECT = [DN of certificate issuer]

この検索は、発行者の証明書が見つからないか、自己署名証明書が見つかるまで継続します。複数の証明書が見つかった場合は、最初の証明書が使用されます。

非公開鍵と証明書が一致して証明書チェーンが作成されると、エンド・エンティティ証明書のCKA_LABELの値をKeyStoreの別名として、非公開鍵エントリに情報が保存されます。

エンド・エンティティ証明書にCKA_LABELがない場合は、別名はCKA_IDから作成されます。CKA_IDが出力可能文字からのみ構成される場合は、CKA_IDのバイトをUTF-8文字セットを使用してデコードして、Stringの別名を作成します。または、16進数のString別名を、CKA_IDバイトから作成します(例: 「0xFFFF」)。

複数の証明書が同一のCKA_LABELを共有する場合、別名はCKA_LABELに加え、エンド・エンティティ証明書の発行者およびシリアル番号(例: "MyCert/CN=foobar/1234")から作成されます。

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

5. 非公開鍵エントリまたは信頼できる証明書エントリの一部ではない非公開鍵または証明書オブジェクトは、すべて無視されます。

6. C_FindObjects[Init|Final]を呼び出して、トークン上のすべての秘密鍵オブジェクトを検索します。検索テンプレートには、次の属性があります。

- CKA_TOKEN = true

- CKA_CLASS = CKO_SECRET_KEY

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

書込みアクセス

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

1. KeyStoreエントリを作成するときには(KeyStore.setEntryなどの使用中)、CKA_TOKEN=trueとしてC_CreateObjectが呼び出され、エントリの内容それぞれに対してトークン・オブジェクトが作成されます。
非公開鍵オブジェクトは、CKA_PRIVATE=trueで保存されます。KeyStoreの別名(UTF-8エンコード)は、非公開鍵と対応するエンド・エンティティ証明書の両方でCKA_IDとして設定されます。KeyStoreの別名では、エンド・エンティティ証明書オブジェクトにCKA_LABELが設定されます。

非公開鍵エントリのチェーン内の各証明書も保存されます。CKA_LABELは、CA証明書には設定されません。CA証明書がトークン内にある場合、複製は保存されません。

秘密鍵オブジェクトは、CKA_PRIVATE=trueで保存されます。KeyStoreの別名は、CKA_LABELとして設定されます。

2. セッション・オブジェクトをトークン・オブジェクトに変換しようとする場合(KeyStore.setEntryが呼び出され、指定されたエントリの非公開鍵オブジェクトがセッション・オブジェクトである場合など)、C_CopyObjectがCKA_TOKEN=trueとして呼び出されます。

3. トークン内に複数の証明書が見つかり、同じCKA_LABELを共有する場合は、トークンへの書込み機能は無効になります。

4. PKCS#11仕様では一般のアプリケーションでCKA_TRUSTED=trueと設定することが許可されないため(トークン初期化アプリケーションのみが可能)、信頼できる証明書エントリを作成できません。

その他

前述の検索の他、SunPKCS11プロバイダのKeyStore実装で次の検索を使用して内部関数を実行できます。具体的には、次のどの属性テンプレートを使用しても、C_FindObjects[Init|Final]を呼び出すことができます。

- CKA_TOKEN    true
CKA_CLASS    CKO_CERTIFICATE
CKA_SUBJECT  [subject DN]

- CKA_TOKEN    true
CKA_CLASS    CKO_SECRET_KEY
CKA_LABEL    [label]

- CKA_TOKEN    true
CKA_CLASS    CKO_CERTIFICATE or CKO_PRIVATE_KEY
CKA_ID       [cka_id]

プロバイダの例

次に、Providerクラスの機能を示す単純なプロバイダの例を示します。

package com.foo;

import java.io.*;
import java.lang.reflect.*;
import java.security.*;
import javax.crypto.*;

/**
* Example provider that demonstrates some Provider class features.
*
*  . implement multiple different algorithms in a single class.
*    Previously each algorithm needed to be implemented in a separate class
*    (e.g. one for SHA-256, one for SHA-384, etc.)
*
*  . multiple concurrent instances of the provider frontend class each
*    associated with a different backend.
*
*  . it uses "unextractable" keys and lets the framework know which key
*    objects it can and cannot support
*
* Note that this is only a simple example provider designed to demonstrate
* several of the new features.  It is not explicitly designed for efficiency.
*/
public final class ExampleProvider extends Provider {

// reference to the crypto backend that implements all the algorithms
final CryptoBackend cryptoBackend;

public ExampleProvider(String name, CryptoBackend cryptoBackend) {
super(name, 1.0, "JCA/JCE provider for " + name);
this.cryptoBackend = cryptoBackend;
// register the algorithms we support (SHA-256, SHA-384, DESede, and AES)
putService(new MyService
(this, "MessageDigest", "SHA-256", "com.foo.ExampleProvider$MyMessageDigest"));
putService(new MyService
(this, "MessageDigest", "SHA-384", "com.foo.ExampleProvider$MyMessageDigest"));
putService(new MyCipherService
(this, "Cipher", "DES", "com.foo.ExampleProvider$MyCipher"));
putService(new MyCipherService
(this, "Cipher", "AES", "com.foo.ExampleProvider$MyCipher"));
}

// the API of our fictitious crypto backend
static abstract class CryptoBackend {
abstract byte[] digest(String algorithm, byte[] data);
abstract byte[] encrypt(String algorithm, KeyHandle key, byte[] data);
abstract byte[] decrypt(String algorithm, KeyHandle key, byte[] data);
abstract KeyHandle createKey(String algorithm, byte[] keyData);
}

// the shell of the representation the crypto backend uses for keys
private static final class KeyHandle {
// fill in code
}

// we have our own ServiceDescription implementation that overrides newInstance()
// that calls the (Provider, String) constructor instead of the no-args constructor
private static class MyService extends Service {

private static final Class[] paramTypes = {Provider.class, String.class};

MyService(Provider provider, String type, String algorithm,
String className) {
super(provider, type, algorithm, className, null, null);
}

public Object newInstance(Object param) throws NoSuchAlgorithmException {
try {
// get the Class object for the implementation class
Class clazz;
Provider provider = getProvider();
ClassLoader loader = provider.getClass().getClassLoader();
if (loader == null) {
clazz = Class.forName(getClassName());
} else {
clazz = loader.loadClass(getClassName());
}
// fetch the (Provider, String) constructor
Constructor cons = clazz.getConstructor(paramTypes);
// invoke constructor and return the SPI object
Object obj = cons.newInstance(new Object[] {provider, getAlgorithm()});
return obj;
} catch (Exception e) {
throw new NoSuchAlgorithmException("Could not instantiate service", e);
}
}
}

// custom ServiceDescription class for Cipher objects. See supportsParameter() below
private static class MyCipherService extends MyService {
MyCipherService(Provider provider, String type, String algorithm,
String className) {
super(provider, type, algorithm, className);
}
// we override supportsParameter() to let the framework know which
// keys we can support. We support instances of MySecretKey, if they
// are stored in our provider backend, plus SecretKeys with a RAW encoding.
public boolean supportsParameter(Object obj) {
if (obj instanceof SecretKey == false) {
return false;
}
SecretKey key = (SecretKey)obj;
if (key.getAlgorithm().equals(getAlgorithm()) == false) {
return false;
}
if (key instanceof MySecretKey) {
MySecretKey myKey = (MySecretKey)key;
return myKey.provider == getProvider();
} else {
return "RAW".equals(key.getFormat());
}
}
}

// our generic MessageDigest implementation. It implements all digest
// algorithms in a single class. We only implement the bare minimum
// of MessageDigestSpi methods
private static final class MyMessageDigest extends MessageDigestSpi {
private final ExampleProvider provider;
private final String algorithm;
private ByteArrayOutputStream buffer;
MyMessageDigest(Provider provider, String algorithm) {
super();
this.provider = (ExampleProvider)provider;
this.algorithm = algorithm;
engineReset();
}
protected void engineReset() {
buffer = new ByteArrayOutputStream();
}
protected void engineUpdate(byte b) {
buffer.write(b);
}
protected void engineUpdate(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
}
protected byte[] engineDigest() {
byte[] data = buffer.toByteArray();
byte[] digest = provider.cryptoBackend.digest(algorithm, data);
engineReset();
return digest;
}
}

// our generic Cipher implementation, only partially complete. It implements
// all cipher algorithms in a single class. We implement only as many of the
// CipherSpi methods as required to show how it could work
private static abstract class MyCipher extends CipherSpi {
private final ExampleProvider provider;
private final String algorithm;
private int opmode;
private MySecretKey myKey;
private ByteArrayOutputStream buffer;
MyCipher(Provider provider, String algorithm) {
super();
this.provider = (ExampleProvider)provider;
this.algorithm = algorithm;
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
this.opmode = opmode;
myKey = MySecretKey.getKey(provider, algorithm, key);
if (myKey == null) {
throw new InvalidKeyException();
}
buffer = new ByteArrayOutputStream();
}
protected byte[] engineUpdate(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
return new byte[0];
}
protected int engineUpdate(byte[] b, int ofs, int len, byte[] out, int outOfs) {
buffer.write(b, ofs, len);
return 0;
}
protected byte[] engineDoFinal(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
byte[] in = buffer.toByteArray();
byte[] out;
if (opmode == Cipher.ENCRYPT_MODE) {
out = provider.cryptoBackend.encrypt(algorithm, myKey.handle, in);
} else {
out = provider.cryptoBackend.decrypt(algorithm, myKey.handle, in);
}
buffer = new ByteArrayOutputStream();
return out;
}
// code for remaining CipherSpi methods goes here
}

// our SecretKey implementation. All our keys are stored in our crypto
// backend, we only have an opaque handle available. There is no
// encoded form of these keys.
private static final class MySecretKey implements SecretKey {

final String algorithm;
final Provider provider;
final KeyHandle handle;

MySecretKey(Provider provider, String algorithm, KeyHandle handle) {
super();
this.provider = provider;
this.algorithm = algorithm;
this.handle = handle;
}
public String getAlgorithm() {
return algorithm;
}
public String getFormat() {
return null; // this key has no encoded form
}
public byte[] getEncoded() {
return null; // this key has no encoded form
}
// Convert the given key to a key of the specified provider, if possible
static MySecretKey getKey(ExampleProvider provider, String algorithm, Key key) {
if (key instanceof SecretKey == false) {
return null;
}
// algorithm name must match
if (!key.getAlgorithm().equals(algorithm)) {
return null;
}
// if key is already an instance of MySecretKey and is stored
// on this provider, return it right away
if (key instanceof MySecretKey) {
MySecretKey myKey = (MySecretKey)key;
if (myKey.provider == provider) {
return myKey;
}
}
// otherwise, if the input key has a RAW encoding, convert it
if (!"RAW".equals(key.getFormat())) {
return null;
}
byte[] encoded = key.getEncoded();
KeyHandle handle = provider.cryptoBackend.createKey(algorithm, encoded);
return new MySecretKey(provider, algorithm, handle);
}
}
}

6 JAAS (Java Authentication and Authorization Service)

JDK 8ドキュメント内のJAASリファレンス・ガイドでは、Java Authentication and Authorization Service (JAAS)について説明します。JAASでは、ユーザーを認証して、Javaコードを現在実行しているユーザーをセキュアに特定することや、実行される操作に必要なアクセス制御権限、すなわちアクセス権を必ず保持するようにユーザーを認可できます。

JDK 8ドキュメント内のJAASチュートリアルでは、Java Authentication and Authorization Service (JAAS)の認証と承認についてチュートリアルを提供します。

Java Authentication and Authorization Service (JAAS): LoginModule開発者ガイドでは、特定のタイプの認証を提供するためにアプリケーションにプラグインする、LoginModuleインタフェースの実装方法を示します。

JAASリファレンス・ガイド

Java Authentication and Authorization Service (JAAS)の詳細は、JDK 8ドキュメント内のJAASリファレンス・ガイドを参照してください。JAASでは、次のことが可能になります。

- ユーザーを認証し、Javaコードがアプリケーション、アプレット、Beanまたはサーブレットとして実行されているかどうかにかかわらず、現在そのコードを実行しているユーザーをセキュアな方法で判別します。

- 実行される操作に必要なアクセス制御権限(アクセス権)を必ず保持するように、ユーザーに権限を与えます。

JAASチュートリアル

Java Authentication and Authorization Service (JAAS)の認証と承認のチュートリアルは、JDK 8ドキュメント内のJAASチュートリアルを参照してください。

Java Authentication and Authorization Service (JAAS): LoginModule開発者ガイド

当初、Java認証・承認サービス(Java Authentication and Authorization Service: JAAS)は、Java 2 SDK, Standard Edition (J2SDK), v 1.3のオプション・パッケージでした。JAASは、J2SDK 1.4よりJava Standard Edition Development Kitに統合されています。

JAASは、認証されたアイデンティティにおけるサブジェクトベースの認証を提供します。このドキュメントでは、JAASの認証面、特にInterface LoginModuleに焦点を当てて解説します。

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

このドキュメントは、認証技術を実装するInterface LoginModuleを記述する必要がある、経験を積んだプログラマ向けに書かれています。

関連ドキュメント

このドキュメントでは、読者がすでに次のドキュメントを読んでいることを前提としています。

- Java認証・承認サービス(JAAS)リファレンス・ガイド

JAAS APIのさまざまなクラスおよびインタフェースについての説明も含まれます。詳細は、JAAS API仕様のJavadoc APIドキュメントを参照してください。

- javax.security.auth

- com.sun.security.auth.callback

- javax.security.auth.kerberos

- com.sun.security.auth.login

- javax.security.auth.spi

- javax.security.auth.x500

- com.sun.security.auth

- com.sun.security.auth.callback

- com.sun.security.auth.login

- com.sun.security.auth.module

次のチュートリアルは、JAAS認証および承認を利用するすべてのユーザーを対象としています。

- JAAS認証チュートリアル

- JAAS承認チュートリアル

次のチュートリアルは、JAAS認証および承認のチュートリアルと似ていますが、Kerberos LoginModuleの使用方法の解説が含まれるため、使用する前にKerberosをインストールする必要があります。

- JAAS認証

- JAAS承認

この2つのチュートリアルは、認証とセキュアな通信のための基盤技術としてKerberosを利用するJAASおよびJava GSS-APIチュートリアルに含まれています。

LoginModuleの概要

LoginModuleは特定タイプの認証を提供するアプリケーションへプラグインされます。

Interface LoginModuleドキュメントでは、認証技術のプロバイダが実装する必要のあるインタフェースについて説明します。

アプリケーションはLoginContextアプリケーション・プログラミング・インタフェース(API)への書込みを実行するのに対し、認証技術のプロバイダはLoginModuleインタフェースを実装します。Configurationでは、特定のログイン・アプリケーションで使用されるLoginModuleを指定します。アプリケーション自体を変更せずに、複数の異なるLoginModuleをアプリケーションのプラグインとして使用できます。

LoginContextは、Configurationの読取りと特定のLoginModuleのインスタンス化を行います。各LoginModuleは、Subject、Interface CallbackHandler、共有のLoginModuleの状態、およびLoginModule固有のオプションを使用してインスタンス化されます。

Subjectは、認証中のユーザーまたはサービスを表し、認証が成功すると、関連するInterface Principalおよびクレデンシャルを保持するLoginModuleにより更新されます。LoginModuleは、loginメソッドの説明で述べるように、CallbackHandlerを使用してユーザーと通信します(ユーザー名とパスワードの入力を求めるためなど)。CallbackHandlerはnullも指定できることに注意してください。Subjectの認証にCallbackHandlerを必要とするLoginModuleは、nullのCallbackHandlerで初期化されていると、LoginExceptionをスローする場合があります。LoginModuleはオプションで共有状態を使用して、それら自体の間で情報やデータを共有できます。

LoginModule固有のオプションは、ログインConfiguration内で、このLoginModule用に構成されたオプションを表します。これらのオプションはLoginModule自体によって定義され、その中での動作を制御します。たとえば、LoginModuleでデバッグ/テスト機能をサポートするオプションを定義する場合を考えましょう。オプションは、debug=trueなどの、キーと値の構文を使用して定義されます。キーを使用して値を取得できるように、LoginModuleはオプションをMapとして格納します。LoginModuleが定義することを選択するオプションの数に制限はありません。

呼出し元アプリケーションは、認証プロセスをLoginContextのloginメソッド呼出しによって呼び出された単一の操作であると見なします。ただし、各LoginModule内部の認証プロセスは、明確な2つのフェーズにわかれています。認証の最初のフェーズでは、LoginContextのloginメソッドにより、Configuration内に指定された各LoginModuleのloginメソッドが呼び出されます。LoginModuleのloginメソッドは、実際の認証を実行してから(たとえば、パスワードの入力を促し、入力されたパスワードを検証する)、認証ステータスを非公開の状態情報として保存します。LoginModuleのloginメソッドは終了後に、true (成功した場合)またはfalse (無視する場合)を返すか、LoginExceptionをスローして障害を指定します。障害が発生した場合、LoginModuleは認証を再試行したり、遅延を招いたりしてはいけません。これらのタスクの実行は、アプリケーションが担当します。アプリケーションが認証の再実行を試みると、各LoginModuleのloginが再度呼び出されます。

次のフェーズでは、LoginContextの認証がすべて成功した(関連するrequired、requisite、sufficientおよびoptional LoginModuleのloginメソッドの呼出しに成功した)場合、各LoginModuleのcommitメソッドが呼び出されます。LoginModuleフラグ(required、requisite、sufficientおよびoptional)については、Configurationのドキュメント、およびJAASリファレンス・ガイドの付録B: ログイン構成の例を参照してください。LoginModuleのcommitメソッドは、非公開で保存された状態をチェックして、独自の認証が成功したかどうかを確認します。LoginContextの認証全体が成功し、LoginModule自体の認証が成功すると、commitメソッドにより、関連するPrincipal (認証済みの識別情報)およびクレデンシャル(暗号化鍵などの認証データ)がSubjectに関連付けられます。

LoginContextの認証全体が失敗した(関連するREQUIRED、REQUISITE、SUFFICIENTおよびOPTIONAL LoginModuleのloginメソッドが成功しなかった)場合、各LoginModuleのabortメソッドが呼び出されます。この場合、LoginModuleは、当初保存されていた認証状態をすべて削除/破棄します。

Subjectからのログアウトには、1つのフェーズのみが含まれます。LoginContextは、LoginModuleのlogoutメソッドを呼び出します。その後、LoginModuleのlogoutメソッドは、SubjectからのPrincipalまたはクレデンシャルの削除や、セッション情報のロギングなど、ログアウト処理を実行します。

LoginModuleの実装手順

次は、LoginModuleの実装およびテストに必要なステップです。

ステップ1: 認証技術の理解

最初に、新規LoginModuleプロバイダが実装する認証技術について理解した上で、要件を決定します。

1. LoginModuleがユーザーとの通信(ユーザー名とパスワードの取得など)を必要とするかどうかを決定します。必要な場合は、Interface CallbackHandlerとjavax.security.auth.callbackに関する知識が必要です。

このパッケージには、使用できるCallback実装がいくつか含まれています。独自のCallback実装を作成することもできます。LoginModuleは、アプリケーション自体によって指定され、LoginModuleのinitializeメソッドに渡されたCallbackHandlerを呼び出します。LoginModuleは、適切なCallbackからなる配列をCallbackHandlerに渡します。ステップ3のloginメソッドを参照してください。

注意:
LoginModule実装がエンド・ユーザーと通信しない設定にすることもできます。その場合には、LoginModuleからcallbackパッケージにアクセスする必要はありません。

2. ユーザーに使用できるようにする構成オプションを決定します。ユーザーは、現在のConfiguration実装で期待する形式(たとえばファイル)で構成情報を指定します。各オプションに対して、オプション名と使用可能な値を決定します。
たとえば、LoginModuleを構成して、特定の認証サーバー・ホストへの問合わせを行う場合、オプションのキー名(「auth_server」など)およびこのオプションに指定可能なサーバー・ホスト名(例、「server_one.example.com」や「server_two.example.com」など)を決定します。

ステップ2: LoginModule実装への命名

LoginModuleの適切なパッケージおよびクラス名を決定します。

たとえば、IBMにより開発されたLoginModuleにはcom.ibm.auth.Moduleと命名できます。ここで、com.ibm.authはパッケージ名、ModuleはLoginModuleクラス実装の名前です。

ステップ3: 抽象LoginModuleメソッドの実装

LoginModuleインタフェースは、実装を必要とする5つのabstractメソッドを指定します。

LoginModule.initializeメソッド

public void initialize (
Subject subject,
CallbackHandler handler,
Map<java.lang.String, ?> sharedState,
Map<java.lang.String, ?> options) { ... }

initializeメソッドが呼び出され、関連する認証および状態情報でLoginModuleの初期化が行われます。

このメソッドは、LoginModuleをインスタンス化したあと(かつほかのpublicメソッドを呼び出す前)、すぐにLoginContextにより呼び出されます。このメソッド実装は、将来の使用に備えて指定された引数を格納します。

initializeメソッドはさらに、指定されたsharedStateを調べて、他のLoginModuleから提供された追加の認証状態を特定し、指定されたoptionsも詳しく調べて、LoginModuleの動作に影響を与えるように指定された構成オプションを特定します。将来の使用に備えて、変数内にオプションの値を保存することもできます。

注意: JAAS LoginModuleは、PAM (use_first_pass、try_first_pass、use_mapped_passおよびtry_mapped_pass)に定義されたオプションを使用してシングル・サインオンします。詳細については、「Making Login Services Independent of Authentication Technologies」を参照してください。

次は、ログイン・モジュールがサポートする一般的なオプションの一覧です。ただし、これはガイドラインにすぎません。モジュールは、次のオプションのサブセットを随意サポートします。

- try_first_pass - trueの場合、スタック内の最初のログイン・モジュールが入力されたパスワードを保存し、それ以降のログイン・モジュールはこれを使用しようとする。認証に失敗した場合、ログイン・モジュールは新しいパスワードを要求し、認証を再試行する。

- use_first_pass - trueの場合、スタック内の最初のログイン・モジュールが入力されたパスワードを保存し、それ以降のログイン・モジュールはこれを使用しようとする。ログイン・モジュールは、認証に失敗しても新しいパスワードを要求しない。

- try_mapped_pass - trueの場合、スタック内の最初のログイン・モジュールが入力されたパスワードを保存し、それ以降のログイン・モジュールはこれをサービス固有のパスワードにマッピングしようとする。認証に失敗した場合、ログイン・モジュールは新しいパスワードを要求し、認証を再試行する。

- use_mapped_pass - trueの場合、スタック内の最初のログイン・モジュールが入力されたパスワードを保存し、それ以降のログイン・モジュールはこれをサービス固有のパスワードにマッピングしようとする。ログイン・モジュールは、認証に失敗しても新しいパスワードを要求しない。

- moduleBanner - trueの場合、ログイン・モジュールは、CallBackHandlerの呼出し時に最初のCallbackとしてTextOutputCallbackを提供する。このCallbackには、認証を実行しているログイン・モジュールに関する情報が記述されている。

- debug - trueの場合、ログイン・モジュールに対してデバッグ情報の出力を指示する。

initializeメソッドは、認識できない状態やオプションを無視できます。ただし、この種のイベントが発生した場合、それを記録する方が望ましい場合もあります。

このLoginModule (およびその他の構成済LoginModule)を呼び出すLoginContextはすべて、指定されたSubjectおよびsharedStateへの同一の参照を共有します。SubjectおよびsharedStateへの変更は、すべてが認識します。

LoginModule.loginメソッド

boolean login() throws LoginException;

loginメソッドが呼び出され、Subjectの認証が行われます。これが認証の第1フェーズです。

このメソッド実装は、実際の認証を実行します。たとえば、ユーザー名およびパスワードの入力を求めてから、パスワード・データベースに対しパスワードの検証を試みます。別のサンプル実装として、フィンガプリント・リーダーに指を挿入するようユーザーに指示し、入力されたフィンガ・プリントをフィンガプリント・データベースと照合する場合が考えられます。

LoginModuleがなんらかのユーザーとの通信の形式を必要とする(ユーザー名とパスワードの取得など)場合は、それを直接実行しないでください。それは、ユーザーとの様々な通信方法が存在するためであり、LoginModuleが様々なユーザーとの通信のタイプから独立させておくことが望ましいです。それよりも、LoginModuleのloginメソッドから、initializeメソッドに渡されたInterface CallbackHandlerのhandleメソッドを呼び出して、ユーザーと通信し、適切な結果(ユーザー名、パスワードなど)を設定するようにしてください。LoginModuleはCallbackHandlerに適切なCallback (ユーザー名に対してはNameCallback、パスワードに対してはPasswordCallback)からなる配列を渡し、CallbackHandlerは要求に従ってユーザーと通信し、Callback内に適切な値を設定します。たとえば、NameCallbackを処理する場合、CallbackHandlerはユーザーから名前を取得し、NameCallbackのsetNameメソッドを呼び出してその名前を格納します。

認証プロセスに、ネットワーク経由の通信が含まれる場合もあります。たとえば、このメソッド実装がKerberosのkinitと等価な機能を実行する場合、KDCとのコンタクトが必要になります。パスワード・データベースのエントリがリモート・ネーム・サービス内に存在する場合、Java Naming and Directory Interface (JNDI)を利用するなどして、そのネーム・サービスとコンタクトを取る必要があります。実装は、基盤となるオペレーティング・システムと通信する必要もあります。たとえば、ユーザーがSolaris、Linux、macOS、Windows NTなどのオペレーティング・システムにすでにログインしている場合、このメソッドは基盤となるオペレーティング・システムの識別情報を単にインポートします。

loginメソッドは、次の処理を行う必要があります。

1. このLoginModuleを無視するかどうかを決定します。たとえば、ユーザーがこのLoginModuleと無関係な識別情報を使って認証を試みた場合(たとえば、ユーザーがNISを使い、rootで認証を試みた場合)は、LoginModuleを無視する必要があります。このLoginModuleを無視する必要がある場合、loginの戻り値はfalseになります。それ以外の場合、次の処理を行います。

2. ユーザーとの通信が必要な場合はCallbackHandlerのhandleメソッドを呼び出します。

3. 認証を実行します。

4. 認証結果(成功または失敗)を格納します。

5. 認証に成功した場合は、関連状態情報をすべて保存します。この情報は、commitメソッドで使用される可能性があります。

6. 認証に成功した場合はtrueを返し、認証に失敗した場合はLoginException (FailedLoginExceptionなど)をスローします。

loginメソッド実装が、新規Principalまたはクレデンシャル情報を保存済みのSubjectオブジェクトに関連付けることはできません。このメソッドは、認証のみを実行して、認証結果および対応する認証状態を格納します。あとで、commitまたはabortメソッドからこの結果および状態へのアクセスが行われます。結果および状態は、ほかのLoginModuleと共有ではないので、通常、sharedState Mapには保存できません。

たとえば、LoginModuleがパスワードを共有するように構成されている場合であれば、このメソッドにとって、sharedStateのMapに状態情報を保存することは有利です。この場合、入力されたパスワードは、共有状態として保存されます。パスワードの共有により、パスワードを1回入力するだけで後続のLoginModuleでも認証された状態を維持できます。次に、sharedStateのMapから名前とパスワードを格納および保存する際の標準的な規約を示します。

- javax.security.auth.login.name - 名前を保存/取得するための共有状態マップ・キーとして使用。

- javax.security.auth.login.password - パスワードを保存/取得するための共有状態マップ・キーとして使用。

認証に失敗した場合、loginメソッドは認証を再試行できません。この処理はアプリケーションが担当します。LoginModule.login()内から何度もログインを試みるよりも、アプリケーションから繰り返しLoginContextのloginメソッドを呼び出すことをお薦めします。

LoginModule.commitメソッド

boolean commit() throws LoginException;

commitメソッドが呼び出され、認証プロセスがコミットされます。これは、第1認証フェーズが成功した場合の第2認証フェーズです。LoginContext全体の認証が成功した場合(関連するREQUIRED、REQUISITE、SUFFICIENT、およびOPTIONALのLoginModuleが成功した場合)に呼び出されます。

このメソッドは、loginメソッドにより保存された認証結果および対応する認証状態にアクセスします。

認証結果からloginメソッドの失敗が明らかになった場合、commitメソッドは最初に保存された対応する状態をすべて削除/破棄します。

保存された結果からLoginModuleのloginメソッドの成功が明らかになった場合は、対応する状態情報から関連するPrincipalおよびクレデンシャルの情報が構築されます。このPrincipalおよびクレデンシャルの情報が、initializeメソッドによって格納されたSubjectに追加されます。

Principalおよびクレデンシャルの追加後は、不要な状態フィールドを迅速に破棄する必要があります。たとえば、認証プロセスで格納されたユーザー名やパスワードは破棄されます。

commitメソッドは、コミットの成功または失敗を示す非公開の状態を保存する必要があります。

次に、LoginModuleのcommitメソッドから返される結果を図示します。各ボックスは、発生する可能性がある各種の状況を表します。たとえば、上部左側のボックスは、以前のloginメソッド呼出しとcommitメソッド自体の呼出しに成功した場合に返されるcommitメソッドの結果を示しています。

表6-1 LoginModule.commitメソッドの戻り値

ログイン・ステータス

COMMIT: 成功

COMMIT: 失敗

LOGIN: 成功

TRUEを返す

例外をスローする

LOGIN: 失敗

FALSEを返す

FALSEを返す

LoginModule.abortメソッド

boolean abort() throws LoginException;

abortメソッドが呼び出され、認証プロセスが強制的に中断されます。これは、第1認証フェーズが失敗した場合の第2認証フェーズです。これは、LoginContextの全体の認証に失敗した場合に呼び出されます。

このメソッドは、最初に、loginメソッド(および場合によりcommitメソッド)によって保存されているLoginModuleの認証結果および対応する認証状態にアクセスし、情報を消去および破棄します。たとえば、ユーザー名およびパスワードは破棄されます。

LoginModuleの認証に失敗した場合、非公開の状態を消去する必要は生じません。

次に、LoginModuleのabortメソッドから返される結果を図示します。最初の図は、以前のlogin呼出しが成功したことを想定しています。たとえば、上部左側のボックスは、以前のloginメソッド呼出しとcommitメソッド呼出しに成功し、さらにabortメソッド自体の呼出しにも成功した場合に返されるabortメソッドの結果を示しています。

表6-2 ログイン成功時のLoginModule.abortメソッドの戻り値

ログイン・ステータス

ABORT: 成功

ABORT: 失敗

COMMIT: 成功

TRUEを返す

例外をスローする

COMMIT: 失敗

TRUEを返す

例外をスローする

LoginModule.logoutメソッド

boolean logout() throws LoginException;

logoutメソッドが呼び出され、Subjectからログアウトします。

このメソッドは、Principalを削除し、commit操作中にSubjectに関連付けられたクレデンシャルを削除/破棄します。このメソッドは、Subject内の既存のPrincipalやクレデンシャル、またはほかのLoginModuleによって追加されたPrincipalやクレデンシャルに対しては、一切の操作を実行できません。

Subjectが読取り専用 (SubjectのisReadOnlyメソッドがtrueを返す)の場合、このメソッドはcommit操作中にSubjectに関連付けられているクレデンシャルのみを破棄します(クレデンシャルを削除することはできません)。Subjectが読取り専用であり、commit操作中にSubjectと関連付けられたクレデンシャルを破棄できない(このクレデンシャルがDestroyableインタフェースを実装していない)場合、このメソッドはLoginExceptionをスローする可能性があります。

logoutメソッドは、ログアウトに成功した場合はtrueを返し、それ以外の場合はLoginExceptionをスローします。

ステップ4: サンプル・アプリケーションの選択または記述

テスト用として、既存のサンプル・アプリケーションを選択するか、新しいサンプル・アプリケーションを作成します。

アプリケーション要件、およびテストに使用できるサンプル・アプリケーションについては、Java Authentication and Authorization Service (JAAS)リファレンス・ガイドを参照してください。

ステップ5: LoginModuleおよびアプリケーションのコンパイル

テストに使用する新しいLoginModuleおよびアプリケーションをコンパイルします。

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

LoginModuleのテストを準備します。

ステップ6a: LoginModuleとアプリケーション・コードをJARファイルに配置

ステップ6c: LoginModuleおよびアプリケーションのJARファイルのアクセス権の設定でポリシー内のJARファイルを参照できるように、LoginModuleおよびアプリケーション・コードを別々のJARファイルに格納します。次は、JARファイルを作成するサンプル・コマンドです。

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

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

jarツールの詳細は、jarを参照してください。

ステップ6b: JARファイルの格納場所を決定

本来、アプリケーションはユーザーの好みの場所に格納できます。

LoginModuleも、ユーザーまたはその他のクライアントの好みの場所に格納できます。完全に信頼できるLoginModuleは、JREのlib/ext (標準拡張)ディレクトリに格納できます。

lib/extディレクトリ内にあるLoginModuleとその他のディレクトリにあるLoginModuleを両方ともテストする必要があります。これは、LoginModuleにセキュリティ関連操作の実行に必要なJava Development Kit (JDK)でのアクセス権を明示的に付与する必要がある場合と、そうでない場合があるためです。

JREのlib/extディレクトリ内のLoginModuleは、インストール済拡張機能と見なされるので、これにアクセス権を付与する必要はありません。インストール済拡張機能には、デフォルトのシステム・ポリシー・ファイル構文により、すべてのアクセス権が付与されます。

一方、それ以外のディレクトリ内のLoginModuleには、ポリシー・ファイル内のgrant文を使うなどしてアクセス権を付与する必要があります。

LoginModule JARファイルがインストール済み拡張機能と見なされない場合は、このファイルの格納場所を指定してテストします。次のステップでは、指定された場所にあるJARファイルにアクセス権を付与します。

ステップ6c: LoginModuleとアプリケーションのJARファイルにアクセス権を設定

LoginModuleやアプリケーションが、セキュリティ・チェックをトリガーするセキュリティ関連タスク(ネットワーク接続の確立、ローカル・ディスク上のファイルの読取り、書込みなど)を実行するとします。これらがインストール済拡張機能(ステップ6b: JARファイルの格納場所を決定を参照)ではなく、セキュリティ・マネージャがインストールされている環境で実行される場合は、Java Development Kit (JDK)でのアクセス権を付与する必要があります。

通常、LoginModuleはPrincipalとクレデンシャルを認証済サブジェクトに関連付けます。このため、LoginModuleは、アクセス権として、modifyPrincipals、modifyPublicCredentialsおよびmodifyPrivateCredentialsというターゲット名のAuthPermissionを必要とします。

次のコード例MyLM.jarには、LoginModuleにアクセス権を付与する文が含まれています。この種の文は、ポリシー・ファイルに記述されます。この例では、MyLM.jarファイルは/localWorkディレクトリに格納されるものとします。

grant codeBase "file:/localWork/MyLM.jar" {
permission javax.security.auth.AuthPermission "modifyPrincipals";
permission javax.security.auth.AuthPermission "modifyPublicCredentials";
permission javax.security.auth.AuthPermission "modifyPrivateCredentials";
};

注意:
LoginModule呼出しは、常にAccessController.doPrivileged呼出し内で行われるため、doPrivileged自体を呼び出す必要はありません。これを呼び出すと、意図せずにセキュリティ・ホールを作り出してしまう可能性があります。たとえば、LoginModuleがdoPrivileged呼出しの中でアプリケーション提供のCallbackHandlerを呼び出す場合、他の場合にはアクセス不可能なリソースへのアクセスがアプリケーションのCallbackHandlerに許可されるため、セキュリティ・ホールが生まれます。

ステップ6d: ログイン・モジュールを参照する構成を作成

JAASはプラガブルな認証アーキテクチャをサポートするため、既存のアプリケーションを変更せずに新しいLoginModuleを使用できます。新しいLoginModuleの使用を示すためには、ログインConfigurationを更新するだけで済みます。

OracleのデフォルトのConfiguration実装は、ConfigFileで説明するように、構成ファイルから構成情報を読み取ります。

テスト用の構成ファイルを作成します。たとえば、以前取り上げた架空のIBMのアプリケーション用LoginModuleを構成する場合、次のような内容の構成ファイルを使用することになります。

AppName {
com.ibm.auth.Module REQUIRED debug=true;
};

AppNameは、アプリケーションがログイン構成ファイル内のこのエントリを参照するために使用する任意の名前です。アプリケーションはこの名前をLoginContextコンストラクタの第1引数として指定します。

ステップ7: LoginModuleの試用

アプリケーション、およびそれによるLoginModuleの使用をテストします。アプリケーションの実行時、使用するログイン構成ファイルを指定します。たとえば、MyApp.jarにMyAppという名前のアプリケーションが格納されていて、構成ファイルの名前がtest.confだとします。

この場合、次のようにしてアプリケーションを実行し、構成ファイルを指定できます。

java -classpath MyApp.jar
-Djava.security.auth.login.config=test.conf MyApp

コマンド全体は、1行で入力してください。ここでは、読みやすくするために複数行に分けて表示してあります。

my.policyという名前のポリシー・ファイルを指定し、インストールされているセキュリティ・マネージャを使ってアプリケーションを実行するには、次のように入力します。

java -classpath MyApp.jar -Djava.security.manager
-Djava.security.policy=my.policy
-Djava.security.auth.login.config=test.conf MyApp

ここでも、コマンド全体は1行で入力してください。

debugオプションを付けてLoginModuleを構成すると、適正に動作することを確認できます。

コードをデバッグし、必要に応じてテストを続行します。問題が発生する場合は、上記のすべての手順が完了していることを確認してください。

ユーザーによる入力と、構成ファイルに指定したLoginModuleオプションを確実に変更してください。

異なったインストール・オプション(LoginModuleをインストール済み拡張機能にする、クラス・パス内に配置する、など)および実行環境(セキュリティ・マネージャを実行する、または実行しない)を使用するテストも実行してください。インストール・オプションの詳細は、ステップ6b: JARファイルの格納場所を決定を参照してください。特に、セキュリティ・マネージャがインストールされていてLoginModuleとアプリケーションがインストール済拡張機能でない場合は、LoginModuleを確実に動作させるため、ステップ6c: LoginModuleおよびアプリケーションのJARファイルのアクセス権の設定で説明されているように、必要なアクセス権を付与した後、インストールおよび実行環境をテストする必要があります。

1. テスト中に、LoginModuleまたはアプリケーションに変更が必要なことが判明した場合は、変更を行い、再コンパイルします(ステップ5: LoginModuleおよびアプリケーションのコンパイル)。

2. 更新したコードをJARファイルに配置します(ステップ6: JARファイルへのLoginModuleおよびアプリケーション・コードの記述)。

3. JARファイルを再インストールします(ステップ6b: JARファイルの格納場所を決定)。

4. 必要な場合は、アクセス権を修正するか追加します(ステップ6c: LoginModuleおよびアプリケーションのJARファイルのアクセス権の設定)。

5. 必要な場合は、ログイン構成ファイルを変更します(ステップ6d: LoginModuleを参照する構成を作成)。

6. アプリケーションを再実行し、必要に応じてこれらのステップを繰り返します。

ステップ8: LoginModule実装のドキュメント化

LoginModuleのクライアント用ドキュメントを記述します。

たとえば、次のようなドキュメントを記述できます。

- READMEまたはユーザー・ガイド

1. LoginModule実装で使用する認証プロセス。

2. LoginModuleのインストール方法。

3. LoginModuleで有効な構成オプション。各オプションには、そのオプションの制御内容、オプション名、戻り値(または値の型)を指定。

4. LoginModuleがインストール済拡張機能ではなく、セキュリティ・マネージャとともに実行される場合に必要なアクセス権。

- 新規LoginModuleを参照するサンプルConfigurationファイル。

- LoginModuleに必要なアクセス権を付与するサンプル・ポリシー・ファイル。

- APIのドキュメント。ソース・コードを記述する際、javadocコメントをソース・コード内に挿入すると、API javadocの生成が容易になる。

ステップ9: LoginModule JARファイルおよびドキュメントの有効化

LoginModule JARファイルおよびドキュメントをクライアントから使用できるようにします。

7 Java Generic Security Services (Java GSS-API)

Java Generic Security Services (Java GSS-API)は、通信アプリケーション間のセキュアなメッセージ交換に使用されます。

Kerberosを使ったJava GSS-APIおよびJAASのチュートリアル

Java Authentication and Authorization Service (JAAS)およびJava Generic Security Services Application Program Interface (GSS-API)の様々な側面を示す一連のチュートリアルは、JDK 8ドキュメント内のKerberosを使ったJava GSS-APIおよびJAASのチュートリアルを参照してください。

Kerberosを使ったJavaのシングル・サインオン

Kerberos V5プロトコルに基づくシングル・サインオンの使用方法の詳細は、JDK 8ドキュメント内のKerberosを使ったJavaのシングル・サインオンを参照してください。

Java GSSの高度なセキュリティ・プログラミング

Java GSS-APIの使用によるユーザー認証アプリケーションの構築、他のアプリケーションやサービスとのセキュアな通信、およびシングル・サインオンを実現するためのKerberos環境でのアプリケーション構成の方法を示す演習は、JDK 8ドキュメント内のJava GSSの高度なセキュリティ・プログラミングを参照してください。さらに、これらの演習では、Kerberos環境でより強力な暗号化アルゴリズムを使用する方法、およびSPNEGOなどのJava GSSメカニズムを使用してアソシエーションをセキュリティ保護する方法も示します。

Kerberos 5 GSS-APIメカニズム

Kerberos 5用Java Generic Security Services (Java GSS)の詳細は、JDK 8ドキュメント内のKerberos 5 GSS-APIメカニズムを参照してください。

8 Java Secure Socket Extension (JSSE)リファレンス・ガイド

Java Secure Socket Extension (JSSE)により、セキュアなインターネット通信が可能になります。これは、JavaバージョンのSSL、TLSおよびDTLSプロトコルのフレームワークおよび実装を提供し、データ暗号化、サーバー認証、メッセージの整合性の他、オプションでクライアント認証の機能を含んでいます。

JSSEの概要

ネットワークを通じてやり取りされるデータには、意図された受信者以外の人も、簡単にアクセスできます。データにパスワードやクレジット・カード番号などの個人情報が含まれる場合、権限のない者がデータを理解できないよう、手段を講じる必要があります。また、意図的であるかどうかにかかわらず、通信中にデータが変更されていないことを確認することも重要です。Secure Sockets Layer (SSL)およびTransport Layer Security (TLS)は、ネットワークを通じたデータの送信時に、データの機密性および整合性を保護するために設計されたプロトコルです。

Java Secure Socket Extension (JSSE)により、セキュアなインターネット通信が可能になります。これにより、JavaバージョンのSSLおよびTLSプロトコルのフレームワークおよび実装が提供されます。また、データ暗号化、サーバー認証、メッセージの整合性の他、オプションでクライアント認証の機能が含まれます。JSSEを使用すると、開発者はHTTP、Telnet、FTPなど、TCP/IP上のアプリケーション・プロトコルを実行するクライアントとサーバーの間で、セキュアなデータのやり取りを実現できます。SSLの概要については、「Secure Sockets Layer (SSL)プロトコルの概要」を参照してください。

JSSEは、基盤となる複雑なセキュリティ・アルゴリズムやハンドシェーク・メカニズムを抽象化することにより、識別するのが難しく、しかし危険なセキュリティ上の弱点が生まれるリスクを最小限に抑えます。また、開発者がそれをアプリケーションに直接統合できる構築ブロックとして使用すると、アプリケーション開発が簡単になります。

JSSEは、アプリケーション・プログラミング・インタフェース(API)フレームワークと、そのAPIの実装を提供します。JSSE APIは、java.securityおよびjava.netパッケージによって定義されたコア・ネットワークおよび暗号化サービスを補い、拡張されたネットワーク・ソケット・クラス、トラスト・マネージャ、キー・マネージャ、SSLコンテキストおよびソケット作成動作をカプセル化するソケット・ファクトリのフレームワークを提供します。SSLSocketクラスはブロック入出力モデルに基づいているため、Java Development Kit (JDK)には、実装で独自の入出力メソッドを選択できるようにするために非ブロックSSLEngine クラスが含まれます。

JSSE APIでは、次のセキュリティ・プロトコルがサポートされています。

- SSL: バージョン3.0

- TLS: バージョン1.0、1.1および1.2

- DTLS: バージョン1.0および1.2

これらのセキュリティ・プロトコルは、通常の双方向のストリーム・ソケットをカプセル化し、JSSE APIは認証、暗号化および整合性保護の透過的なサポートを追加します。JDKに付属のJSSE実装ではSSL 2.0は実装されないことに注意してください。

JSSEはJava SEプラットフォームのセキュリティ・コンポーネントであり、Java暗号化アーキテクチャ(JCA)リファレンス・ガイドのフレームワーク内の至るところで見られる同じ設計方針に基づいています。暗号化に関するセキュリティ・コンポーネントのこのフレームワークにより、実装の独立性と、可能な場合にはアルゴリズムの独立性を実現できます。JSSEはJCAフレームワークによって定義された暗号化サービス・プロバイダを使用します。

Java SEプラットフォーム内の他のセキュリティ・コンポーネントには、Java Authentication and Authorization Service (JAAS)リファレンス・ガイドおよびJavaセキュリティ・ツールがあります。JSSEはJCAと同じ概念およびアルゴリズムを多く含んでいますが、単純なストリーム・ソケットAPIの下でこれらを自動的に適用します。

JSSE APIは、その他のSSL/TLS/DTLSプロトコルと公開鍵インフラストラクチャ(PKI)実装をシームレスにプラグインできる設計になっています。開発者が、リモート・ホストの信頼性やリモート・ホストに送信する認証鍵データを決定するロジックを提供することもできます。

JSSEの特長と利点

JSSEには、次のような重要な利点と特長があります。

- JDKの標準のコンポーネントとして含まれている

- 拡張可能なプロバイダ・ベースのアーキテクチャ

- 100% Pure Javaに実装される

- SSL/TLS/DTLSのAPIサポートを提供する

- SSL 3.0、TLS (バージョン1.0、1.1および1.2)およびDTLS (バージョン1.0および1.2)の実装を提供する

- セキュアなチャネルを作成するためにインスタンス化可能なクラスを含む(SSLSocket、SSLServerSocket、およびSSLEngine)

- セキュアな通信を開始または検証するのに使用されるSSL/TLS/DTLSハンドシェーク機能の一部として、暗号群ネゴシエーションをサポートする

- 通常のSSL/TLS/DTLSハンドシェーク機能の一部として、クライアントとサーバーの認証をサポートする

- SSL/TLSプロトコルでカプセル化されたHTTPをサポートし、これにより、HTTPSを使用するWebページなどのデータにアクセスできる

- メモリー常駐型のSSLセッションを管理するためのサーバー・セッション管理APIを提供する

- サーバー名指定拡張機能をサポートする。これにより、仮想サーバーとのセキュアな接続が容易になります。

- 証明書ステータス要求拡張機能(OCSPステープリング)をサポートする。これにより、クライアント証明書の検証のラウンドトリップおよびリソースが節約されます。

- Server Name Indication (SNI)拡張機能をサポートする。これにより、ハンドシェーク時にクライアントが接続を試みているサーバーの名前を示すようSSL/TLS/DTLSプロトコルが拡張されます。

- ハンドシェーク時のエンドポイント識別をサポートする。これにより、介入者攻撃を防ぐことができます。

- 暗号化アルゴリズム制約をサポートする。これにより、JSSEによってネゴシエーションされたアルゴリズムを、詳細に管理できるようになります。

JSSEは、次の暗号化アルゴリズムを使用します。

表8-1 JSSEで使用される暗号化アルゴリズム

暗号化機能

暗号化アルゴリズム脚注1

鍵の長さ(ビット)脚注2

バルク暗号化

Advanced Encryption Standard(AES)

256脚注3
128

バルク暗号化

データ暗号化規格(DES)

64 (56が有効)
64 (40が有効)

バルク暗号化

Rivest Cipher 4 (RC4)

128
128 (40が有効)

バルク暗号化

トリプルDES (3DES)

192 (112が有効)

ハッシュ・アルゴリズム

メッセージ・ダイジェスト・アルゴリズム(MD5)

128

ハッシュ・アルゴリズム

Secure Hash Algorithm 1 (SHA1)

160

ハッシュ・アルゴリズム

Secure Hash Algorithm 224 (SHA224)

224

ハッシュ・アルゴリズム

Secure Hash Algorithm 256 (SHA256)

256

ハッシュ・アルゴリズム

Secure Hash Algorithm 384 (SHA384)

384

ハッシュ・アルゴリズム

Secure Hash Algorithm 512 (SHA512)

512

認証

デジタル署名アルゴリズム(DSA)

1024, 2048, 3072

認証

Elliptic Curve Digital Signature Algorithm

160から512

認証と鍵交換

Rivest-Shamir-Adleman (RSA)

512以上

鍵交換

Static Elliptic Curve Diffie-Hellman (ECDH)

160から512

鍵交換

Ephemeral Elliptic Curve Diffie-Hellman (ECDHE)

160から512

鍵合意

Diffie-Hellman (DH)

512, 768, 1024, 2048, 3072, 4096, 6144, 8192

脚注1 SunJSSE実装では、そのすべての暗号化アルゴリズムでJava暗号化アーキテクチャ(JCA)が使用されています。

脚注2 JSSEプロバイダでは、弱いアルゴリズムおよび弱い鍵は無効化または非アクティブ化される場合があります。

脚注3 AES_256を使用する暗号群では、Java Cryptography Extension (JCE)の無制限強度の適切な管轄ポリシー・ファイル・セットが必要となります。これは、JDKに含まれています。デフォルトでは、アクティブな暗号化ポリシーは、無制限です。暗号強度の構成を参照してください。

JSSEの標準API

JSSE標準APIは、javax.netおよびjavax.net.sslパッケージで利用でき、次を提供します。

- クライアント側アプリケーションとサーバー側アプリケーションに合わせたセキュア・ソケット。

- SSL/TLS/DTLSデータのストリームを生成および消費する非ブロック・エンジン(SSLEngine)。

- ソケット、サーバー・ソケット、SSLソケット、およびSSLサーバー・ソケットを作成するファクトリ。ソケット・ファクトリを使用すると、ソケットの作成および構成動作をカプセル化できます。

- セキュアなソケット・ファクトリとエンジンのファクトリとして動作するセキュアなソケット・コンテキストを表すクラス。

- X.509固有のキー・マネージャやトラスト・マネージャなどの鍵およびトラスト・マネージャ・インタフェース、およびそれらのインタフェースを作成するために使用可能なファクトリ。

- セキュアなHTTP URL接続用のクラス(HTTPS)。

SunJSSEプロバイダ

OracleのJava SEの実装には、SunJSSEという名前のJSSEプロバイダが含まれており、これはあらかじめインストールされ、JCAに登録されています。このプロバイダが提供する暗号化サービスは次のとおりです。

- SSL 3.0、TLS (バージョン1.0、1.1および1.2)およびDTLS (バージョン1.0および1.2)セキュリティ・プロトコルの実装。

- 最も一般的なSSL、TLSおよびDTLS暗号群の実装。この実装は、認証、鍵合意、暗号化および整合性保護の組合せを含みます。

- 標準JCAキーストアから適切な認証鍵を選択するX.509ベースのキー・マネージャの実装。

- 証明書チェーン・パスを検証する規則を実装する、X.509ベースのトラスト・マネージャの実装。

SunJSSEプロバイダを参照してください。

JSSE関連ドキュメント

次のリストにはオンライン・ドキュメントのリンクと、関連サブジェクトの文書名を示しています。

JSSE APIドキュメント

- javax.netパッケージ

- javax.net.sslパッケージ

Java SEセキュリティ

- Java SEセキュリティのホーム・ページ

- JavaチュートリアルのJava SEのセキュリティ機能トレール

- Java PKIプログラマーズ・ガイド

- Java 2プラットフォーム・セキュリティの詳細、第2版: アーキテクチャ、API設計および実装

暗号化

- 暗号化とセキュリティに関するページ、Ronald L. Rivest博士著(保守されなくなりました)

- 『Applied Cryptography』(Bruce Schneier著)、第2版。John Wiley and Sons, Inc.、1996年

- 『Cryptography Theory and Practice』(Doug Stinson著)、CRC Press, Inc.、1995年。2005年発行第3版。

- 『Cryptography & Network Security: Principles & Practice』(William Stallings著)、Prentice Hall、1998年。2010年発行第5版。

Secure Sockets Layer(SSL)

- Secure Sockets Layer (SSL)プロトコル・バージョン3.0 RFC

- TLSプロトコル・バージョン1.0 RFC

- HTTP Over TLS RFC

- 『SSL and TLS: Designing and Building Secure Systems』(Eric Rescorla著)、Addison Wesley Professional、2000年。

- 『SSL and TLS Essentials: Securing the Web』(Stephen Thomas著)、John Wiley and Sons, Inc.、2000年。

- 『Java 2 Network Security』、第2版(Marco Pistoia,、Duane F Reller、Deepak Gupta、Milind Nagnur、Ashok K Ramani著)、Prentice Hall、1999年。

Transport Layer Security(TLS)

- TLSプロトコル・バージョン1.0 RFC

- TLSプロトコル・バージョン1.1 RFC

- TLSプロトコル・バージョン1.2 RFC

- トランスポート・レイヤー・セキュリティ(TLS)の拡張

- HTTP Over TLS RFC

Datagram Transport Layer Security (DTLS)

- DTLSプロトコル・バージョン1.0 RFC

- DTLSプロトコル・バージョン1.2 RFC

米国の暗号化政策

- 米国商務省

- Technology CEO Council

- 現在の輸出政策: 暗号と輸出管理規則(EAR)

- NIST発行の情報セキュリティ関連文書

用語と定義

次に、一般的に使用されている暗号化用語とその定義を示します。

認証

通信している相手側の識別情報を確認するプロセスです。

証明書

デジタル署名付きの文で、あるエンティティ(人や会社など)の識別情報および公開鍵の内容を保証します。証明書は、自己署名されるか証明書発行局(CA)(他のエンティティのために有効な証明書を発行する信頼されているエンティティ)によって発行されます。よく知られているCAには、Comodo、EntrustおよびGoDaddyなどがあります。X509は証明書の一般的な形式であり、JDKのkeytoolで管理できます。

暗号化方式群

暗号化パラメータの組合わせで、認証、鍵合意、暗号化および整合性保護に使用するセキュリティ・アルゴリズムおよび鍵のサイズを定義します。

暗号化ハッシュ関数

データの任意のブロックから比較的小さな固定サイズのビットの文字列(ハッシュと呼ばれる)を生成するために使われるアルゴリズム。暗号化ハッシュ関数はチェックサムに似ており、3つの主な特性があります。一方向の関数であるため、ハッシュからオリジナル・データを生成することはできません。オリジナル・データをわずかに変更しても、結果となるハッシュでは大きな変更になります。暗号化鍵は必要ありません。

暗号化サービス・プロバイダ(CSP)

短縮形としてプロバイダとだけ呼ばれることもあり、Java暗号化アーキテクチャ(JCA)ではそれを、特定の暗号化アルゴリズムの1つまたは複数のエンジン・クラスを実装するパッケージ(または一連のパッケージ)と定義しています。エンジン・クラスは、具体的な実装のない抽象的な方法で暗号化サービスを定義します。

Datagram Transport Layer Security (DTLS)プロトコル

クライアントとサーバーの認証、データ整合性、およびUDPなどの信頼できないトランスポート・チャネルに基づいたクライアントとサーバーとの間の暗号化通信を管理するプロトコルです。

復号化

暗号化/復号化を参照してください。

デジタル署名

デジタル署名とは、手書きの署名のデジタル版です。これは、ネットワークで伝送されるデータが、それを送信したと主張する人物からのものであり、送信中にデータが変更されていないことを保証するものです。たとえば、RSAベースのデジタル署名を計算するには、まずデータの暗号化ハッシュを計算し、次に送信者の非公開鍵でハッシュを暗号化します。

暗号化/複合化

暗号化は複雑なアルゴリズムを使用して、元のメッセージ(クリアテキスト)を、復号化しないかぎり、その内容を理解できないエンコードされたメッセージ(暗号テキスト)に変換するプロセスです。復号化とは、暗号テキストからクリアテキストを生成する逆のプロセスです。

データの暗号化および復号化に使用するアルゴリズムは一般に、秘密鍵(対称)暗号化と公開鍵(非対称)暗号化の2つのカテゴリに分けられます。

エンドポイント識別

ネットワーク上のエンドポイントを識別するために使用されるIPv4アドレスまたはIPv6アドレスです。

エンドポイント識別の手順は、SSL/TLSハンドシェーク中に処理されます。

ハンドシェーク・プロトコル

2つのソケット同士が新しいセッションや既存のセッションの使用に同意するネゴシエーションのフェーズです。ハンドシェーク・プロトコルは、レコード・プロトコルを介して交換される一連のメッセージです。ハンドシェークの終了時に、セッションの接続に固有の暗号化鍵や、整合性を保護するための鍵が、鍵合意による秘密に基づいて新たに生成されます。

java-home

このドキュメント全体でJava Development Kit (JDK)がインストールされているディレクトリを示すために使用される、可変プレースホルダです。

鍵合意

二者が協力して共通鍵を確立するための方法です。それぞれの側が一定のデータを生成して交換します。そのあと、2つのデータが組み合わされて、1つの鍵が生成されます。適正な非公開初期化データを保持しているユーザーのみが、最終的な鍵を取得できます。Diffie-Hellman (DH)は、一般的な鍵合意アルゴリズムの一例です。

鍵交換

鍵を交換する方法です。一方の側が秘密鍵を生成し、標準的にはRSAにより、ピアの公開鍵を使用して暗号化します。データがピアに送信され、ピアは対応する秘密鍵を使用して鍵を復号化します。

キー・マネージャ/トラスト・マネージャ

キー・マネージャとトラスト・マネージャは、それぞれの鍵データにキーストアを使用します。キー・マネージャはキーストアを管理し、ユーザーを他のユーザーに対して承認する場合に使用するなど必要に応じて、他のユーザーに公開鍵を提供します。トラスト・マネージャは、管理するトラストストアの情報に基づいて、トラストの対象者を決定します。

キーストア/トラストストア

キーストアは、鍵データのデータベースです。鍵データにはさまざまな用途があり、それには認証やデータ整合性も含まれます。利用できるキーストアには様々なタイプがあり、その中にはPKCS12やOracleのJKSも含まれます。

一般に、キーストア情報は、鍵エントリと信頼される証明書エントリ2つのカテゴリに分類できます。鍵エントリはエンティティの識別情報とその秘密鍵から構成されており、様々な暗号化の目的で使用できます。これとは対照的に、信頼される証明書のエントリには、公開鍵とそのエンティティの識別情報しか含まれていません。したがって、javax.net.ssl.KeyManagerの場合など、秘密鍵が必要な場合は、信頼される証明書エントリを使用することはできません。JKSのJDK実装では、キーストアに鍵のエントリと、信頼される証明書エントリの両方を含めることができます。

トラストストアとは、トラストの対象を決めるときに使用するキーストアです。すでに信頼しているエンティティからデータを受け取る場合、およびそのエンティティが発信元を名乗るエンティティであることを確認できる場合は、データは実際にそのエンティティから届いたものであると仮定できます。

ユーザーがそのエンティティを信頼する場合にのみ、エントリをトラストストアに追加する必要があります。ユーザーは、鍵のペアを生成するか、証明書をインポートすることにより、そのエントリにトラストを与えます。トラストストア内のすべてのエントリは信頼されたエントリとみなされます。

2つの異なるキーストア・ファイルを持つと便利な場合があります。1つは鍵エントリのみのファイル、もう1つはCA証明書を含む信頼された証明書エントリを含むファイルです。前者には機密性のある情報が含まれますが、後者には含まれません。単独のキーストア・ファイルではなく、2つのファイルを使用すると、独自の証明書(および対応する秘密鍵)と他の証明書を論理的に区別した明確な区分が提供されます。秘密鍵の保護を強化するには、アクセスが制限されたキーストアにそれらを保存し、必要に応じて、より公的にアクセスできるキーストアで信頼される証明書を提供することもできます。

メッセージ認証コード(MAC)

信頼できない媒体に送信または格納された情報の整合性を、秘密鍵に基づいてチェックする方法を提供します。通常、MACは秘密鍵を共有する2つの当事者間で、お互いが送信する情報を検証するために使用されます。

暗号化ハッシュ機能に基づくMACメカニズムは、HMACと呼ばれます。HMACは、共有する秘密鍵と組み合せて、Message Digest 5 (MD5)やSecure Hash Algorithm (SHA-256)などの暗号化ハッシュ関数とともに使用できます。HMACについては、RFC 2104で規定されています。

公開鍵暗号化

2つの鍵を生成する暗号化アルゴリズムを使用する鍵暗号化システムです。一方の鍵は公開されますが、他方は秘密のままです。公開鍵と非公開鍵では、逆の暗号化処理がなされ、一方の鍵で暗号化したものを他方の鍵で復号化します。公開鍵暗号化は、非対称暗号化とも呼ばれます。

レコード・プロトコル

すべてのデータ(アプリケーション・レベルであるかハンドシェーク・プロセスの一部であるかに関係なく)を独立したデータのレコードにパッケージ化するプロトコルで、TCPストリーム・ソケットがアプリケーション・バイト・ストリームをネットワーク・パケットに変換するのとよく似ています。個々のレコードは、現在の暗号化鍵と整合性保護鍵によって保護されます。

秘密鍵暗号化

データの暗号化と復号化に同じ鍵を使用する暗号化アルゴリズムを使用する暗号化システム。秘密鍵暗号化は対称暗号化とも呼ばれます。

Secure Sockets Layer (SSL)プロトコル

クライアントとサーバーの認証、データ整合性、およびクライアントとサーバーとの間の暗号化通信を管理するプロトコルです。

セッション

認証されたピア識別情報、暗号化方式群、鍵合意の秘密を含む名前付きの状態情報のコレクションで、セキュアなソケット・ハンドシェークを通じてネゴシエーションが行われ、複数のセキュアなソケット・インスタンス間で共有できます。

Transport Layer Security (TLS)プロトコル

クライアントとサーバーの認証、データ整合性、およびTCPなどの信頼できるトランスポート・チャネルに基づいたクライアントとサーバーとの間の暗号化通信を管理するプロトコルです。

TLS 1は、SSL 3.0プロトコルの後継です。

トラスト・マネージャ

キー・マネージャ/トラスト・マネージャを参照してください。

トラストストア

キーストア/トラストストアを参照してください。

Secure Sockets Layer (SSL)プロトコルの概要

Secure Sockets Layer (SSL)は、Webで暗号化を実装する場合にもっともよく使用されるプロトコルです。SSLは、ネットワークでセキュアな通信を行うために暗号化プロセスを組み合わせて使用します。このセクションでは、SSLおよびSSLが使用する暗号化プロセスについて簡単に説明します。

SSLは、インターネット通信で使用される標準的なTCP/IPソケットプロトコルをセキュアに拡張します。表8-2に示すように、Secure Sockets Layerは標準TCP/IPプロトコル・スタックのトランスポート層とアプリケーション層の間に追加されます。SSLとともにもっともよく使用されるアプリケーションは、インターネットWebページ用のプロトコルであるHypertext Transfer Protocol (HTTP)です。この他にも、Net News Transfer Protocol (NNTP)、Telnet、Lightweight Directory Access Protocol (LDAP)、Interactive Message Access Protocol (IMAP)、File Transfer Protocol (FTP)などのアプリケーションがあり、同様にSSLとともに使用します。

表8-2 SSLによるTCP/IPプロトコル・スタック

TCP/IPの層

プロトコル

アプリケーション層

HTTP、NNTP、Telnet、FTPなど

Secure Sockets Layer

SSL

トランスポート層

TCP

インターネット層

IP

SSLは1994年にNetscape社によって開発され、インターネットの世界で使用されるようになると、標準的な存在になりました。現在では、国際的な標準化機構であるInternet Engineering Task Force (IETF)が管理しています。IETFはSSLの名称をTransport Layer Security (TLS)に変更し、1999年1月に最初の仕様のバージョン1.0をリリースしました。TLS 1.0は、SSLの最新バージョン3.0を少しだけ変更したものです。このアップグレードでは、前のバージョンでの不具合が修正され、既知の弱いアルゴリズムの使用が禁止されました。TLS 1.1は2006年4月にリリースされ、TLS 1.2は2008年8月にリリースされました。

SSLを使用することの利点

次の3つの理由から、機密情報をネットワークで送信する際に危険が伴う場合があります。

- 通信相手のエンティティが、実際には必ずしも想定する相手とはかぎらない。

- ネットワーク上のデータは傍受されることがあり、権限のない第三者(場合によっては攻撃者)にデータを読み取られてしまうことがある。

- データを傍受する攻撃者はデータが受信者に届く前に内容を変更できる可能性があります。

SSLはこれらの各問題に対処します。最初の問題には、認証と呼ばれるプロセスで、通信の当事者双方に相手側の識別情報をオプションで確認させることで対応しています。両者が認証されると、SSLはセキュアなメッセージ伝送のために両者間の暗号化接続を提供します。両者の通信を暗号化することで機密性が保持されるため、2番目の問題に対処します。SSLで使用する暗号化アルゴリズムには、セキュアなハッシュ関数が含まれており、これはチェックサムに似ています。これにより、送信中にデータが変更されていないことが保証されます。セキュアなハッシュ関数により、3番目のデータの整合性の問題に対処します。

注意:
認証も暗号化もオプションであり、2つのエンティティ間のネゴシエーションされた暗号化方式群に依存します。

SSLを使用する場合の明確な例は電子商取引です。電子商取引では、通信するサーバーの識別情報は保証されていると考えるべきではありません。クレジット・カードの番号を入力するだけですばらしいサービスが受けられるという偽のWebサイトを作成するのは簡単なことです。SSLを使うと、クライアントがサーバーの識別情報を認証することができます。また、サーバーもクライアントの情報を認証できますが、インターネット上の取引では、この方法はあまり使われていません。

クライアントとサーバーが互いの情報を認証すると、SSLは暗号化アルゴリズムを使用して機密性とデータの整合性を提供します。これにより、クレジット・カード番号のような機密情報をインターネット上でセキュアに送信することができます。

SSLは認証、機密性およびデータの整合性を提供しますが、非拒否サービスは提供しません。非拒否性とは、メッセージを送信したエンティティは、後で送信を拒否できないということを意味します。メッセージとデジタル署名が関連付けられていると、後になって通信内容を証明することができます。SSL単独では、非拒否性を提供しません。

SSLの仕組み

SSLが有効な理由の1つに、複数の暗号化プロセスを使用していることがあります。SSLは、公開鍵暗号化で認証を行い、秘密鍵暗号化とハッシュ関数で機密性とデータ整合性を提供します。SSLについて理解する前に、暗号化の処理方法を理解しておくと役立ちます。

暗号化処理

暗号化の主な目的は、2者間の秘密の通信に権限のない第三者がアクセスしたり、その内容を理解するのを困難にすることです。暗号化のプロセスによって、データに対する権限のないすべてのアクセスを必ずしも制限できるわけではありませんが、権限のない者が秘密のデータを理解できないようにすることができます。暗号化では、複雑なアルゴリズムを使用して、元のメッセージ(クリアテキスト)をエンコードされたメッセージ(暗号テキスト)に変更します。ネットワーク上で転送されるデータの暗号化および復号化に使用するアルゴリズムは一般に、秘密鍵暗号化と公開鍵暗号化の2つのカテゴリに分けられます。

秘密鍵暗号化も公開鍵暗号化も、合意に基づく暗号鍵または暗号鍵のペアを使用します。鍵は、データの暗号化プロセスおよび復号化プロセスで暗号化アルゴリズムが使用するビット文字列です。暗号化鍵は、錠の鍵と同様、錠を開けることができるのは正しい鍵のみです。

通信の当事者が互いに鍵を安全に送信するのは、些細な問題ではありません。公開鍵証明書を使用すると、公開鍵を安全に送信し、受信者に公開鍵の信頼性を保証できます。公開鍵証明書を参照してください。

秘密鍵暗号化と公開鍵暗号化における暗号化処理の説明では、セキュリティのコミュニティで広く使用されている慣例(通信する2人の当事者はAliceとBobという名前で示される)に従います。権限のない第三者は攻撃者とも呼ばれ、Charlieと名付けられます。

秘密鍵暗号化

秘密鍵暗号化では、通信するAliceとBobはメッセージの暗号化と復号化に同じ鍵を使用します。暗号化されたデータをネットワークで送信する前に、AliceとBobは鍵を持っていることが必要で、暗号化と復号化に使用する暗号化アルゴリズムに同意している必要があります

秘密鍵暗号化で大きな問題の1つが、攻撃者にアクセスされずに一方から他方に鍵を渡す方法の問題です。AliceとBobが秘密鍵暗号化でデータを保護しても、Charlieがその鍵にアクセスできればAliceとBobの間で傍受した秘密メッセージを理解できます。CharlieはAliceとBobのメッセージを復号化できるだけではなく、Aliceになりすまして暗号化データをBobに送信することもできるのです。Bobには、メッセージがCharlieから届いたものかAliceから届いたものかはわかりません。

秘密鍵の配布の問題が解決すれば、秘密鍵暗号化はたいへん貴重なツールになります。そのアルゴリズムにより、優れたセキュリティと暗号化データが比較的迅速に提供できるからです。SSLセッションで送信される機密性の高いデータの多くは、秘密鍵暗号化で送信されます。

秘密鍵暗号化は、データの暗号化と復号化の両方に同じ鍵を使用するため、対称暗号化とも呼ばれます。よく知られている秘密鍵暗号化アルゴリズムには、Advanced Encryption Standard (AES)、Triple Data Encryption Standard (3DES)およびRivest Cipher 4 (RC4)があります。

公開鍵暗号化

公開鍵暗号化は、公開鍵と秘密鍵の両方を使用することで鍵の配布方法の問題を解決しました。公開鍵はネットワークを通じて公開し、送信できますが、非公開鍵は通信の1人の当事者にしか公開されません。公開鍵と非公開鍵は暗号化方式が逆で、一方の鍵で暗号化したものをもう一方の鍵で復号化します。

Bobが公開鍵暗号化を使用して、Aliceに秘密のメッセージを送信するとします。Aliceは公開鍵と非公開鍵をどちらも持っているので、非公開鍵は安全な場所に保管しておき、公開鍵をBobに送信します。BobはAliceの公開鍵を使ってAliceへの秘密のメッセージを暗号化します。Aliceは非公開鍵を使ってメッセージを復号化します。

Aliceが自分の秘密鍵を使用してメッセージを暗号化し、その暗号化されたメッセージをBobに送信すれば、Bobが受信するデータはAliceから届いたものだと考えることができます。BobがAliceの公開鍵でデータを復号化できれば、そのメッセージはAliceが自分の秘密鍵で暗号化したものに間違いなく、Aliceの秘密鍵を持っているのはAliceのみです。問題は、Aliceの公開鍵が公開されているために、だれもがメッセージを読めてしまうことです。このシナリオは、セキュアなデータ通信を考慮に入れていませんが、デジタル署名の基本を示しています。デジタル署名とは公開鍵証明書のコンポーネントの1つで、SSLでクライアントやサーバーを認証するために使用します。公開鍵証明書とデジタル署名を参照してください。

公開鍵暗号化は、データの暗号化と復号化に別の鍵を使用するので、非対称暗号化とも呼ばれます。SSLでよく使われる、よく知られている公開鍵暗号化アルゴリズムは、Rivest Shamir Adleman (RSA)アルゴリズムです。このほかにも、秘密鍵を交換するために設計されたSSLを使う公開鍵暗号化アルゴリズムには、Diffie-Hellman (DH)があります。公開鍵暗号化には膨大な計算が必要なため、速度が大幅に遅くなります。そこで、この方式は暗号化データ通信全体に使用するよりもむしろ、秘密鍵など少量のデータを暗号化する場合にだけ使用します。

秘密鍵暗号化と公開鍵暗号化の比較

秘密鍵暗号化と公開鍵暗号化のどちらにも、長所と短所があります。秘密鍵暗号化では、データの暗号化や復号化に時間はかかりませんが、通信者同士が同じ秘密鍵情報を共有する必要があり、鍵の交換の方法が問題になります。公開鍵暗号化では、鍵を秘密にする必要がないので鍵の交換は問題になりませんが、データの暗号化と復号化に使用するアルゴリズムには膨大な計算が必要で、著しく遅くなります

公開鍵証明書

公開鍵証明書を使用すると、エンティティは非対称暗号化で使用する公開鍵を安全に配布できます。公開鍵証明書は、次の状況を回避します。Charlieが自分の公開鍵と秘密鍵を作成すれば、自分はAliceだと名乗ってBobに公開鍵を送信できます。BobはCharlieと通信できますが、データをAliceに送信していると思い込んでしまいます。

公開鍵証明書は電子的なパスポートだと考えることができます。これは、信頼できる組織によって発行され、所有者に識別情報を提供します。公開鍵証明書を発行する信頼できる組織を、証明書発行局(CA)と呼びます。CAは公証人にたとえることができます。CAから証明書を取得するには、識別情報の証拠となるものを提供する必要があります。CAは、申請者が申し立てる組織の代表であるとの確証が得られたら、証明書に含まれる情報の妥当性を証明する証明書に署名します。

公開鍵証明書には、次のようなフィールドがあります。

発行者

証明書を発行した認証局(CA)。証明書を発行したCAが信頼でき、証明書が有効であれば、証明書は信頼できます。

有効期間

証明書には有効期限があります。証明書の有効性を検証する場合は、この日付をチェックしてください。

サブジェクト

証明書が表すエンティティに関する情報が含まれます。

サブジェクトの公開鍵

証明書が提供する情報の主要な部分は、サブジェクトの公開鍵です。その他のフィールドは、この鍵の妥当性を確認するためのものです。

署名

証明書は、証明書を発行したCAによってデジタルで署名されます。署名はCAの非公開鍵を使って作成され、証明書の妥当性を保証するものです。証明書のみに署名され、SSLトランザクションで送信されるデータには署名されないので、SSLには非拒否性がありません。

AliceがBobに公開鍵証明書で自分の公開鍵を送信するとき、BobがAliceの公開鍵のみを有効として受け付ける場合、CharlieがAliceになりすましたとしても、BobがだまされてCharlieに秘密情報を送信することはありません。

複数の証明書を証明書チェーンでリンクすることもできます。証明書チェーンを使用する場合、最初の証明書は必ず送信者の証明書です。次は送信者の証明書を発行したエンティティの証明書です。チェーン内に他の証明書がある場合、それぞれ直前の証明書を発行した証明書発行局の証明書です。チェーンの最後の証明書は、ルートCAの証明書です。ルートCAは、広く信頼されている公開証明書発行局です。複数のルートCAの情報は、通常、クライアントのインターネット・ブラウザに保存されています。この情報には、CAの公開鍵が含まれています。よく知られているCAにはVeriSign、Entrust、およびGTE CyberTrustがあります。

暗号化ハッシュ関数

暗号化されたデータを送信する場合、SSLは通常、暗号化ハッシュ関数を使ってデータの整合性を保証します。ハッシュ関数を使って、AliceがBobに送ったデータをCharlieが改ざんできないようにします。

暗号化ハッシュ関数はチェックサムに似ています。主な違いは、チェックサムがデータの偶発的変化を検出するのに対し、暗号化ハッシュ関数は故意による変更を検出するように設計されています。データが暗号化ハッシュ関数で処理されると、ハッシュと呼ばれる小さなビット文字列が生成されます。メッセージがごくわずかだけ変更された場合も、結果として生成されるハッシュは大きく変更されます。暗号化ハッシュ関数には、暗号化鍵が必要ありません。SSLとともによく使用されるハッシュ関数は、Secure Hash Algorithm (SHA)です。SHAは、U.S. National Institute of Standards and Technology (NIST)によって提案されました。

メッセージ認証コード

メッセージ認証コード(MAC)は暗号化ハッシュに似ていますが、秘密鍵をベースにしている点が異なります。秘密鍵情報が暗号化ハッシュ関数で処理したデータに含まれている場合、その結果生成されるハッシュはHMACと呼ばれます。

Aliceは、BobへのメッセージをCharlieが確実に改ざんしないようにする場合、メッセージのHMACを計算して元のメッセージにHMACを追加できます。次に、Bobと共有している秘密鍵を使用してメッセージとHMACを暗号化できます。Bobは、メッセージを復号化してHMACを計算すれば、送信中にメッセージが変更されたかどうかを知ることができます。SSLでは、HMACを使ってセキュアなデータを送信します。

デジタル署名

メッセージに暗号化ハッシュが作成されると、ハッシュは送信者の非公開鍵で暗号化されます。このような暗号化ハッシュをデジタル署名と呼びます。

SSLハンドシェーク

SSLを使った通信は、クライアントとサーバー間の情報交換から始まります。この情報交換をSSLハンドシェークと呼びます。SSLハンドシェークには、次のステージがあります。

1. 暗号化方式群のネゴシエーション
SSLセッションは、どの暗号群を使用するかについて、クライアントとサーバーがネゴシエーションを行うことから始まります。暗号化方式群とは、コンピュータがデータを暗号化するために使用する暗号化アルゴリズムと鍵のサイズのセットです。符号化方式には、公開鍵交換アルゴリズムまたは鍵合意アルゴリズム、および暗号化ハッシュ関数に関する情報が含まれます。クライアントは利用できる暗号群をサーバーに伝え、サーバーは、どちらにも適用できる暗号群を選択します。

2. サーバーの識別情報の認証(オプション)
SSLの認証ステップはオプションです。しかし、Web上の電子商取引の例では、一般にクライアントがサーバーを認証します。サーバーの認証により、サーバーが表すとクライアントが信じているエンティティを、そのサーバーが実際に表していることをクライアントが確認できます。

サーバーは、自らが表すと唱える組織に属していることを証明するため、クライアントに公開鍵証明書を提示します。この証明書が有効であれば、クライアントはサーバーの識別情報について確信できます。

クライアントとサーバーは、同じ秘密鍵について同意できる情報を交換します。たとえば、RSAを使う場合、クライアントは公開鍵証明書で取得したサーバーの公開鍵を使用して、秘密鍵情報を暗号化します。クライアントは暗号化された秘密鍵情報をサーバーに送信します。復号化にはサーバーの非公開鍵が必要なので、サーバーでだけ、このメッセージを復号化できます。

3. 暗号化メカニズムの合意
クライアントとサーバーは、同じ秘密鍵にアクセスします。それぞれのメッセージでは、ハンドシェークの最初のステップで選択した暗号化ハッシュ関数と、共有された秘密情報を使用して、メッセージに添付されるHMACを計算します。次に、秘密鍵と、ハンドシェークの最初のステップでネゴシエーションされた秘密鍵アルゴリズムを使用し、セキュアなデータとHMACを暗号化します。そのあと、クライアントとサーバーは、暗号化されハッシュ化されたデータを使ってセキュアに通信することができます。

SSLプロトコル

SSLハンドシェークでは、SSLハンドシェークについて概要を説明しました。それは、暗号化されたメッセージを送信する前にクライアントとサーバーの間で行われる情報の交換です。図8-1に詳細を示します。これは、SSLハンドシェークで交換される一連のメッセージを示しています。特定の状況下でだけ送信されるメッセージには「optional」と記されています。各SSLメッセージについては、後で詳しく説明します。

図8-1 SSL/TLSハンドシェーク

SSLメッセージは、次の順序で送信されます。

1. Client hello: クライアントは、それがサポートする最上位バージョンのSSLと暗号化方式群のリスト(TLS 1.0はSSL 3.1と示される)を含むサーバー情報を送信します。暗号化方式群の情報には、暗号化アルゴリズムと鍵のサイズが含まれます。

2. Server hello: サーバーは、クライアントとサーバーの両方がサポートする最上位バージョンのSSLと最適な暗号化方式群を選択し、この情報をクライアントに送信します。

3. (オプション) Certificate: サーバーはクライアントに証明書または証明書チェーンを送信します。証明書チェーンは通常、サーバーの公開鍵証明書で始まり、認証局のルート証明書で終わります。このメッセージはオプションで、サーバー認証を求められた場合に使用します。

4. (オプション) Certificate request: サーバーがクライアントを認証する必要がある場合、クライアントに証明書要求を送信します。インターネット・アプリケーションでは、このメッセージが使われることはほとんどありません。

5. (オプション) Server key exchange: Certificateからの公開鍵情報が鍵交換を行うのに不十分な場合、サーバーはクライアントにサーバー鍵交換メッセージを送信します。たとえば、Diffie-Hellman (DH)に基づく暗号化方式群では、このメッセージにはサーバーのDH公開鍵が含まれています。

6. Server hello done: サーバーは、最初のネゴシエーション・メッセージを終了したことをクライアントに伝えます。

7. (オプション) Certificate: サーバーがクライアントに証明書を要求すると、クライアントはサーバーが前に行ったように、その証明書チェーンを送信します。

注意:
クライアントに証明書を要求するのは、ごく一部のインターネット・サーバー・アプリケーションのみです。

8. Client key exchange: クライアントは、対称暗号化で使用する鍵を作成するために使用される情報を生成します。RSAでは、クライアントはサーバーの公開鍵でこの鍵情報を暗号化してサーバーに送信します。DHに基づく暗号化方式群の場合、このメッセージにはクライアントのDH公開鍵が含まれています。

9. (オプション) Certificate verify: このメッセージは、上述のとおりクライアントが証明書を提示する場合にクライアントによって送信されます。このメッセージは、サーバーにクライアントの認証処理を完了させるためのものです。このメッセージが使用されると、クライアントは暗号化ハッシュ関数で電子的に署名した情報を送信します。サーバーがクライアントの公開鍵でこの情報を復号化すれば、サーバーはクライアントを認証できます。

10. Change cipher spec: クライアントはメッセージを送信し、暗号化モードに変更するようサーバーに伝えます。

11. Finished クライアントはサーバーに、セキュアなデータ通信を開始する準備ができたことを伝えます。

12. Change cipher spec: サーバーはメッセージを送信し、暗号化モードを変更するようクライアントに伝えます。

13. Finished: サーバーはクライアントに、セキュアなデータ通信を開始する準備ができたことを伝えます。SSLハンドシェークが終了します。

14. Encrypted data: クライアントとサーバーは、client helloとserver hello時にネゴシエーションされた対称暗号化アルゴリズムと暗号化ハッシュ関数およびclient key exchange時にクライアントがサーバーに送信した秘密鍵を使用して通信します。このときハンドシェークの再ネゴシエーションを行うことができます。ハンドシェークの再実行(再ネゴシエーション)を参照してください。

15. Close Messages: 接続の終わりに、それぞれの側がclose_notifyアラートを送信し、接続が終了したことをピアに伝えます。

SSLセッションで生成したパラメータを保存しておけば、将来のSSLセッションでこれらのパラメータを再利用できます。SSLセッションのパラメータを保存しておけば、暗号化通信をすばやく開始できます。

ハンドシェークの再実行(再ネゴシエーション)

初期のハンドシェークが完了してアプリケーション・データが流れているとき、いずれの側からでも新しいハンドシェークをいつでも開始できます。特に重要な操作について、アプリケーションで強力な暗号化方式群を使用したり、サーバー・アプリケーションでクライアント認証が必要になる場合もあります。

理由は何であれ、新しいハンドシェークが既存の暗号化セッションに置き換わり、新しいセッションが確立されるまで、アプリケーション・データとハンドシェーク・メッセージが交互に配置されます。

アプリケーションで次のいずれかのメソッドを使用して、新しいハンドシェークを開始できます。

- SSLSocket.startHandshake()

- SSLEngine.beginHandshake()

注意:
再ネゴシエーションに関するプロトコルの問題が2009年に見つかりました。プロトコルおよびJava SE実装はいずれも修正されています。Transport Layer Security (TLS)再ネゴシエーションの問題を参照してください。

符号化方式の選択とリモート・エンティティの検証

SSL/TLSプロトコルは、保護された接続を確保するための一連の特定のステップを定義します。ただし、暗号化方式群の選択が、接続で確保するセキュリティのタイプに直接影響します。たとえば、匿名暗号化方式群を選択した場合、アプリケーションにはリモート・ピアの識別情報を検証する方法がありません。暗号化しない方式群が選択された場合は、データの機密性を保護できません。またSSL/TLSプロトコルでは、受信した資格と、ピアから送信されることが予期される資格が一致するようにとは規定していません。接続がなんらかの理由で悪意のあるピアにリダイレクトされたときに、悪意のあるピアのクレデンシャルが現在のトラスト・データに基づいて受け付けられた場合、その接続は有効とみなされてしまいます。

raw SSLSocketおよびSSLEngineクラスを使用する場合は、データの送信前に必ずピアのクレデンシャルをチェックしてください。SSLSocketおよびSSLEngineクラスは、URL内のホスト名がピアのクレデンシャル内のホスト名と一致することを自動的に検証しません。ホスト名が検証されない場合、URL不正行為によってアプリケーションが悪用される可能性があります。JDK 7以降は、エンドポイント識別/検証の手順は、SSL/TLSハンドシェーク中に処理できます。SSLParameters.getEndpointIdentificationAlgorithmメソッドを参照してください。

HTTPS (HTTP Over TLS)などのプロトコルでは、ホスト名検証が必要です。JDK 7以降、HTTPSエンドポイント識別は、HttpsURLConnectionのためのハンドシェーク中にはデフォルトで強制されます。SSLParameters.getEndpointIdentificationAlgorithmメソッドを参照してください。また、アプリケーションは、HostnameVerifierインタフェースを使用してデフォルトのHTTPSホスト名規則をオーバーライドできます。HostnameVerifierインタフェースおよびHttpsURLConnectionクラスを参照してください。

クライアント主導型OCSPとOCSPステープリング

Transport Layer Security (TLS)のハンドシェーク中にX.509証明書失効ステータスを判定するには、オンライン証明書ステータス・プロトコル(OCSP)を使用します。

TLSで使用されるX.509証明書は、証明書に欠陥があるとみなす理由がある場合は、発行元の証明書発行局(CA)によって無効にされる可能性があります。次のいずれかの手法を使用することで、TLSハンドシェーク中に証明書の失効ステータスを確認できます。

- 証明書失効リスト(CRL)の実装
CRLは、失効した証明書を単純に示すリストです。証明書を受け取るアプリケーションは、CRLサーバーからCRLを取得し、受け取った証明書がリストにあるかどうかを確認します。CRLの使用で不便な点は2つあります。それは、証明書を失効させることができるが、その失効した証明書はCRLにリストされないということです。

- CRLは非常に大きくなる可能性があるため、ネットワーク・トラフィックがかなり増加する可能性があります。

- 多くのCRLは長い有効期間で作成されます。これにより、証明書がその有効期間内に失効し次のCRL更新までリストに含まれなくなる可能性が高くなります。

Java PKIプログラマーズ・ガイドの証明書/CRLストレージ・クラスのトピックを参照してください。

- クライアント主導型OCSP
クライアント主導型OCSPでは、クライアントはOCSPを使用してOCSPレスポンダに連絡し、証明書の失効ステータスを確認します。必要なデータの量は少なく、OCSPレスポンダは、CRLよりも最新の失効ステータスを把握している可能性があります。サーバーに接続する各クライアントは、確認する証明書ごとに1つのOCSP応答が必要となります。サーバーがよく使用されるものであり、クライアントの多くでクライアント主導型OCSPが使用されている場合、これらのOCSP要求は、OCSPレスポンダのパフォーマンスに悪影響を及ぼす可能性があります。

- OCSPステープリング
OCSPステープリングにより、クライアントではなくサーバーで、OCSPレスポンダへの要求を行うことができます。サーバーは、証明書へのOCSP応答をステープリングして、TLSハンドシェーク中にそれをクライアントに返します。この手法では、発行元のCAではなく、証明書の提示者が、OCSP応答の提供のリソース・コストを負担できます。また、サーバーでOCSP応答をキャッシュし、それらをすべてのクライアントに提供できるようになります。これにより、応答をキャッシュし各クライアントによってではなくサーバーによって定期的に更新できるため、OCSPレスポンダの負荷が大幅に軽減されます。

クライアント主導型OCSPと証明書失効

クライアント主導型オンライン証明書ステータス・プロトコル(OCSP)では、クライアントが、Transport Layer Security (TLS)ハンドシェーク中にOCSPレスポンダに接続することで、証明書失効ステータスを確認できます。

クライアント主導型OCSPの要求は、TLSハンドシェーク中の、クライアントがサーバーから証明書を受け取りそれを検証したすぐ後に発生します。SSLハンドシェークを参照してください。

クライアント主導型OCSPを使用するTLSハンドシェーク

クライアント主導型OCSPは、サーバーの証明書失効ステータスを確認するために、クライアントとサーバーとの間のTLSハンドシェーク中に使用されます。クライアントは証明書を受け取った後、証明書検証を実行します。検証が成功すると、クライアントは、証明書が発行者によって失効にされていないことを確認します。これは、OCSPレスポンダにOCSP要求を送信することで行われます。OCSP応答を受け取ると、クライアントは、TLSハンドシェークが完了する前にこの応答を確認します。

図8-2 クライアント主導型OCSPを使用するTLSハンドシェーク

通常、クライアントは証明書のAuthority Information Access (AIA)拡張情報を調べてOCSPレスポンダのURLを見つけますが、システム・プロパティの使用によってそれを静的URLに設定できます。

クライアント主導型OCSPを使用するためのJavaクライアントの設定

クライアント主導型OCSPは、失効チェックとOCSPを有効にすることで有効になります。

クライアント主導型OCSPを使用するようにJavaクライアントを構成するには、Javaクライアントを、TLSを使用してサーバーに接続するように設定してある必要があります。

1. 失効チェックを有効にします。これを行うには、異なる2通りの方法があります。

- システム・プロパティcom.sun.net.ssl.checkRevocationをtrueに設定します。

- PKIXParametersでsetRevocationEnabledメソッドを使用します。PKIXParametersクラスを参照してください。

2. クライアント主導型OCSPを有効にします。

セキュリティ・プロパティocsp.enableをtrueに設定します。

両方のステップが必要となります。失効チェックが有効になっている場合以外は、ocsp.enable設定に効果はありません。

OCSPステープリングと証明書失効

オンライン証明書ステータス・プロトコル(OCSP)ステープリングでは、発行元の証明書発行局(CA)ではなく、証明書の提示者が、証明書の失効ステータスを含むOCSP応答の提供のリソース・コストを負担できます。

OCSPステープリングを使用するTLSハンドシェーク

OCSPステープリングは、サーバーの証明書失効ステータスを確認するために、クライアントとサーバーとの間のTransport Layer Security (TLS)ハンドシェーク中に使用されます。サーバーはOCSPレスポンダへのOCSP要求を行い、クライアントに返された証明書にOCSP要求をステープリングします。サーバーにOCSPレスポンダへの要求を行わせることで、応答をキャッシュし、多数のクライアントのために複数回使用できます。

TLSハンドシェークは、TLS ClientHelloメッセージで始まります。OCSPステープリングが使用される場合、このメッセージは、サーバーがOCSP要求を実行する必要があることを示すstatus_request拡張情報とともにサーバーに送信されます。ClientHelloメッセージを処理した後、サーバーは、証明書ごとに適切なOCSPレスポンダにOCSP要求を送信します。サーバーはOCSPレスポンダからOCSP応答を受け取ると、TLSハンドシェークでOCSP応答が提供されることを示すstatus_request拡張情報とともに、ServerHelloメッセージを送信します。次に、サーバーはサーバー証明書チェーンを提示し、その後にそれらの証明書の1つ以上のOCSP応答で構成されているメッセージが続きます。ステープリングされたOCSP応答とともに証明書を受け取るクライアントは、各証明書を検証してから、ハンドシェークを続行する前にそのOCSP応答を確認します。

クライアントの視点から述べると、証明書のための、サーバーからのステープリング済OCSP応答がない場合、クライアントはクライアント主導型OCSPまたはCRLを使用して失効情報を取得しようとします。これらのどちらかが有効になっている場合、失効チェックはtrueに設定されます。

図8-3 OCSPステープリングを使用するTLSハンドシェーク

TLSハンドシェーク・メッセージの詳細は、SSLハンドシェークを参照してください。

ステータス要求と複数ステータス要求との比較

OCSPステープリング機能は、TLS Certificate Status Request拡張機能(RFC 6066の第8項)およびMultiple Certificate Status Request拡張機能(RFC 6961)を実装します。

TLS Certificate Status Request拡張機能は、証明書チェーン内のサーバー証明書のみのために失効情報を要求しますが、Multiple Certificate Status Request拡張機能は、証明書チェーン内のすべての証明書のために失効情報を要求します。サーバー証明書の失効情報のみがクライアントに送信される場合、チェーン内の他の証明書を、証明書失効リスト(CRL)またはクライアント主導型OCSPを使用して検証できます(ただし、クライアントを、これを行うように設定する必要があります)。

TLSによって、サーバーがクライアントの証明書も要求するようにできますが、クライアントが適切なOCSPレスポンダに連絡して、サーバーに送信された証明書に応答をステープリングできるようにする、OCSPステープリングでの用意はできていません。

OCSPの要求と応答

OCSPの要求メッセージと応答メッセージは、通常は、暗号化されていないHTTPを介して送信されます。応答は、CAによって署名されます。

必要な場合は、ステープリングされた応答は、クライアント・コードで、ExtendedSSLSessionオブジェクトでgetStatusResponsesメソッドを呼び出すことで取得できます。メソッド・シグネチャは次のとおりです。

public List<byte[]> getStatusResponses();

OCSP応答は、RFC 6960内のASN.1で示されている形式でDistinguished Encoding Rules (DER)を使用してエンコードされます。

OCSPステープリングを使用するためのJavaクライアントの設定

オンライン証明書ステータス・プロトコル(OCSP)ステープリングは、クライアント側で、システム・プロパティjdk.tls.client.enableStatusRequestExtensionをtrue(そのデフォルト値)に設定することで有効になります。

サーバーから返された証明書にステープリングされたOCSP応答を使用するようにJavaクライアントを構成するには、Javaクライアントを、TLSを使用してサーバーに接続するように設定してある必要があり、サーバーを、TLSハンドシェークの一環でそれが返す証明書にOCSP応答をステープリングするように設定する必要があります。

1. クライアントでOCSPステープリングを有効にします。

必要な場合は、システム・プロパティjdk.tls.client.enableStatusRequestExtensionをtrueに設定します。

2. 失効チェックを有効にします。これを行うには、異なる2通りの方法があります。

- システム・プロパティcom.sun.net.ssl.checkRevocationをtrueに設定します。これは、コマンド行から、またはコードで実行できます。

- PKIXParametersクラスでsetRevocationEnabledメソッドを使用します。PKIXParametersクラスを参照してください。

クライアントで、証明書検証にサーバーから受け取ったステープリング済応答を含める場合は、失効チェックをtrueに設定する必要があります。失効チェックがtrueに設定されていない場合、失効情報の存在またはステータスに関係なく、接続を継続できます。

OCSPステープリングを使用するためのJavaサーバーの設定

オンライン証明書ステータス・プロトコル(OCSP)ステープリングは、サーバーで、システム・プロパティjdk.tls.server.enableStatusRequestExtensionをtrueに設定することで有効になります。(デフォルトでは、falseに設定されています。)

Javaサーバーを、OCSPレスポンダに接続しOCSP応答を証明書にステープリングしてクライアントに返すように構成するには、次の手順を使用します。Javaサーバーを、TLSを使用してクライアントに応答するように設定してある必要があります。

1. サーバーでOCSPステープリングを有効にします。

システム・プロパティjdk.tls.server.enableStatusRequestExtensionをtrueに設定します。

2. オプション: 必要に応じて他のプロパティを設定します。有効なプロパティのリストは、OCSPステープリングの構成プロパティを参照してください。

OCSPステープリングの構成プロパティ

このトピックでは、オンライン証明書ステータス・プロトコル(OCSP)使用時の各種プロパティの設定の効果をリストします。クライアント主導型OCSPとOCSPステープリングの両方で使用されるプロパティを示します。

サーバー側のプロパティ

大部分のプロパティは、SSLContextのインスタンス化時に読み取られます。これは、プロパティを設定した場合に、新しいSSLContextオブジェクトを取得して、そのSSLContextオブジェクトから取得するSSLSocketまたはSSLEngineオブジェクトがそのプロパティ設定を反映するようにする必要があることを意味します。1つの例外は、jdk.tls.stapling.responseTimeoutプロパティです。このプロパティは、ServerHandshakerオブジェクトの作成時に評価されます(基本的に、SSLSocketまたはSSLEngineオブジェクトの作成と同時)。

表8-3 サーバー側のOCSPステープリングのプロパティ

プロパティ

説明

デフォルト値

jdk.tls.server.enableStatusRequestExtension

OCSPステープリングのサーバー側サポートを有効にします。

False

jdk.tls.stapling.responseTimeout

キャッシュから取得するかOCSPレスポンダに連絡することで取得するかに関係なく、サーバーでOCSP応答の取得に使用される最長時間を制御します。

受取済の応答は、該当する場合は実行中のステープリングのタイプに基づいて、CertificateStatusメッセージで送信されます。

5000 (ミリ秒単位の整数値)

jdk.tls.stapling.cacheSize

エントリ内の最大キャッシュ・サイズを制御します。

キャッシュがいっぱいで、新しい応答をキャッシュする必要がある場合は、最も長い間使用されていないキャッシュ・エントリが、新しいものに置き換えられます。このプロパティの値がゼロ以下の場合は、キャッシュに含めることができる応答の数に上限がないことを意味します。

256個のオブジェクト

jdk.tls.stapling.cacheLifetime

キャッシュされた応答の最長存続時間を制御します。

応答にキャッシュ存続時間より早く期限切れになるnextUpdateフィールドがある場合は、応答の存続時間を、このプロパティで設定されている値よりも短くできます。このプロパティの値がゼロ以下の場合は、キャッシュ存続時間が無効になります。オブジェクトにnextUpdate値がなく、キャッシュ存続時間が無効になっている場合、応答はキャッシュされません。

3600秒(1時間)

jdk.tls.stapling.responderURI

TLSのために使用される証明書にAuthority Info Access (AIA)拡張情報がない場合は、管理者がデフォルトURIを設定できるようになります。

jdk.tls.stapling.responderOverrideプロパティが設定されている場合を除き、Authority Info Access拡張情報の値はオーバーライドされません。

未設定

jdk.tls.stapling.responderOverride

jdk.tls.stapling.responderURIプロパティによって提供されるURIで、AIA拡張情報の値をオーバーライドできるようになります。

False

jdk.tls.stapling.ignoreExtensions

status_requestまたはstatus_request_v2 TLS拡張情報で指定されているOCSP拡張情報の転送を無効にします。

False

クライアント側の設定

表8-4 OCSPステープリングで使用されるクライアント側設定

PKIXBuilderParameters

checkRevocationプロパティ

PKIXRevocationChecker

結果

デフォルト

デフォルト

デフォルト

失効チェックは無効です。

デフォルト

True

デフォルト

失効チェックは有効です。[1]

インスタンス化済

デフォルト

デフォルト

失効チェックは有効です。[1]

インスタンス化済

デフォルト

インスタンス化され、PKIXBuilderParametersオブジェクトに追加されています。

失効チェックは有効であり、[1]PKIXRevocationCheckerの設定に従って動作します。

脚注1 ocsp.enableセキュリティ・プロパティがtrueに設定されている場合のみクライアント側のOCSPのフォールバックが起こるということに注意してください。

開発者は、OCSPステープリングによって提供された応答をいくらか柔軟に処理できます。OCSPステープリングによって、証明書パス・チェックおよび失効チェックに関わる現在の方法論が変更されることはありません。これは、クライアントとサーバーの両方でstatus_request拡張情報をアサートし、CertificateStatusメッセージによってOCSP応答を取得し、ユーザーが失効情報またはその不足に柔軟に対応できるようになることを意味します。

呼出し側によってPKIXBuilderParametersが提供されない場合は、失効チェックは無効になります。呼出し側がPKIXBuilderParametersオブジェクトを作成し、setRevocationEnabledメソッドを使用して失効チェックを有効にする場合は、ステープリングされたOCSP応答が評価されます。これは、com.sun.net.ssl.checkRevocationプロパティがtrueに設定されている場合にも当てはまります。

JSSEクラスとインタフェース

セキュアな通信を行うには、接続の両側がSSL対応であることが必要です。JSSE APIの接続のエンドポイント・クラスは、SSLSocketおよびSSLEngineです。図8-4では、SSLSocketとSSLEngineの作成に使用される主なクラスを論理的な順序で並べています。

図8-4 SSLSocketとSSLEngineの作成に使用されるJSSEクラス

SSLSocketはSSLSocketFactoryまたはイン・バウンド接続を受け取るSSLServerSocketによって作成されます。SSLServerSocketはSSLServerSocketFactoryで作成されます。SSLSocketFactoryおよびSSLServerSocketFactoryオブジェクトはどちらもSSLContextで作成されます。SSLEngineは、SSLContextによって直接作成され、アプリケーションに依存してすべての入出力を処理します。

注意:
raw SSLSocketクラスまたはSSLEngineクラスを使用する場合は、データの送信前に必ずピアのクレデンシャルをチェックしてください。JDK 7以降は、エンドポイント識別/検証の手順は、SSL/TLSハンドシェーク中に処理できます。メソッドSSLParameters.setEndpointIdentificationAlgorithmを参照してください。
たとえば、URL内のホスト名は、ピアのクレデンシャル内のホスト名と一致します。ホスト名が検証されない場合、URL不正行為によってアプリケーションが悪用される可能性があります。

JSSEのコア・クラスとインタフェース

コアJSSEクラスは、javax.netパッケージとjavax.net.sslパッケージに含まれています。

SocketFactoryおよびServerSocketFactoryクラス

抽象クラスjavax.net.SocketFactoryは、ソケットの作成に使われます。このクラスのサブクラスは、ソケットの特定のサブクラスを作成し、パブリック・ソケット・レベルの機能を追加するための汎用フレームワークを提供するファクトリです。たとえば、SSLSocketFactory and SSLServerSocketFactoryクラスを参照してください。

javax.net.ServerSocketFactory抽象クラスはSocketFactoryクラスに似ていますが、サーバー・ソケットの作成に特化して使われます。

ソケット・ファクトリは、構築されるソケットに関する様々なポリシーを取得するための簡単な方法であり、ソケットを要求する特別なコード構成を必要としない方法でそれらのソケットを生成します。

- ファクトリとソケットに多相性があるため、種類が異なるファクトリを渡すだけで、種類が異なるソケットに同じアプリケーション・コードを使用できます。

- ソケット構築時に使用するパラメータを使って、ファクトリ自身をカスタマイズできます。たとえば、ファクトリをカスタマイズして、異なるネットワーク・タイムアウトのソケットや、構成済のセキュリティ・パラメータを返すことができます。

- アプリケーションに返されるソケットはjava.net.Socket (またはjavax.net.ssl.SSLSocket)のサブクラスにすることができます。そうすれば、圧縮、セキュリティ、レコード宣言、統計情報収集、ファイアウォール・トンネリングなどの機能の新しいAPIを直接公開できます。

SSLSocketFactoryおよびSSLServerSocketFactoryクラス

javax.net.ssl.SSLSocketFactoryクラスは、セキュアなソケットを作成するファクトリとして動作します。このクラスは、javax.net.SocketFactoryの抽象サブクラスです。

セキュアなソケット・ファクトリは、セキュアなソケットの作成と初期設定の詳細情報をカプセル化します。これには、認証鍵、ピア証明書の検証、使用できる暗号化方式群などが含まれます。

javax.net.ssl.SSLServerSocketFactoryクラスはSSLSocketFactoryクラスに似ていますが、サーバー・ソケットの作成に特化して使われます。

SSLSocketFactoryの取得

次の方法でSSLSocketFactoryを取得できます。

- SSLSocketFactory.getDefault() staticメソッドを呼び出してデフォルトのファクトリを取得します。

- APIパラメータとしてファクトリを受信する。つまり、ソケットを作成する必要があるが、そのソケットの構成方法の詳細に関与しないコードには、クライアントによって呼び出され、ソケットの作成時に使用するSSLSocketFactoryを指定できるSSLSocketFactoryパラメータを持つメソッドを含めることができます(javax.net.ssl.HttpsURLConnectionなど)。

- 動作を指定した新規ファクトリを構築する。

通常、デフォルトのファクトリはサーバー認証だけをサポートするように構成されています。このため、デフォルトのファクトリで作成されたソケットは、一般的なTCPソケット以上にクライアントの情報を漏らすことはありません。

ソケットを作成して使用するクラスの多くは、ソケットの作成方法を詳しく知る必要はありません。パラメータとして渡されたソケット・ファクトリを介してソケットを作成するのは、ソケット構成の詳細を分離し、ソケットを作成して使用するクラスの再利用性を高めるよい方法です。

新しいソケット・ファクトリ・インスタンスを作成するには、独自のソケット・ファクトリ・サブクラスを実装するか、ソケット・ファクトリのファクトリとして動作するクラスをべつに使用します。このようなクラスの1つの例がSSLContextで、これはプロバイダ・ベースの構成クラスとしてJSSE実装に提供されます。

SSLSocketおよびSSLServerSocketクラス

javax.net.ssl.SSLSocketクラスは標準のJava java.net.Socketクラスのサブクラスです。標準的なソケット・メソッドをすべてサポートし、セキュアなソケットに固有のメソッドを追加します。標準的なソケット・メソッドをすべてサポートし、セキュアなソケットに固有のメソッドを追加します。このクラスのインスタンスは、その作成に使用されたSSLContextをカプセル化します。SSLContextクラスを参照してください。ソケット・インスタンスのセキュアなソケット・セッションの作成を管理するAPIもありますが、トラストおよび鍵管理は直接公開されません。

javax.net.ssl.SSLServerSocketクラスはSSLSocketクラスに似ていますが、サーバー・ソケットの作成に特化して使われます。

ピアの不正行為を防止するには、常にSSLSocketに提示されるクレデンシャルを検証してください。符号化方式の選択とリモート・エンティティの検証を参照してください。

注意:
SSLプロトコルとTLSプロトコルは複雑なので、接続時の受信バイトがハンドシェークのデータとアプリケーション・データのどちらなのかを予測し、現在の接続状態にどのような影響を与えるか(処理を中断させることもある)を予測するのは困難です。Oracle JSSEの実装では、SSLSocket.getInputStream()によって取得されたオブジェクトのavailable()メソッドは、SSL接続で正常に復号化されても、アプリケーションではまだ読み込まれていないデータのバイト数を返します。

SSLSocketの取得

SSLSocketのインスタンスは、次のいずれかの方法で取得できます。

- SSLSocketは、そのクラスの複数のcreateSocketメソッドのうちの1つを使用して、SSLSocketFactoryのインスタンスによって作成できます。

- SSLSocketはSSLServerSocketクラスのacceptメソッドを使用して作成できます。

SSLEngineクラス

SSL/TLS/DTLSが利用される機会はますます増えています。広範な計算プラットフォームやデバイスを包含するさまざまなアプリケーションで使用されています。この普及に伴い、アプリケーションのパフォーマンス、拡張性、サイズおよびその他の要件を満たすため、様々な入出力モデルやスレッド・モデルでSSL/TLS/DTLSを使用することが求められています。ブロックおよび非ブロック入出力チャネル、非同期入出力、任意の入力ストリームと出力ストリームおよびバイト・バッファでSSL/TLS/DTLSの使用が求められています。また、数千のネットワーク接続を管理することが必要な、非常に拡張性の高いパフォーマンス重視の環境で使用することも求められています。

Java SEのSSLEngineクラスを使用する入出力トランスポート・メカニズムの抽象により、トランスポートに依存せずにSSL/TLS/DTLSプロトコルをアプリケーションで使用できるようになったため、アプリケーション開発者は最もニーズを満たすトランスポート・モデルや計算モデルを自由に選択できます。この新しい抽象化により、非ブロック入出力チャネルや他の入出力モデルをアプリケーションで使用できるだけでなく、異なるスレッド・モデルにも対応できます。事実上これは、入出力とスレッドの決定がアプリケーション開発者に委ねられることになります。こうした柔軟性のため、アプリケーション開発者は、それ自体が複雑な問題でもある入出力とスレッドを管理するとともに、SSL/TLS/DTLSプロトコルをある程度理解する必要があります。このように、新しい抽象は高度なAPIなので、初心者はSSLSocketを使用してください。

Java Generic Security Services (Java GSS-API)やJava Simple Authentication Security Layer (Java SASL)などの他のJavaプログラミング言語APIのユーザーは、アプリケーションがデータをトランスポートする役割を担うことの類似点に気付くでしょう。

コア・クラスは、javax.net.ssl.SSLEngineです。これは、SSL/TLS/DTLS状態マシンをカプセル化し、SSLEngineクラスのユーザーによって供給されるインバウンドとアウトバウンドのバイト・バッファ上で動作します。図8-5は、アプリケーションから、SSLEngineを経由して、トランスポート・メカニズムまで進み、戻ってくるデータのフローを示しています。

図8-5 SSLEngineを通るデータのフロー

左側に示されるアプリケーションは、アプリケーションのプレーンテキスト・データをアプリケーション・バッファに供給し、それをSSLEngineに渡します。SSLEngineオブジェクトは、バッファに格納されているデータ、またはハンドシェーク・データを処理してSSL/TLS/DTLSエンコード・データを生成し、アプリケーションによって提供されるネットワーク・バッファに格納します。次にアプリケーションは、右側に示されている適切なトランスポートを使用して、ネットワーク・バッファの内容をピアに送信する役割を実行します。トランスポートを介してピアからSSL/TLS/DTLSエンコード・データを受け取ると、アプリケーションはそのデータをネットワーク・バッファに格納し、SSLEngineに渡します。SSLEngineオブジェクトは、ネットワーク・バッファの内容を処理し、ハンドシェーク・データまたはアプリケーション・データを生成します。

SSLEngineクラスのインスタンスは次のいずれかの状態になります。

作成

SSLEngineの作成と初期化は完了しましたが、まだ使用されてはいません。この段階では、アプリケーションにより、SSLEngine固有のあらゆる設定(暗号化方式群の有効化、SSLEngineがクライアント・モードとサーバー・モードのどちらでハンドシェークを行うかなど)を行うことができます。ハンドシェークが始まると、次のハンドシェークから新しい設定(クライアント/サーバー・モードの設定を除く)が使用されます。

初期ハンドシェーク

初期ハンドシェークとは、SSLSessionが確立されるまでの間、2つのピアが通信パラメータを交換する手続きです。この段階では、アプリケーション・データは送信できません。

アプリケーション・データ

通信パラメータが確立され、ハンドシェークが完了すると、SSLEngineからアプリケーション・データが送信されます。アウトバウンド・アプリケーション・メッセージは暗号化され、データの整合性が確保されます。インバウンド・メッセージでは、この逆の手続きが行われます。

再ハンドシェーク

アプリケーション・データ段階の間はいつでも、どちら側のピアからでも、必要に応じてセッションの再ネゴシエーションを要求できます。アプリケーション・データに新しいハンドシェーク・データを混合できます。再ハンドシェークの段階を開始する前に、アプリケーションは、SSL/TLS/DTLS通信パラメータ(例: 有効な暗号化方式群のリスト)や、クライアント認証を使用するかどうかの設定をリセットできます。しかし、クライアント・モードとサーバー・モードを切り替えることはできません。前回と同様に、ハンドシェークが始まってから次のハンドシェークまで、新しいSSLEngine構成設定は使用されません。

修了

接続が不要になったとき、アプリケーションは、SSLEngineを終了し、ピアと送受信するメッセージが残っている場合は送受信を完了してから、配下の転送メカニズムを終了する必要があります。いったん閉じられたエンジンを再利用することはできません。新しいSSLEngineを作成する必要があります。

SSLEngineオブジェクトの作成

SSLContext.createSSLEngine()メソッドを使用してSSLEngineオブジェクトを作成します。

SSLEngineオブジェクトを作成する前に、クライアントまたはサーバーとして動作するようにエンジンを構成し、使用する暗号化方式群やクライアント認証が必要かどうかなどの他の構成パラメータも設定します。SSLContext.createSSLEngineメソッドはjavax.net.ssl.SSLEngineオブジェクトを作成します。

注意:
サーバー名とポート番号は、サーバーとの通信には使用されません(すべてのトランスポートはアプリケーションが担当します)。それらは、SSLセッションキャッシングおよび取得すべきサーバー・クレデンシャルを決定するために、Kerberosベースの暗号化方式群の実装に使用するJSSEプロバイダへのヒントです。

例8-1 キーストアとしてJKSを使用してTLSのためのSSLEngineクライアントを作成するサンプル・コード

次のサンプル・コードでは、キーストアとしてJKSを使用するTLSのためのSSLEngineクライアントを作成します。

import javax.net.ssl.*;
import java.security.*;

// Create and initialize the SSLContext with key material
char[] passphrase = "passphrase".toCharArray();

// First initialize the key and trust material
KeyStore ksKeys = KeyStore.getInstance("JKS");
ksKeys.load(new FileInputStream("testKeys"), passphrase);
KeyStore ksTrust = KeyStore.getInstance("JKS");
ksTrust.load(new FileInputStream("testTrust"), passphrase);

// KeyManagers decide which key material to use
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ksKeys, passphrase);

// TrustManagers decide whether to allow connections
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(ksTrust);

// Get an instance of SSLContext for SSL/TLS protocols
sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

// Create the engine
SSLEngine engine = sslContext.createSSLengine(hostname, port);

// Use as client
engine.setUseClientMode(true);

SSL/TLSデータの生成と処理

2つの主要なSSLEngineメソッドはwrap()とunwrap()です。それらは、それぞれネットワーク・データの生成と消費を担当します。SSLEngineオブジェクトの状態に応じて、このデータはハンドシェーク・データかアプリケーション・データになります。

それぞれのSSLEngineオブジェクトには、存続期間中に数種類の段階があります。アプリケーション・データを送信または受信できるようにするには、SSL/TLSプロトコルで、暗号化パラメータを確立するためのハンドシェークが必要です。このハンドシェークでは、SSLEngineオブジェクトによる一連のやり取りのステップが必要です。ハンドシェーク自体の詳細は、SSLハンドシェークを参照してください。

初期ハンドシェーク時に、wrap()およびunwrap()メソッドはハンドシェーク・データを生成および消費し、アプリケーションはデータのトランスポートを担当します。wrap()およびunwrap()メソッド・シーケンスは、ハンドシェークが終了するまで繰り返されます。それぞれのSSLEngine操作によりSSLEngineResultクラスのインスタンスが生成され、その中のSSLEngineResult.HandshakeStatusフィールドは、ハンドシェークを進めるために実行する必要のある次の動作を決定するために使用されます。

表8-5に、一般的なハンドシェーク時に呼び出されるメソッドのシーケンスと、対応するメッセージおよびステータスを示します。

表8-5 一般的なハンドシェーク

クライアント

SSL/TLSメッセージ

HandshakeStatus

wrap()

ClientHello

NEED_UNWRAP

unwrap()

ServerHello/Cert/ServerHelloDone

NEED_WRAP

wrap()

ClientKeyExchange

NEED_WRAP

wrap()

ChangeCipherSpec

NEED_WRAP

wrap()

Finished

NEED_UNWRAP

unwrap()

ChangeCipherSpec

NEED_UNWRAP

unwrap()

Finished

FINISHED

表8-6に、一般的なSSL/TLSハンドシェーク時の状態マシンと、対応するメッセージおよびステータスを示します。

図8-6 SSL/TLSハンドシェーク中の状態マシン

ハンドシェークの完了時に、wrap()をさらに呼び出すと、アプリケーション・データおよびパッケージを消費してトランスポートしようとします。unwrap()メソッドはその逆を試みます。

ピアにデータを送信するには、まずアプリケーションがSSLEngine.wrap()を介して送信するデータを提供し、対応するSSL/TLSエンコード・データを取得します。次にアプリケーションは、選択したトランスポート・メカニズムを使用してエンコード・データをピアに送信します。アプリケーションは、トランスポート・メカニズムを介してピアからSSL/TLSエンコード・データを受け取ると、SSLEngine.unwrap()を介してそのデータをSSLEngineに送り、ピアによって送信されたプレーン・テキスト・データを取得します。

例8-2 非ブロックSocketChannelの作成のサンプル・コード

次の例では、非ブロックSocketChannelを使用してピアと通信するSSLアプリケーションを示します。これは、例8-1で作成したSSLEngineを使用してエンコードすることにより、ピアに文字列helloを送信します。バイト・バッファの大きさを決定するために、SSLSessionからの情報を使用しています。

注意:
この例は、非ブロックSocketChannelを組み込んだSelectorを使用することにより、堅牢性と拡張性を高めることができます。

// Create a nonblocking socket channel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(hostname, port));

// Complete connection
while (!socketChannel.finishedConnect()) {
// do something until connect completed
}

//Create byte buffers for holding application and encoded data

SSLSession session = engine.getSession();
ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());

// Do initial handshake
doHandshake(socketChannel, engine, myNetData, peerNetData);

myAppData.put("hello".getBytes());
myAppData.flip();

while (myAppData.hasRemaining()) {
// Generate SSL/TLS/DTLS encoded data (handshake or application data)
SSLEngineResult res = engine.wrap(myAppData, myNetData);

// Process status of call
if (res.getStatus() == SSLEngineResult.Status.OK) {
myAppData.compact();

// Send SSL/TLS/DTLS encoded data to peer
while(myNetData.hasRemaining()) {
int num = socketChannel.write(myNetData);
if (num == 0) {
// no bytes written; try again later
}
}
}

// Handle other status:  BUFFER_OVERFLOW, CLOSED
...
}

例8-3 非ブロックSocketChannelからのデータの読取りのサンプル・コード

次のサンプル・コードでは、同じ非ブロックSocketChannelからデータを読み取り、例8-1で作成したSSLEngineを使用して、そのデータからプレーン・テキストを抽出する方法を示しています。このコードが反復されるごとに、ハンドシェーク処理が進行しているかどうかに応じて、プレーン・テキストが生成されたり、生成されなかったりします。

// Read SSL/TLS/DTLS encoded data from peer
int num = socketChannel.read(peerNetData);
if (num == -1) {
// The channel has reached end-of-stream
} else if (num == 0) {
// No bytes read; try again ...
} else {
// Process incoming data
peerNetData.flip();
res = engine.unwrap(peerNetData, peerAppData);

if (res.getStatus() == SSLEngineResult.Status.OK) {
peerNetData.compact();

if (peerAppData.hasRemaining()) {
// Use peerAppData
}
}
// Handle other status:  BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED
...
}

Datagram Transport Layer Security (DTLS)プロトコル

Datagram Transport Layer Security (DTLS)プロトコルは、信頼できる順次のデータ配信を必要とせず提供しない、データグラムでのTLSのトラフィックを構築するように設計されています。Java Secure Socket Extension (JSSE) APIおよびSunJSSEセキュリティ・プロバイダでは、DTLSプロトコルがサポートされています。

TLSはTCPなどの透過的な信頼できるトランスポート・チャネルを必要とするため、信頼できないデータグラム・トラフィックの保護には使用できないためです。DTLSは、TLSのデータグラム互換型です。

JSSE APIでは、Secure Socket Layer (SSL)プロトコルとTransport Layer Security (TLS)プロトコルとともにDTLSバージョン1.0とDTLSバージョン1.2がサポートされるようになりました。

javax.net.ssl.SSLEngineプログラミング・モデルは、DTLS用JSSE APIで使用されます。

DTLSハンドシェーク

アプリケーション・データを送信または受信できるようにするには、DTLSプロトコルで、暗号化パラメータを確立するためのハンドシェークが必要です。このハンドシェークでは、SSLEngineオブジェクトによる、クライアントとサーバーとの間の一連のメッセージのやり取りが必要です。

DTLSハンドシェークでは、すべてのメッセージが正しく受け取られる必要があります。したがって、信頼できないデータグラム・トラフィックでは、不足または遅延しているパケットを再送信する必要があります。javax.net.ssl.SSLEngineは入出力操作を担当しないため、アプリケーションが、タイマーを提供し、再送信が必要なときにSSLEngineに知らせる役目を担います。アプリケーションのためにタイマーおよび再送信の戦略を実装することが重要です。DTLS接続での再送信の処理を参照してください。

DTLSハンドシェークには、次のステージがあります。

1. 暗号化方式群のネゴシエーション
DTLSセッションは、どの暗号群を使用するかについて、クライアントとサーバーがネゴシエーションを行うことから始まります。暗号化方式群とは、コンピュータがデータを暗号化するために使用する暗号化アルゴリズムと鍵のサイズのセットです。符号化方式には、公開鍵交換アルゴリズムまたは鍵合意アルゴリズム、および暗号化ハッシュ関数に関する情報が含まれます。クライアントは利用できる暗号群をサーバーに伝え、サーバーは、どちらにも適用できる暗号群を選択します。

Cookieは、サービス妨害攻撃(DoS)を防ぐために、暗号群とともに、クライアントとサーバーとの間ので交換されます。

2. (オプション)サーバーの識別情報の認証(オプション)
認証ステップはオプションです。しかし、Web上の電子商取引の例では、クライアントがサーバーを認証します。サーバーの認証により、サーバーが表すとクライアントが信じているエンティティを、そのサーバーが実際に表していることをクライアントが確認できます。

サーバーは、自らが表すと唱える組織に属していることを証明するため、クライアントに公開鍵証明書を提示します。この証明書が有効であれば、クライアントはサーバーの識別情報について確信できます。

クライアントとサーバーは、同じ秘密鍵について同意できる情報を交換します。たとえば、RSAを使う場合、クライアントは公開鍵証明書で取得したサーバーの公開鍵を使用して、秘密鍵情報を暗号化します。クライアントは暗号化された秘密鍵情報をサーバーに送信します。復号化にはサーバーの非公開鍵が必要なので、サーバーでだけ、このメッセージを復号化できます。

3. 暗号化メカニズムの合意
クライアントとサーバーは、同じ秘密鍵にアクセスします。それぞれのメッセージでは、ハンドシェークの最初のステップで選択した暗号化ハッシュ関数と、共有された秘密情報を使用して、メッセージに添付されるHMACを計算します。次に、秘密鍵と、ハンドシェークの最初のステップでネゴシエーションされた秘密鍵アルゴリズムを使用し、セキュアなデータとHMACを暗号化します。そのあと、クライアントとサーバーは、暗号化されハッシュ化されたデータを使ってセキュアに通信することができます。

DTLSハンドシェークのメッセージの交換

DTLSハンドシェークでは、SSLEngineオブジェクトによって、クライアントとサーバーとの間で一連の送受信メッセージが交換されます。

図8-7に、DTLSハンドシェークで交換される一連のメッセージを示します。特定の状況下でだけ送信されるメッセージには「optional」と記されています。各メッセージをこの図の後に説明します。

DTLSハンドシェーク・メッセージの詳細は、DTLSバージョン1.0およびDTLSバージョン1.2を参照してください。

図8-7 DTLSハンドシェーク

次のハンドシェーク・メッセージは、DTLSハンドシェーク中にクライアントとサーバーとの間で交換されます。

1. ClientHello:
クライアントは、それがサポートする最上位バージョンのDTLSおよび暗号化方式群のリストを含むサーバー情報を送信します。暗号化方式群の情報には、暗号化アルゴリズムと鍵のサイズが含まれます。

2. HelloVerifyRequest:
サーバーは、CookieとともにクライアントからのClientHelloメッセージに応答します。

3. ClientHello:
クライアントは、それがサポートする最上位バージョンのDTLSおよび暗号化方式群のリストとともに、2つ目のClientHelloメッセージをサーバーに送信します。HelloVerifyRequestで受け取ったCookieは、サーバーに送り返されます。

4. ServerHello:
サーバーは、クライアントとサーバーの両方がサポートする最上位バージョンのDTLSと最適な暗号化方式群を選択し、この情報をクライアントに送信します。

5. (オプション) Certificate:
サーバーはクライアントに証明書または証明書チェーンを送信します。証明書チェーンは通常、サーバーの公開鍵証明書で始まり、認証局のルート証明書で終わります。このメッセージはオプションで、サーバー認証を求められた場合に使用します

6. (オプション) CertificateRequest:
サーバーは、クライアントを認証する必要がある場合、クライアントに証明書要求を送信します。インターネット・アプリケーションでは、このメッセージが使われることはほとんどありません。

7. (オプション) ServerKeyExchange:
Certificateからの公開鍵情報が鍵交換を行うのに不十分な場合、サーバーはクライアントにサーバー鍵交換メッセージを送信します。たとえば、Diffie-Hellman (DH)に基づく暗号化方式群では、このメッセージにはサーバーのDH公開鍵が含まれています。

8. ServerHelloDone:
サーバーは、最初のネゴシエーション・メッセージを終了したことをクライアントに伝えます。

9. (オプション) Certificate:
サーバーがクライアントに証明書を要求すると、クライアントはサーバーが前に行ったように、その証明書チェーンを送信します。

注意:
クライアントに証明書を要求するのは、ごく一部のインターネット・サーバー・アプリケーションのみです。

10. ClientKeyExchange:
クライアントは、対称暗号化で使用する鍵を作成するために使用される情報を生成します。RSAでは、クライアントはサーバーの公開鍵でこの鍵情報を暗号化してサーバーに送信します。DHに基づく暗号化方式群の場合、このメッセージにはクライアントのDH公開鍵が含まれています。

11. (オプション) CertificateVerify:
このメッセージは、上述のとおりクライアントが証明書を提示する場合にクライアントによって送信されます。このメッセージは、サーバーにクライアントの認証処理を完了させるためのものです。このメッセージが使用されると、クライアントは暗号化ハッシュ関数で電子的に署名した情報を送信します。サーバーがクライアントの公開鍵でこの情報を復号化すれば、サーバーはクライアントを認証できます。

12. ChangeCipherSpec:
クライアントは、新しくネゴシエーションされたCipherSpecおよび鍵によって後続のデータが保護されることと、そのデータが暗号化されることをサーバーに伝えるメッセージを送信します。

13. Finished:
クライアントはサーバーに、セキュアなデータ通信を開始する準備ができたことを伝えます。

14. ChangeCipherSpec:
サーバーは、新しくネゴシエーションされたCipherSpecおよび鍵によって後続のデータが保護されることと、そのデータが暗号化されることをクライアントに伝えるメッセージを送信します。

15. Finished:
サーバーはクライアントに、セキュアなデータ通信を開始する準備ができたことを伝えます。これでDTLSハンドシェークが終了します。

ハンドシェークの再実行(再ネゴシエーション)

初期のハンドシェークが完了してアプリケーション・データが流れているとき、いずれの側からでも新しいハンドシェークをいつでも開始できます。特に重要な操作について、アプリケーションで強力な暗号化方式群を使用したり、サーバー・アプリケーションでクライアント認証が必要になる場合もあります。

理由は何であれ、新しいハンドシェークが既存の暗号化セッションに置き換わり、新しいセッションが確立されるまで、アプリケーション・データとハンドシェーク・メッセージが交互に配置されます。

アプリケーションでSSLEngine.beginHandshake()メソッドを使用して、新しいハンドシェークを開始できます。

注意:
再ネゴシエーションに関するプロトコルの問題が2009年に見つかりました。プロトコルおよびJava SE実装はいずれも修正されています。Transport Layer Security (TLS)再ネゴシエーションの問題を参照してください。

例8-4 DTLSハンドシェーク・ステータスおよび全体的ステータスの処理のサンプル・コード

void handshake(SSLEngine engine, DatagramSocket socket, SocketAddress peerAddr) throws Exception {
boolean endLoops = false;
// private static int MAX_HANDSHAKE_LOOPS = 60;
int loops = MAX_HANDSHAKE_LOOPS;
engine.beginHandshake();
while (!endLoops && (serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException("Too many loops to produce handshake packets");
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
ByteBuffer iNet;
ByteBuffer iApp;
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
// Receive ClientHello request and other SSL/TLS/DTLS records
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
socket.receive(packet);
} catch (SocketTimeoutException ste) {
// Retransmit the packet if timeout
List <Datagrampacket> packets = onReceiveTimeout(engine, peerAddr);
for (DatagramPacket p : packets) {
socket.send(p);
}
continue;
}
iNet = ByteBuffer.wrap(buf, 0, packet.getLength());
iApp = ByteBuffer.allocate(1024);
} else {
iNet = ByteBuffer.allocate(0);
iApp = ByteBuffer.allocate(1024);
}
SSLEngineResult r = engine.unwrap(iNet, iApp);
SSLEngineResult.Status rs = r.getStatus();
hs = r.getHandshakeStatus();
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// The client maximum fragment size config does not work?
throw new Exception("Buffer overflow: " +
"incorrect client maximum fragment size");
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
// Bad packet, or the client maximum fragment size
// config does not work?
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Buffer underflow: " +
"incorrect client maximum fragment size");
} // Otherwise, ignore this packet
} else if (rs == SSLEngineResult.Status.CLOSED) {
endLoops = true;
} // Otherwise, SSLEngineResult.Status.OK
if (rs != SSLEngineResult.Status.OK) {
continue;
}
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
// Call a function to produce handshake packets
List <DatagramPacket> packets = produceHandshakePackets(engine, peerAddr);
for (DatagramPacket p : packets) {
socket.send(p);
}
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
runDelegatedTasks(engine);
} else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
// OK, time to do application data exchange
endLoops = true;
} else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
endLoops = true;
}
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Not ready for application data yet");
}
}

DTLS接続での再送信の処理

信頼できる接続でのSSL/TLSでは、データは正しい順序で到着することが保証されており、再送信は不要です。ただし、DTLSの場合は、信頼できないメディアで機能することが多く、不足または遅延しているハンドシェーク・メッセージを再送信する必要があります。

SSLEngineクラスはトランスポートにまったく依存しない方法で動作し、アプリケーション層がすべての入出力を実行します。SSLEngineクラスは入出力を担当しないため、かわりにアプリケーションが、タイマーの提供、および再送信が必要なときのSSLEngineへの通知を担当します。アプリケーション層は、正しいタイムアウト値、およびタイムアウト・イベントをトリガーする時期を決定する必要があります。ハンドシェーク中にSSLEngineオブジェクトがHandshakeStatus.NEED_UNWRAP状態の場合、SSLEngine.wrap()の呼出しは、前のパケットが失われ、再送信する必要があることを意味します。そのような場合、SSLEngineクラスのDTLS実装は、必要に応じて前の必要なハンドシェーク・メッセージをラップしなおす役割を担います。

注意:
DTLSエンジンでは、ハンドシェーク・メッセージのみを正しく交換する必要があります。アプリケーション・データは、タイマーを必要とせずにパケット損失に対応できます。

アプリケーションでの再送信の処理

SSLEngine.unwrap()およびSSLEngine.wrap()は、アプリケーションで再送信を処理するために一緒に使用できます。

図8-8では、DTLSハンドシェークの再送信を処理する一般的なシナリオを示します。

図8-8 DTLSハンドシェークの再送信の状態のフロー

1. DTLS SSLEngineのインスタンスを作成し初期化します。
SSLEngineオブジェクトの作成を参照してください。DTLSハンドシェークの処理が開始します。DTLSハンドシェークを参照してください。

2. ハンドシェークのステータスがHandshakeStatus.NEED_UNWRAPである場合は、ネットワークからのデータを待機します。

3. タイマーがタイムアウトした場合、これは、前に配信されたハンドシェーク・メッセージが失われた可能性があることを示します。

注意:
DTLSハンドシェークの再送信では、SSLEngine.wrap()の呼出しに関する判定されるハンドシェーク・ステータスは、必ずしもHandshakeStatus.NEED_WRAPではありません。

4. SSLEngine.wrap()を呼び出します。

5. ラップされたパケットが配信されます。

アプリケーションでのバッファ済ハンドシェーク・メッセージの処理

データグラム・トランスポートでは、信頼できる順次のデータ配信は必要なく、提供されません。ハンドシェーク・メッセージは、失われることや、並替えが必要なことがあります。DTLS実装では、前のすべてのメッセージを受け取る前に、今後の処理のためにハンドシェーク・メッセージをバッファする必要があることがあります。

SSLEngineのDTLS実装は、ハンドシェーク・メッセージを並べ替える役割を担います。ハンドシェーク・メッセージのバッファリングと並替えは、アプリケーションに対して透過的です。

ただし、アプリケーションは、HandshakeStatus.NEED_UNWRAP_AGAINステータスを管理する必要があります。このステータスは、次のSSLEngine.unwrap()操作にはリモート側からのその他のデータが必要ないことを示します。

図8-9では、HandshakeStatus.NEED_UNWRAP_AGAINの使用の一般的なシナリオを示します。

図8-9 NEED_UNWRAP_AGAINでのDTLSバッファ済ハンドシェークの状態マシン

1. DTLS SSLEngineのインスタンスを作成し初期化します。
SSLEngineオブジェクトの作成を参照してください。DTLSハンドシェークの処理が開始します。DTLSハンドシェークを参照してください。

2. オプション: ハンドシェークのステータスがHandshakeStatus.NEED_UNWRAPである場合は、ネットワークからのデータを待機します。

3. オプション: ネットワーク・データを受け取った場合は、SSLEngine.unwrap()を呼び出します。

4. 次の処理のハンドシェーク・ステータスを判定します。ハンドシェーク・ステータスは、HandshakeStatus.NEED_UNWRAP_AGAIN、HandshakeStatus.NEED_UNWRAPまたはHandshakeStatus.NEED_WRAPである可能性があります。

- ハンドシェーク・ステータスがHandshakeStatus.NEED_UNWRAP_AGAINである場合は、SSLEngine.unwrap()を呼び出します。

注意:
HandshakeStatus.NEED_UNWRAP_AGAINステータスの場合、SSLEngine.unwrap()操作のためにネットワークからのその他のデータは必要ありません。

5. さらに次の処理のハンドシェーク・ステータスを判定します。ハンドシェーク・ステータスは、HandshakeStatus.NEED_UNWRAP_AGAIN、HandshakeStatus.NEED_UNWRAPまたはHandshakeStatus.NEED_WRAPである可能性があります。

DTLSのためのSSLEngineオブジェクトの作成

次の例では、DTLSのためのSSLEngineオブジェクトの作成方法を示します。

注意:
サーバー名とポート番号は、サーバーとの通信には使用されません(すべてのトランスポートはアプリケーションが担当します)。それらは、DTLSセッション・キャッシングのためや、取得する必要があるサーバー・クレデンシャルを決定するためのKerberosベースの暗号化方式群の実装のために使用するJSSEプロバイダの手掛かりとなります。

例8-5 キーストアとしてPKCS12を使用してDTLSのためのSSLEngineクライアントを作成するサンプル・コード

次のサンプル・コードでは、キーストアとしてPKCS12を使用するDTLSのためのSSLEngineクライアントを作成します。

import javax.net.ssl.*;
import java.security.*;

// Create and initialize the SSLContext with key material
char[] passphrase = "passphrase".toCharArray();

// First initialize the key and trust material
KeyStore ksKeys = KeyStore.getInstance("PKCS12");
ksKeys.load(new FileInputStream("testKeys"), passphrase);
KeyStore ksTrust = KeyStore.getInstance("PKCS12");
ksTrust.load(new FileInputStream("testTrust"), passphrase);

// KeyManagers decide which key material to use
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ksKeys, passphrase);

// TrustManagers decide whether to allow connections
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(ksTrust);

// Get an instance of SSLContext for DTLS protocols
sslContext = SSLContext.getInstance("DTLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

// Create the engine
SSLEngine engine = sslContext.createSSLengine(hostname, port);

// Use engine as client
engine.setUseClientMode(true);

例8-6 キーストアとしてPKCS12を使用してDTLSのためのSSLEngineサーバーを作成するサンプル・コード

次のサンプル・コードでは、キーストアとしてPKCS12を使用するDTLSのためのSSLEngineサーバーを作成します。

import javax.net.ssl.*;
import java.security.*;

// Create and initialize the SSLContext with key material
char[] passphrase = "passphrase".toCharArray();

// First initialize the key and trust material
KeyStore ksKeys = KeyStore.getInstance("PKCS12");
ksKeys.load(new FileInputStream("testKeys"), passphrase);
KeyStore ksTrust = KeyStore.getInstance("PKCS12");
ksTrust.load(new FileInputStream("testTrust"), passphrase);

// KeyManagers decide which key material to use
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ksKeys, passphrase);

// TrustManagers decide whether to allow connections
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(ksTrust);

// Get an SSLContext for DTLS Protocol without authentication
sslContext = SSLContext.getInstance("DTLS");
sslContext.init(null, null, null);

// Create the engine
SSLEngine engine = sslContext.createSSLeEngine(hostname, port);

// Use the engine as server
engine.setUseClientMode(false);

// Require client authentication
engine.setNeedClientAuth(true);

DTLSデータの生成と処理

DTLSハンドシェークとSSL/TLSハンドシェークでは、データの生成と処理が同様に行われます。(SSL/TLSデータの生成と処理を参照。)それらは両方とも、それぞれSSLEngine.wrap()メソッドとSSLEngine.wrap()メソッドを使用してネットワーク・データを生成および使用します。

次の図では、一般的なDTLSハンドシェーク時の状態マシンと、対応するメッセージおよびステータスを示します。

図8-10 DTLSハンドシェーク中の状態マシン

SSL/TLSとDTLSのSSLEngine.wrap()メソッドの違い

DTLS用のSSLEngine.wrap()メソッドは、SSL/TLSとは次のように異なります。

- SSLEngineのSSL/TLS実装では、SSLEngine.wrap()の出力バッファに、1つ以上のTLSレコードが含まれます(TLSv1 BEAST暗号ブロック連鎖の脆弱性が原因)。

- SSLEngineのDTLS実装では、すべてのDTLSレコードをマーシャリングしデータグラム層に個別に配信できるようにSSLEngine.wrap()の出力バッファに最大で1つのレコードが含まれます。

注意:
SSLEngine.wrap()によって生成された各レコードは、SSLParameters.getMaximumPacketSize()で指定された最大パケット・サイズ制限に従っている必要があります。

SSLEngineの操作のステータスについて

SSLEngineのステータスは、SSLEngineResult.Statusによって表されます。

エンジンのステータスとアプリケーションの取る必要があるアクションを示すため、SSLEngine.wrap()メソッドとSSLEngine.unwrap()メソッドは、例8-2で示すようなSSLEngineResultインスタンスを返します。このSSLEngineResultオブジェクトには、エンジンの全体的なステータスとハンドシェークのステータスの2つのステータス情報が格納されます。

全体的なステータスは、SSLEngineResult.Status enumによって表されます。次のステータスがあります。

OK

エラーはありませんでした。

CLOSED

操作によってSSLEngineが閉じられたか、操作はすでに閉じられていたために完了できませんでした。

BUFFER_UNDERFLOW

入力バッファのデータが不十分で、アプリケーションがピアから追加のデータを取得する(ネットワークから追加のデータを読み取ることによってなど)必要があることを示しています。

BUFFER_OVERFLOW

出力バッファに結果を保持するためのスペースが不足しており、アプリケーションは目的のバッファをクリアするか大きくする必要があることを示しています。

例8-7は、SSLEngine.unwrap()メソッドのBUFFER_UNDERFLOWおよびBUFFER_OVERFLOWステータスの処理方法を示しています。SSLSession.getApplicationBufferSize()およびSSLSession.getPacketBufferSize()を使用して、バイト・バッファのサイズを決定します。

ハンドシェークのステータスは、SSLEngineResult.HandshakeStatus enumによって表されます。それらは、ハンドシェークが完了しているかどうか、発信側はピアから追加のハンドシェーク・データを取得する必要があるかどうか、またピアに追加のハンドシェーク・データを送信する必要があるかどうかなどを表します。使用可能なステータスは次のとおりです。

FINISHED

SSLEngineがハンドシェークを完了したところです。

NEED_TASK

SSLEngineがハンドシェークを続行するには、事前に1つ(または複数の)委譲されたタスクの結果が必要です。

NEED_UNWRAP

SSLEngineがハンドシェークを続行するには、事前にリモート側からデータを受信する必要があります。

NEED_UNWRAP_AGAIN

SSLEngineは、ハンドシェークを続行するにはアンラップする必要があります。この値は、まだ解釈されていないデータをリモート側から以前に受け取っており再度受け取る必要がないことを示しています。このデータはJSSEフレームワークに受信されていますが、まだ処理されていません。

NEED_WRAP

SSLEngineがハンドシェークを続行するには、事前にリモート側にデータを送信する必要があるため、SSLEngine.wrap()を呼び出すようにしてください。

NOT_HANDSHAKING

SSLEngineは現在ハンドシェークしていません。

結果ごとに2つのステータスがあることにより、SSLEngineは、アプリケーションがハンドシェークへの応答におけるアクションとwrap()およびunwrap()メソッドの全体のステータスを表すアクションという2つのアクションを取る必要があることを示すことができます。たとえばエンジンは、1回のSSLEngine.unwrap()呼出しの結果として、SSLEngineResult.Status.OKを返すことで、入力データが正常に処理されたことを示し、SSLEngineResult.HandshakeStatus.NEED_UNWRAPを返すことで、ハンドシェークを継続するためにアプリケーションがピアからさらにSSL/TLS/DTLSエンコード・データを取得し、もう一度SSLEngine.unwrap()に供給する必要があることを示します。お気付きのとおり、先の例はかなり単純化されていますが、これらすべてのステータスを適正に処理するにはコードをかなり拡張する必要があります。

例8-9と例8-8に、wrap()メソッドおよびunwrap()メソッドのハンドシェーク・ステータスと全体のステータスをチェックすることによってハンドシェーク・データを処理する方法を示します。

例8-7 BUFFER_UNDERFLOWおよびBUFFER_OVERFLOWの処理のサンプル・コード

次のコード例では、BUFFER_UNDERFLOWステータスとBUFFER_OVERFLOWステータスを処理する方法を示します。

SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
switch (res.getStatus()) {

case BUFFER_OVERFLOW:
// Maybe need to enlarge the peer application data buffer.
if (engine.getSession().getApplicationBufferSize() > peerAppData.capacity()) {
// enlarge the peer application data buffer
} else {
// compact or clear the buffer
}
// retry the operation
break;

case BUFFER_UNDERFLOW:
// Maybe need to enlarge the peer network packet buffer
if (engine.getSession().getPacketBufferSize() > peerNetData.capacity()) {
// enlarge the peer network packet buffer
} else {
// compact or clear the buffer
}
// obtain more inbound network data and then retry the operation
break;

// Handle other status: CLOSED, OK
// ...
}

例8-8 ハンドシェーク・ステータスと全体のステータスの確認と処理のサンプル・コード

次のコード例では、wrap()メソッドとunwrap()メソッドのハンドシェーク・ステータスと全体のステータスをチェックすることによって、ハンドシェーク・データを処理する方法を示します。

void doHandshake(SocketChannel socketChannel, SSLEngine engine,
ByteBuffer myNetData, ByteBuffer peerNetData) throws Exception {

// Create byte buffers to use for holding application data
int appBufferSize = engine.getSession().getApplicationBufferSize();
ByteBuffer myAppData = ByteBuffer.allocate(appBufferSize);
ByteBuffer peerAppData = ByteBuffer.allocate(appBufferSize);

// Begin handshake
engine.beginHandshake();
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();

// Process handshaking message
while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {

switch (hs) {

case NEED_UNWRAP:
// Receive handshaking data from peer
if (socketChannel.read(peerNetData) < 0) {
// The channel has reached end-of-stream
}

// Process incoming handshaking data
peerNetData.flip();
SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
peerNetData.compact();
hs = res.getHandshakeStatus();

// Check status
switch (res.getStatus()) {
case OK :
// Handle OK status
break;

// Handle other status: BUFFER_UNDERFLOW, BUFFER_OVERFLOW, CLOSED
// ...
}
break;

case NEED_WRAP :
// Empty the local network packet buffer.
myNetData.clear();

// Generate handshaking data
res = engine.wrap(myAppData, myNetData);
hs = res.getHandshakeStatus();

// Check status
switch (res.getStatus()) {
case OK :
myNetData.flip();

// Send the handshaking data to peer
while (myNetData.hasRemaining()) {
socketChannel.write(myNetData);
}
break;

// Handle other status:  BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED
// ...
}
break;

case NEED_TASK :
// Handle blocking tasks
break;

// Handle other status:  // FINISHED or NOT_HANDSHAKING
// ...
}
}

// Processes after handshaking
// ...
}

例8-9 DTLSハンドシェーク・ステータスおよび全体的ステータスの処理のサンプル・コード

次のコード例では、DTLSハンドシェーク・ステータスを処理する方法を示します。

void handshake(SSLEngine engine, DatagramSocket socket,
SocketAddress peerAddr) throws Exception {
boolean endLoops = false;
// private static int MAX_HANDSHAKE_LOOPS = 60;
int loops = MAX_HANDSHAKE_LOOPS;
engine.beginHandshake();
while (!endLoops && (serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException("Too many loops to produce handshake packets");
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
ByteBuffer iNet;
ByteBuffer iApp;
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
// receive ClientHello request and other SSL/TLS/DTLS records
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
socket.receive(packet);
} catch (SocketTimeoutException ste) {
// retransmit the packet if timeout
List <Datagrampacket> packets =
onReceiveTimeout(engine, peerAddr);
for (DatagramPacket p : packets) {
socket.send(p);
}
continue;
}
iNet = ByteBuffer.wrap(buf, 0, packet.getLength());
iApp = ByteBuffer.allocate(1024);
} else {
iNet = ByteBuffer.allocate(0);
iApp = ByteBuffer.allocate(1024);
}
SSLEngineResult r = engine.unwrap(iNet, iApp);
SSLEngineResult.Status rs = r.getStatus();
hs = r.getHandshakeStatus();
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// the client maximum fragment size config does not work?
throw new Exception("Buffer overflow: " +
"incorrect client maximum fragment size");
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
// bad packet, or the client maximum fragment size
// config does not work?
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Buffer underflow: " +
"incorrect client maximum fragment size");
} // otherwise, ignore this packet
} else if (rs == SSLEngineResult.Status.CLOSED) {
endLoops = true;
}   // otherwise, SSLEngineResult.Status.OK:
if (rs != SSLEngineResult.Status.OK) {
continue;
}
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
List <DatagramPacket> packets =
// Call a function to produce handshake packets
produceHandshakePackets(engine, peerAddr);
for (DatagramPacket p : packets) {
socket.send(p);
}
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
runDelegatedTasks(engine);
} else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
// OK, time to do application data exchange.
endLoops = true;
} else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
endLoops = true;
}
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Not ready for application data yet");
}
}

ブロック・タスクの処理

ハンドシェーク時に、SSLEngineはブロックしたり処理に長い時間がかかったりするタスクに直面することがあります。たとえば、TrustManagerは、リモート証明書検証サービスに接続する必要があることがあります。またはKeyManagerは、クライアント認証の一環として使用する証明書を決定するようにユーザーに求める必要があることがあります。SSLEngineの非ブロック性を保持するため、エンジンはそれらのタスクに直面した場合にSSLEngineResult.HandshakeStatus.NEED_TASKを返します。このステータスを受け取ると、アプリケーションはSSLEngine.getDelegatedTask()を呼び出してタスクを取得し、その要件に適したスレッド・モデルを使用してタスクを処理すべきです。アプリケーションは、たとえばスレッド・プールからスレッドを入手してタスクを処理し、メイン・スレッドは他の入出力を処理できます。

次のコードは、新しく作成されたスレッドで各タスクを実行します。

if (res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
new Thread(task).start();
}
}

SSLEngineは、未処理のタスクすべてが完了するまで、将来のwrap()およびunwrap()呼出しをブロックします。

SSL/TLS/DTLS接続の停止

SSL/TLS/DTLS接続を正しい順序で停止するため、SSL/TLS/DTLSプロトコルではクローズ・メッセージを送信する必要があります。したがって、アプリケーションがSSL/TLS/DTLS接続を終了するときは、最初にSSLEngineからクローズ・メッセージを取得し、そのトランスポート・メカニズムを使用してピアに送信して、最後にトランスポート・メカニズムを停止する必要があります。例8-10に、これを示します。

SSLEngineを明示的に閉じるアプリケーションの他に、SSLEngineは、ピアによって(ハンドシェーク・データを処理している間のクローズ・メッセージの受取りによる)、またはアプリケーション・データやハンドシェーク・データを処理している間に、SSLExceptionをスローすることによって示される、エラーが発生しているSSLEngineによって閉じられることがあります。そのような場合、アプリケーションはSSLEngine.wrap()を呼び出してクローズメッセージを取得し、SSLEngine.isOutboundDone()がtrueを返すまで(例8-10に示すように)、またはSSLEngineResult.getStatus()がCLOSEDを返すまで、それをピアに送信する必要があります。

正常なシャットダウンに加え、クローズメッセージが交換される前にトランスポート・リンクを切断する非常シャットダウンもあります。前の例では、アプリケーションは非ブロックSocketChannelからの読取りを実行しようとすると-1またはIOExceptionを受け取り、非ブロックSocketChannelへの書込みを実行しようとするとIOExceptionを受け取ります。入力データの最後に達したら、engine.closeInbound()を呼び出して、SSLEngineによって、リモートのピアがSSL/TLS/DTLSの観点から正常に閉じたことを検証する必要があります。次に、アプリケーションが例8-10の手順を使用して正常な停止を試みる必要があります。SSLSocketと異なり、SSLEngineを使用するアプリケーションは、多くの状態移行、ステータスおよびプログラミングを処理する必要があります。SSLEngineの使用を表すサンプル・コードを参照してください。

例8-10 SSL/TLS/DTLS接続の停止のサンプル・コード

次のコード例では、SSL/TLS/DTLS接続の停止方法を示します。

// Indicate that application is done with engine
engine.closeOutbound();

while (!engine.isOutboundDone()) {
// Get close message
SSLEngineResult res = engine.wrap(empty, myNetData);

// Check res statuses

// Send close message to peer
while(myNetData.hasRemaining()) {
int num = socketChannel.write(myNetData);
if (num == 0) {
// no bytes written; try again later
}
myNetData().compact();
}
}

// Close transport
socketChannel.close();

SSLSessionとExtendedSSLSession

javax.net.ssl.SSLSessionインタフェースは、SSLSocketまたはSSLEngine接続の2つのピアの間でネゴシエーションされたセキュリティ・コンテキストを表します。一度確立されたセッションは、同じ2つのピアの間で接続されるそのあとのSSLSocketまたはSSLEngineオブジェクトによっても共有できます。

場合によっては、ハンドシェーク中に取り決めたパラメータが、後のハンドシェークでトラストについて判断を下す際に必要になることもあります。たとえば、有効な署名アルゴリズムのリストによって、認証に使用できる証明書タイプが制限されることがあります。SSLSessionはハンドシェーク時に、SSLSocketまたはSSLEngineのgetHandshakeSession()を呼び出すことによって取得できます。TrustManagerまたはKeyManagerの実装により、getHandshakeSession()メソッドを使用して、それらが判断を下す際に役立つセッション・パラメータに関する情報を取得できます。

完全に初期化されたSSLSessionには暗号化方式群が含まれ、これは、リモート・ピアのネットワーク・アドレスに関する権限のないヒントと同様、セキュアなソケットの通信でも使用され、作成や最後の使用の時点などで、管理情報としても使用されます。セッションには、SSLSocketまたはSSLEngine接続による通信を暗号化して整合性を保証する暗号鍵を作成するために使用される、ピア間でネゴシエーションされた共用マスターとなる秘密も含まれます。このマスターとなる秘密の値は、基盤となるセキュアなソケット実装のみに伝えられ、SSLSession APIによって公開されません。

ExtendedSSLSessionは、追加のセッション属性をサポートするようにSSLSessionインタフェースを拡張します。ExtendedSSLSessionクラスは、ローカル実装およびピアによってサポートされる署名アルゴリズムを記述するメソッドを追加します。ExtendedSSLSessionインスタンスで呼び出されるgetRequestedServerNames()メソッドは、要求されたServer Name Indication (SNI)拡張でSNIServerNameオブジェクトのリストを取得するために使用されます。サーバーは、要求されたサーバー名を使用して、適切な認証証明書の選択やセキュリティ・ポリシーのその他の側面をガイドする必要があります。クライアントは、要求されたサーバー名を使用して、ピアの識別情報のエンドポイントの識別やセキュリティ・ポリシーのその他の側面をガイドする必要があります。

SSLSessionでのgetPacketBufferSize()およびgetApplicationBufferSize()メソッドの呼出しは、SSLEngineによって使用される適切なバッファ・サイズを決定するために使用します。

注意:
SSL/TLSプロトコルは、実装が最大16Kバイト(KB)のプレーン・テキストを含むパケットを生成することを指定します。ただし、一部の実装はこの指定に違反し、32Kバイトまでの大きいレコードを生成します。SSLEngine.unwrap()コードが大きいインバウンド・パケットを検出した場合、SSLSessionから返されるバッファ・サイズは動的に更新されます。アプリケーションは常に、必要に応じてBUFFER_OVERFLOWおよびBUFFER_UNDERFLOWのステータスをチェックして、対応するバッファを拡大するべきです。SSLEngineの操作のステータスについてを参照してください。SunJSSE初値に標準に準拠した16Kバイトのレコードを送信し、32Kバイトの着信レコードを許可します。回避策については、「JSSEのカスタマイズ」のシステム・プロパティjsse.SSLEngine.acceptLargeFragmentsを参照してください。

HttpsURLConnectionクラス

javax.net.ssl.HttpsURLConnectionはjava.net.HttpURLConnectionクラスを拡張し、HTTPS固有の機能のサポートを追加します。

HTTPSプロトコルはHTTPプロトコルに似ていますが、データを要求または受信する前に、まずSSL/TLSソケットを利用してセキュアなチャネルを確立してピアの識別情報を検証します(符号化方式の選択とリモート・エンティティの検証を参照)。javax.net.ssl.HttpsURLConnectionはjava.net.HttpURLConnectionクラスを拡張し、HTTPS固有の機能のサポートを追加します。HTTPS URLの構築方法と使用方法の詳細は、java.net.URL、java.net.URLConnection、java.net.HttpURLConnectionおよびjavax.net.ssl.HttpsURLConnectionクラスを参照してください。

HttpsURLConnectionインスタンスを取得する際、URLConnection.connect()メソッドを使用して実際にネットワーク接続を開始する前に、複数のHTTPおよびHTTPSパラメータを構成できます。これらについては、次を参照してください。

- 割当て済のSSLSocketFactoryの設定

- 割当て済のHostnameVerifierの設定

割当て済のSSLSocketFactoryの設定

状況によっては、HttpsURLConnectionのインスタンスによって使用されるSSLSocketFactoryを指定した方がよい場合があります。たとえば、デフォルト実装ではサポートされないプロキシ・タイプを使用してトンネリングを行いたいと考える場合があります。新しいSSLSocketFactoryは、すでに必要なトンネリングの完了したソケットを返すことができます。このため、HttpsURLConnectionは追加のプロキシを使用できます。

HttpsURLConnectionクラスには、ロード時に割り当てられたデフォルトのSSLSocketFactoryがあります(これはSSLSocketFactory.getDefault()メソッドによって返されるファクトリです)。以降、HttpsURLConnectionのインスタンスは、staticメソッドHttpsURLConnection.setDefaultSSLSocketFactoryによってクラスに新しいデフォルトのSSLSocketFactoryが割り当てられるまで、現在のデフォルトのSSLSocketFactoryを継承します。HttpsURLConnectionのインスタンスが作成された後、setSSLSocketFactory()メソッドへの呼出しにより、このインスタンス上の継承されたSSLSocketFactoryをオーバーライドできます。

注意:
デフォルトのstatic SSLSocketFactoryの変更はHttpsURLConnectionの既存のインスタンスに影響しません。既存のインスタンスを変更するには、setSSLSocketFactory()メソッドの呼出しが必要です。

getSSLSocketFactory()メソッドまたはgetDefaultSSLSocketFactory()メソッドを呼び出すことにより、インスタンスごと、またはクラスごとにSSLSocketFactoryを取得できます。

割当て済のHostnameVerifierの設定

URLのホスト名がSSL/TLSハンドシェークの一部として受け取ったクレデンシャル内のホスト名と一致しない場合、URL不正行為が発生した可能性があります。実装で、十分な確信を持ってホスト名の一致を判断できない場合、SSL実装で、インスタンスの割当て済HostnameVerifierのコールバックを実行して、より詳しいチェックを行います。ホスト名ベリファイアは、ホスト名パターン・マッチングを実行したり、対話型のダイアログ・ボックスを表示したりなど、判定を下すために必要なあらゆるステップを取ることができます。ホスト名ベリファイアによる検証に失敗した場合は、接続が切断されます。ホスト名の検証に関する詳細については、RFC 2818を参照してください。

setHostnameVerifier()メソッドおよびsetDefaultHostnameVerifier()メソッドの動作は、インスタンスごと、またはクラスごとにHostnameVerifierオブジェクトが割り当てられ、現在の値がgetHostnameVerifier()メソッドまたはgetDefaultHostnameVerifier()メソッドの呼出しによって取得できる点で、setSSLSocketFactory()メソッドおよびsetDefaultSSLSocketFactory()メソッドと似ています。

サポート・クラスとインタフェース

このセクションのクラスとインタフェースは、SSLSocketFactory、SSLServerSocketFactoryおよびSSLEngineオブジェクトを作成するために使用されるSSLContextオブジェクトの作成と初期化をサポートするために提供されます。サポート・クラスとインタフェースはjavax.net.sslパッケージに含まれています。

この項で説明する3つのクラス(SSLContextクラス、KeyManagerFactoryクラスおよびTrustManagerFactoryクラス)はエンジン・クラスです。エンジン・クラスとは、特定のアルゴリズムのAPIクラス(SSLContextの場合はプロトコル)です。その実装では1つまたは複数の暗号化サービス・プロバイダ(プロバイダ)パッケージで提供されることがあります。JCAの設計の原則およびエンジン・クラスとアルゴリズムを参照してください。

JSSEに標準で付属するSunJSSEプロバイダは、SSLContext、KeyManagerFactoryおよびTrustManagerFactory実装を提供し、標準のjava.security APIではエンジン・クラスの実装も提供します。表8-6では、SunJSSEによって提供される実装をリストします。

表8-6 SunJSSEによって提供される実装

実装されるエンジン・クラス

アルゴリズムまたはプロトコル

KeyStore

PKCS12

KeyManagerFactory

PKIX、SunX509

TrustManagerFactory

PKIX (X509またはSunPKIX)、SunX509

SSLContext

SSLv3[1]、TLSv1、TLSv1.1、TLSv1.2、DTLSv1.0、DTLSv1.2

脚注1: JDK 8u31リリース以降、SSLv3プロトコル(Secure Socket Layer)は無効になっており、デフォルトでは使用できません。<java_home>/conf/security/java.securityファイルのjava.security.Securityプロパティjdk.tls.disabledAlgorithmsを参照してください。SSLv3が絶対に必要な場合は、java.securityファイルのjdk.tls.disabledAlgorithmsプロパティからSSLv3を削除するか、JSSEが初期化される前にこのセキュリティ・プロパティを動的に設定すれば、プロトコルを再度アクティブにできます。デプロイ・レベルでSSLv3プロトコルを有効にするには、前の手順に従った後、deployment.security.SSLv3=trueという行をdeployment.propertiesファイルに追加します。

SSLContextクラス

javax.net.ssl.SSLContextクラスは、セキュアなソケット・プロトコルの実装のエンジン・クラスです。このクラスのインスタンスは、SSLSocket、SSLServerSocketおよびSSLEngineのファクトリの役目を果たします。SSLContextオブジェクトは、そのコンテキストの下で作成されたすべてのオブジェクトで共有される状態情報をすべて保持します。たとえば、セッションの状態は、ソケット・ファクトリにより作成され、コンテキストにより提供されたソケットによってハンドシェーク・プロトコルが取り決められると、SSLContextと関連付けられます。キャッシュに書き込まれたこれらのセッションは、同じコンテキストで作成された別のソケットで再利用したり共有することができます。

各インスタンスは、認証の実行に必要な鍵、証明書チェーン、および信頼されたルートCA証明書を使ってinitメソッドで構成されます。この構成は、鍵とトラスト・マネージャの形で提供されます。これらのマネージャは認証をサポートし、コンテキストによってサポートされる暗号群の鍵合意を提供します。

現在は、X.509ベースのマネージャだけがサポートされています。

SSLContextクラスの取得と初期化

SSLContextクラスは、SSLSocketFactoryクラスまたはSSLServerSocketFactoryクラスを作成するために使用されます。

SSLContextを取得して初期化するには、次の2つの方法があります。

- 最も簡単な方法は、SSLSocketFactoryまたはSSLServerSocketFactoryクラスでSSLContext.getDefault staticメソッドを呼び出すことです。このメソッドは、デフォルトのKeyManager、TrustManagerおよびSecureRandom (セキュアな乱数ジェネレータ)を使用してデフォルトのSSLContextを作成します。デフォルトのKeyManagerFactoryおよびTrustManagerFactoryを使用すると、KeyManagerおよびTrustManagerがそれぞれ作成されます。使用する鍵データは、「デフォルトのキーストアとトラストストア、ストア・タイプ、およびストア・パスワードのカスタマイズ」で説明するシステム・プロパティで指定されるデフォルトのキーストアおよびトラストストアにあります。

- 作成したコンテキストの動作を呼出し側で最も厳密に制御できるようにする方法は、SSLContextクラスでstaticメソッドSSLContext.getDefaultを呼び出してから、そのインスタンスの適切なinit()メソッドを呼び出してコンテキストを初期化することです。init()メソッドの1つのバリアントは、KeyManagerオブジェクトの配列、TrustManagerオブジェクトの配列、およびSecureRandomオブジェクトの3つの引数を取ります。適切なインタフェースを実装するか、実装を生成するKeyManagerFactoryクラスとTrustManagerFactoryクラスを使用して、KeyManagerオブジェクトとTrustManagerオブジェクトが作成されます。その後、KeyManagerFactoryとTrustManagerFactoryを、TrustManagerFactoryまたはKeyManagerFactoryクラスのinit()メソッドへの引数として渡されたKeyStoreに含まれる鍵データでそれぞれ初期化できます。最後に、TrustManagerFactoryのgetTrustManagers()メソッドとKeyManagerFactoryのgetKeyManagers()メソッドを呼び出して、トラスト・データまたは鍵データの型ごとに1つずつトラスト・マネージャまたはキー・マネージャの配列を取得できます。

SSL接続が確立されると、SSLSessionが作成され、これには設定した識別情報、使用する暗号化方式群などの様々な情報が含まれます。次にSSLSessionを使用して、2つのエンティティ間の進行中の関係と状態情報が記述されます。各SSL接続には、一度に1つのセッションが含まれますが、そのセッションがエンティティ間の接続に、同時に、または連続して何度も使用されることがあります。

SSLContextオブジェクトの作成

他のJCAプロバイダ・ベースのエンジン・クラスと同様に、SSLContextオブジェクトは、SSLContextクラスのgetInstance()ファクトリ・メソッドを使用して作成されます。このようなstaticメソッドは、最低限要求されたセキュアなソケット・プロトコルを実装するインスタンスを返します。返されるインスタンスはその他のプロトコルも実装できます。たとえば、getInstance("TLSv1")はTLSv1、TLSv1.1およびTLSv1.2を実装するインスタンスを返すことができます。getSupportedProtocols()メソッドは、このコンテキストからSSLSocket、SSLServerSocketまたはSSLEngineが作成されたときに、サポート対象のプロトコルのリストを返します。実際のSSL接続でどのプロトコルを有効にするかは、setEnabledProtocols(String[] protocols)メソッドを使用して制御できます。

注意:
SSLSocketFactory.getDefault()メソッドを呼び出すと、SSLContextオブジェクトが自動的に作成され、初期化され、SSLSocketFactoryクラスに静的に割り当てられます。したがって、デフォルト動作をオーバーライドする場合を除き、SSLContextオブジェクトを直接作成したり初期化したりする必要はありません。

getInstance()ファクトリ・メソッドを呼び出してSSLContextオブジェクトを作成するには、プロトコル名を指定する必要があります。または、要求されたプロトコルの実装を提供するプロバイダを次のように指定することもできます。

- public static SSLContext getInstance(String protocol);

- public static SSLContext getInstance(String protocol, String provider);

- public static SSLContext getInstance(String protocol, Provider provider);

プロトコル名のみを指定すると、システムは、要求されたプロトコルの実装がその環境で利用できるかどうかを判断します。複数の実装がある場合、より望ましいものがあるかどうかを判断します。

プロトコル名とプロバイダの両方を指定すると、システムは、要求されたプロトコルの実装が要求されたプロバイダにあるかどうかを判断します。実装がない場合は、例外がスローされます。

プロトコルは、希望するセキュアなソケット・プロトコルを記述する文字列(TLSなど)です。SSLContextオブジェクトの一般的なプロトコル名は、Javaセキュリティ標準アルゴリズム名仕様で定義されています。

SSLContextは次のように取得できます。

SSLContext sc = SSLContext.getInstance("TLS");

新しく作成されたSSLContextは、initメソッドを呼び出すことによって初期化すべきです。

public void init(KeyManager[] km, TrustManager[] tm, SecureRandom random);

KeyManager[]パラメータがnullの場合、このコンテキストには空のKeyManagerが定義されます。TrustManager[]パラメータがnullの場合、インストールされたセキュリティ・プロバイダは、TrustManagerFactoryクラスの最も優先度の高い実装で検索され(TrustManagerFactoryクラスを参照)、そこから適切なTrustManagerが取得されます。同様に、SecureRandomパラメータもNULLにできます。その場合、デフォルト実装が使用されます。

内部のデフォルト・コンテキストが使用される場合(SSLContextはSSLSocketFactory.getDefault()またはSSLServerSocketFactory.getDefault()によって作成されるなど)、デフォルトのKeyManagerとTrustManagerが作成されます。また、デフォルトのSecureRandom実装も選択されます。

TrustManagerインタフェース

TrustManagerは、提示された認証クレデンシャルの信頼性を判定します。信頼できないクレデンシャルの場合、接続は切断されます。セキュアなソケット・ピアのリモート識別情報を認証するには、1つまたは複数のTrustManagerオブジェクトでSSLContextオブジェクトを初期化する必要があります。サポートされる認証メカニズムのそれぞれに対し、TrustManagerを1つずつ渡す必要があります。SSLContextの初期化にnullが渡されると、自動的にトラスト・マネージャが作成されます。通常、単一のトラスト・マネージャは、X.509公開鍵証明書(X509TrustManagerなど)に基づく認証をサポートしています。セキュアなソケット実装には、共有の秘密鍵、Kerberos、またはほかのメカニズムに基づく認証をサポートするものもあります。

TrustManagerオブジェクトはTrustManagerFactoryによって、またはインタフェースの具体的な実装を提供することによって作成されます。

TrustManagerFactoryクラス

javax.net.ssl.TrustManagerFactoryはプロバイダ・ベースのサービスのエンジン・クラスで、1つまたは複数の型のTrustManagerオブジェクトのファクトリとして動作します。これはプロバイダ・ベースなので、追加のファクトリを実装して構成し、より高度なサービスを提供したり、インストール固有の認証ポリシーを実装する追加または代替のトラスト・マネージャを提供できます。

TrustManagerFactoryの作成

このクラスのインスタンスはSSLContextと同様の方法で作成しますが、getInstanceメソッドにプロトコル名ではなくアルゴリズム名の文字列を渡す点が異なります。

TrustManagerFactory tmf = TrustManagerFactory.getInstance(String algorithm);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(String algorithm, String provider);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(String algorithm, Provider provider);

呼出しの例を次に示します。

TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX", "SunJSSE");

前の呼出しは、SunJSSEプロバイダのPKIXトラスト・マネージャ・ファクトリのインスタンスを作成します。このファクトリを使用して、X.509 PKIXベースの証明書パス妥当性検査を実行するトラスト・マネージャを作成できます。

SSLContextを初期化する場合は、トラスト・マネージャ・ファクトリから作成したトラスト・マネージャを使用するか、CertPath APIなどを使用して独自のトラスト・マネージャを記述できます。Java PKIプログラマーズ・ガイドを参照してください。X509TrustManagerインタフェースを使用してトラスト・マネージャを実装する場合は、トラスト・マネージャ・ファクトリを使用する必要はありません。

新しく作成されたファクトリは、init()メソッドの1つを呼び出すことによって初期化してください。

public void init(KeyStore ks);
public void init(ManagerFactoryParameters spec);

使用するTrustManagerFactoryに適したinit()メソッドを呼び出します。不明な場合は、プロバイダのベンダーに問い合わせてください。

SunX509 TrustManagerFactoryなど、SunJSSEプロバイダが提供するファクトリは多数ありますが、TrustManagerFactoryを初期化するために必要な情報はKeyStoreのみであるため、最初にinitメソッドを呼び出すのが適切です。TrustManagerFactoryはKeyStoreに、認証チェック中に信頼すべきリモート証明書の情報を問い合わせます。

プロバイダでは、KeyStore以外の初期化パラメータを必要とすることがあります。そのプロバイダのユーザーは、プロバイダによる定義に従って、適切なManagerFactoryParametersの実装を渡すことが期待されます。そのあと、プロバイダはManagerFactoryParameters実装の特定のメソッドを呼び出し、必要な情報を取得できます。

たとえば、TrustManagerFactoryプロバイダが、そのプロバイダを使用するアプリケーションからの初期化パラメータB、RおよびSを必要としているとします。KeyStore以外の初期化パラメータを必要とするすべてのプロバイダと同様に、プロバイダはアプリケーションがManagerFactoryParametersの特定のサブインタフェースを実装するクラスのインスタンスを提供することを必要とします。たとえば、プロバイダは呼出し側のアプリケーションがMyTrustManagerFactoryParamsのインスタンスを実装して作成し、2つ目のinit()メソッドに渡すことを必要としているとします。次の例に、MyTrustManagerFactoryParamsがどのように見えるかを示します。

public interface MyTrustManagerFactoryParams extends ManagerFactoryParameters {
public boolean getBValue();
public float getRValue();
public String getSValue();
}

一部のトラスト・マネージャでは、KeyStoreオブジェクトやその他のパラメータで明示的に初期化されなくても、信頼性を判定できます。たとえば、LDAP経由でローカル・ディレクトリ・サービスのトラスト・データにアクセスしたり、オンラインの証明書ステータスチェックサーバーをリモートで使用したり、または標準のローカル位置からデフォルトのトラスト・データにアクセスすることもできます。

PKIX TrustManagerのサポート

デフォルトのトラスト・マネージャのアルゴリズムは「PKIX」です。これは、java.securityファイルのssl.TrustManagerFactory.algorithmプロパティを編集することによって変更できます。

PKIXトラスト・マネージャ・ファクトリは、インストールされたセキュリティ・プロバイダのCertPath PKIX実装(PKIプログラマーズ・ガイドの概要を参照)を使用します。トラスト・マネージャ・ファクトリを初期化するには、通常のinit(KeyStores)メソッドを使用するか、CertPathTrustManagerParametersクラスを使用して、CertPathパラメータをPKIXトラスト・マネージャに渡します。

例8-11では、トラスト・マネージャを取得して特定のLDAP証明書ストアを使用する方法と、失効チェックを有効にする方法を示します。

TrustManagerFactory.init(KeyStore)メソッドが使用される場合は、失効チェックが無効にされる点を除いて、デフォルトのPKIXパラメータが使用されます。有効にするには、com.sun.net.ssl.checkRevocationシステム・プロパティをtrueに設定します。この設定では、CertPath実装自身が失効情報を見つけられる必要があります。プロバイダのPKIX実装では多くの場合にこの動作を実行できますが、システム・プロパティcom.sun.security.enableCRLDPをtrueに設定する必要があります。TrustManagerFactory.init(ManagerFactoryParameters)メソッドではデフォルトで失効チェックが有効になっているということに注意してください。

PKIXクラスおよびCertPathクラスを参照してください。

例8-11 LDAP証明書を使用して失効チェックを有効にするサンプル・コード

次の例に、トラスト・マネージャを取得して特定のLDAP証明書ストアを使用する方法と、失効チェックを有効にする方法を示します。

import javax.net.ssl.*;
import java.security.cert.*;
import java.security.KeyStore;
import java.io.FileInputStream;
...

// Obtain Keystore password
char[] pass = System.console().readPassword("Password: ");

// Create PKIX parameters
KeyStore anchors = KeyStore.getInstance("JKS");
anchors.load(new FileInputStream(anchorsFile, pass));
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(anchors, new X509CertSelector());

// Specify LDAP certificate store to use
LDAPCertStoreParameters lcsp = new LDAPCertStoreParameters("ldap.imc.org", 389);
pkixParams.addCertStore(CertStore.getInstance("LDAP", lcsp));

// Specify that revocation checking is to be enabled
pkixParams.setRevocationEnabled(true);

// Wrap PKIX parameters as trust manager parameters
ManagerFactoryParameters trustParams = new CertPathTrustManagerParameters(pkixParams);

// Create TrustManagerFactory for PKIX-compliant trust managers
TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX");

// Pass parameters to factory to be passed to CertPath implementation
factory.init(trustParams);

// Use factory
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, factory.getTrustManagers(), null);

X509TrustManagerインタフェース

javax.net.ssl.X509TrustManagerインタフェースは、汎用のTrustManagerインタフェースを拡張したものです。X. 509ベースの認証を使用する場合、トラスト・マネージャによって実装される必要があります。

JSSEを使用したリモート・ソケット・ピアのX.509認証をサポートするには、このインタフェースのインスタンスをSSLContextオブジェクトのinitメソッドに渡す必要があります。

X509TrustManagerの作成

このインタフェースは、ユーザーが直接実装することも、SunJSSEプロバイダによって提供されているものなど、プロバイダ・ベースのTrustManagerFactoryから取得することもできます。また、独自のインタフェースを実装して、ファクトリで生成されたトラスト・マネージャに委譲することもできます。たとえば、結果の信頼性の判定をフィルタし、グラフィカル・ユーザー・インタフェースからエンド・ユーザーに問い合わせる場合に、これを実行できます。

nullのKeyStoreパラメータがSunJSSEのPKIXまたはSunX509 TrustManagerFactoryに渡された場合、ファクトリは次のプロセスを使用してトラスト・データを検索します。

1. javax.net.ssl.trustStoreプロパティが定義されている場合、TrustManagerFactoryは、このシステム・プロパティで指定されたファイル名を使用してファイルを検索し、KeyStoreパラメータにそのファイルを使用しようとします。javax.net.ssl.trustStorePasswordシステム・プロパティも定義されている場合は、ファイルを開く前に、その値を使用してトラストストアのデータの整合性をチェックします。

javax.net.ssl.trustStoreプロパティが定義されているが、指定したファイルが存在しない場合、空のキーストアを使用するデフォルトのTrustManagerが作成されます。

2. javax.net.ssl.trustStoreシステム・プロパティが指定されていない場合:

- java-home/lib/security/jssecacertsファイルが存在すれば、そのファイルが使用されます。

- java-home/lib/security/cacertsファイルが存在すれば、そのファイルが使用されます。

- これらのどちらのファイルも存在しない場合、SSL暗号化方式群が匿名であり、認証を行わないので、トラストストアは必要ありません。

java-homeが何を示すかについては、用語と定義を参照してください。

ファクトリは、cacertsファイルをチェックする前に、javax.net.ssl.trustStoreセキュリティ・プロパティによって指定されたファイルまたはjssecacertsファイルを検索します。そのため、コード署名の目的で、cacerts内に存在するものとは別に、JSSE固有の一連の信頼されたルート証明書を提供できます。

独自のX509TrustManagerの作成

指定したX509TrustManagerの動作が状況に適していない場合は、独自のTrustManagerFactoryを作成して登録するか、X509TrustManagerインタフェースを直接実装して、独自のX509TrustManagerを作成できます。

例8-12では、デフォルトのX509TrustManagerが失敗した場合に代替の認証ロジックを提供することによってデフォルトのSunJSSE X509TrustManagerの動作を拡張する、MyX509TrustManagerクラスを示します。

このようなトラスト・マネージャを作成できたら、次の例のように、init()メソッドを使用して、これをSSLContextに割り当てます。以降、このSSLContextから作成されたSocketFactoriesは、ユーザー独自のTrustManagerを使用して信頼性を判定するようになります。

TrustManager[] myTMs = new TrustManager[] { new MyX509TrustManager() };
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, myTMs, null);

例8-12 X509TrustManagerの作成のサンプル・コード

次のコード例では、デフォルトのX509TrustManagerが失敗した場合に代替の認証ロジックを提供することによってデフォルトのSunJSSE X509TrustManagerの動作を拡張する、MyX509TrustManagerクラスを示します。

class MyX509TrustManager implements X509TrustManager {

/*
* The default PKIX X509TrustManager9.  Decisions are delegated
* to it, and a fall back to the logic in this class is performed
* if the default X509TrustManager does not trust it.
*/
X509TrustManager pkixTrustManager;

MyX509TrustManager() throws Exception {
// create a "default" JSSE X509TrustManager.

KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("trustedCerts"), "passphrase".toCharArray());

TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(ks);

TrustManager tms [] = tmf.getTrustManagers();

/*
* Iterate over the returned trust managers, looking
* for an instance of X509TrustManager.  If found,
* use that as the default trust manager.
*/
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509TrustManager) {
pkixTrustManager = (X509TrustManager) tms[i];
return;
}
}

/*
* Find some other way to initialize, or else the
* constructor fails.
*/
throw new Exception("Couldn't initialize");
}

/*
* Delegate to the default trust manager.
*/
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
pkixTrustManager.checkClientTrusted(chain, authType);
} catch (CertificateException excep) {
// do any special handling here, or rethrow exception.
}
}

/*
* Delegate to the default trust manager.
*/
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
pkixTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException excep) {
/*
* Possibly pop up a dialog box asking whether to trust the
* cert chain.
*/
}
}

/*
* Merely pass this through.
*/
public X509Certificate[] getAcceptedIssuers() {
return pkixTrustManager.getAcceptedIssuers();
}
}

keyStoreの動的更新

MyX509TrustManagerを拡張して、キーストアの動的更新処理を行うことができます。checkClientTrustedまたはcheckServerTrustedのテストに失敗し、信頼できる証明書チェーンを確立できなかった場合、キーストアに対して、要求された信頼できる証明書を追加できます。更新されたキーストアを使用して初期化されたTrustManagerFactoryから新しいpkixTrustManagerを作成する必要があります。以前に初期化したSSLContextを使って新しい接続を確立すると、信頼性の判定を行うときに、新しく追加された証明書が使用されます。

X509ExtendedTrustManagerクラス

X509ExtendedTrustManagerクラスはX509TrustManagerインタフェースの抽象実装です。これは、接続を考慮したトラスト管理のためのメソッドを追加します。さらに、TLSレイヤーでのエンド・ポイントの検証を可能にします。

TLS 1.2以降では、クライアントとサーバーの両方が、受け入れるハッシュ・アルゴリズムと署名アルゴリズムを指定できます。リモート側を認証するには、認証の決定が、X509証明書と、ローカルで受け入れられるハッシュ・アルゴリズムおよび署名アルゴリズムの両方に基づいていることが必要です。ローカルで受け入れられるハッシュ・アルゴリズムおよび署名アルゴリズムはExtendedSSLSession.getLocalSupportedSignatureAlgorithms()メソッドを使用して取得できます。

ExtendedSSLSessionオブジェクトは、SSLSocket.getHandshakeSession()メソッドまたはSSLEngine.getHandshakeSession()メソッドを呼び出すことによって取得できます。

X509TrustManagerインタフェースは、接続を考慮しません。SSLSocketまたはSSLEngineセッション・プロパティにアクセスする方法を提供しません。

X509ExtendedTrustManagerクラスはTLS 1.2サポート以外に、アルゴリズムの制約とSSLレイヤーのホスト名の検証もサポートします。JSSEプロバイダおよびトラスト・マネージャの実装については、レガシーのX509TrustManagerインタフェースよりもX509ExtendedTrustManagerクラスの方が強く推奨されます。

X509ExtendedTrustManagerの作成

X509ExtendedTrustManagerサブクラスを自分自身で作成するか(概略は次のセクションに記載)、プロバイダ・ベースのTrustManagerFactoryから取得できます(SunJSSEプロバイダによって提供されるものなど)。Java SE 7では、PKIXまたはSunX509 TrustManagerFactoryはX509ExtendedTrustManagerインスタンスを返します。

独自のX509ExtendedTrustManagerの作成

このセクションでは、X509TrustManagerについて記載されているのとほぼ同じ方法でサブクラスX509ExtendedTrustManagerを作成する方法を示します。

例8-13では、PKIX TrustManagerFactoryを使用して、信頼性についての判定を下すために使用するデフォルトのX509ExtendedTrustManagerを見つけるクラスの作成方法を示します。

例8-13 PKIX TrustManagerFactoryの作成のサンプル・コード

次のコード例では、PKIX TrustManagerFactoryを使用して、信頼性についての判定を下すために使用するデフォルトのX509ExtendedTrustManagerを見つけるクラスの作成方法を示します。なんらかの理由でデフォルトのトラスト・マネージャに障害が発生した場合、サブクラスは他の動作を追加できます。このサンプルでは、これらの場所はcatch節内のコメントによって示されています。

import java.io.*;
import java.net.*;
import java.security.*;
import java.security.cert.*;
import javax.net.ssl.*;

public class MyX509ExtendedTrustManager extends X509ExtendedTrustManager {

/*
* The default PKIX X509ExtendedTrustManager.  Decisions are
* delegated to it, and a fall back to the logic in this class is
* performed if the default X509ExtendedTrustManager does not
* trust it.
*/

X509ExtendedTrustManager pkixTrustManager;

MyX509ExtendedTrustManager() throws Exception {
// create a "default" JSSE X509ExtendedTrustManager.

KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("trustedCerts"), "passphrase".toCharArray());

TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(ks);

TrustManager tms [] = tmf.getTrustManagers();

/*
* Iterate over the returned trust managers, looking
* for an instance of X509ExtendedTrustManager. If found,
* use that as the default trust manager.
*/
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509ExtendedTrustManager) {
pkixTrustManager = (X509ExtendedTrustManager) tms[i];
return;
}
}

/*
* Find some other way to initialize, or else we have to fail the
* constructor.
*/
throw new Exception("Couldn't initialize");
}

/*
* Delegate to the default trust manager.
*/
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
pkixTrustManager.checkClientTrusted(chain, authType);
} catch (CertificateException excep) {
// do any special handling here, or rethrow exception.
}
}

/*
* Delegate to the default trust manager.
*/
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
pkixTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException excep) {
/*
* Possibly pop up a dialog box asking whether to trust the
* cert chain.
*/
}
}

/*
* Connection-sensitive verification.
*/
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
try {
pkixTrustManager.checkClientTrusted(chain, authType, socket);
} catch (CertificateException excep) {
// do any special handling here, or rethrow exception.
}
}

public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
try {
pkixTrustManager.checkClientTrusted(chain, authType, engine);
} catch (CertificateException excep) {
// do any special handling here, or rethrow exception.
}
}

public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
try {
pkixTrustManager.checkServerTrusted(chain, authType, socket);
} catch (CertificateException excep) {
// do any special handling here, or rethrow exception.
}
}

public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
try {
pkixTrustManager.checkServerTrusted(chain, authType, engine);
} catch (CertificateException excep) {
// do any special handling here, or rethrow exception.
}
}

/*
* Merely pass this through.
*/
public X509Certificate[] getAcceptedIssuers() {
return pkixTrustManager.getAcceptedIssuers();
}
}

KeyManagerインタフェース

KeyManagerは、最終的にリモート・ホストに送信される認証クレデンシャルを選択します。自分自身(ローカルのセキュアなソケット・ピア)をリモート・ピアに対して認証させるには、1つまたは複数のKeyManagerオブジェクトでSSLContextオブジェクトを初期化する必要があります。サポートされる認証メカニズムごとに、KeyManagerを1つ渡す必要があります。SSLContextの初期化中にnullが渡されると、空のKeyManagerが作成されます。内部のデフォルト・コンテキストが使用される場合(SSLSocketFactory.getDefault()またはSSLServerSocketFactory.getDefault()によって作成されるSSLContextなど)は、デフォルトのKeyManagerが作成されます。デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照してください。通常、単一のキー・マネージャは、X.509公開鍵証明書に基づく認証をサポートしています。セキュアなソケット実装には、共有の秘密鍵、Kerberos、またはほかのメカニズムに基づく認証をサポートするものもあります。

KeyManagerオブジェクトはKeyManagerFactoryによって、またはインタフェースの固定実装を提供することによって作成されます。

KeyManagerFactoryクラス

javax.net.ssl.KeyManagerFactoryクラスはプロバイダ・ベースのサービスのエンジン・クラスで、1つまたは複数の型のKeyManagerオブジェクトのファクトリとして動作します。SunJSSEプロバイダは、基本となるX.509キー・マネージャを返すことができるファクトリを実装します。これはプロバイダ・ベースであるため、追加のファクトリを実装し、構成することにより、追加の、または代替のキー・マネージャを提供できます。

KeyManagerFactoryの作成

このクラスのインスタンスはSSLContextと同様の方法で作成しますが、getInstanceメソッドにプロトコル名ではなくアルゴリズム名の文字列を渡す点が異なります。

KeyManagerFactory kmf = getInstance(String algorithm);
KeyManagerFactory kmf = getInstance(String algorithm, String provider);
KeyManagerFactory kmf = getInstance(String algorithm, Provider provider);

呼出しの例を次に示します。

KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509", "SunJSSE");

前の呼出しでSunJSSEプロバイダのデフォルトのキー・マネージャ・ファクトリのインスタンスが作成され、これは、基本となるX.509ベースの認証鍵を提供します。

新しく作成されたファクトリは、initメソッドの1つを呼び出すことによって初期化してください。

public void init(KeyStore ks, char[] password);
public void init(ManagerFactoryParameters spec);

使用するKeyManagerFactoryに適したinitメソッドを呼び出します。不明な場合は、プロバイダのベンダーに問い合わせてください。

SunX509 KeyManagerFactoryなど、SunJSSEプロバイダが提供するファクトリは多数ありますが、KeyManagerFactoryを初期化するために必要な情報はKeyStoreのみであるため、最初にinitメソッドを呼び出すのが適切です。KeyManagerFactoryはKeyStoreに、リモートのソケット・ピアを認証するために使用すべき秘密鍵、および対応する公開鍵証明書について問い合わせます。パスワード・パラメータは、KeyStoreの鍵にアクセスするメソッドで使用するパスワードを指定します。KeyStoreの鍵はすべて、同じパスワードで保護する必要があります。

プロバイダでは、KeyStoreとパスワード以外の初期化パラメータを必要とすることがあります。そのプロバイダのユーザーは、プロバイダによる定義に従って、適切なManagerFactoryParametersの実装を渡すことが期待されます。そのあと、プロバイダはManagerFactoryParameters実装の特定のメソッドを呼び出し、必要な情報を取得できます。

一部のファクトリでは、KeyStoreオブジェクトやその他のパラメータで初期化されなくても、認証データにアクセスできます。たとえば、Java認証・承認サービス(JAAS)などのログイン・メカニズムの一部として鍵データにアクセスできる場合があります。

前に述べたように、SunJSSEプロバイダは、KeyStoreパラメータで初期化されている必要があるSunX509ファクトリをサポートします。

X509KeyManagerインタフェース

javax.net.ssl.X509KeyManagerインタフェースは、汎用のKeyManagerインタフェースを拡張したものです。X.509ベースの認証を行うキー・マネージャで実装します。JSSEを使用したリモート・ソケット・ピアのX.509認証をサポートするには、このインタフェースのインスタンスをSSLContextオブジェクトのinit()メソッドに渡す必要があります。

X509KeyManagerの作成

このインタフェースは、ユーザーが直接実装することも、SunJSSEプロバイダによって提供されているものなど、プロバイダ・ベースのKeyManagerFactoryから取得することもできます。また、独自のインタフェースを実装して、ファクトリで生成されたキー・マネージャに委譲することもできます。たとえば、結果の鍵をフィルタし、グラフィカル・ユーザー・インタフェースを使用してエンド・ユーザーに問い合わせる場合に、これを実行できます。

独自のX509KeyManagerの作成

X509KeyManagerのデフォルトの動作が状況に適していない場合は、独自のX509TrustManagerの作成に示す同様の方法で独自のX509KeyManagerを作成できます。

X509ExtendedKeyManagerクラス

X509ExtendedKeyManager抽象クラスは、接続に固有の鍵の選択が可能なX509KeyManagerインタフェースの実装です。これは、鍵のタイプ、許可される発行者および現在のSSLEngineに基づいて、クライアントまたはサーバー用の鍵の別名を選択する2つのメソッドを追加します。

- public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine)

- public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)

キー・マネージャがX509ExtendedKeyManagerクラスのインスタンスでない場合、SSLEngineクラスと連携して動作しません。

JSSEプロバイダおよびトラスト・マネージャの実装については、レガシーのX509KeyManagerインタフェースよりもX509ExtendedKeyManagerクラスの方が強く推奨されます。

TLS 1.2以降では、クライアントとサーバーの両方が、受け入れるハッシュ・アルゴリズムと署名アルゴリズムを指定できます。リモート側から要求される認証に合格するには、ローカルの鍵選択の決定が、X509証明書と、リモート側で受け入れられるハッシュ・アルゴリズムおよび署名アルゴリズムの両方に基づいていることが必要です。リモート側で受け入れられるハッシュ・アルゴリズムおよび署名アルゴリズムはExtendedSSLSession.getPeerSupportedSignatureAlgorithms()メソッドを使用して取得できます。

独自のX509TrustManagerの作成に示す同様の方法で、独自のX509ExtendedKeyManagerサブクラスを作成できます。

サーバー側でのServer Name Indication (SNI)拡張のサポートにより、キー・マネージャはサーバー名をチェックし、それに従って適切な鍵を選択できます。たとえば、キーストアに証明書付きの鍵エントリが3つあるとします。

- cn=www.example.com

- cn=www.example.org

- cn=www.example.net

ClientHelloメッセージで、SNI拡張のwww.example.netに接続するように要求する場合、サーバーはサブジェクトcn=www.example.netで証明書を選択できる必要があります。

TrustManagerとKeyManagerの関係

これまでは、TrustManagerとKeyManagerの機能に関して混乱がありました。

TrustManagerはリモート認証クレデンシャル(すなわち接続)が信頼できるかどうかを判定します。

KeyManagerはリモート・ホストに送信される認証クレデンシャルを決定します。

二次サポート・クラスおよびインタフェース

二次サポート・クラスは、セキュアなソケットの作成、使用、および管理をサポートするJSSE APIの一部として提供されます。このクラスは、セキュアなソケット・アプリケーションでは、コア・クラスやサポート・クラスほどには使用されません。セカンダリ・サポート・クラスおよびインタフェースはjavax.net.sslおよびjavax.security.certパッケージに含まれています。

SSLParametersクラス

SSLParametersクラスは、SSL/TLS/DTLS接続に影響する次のパラメータをカプセル化します。

- SSL/TLS/DTLSハンドシェークで受け付けられる暗号化方式群のリスト

- 許可されるプロトコルのリスト

- SSL/TLS/DTLSハンドシェーク中のエンドポイント識別アルゴリズム

- サーバー名とサーバー名のマッチャ(Server Name Indication (SNI)拡張を参照)

- SSL/TLS/DTLSハンドシェークで使用される暗号化方式群の優先順位

- SSL/TLS/DTLSハンドシェーク中のアルゴリズム

- Server Name Indication (SNI)

- 最大ネットワーク・パケット・サイズ

- アルゴリズム制約、およびSSL/TLS/DTLSサーバーがクライアント認証を要求する必要があるか必要とするかどうか

次のメソッドを使用して、SSLSocketまたはSSLEngineの現在のSSLParametersを取得できます。

- SSLSocket、SSLServerSocketおよびSSLEngine内のgetSSLParameters()

- SSLContext内のgetDefaultSSLParameters()およびgetSupportedSSLParamters()

SSLSocket、SSLServerSocketおよびSSLEngineのsetSSLParameters()メソッドで、SSLParametersを割り当てることができます。

SSLParameters.setServerNames()メソッドによって、サーバー名の表示を明示的に設定できます。クライアント・モードでのサーバー名の表示はエンドポイントの識別にも影響します。X509ExtendedTrustManagerの実装で、それはExtendedSSLSession.getRequestedServerNames()メソッドによって取得されたサーバー名の表示を使用します。例8-14を参照してください。

例8-14 Server Name Indicationを設定するサンプル・コード

この例では、サーバー名表示のホスト名(www.example.com)を使用して、エンド・エンティティのX.509証明書に提示されるピアのIDに対してエンドポイントを識別します。

SSLSocketFactory factory = ...
SSLSocket sslSocket = factory.createSocket("172.16.10.6", 443);
// SSLEngine sslEngine = sslContext.createSSLEngine("172.16.10.6", 443);

SNIHostName serverName = new SNIHostName("www.example.com");
List<SNIServerName> serverNames = new ArrayList<>(1);
serverNames.add(serverName);

SSLParameters params = sslSocket.getSSLParameters();
params.setServerNames(serverNames);
sslSocket.setSSLParameters(params);
// sslEngine.setSSLParameters(params);

暗号化方式群の優先順位

TLSハンドシェーク時に、クライアントは、それがサポートする暗号化オプションのリストの、最優先順位から暗号化方式群をネゴシエーションすることを要求します。次に、サーバーはクライアントによって要求された暗号化方式群のリストから、単一の暗号化方式群を選択します。通常、選択はクライアントの優先順位が尊重されます。ただし、弱い暗号化方式群の使用のリスクを緩和するため、サーバーはSSLParameters.setUseCipherSuitesOrder(true)メソッドを呼び出して、クライアントの優先順位ではなく、独自の優先順位に基づいて暗号化方式群を選択することがあります。

SSLSessionContextインタフェース

javax.net.ssl.SSLSessionContextインタフェースは、1つのエンティティに関連付けられているSSLSessionオブジェクトのグループです。たとえば、多数のセッションに同時に参加するサーバーやクライアントに関連付けることができます。このインタフェースのメソッドを使用すると、コンテキストの全セッションを列挙したり、セッションIDで特定のセッションを検索したりできます。

SSLSessionContextは、オプションで、SSLSessionのgetSessionContext()メソッドを呼び出してSSLSessionから取得することもできます。一部の環境では、コンテキストが使用できないことがあり、その場合、getSessionContextメソッドはnullを返します。

SSLSessionBindingListenerインタフェース

javax.net.ssl.SSLSessionBindingListenerインタフェースは、SSLSessionからバインドまたはアンバインドされるときに通知を受けるオブジェクトによって実装されます。

SSLSessionBindingEventクラス

javax.net.ssl.SSLSessionBindingEventクラスは、SSLSession (SSLSessionとExtendedSSLSessionを参照)からバインドまたはアンバインドされるときにSSLSessionBindingListener (SSLSessionBindingListenerインタフェースを参照)に伝えられるイベントを定義します。

HandShakeCompletedListenerインタフェース

javax.net.ssl.HandShakeCompletedListenerインタフェースは、指定のSSLSocket接続時にSSLプロトコル・ハンドシェークの完了通知を受け取る任意のクラスに実装されるインタフェースです。

HandShakeCompletedEventクラス

javax.net.ssl.HandShakeCompletedEventクラスは、指定のSSLSocket接続のSSLプロトコル・ハンドシェークの完了時にHandShakeCompletedListener (HandShakeCompletedListenerインタフェースを参照)に伝えられるイベントを定義します。

HostnameVerifierインタフェース

SSL/TLS実装の標準ホスト名検証ロジックが失敗した場合、実装は、このインタフェースを実装し、このHttpsURLConnectionインスタンスに割り当てられたクラスのverifyメソッドを呼び出します。コールバック・クラスは、指定のパラメータでホスト名が受け付け可能であると判断できる場合、接続を許可すべきであると報告します。応答が受け付けられない場合、接続は切断されます。例8-15を参照してください。

HostnameVerifierをHttpsURLConnectionに割り当てる方法の詳細は、HttpsURLConnectionを参照してください。

例8-15 HostnameVerifierインタフェース実装のサンプル・コード

次の例では、HostnameVerifierインタフェースを実装するクラスを示します。

public class MyHostnameVerifier implements HostnameVerifier {

public boolean verify(String hostname, SSLSession session) {
// pop up an interactive dialog box
// or insert additional matching logic
if (good_address) {
return true;
} else {
return false;
}
}
}

//...deleted...

HttpsURLConnection urlc = (HttpsURLConnection)
(new URL("https://www.example.com/")).openConnection();
urlc.setHostnameVerifier(new MyHostnameVerifier());

X509Certificateクラス

セキュアなソケット・プロトコルの多くは、X.509証明書という公開鍵証明書を使って認証を行います。これは、SSL/TLSプロトコルのデフォルト認証メカニズムです。

java.security.cert.X509Certificate抽象クラスは、X.509証明書の属性にアクセスする標準的な方法を提供します。

注意:
javax.security.cert.X509Certificateクラスは、以前のバージョン(1.0.xおよび1.1.x)のJSSEとの後方互換性のためにのみサポートされています。新しいアプリケーションでは、かわりにjava.security.cert.X509Certificateクラスを使用してください。

AlgorithmConstraintsインタフェース

java.security.AlgorithmConstraintsインタフェースは、許可される暗号化アルゴリズムを制御するために使用します。AlgorithmConstraintsは3つのpermits()メソッドを定義します。これらのメソッドは、ある暗号化関数について許可されるアルゴリズム名または鍵を指定します。暗号化関数は一連のCryptoPrimitiveで表現され、これはSTREAM_CIPHER、MESSAGE_DIGEST、SIGNATUREなどのフィールドを含む列挙です。

したがって、AlgorithmConstraints実装は、「この鍵とこのアルゴリズムを暗号化操作の目的で使用できるのか」といった質問に答えることができます。

新しいsetAlgorithmConstraints()メソッドを使用して、AlgorithmConstraintsオブジェクトをSSLParametersオブジェクトと関連付けることができます。SSLParametersオブジェクトに対する現在のAlgorithmConstraintsオブジェクトは、getAlgorithmConstraints()メソッドを使用して取得します。

StandardConstantsクラス

StandardConstantsクラスは、JSSEでの標準の定数定義を表すために使用します。

StandardConstants.SNI_HOST_NAMEServer Name Indication (SNI)拡張でのドメイン・ネーム・サーバー(DNS)ホスト名を表し、SNIServerNameまたはSNIMatcherオブジェクトのインスタンス化時に使用できます。

SNIServerNameクラス

抽象SNIServerNameクラスのインスタンスはServer Name Indication (SNI)拡張のサーバー名を表します。それは、指定したサーバー名のタイプとエンコード値を使用してインスタンス化されます。

getType()およびgetEncoded()メソッドを使用して、サーバー名のタイプとエンコードされたサーバー名の値のコピーをそれぞれ返します。equals()メソッドは他のオブジェクトがこのサーバー名と「等しい」かどうかをチェックするために使用できます。hashCode()メソッドはこのサーバー名のハッシュ・コード値を返します。サーバー名(サーバー名のタイプとエンコードされたサーバー名の値)の文字列表現を取得するには、toString()メソッドを使用します。

SNIMatcherクラス

抽象SNIMatcherクラスのインスタンスはSNIServerNameオブジェクトの一致操作を実行します。サーバーはServer Name Indication (SNI)拡張からの情報を使用して、特定のSSLSocketまたはSSLEngineで接続を受け付けるべきであるかどうかを判定できます。たとえば、単一の基礎となるネットワーク・アドレスで複数の「仮想」または「名前ベース」のサーバーがホストされている場合、サーバー・アプリケーションは、SNI情報を使用して、このサーバーが、クライアントがアクセスしようとしている正しいサーバーであるかどうかを判断できます。このクラスのインスタンスは、サーバーによって、ホスト名などの特定のタイプの受け付け可能なサーバー名を確認するために使用できます。

SNIMatcherクラスは、一致操作が実行される指定したサーバー名タイプを使用してインスタンス化されます。指定のSNIServerNameを照合するには、matches()メソッドを使用します。指定のSNIMatcherオブジェクトのサーバー名タイプを返すには、getType()メソッドを使用します。

SNIHostNameクラス

SNIHostNameクラス(SNIServerNameクラスを拡張する)のインスタンスは、Server Name Indication (SNI)拡張のタイプhost_name(StandardConstantsクラスを参照)のサーバー名を表します。SNIHostNameをインスタンス化するには、String引数で、サーバーの完全修飾DNSホスト名(クライアントから理解できる)を指定します。この引数は、次の場合に不正です。

- 引数が空である。

- 引数の末尾がピリオドで終わっている。

- 引数がRFC 3490仕様に準拠した有効なInternationalized Domain Name (IDN)でない。

エンコードされたホスト名値をバイト配列で指定して、SNIHostNameをインスタンス化することもできます。このメソッドは一般に、要求されたSNI拡張でエンコードされた名前値を解釈するために使用します。そうでない場合、SNIHostName(String hostname)コンストラクタを使用します。encoded引数は、次の場合に不正です。

- 引数が空である。

- 引数の末尾がピリオドで終わっている。

- 引数がRFC 3490仕様に準拠した有効なInternationalized Domain Name (IDN)でない。

- 引数がUTF-8またはUS-ASCIIでエンコードされていない。

注意:
引数として渡されたencodedバイト配列は、以降の変更から保護するために、複製が作成されます。

US-ASCIIエンコーディングのSNIHostNameオブジェクトのホスト名を返すには、getAsciiName()メソッドを使用します。サーバー名を他のオブジェクトと比較するには、equals()メソッドを使用します(比較は大文字と小文字が区別されません)。SNIHostNameのハッシュ・コード値を返すには、hashCode()メソッドを使用します。DNSホスト名を含むSNIHostNameの文字列表現を返すには、toString()メソッドを使用します。

createSNIMatcher()メソッドに、1つ以上の照合するホスト名を表す正規表現を渡すことによって、SNIHostNameオブジェクトのSNIMatcherオブジェクトを作成します。

JSSEのカスタマイズ

JSSEには、様々な実装をプラグインしたり、デフォルトのキーストアを指定したりして、カスタマイズ可能な標準実装が含まれます。

表8-7と表8-8では、カスタマイズが可能な側面、デフォルトの内容、およびカスタマイズを提供するために使用するメカニズムを要約しています。

一部の機能は、システム・プロパティやセキュリティ・プロパティの値を設定してカスタマイズできます。表に続くセクションでは、プロパティ値の設定方法について説明します。

注意:
この表に示すプロパティの多くは、現在JSSE実装で使用されていますが、それらの名前や型(システムまたはセキュリティ)が引き続き同じで、それらが将来のリリースにも存在するという保証はありません。それらのすべてのプロパティは「*」でフラグ付けされています。ここでは、JSSE実装で使用する場合の参考として、それらに言及しています。

表8-7では、java.security.Securityプロパティの設定によってカスタマイズされる項目を示します。java.security.Securityプロパティの指定方法を参照してください。

表8-7 セキュリティ・プロパティとカスタマイズされる項目

セキュリティ・プロパティ

カスタマイズされる項目

デフォルト値

注意

cert.provider.x509v1

X509証明書実装のカスタマイズ

OracleからのX509Certificate実装

なし

security.provider.n

暗号化サービス・プロバイダ。プロバイダ実装のカスタマイズと暗号化アルゴリズム・プロバイダのカスタマイズを参照してください。

優先度順の上位5つのプロバイダは、次のとおりです。

1. SUN

2. SunRsaSign

3. SunEC

4. SunJSSE

5. SunJCE

セキュリティ・プロパティ・ファイル内のsecurity.provider.n=の行でプロバイダを指定します。ここでは、nは1以上の整数値です。

*ssl.SocketFactory.provider

デフォルトのSSLSocketFactory実装

OracleからのSSLSocketFactory実装

なし

*ssl.ServerSocketFactory.provider

デフォルトのSSLServerSocketFactory実装

OracleからのSSLServerSocketFactory実装

なし

ssl.KeyManagerFactory.algorithm

デフォルトのキー・マネージャ・ファクトリ・アルゴリズム名(デフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズを参照)

SunX509

なし

*jdk.certpath.disabledAlgorithms

無効化された証明書検証暗号化アルゴリズム(無効化された制限付き暗号化アルゴリズムを参照)

MD2、MD5、SHA1 jdkCA & usage TLSServer、RSA keySize < 1024、DSA keySize < 1024、EC keySize < 224脚注4

なし

ssl.TrustManagerFactory.algorithm

デフォルトのトラスト・マネージャ・ファクトリ・アルゴリズム名(デフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズを参照)

PKIX

なし

SunJSSEプロバイダが使用するJCE暗号化アルゴリズム

代替のJCEアルゴリズム・プロバイダにSunJCEプロバイダより古い高い優先順位を与えます。

SunJCE実装

なし

*jdk.tls.disabledAlgorithms

無効化された制限付き暗号化アルゴリズム

SSLv3、RC4、MD5withRSA、DH keySize < 1024、EC keySize < 224脚注4

特定のアルゴリズム(プロトコル・バージョン、暗号化方式群、鍵交換メカニズムなど)を無効化し、アプリケーションで明示的に有効化されてもSSL/TLS/DTLS接続でネゴシエーションされないようにします。

jdk.tls.server.defaultDHEParameters

Diffie-Hellmanグループ

OpenJDK SSL/TLS/DTLS実装での安全な主要Diffie-Hellmanグループ

Transport Layer Security (SSL/TLS/DTLS)処理のためにデフォルトの有限フィールドDiffie-Hellman ephemeral (DHE)パラメータを定義します。

* このプロパティは現在JSSE実装で使用されていますが、他の実装で調査され、使用されている保証はありません。他の実装で調査する場合は、その実装で、JSSE実装と同じ方法でそれを処理すべきです。プロパティが今後も存在すること、またはシステム型やセキュリティ型が将来のリリースでも変更されないことの保証はされません。

脚注4

表8-8では、java.lang.Systemプロパティの設定によってカスタマイズされる項目を示します。java.lang.Systemプロパティの指定方法を参照してください。

表8-8 システム・プロパティとカスタマイズされる項目

システム・プロパティ

カスタマイズされる項目

デフォルト

注意

java.protocol.handler.pkgs

HTTPSプロトコルの代替実装の指定

Oracleからの実装

なし

*javax.net.ssl.keyStore

デフォルトのキーストア(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照)

なし

値NONEを指定できます。この設定は、キーストアがファイルベースでない場合に適切です(ハードウェア・トークンに存在する場合など)

*javax.net.ssl.keyStorePassword

デフォルトのキーストア・パスワード(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照)

なし

他のユーザーに見つかるような方法でパスワードを指定することはお薦めできません。

たとえば、コマンド行でのパスワードの指定です。パスワードのセキュリティを維持するには、アプリケーションでパスワードの入力を求めるか、適切に保護されたオプション・ファイルにパスワードを指定します

*javax.net.ssl.keyStoreProvider

デフォルトのキーストア・プロバイダ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照)

なし

なし

*javax.net.ssl.keyStoreType

デフォルトのキーストア・タイプ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照)

KeyStore.getDefaultType()

なし

*javax.net.ssl.trustStore

デフォルトのトラストストア(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照)

存在する場合は、jssecacerts。
そうでない場合、cacerts

なし

*javax.net.ssl.trustStorePassword

デフォルトのトラストストア・パスワード(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照)

なし

他のユーザーに見つかるような方法でパスワードを指定することはお薦めできません。

たとえば、コマンド行でのパスワードの指定です。パスワードのセキュリティを維持するには、アプリケーションでパスワードの入力を求めるか、適切に保護されたオプション・ファイルにパスワードを指定します

*javax.net.ssl.trustStoreProvider

デフォルトのトラストストア・プロバイダ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照)

なし

なし

*javax.net.ssl.trustStoreType

デフォルトのトラストストア・タイプ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照)

KeyStore.getDefaultType()

値NONEを指定できます。この設定は、トラストストアがファイルベースでない場合に適切です(ハードウェア・トークンに存在する場合など)

*https.proxyHost

デフォルトのプロキシ・ホスト

なし

なし

*https.proxyPort

デフォルトのプロキシ・ポート

80

なし

*jsse.enableSNIExtension

Server Name Indicationオプション

true

Server Name Indication (SNI)はTLS拡張で、RFC 6066で定義されています。これは仮想サーバーへのTLS接続を可能にし、さまざまなネットワーク名に対して複数のサーバーが単一の基本ネットワーク・アドレスでホスティングされます。かなり古い一部のSSL/TLSベンダーは、SSL/TLS拡張を処理できないことがあります。この場合、このプロパティをfalseに設定してSNI拡張を無効化します

*https.cipherSuites

デフォルトの暗号化方式群

ソケット・ファクトリによって決定

HttpsURLConnectionで使用できる暗号群を指定する暗号群名リスト(カンマ区切り形式)を含む。SSLSocket.setEnabledCipherSuites(String[])を参照してください。

*https.protocols

デフォルトのハンドシェーク・プロトコル

ソケット・ファクトリによって決定

HttpsURLConnectionで有効にするプロトコルを指定するプロトコル名リスト(カンマ区切り形式)を含む。SSLSocket.setEnabledProtocols(String[])を参照してください。

* HTTPS URL内のportフィールドによってカスタマイズします。

デフォルトのHTTPSポート

443

なし

*jsse.SSLEngine.acceptLargeFragments

大きいSSL/TLSパケット用のバッファのデフォルトのサイズ設定

なし

このシステム・プロパティをtrueに設定すると、SSLSessionはデフォルトで大きいデータ・パケットを処理するようにバッファをサイズ設定します。これにより、アプリケーションが不要な大きいSSLEngineバッファを割り当てる場合があります。代わりに、アプリケーションはバッファ・オーバーフロー条件を動的にチェックして、バッファを適宜サイズ変更する必要があります

*sun.security.ssl.allowUnsafeRenegotiation

安全ではないSSL/TLS再ネゴシエーションを許可します(フェーズ2修正の説明を参照)

false

このシステム・プロパティをtrueに設定すると、完全な(安全でない)レガシーの再ネゴシエーションが許可されます。

このシステム・プロパティは推奨されておらず、将来のJDKリリースで削除される可能性があります。

*sun.security.ssl.allowLegacyHelloMessages

旧式のHelloメッセージを許可します(フェーズ2修正の説明を参照)

true

このシステム・プロパティをtrueに設定すると、適切なRFC 5746メッセージを必要とすることなくピアがハンドシェークを実行できます。

このシステム・プロパティは推奨されておらず、将来のJDKリリースで削除される可能性があります。

jdk.tls.client.protocols

SunJSSEプロバイダ

なし

クライアント上で特定のSunJSSEプロトコルを有効化するには、引用符で囲んでカンマ区切りリスト形式で指定します。それ以外のサポート対象プロトコルはクライアント上では有効になりません。

次に例を示します。

- jdk.tls.client.protocols="TLSv1,TLSv1.1"の場合は、TLSv1およびTLSv1.1用のクライアント上のデフォルトのプロトコル設定が有効になり、SSLv3、TLSv1.2およびSSLv2Helloは有効になりません。

- jdk.tls.client.protocols="DTLSv1.2"の場合は、DTLS1.2用のクライアント上のプロトコル設定が有効になり、DTLS1.0は有効になりません。

jdk.tls.ephemeralDHKeySize

エフェメラルDiffie-Hellman鍵のサイズのカスタマイズ

1024ビット

なし

jsse.enableMFLNExtension

最大断片長ネゴシエーション(MFLN)拡張のカスタマイズ

false

なし

* このプロパティは現在JSSE実装で使用されていますが、他の実装で調査され、使用されている保証はありません。他の実装で調査する場合は、その実装で、JSSE実装と同じ方法でそれを処理すべきです。プロパティが今後も存在すること、またはシステム型やセキュリティ型が将来のリリースでも変更されないことの保証はされません。

java.lang.Systemプロパティの指定方法

JSSEの一部の側面は、システム・プロパティを設定してカスタマイズできます。次のいくつかの方法によって、これらのプロパティを設定できます。

- システム・プロパティを静的に設定するには、javaコマンドの-Dオプションを使用します。たとえば、MyAppというアプリケーションを実行して、javax.net.ssl.trustStoreシステム・プロパティを設定し、MyCacertsFileというトラストストアを指定します。トラストストアを参照してください。次のように入力します。

java -Djavax.net.ssl.trustStore=MyCacertsFile MyApp

- システム・プロパティを動的に設定するには、次のコードでjava.lang.System.setProperty()メソッドを呼び出します。

System.setProperty("propertyName", "propertyValue");

たとえば、システム・プロパティjavax.net.ssl.trustStoreを設定してMyCacertsFileというトラストストアを指定する、前の例に対応したsetProperty呼出しでは:

System.setProperty("javax.net.ssl.trustStore", "MyCacertsFile");

- Java配備環境(プラグイン/Web Start)では、いくつかの方法でシステム・プロパティを設定できます。

- Javaコントロール・パネルを使用して、Runtime Environment PropertyをローカルまたはVM単位で設定します。これにより、ローカルでdeployment.propertiesファイルが作成されます。配備担当開発者はdeployment.configメカニズムを使用して、企業全体のdeployment.propertiesファイルを配布することもできます。

- 特定のアプレットにプロパティを設定するには、<APPLET>タグ内のHTMLサブタグ<PARAM> "java_arguments"を使用します。

- 特定のJava Web Startアプリケーションまたはアプレット内でPlugin2を使用してプロパティを設定するには、resources要素のJNLP propertyサブ要素を使用します。Java Platform, Standard Editionデプロイメント・ガイドでresources要素を参照してください。

java.security.Securityプロパティの指定方法

JSSEの一部の側面は、セキュリティ・プロパティを設定してカスタマイズできます。セキュリティ・プロパティは静的または動的に設定できます。

- セキュリティ・プロパティを静的に設定するには、セキュリティ・プロパティ・ファイルに1行追加します。セキュリティ・プロパティ・ファイルは、java-home/conf/security/java.securityにあります。

java-home

用語と定義を参照してください。

セキュリティ・プロパティ・ファイルでセキュリティ・プロパティ値を指定するには、次の行を追加します。

propertyName=propertyValue

たとえば、デフォルトのSunX509以外のキー・マネージャ・ファクトリのアルゴリズム名を指定するとします。これを実行するには、ssl.KeyManagerFactory.algorithmというセキュリティ・プロパティの値として、アルゴリズム名を指定します。たとえば、値をMyX509に設定するには、次の行をセキュリティ・プロパティ・ファイルに追加します。

ssl.KeyManagerFactory.algorithm=MyX509

- セキュリティ・プロパティを動的に設定するには、自分のコードでjava.security.Security.setPropertyメソッドを呼び出します。

Security.setProperty("propertyName," "propertyValue");

たとえば、キー・マネージャ・ファクトリのアルゴリズム名を指定する、前の例に対応したsetProperty()メソッドの呼出しでは:

Security.setProperty("ssl.KeyManagerFactory.algorithm", "MyX509");

X509証明書実装のカスタマイズ

X509Certificate.getInstance()メソッドで返されたX509証明書実装は、デフォルトでJSSE実装の実装です。

別の実装を返すようにするには、次の手順に従います。

他の実装のクラスの名前(およびパッケージ)を、cert.provider.x509v1という名前のjava.security.Securityプロパティの指定方法の値として指定します。

たとえば、クラスがMyX509CertificateImplと呼ばれ、com.cryptoxパッケージにある場合、セキュリティ・プロパティ・ファイルに次の行を追加してください。

cert.provider.x509v1=com.cryptox.MyX509CertificateImpl

HTTPSプロトコルの代替実装の指定

java.net.URLクラスにHTTPS URLスキームを使用することで、SSL対応のWebサーバーでセキュアに通信できます。JDKは、デフォルトのHTTPS URL実装を提供します。

別のHTTPSプロトコル実装を使用する必要がある場合は、新しいクラス名を含めるようにjava.protocol.handler.pkgsのjava.lang.Systemプロパティの指定方法を設定します。その結果、JDKのデフォルト・クラスより前に、指定したクラスが検索され、ロードされます。詳細は、URLクラスを参照してください。

注意:
過去のJSSEリリースでは、JSSEのインストール中にjava.protocol.handler.pkgsシステム・プロパティを設定する必要がありました。このステップは、com.sun.net.ssl.HttpsURLConnectionのインスタンスを取得する場合以外は不要になりました。

プロバイダ実装のカスタマイズ

JDKには、SunJSSEというJSSE暗号化サービス・プロバイダ(略称はプロバイダ)が付属しています。基本的に、プロバイダは特定の暗号化アルゴリズムのエンジン・クラスを実装するパッケージです。

JSSEのエンジン・クラスはSSLContext、KeyManagerFactory、およびTrustManagerFactoryです。プロバイダとエンジン・クラスの詳細は、Java暗号化アーキテクチャ(JCA)リファレンス・ガイドを参照してください。

使用する前に、プロバイダを静的または動的に登録する必要があります。SunJSSEプロバイダは登録済なので、登録する必要はありません。ほかのプロバイダを使用する場合は、後述のセクションでプロバイダの登録方法を確認してください。

暗号化プロバイダの静的な登録

セキュリティ・プロパティ・ファイル<java-home>/conf/security/java.securityに次のような1行を追加することで、プロバイダを静的に登録します。

security.provider.n=provName|className

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

provNameはプロバイダの名前であり、classNameはプロバイダの完全修飾クラス名です。

標準のセキュリティ・プロバイダは、java.securityセキュリティ・プロパティ・ファイルに自動的に登録されます。

他のJSSEプロバイダを使用する場合は、行を追加して他のプロバイダを登録し、目的の優先順位を指定します。

複数のJSSEプロバイダを同時に登録できます。登録されたプロバイダには、様々なエンジン・クラスの、様々なアルゴリズムの様々な実装が含まれる場合があり、同じ型のアルゴリズムおよびエンジン・クラスの一部または全部をサポートする場合もあります。特定のアルゴリズムの特定のエンジン・クラス実装を検索するとき、その検索に特定のプロバイダが指定されていない場合、プロバイダは優先順位で検索され、指定したアルゴリズムの実装を提供する最初のプロバイダの実装が使用されます。

プロバイダの実装および統合までのステップでステップ8.1: プロバイダの構成を参照してください。

暗号化サービス・プロバイダを動的に登録する

プロバイダを静的に登録するかわりに、SecurityクラスのaddProviderまたはinsertProviderAtメソッドを呼び出すことで実行時に動的にプロバイダを追加できます。こうした登録は持続的なものではなく、実行できるのはinsertProvider.<provider name>権限を付与されたコードのみです。

プロバイダの実装および統合までのステップでステップ8.1: プロバイダの構成を参照してください。

プロバイダ構成

一部のプロバイダは、構成が必要な場合があります。これは、SecurityクラスのaddProviderメソッドを呼び出す前に、Providerクラスのconfigureメソッドを使用して行われます。一例として、SunPKCS11の構成を参照してください。Provider.configure()メソッドは、Java SE 9の新機能です。

特定の複数のアルゴリズムのための推奨プロバイダの構成

jdk.security.provider.preferredセキュリティ・プロパティで特定のアルゴリズムのための推奨プロバイダを指定します。推奨プロバイダを指定することで、特定のアルゴリズムのパフォーマンスを向上させるプロバイダを構成できますが、それらは、他のアルゴリズムにとっては最善のプロバイダではありません。security.provider.nプロパティを使用して指定された順序付きプロバイダ・リストは、特定のアルゴリズムのパフォーマンスを向上させるが他のアルゴリズムにとっては最善ではないプロバイダを順序付けるには十分ではありません。パフォーマンスを向上させるには、プロバイダ・リストの順序付けの構成をより柔軟に行う必要があります。

jdk.security.provider.preferredセキュリティ・プロパティでは、登録済プロバイダのリストにアクセスする前に、特定のアルゴリズム、またはサービスの型を一連の推奨プロバイダから選択できます。java.security.Securityプロパティの指定方法を参照してください。

jdk.security.provider.preferredセキュリティ・プロパティでは、プロバイダは登録されません。順序付きプロバイダ・リストは、security.provider.nプロパティを使用して暗号化プロバイダを静的に登録している必要があります。登録されていないプロバイダは無視されます。

1つのアルゴリズムのための推奨プロバイダの指定

jdk.security.provider.preferredセキュリティ・プロパティで推奨プロバイダ文字列を指定するための構文は、ServiceType.Algorithm:Providerのカンマ区切りのリストです。

この構文の説明は次のとおりです。

ServiceType

サービスの型の名前。(例: "MessageDigest")ServiceTypeはオプションです。指定しない場合は、そのアルゴリズムがすべてのサービス型に適用されます。

Algorithm

標準アルゴリズム名。Javaセキュリティ標準アルゴリズム名仕様を参照してください。アルゴリズムは、標準名全体(AES/CBC/PKCS5Padding)または一部分(AES、AES/CBC、AES//PKCS5Padding)として指定できます。

Provider

プロバイダの名前。登録済リストに示されていないプロバイダは無視されます。JDKプロバイダを参照してください。

解析エラーなど、エラーを含むエントリは無視されます。エラーをデバッグするには、コマンドjava -Djava.security.debug=jcaを使用します。

推奨プロバイダとFIPS

FIPSプロバイダをsecurity.provider.nプロパティに追加し、jdk.security.provider.preferredプロパティで推奨プロバイダの順序付けを指定する場合は、jdk.security.provider.preferredで指定された推奨プロバイダが最初に選択されます。

したがって、FIPSプロバイダ構成のjdk.security.provider.preferredプロパティは構成しないようにすることをお薦めします。

jdk.security.provider.preferredのデフォルト値

jdk.security.provider.preferredプロパティは、デフォルトでは設定されておらず、アプリケーションのパフォーマンス・チューニングのためにのみ使用されます。

例8-16 jdk.security.provider.preferredプロパティのサンプル

jdk.security.provider.preferred プロパティを指定するための構文は、次のとおりです。

jdk.security.provider.preferred=AES/GCM/NoPadding:SunJCE, MessageDigest.SHA-256:SUN

この構文の説明は次のとおりです。

ServiceType

MessageDigest

Algorithm

AES/GCM/NoPadding、SHA-256

Provider

SunJCE、SUN

デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ

SSLSocketFactory.getDefaultまたはSSLServerSocketFactory.getDefaultを呼び出すことでデフォルトのSSLSocketFactoryまたはSSLServerSocketFactoryが作成され、このデフォルトのSSLSocketFactory (またはSSLServerSocketFactory)がJSSEリファレンス実装に由来するものであれば、デフォルトのSSLContextは必ずソケット・ファクトリに関連付けられます。デフォルトのソケット・ファクトリは、JSSE実装に由来します。

デフォルトのSSLContextは、デフォルトのKeyManagerおよびデフォルトのTrustManagerで初期化されます。javax.net.ssl.keyStoreシステム・プロパティおよび適切なjavax.net.ssl.keyStorePasswordシステム・プロパティでキーストアを指定すると(java.lang.Systemプロパティの指定方法を参照)、デフォルトのSSLContextで作成したKeyManagerは、指定したキーストアを管理するKeyManager実装になります。(実際にはデフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズで説明したとおりに実装されます。)システム・プロパティが指定されない場合は、KeyManagerが管理するキーストアは新しい空のキーストアです。

一般に、ハンドシェークでサーバーとして動作するピアには、クライアントへの認証の資格を取得するため、KeyManagerのキーストアが必要です。ただし、匿名の暗号化方式群のいずれかを選択する場合、サーバーのKeyManagerキーストアは必要ありません。また、サーバーがクライアント認証を要求しないかぎり、クライアントとして動作するピアには、KeyManagerキーストアは必要ありません。したがって、このような状況では、javax.net.ssl.keyStoreのシステム・プロパティ値が定義されていなくてもかまわない場合があります。

同様に、トラスト・ストアをjavax.net.ssl.trustStoreシステム・プロパティで指定すると、デフォルトのSSLContextで作成したTrustManagerが、指定したトラスト・ストアを管理するTrustManager実装になります。この場合、そのようなプロパティが存在しても指定するファイルが存在しなければ、トラストストアは使用されません。javax.net.ssl.trustStoreプロパティが存在しない場合は、デフォルトのトラスト・ストアを検索します。java-home/lib/security/jssecacertsという名前のトラストストアが見つかった場合、それが使用されます。そうでない場合、java-home/lib/security/cacertsというトラストストアが検索され、それが存在していれば使用されます。トラスト・ストアが見つからない場合、TrustManagerは新しい空のトラスト・ストアを管理します。

注意:

JDKには、java-home/lib/security/cacertsファイルで、限定された数の信頼されたルート証明書が付属しています。Java Platform, Standard Editionツール・リファレンスのkeytoolに記載したとおり、このファイルをトラストストアとして使用する場合は、ユーザーにこのファイルに含まれる証明書を管理(追加または削除)する責任があります。

接続先のサーバーの証明書構成によっては、ルート証明書を追加する必要がある場合もあります。適切なベンダーから必要な特定のルート証明書を入手してください。

javax.net.ssl.keyStoreTypeまたはjavax.net.ssl.keyStorePasswordシステム・プロパティも指定されている場合、それぞれがデフォルトのKeyManagerキーストア・タイプとパスワードとして扱われます。タイプが指定されていない場合、デフォルトのタイプはKeyStore.getDefaultType()メソッドが返すもので、keystore.typeセキュリティ・プロパティの値か、または、そうしたセキュリティ・プロパティが指定されていない場合はjksになります。キーストアのパスワードが指定されていない場合は空白の文字列「」とみなされます。

同様に、javax.net.ssl.trustStoreTypeまたはjavax.net.ssl.trustStorePasswordシステム・プロパティも指定されている場合、それぞれがデフォルトのトラストストア・タイプとパスワードとして扱われます。タイプが指定されていない場合、デフォルトのタイプは、KeyStore.getDefaultType()メソッドによって返されるタイプです。トラストストアのパスワードが指定されていない場合は空白の文字列「」とみなされます。

注意:
この項では、現在のJSSEリファレンス実装の動作を説明します。このセクションで説明するシステム・プロパティの名前と型(システムまたはセキュリティ)が引き続き同じで、将来のリリースにも存在するという保証はありせん。また、他のJSSE実装での検証や使用も保証されていません。実装で検証された場合、ここで説明するように、実装では、JSSEリファレンス実装と同じ方法でそれらを処理してください。

デフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズ

デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズで説明したように、デフォルトのSSLSocketFactoryまたはSSLServerSocketFactoryが作成され、このデフォルトのSSLSocketFactory (またはSSLServerSocketFactory)がJSSEリファレンス実装に由来する場合は、デフォルトのSSLContextは常にソケット・ファクトリに関連付けられます。

デフォルトのSSLContextは、KeyManagerおよびTrustManagerで初期化されます。デフォルトのSSLContextに提供されたKeyManagerとTrustManagerの両方またはどちらか一方は、前のセクションで説明したように、指定したキーストアまたはトラストストアを管理するための実装になります。

選択されるKeyManager実装は、まずssl.KeyManagerFactory.algorithmセキュリティ・プロパティを調査することによって決定されます。そのようなプロパティ値が指定されていると、指定したアルゴリズムのKeyManagerFactory実装が検索されます。実装を提供する最初のプロバイダの実装が使用されます。そのgetKeyManagers()メソッドが呼び出され、デフォルトのSSLContextに提供するKeyManagerが決定されます。技術的には、getKeyManagers()は、鍵データの型ごとに1つずつのKeyManagerで、KeyManagerオブジェクトの配列を返します。そのようなセキュリティ・プロパティ値が指定されていない場合、SunX509のデフォルト値を使用して検索を実行します。

注意:
SunX509アルゴリズムのKeyManagerFactory実装はSunJSSEプロバイダによって提供されます。それが指定するKeyManagerはjavax.net.ssl.X509KeyManager実装です。

同様に、選択されるTrustManager実装は、まずssl.TrustManagerFactory.algorithmセキュリティ・プロパティを調査することによって決定されます。そのようなプロパティ値が指定されていると、指定したアルゴリズムのTrustManagerFactory実装が検索されます。実装を提供する最初のプロバイダの実装が使用されます。そのgetTrustManagersメソッドが呼び出され、デフォルトのSSLContextに提供するTrustManagerが決定されます。技術的には、getTrustManagers()は、トラスト・データの型ごとに1つずつのTrustManagerで、TrustManagerオブジェクトの配列を返します。そのようなセキュリティ・プロパティ値が指定されていない場合、PKIXのデフォルト値を使用して検索を実行します。

注意:
PKIXアルゴリズムのTrustManagerFactory実装はSunJSSEプロバイダによって提供されます。それが指定するTrustManagerはjavax.net.ssl.X509TrustManager実装です。

注意:
この項では、現在のJSSEリファレンス実装の動作を説明します。このセクションで説明するシステム・プロパティの名前と型(システムまたはセキュリティ)が引き続き同じで、将来のリリースにも存在するという保証はありせん。また、他のJSSE実装での検証や使用も保証されていません。実装で検証された場合、ここで説明するように、実装では、JSSEリファレンス実装と同じ方法でそれらを処理してください。

無効化された制限付き暗号化アルゴリズム

一部の環境では、SSL/TLS/DTLSの使用時は、特定のアルゴリズムまたは鍵の長さが不適切である場合があります。Oracle JDKでは、jdk.certpath.disabledAlgorithmsおよびjdk.tls.disabledAlgorithmセキュリティ・プロパティを使用して、バージョンのネゴシエーション、暗号化方式群の選択、ピア認証および鍵交換メカニズムなど、SSL/TLS/DTLSプロトコルのネゴシエーション中にアルゴリズムを無効にします。これらのセキュリティ・プロパティが他のJDK実装によって使用される保証はないことに注意してください。これらのセキュリティ・プロパティの構文とその現在アクティブな値については、<java-home>/conf/security/java.securityファイルを参照してください。

- jdk.certpath.disabledAlgorithmsプロパティ: CertPathコードでは、jdk.certpath.disabledAlgorithmsセキュリティ・プロパティを使用して、CertPathチェック中に許可しないようにする必要があるアルゴリズムが決定されます。たとえば、TLSサーバーによって識別証明書チェーンが送信される場合、受け取ったチェーンをCertPath実装を使用して検証するクライアントTrustManagerでは、示された条件は許可されません。たとえば、次の行は、cacaertsキーストアにプレインストールされている信頼できるアンカーに連鎖するSHA1 TLSServer証明書と同様に、MD2ベースの証明書をブロックします。さらに、この行は、1024ビット未満のRSA鍵をブロックします。

jdk.certpath.disabledAlgorithms=MD2, SHA1 jdkCA & usage TLSServer, RSA keySize < 1024

- jdk.tls.disabledAlgorithmsプロパティ: SunJSSEコードでは、jdk.tls.disabledAlgorithmsセキュリティ・プロパティを使用して、SSL/TLS/DTLSプロトコル、暗号化方式群、鍵などが無効にされます。この構文は、jdk.certpath.disabledAlgorithmsセキュリティ・プロパティに似ています。たとえば、次の行は、SSLv3アルゴリズムおよびすべてのTLS_*_RC4_*暗号化方式群を無効にします。

jdk.tls.disabledAlgorithms=SSLv3, RC4

特定の条件が必要な場合は、java.securityファイル内のセキュリティ・プロパティで関連する値を削除するか、JSSEの初期化前に正しいセキュリティ・プロパティを動的に設定することで、それを再アクティブ化できます。

これらのセキュリティ・プロパティでは暗号化方式群の3番目のセットDisabledが事実上作成されることに注意してください。次のリストで、これら3つのセットを説明します。

- Disabled: 暗号化方式群に無効リスト内のコンポーネント(RC4など)が含まれている場合(たとえば、RC4がjdk.tls.disabledAlgorithmsセキュリティ・プロパティで指定されている)、その暗号化方式群は無効になり、接続ハンドシェークに考慮されません。

- Enabled: 接続に考慮される特定の暗号化方式群のリストです。

- Not Enabled: 接続に考慮されない、無効でない暗号化方式群のリストです。これらの暗号化方式群を再度有効にするには、適切なsetEnabledCipherSuites()またはsetSSLParameters()メソッドを呼び出します。

暗号化アルゴリズム・プロバイダのカスタマイズ

SunJSSEプロバイダは、その暗号化のすべてニーズに対してSunJCE実装を使用します。プロバイダは通常の位置に置くことが推奨されていますが、SunJCEプロバイダより前に登録することにより、他のJCAまたはJCEプロバイダからの実装を使用できます。

セキュリティ・プロパティ・ファイル<java-home>/conf/security/java.securityを介して静的に、またはjava.security.SecurityクラスのaddProvider()またはinsertProviderAt()メソッドを介して動的に、標準JCAメカニズム(プロバイダ実装の要求および獲得方法を参照)を使用してプロバイダを構成できます。

エフェメラルDiffie-Hellman鍵のサイズのカスタマイズ

SSL/TLS/DTLS接続では、ハンドシェーク中に内部的にephemeral Diffie-Hellman (DH)鍵が使用される場合があります。SunJSSEプロバイダでは、SSL/TLS/DTLSハンドシェーク中にephemeral DH鍵サイズの強度を柔軟にカスタマイズできます。

1024ビット未満のサイズのDiffie-Hellman (DH)鍵は強度が不十分なため、非推奨になりました。システム・プロパティjdk.tls.ephemeralDHKeySizeによって、ephemeral DH鍵のサイズをカスタマイズできます。このシステム・プロパティは、エクスポート可能な暗号化方式群のServerKeyExchangeメッセージ内のDH鍵のサイズには影響しません。これは、JSSE OracleプロバイダのDHE_RSA、DHE_DSSおよびDH_anonベースの暗号化方式群にのみ影響します。

このプロパティには次のいずれかの値を指定できます。

- 未定義: 1024ビットのサイズのDH鍵は、常にエクスポート不可能な暗号化方式群に使用されます。これが、このプロパティのデフォルト値です。

- legacy: JSSE Oracleプロバイダは、JDK 7以前のリリースのレガシーの動作を保持しています(512ビットおよび768ビットのサイズのephemeral DH鍵の使用など)。

- matched: エクスポート不可能な匿名の暗号化方式群の場合、ServerKeyExchangeメッセージ内のDH鍵のサイズは1024ビットです。X.509証明書ベースの認証(エクスポート不可能な暗号化方式群の)の場合、サイズが1024ビットから2048ビットでなければならないことを除けば、対応する認証鍵に一致するDH鍵のサイズが使われます。たとえば、認証証明書の公開鍵サイズが2048ビットの場合、暗号化方式群がエクスポート可能でないかぎり、ephemeral DH鍵サイズは2048ビットにすべきです。この鍵サイズ設定スキームにより、認証鍵と鍵交換鍵間の暗号化強度の整合性を確保しています。

- 1024以上2048以下の有効な整数: エクスポート不可能な暗号化方式群に、指定した値の固定のephemeral DH鍵サイズ(ビット単位)が使われます。

次の表に、システム・プロパティjdk.tls.ephemeralDHKeySizeの可能性のある各値について、DH鍵の最小および最大許容サイズをまとめています。

表8-9 システム・プロパティjdk.tls.ephemeralDHKeySizeのDH鍵サイズ

jdk.tls.ephemeralDHKeySizeの値

未定義

legacy

matched

整数値(固定)

エクスポート可能なDH鍵サイズ

512

512

512

512

エクスポート不可能な匿名暗号化方式群

1024

768

1024

固定鍵サイズは有効な整数プロパティ値で指定し、1024以上2048以下である必要があります。

認証証明書

1024

768

鍵サイズは認証証明書と同じですが、1024ビット以上2048ビット以下である必要があります。ただし、1024ビットより大きい、SunJCEプロバイダでサポートされている唯一のDH鍵サイズは、2048ビットです。

結果として、使用できる値は1024または2048のみになります。

固定鍵サイズは有効な整数プロパティ値で指定し、1024以上2048以下である必要があります。

最大断片長ネゴシエーション(MFLN)拡張のカスタマイズ

より小さい最大断片長をネゴシエーションするために、クライアントは、ClientHelloメッセージにmax_fragment_lengthタイプの拡張情報を含めることを選択できます。システム・プロパティjsse.enableMFLNExtensionを使用して、SSL/TLS/DTLS用のMFLN拡張を有効または無効にできます。

最大断片長ネゴシエーション

制約があるSSL/TLS/DTLSクライアントでは、メモリー制限や帯域幅制限が原因で、より小さい最大断片長をネゴシエーションしたほうが望ましい場合があります。より小さい最大断片長をネゴシエーションするために、クライアントは、(拡張された) ClientHelloメッセージにmax_fragment_lengthタイプの拡張情報を含めることを選択できます。RFC 6066を参照してください。

最大断片長が正常にネゴシエーションされると、SSL/TLS/DTLSクライアントおよびサーバーで、すぐにメッセージ(ハンドシェーク・メッセージを含む)の断片化を開始して、ネゴシエーションされた長さより長い断片が送信されないようにすることができます。

システム・プロパティjsse.enableMFLNExtension

システム・プロパティjsse.enableMFLNExtensionは、MFLN拡張を有効または無効にするために定義されます。jsse.enableMFLNExtensionは、デフォルトでは無効になっています。

このシステム・プロパティの値は、次のように設定できます。

表8-10 jsse.enableMFLNExtensionシステム・プロパティ

システム・プロパティ

説明

jsse.enableMFLNExtension=true

MFLN拡張を有効にします。SSLParameters.getMaximumPacketSize()で返された値が(2^12 + header-size)未満の場合、最大断片長ネゴシエーション拡張が有効になります。

jsse.enableMFLNExtension=false

MFLN拡張を無効にします。

最大および最小パケット・サイズの構成

SSLParameters.setMaximumPacketSizeメソッドを使用して、SSL/TLS/DTLSレコードのための予期される最大ネットワーク・パケット・サイズ(バイト単位)を設定します。

HelloVerifyRequestsなど、小さいハンドシェーク・メッセージが断片化されないように、パケット・サイズは256バイト未満にしないようにすることをお薦めします。

Transport Layer Security (TLS)再ネゴシエーションの問題

2009年秋に、SSL/TLSプロトコルの問題が見つかりました。IETF TLS Working Groupによってプロトコルの修正が開発され、JDKの現行バージョンにはこの修正が含まれています。このセクションでは、このプロトコル修正を含まない以前の実装との通信時における相互運用性の問題を含め、状況をさらに詳しく説明します。

この脆弱性により、選択されたプレーン・テキストを接頭辞としてTLS接続に注入できるというMan-In-The-Middle (MITM)攻撃を許していました。この脆弱性は、クライアントとサーバーがセッションのネゴシエーションに成功した後、傍受されたネットワーク通信を攻撃者が復号化または変更することを許すものではありません。

SSL/TLSの脆弱性の詳細は、次のリンクを参照してください。

- CVE-2009-3555 (Mitreの共通脆弱性識別子リストに掲載、2009年)

- TLS再ネゴシエーション攻撃の理解(Eric RescorlaのブログEducated Guessworkに掲載、2009年11月5日)。

この問題を解決するためのフェーズ別アプローチ

この問題の修正は、2つのフェーズに分けて扱われています。

- フェーズ1: プロトコル修正が開発されるまで、SSL/TLSの再ネゴシエーションをデフォルトで無効化する中間修正が、「March 30, 2010 Java SE and Java for Business Critical Patch Update」から利用できるようになりました。

- フェーズ2: IETFによって、再ネゴシエーション・プロトコルの問題を扱うRFC 5746が発表されました。次の表に、RFC 5746を実装してセキュアな再ネゴシエーションをサポートする修正を含むJDKおよびJREリリースを示します。

表8-11 TLS再ネゴシエーション問題の修正を含むJDKおよびJREリリース

JDKファミリ

脆弱性のあるリリース

フェーズ1の修正(再ネゴシエーションの無効化)

フェーズ2の修正(RFC 5746)

JDKおよびJRE 6

Update 18以前

Update 19-21

Update 22

JDKおよびJRE 5.0

Update 23以前

Update 24-25

Update 26

JDKおよびJRE 1.4.2

Update 25以前

Update 26-27

Update 28

注意:
再ネゴシエーションを必要としないアプリケーションはフェーズ2のデフォルト構成に影響を受けません。ただし、再ネゴシエーションを必要とするアプリケーション(最初は匿名のクライアント・ブラウズを許可するが、後でSSL/TLS認証済のクライアントを要求するWebサーバーなど)は:

- ピアもRFC 5746に準拠している場合は影響を受けません

- ピアがRFC 5746にアップグレードしていない場合は影響を受けます(詳細は次のセクションを参照してください)

フェーズ2修正の説明

SunJSSE実装は、RFC 5746に準拠したピアへの接続について、再ネゴシエーションをデフォルトで再び有効にします。つまり、セキュアに再ネゴシエーションを行うためには、クライアントとサーバーが両方ともRFC 5746をサポートする必要があります。まだアップグレードされていないピアとの接続について、SunJSSEではある程度の相互運用性モードが提供されていますが、ユーザーがクライアントとサーバーの両方の実装をできるだけ早く更新することを強く推奨します。

フェーズ2修正により、SunJSSEは現在3つの再ネゴシエーション相互運用性モードを用意しています。どのモードもRFC 5746のセキュアな再ネゴシエーションを完全にサポートしていますが、アップグレードされていないピアと通信する場合、次のような意味合いが加わります。

- 厳密モード: クライアントとサーバーの両方がRFC 5746にアップグレードされていること、および適切なRFC 5746メッセージを送信することが求められます。そうでない場合、初期の(または後続の)ハンドシェークが失敗して接続が切断されます。

- 相互運用モード(デフォルト): 正しいRFC 5746メッセージの使用はオプションですが、適切なメッセージが使用されない場合はレガシーの(元のSSL/TLS仕様の)再ネゴシエーションが無効になります。最初のレガシー接続は許可されますが、レガシーの再ネゴシエーションは無効化されます。これはセキュリティと相互運用性の最適な組み合わせであるため、デフォルト設定です。

- セキュアでないモード: レガシーの再ネゴシエーションを完全に許可します。レガシーのピアとの相互運用性がもっとも高いですが、本来のMITM攻撃に対して脆弱です。

3つのモード区分は、アップグレードされていないピアとの接続にのみ影響します。すべてのクライアントおよびサーバーで厳密モード(完全なRFC 5746モード)を使用することが望ましいのですが、配備されているすべてのSSL/TLS実装がRFC 5746をサポートするようになるまである程度時間がかかるため、今のところは相互運用モードがデフォルトになっています。

次の表に、クライアントまたはサーバー(あるいはその両方)がRFC 5746をサポートするように更新されている場合と更新されていない場合の様々な例でのモードに関する相互運用性情報を示しています。

表8-12 相互運用性情報

クライアント

サーバー

モード

更新済

更新済

すべてのモードで再ネゴシエーションがセキュリティ保護されます。

レガシー[1]

更新済

- 厳密クライアントが適切なRFC 5746メッセージを送信しない場合、最初の接続がサーバーによってすぐに切断されます(SSLHandshakeExceptionまたはhandshake_failure)。

- 相互運用可能 レガシー・クライアントからの初期接続(RFC 5746メッセージが欠落している)は許可されますが、再ネゴシエーションはサーバーによって許可されません。[3] [2]

- セキュアでないレガシー・クライアントとの接続および再ネゴシエーションは許可されますが、元のMITM攻撃に対して脆弱です。

更新済

レガシー[1]

- 厳密サーバーが正しいRFC 5746メッセージで応答しない場合、クライアントは接続をすぐに切断します(SSLHandshakeExceptionまたはhandshake_failure)。

- 相互運用可能 レガシー・サーバーからの初期接続(RFC 5746メッセージが欠落している)は許可されますが、再ネゴシエーションはサーバーによって許可されません。[2] [3]

- セキュアでないレガシー・サーバーとの接続および再ネゴシエーションは許可されますが、元のMITM攻撃に対して脆弱です。

レガシー[1]

レガシー[1]

既存のSSL/TLS動作を行い、MITM攻撃に対して脆弱です。

脚注[1] レガシーとは元のSSL/TLS仕様を意味します(つまり、RFC 5746でない)。

脚注[2] SunJSSEフェーズ1実装は、明示的に再有効化されないかぎり再ネゴシエーションを拒否します。再ネゴシエーションが再有効化された場合、それらは正しいRFC 5746メッセージを送信しないため、RFC 5746に準拠したピアによって「レガシー」として扱われます。

脚注[3] SSL/TLSでは、再ネゴシエーションをどちら側からでも開始できます。フェーズ1修正のように、アップグレードされていないピアと相互運用モードで通信しているアプリケーションが(SSLSocket.startHandshake()またはSSLEngine.beginHandshake()を使用して)再ネゴシエーションを開始しようとすると、アプリケーションはSSLHandshakeException (IOException)を受け取り、接続は停止されます(handshake_failure)。まだアップグレードされていないピアから再ネゴシエーション要求を受け取ったアプリケーションは、現在の接続のタイプに応じて応答します。

- TLSv1no_renegotiation(100)タイプの警告メッセージがピアに送信され、接続は開いたままになります。古いバージョンのSunJSSEは、no_renegotiationアラートを受け取ると接続を停止します。

- SSLv3 アプリケーションはSSLHandshakeExceptionを受け取り、接続は閉じられます(handshake_failure)。no_renegotiationアラートはSSLv3仕様に定義されていません。

次のシステム・プロパティを使用してモードを設定します(java.lang.Systemプロパティの指定方法を参照)。

- sun.security.ssl.allowUnsafeRenegotiation (フェーズ1で導入)は、レガシー(安全でない)再ネゴシエーションを許可するかどうかを制御します。

- sun.security.ssl.allowLegacyHelloMessages (フェーズ2で導入)は、適切なRFC 5746メッセージを必要とすることなくピアがハンドシェーク・プロセスを実行することを許可します。

注意:

システム・プロパティsun.security.ssl.allowUnsafeRenegotiationおよびsun.security.ssl.allowLegacyHelloMessagesは推奨されておらず、将来のJDKリリースで削除される可能性があります。

表8-13 相互運用性モードを設定するためのシステム・プロパティの値

モード

allowLegacyHelloMessages

allowUnsafeRenegotiation

厳密

false

false

相互運用(デフォルト)

true

false

セキュアでない

true

true

注意:
セキュアでないSSL/TLS再ネゴシエーションは、脆弱性が再確立されるため、再有効化しないでください。

SSL/TLS再ネゴシエーションに対する回避方法と代替方法

すべてのピアは、できるだけ早くRFC 5746準拠の実装に更新する必要があります。このRFC 5746修正を適用しても、再ネゴシエーションが必要な場合は、アップグレードされていないピアとの通信に影響が生じます。推奨されるいくつかの方法を次に示します。

- ピアを再構築して再ネゴシエーションを要求しないようにする。

再ネゴシエーションは通常、Webサーバーが最初は匿名のクライアント・ブラウズを許可するが、後でSSL/TLS認証済クライアントを要求する場合や、Webサーバーが最初は弱い暗号化方式群を許可するが、後で強い暗号化方式群を必要とする場合に、Webサーバーによって使用されます。代替策は、最初のネゴシエーション中にクライアント認証および強い暗号化方式群を必要とすることです。これを行うには、2つの選択肢があります。

- アプリケーションに、あるポイントに到達して、再ネゴシエーションが必要になるまでの「ブラウズ・モード」がある場合、「ブラウズ・モード」を削除し、すべての初期接続を強化するようにサーバーを再構築できます。

- サーバーを2つのエンティティに分割して、1つのエンティティ上でブラウズ・モードを実行し、2つ目のエンティティをよりセキュアなモードで使用します。ネゴシエーション・ポイントに到達したら、関連情報をサーバー間で転送します。

これらのどちらのオプションも、ある程度の作業が必要ですが、元のセキュリティ上の欠陥が再度発生することはありません。

- システム・プロパティを使用して、再ネゴシエーション相互運用性モードを「セキュアでない」に設定します。

フェーズ2修正の説明を参照してください。

TLS実装の詳細

RFC 5746では2つの新しいデータ構造が定義されており、上級ユーザーのために、ここで説明します

- Signaling Cipher Suite Value (SCSV)と呼ばれる擬似暗号化方式群、「TLS_EMPTY_RENEGOTIATION_INFO_SCSV」

- Renegotiation Info (RI)と呼ばれるTLS拡張。

これらのいずれも、実装がRFC 5746に準拠していることと、セキュアな再ネゴシエーションが実行できることを通知するために使用できます。2009年11月から2010年2月までのIETF電子メール・ディスカッションを参照してください。

RFC 5746により、クライアントは最初のClientHelloでSCSVまたはRIを送信できます。相互運用性を最大限に高めるため、SunJSSEはデフォルトでSCSVを使用しますが、これは、いくつかのTLSサーバー/SSLサーバーが不明な拡張機能を正しく処理できないためです。有効化された暗号化方式群(SSLSocket.setEnabledCipherSuites()またはSSLEngine.setEnabledCipherSuites())にSCSVが存在することによって、最初のClientHello内でSCSVを送信するか、あるいはRIをかわりに送信すべきかどうかが判別されます。

SSLv2はSSL/TLS拡張機能をサポートしません。SSLv2Helloプロトコルが有効化された場合、SCSVが最初のClientHello内で送信されます。

フェーズ1修正の説明

前述のとおり、フェーズ1修正は、RFC 5746に準拠した修正が開発されるまでの間、再ネゴシエーションをデフォルトで無効にするためのものでした。再ネゴシエーションは、sun.security.ssl.allowUnsafeRenegotiationシステム・プロパティを設定することによって再び有効化できました。フェーズ2修正では同じsun.security.ssl.allowUnsafeRenegotiationシステム・プロパティを使用するだけでなく、それにRFC 5746メッセージを使用させる必要もあります。

すべてのアプリケーションを、できるだけ早くフェーズ2 RFC 5746修正にアップグレードする必要があります。

注意:

システム・プロパティsun.security.ssl.allowUnsafeRenegotiationおよびsun.security.ssl.allowLegacyHelloMessagesは推奨されておらず、将来のJDKリリースで削除される可能性があります。

SSL/TLS再ネゴシエーションにおける安全でないサーバー証明書変更の許可

次の場合、SSL/TLS再ネゴシエーションでサーバー証明書を変更することは、安全でないことがあります。

1. エンドポイント識別がSSL/TLSハンドシェークで有効でない場合、および

2. 以前のハンドシェークがセッション再開省略初期ハンドシェークである場合、および

3. 両方の証明書によって表されるアイデンティティが異なると見なせる場合。

2つの証明書は、次の場合に、同じアイデンティティを表すと見なせます。

1. IPアドレスのサブジェクトの代替名が両方の証明書に存在する場合、同一であるはずです。または、

2. DNS名のサブジェクトの代替名が両方の証明書に存在する場合、同一であるはずです。または、

3. サブジェクト・フィールドが両方の証明書に存在する場合、証明書のサブジェクトと発行人は同一であるはずです。

JDK 8u25以降では、SSL/TLS再ネゴシエーションでの安全でないサーバー証明書の変更は、デフォルトでできません。新しいシステム・プロパティjdk.tls.allowUnsafeServerCertChangeを使用して、SSL/TLS再ネゴシエーションにおける安全でないサーバー証明書変更を制限するかどうかを定義できます。

このシステム・プロパティのデフォルト値はfalseです。

注意:
どうしても必要でないかぎり、このシステム・プロパティをtrueに設定しないでください。安全でないサーバー証明書変更の脆弱性が再び確立される可能性があります。

ハードウェア高速化およびスマート・カードのサポート

Java暗号化アーキテクチャ(JCA)は、暗号化、鍵生成と鍵合意およびメッセージ認証コード(MAC)アルゴリズム用のフレームワークと実装を提供するパッケージ・セットです。(Java暗号化アーキテクチャ(JCA)リファレンス・ガイドを参照。)SunJSSEプロバイダは、すべての暗号化操作にJCAを排他的に使用するので、JCAのRSA PKCS#11のサポートを含め、JCEの機能や拡張機能を自動的に利用できます。このサポートによりSunJSSEプロバイダは、ハードウェア暗号化アクセラレータを使用してパフォーマンスを大幅に向上させ、キーストアとしてスマート・カードを使用し、鍵および信頼性管理の柔軟性を高めることができます。

基盤となるアクセラレータ・ハードウェアを使用するようにOracle PKCS#11プロバイダが構成され、そのPKCS#11プロバイダを使用するようにJCAが構成されていれば、ハードウェア暗号化アクセラレータは自動的に使用されます。プロバイダは、プロバイダ・リストにある他のJCAプロバイダより前に構成する必要があります。Oracle PKCS#11プロバイダの構成方法の詳細は、PKCS#11リファレンス・ガイドを参照してください。

スマートカードをキーストアおよびトラストストアとして使用するためのJSSEの構成

JCAでのPKCS#11のサポートにより、キーストアとしてスマートカードにアクセスすることもできます。JSSEによって使用されるキーストアのタイプと場所の構成方法の詳細は、JSSEのカスタマイズを参照してください。スマートカードをキーストアまたはトラストストアとして使用するには、javax.net.ssl.keyStoreTypeおよびjavax.net.ssl.trustStoreTypeシステム・プロパティをそれぞれpkcs11に設定し、javax.net.ssl.keyStoreおよびjavax.net.ssl.trustStoreシステム・プロパティをそれぞれNONEに設定します。特定のプロバイダを使用するように指定するには、javax.net.ssl.keyStoreProviderおよびjavax.net.ssl.trustStoreProviderシステム・プロパティを使用します(たとえば、それらをSunPKCS11-joeに設定します)。これらのプロパティを使用することにより、以前はファイルベースのキーストアにアクセスするためにこれらのプロパティに依存していたアプリケーションを構成して、アプリケーションに変更を加えずにスマートカードのキーストアを使用できます。

アプリケーションによっては、キーストアをプログラムで使用する必要があります。こうしたアプリケーションでは、引き続き既存のAPIを使用してKeystoreをインスタンス化し、キー・マネージャとトラスト・マネージャに渡すことができます。Keystoreインスタンスがスマートカードに基づくPKCS#11キーストアを参照する場合は、JSSEアプリケーションがスマートカードの鍵にアクセスすることになります。

複数の動的キーストア

スマートカードおよびその他の取り外し可能トークンには、X509KeyManagerについて追加の要件があります。Javaアプリケーションの存続期間の間、異なるスマートカードをスマートカード・リーダーに入れることができ、それらのスマートカードは異なるパスワードを使用して保護できます。

KeyStore.Builderクラスは、KeyStoreオブジェクトの構造と初期化を抽象化します。パスワードのプロンプト用にCallbackHandlerの使用をサポートし、そのサブクラスを使用して、アプリケーションに望まれる追加機能をサポートできます。たとえば、Builderを実装して、個々のKeyStoreエントリを異なるパスワードで保護するようにすることが可能です。その後、KeyStoreBuilderParametersクラスを使用し、これらのBuilderオブジェクトを1つ以上使用してKeyManagerFactoryを初期化できます。

NewSunX509と呼ばれる、SunJSSEプロバイダのX509KeyManager実装は、これらのパラメータをサポートしています。複数の証明書が使用可能な場合は、適切な鍵を使用する証明書を選択し、期限切れの証明書より有効な証明書を優先させます。

例8-17では、PKCS#11キーストア(スマートカードを使用できる)とPKCS#12ファイルベース・キーストアの両方を使用するようJSSEに指示する方法を示します。

例8-17 PKCS#11およびPKCS#12ファイルベース・キーストアを使用するサンプル・コード

import javax.net.ssl.*;
import java.security.KeyStore.*;
// ...

// Specify keystore builder parameters for PKCS#11 keystores
Builder scBuilder = Builder.newInstance("PKCS11", null,
new CallbackHandlerProtection(myGuiCallbackHandler));

// Specify keystore builder parameters for a specific PKCS#12 keystore
Builder fsBuilder = Builder.newInstance("PKCS12", null,
new File(pkcsFileName), new PasswordProtection(pkcsKsPassword));

// Wrap them as key manager parameters
ManagerFactoryParameters ksParams = new KeyStoreBuilderParameters(
Arrays.asList(new Builder[] { scBuilder, fsBuilder }) );

// Create KeyManagerFactory
KeyManagerFactory factory = KeyManagerFactory.getInstance("NewSunX509");

// Pass builder parameters to factory
factory.init(ksParams);

// Use factory
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(factory.getKeyManagers(), null, null);

Kerberos暗号化方式群

SunJSSEプロバイダは、RFC 2712で規定されるKerberos暗号化方式群をサポートします。次の暗号化方式群がサポートされていますが、デフォルトでは有効になっていません。

注意:
DTLSバージョン1.0とDTLSバージョン1.2によると、RC4暗号化方式群はDTLSとは使用できません。

- TLS_KRB5_WITH_RC4_128_SHA

- TLS_KRB5_WITH_RC4_128_MD5

- TLS_KRB5_WITH_3DES_EDE_CBC_SHA

- TLS_KRB5_WITH_3DES_EDE_CBC_MD5

- TLS_KRB5_WITH_DES_CBC_SHA

- TLS_KRB5_WITH_DES_CBC_MD5

- TLS_KRB5_EXPORT_WITH_RC4_40_SHA

- TLS_KRB5_EXPORT_WITH_RC4_40_MD5

- TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA

- TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5

これらの暗号化方式群を使用できるようにするには、明示的に指定する必要があります。SSLEngine.setEnabledCipherSuites(String[])メソッドとSSLSocket.setEnabledCipherSuites(String[])メソッドについては、APIドキュメントを参照してください。その他のすべてのSSL/TLS/DTLS暗号化方式群と同様、暗号化方式群がピアによってサポートされていない場合は、暗号化のネゴシエーション時に選択されません。また、アプリケーションまたはサーバーが必要なKerberos資格を取得できない場合は、Kerberos符号化方式も選択されません。

次に、TLS_KRB5_WITH_DES_CBC_SHA暗号化方式群のみを使用するTLSクライアントの例を示します。

// Create socket
SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(tlsServer, serverPort);

// Enable only one cipher suite
String enabledSuites[] = { "TLS_KRB5_WITH_DES_CBC_SHA" };
sslSocket.setEnabledCipherSuites(enabledSuites);

Kerberos要件

JSSEでKerberos符号化方式を使用する前に、配備されている環境でKerberosインフラストラクチャを設定しておく必要があります。特に、TLSクライアントとサーバーの両方に、Kerberos Key Distribution Center (KDC)によるアカウントが設定されていことが必要です。実行時にKerberos暗号化方式群の1つ以上が有効化されると、TLSクライアントとサーバーは、それぞれのアカウントに関連付けられている自身のKerberosクレデンシャルをKDCから取得します。たとえば、Kerberos領域IMC.ORGのマシンmach1.imc.orgで動作するTLSサーバーは、host/mach1.imc.org@IMC.ORGという名前のアカウントを持ち、IMC.ORG用のKDCを使用するように構成されている必要があります。Kerberos要件を参照してください。

アプリケーションは、Java Authentication and Authorization Service (JAAS)リファレンス・ガイドとKerberosログイン・モジュールを使用して、自身のKerberos資格を取得できます。JDKには、Krb5LoginModuleが付属しています。JSSEでのKerberos暗号化方式群は、JAASプログラミングありまたはなしでJAASおよびJava GSS-APIチュートリアルを使用する方法と同様に、JAASプログラミングありでもなしでも使用できます。

JAASプログラミングなしでJSSEでKerberos暗号化方式群を使用するには、TLSサーバーJAAS構成エントリ用にcom.sun.net.ssl.serverまたはother、およびTLSクライアント用にcom.sun.net.ssl.clientまたはotherというインデックス名を使用し、javax.security.auth.useSubjectCredsOnlyシステム・プロパティをfalseに設定する必要があります。たとえば、JAASプログラミングを使用しないTLSサーバーには次のJAAS構成ファイルを使用できます。

com.sun.net.ssl.server {
com.sun.security.auth.module.Krb5LoginModule required
principal="host/mach1.imc.org@IMC.ORG"
useKeyTab=true
keyTab=mach1.keytab
storeKey=true;
};

JAASプログラミングなしでJava GSSおよびKerberosを使用する方法の例は、JDK 8ドキュメント内のチュートリアルJAASプログラミングなしでの安全なメッセージ交換のためのJava GSS-APIの使用で説明されています。Java GSS呼出しをJSSE呼出しに置き換えることにより、JSSEの使用例に適応できます。

JAASプログラミングありでKerberos暗号化方式群を使用するには、任意のインデックス名を使用できます。これは、アプリケーションが、インデックス名を使用してJAAS LoginContextを作成し、JSSE呼出しをSubject.doAs()またはSubject.doAsPrivileged()呼出しの内部にラップする役割を持つためです。Java GSSおよびKerberosでJAASを使用する方法の例は、JDK 8ドキュメント内のチュートリアル安全なメッセージ交換のためのJAASログイン・ユーティリティおよびJava GSS-APIの使用で説明されています。Java GSS呼出しをJSSE呼出しに置き換えることにより、JSSEの使用例に適応できます。

Kerberosを使用するJSSEアプリケーションを使用または構成する場合の問題については、JDK 8ドキュメント内のJava GSSチュートリアルのトラブルシューティングを参照してください。

ピアのアイデンティティ情報

SSL/TLS/DTLS接続のピアの識別情報を判別するには、次のクラスで、getPeerPrincipal()メソッドを使用します。

- javax.net.ssl.SSLSession

- javax.net.ssl.HttpsURLConnection

- javax.net.HandshakeCompletedEvent

同様に、(ローカル・エンティティを識別するために)ピアに送信された識別情報を取得するには、これらのクラスでgetLocalPrincipal()メソッドを使用します。X509ベースの暗号化方式群の場合、これらのメソッドはjavax.security.auth.x500.X500Principalのインスタンスを返します。Kerberos暗号化方式群の場合、これらのメソッドはjavax.security.auth.kerberos.KerberosPrincipalのインスタンスを返します。

JSSEアプリケーションは、getPeerCertificates()と、javax.net.ssl.SSLSession、javax.net.ssl.HttpsURLConnection、javax.net.HandshakeCompletedEventの類似のメソッドを使用して、ピアに関する情報を取得します。ピアに証明書がない場合、SSLPeerUnverifiedExceptionがスローされます。

アプリケーションでピアの識別情報またはピアに送信された識別情報のみを判別する必要がある場合は、それぞれgetPeerPrincipal()およびgetLocalPrincipal()メソッドを使用してください。getPeerCertificates()およびgetLocalCertificates()メソッドは、それらの証明書の内容を調べる必要がある場合にのみ使用してください。また、アプリケーションでは、認証されたピアに証明書がない場合の処理を準備しておく必要があります。

セキュリティ・マネージャ

セキュリティ・マネージャが有効な場合、ピアとの通信に必要なSocketPermissionに加えて、Kerberos暗号化方式群を使用するTLSクライアント・アプリケーションには次のアクセス権も必要です。

javax.security.auth.kerberos.ServicePermission(serverPrincipal, "initiate");

説明は次のとおりです。

serverPrincipal

TLSクライアントが通信するTLSサーバーのKerberosプリンシパル名を示します(host/mach1.imc.org@IMC.ORGなど)。

TLSサーバー・アプリケーションには、次のアクセス権が必要です。

javax.security.auth.kerberos.ServicePermission(serverPrincipal, "accept");

説明は次のとおりです。

serverPrincipal

TLSサーバーのKerberosプリンシパル名を示します(host/mach1.imc.org@IMC.ORGなど)。

サーバーまたはクライアントがKDCに接続する必要がある場合(そのクレデンシャルがローカルにキャッシュされていない場合など)、次のアクセス権も必要です。

javax.security.auth.kerberos.ServicePermission(tgtPrincipal, "initiate");

説明は次のとおりです。

tgtPrincipal

KDCのプリンシパル名を示します(krbtgt/IMC.ORG@IMC.ORGなど)。

その他のキーストア形式(PKCS12)

PKCS#12 (Personal Information Exchange Syntax Standard)では、移植可能な保存形式、およびユーザーの非公開鍵、証明書、その他の秘密およびほかの項目の転送について規定されています。SunJSSEプロバイダは、PKCS12ファイルの読取りおよび書込みのためのPKCS12 java.security.KeyStore形式の完全な実装を提供します。この形式は、鍵と証明書のインポートおよびエクスポートのために、Mozilla Firefox、Microsoft Internet ExplorerおよびOpenSSLなど、他のツールキットやアプリケーションでもサポートされています。たとえば、これらの実装は、クライアントの証明書と鍵を.p12ファイル名拡張子を使用してファイルにエクスポートできます。

SunJSSEプロバイダでは、PKCS12のキーストア・タイプを使用して、KeyStore APIを介してPKCS12鍵にアクセスできます。さらにkeytoolコマンドと、pkcs12に設定された-storetypeオプションを使用して、インストールされた鍵および関連する証明書を表示できます。Java Platform, Standard Editionツール・リファレンスでkeytoolを参照してください。

Server Name Indication (SNI)拡張

SNI拡張は、SSL/TLS/DTLSプロトコルを拡張し、クライアントがハンドシェーク時に接続を試みるサーバー名を示す機能です。サーバーはServer Name Indicationの情報を使って、特定のSSLSocketまたはSSLEngineインスタンスが接続を受け入れる必要があるかどうかを判定できます。たとえば、単一の基礎となるネットワーク・アドレスで複数の仮想または名前ベースのサーバーがホストされている場合、サーバー・アプリケーションは、SNI情報を使用して、このサーバーが、クライアントがアクセスしようとしている正しいサーバーであるかどうかを判断できます。このクラスのインスタンスは、サーバーによって、ホスト名などの特定のタイプの受け付け可能なサーバー名を確認するために使用できます。TLS拡張(RFC 6066)の第3項を参照してください。

クライアント・アプリケーションの開発者は、SSLParameters.setServerNames(List<SNIServerName> serverNames)メソッドを使用して、サーバー名の表示を明示的に設定できます。例8-18を参照してください。

サーバー・アプリケーションの開発者はSNIMatcherクラスを使用して、サーバー名の表示を認識する方法を決定できます。例8-19と例8-20では、この機能を示します。

例8-18 Server Name Indicationを設定するサンプル・コード

次のコード例では、メソッドSSLParameters.setServerNames(List<SNIServerName> serverNames)を使用してサーバー名表示を設定する方法を示します。

SSLSocketFactory factory = ...
SSLSocket sslSocket = factory.createSocket("172.16.10.6", 443);
// SSLEngine sslEngine = sslContext.createSSLEngine("172.16.10.6", 443);

SNIHostName serverName = new SNIHostName("www.example.com");
List<SNIServerName> serverNames = new ArrayList<>(1);
serverNames.add(serverName);

SSLParameters params = sslSocket.getSSLParameters();
params.setServerNames(serverNames);
sslSocket.setSSLParameters(params);
// sslEngine.setSSLParameters(params);

例8-19 SSLSocketクラスを使用してSNIを認識するサンプル・コード

次のコード例では、サーバー・アプリケーションでSNIMatcherクラスを使用してサーバー名表示の認識方法を決定する方法を示します。

SSLSocket sslSocket = sslServerSocket.accept();

SNIMatcher matcher = SNIHostName.createSNIMatcher("www\\.example\\.(com|org)");
Collection<SNIMatcher> matchers = new ArrayList<>(1);
matchers.add(matcher);

SSLParameters params = sslSocket.getSSLParameters();
params.setSNIMatchers(matchers);
sslSocket.setSSLParameters(params);

例8-20 SSLServerSocketクラスを使用してSNIを認識するサンプル・コード

次のコード例では、サーバー・アプリケーションでSNIMatcherクラスを使用してサーバー名表示の認識方法を決定する方法を示します。

SSLServerSocket sslServerSocket = ...;

SNIMatcher matcher = SNIHostName.createSNIMatcher("www\\.example\\.(com|org)");
Collection<SNIMatcher> matchers = new ArrayList<>(1);
matchers.add(matcher);

SSLParameters params = sslServerSocket.getSSLParameters();
params.setSNIMatchers(matchers);
sslServerSocket.setSSLParameters(params);

SSLSocket sslSocket = sslServerSocket.accept();

次のリストは、ClientHelloメッセージで様々なサーバー名表示要求を受け取った場合のSNIMatcherの動作の例を示しています。

- www\\.example\\.comに構成されたマッチャ:

- 要求されたホスト名がwww.example.comの場合、それは受け付けられ、ServerHelloメッセージで確認が送信されます。

- 要求されたホスト名がwww.example.orgの場合、それはunrecognized_name致命的エラーで拒否されます。

- 要求されたホスト名がないか、空の場合、要求は受け付けられますが、ServerHelloメッセージで確認が送信されません。

- www\\.invalid\\.comに構成されたマッチャ:

- 要求されたホスト名がwww.example.comの場合、それはunrecognized_name致命的エラーで拒否されます。

- 要求されたホスト名がwww.example.orgの場合、それは受け付けられ、ServerHelloメッセージで確認が送信されます。

- 要求されたホスト名がないか、空の場合、要求は受け付けられますが、ServerHelloメッセージで確認が送信されません。

- マッチャが構成されていない:

要求されたすべてのホスト名が受け付けられますが、ServerHelloメッセージで確認は送信されません。

SNI拡張を実装する新しいクラスの説明については、次を参照してください。

- StandardConstantsクラス

- SNIServerNameクラス

- SNIMatcherクラス

- SNIHostNameクラス

たとえば、「Server Name Indication (SNI)拡張の使用」を参照してください。

TLSのアプリケーション層プロトコル・ネゴシエーション

アプリケーション層プロトコル・ネゴシエーション(ALPN)を使用してTLS接続のためにアプリケーション・プロトコルをネゴシエーションします。

ALPNとは

アプリケーションによっては、TLSハンドシェークが完了する前に、共有アプリケーション・レベル値のネゴシエーションが必要な場合があります。たとえば、HTTP/2では、特定のTCPまたはUDPポートで使用されるか使用できるHTTPバージョン(h2、spdy/3、http/1.1)の確立に役立つように、アプリケーション層プロトコル・ネゴシエーションのメカニズムが使用されます。ALPN (RFC 7301)では、クライアントとサーバーとの間のネットワーク・ラウンドトリップを増やすことなく、これが行われます。HTTP/2の場合、プロトコルは、接続をネゴシエーションする前に確立される必要があります。これは、クライアントとサーバーが、通信を開始する前に、使用するHTTPのバージョンを把握する必要があるためです。ALPNを使用しないと、アプリケーション・プロトコルHTTP/1とHTTP/2を同じポートで使用できません。

クライアントは、TLSハンドシェークの開始時にALPN拡張を使用して、サポートされているアプリケーション・プロトコルのリストをClientHelloの一部としてサーバーに送信します。サーバーは、ClientHello内のサポートされているアプリケーション・プロトコルのリストを読み取り、サポートされているプロトコルのうちどれが望ましいかを判断します。次に、ネゴシエーション結果とともにServerHelloメッセージをクライアントに送り返します。メッセージには、選択されているプロトコルの名前、またはプロトコルが選択されていないことが示されている場合があります。

そのようにして、アプリケーション・プロトコル・ネゴシエーションは、ネットワーク・ラウンドトリップを増やすことなく、TLSハンドシェーク内で行うことができ、必要に応じてサーバーで異なる証明書を各アプリケーション・プロトコルに関連付けることができるようにします。

他の多くのTLS拡張と異なり、この拡張では、セッションのプロパティは確立されず、接続のプロパティのみとなります。それが、ネゴシエーションした値がSSLSessionではなくSSLSocket/SSLEngineで見つかる理由です。セッション再開またはセッション・チケットが使用される場合(サーバー側の状態なしでのTLSセッション再開を参照)、前にネゴシエーションした値は無関係であり、新しいハンドシェーク・メッセージ内の値のみが考慮されます。

クライアントでのALPNの設定

クライアントによってサポートされているアプリケーション層プロトコル・ネゴシエーション(ALPN)値を設定します。サーバーとのハンドシェーク中に、サーバーは、クライアントのアプリケーション・プロトコル・リストを読み取り、どれが最も適切かを判断します。

クライアントの場合は、SSLParameters.setApplicationProtocols(String[])メソッドの次にSSLSocketまたはSSLEngineのsetSSLParametersメソッドを使用して、サーバーに送信するためのアプリケーション・プロトコルを設定します。

例8-21 JavaクライアントでのALPN値の設定および取得のサンプル・コード

たとえば、ここでは、クライアントでthreeとtwoというALPN値を設定する手順を示します。

コードを実行するには、プロパティjavax.net.ssl.trustStoreを有効なルート証明書に設定する必要があります。(これはコマンド行で実行できます)。

import java.io.*;
import java.util.*;
import javax.net.ssl.*;
public class SSLClient {
public static void main(String[] args) throws Exception {

// Code for creating a client side SSLSocket
SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket) sslsf.createSocket("localhost", 9999);

// Get an SSLParameters object from the SSLSocket
SSLParameters sslp = sslSocket.getSSLParameters();

// Populate SSLParameters with the ALPN values
// On the client side the order doesn't matter as
// when connecting to a JDK server, the server's list takes priority
String[] clientAPs = {"three", "two"};
sslp.setApplicationProtocols(clientAPs);

// Populate the SSLSocket object with the SSLParameters object
// containing the ALPN values
sslSocket.setSSLParameters(sslp);

sslSocket.startHandshake();

// After the handshake, get the application protocol that has been negotiated
String ap = sslSocket.getApplicationProtocol();
System.out.println("Application Protocol client side: \"" + ap + "\"");

// Do simple write/read
InputStream sslIS = sslSocket.getInputStream();
OutputStream sslOS = sslSocket.getOutputStream();
sslOS.write(280);
sslOS.flush();
sslIS.read();
sslSocket.close();
}
}

このコードを実行し、ALPN値one、twoおよびthreeが設定されているJavaサーバーにClientHelloが送信されると、出力は次のようになります。

Application Protocol client side: two
ハンドシェークの詳細は、SSLハンドシェークを参照してください。ハンドシェーク中にネゴシエーションの結果を確認することもできます。ハンドシェーク中のネゴシエーション済ALPN値の特定を参照してください。

サーバーでのデフォルトALPNの設定

サーバーでALPN値を設定することで、デフォルトのALPNメカニズムを使用して、適切なアプリケーション・プロトコルを決定します。

サーバーでALPNのデフォルト・メカニズムを使用するには、クライアントでALPNを設定したときに行ったように(クライアントでのALPNの設定の項を参照)、設定するALPN値をSSLParametersオブジェクトに移入してから、このSSLParametersオブジェクトを使用してSSLSocketオブジェクトかSSLEngineオブジェクトにこれらのパラメータを移入します。サーバーで設定されたALPN値のうち、ClientHelloに含まれているALPN値のいずれかに一致する最初の値が選択され、ServerHelloの一部としてクライアントに返されます。

例8-22 サーバーでのデフォルトALPN値ネゴシエーションのサンプル・コード

次に、プロトコル・ネゴシエーションにデフォルトの手法を使用するJavaサーバー用コードを示します。コードを実行するには、プロパティjavax.net.ssl.keyStoreを有効なキーストアに設定する必要があります。(これはコマンド行で実行できます。JSSEで使用するキーストアの作成を参照)。

import java.util.*;
import javax.net.ssl.*;
public class SSLServer {
public static void main(String[] args) throws Exception {

// Code for creating a server side SSLSocket
SSLServerSocketFactory sslssf =
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket sslServerSocket =
(SSLServerSocket) sslssf.createServerSocket(9999);
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();

// Get an SSLParameters object from the SSLSocket
SSLParameters sslp = sslSocket.getSSLParameters();

// Populate SSLParameters with the ALPN values
// As this is server side, put them in order of preference
String[] serverAPs ={ "one", "two", "three" };
sslp.setApplicationProtocols(serverAPs);

// If necessary at any time, get the ALPN values set on the
// SSLParameters object with:
// String serverAPs = sslp.setApplicationProtocols();

// Populate the SSLSocket object with the ALPN values
sslSocket.setSSLParameters(sslp);

sslSocket.startHandshake();

// After the handshake, get the application protocol that
// has been negotiated

String ap = sslSocket.getApplicationProtocol();
System.out.println("Application Protocol server side: \"" + ap + "\"");

// Continue with the work of the server
InputStream sslIS = sslSocket.getInputStream();
OutputStream sslOS = sslSocket.getOutputStream();
sslIS.read();
sslOS.write(85);
sslOS.flush();
sslSocket.close();
}
}

このコードを実行し、Javaクライアントによって、ALPN値threeおよびtwoを含むClientHelloが送信されると、出力は次のようになります。

Application Protocol server side: two
ハンドシェークの詳細は、SSLハンドシェークを参照してください。ハンドシェーク中にネゴシエーションの結果を確認することもできます。ハンドシェーク中のネゴシエーション済ALPN値の特定を参照してください。

サーバーでのカスタムALPNの設定

コールバック・メソッドを設定することで、カスタムのALPNメカニズムを使用して、適切なアプリケーション・プロトコルを決定します。

サーバーのデフォルト・ネゴシエーション・プロトコルを使用しない場合は、SSLEngineまたはSSLSocketのsetHandshakeApplicationProtocolSelectorメソッドを使用して、現在までのハンドシェーク状態を確認できるBiFunction (ラムダ)コールバックを登録し、クライアントのアプリケーション・プロトコル・リストおよびその他の関連情報に基づいて選択できます。たとえば、推奨されている暗号化方式群、または選択に当たって取得できるServer Name Indication (SNI)やその他のデータの使用を検討できます。カスタム・ネゴシエーションを使用する場合は、setApplicationProtocolsメソッドによって設定された値(デフォルト・ネゴシエーション)は無視されます。

例8-23 サーバーでのカスタムALPN値ネゴシエーションのサンプル・コード

次に、プロトコル・ネゴシエーションにカスタム・メカニズムを使用するJavaサーバー用コードを示します。コードを実行するには、プロパティjavax.net.ssl.keyStoreを有効な証明書に設定する必要があります。(これはコマンド行で実行できます。JSSEで使用するキーストアの作成を参照)。

import java.util.*;
import javax.net.ssl.*;
public class SSLServer {
public static void main(String[] args) throws Exception {

// Code for creating a server side SSLSocket
SSLServerSocketFactory sslssf =
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket sslServerSocket =
(SSLServerSocket) sslssf.createServerSocket(9999);
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();

// Code to set up a callback function
// Pass in the current SSLSocket to be inspected and client AP values
sslSocket.setHandshakeApplicationProtocolSelector(
(serverSocket, clientProtocols) -> {
SSLSession handshakeSession = serverSocket.getHandshakeSession();
// callback function called with current SSLSocket and client AP values
// plus any other useful information to help determine appropriate
// application protocol. Here the protocol and ciphersuite are also
// passed to the callback function.
return chooseApplicationProtocol(
serverSocket,
clientProtocols,
handshakeSession.getProtocol(),
handshakeSession.getCipherSuite());
});

sslSocket.startHandshake();

// After the handshake, get the application protocol that has been
// returned from the callback method.

String ap = sslSocket.getApplicationProtocol();
System.out.println("Application Protocol server side: \"" + ap + "\"");

// Continue with the work of the server
InputStream sslIS = sslSocket.getInputStream();
OutputStream sslOS = sslSocket.getOutputStream();
sslIS.read();
sslOS.write(85);
sslOS.flush();
sslSocket.close();
}

// The callback method. Note how the parameters match the call within
// the setHandshakeApplicationProtocolSelector method above.
public static String chooseApplicationProtocol(SSLSocket serverSocket,
List<String> clientProtocols, String protocol, String cipherSuite ) {
// For example, check the cipher suite and return an application protocol
// value based on that.
if (cipherSuite.equals("<--a_particular_ciphersuite-->")) {
return "three";
} else {
return "";
}
}
}

暗号化方式群が、このコードの実行時に条件文で指定されたものと一致する場合は、値threeが返されます。そうでない場合は、空の文字列が返されます。

BiFunctionオブジェクトの戻り値がStringであることに注意してください。これは、アプリケーション・プロトコル名になるか、通知された値をどれも受け入れられないことを示すnullになります。戻り値が空のStringである場合は、アプリケーション・プロトコルの指示は使用されません。戻り値がnull (選択値なし)であるか、ピアによって通知されていない値である場合は、基になるプロトコルで、実行するアクションが決定されます。(たとえば、サーバー・コードでno_application_protocolアラートが送信され、接続が終了されます。)

クライアントとサーバーの両方でハンドシェークが完了した後、SSLSocketオブジェクトまたはSSLEngineオブジェクトでgetApplicationProtocolメソッドを呼び出すことで、ネゴシエーションの結果を確認できます。ハンドシェークの詳細は、SSLハンドシェークを参照してください。

ハンドシェーク中のネゴシエーション済ALPN値の特定

ハンドシェーク中にネゴシエーションされたALPN値を特定するには、カスタムのKeyManagerまたはTrustManagerクラスを作成し、このカスタム・クラスにgetHandshakeApplicationProtocolメソッドの呼出しを含めます。

選択したALPN値とSNI値がKeyManagerまたはTrustManagerによる選択内容に影響するユースケースがいくつかあります。たとえば、アプリケーションが、サーバーの属性および選択されたALPN/SNI/暗号化方式群値に応じて、異なる証明書/公開鍵のセットを選択する必要がある場合があります。

与えられたサンプル・コードでは、KeyManagerオブジェクトとして作成し登録するカスタムX509ExtendedKeyManager内から、getHandshakeApplicationProtocolメソッドを呼び出す方法を示します。

例8-24 カスタムKeyManagerのサンプル・コード

この例では、X509ExtendedKeyManagerを拡張するカスタムKeyManagerのコード全体を示します。大部分のメソッドは、このMyX509ExtendedKeyManagerクラスによってラップされているKeyManagerクラスから返された値を返すのみです。ただし、chooseServerAliasメソッドは、SSLSocketオブジェクトでgetHandshakeApplicationProtocolを呼び出し、それにより、ネゴシエーションされた現在のALPN値を特定できます。

import java.net.Socket;
import java.security.*;
import javax.net.ssl.*;

public class MyX509ExtendedKeyManager extends X509ExtendedKeyManager {

// X509ExtendedKeyManager is an abstract class so your new class
// needs to implement all the abstract methods in this class.
// The easiest way to do this is to wrap an existing KeyManager
// and call its methods for each of the methods you need to implement.

X509ExtendedKeyManager akm;

public MyX509ExtendedKeyManager(X509ExtendedKeyManager akm) {
this.akm = akm;
}

@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return akm.getClientAliases(keyType, issuers);
}

@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers,
Socket socket) {
return akm.chooseClientAlias(keyType, issuers, socket);
}

@Override
public String chooseServerAlias(String keyType, Principal[] issuers,
Socket socket) {

// This method has access to a Socket, so it is possible to call the
// getHandshakeApplicationProtocol method here. Note the cast from
// a Socket to an SSLSocket
String ap = ((SSLSocket) socket).getHandshakeApplicationProtocol();
System.out.println("In chooseServerAlias, ap is: " + ap);
return akm.chooseServerAlias(keyType, issuers, socket);
}

@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return akm.getServerAliases(keyType, issuers);
}

@Override
public X509Certificate[] getCertificateChain(String alias) {
return akm.getCertificateChain(alias);
}

@Override
public PrivateKey getPrivateKey(String alias) {
return akm.getPrivateKey(alias);
}
}

このコードがJavaサーバー用のKeyManagerとして登録されており、Javaクライアントによって、ALPN値を含むClientHelloが送信される場合、出力は次のようになります。

In chooseServerAlias, ap is: <negotiated value>

例8-25 JavaサーバーでのカスタムKeyManagerの使用のサンプル・コード

この例では、デフォルトのALPNネゴシエーション戦略、および前のコード例で示されたカスタムKeyManager、MyX509ExtendedKeyManagerを使用する単純なJavaサーバーを示します。

import java.io.*;
import java.util.*;
import javax.net.ssl.*;
import java.security.KeyStore;

public class SSLServerHandshake {

public static void main(String[] args) throws Exception {
SSLContext ctx = SSLContext.getInstance("TLS");

// You need to explicitly create a create a custom KeyManager

// Keystores
KeyStore keyKS = KeyStore.getInstance("PKCS12");
keyKS.load(new FileInputStream("serverCert.p12"),
"password".toCharArray());

// Generate KeyManager
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(keyKS, "password".toCharArray());
KeyManager[] kms = kmf.getKeyManagers();

// Code to substitute MyX509ExtendedKeyManager
if (!(kms[0] instanceof X509ExtendedKeyManager)) {
throw new Exception("kms[0] not X509ExtendedKeyManager");
}

// Create a new KeyManager array and set the first index
// of the array to an instance of MyX509ExtendedKeyManager.
// Notice how creating this object is done by passing in the
// existing default X509ExtendedKeyManager
kms = new KeyManager[] {
new MyX509ExtendedKeyManager((X509ExtendedKeyManager) kms[0])};

// Initialize SSLContext using the new KeyManager
ctx.init(kms, null, null);

// Instead of using SSLServerSocketFactory.getDefault(),
// get a SSLServerSocketFactory based on the SSLContext
SSLServerSocketFactory sslssf = ctx.getServerSocketFactory();
SSLServerSocket sslServerSocket =
(SSLServerSocket) sslssf.createServerSocket(9999);
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
SSLParameters sslp = sslSocket.getSSLParameters();
String[] serverAPs ={"one","two","three"};
sslp.setApplicationProtocols(serverAPs);
sslSocket.setSSLParameters(sslp);
sslSocket.startHandshake();

String ap = sslSocket.getApplicationProtocol();
System.out.println("Application Protocol server side: \"" + ap + "\"");

InputStream sslIS = sslSocket.getInputStream();
OutputStream sslOS = sslSocket.getOutputStream();
sslIS.read();
sslOS.write(85);
sslOS.flush();

sslSocket.close();
sslServerSocket.close();
}
}

カスタムX509ExtendedKeyManagerの準備が整い、ハンドシェーク中にchooseServerAliasが呼び出されると、KeyManagerは、ネゴシエーションされたアプリケーション・プロトコル値を確認できるようになります。示されている例の場合、この値はコンソールに出力されます。

たとえば、このコードを実行し、Javaクライアントによって、ALPN値threeおよびtwoを含むClientHelloが送信されると、出力は次のようになります。

Application Protocol server side: two

ALPN関連のクラスとメソッド

これらのクラスとメソッドは、アプリケーション層プロトコル・ネゴシエーション(ALPN)の使用時に使用されます。

使用するクラスとメソッド

SSLEngineとSSLSocketには同じALPN関連メソッドが含まれており、それらには同じ機能があります。

クラス

メソッド

用途

SSLParameters

public String[] getApplicationProtocols();

クライアント側とサーバー側: 各プロトコル・セットを含むString配列を返すには、このメソッドを使用します。

SSLParameters

public void setApplicationProtocols([] protocols);

クライアント側: サーバーに選択可能なプロトコルを設定するには、このメソッドを使用します。

サーバー側: サーバーで使用可能なプロトコルを設定するには、このメソッドを使用します。String配列には、プロトコルが優先度順に含まれている必要があります。

SSLEngine

SSLSocket

public String getApplicationProtocol();

クライアント側とサーバー側: 接続のために選択されているプロトコルを含むStringを返すには、TLSプロトコル・ネゴシエーションが完了した後に、このメソッドを使用します。

SSLEngine

SSLSocket

public String getHandshakeApplicationProtocol();

クライアント側とサーバー側: 接続のために選択されているプロトコルを含むStringを返すには、ハンドシェーク中にこのメソッドを使用します。ハンドシェークの前または後にこのメソッドが呼び出された場合は、nullが返されます。このメソッドの呼出し方法の詳細は、ハンドシェーク中のネゴシエーション済ALPN値の特定を参照してください。

SSLEngine

SSLSocket

public void setHandshakeApplicationProtocolSelector(BiFunction,String> selector)

サーバー側: コールバック関数を登録するには、このメソッドを使用します。その後、プロトコルや暗号化方式群など、利用可能な任意の情報に基づいて、アプリケーション・プロトコル値をコールバック内で設定できます。このメソッドの使用方法の詳細は、サーバーでのカスタムALPNの設定を参照してください。

JSSEのトラブルシューティング

このセクションにはJSSEのトラブルシューティングに関する情報が含まれます。構成に関する一般的な問題の解決策を提供します。

まず、一般的な構成の問題とそれらの解決方法を説明し、次に、役に立つデバッグ・ユーティリティについて説明します。

構成の問題

構成に関するいくつかの一般的問題のための解決策。

ハンドシェーク時のCertificateException

問題: SSL/TLS/DTLS接続のネゴシエーション中に、クライアントまたはサーバーがCertificateExceptionをスローします。

原因1: 一般に、リモート側がローカル側に不明な証明書を送信することが原因です。

解決法1: このタイプの問題をデバッグするもっともよい方法は、デバッグをオンにして(「デバッグ・ユーティリティ」を参照)、証明書のロード時およびネットワーク接続経由での証明書の受信時に観察することです。多くの場合、間違ったトラスト・ファイルをロードしたため、受信した証明書がトラスト・メカニズムにとって不明です。

次の項を参照してください。

- JSSEクラスとインタフェース

- TrustManagerインタフェース

- KeyManagerインタフェース

原因2: システム・クロックが正しく設定されていません。この場合、認識された時間が証明書の有効期間外になっている可能性があります。トラストストアからの有効な証明書と置き換えないかぎり、システムはこの証明書を無効とみなすため、例外をスローします。

解決法2: システム・クロックの時間を修正します。

実行時例外: SSL Service Not Available

問題: JSSEを使用するプログラムを実行すると、SSLサービスが利用できないという例外が発生します。たとえば、次のような例外がスローされます。

Exception in thread "main" java.net.SocketException:
no SSL Server Sockets

Exception in thread "main":
SSL implementation not available

原因: キーストア上の誤ったパスワードやキーストアの破損などのため、SSLContextの初期化で問題が発生しました(かつてあるベンダーが不明な形式のキーストアを出荷し、それが原因でこのタイプのエラーが発生しました。)

解決法: 初期化パラメータを確認します。指定したキーストアが有効であり、指定したパスワードが正しいことを確認します。これをチェックできる1つの方法は、keytoolを使用して、キーストアと関連の内容を調べることです。Java Platform, Standard Editionツール・リファレンスでkeytoolを参照してください。

実行時例外: "No available certificate corresponding to the SSL cipher suites which are enabled"

問題: 単純なSSLサーバー・プログラムを実行しようとすると、次の例外がスローされます。

Exception in thread "main" javax.net.ssl.SSLException:
No available certificate corresponding to the SSL cipher suites which are enabled...

原因: 様々な暗号化方式群では特定のタイプの鍵データが必要です。たとえば、RSA暗号化方式群が有効になっている場合、キーストアでRSAのkeyEntryを有効にする必要があります。該当する鍵を使用できない場合、その暗号化方式群を使用することはできません。有効になっているすべての暗号化方式群の鍵エントリが使用できない場合、この例外がスローされます。

解決法: 様々な暗号化方式群タイプの鍵エントリを作成するか、匿名の暗号化方式群を使用します。匿名の暗号化方式群には、MITM (man-in-the-middle)攻撃に対して脆弱であるため、潜在的に危険です。RFC 2246を参照してください。

正しいキーストアおよび証明書を渡す方法については、次のセクションを参照してください。

- JSSEクラスとインタフェース

- デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ

- その他のキーストア形式(PKCS12)

実行時例外: No Cipher Suites in Common

問題1: ハンドシェーク時にクライアントやサーバーがこの例外をスローします。

原因1: SSL接続の両側が共通の暗号化方式群について合意している必要があります。クライアントの暗号化方式群セットとサーバーの暗号化方式群セットの共通点がない場合、この例外がスローされます。

解決法1: 有効な暗号化方式群を構成し、共通の暗号化方式群を含めます。さらに、非対称の暗号化方式群に適切なkeyEntryが提供されるようにします。この項の実行時例外: "No available certificate corresponding to the SSL cipher suites which are enabled"も参照してください。

問題2: DSAベースの証明書しかないサーバーのファイルにMozilla FirefoxやMicrosoft Internet Explorerでアクセスすると、共通の暗号化方式群がないことを示す実行時例外が発生します。

原因2: デフォルトでは、keytoolで作成されたkeyEntriesはDSA公開鍵を使用します。キーストア内にDSAのkeyEntriesのみが存在する場合、使用できるのはDSAベースの暗号化方式群のみです。デフォルトでは、NavigatorとInternet ExplorerはRSAベースの暗号化方式群のみを送信します。クライアントとサーバーの暗号化方式群セットの共通点がないため、この例外がスローされます。

解決法2: NavigatorやInternet Explorerと対話するには、RSAベースの鍵を使用する証明書を作成してください。そのためには、keytool使用時に-keyalg RSAオプションを指定します。次に例を示します。

keytool -genkeypair -alias duke -keystore testkeys -keyalg rsa

ClientHelloメッセージの送信後ソケットが切断される

問題: ソケットが接続を試み、ClientHelloメッセージを送信すると、ただちに切断されます。

原因: SSL/TLSサーバーの中には、理解できない形式や、サポートしていないプロトコル・バージョン番号でClientHelloメッセージを受信した場合、接続を切断するものがあります。

解決法: クライアント側で、有効なプロトコルの調整を試みてください。これには、次のシステム・プロパティやメソッドの変更や呼び出しが含まれます。

- HttpsURLConnectionクラスのシステム・プロパティhttps.protocols

- システム・プロパティjdk.tls.client.protocols

- SSLContext.getInstanceメソッド

- SSLEngine.setEnabledProtocolsメソッド

- SSLSocket.setEnabledProtocolsメソッド

- SSLParameters.setProtocolsおよびSSLEngine.setSSLParametersメソッド

- SSLParameters.setProtocolsおよびSSLSocket.setSSLParametersメソッド

後方互換性のため、一部のSSL/TLS実装(SunJSSEなど)はSSL/TLSのClientHelloメッセージをSSLv2のClientHello形式にカプセル化して送信できます。SunJSSEプロバイダはこの機能をサポートしています。この機能を使用する場合は、必要に応じて「SSLv2Hello」を有効なプロトコル・リストに追加します。(「JDKプロバイダ」で「プロトコル」を参照してください。ここでは、SunJSSEプロバイダでデフォルトで有効になっているプロトコルがリストされています。)

SSL/TLS RFC標準で、実装は、両側で使用されている最新バージョンとネゴシエーションする必要がありますが、認識できないバージョンを提示されると、一部の非準拠実装は単にハングアップします。たとえば、SSLv3のみを認識する一部の古いサーバー実装は、TLSv1.2を要求されると、シャットダウンします。この状況では、SSL/TLSバージョンのフォールバック・スキームを使用することを検討してください。

1. サーバーでTLSv1.2を理解しない場合、TLSv1.2からTLSv1.1にフォールバックします。

2. 前のステップが機能しない場合は、TLSv1.1からTLSv1.0にフォールバックします。

たとえば、クライアントの有効なプロトコル・リストがTLSv1、TLSv1.1およびTLSv1.2の場合、一般的なSSL/TLSバージョンのフォールバック・スキームが次のように見えます。

1. サーバーに接続を試みます。サーバーがSSL/TLS接続要求をただちに拒否した場合は、ステップ2に進みます。

2. 有効なプロトコル・リスト内の最高のプロトコル・バージョン(最初の失敗の場合TLSv1.2など)を削除して、バージョン・フォールバック・スキームを試行します。

3. 再度サーバーに接続を試みます。サーバーが接続を拒否した場合、サーバーがフォールバックできるバージョンがない場合を除いて、ステップ2に進みます。

4. 接続が失敗し、SSLv2Helloが有効なプロトコル・リストにない場合は、有効なプロトコル・リストを復元して、SSLv2Helloを有効にします。(たとえば、有効なプロトコル・リストは、SSLv2Hello、TLSv1、TLSv1.1およびTLSv1.2となる必要があります。)ステップ1から再度開始します。

注意:
以前のバージョンにフォールバックすることは、通常、セキュリティ強度が弱いプロトコルにダウングレードすることを意味します。本当に必要で、サーバーが上位のプロトコル・バージョンをサポートしていないことが確実にわかっている場合でないかぎり、フォールバック・スキームを使用することはお薦めしません。

注意:
一部のサーバーはSSLv3無効化の過程でSSLv2Helloも無効にしていますが、それは、SSLv2Helloがアクティブなクライアント(JDK 6u95)との通信が失敗することを意味します。JDK 7以降では、デフォルトで、SSLv2Helloはクライアント上では無効、サーバー上では有効です。

必要なアルゴリズムをサポートするJCAプロバイダをSunJSSEが見つけられず、NoSuchAlgorithmExceptionが発生する

問題: ハンドシェークが試行され、必要なアルゴリズムが見つからない場合は失敗します。例は次のとおりです。

Exception in thread ...deleted...
...deleted...
Caused by java.security.NoSuchAlgorithmException: Cannot find any
provider supporting RSA/ECB/PKCS1Padding

または

Caused by java.security.NoSuchAlgorithmException: Cannot find any
provider supporting AES/CBC/NoPadding

原因: SunJSSEは、その暗号化アルゴリズムすべてでJCEを使用します。SunJCEプロバイダがProviderメカニズムから登録解除されており、JCEからの代替実装を利用できない場合、この例外がスローされます。

解決法: プロバイダがProviderインタフェースで登録されていることをチェックして、SunJCEが使用可能であることを確認します。SSL接続のコンテキストで次のコードを実行してみます。

import javax.crypto.*;

System.out.println("=====Where did you get AES=====");
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
System.out.println(c.getProvider());

WebサーバーからSSLでアプリケーション・リソースを取得しようとするとFailedDownloadExceptionがスローされる

問題: WebサーバーからSSLでアプリケーション・リソースを取得しようとしたときにcom.sun.deploy.net.FailedDownloadExceptionを受け取り、WebサーバーではServer Name Indication (SNI)拡張を持つ仮想ホストが使用されている場合(Apache HTTPサーバーなど)、Webサーバーが正しく構成されていないことがあります。

原因: Java SE 7では、JSSEクライアントのSNI拡張がサポートされているため、要求された仮想サーバーのホスト名は、SSLハンドシェーク中にクライアントからサーバーに送信された最初のメッセージに含まれています。要求されたホスト名(Server Name Indication)が、仮想ホストの構成に指定されているはずの期待されるサーバー名と一致しない場合、サーバーはクライアントの接続要求を拒否することがあります。これにより、SSLハンドシェークの認識されない名前の警告がトリガーされ、FailedDownloadExceptionがスローされます。

解決法: 問題を適切に診断するために、Javaコンソールを使用してトレースを有効にします。Java Platform, Standard Editionデプロイメント・ガイドでデバッグおよびJavaコンソールを参照してください。問題の原因がjavax.net.ssl.SSLProtocolException: handshake alert: unrecognized_nameの場合、SNIのための仮想ホスト構成が正しくない可能性があります。Apache HTTPサーバーを使用している場合、仮想ホストの構成については、名前ベースの仮想ホストのサポートを参照してください。特に、<VirtualHostブロック内でServerNameディレクティブが正しく構成されていることを確認してください。

次を参照してください。

- 「Apache HTTP Server Wiki」の「SSL with Virtual Hosts Using SNI」

- 「Apache HTTP Server Documentation」の「SSL/TLS Strong Encryption: FAQ」

- RFC 3546、Transport Layer Security (TLS) Extensions

- バグ7194590: SSL handshaking error caused by virtual server misconfiguration

RC4暗号化方式群がDTLS用に構成されている場合のIllegalArgumentException

問題: SSLEngine.setEnabledCipherSuites(String[] suites)メソッドでRC4暗号化方式群アルゴリズムが指定されており、SSLEngineがDTLSエンジンである場合に、IllegalArgumentException例外がスローされます。

sslContext = SSLContext.getInstance("DTLS");

// Create the engine
SSLEngine engine = sslContext.createSSLengine(hostname, port);

String enabledSuites[] = { "SSL_RSA_WITH_RC4_128_SHA" };
engine.setEnabledCipherSuites(enabledSuites);

原因: DTLSバージョン1.0とDTLSバージョン1.2によると、RC4暗号化方式群はDTLSとは使用できません。

解決法: DTLS接続にはRC4ベースの暗号化方式群は使用しないでください。Javaセキュリティ標準アルゴリズム名でJSSE暗号化方式群の名前を参照してください。

デバッグ・ユーティリティ

JSSEには、動的デバッグのトレースをサポートする機能があります。これは、Java SE プラットフォームでデバッグのアクセス制御に失敗した場合に使用するサポート機能に似ています。一般的なJava動的デバッグ・トレースのサポートにはjava.security.debugシステム・プロパティを使用してアクセスしますが、JSSE固有の動的デバッグ・トレースのサポートにはjavax.net.debugシステム・プロパティを使用してアクセスします。

注意:
debugユーティリティは、JSSEの公式にサポートされている機能ではありません。

JSSE動的デバッグ・ユーティリティのオプションを表示するには、javaコマンドで次のコマンド行オプションを使用します。

-Djavax.net.debug=help

注意:
ユーティリティがデバッグするように設計されたクラスを使用しないプログラムの実行中に、いずれかの動的デバッグ・ユーティリティで値helpを指定しても、デバッグ・オプションは得られません。

次の完全な例では、いくつかのJSSEクラスを使用するMyAppというアプリケーションのデバッグ・オプションのリストを取得する方法を示しています。

java -Djavax.net.debug=help MyApp

MyAppアプリケーションは、デバッグのヘルプ情報が表示されると動作しなくなりますが、これはヘルプコードによりアプリケーションが終了するためです。

現在のオプション:

- all: すべてのデバッグを有効にします

- ssl: SSLデバッグを有効にします

sslオプションとともに次のオプションを使用できます:

- record: レコードごとのトレースを有効にします

- handshake: 各ハンドシェーク・メッセージを出力します

- keygen: 鍵生成データを出力します

- session: セッション・アクティビティを出力します

- defaultctx: デフォルトのSSL初期化を出力します

- sslctx: SSLContextのトレースを出力します

- sessioncache: セッション・キャッシュのトレースを出力します

- keymanager: キー・マネージャのトレースを出力します

- trustmanager: トラスト・マネージャのトレースを出力します

handshakeオプションによって生成されるメッセージは、次のオプションで拡張できます:

- data: 各ハンドシェイク・メッセージの16進ダンプ

- verbose: ハンドシェイク・メッセージの詳細出力

recordオプションによって生成されるメッセージは、次のオプションで拡張できます:

- plaintext: レコードのプレーン・テキストの16進ダンプ

- packet: raw SSL/TLSパケットを出力します

javax.net.debugプロパティ値は、allまたはsslを指定する必要があり、オプションでデバッグ指定子をその後に続けます。1つまたは複数のオプションが使用できます。オプションをセパレータで区切る必要はありませんが、コロン(:)やカンマ(,)などの区切り文字を使用すると読みやすくなります。どのセパレータでも使用でき、オプション・キーワードの順序も重要ではありません。

このデバッグ情報の見方の説明は、ガイド「SSL/TLS接続のデバッグ」を参照してください。

次に、javax.net.debugプロパティの使用例を示します。

- デバッグ・メッセージをすべて表示する場合は、次のように入力します。

java -Djavax.net.debug=all MyApp

- 各ハンドシェーク・メッセージを16進ダンプで表示するには、次のように入力します(コロンはオプションです)。

java -Djavax.net.debug=ssl:handshake:data MyApp

- 各ハンドシェーク・メッセージを16進ダンプで表示し、さらにトラスト・マネージャのトレース状態を表示するには、次のように入力します(カンマはオプションです)。

java -Djavax.net.debug=SSL,handshake,data,trustmanager MyApp

SSL/TLS接続のデバッグ

SSL/TLS接続における問題は、理解するのが難しい場合があり、実際に送受信されたメッセージが明確でない場合は特に面倒です。SunJSSEにはデバッグ機能が組み込まれており、javax.net.debugシステム・プロパティによって起動されます。

javax.net.debugシステム・プロパティの詳細は、デバッグ・ユーティリティを参照してください。

デバッグ出力ファイルの読み方を例示する短いサンプルを次に示します。出力は標準的なものではなく、リリースによって変化することがあります。例では、デフォルトのSunJSSE X509KeyManagerとX509TrustManagerを使用してデバッグ情報を出力しています。

この例では、SSL/TLSプロトコルの基礎を理解していることを前提としています。プロトコルの詳細(ハンドシェーク・メッセージなど)は、Secure Sockets Layer (SSL)プロトコルの概要を参照してください。

次の例では、SSLSocketClientWithClientAuthサンプル・アプリケーションを使用してクライアント認証を必要とする簡単なHTTPSサーバーに接続し、HTTPS要求を送信して応答を受信します。

java  -Djavax.net.debug=all  \
-Djavax.net.ssl.trustStore=trustStore
SSLSocketClientWithClientAuth bongos 2001 /index.html

まずX509KeyManagerが初期化され、「duke」と呼ばれる被認証者のKeyStoreに1つのkeyEntryがあることを発見します。サーバーがクライアントに対してクライアント自体の認証を要求すると、X509KeyManagerはkeyEntryのリストを検索して適切な資格を見つけます。

***
found key for : duke
chain [0] = [
[
Version: V1
Subject: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.",
L=Cupertino, ST=CA, C=US
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4

Key:  Sun RSA public key, 1024 bits
modulus: 134968166047563266914058280571444028986498087544923991226919517
667593269213420979048109900052353578998293280426361122296881234393722020
704208851688212064483570055963805034839797994154526862998272017486468599
962268346037652120279791547218281230795146025359480589335682217749874703
510467348902637769973696151441
public exponent: 65537
Validity: [From: Tue May 22 16:46:46 PDT 2001,
To: Sun May 22 16:46:46 PDT 2011]
Issuer: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.",
L=Cupertino, ST=CA, C=US
SerialNumber: [    3b0afa66]

]
Algorithm: [MD5withRSA]
Signature:
0000: 5F B5 62 E9 A0 26 1D 8E   A2 7E 7C 02 08 36 3A 3E  _.b..&.......6:>
0010: C9 C2 45 03 DD F9 BC 06   FC 25 CF 30 92 91 B1 4E  ..E......%.0...N
0020: 62 17 08 48 14 68 80 CF   DD 89 11 EA 92 7F CE DD  b..H.h..........
0030: B4 FD 12 A8 71 C7 9E D7   C3 D0 E3 BD BB DE 20 92  ....q......... .
0040: C2 3B C8 DE CB 25 23 C0   8B B6 92 B9 0B 64 80 63  .;...%#......d.c
0050: D9 09 25 2D 7A CF 0A 31   B6 E9 CA C1 37 93 BC 0D  ..%-z..1....7...
0060: 4E 74 95 4F 58 31 DA AC   DF D8 BD 89 BD AF EC C8  Nt.OX1..........
0070: 2D 18 A2 BC B2 15 4F B7   28 6F D3 00 E1 72 9B 6C  -.....O.(o...r.l

]
***

次にX509TrustManagerが初期化され、「JSSE Test CA」という名前の証明書発行局(CA)の証明書を見つけます。このCAによって署名された

有効な

クレデンシャルを提示しているサーバーが信頼されます。

trustStore is: trustkeys
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
Subject: CN=JSSE Test CA, OU=JWS, O=Sun,
L=Santa Clara, ST=CA, C=US
Issuer:  CN=JSSE Test CA, OU=JWS, O=Sun,
L=Santa Clara, ST=CA, C=US
Algorithm: RSA; Serial number: 0x0
Valid from Mon Jul 19 13:30:15 PDT 2004 until Fri Dec 05 12:30:15 PST 2031

付加的な初期化コードの一部が終了しました。このあと、いよいよサーバーに接続することができます。

trigger seeding of SecureRandom
done seeding SecureRandom
export control - checking the cipher suites
export control - no cached value available...
export control - storing legal entry into cache...
%% No cached client session

サーバーへの接続が確立されて、初期ClientHelloメッセージが表示されます。メッセージには次の情報が含まれています。

- 暗号化ルーチンを初期化するランダム情報

- SessionID。nullでない場合は、前のセッションを再確立する際に使用

- クライアントが要求する暗号群のリスト

- および非圧縮アルゴリズム。

このあと、SSLv2Helloヘッダー形式でのTLSv1ヘッダーのカプセル化(setEnabledProtocols()参照)など、さまざまなフィルタ出力が続きます。

*** ClientHello, TLSv1
RandomCookie:  GMT: 1073239164 bytes = { 10, 80, 71, 86, 124, 135, 104,
151, 72, 153, 70, 28, 97, 232, 160, 217, 146, 178, 87, 255, 122, 147, 83,
197, 60, 187, 227, 76 }
Session ID:  {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA,
TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA,
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA,
SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5,
SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA]
Compression Methods:  { 0 }
***
[write] MD5 and SHA1 hashes:  len = 73
0000: 01 00 00 45 03 01 40 F8   54 7C 0A 50 47 56 7C 87  ...E..@.T..PGV..
0010: 68 97 48 99 46 1C 61 E8   A0 D9 92 B2 57 FF 7A 93  h.H.F.a.....W.z.
0020: 53 C5 3C BB E3 4C 00 00   1E 00 04 00 05 00 2F 00  S.<..L......../.
0030: 33 00 32 00 0A 00 16 00   13 00 09 00 15 00 12 00  3.2.............
0040: 03 00 08 00 14 00 11 01   00                       .........
main, WRITE: TLSv1 Handshake, length = 73
[write] MD5 and SHA1 hashes:  len = 98
0000: 01 03 01 00 39 00 00 00   20 00 00 04 01 00 80 00  ....9... .......
0010: 00 05 00 00 2F 00 00 33   00 00 32 00 00 0A 07 00  ..../..3..2.....
0020: C0 00 00 16 00 00 13 00   00 09 06 00 40 00 00 15  ............@...
0030: 00 00 12 00 00 03 02 00   80 00 00 08 00 00 14 00  ................
0040: 00 11 40 F8 54 7C 0A 50   47 56 7C 87 68 97 48 99  ..@.T..PGV..h.H.
0050: 46 1C 61 E8 A0 D9 92 B2   57 FF 7A 93 53 C5 3C BB  F.a.....W.z.S.<.
0060: E3 4C                                              .L
main, WRITE: SSLv2 client hello message, length = 98

「[Raw write]」というラベルのセクションは、raw出力オブジェクト(この場合はOutputStream)に送信される実際のデータを表します。

[Raw write]: length = 100
0000: 80 62 01 03 01 00 39 00   00 00 20 00 00 04 01 00  .b....9... .....
0010: 80 00 00 05 00 00 2F 00   00 33 00 00 32 00 00 0A  ....../..3..2...
0020: 07 00 C0 00 00 16 00 00   13 00 00 09 06 00 40 00  ..............@.
0030: 00 15 00 00 12 00 00 03   02 00 80 00 00 08 00 00  ................
0040: 14 00 00 11 40 F8 54 7C   0A 50 47 56 7C 87 68 97  ....@.T..PGV..h.
0050: 48 99 46 1C 61 E8 A0 D9   92 B2 57 FF 7A 93 53 C5  H.F.a.....W.z.S.
0060: 3C BB E3 4C                                        <..L

初期ClientHelloを送信したあと、サーバーの応答であるServerHelloを待ちます。「[Raw read]」は、処理が実行される前に、入力デバイス(InputStream)から読み取ったrawデータを表示します。

[Raw read]: length = 5
0000: 16 03 01 06 F0                                     .....
[Raw read]: length = 1776
0000: 02 00 00 46 03 01 40 FC   31 10 79 AB 17 66 FA 8B  ...F..@.1.y..f..
0010: 3F AA FD 5E 48 23 FA 90   31 D8 3C B9 A3 2C 8C F5  ?..^H#..1.<..,..
0020: E9 81 9B A2 63 6C 20 40   FC 31 10 BD 8D A5 91 06  ....cl @.1......
0030: 8B E1 E6 80 C6 5A 5C D9   8D 0A AE CA 58 4A BA 36  .....Z\.....XJ.6
0040: B1 3D 04 8D 82 21 B4 00   04 00 0B 00 06 1B 00 06  .=...!..........
0050: 18 00 03 11 30 82 03 0D   30 82 02 76 A0 03 02 01  ....0...0..v....
0060: 02 02 01 01 30 0D 06 09   2A 86 48 86 F7 0D 01 01  ....0...*.H.....
0070: 04 05 00 30 63 31 0B 30   09 06 03 55 04 06 13 02  ...0c1.0...U....
0080: 55 53 31 0B 30 09 06 03   55 04 08 13 02 43 41 31  US1.0...U....CA1
0090: 14 30 12 06 03 55 04 07   13 0B 53 61 6E 74 61 20  .0...U....Santa
00A0: 43 6C 61 72 61 31 0C 30   0A 06 03 55 04 0A 13 03  Clara1.0...U....
00B0: 53 75 6E 31 0C 30 0A 06   03 55 04 0B 13 03 4A 57  Sun1.0...U....JW
00C0: 53 31 15 30 13 06 03 55   04 03 13 0C 4A 53 53 45  S1.0...U....JSSE
00D0: 20 54 65 73 74 20 43 41   30 1E 17 0D 30 34 30 37   Test CA0...0407
00E0: 31 39 32 30 33 30 35 31   5A 17 0D 33 31 31 32 30  19203051Z..31120
00F0: 35 32 30 33 30 35 31 5A   30 48 31 0B 30 09 06 03  5203051Z0H1.0...
0100: 55 04 06 13 02 55 53 31   0B 30 09 06 03 55 04 08  U....US1.0...U..
0110: 13 02 43 41 31 0C 30 0A   06 03 55 04 0A 13 03 53  ..CA1.0...U....S
0120: 75 6E 31 0D 30 0B 06 03   55 04 0B 13 04 4A 61 76  un1.0...U....Jav
0130: 61 31 0F 30 0D 06 03 55   04 03 13 06 62 6F 6E 67  a1.0...U....bong
0140: 6F 73 30 81 9F 30 0D 06   09 2A 86 48 86 F7 0D 01  os0..0...*.H....
0150: 01 01 05 00 03 81 8D 00   30 81 89 02 81 81 00 CC  ........0.......
0160: 09 74 CB 43 AB 6D ED F6   35 AA 0E 49 29 D9 E0 F0  .t.C.m..5..I)...
0170: A1 D5 E2 3E 8F 5E C5 CE   F4 DE C1 A4 F3 CB 8C 45  ...>.^.........E
0180: 0B 0F 6E 21 E1 00 65 CB   3C D1 5C EF 6A FB 5D 96  ..n!..e.<.\.j.].
0190: 93 F4 71 41 41 45 FF 37   86 4C AB F9 EA 9A 3F A5  ..qAAE.7.L....?.
01A0: 82 60 BF 0A 81 84 C9 3E   AC 0F 3D 20 3D AC A0 69  .`.....>..= =..i
01B0: EF CA 4A A7 94 AD C8 A5   CE 37 66 52 D1 25 43 CB  ..J......7fR.%C.
01C0: 10 44 07 1E 93 74 D9 68   01 D7 06 48 C9 0D 52 2D  .D...t.h...H..R-
01D0: D5 6A 2E A6 48 4C 59 E2   5C C6 C1 5C C8 4C 1B 02  .j..HLY.\..\.L..
01E0: 03 01 00 01 A3 81 EB 30   81 E8 30 09 06 03 55 1D  .......0..0...U.
01F0: 13 04 02 30 00 30 2C 06   09 60 86 48 01 86 F8 42  ...0.0,..`.H...B
0200: 01 0D 04 1F 16 1D 4F 70   65 6E 53 53 4C 20 47 65  ......OpenSSL Ge
0210: 6E 65 72 61 74 65 64 20   43 65 72 74 69 66 69 63  nerated Certific
0220: 61 74 65 30 1D 06 03 55   1D 0E 04 16 04 14 58 D7  ate0...U......X.
0230: 3A A9 37 AA 3E 14 27 FC   EC CC 45 08 04 8E 2A 8B  :.7.>.'...E...*.
0240: 77 28 30 81 8D 06 03 55   1D 23 04 81 85 30 81 82  w(0....U.#...0..
0250: 80 14 08 A3 7E 35 96 15   FA B0 F5 1B 5F CD 4F 54  .....5......_.OT
0260: EF 31 33 70 E4 A7 A1 67   A4 65 30 63 31 0B 30 09  .13p...g.e0c1.0.
0270: 06 03 55 04 06 13 02 55   53 31 0B 30 09 06 03 55  ..U....US1.0...U
0280: 04 08 13 02 43 41 31 14   30 12 06 03 55 04 07 13  ....CA1.0...U...
0290: 0B 53 61 6E 74 61 20 43   6C 61 72 61 31 0C 30 0A  .Santa Clara1.0.
02A0: 06 03 55 04 0A 13 03 53   75 6E 31 0C 30 0A 06 03  ..U....Sun1.0...
02B0: 55 04 0B 13 03 4A 57 53   31 15 30 13 06 03 55 04  U....JWS1.0...U.
02C0: 03 13 0C 4A 53 53 45 20   54 65 73 74 20 43 41 82  ...JSSE Test CA.
02D0: 01 00 30 0D 06 09 2A 86   48 86 F7 0D 01 01 04 05  ..0...*.H.......
02E0: 00 03 81 81 00 05 3E 17   DA F2 05 CB 4E 9E BF 12  ......>.....N...
02F0: CE 13 76 FF B2 FB 7F 9C   3D 45 28 43 6C 98 28 E3  ..v.....=E(Cl.(.
0300: 92 17 C2 C6 F1 62 CA 60   C2 B0 EC E6 7E 4C 2F C2  .....b.`.....L/.
0310: 40 FE 06 CB 34 60 B1 F4   26 1C E8 46 39 24 E1 8A  @...4`..&..F9$..
0320: 71 F2 13 90 A4 0A 7B 0B   13 AB 51 68 53 D9 7A 31  q.........QhS.z1
0330: 5A C1 7E 3C 44 2C 49 70   57 25 F9 18 FE 5D A5 42  Z..<D,IpW%...].B
0340: 7F 3E 61 1F 29 A3 31 46   02 C6 D2 8C 27 79 40 76  .>a.).1F....'y@v
0350: 97 B6 25 19 BE 6C 6A 92   DC EF 11 BE E7 4A FF 2A  ..%..lj......J.*
0360: E6 D6 AC 39 31 00 03 01   30 82 02 FD 30 82 02 66  ...91...0...0..f
0370: A0 03 02 01 02 02 01 00   30 0D 06 09 2A 86 48 86  ........0...*.H.
0380: F7 0D 01 01 04 05 00 30   63 31 0B 30 09 06 03 55  .......0c1.0...U
0390: 04 06 13 02 55 53 31 0B   30 09 06 03 55 04 08 13  ....US1.0...U...
03A0: 02 43 41 31 14 30 12 06   03 55 04 07 13 0B 53 61  .CA1.0...U....Sa
03B0: 6E 74 61 20 43 6C 61 72   61 31 0C 30 0A 06 03 55  nta Clara1.0...U
03C0: 04 0A 13 03 53 75 6E 31   0C 30 0A 06 03 55 04 0B  ....Sun1.0...U..
03D0: 13 03 4A 57 53 31 15 30   13 06 03 55 04 03 13 0C  ..JWS1.0...U....
03E0: 4A 53 53 45 20 54 65 73   74 20 43 41 30 1E 17 0D  JSSE Test CA0...
03F0: 30 34 30 37 31 39 32 30   33 30 31 35 5A 17 0D 33  040719203015Z..3
0400: 31 31 32 30 35 32 30 33   30 31 35 5A 30 63 31 0B  11205203015Z0c1.
0410: 30 09 06 03 55 04 06 13   02 55 53 31 0B 30 09 06  0...U....US1.0..
0420: 03 55 04 08 13 02 43 41   31 14 30 12 06 03 55 04  .U....CA1.0...U.
0430: 07 13 0B 53 61 6E 74 61   20 43 6C 61 72 61 31 0C  ...Santa Clara1.
0440: 30 0A 06 03 55 04 0A 13   03 53 75 6E 31 0C 30 0A  0...U....Sun1.0.
0450: 06 03 55 04 0B 13 03 4A   57 53 31 15 30 13 06 03  ..U....JWS1.0...
0460: 55 04 03 13 0C 4A 53 53   45 20 54 65 73 74 20 43  U....JSSE Test C
0470: 41 30 81 9F 30 0D 06 09   2A 86 48 86 F7 0D 01 01  A0..0...*.H.....
0480: 01 05 00 03 81 8D 00 30   81 89 02 81 81 00 9A 0A  .......0........
0490: B6 45 66 D5 DE 4A D9 3C   8C AC A6 B5 A5 88 B4 CF  .Ef..J.<........
04A0: 14 E1 A6 1B 25 25 4F 44   C9 1F 22 38 32 29 CF A1  ....%%OD.."82)..
04B0: 7C 18 30 93 DC 2B EC 2B   67 EE 2E 08 66 2D 0F 47  ..0..+.+g...f-.G
04C0: E0 12 3A DC E0 03 E9 65   16 F6 18 C6 16 14 56 24  ..:....e......V$
04D0: 55 7D 32 3E F9 66 A2 DD   55 EB 4D 0A 67 C7 5D 21  U.2>.f..U.M.g.]!
04E0: 9B 29 EA 2E 51 C5 83 A3   55 FF 35 CA A6 99 8F 46  .)..Q...U.5....F
04F0: F8 8E 56 BB A2 B1 39 83   D8 61 42 79 E0 95 78 FA  ..V...9..aBy..x.
0500: C6 E3 65 B0 FD 74 2D 64   51 71 04 F2 A1 91 02 03  ..e..t-dQq......
0510: 01 00 01 A3 81 C0 30 81   BD 30 1D 06 03 55 1D 0E  ......0..0...U..
0520: 04 16 04 14 08 A3 7E 35   96 15 FA B0 F5 1B 5F CD  .......5......_.
0530: 4F 54 EF 31 33 70 E4 A7   30 81 8D 06 03 55 1D 23  OT.13p..0....U.#
0540: 04 81 85 30 81 82 80 14   08 A3 7E 35 96 15 FA B0  ...0.......5....
0550: F5 1B 5F CD 4F 54 EF 31   33 70 E4 A7 A1 67 A4 65  .._.OT.13p...g.e
0560: 30 63 31 0B 30 09 06 03   55 04 06 13 02 55 53 31  0c1.0...U....US1
0570: 0B 30 09 06 03 55 04 08   13 02 43 41 31 14 30 12  .0...U....CA1.0.
0580: 06 03 55 04 07 13 0B 53   61 6E 74 61 20 43 6C 61  ..U....Santa Cla
0590: 72 61 31 0C 30 0A 06 03   55 04 0A 13 03 53 75 6E  ra1.0...U....Sun
05A0: 31 0C 30 0A 06 03 55 04   0B 13 03 4A 57 53 31 15  1.0...U....JWS1.
05B0: 30 13 06 03 55 04 03 13   0C 4A 53 53 45 20 54 65  0...U....JSSE Te
05C0: 73 74 20 43 41 82 01 00   30 0C 06 03 55 1D 13 04  st CA...0...U...
05D0: 05 30 03 01 01 FF 30 0D   06 09 2A 86 48 86 F7 0D  .0....0...*.H...
05E0: 01 01 04 05 00 03 81 81   00 73 6A 46 A2 05 E3 D8  .........sjF....
05F0: 6E 5C F4 18 A2 74 BC CF   EB 0C 5B FF 81 1C 28 85  n\...t....[...(.
0600: C7 FA E4 ED 5C 4F 71 22   FB 26 E3 01 3D 0C 10 AA  ....\Oq".&..=...
0610: BB 3E 90 ED 0E 1F 0C 9B   B1 8C 49 6A 51 E4 C3 52  .>........IjQ..R
0620: D6 FB 42 6C B4 A9 A9 57   A5 84 00 42 6D 37 37 6D  ..Bl...W...Bm77m
0630: C7 6C 23 BC DC 60 D1 9D   6F B3 75 47 3A 15 33 1A  .l#..`..o.uG:.3.
0640: EC 90 09 9D F9 EB BD 88   96 E7 1D 41 BC 01 8D CA  ...........A....
0650: 88 D9 5B 04 09 8F 3E EA   C8 15 A0 AA 4E 85 95 AE  ..[...>.....N...
0660: 2F 0E 31 92 AC 3C FB 2F   C4 0D 00 00 7F 02 01 02  /.1..<./........
0670: 00 7A 00 78 30 76 31 0B   30 09 06 03 55 04 06 13  .z.x0v1.0...U...
0680: 02 55 53 31 0B 30 09 06   03 55 04 08 13 02 43 41  .US1.0...U....CA
0690: 31 12 30 10 06 03 55 04   07 13 09 43 75 70 65 72  1.0...U....Cuper
06A0: 74 69 6E 6F 31 1F 30 1D   06 03 55 04 0A 13 16 53  tino1.0...U....S
06B0: 75 6E 20 4D 69 63 72 6F   73 79 73 74 65 6D 73 2C  un Microsystems,
06C0: 20 49 6E 63 2E 31 16 30   14 06 03 55 04 0B 13 0D   Inc.1.0...U....
06D0: 4A 61 76 61 20 53 6F 66   74 77 61 72 65 31 0D 30  Java Software1.0
06E0: 0B 06 03 55 04 03 13 04   44 75 6B 65 0E 00 00 00  ...U....Duke....
main, READ: TLSv1 Handshake, length = 1776

データは展開され、メッセージがSSL/TLS形式の場合はServerHelloに解析されます。非SSL/TLSソケット(プレーン・テキスト)に接続した場合、受信データはSSL/TLS形式ではないので、正常に接続できません。

ServerHelloは、次の事柄を指定します。

- サーバーのランダム・データ。暗号化アルゴリズムの初期化にも使用される

- このセッションの識別子(別の接続を使用してクライアントがこのセッションに再び参加する場合は、ClientHelloにあるこのIDを送信できる。クライアント・セッションIDとサーバー・セッションIDが等しい場合は、省略されたハンドシェークが実行され、以前に確立されたパラメータが使用される)

- 選択された符号化方式

- 圧縮方法。この場合はなし。

最後に、ServerHelloは、接続において「SSLv3」ではなく「TLSv1」を使用するように指定しています。

*** ServerHello, TLSv1
RandomCookie:  GMT: 1073492240 bytes = { 121, 171, 23, 102, 250, 139, 63,
170, 253, 94, 72, 35, 250, 144, 49, 216, 60, 185, 163, 44, 140, 245, 233,
129, 155, 162, 99, 108 }
Session ID:  {64, 252, 49, 16, 189, 141, 165, 145, 6, 139, 225, 230, 128,
198, 90, 92, 217, 141, 10, 174, 202, 88, 74, 186, 54, 177, 61, 4, 141, 130,
33, 180}
Cipher Suite: SSL_RSA_WITH_RC4_128_MD5
Compression Method: 0
***
%% Created:  [Session-1, SSL_RSA_WITH_RC4_128_MD5]
** SSL_RSA_WITH_RC4_128_MD5
[read] MD5 and SHA1 hashes:  len = 74
0000: 02 00 00 46 03 01 40 FC   31 10 79 AB 17 66 FA 8B  ...F..@.1.y..f..
0010: 3F AA FD 5E 48 23 FA 90   31 D8 3C B9 A3 2C 8C F5  ?..^H#..1.<..,..
0020: E9 81 9B A2 63 6C 20 40   FC 31 10 BD 8D A5 91 06  ....cl @.1......
0030: 8B E1 E6 80 C6 5A 5C D9   8D 0A AE CA 58 4A BA 36  .....Z\.....XJ.6
0040: B1 3D 04 8D 82 21 B4 00   04 00                    .=...!....

次にサーバーは、証明書チェーンを渡すことによってクライアントに自身の身元を証明します。この例では、発行者「JSSE Test CA」によって署名された被認証者「bongos」の証明書を使用します。「JSSE Test CA」は信頼されるCAであることがわかっているので、証明書チェーンがX509TrustManagerによって正常に検証されれば、この接続を受け入れることができます。

信頼関係を確立する方法は数多く存在するので、デフォルトのX509TrustManagerが必要とする信頼管理を実行しない場合は、独自のX509TrustManagerをSSLContextに設定できます。

*** Certificate chain
chain [0] = [
[
Version: V3
Subject:
CN=bongos, OU=Java, O=Sun, ST=CA, C=US
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4

Key:  Sun RSA public key, 1024 bits
modulus: 143279610700427050704216702734995283650706638118826657356308087
682552751165540126665070195006746918193702313900836063802045448392771274
463088345157808670190122017153821642985630288017629294930800445939721128
735250668515619736933648548512047941708018130926985936894512063397816602
623867976474763783110866258971
public exponent: 65537
Validity: [From: Mon Jul 19 13:30:51 PDT 2004,
To: Fri Dec 05 12:30:51 PST 2031]
Issuer: CN=JSSE Test CA, OU=JWS, O=Sun,
L=Santa Clara, ST=CA, C=US
SerialNumber: [    01]

Certificate Extensions: 3

[1]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 58 D7 3A A9 37 AA 3E 14   27 FC EC CC 45 08 04 8E  X.:.7.>.'...E...
0010: 2A 8B 77 28                                        *.w(
]
]

[2]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 08 A3 7E 35 96 15 FA B0   F5 1B 5F CD 4F 54 EF 31  ...5......_.OT.1
0010: 33 70 E4 A7                                        3p..
]

[CN=JSSE Test CA, OU=JWS, O=Sun, L=Santa Clara, ST=CA, C=US]
SerialNumber: [    00]
]

[3]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]

]
Algorithm: [MD5withRSA]
Signature:
0000: 05 3E 17 DA F2 05 CB 4E   9E BF 12 CE 13 76 FF B2  .>.....N.....v..
0010: FB 7F 9C 3D 45 28 43 6C   98 28 E3 92 17 C2 C6 F1  ...=E(Cl.(......
0020: 62 CA 60 C2 B0 EC E6 7E   4C 2F C2 40 FE 06 CB 34  b.`.....L/.@...4
0030: 60 B1 F4 26 1C E8 46 39   24 E1 8A 71 F2 13 90 A4  `..&..F9$..q....
0040: 0A 7B 0B 13 AB 51 68 53   D9 7A 31 5A C1 7E 3C 44  .....QhS.z1Z..<D
0050: 2C 49 70 57 25 F9 18 FE   5D A5 42 7F 3E 61 1F 29  ,IpW%...].B.>a.)
0060: A3 31 46 02 C6 D2 8C 27   79 40 76 97 B6 25 19 BE  .1F....'y@v..%..
0070: 6C 6A 92 DC EF 11 BE E7   4A FF 2A E6 D6 AC 39 31  lj......J.*...91

]
chain [1] = [
[
Version: V3
Subject: CN=JSSE Test CA, OU=JWS, O=Sun,
L=Santa Clara, ST=CA, C=US
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4

Key:  Sun RSA public key, 1024 bits
modulus: 108171861314934294923646852258201093253619460299818135230481040
615923025149195168140458238629251726950220398889722590740552079782864577
976838691751841449679901644183317203824143803940037883199193775839934767
304560313841716869284745769157293013188246601563271959824290073095150730
505329011956986145636520993169
public exponent: 65537
Validity: [From: Mon Jul 19 13:30:15 PDT 2004,
To: Fri Dec 05 12:30:15 PST 2031]
Issuer: CN=JSSE Test CA, OU=JWS, O=Sun,
L=Santa Clara, ST=CA, C=US
SerialNumber: [    00]

Certificate Extensions: 3
[1]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 08 A3 7E 35 96 15 FA B0   F5 1B 5F CD 4F 54 EF 31  ...5......_.OT.1
0010: 33 70 E4 A7                                        3p..
]
]

[2]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 08 A3 7E 35 96 15 FA B0   F5 1B 5F CD 4F 54 EF 31  ...5......_.OT.1
0010: 33 70 E4 A7                                        3p..
]

[CN=JSSE Test CA, OU=JWS, O=Sun, L=Santa Clara, ST=CA, C=US]
SerialNumber: [    00]
]

[3]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:true
PathLen:2147483647
]

]
Algorithm: [MD5withRSA]
Signature:
0000: 73 6A 46 A2 05 E3 D8 6E   5C F4 18 A2 74 BC CF EB  sjF....n\...t...
0010: 0C 5B FF 81 1C 28 85 C7   FA E4 ED 5C 4F 71 22 FB  .[...(.....\Oq".
0020: 26 E3 01 3D 0C 10 AA BB   3E 90 ED 0E 1F 0C 9B B1  &..=....>.......
0030: 8C 49 6A 51 E4 C3 52 D6   FB 42 6C B4 A9 A9 57 A5  .IjQ..R..Bl...W.
0040: 84 00 42 6D 37 37 6D C7   6C 23 BC DC 60 D1 9D 6F  ..Bm77m.l#..`..o
0050: B3 75 47 3A 15 33 1A EC   90 09 9D F9 EB BD 88 96  .uG:.3..........
0060: E7 1D 41 BC 01 8D CA 88   D9 5B 04 09 8F 3E EA C8  ..A......[...>..
0070: 15 A0 AA 4E 85 95 AE 2F   0E 31 92 AC 3C FB 2F C4  ...N.../.1..<./.

]

この証明書を認識します。証明書を信頼してハンドシェークを続行できます。

***
Found trusted certificate:
[
[
Version: V3
Subject: CN=JSSE Test CA, OU=JWS, O=Sun, L=Santa Clara, ST=CA, C=US
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4

Key:  Sun RSA public key, 1024 bits
modulus: 108171861314934294923646852258201093253619460299818135230481040
615923025149195168140458238629251726950220398889722590740552079782864577
976838691751841449679901644183317203824143803940037883199193775839934767
304560313841716869284745769157293013188246601563271959824290073095150730
505329011956986145636520993169
public exponent: 65537
Validity: [From: Mon Jul 19 13:30:15 PDT 2004,
To: Fri Dec 05 12:30:15 PST 2031]
Issuer: CN=JSSE Test CA, OU=JWS, O=Sun, L=Santa Clara, ST=CA, C=US
SerialNumber: [    00]

Certificate Extensions: 3
[1]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 08 A3 7E 35 96 15 FA B0   F5 1B 5F CD 4F 54 EF 31  ...5......_.OT.1
0010: 33 70 E4 A7                                        3p..
]
]

[2]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 08 A3 7E 35 96 15 FA B0   F5 1B 5F CD 4F 54 EF 31  ...5......_.OT.1
0010: 33 70 E4 A7                                        3p..
]

[CN=JSSE Test CA, OU=JWS, O=Sun, L=Santa Clara, ST=CA, C=US]
SerialNumber: [    00]
]

[3]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:true
PathLen:2147483647
]

]
Algorithm: [MD5withRSA]
Signature:
0000: 73 6A 46 A2 05 E3 D8 6E   5C F4 18 A2 74 BC CF EB  sjF....n\...t...
0010: 0C 5B FF 81 1C 28 85 C7   FA E4 ED 5C 4F 71 22 FB  .[...(.....\Oq".
0020: 26 E3 01 3D 0C 10 AA BB   3E 90 ED 0E 1F 0C 9B B1  &..=....>.......
0030: 8C 49 6A 51 E4 C3 52 D6   FB 42 6C B4 A9 A9 57 A5  .IjQ..R..Bl...W.
0040: 84 00 42 6D 37 37 6D C7   6C 23 BC DC 60 D1 9D 6F  ..Bm77m.l#..`..o
0050: B3 75 47 3A 15 33 1A EC   90 09 9D F9 EB BD 88 96  .uG:.3..........
0060: E7 1D 41 BC 01 8D CA 88   D9 5B 04 09 8F 3E EA C8  ..A......[...>..
0070: 15 A0 AA 4E 85 95 AE 2F   0E 31 92 AC 3C FB 2F C4  ...N.../.1..<./.

]

次のハンドシェークの数バイトを読み取ります。

[read] MD5 and SHA1 hashes:  len = 1567
0000: 0B 00 06 1B 00 06 18 00   03 11 30 82 03 0D 30 82  ..........0...0.
0010: 02 76 A0 03 02 01 02 02   01 01 30 0D 06 09 2A 86  .v........0...*.
0020: 48 86 F7 0D 01 01 04 05   00 30 63 31 0B 30 09 06  H........0c1.0..
0030: 03 55 04 06 13 02 55 53   31 0B 30 09 06 03 55 04  .U....US1.0...U.
0040: 08 13 02 43 41 31 14 30   12 06 03 55 04 07 13 0B  ...CA1.0...U....
0050: 53 61 6E 74 61 20 43 6C   61 72 61 31 0C 30 0A 06  Santa Clara1.0..
0060: 03 55 04 0A 13 03 53 75   6E 31 0C 30 0A 06 03 55  .U....Sun1.0...U
0070: 04 0B 13 03 4A 57 53 31   15 30 13 06 03 55 04 03  ....JWS1.0...U..
0080: 13 0C 4A 53 53 45 20 54   65 73 74 20 43 41 30 1E  ..JSSE Test CA0.
0090: 17 0D 30 34 30 37 31 39   32 30 33 30 35 31 5A 17  ..040719203051Z.
00A0: 0D 33 31 31 32 30 35 32   30 33 30 35 31 5A 30 48  .311205203051Z0H
00B0: 31 0B 30 09 06 03 55 04   06 13 02 55 53 31 0B 30  1.0...U....US1.0
00C0: 09 06 03 55 04 08 13 02   43 41 31 0C 30 0A 06 03  ...U....CA1.0...
00D0: 55 04 0A 13 03 53 75 6E   31 0D 30 0B 06 03 55 04  U....Sun1.0...U.
00E0: 0B 13 04 4A 61 76 61 31   0F 30 0D 06 03 55 04 03  ...Java1.0...U..
00F0: 13 06 62 6F 6E 67 6F 73   30 81 9F 30 0D 06 09 2A  ..bongos0..0...*
0100: 86 48 86 F7 0D 01 01 01   05 00 03 81 8D 00 30 81  .H............0.
0110: 89 02 81 81 00 CC 09 74   CB 43 AB 6D ED F6 35 AA  .......t.C.m..5.
0120: 0E 49 29 D9 E0 F0 A1 D5   E2 3E 8F 5E C5 CE F4 DE  .I)......>.^....
0130: C1 A4 F3 CB 8C 45 0B 0F   6E 21 E1 00 65 CB 3C D1  .....E..n!..e.<.
0140: 5C EF 6A FB 5D 96 93 F4   71 41 41 45 FF 37 86 4C  \.j.]...qAAE.7.L
0150: AB F9 EA 9A 3F A5 82 60   BF 0A 81 84 C9 3E AC 0F  ....?..`.....>..
0160: 3D 20 3D AC A0 69 EF CA   4A A7 94 AD C8 A5 CE 37  = =..i..J......7
0170: 66 52 D1 25 43 CB 10 44   07 1E 93 74 D9 68 01 D7  fR.%C..D...t.h..
0180: 06 48 C9 0D 52 2D D5 6A   2E A6 48 4C 59 E2 5C C6  .H..R-.j..HLY.\.
0190: C1 5C C8 4C 1B 02 03 01   00 01 A3 81 EB 30 81 E8  .\.L.........0..
01A0: 30 09 06 03 55 1D 13 04   02 30 00 30 2C 06 09 60  0...U....0.0,..`
01B0: 86 48 01 86 F8 42 01 0D   04 1F 16 1D 4F 70 65 6E  .H...B......Open
01C0: 53 53 4C 20 47 65 6E 65   72 61 74 65 64 20 43 65  SSL Generated Ce
01D0: 72 74 69 66 69 63 61 74   65 30 1D 06 03 55 1D 0E  rtificate0...U..
01E0: 04 16 04 14 58 D7 3A A9   37 AA 3E 14 27 FC EC CC  ....X.:.7.>.'...
01F0: 45 08 04 8E 2A 8B 77 28   30 81 8D 06 03 55 1D 23  E...*.w(0....U.#
0200: 04 81 85 30 81 82 80 14   08 A3 7E 35 96 15 FA B0  ...0.......5....
0210: F5 1B 5F CD 4F 54 EF 31   33 70 E4 A7 A1 67 A4 65  .._.OT.13p...g.e
0220: 30 63 31 0B 30 09 06 03   55 04 06 13 02 55 53 31  0c1.0...U....US1
0230: 0B 30 09 06 03 55 04 08   13 02 43 41 31 14 30 12  .0...U....CA1.0.
0240: 06 03 55 04 07 13 0B 53   61 6E 74 61 20 43 6C 61  ..U....Santa Cla
0250: 72 61 31 0C 30 0A 06 03   55 04 0A 13 03 53 75 6E  ra1.0...U....Sun
0260: 31 0C 30 0A 06 03 55 04   0B 13 03 4A 57 53 31 15  1.0...U....JWS1.
0270: 30 13 06 03 55 04 03 13   0C 4A 53 53 45 20 54 65  0...U....JSSE Te
0280: 73 74 20 43 41 82 01 00   30 0D 06 09 2A 86 48 86  st CA...0...*.H.
0290: F7 0D 01 01 04 05 00 03   81 81 00 05 3E 17 DA F2  ............>...
02A0: 05 CB 4E 9E BF 12 CE 13   76 FF B2 FB 7F 9C 3D 45  ..N.....v.....=E
02B0: 28 43 6C 98 28 E3 92 17   C2 C6 F1 62 CA 60 C2 B0  (Cl.(......b.`..
02C0: EC E6 7E 4C 2F C2 40 FE   06 CB 34 60 B1 F4 26 1C  ...L/.@...4`..&.
02D0: E8 46 39 24 E1 8A 71 F2   13 90 A4 0A 7B 0B 13 AB  .F9$..q.........
02E0: 51 68 53 D9 7A 31 5A C1   7E 3C 44 2C 49 70 57 25  QhS.z1Z..<D,IpW%
02F0: F9 18 FE 5D A5 42 7F 3E   61 1F 29 A3 31 46 02 C6  ...].B.>a.).1F..
0300: D2 8C 27 79 40 76 97 B6   25 19 BE 6C 6A 92 DC EF  ..'y@v..%..lj...
0310: 11 BE E7 4A FF 2A E6 D6   AC 39 31 00 03 01 30 82  ...J.*...91...0.
0320: 02 FD 30 82 02 66 A0 03   02 01 02 02 01 00 30 0D  ..0..f........0.
0330: 06 09 2A 86 48 86 F7 0D   01 01 04 05 00 30 63 31  ..*.H........0c1
0340: 0B 30 09 06 03 55 04 06   13 02 55 53 31 0B 30 09  .0...U....US1.0.
0350: 06 03 55 04 08 13 02 43   41 31 14 30 12 06 03 55  ..U....CA1.0...U
0360: 04 07 13 0B 53 61 6E 74   61 20 43 6C 61 72 61 31  ....Santa Clara1
0370: 0C 30 0A 06 03 55 04 0A   13 03 53 75 6E 31 0C 30  .0...U....Sun1.0
0380: 0A 06 03 55 04 0B 13 03   4A 57 53 31 15 30 13 06  ...U....JWS1.0..
0390: 03 55 04 03 13 0C 4A 53   53 45 20 54 65 73 74 20  .U....JSSE Test
03A0: 43 41 30 1E 17 0D 30 34   30 37 31 39 32 30 33 30  CA0...0407192030
03B0: 31 35 5A 17 0D 33 31 31   32 30 35 32 30 33 30 31  15Z..31120520301
03C0: 35 5A 30 63 31 0B 30 09   06 03 55 04 06 13 02 55  5Z0c1.0...U....U
03D0: 53 31 0B 30 09 06 03 55   04 08 13 02 43 41 31 14  S1.0...U....CA1.
03E0: 30 12 06 03 55 04 07 13   0B 53 61 6E 74 61 20 43  0...U....Santa C
03F0: 6C 61 72 61 31 0C 30 0A   06 03 55 04 0A 13 03 53  lara1.0...U....S
0400: 75 6E 31 0C 30 0A 06 03   55 04 0B 13 03 4A 57 53  un1.0...U....JWS
0410: 31 15 30 13 06 03 55 04   03 13 0C 4A 53 53 45 20  1.0...U....JSSE
0420: 54 65 73 74 20 43 41 30   81 9F 30 0D 06 09 2A 86  Test CA0..0...*.
0430: 48 86 F7 0D 01 01 01 05   00 03 81 8D 00 30 81 89  H............0..
0440: 02 81 81 00 9A 0A B6 45   66 D5 DE 4A D9 3C 8C AC  .......Ef..J.<..
0450: A6 B5 A5 88 B4 CF 14 E1   A6 1B 25 25 4F 44 C9 1F  ..........%%OD..
0460: 22 38 32 29 CF A1 7C 18   30 93 DC 2B EC 2B 67 EE  "82)....0..+.+g.
0470: 2E 08 66 2D 0F 47 E0 12   3A DC E0 03 E9 65 16 F6  ..f-.G..:....e..
0480: 18 C6 16 14 56 24 55 7D   32 3E F9 66 A2 DD 55 EB  ....V$U.2>.f..U.
0490: 4D 0A 67 C7 5D 21 9B 29   EA 2E 51 C5 83 A3 55 FF  M.g.]!.)..Q...U.
04A0: 35 CA A6 99 8F 46 F8 8E   56 BB A2 B1 39 83 D8 61  5....F..V...9..a
04B0: 42 79 E0 95 78 FA C6 E3   65 B0 FD 74 2D 64 51 71  By..x...e..t-dQq
04C0: 04 F2 A1 91 02 03 01 00   01 A3 81 C0 30 81 BD 30  ............0..0
04D0: 1D 06 03 55 1D 0E 04 16   04 14 08 A3 7E 35 96 15  ...U.........5..
04E0: FA B0 F5 1B 5F CD 4F 54   EF 31 33 70 E4 A7 30 81  ...._.OT.13p..0.
04F0: 8D 06 03 55 1D 23 04 81   85 30 81 82 80 14 08 A3  ...U.#...0......
0500: 7E 35 96 15 FA B0 F5 1B   5F CD 4F 54 EF 31 33 70  .5......_.OT.13p
0510: E4 A7 A1 67 A4 65 30 63   31 0B 30 09 06 03 55 04  ...g.e0c1.0...U.
0520: 06 13 02 55 53 31 0B 30   09 06 03 55 04 08 13 02  ...US1.0...U....
0530: 43 41 31 14 30 12 06 03   55 04 07 13 0B 53 61 6E  CA1.0...U....San
0540: 74 61 20 43 6C 61 72 61   31 0C 30 0A 06 03 55 04  ta Clara1.0...U.
0550: 0A 13 03 53 75 6E 31 0C   30 0A 06 03 55 04 0B 13  ...Sun1.0...U...
0560: 03 4A 57 53 31 15 30 13   06 03 55 04 03 13 0C 4A  .JWS1.0...U....J
0570: 53 53 45 20 54 65 73 74   20 43 41 82 01 00 30 0C  SSE Test CA...0.
0580: 06 03 55 1D 13 04 05 30   03 01 01 FF 30 0D 06 09  ..U....0....0...
0590: 2A 86 48 86 F7 0D 01 01   04 05 00 03 81 81 00 73  *.H............s
05A0: 6A 46 A2 05 E3 D8 6E 5C   F4 18 A2 74 BC CF EB 0C  jF....n\...t....
05B0: 5B FF 81 1C 28 85 C7 FA   E4 ED 5C 4F 71 22 FB 26  [...(.....\Oq".&
05C0: E3 01 3D 0C 10 AA BB 3E   90 ED 0E 1F 0C 9B B1 8C  ..=....>........
05D0: 49 6A 51 E4 C3 52 D6 FB   42 6C B4 A9 A9 57 A5 84  IjQ..R..Bl...W..
05E0: 00 42 6D 37 37 6D C7 6C   23 BC DC 60 D1 9D 6F B3  .Bm77m.l#..`..o.
05F0: 75 47 3A 15 33 1A EC 90   09 9D F9 EB BD 88 96 E7  uG:.3...........
0600: 1D 41 BC 01 8D CA 88 D9   5B 04 09 8F 3E EA C8 15  .A......[...>...
0610: A0 AA 4E 85 95 AE 2F 0E   31 92 AC 3C FB 2F C4     ..N.../.1..<./.

それから解析を行い、CertificateRequestメッセージを探し出します。

サーバーは、共通名(CN=)「Duke」を持つX509証明書の被認証者により、自身の身元を証明するようクライアントに要求しています。サーバーのX509TrustManagerには、クライアントによって提供される資格、または資格がない場合を拒否するオプションがあります。実際の使用状況では、CAによって署名された証明書を使用することがほとんどであり、代わりに、信頼されるCAのリストがこのメッセージに含まれています。

*** CertificateRequest
Cert Types: RSA, DSS,
Cert Authorities:
CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.",
L=Cupertino, ST=CA, C=US>
[read] MD5 and SHA1 hashes:  len = 131
0000: 0D 00 00 7F 02 01 02 00   7A 00 78 30 76 31 0B 30  ........z.x0v1.0
0010: 09 06 03 55 04 06 13 02   55 53 31 0B 30 09 06 03  ...U....US1.0...
0020: 55 04 08 13 02 43 41 31   12 30 10 06 03 55 04 07  U....CA1.0...U..
0030: 13 09 43 75 70 65 72 74   69 6E 6F 31 1F 30 1D 06  ..Cupertino1.0..
0040: 03 55 04 0A 13 16 53 75   6E 20 4D 69 63 72 6F 73  .U....Sun Micros
0050: 79 73 74 65 6D 73 2C 20   49 6E 63 2E 31 16 30 14  ystems, Inc.1.0.
0060: 06 03 55 04 0B 13 0D 4A   61 76 61 20 53 6F 66 74  ..U....Java Soft
0070: 77 61 72 65 31 0D 30 0B   06 03 55 04 03 13 04 44  ware1.0...U....D
0080: 75 6B 65                                           uke
*** ServerHelloDone
[read] MD5 and SHA1 hashes:  len = 4
0000: 0E 00 00 00                                        ....

クライアントのクレデンシャルをサーバーに返信し、クライアントのX509KeyManagerが調べられるようにする必要があります。受け入れられる発行者のリスト(上述)と、KeyStoreに保管された証明書が一致することが期待されます。この場合は(運良く)一致しました。これで、「duke」としてクレデンシャルを有することになります。これらのクレデンシャルが受け入れられるかどうかは、サーバーのX509TrustManagerしだいです。

matching alias: duke
*** Certificate chain
chain [0] = [
[
Version: V1
Subject: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.",
L=Cupertino, ST=CA, C=US
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4

Key:  Sun RSA public key, 1024 bits
modulus: 134968166047563266914058280571444028986498087544923991226919517
667593269213420979048109900052353578998293280426361122296881234393722020
704208851688212064483570055963805034839797994154526862998272017486468599
962268346037652120279791547218281230795146025359480589335682217749874703
510467348902637769973696151441
public exponent: 65537
Validity: [From: Tue May 22 16:46:46 PDT 2001,
To: Sun May 22 16:46:46 PDT 2011]
Issuer: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.",
L=Cupertino, ST=CA, C=US
SerialNumber: [    3b0afa66]

]
Algorithm: [MD5withRSA]
Signature:
0000: 5F B5 62 E9 A0 26 1D 8E   A2 7E 7C 02 08 36 3A 3E  _.b..&.......6:>
0010: C9 C2 45 03 DD F9 BC 06   FC 25 CF 30 92 91 B1 4E  ..E......%.0...N
0020: 62 17 08 48 14 68 80 CF   DD 89 11 EA 92 7F CE DD  b..H.h..........
0030: B4 FD 12 A8 71 C7 9E D7   C3 D0 E3 BD BB DE 20 92  ....q......... .
0040: C2 3B C8 DE CB 25 23 C0   8B B6 92 B9 0B 64 80 63  .;...%#......d.c
0050: D9 09 25 2D 7A CF 0A 31   B6 E9 CA C1 37 93 BC 0D  ..%-z..1....7...
0060: 4E 74 95 4F 58 31 DA AC   DF D8 BD 89 BD AF EC C8  Nt.OX1..........
0070: 2D 18 A2 BC B2 15 4F B7   28 6F D3 00 E1 72 9B 6C  -.....O.(o...r.l

]
***

この符号化方式の場合は、サーバーとクライアントの間で共有する秘密鍵を確立するために使用される、ClientKeyExchangeと呼ばれるメッセージを渡す必要があります。

このデータ全体は最終的に収集され、rawデバイスに書き込まれます。

*** ClientKeyExchange, RSA PreMasterSecret, TLSv1
Random Secret:  { 3, 1, 132, 84, 245, 214, 235, 245, 168, 8, 186, 250,
122, 34, 97, 45, 117, 220, 64, 232, 152, 249, 14, 178, 135, 128, 184,
26, 143, 104, 37, 184, 81, 208, 84, 69, 97, 138, 80, 201, 187, 14, 57,
83, 69, 120, 190, 121 }
[write] MD5 and SHA1 hashes:  len = 754
0000: 0B 00 02 68 00 02 65 00   02 62 30 82 02 5E 30 82  ...h..e..b0..^0.
0010: 01 C7 02 04 3B 0A FA 66   30 0D 06 09 2A 86 48 86  ....;..f0...*.H.
0020: F7 0D 01 01 04 05 00 30   76 31 0B 30 09 06 03 55  .......0v1.0...U
0030: 04 06 13 02 55 53 31 0B   30 09 06 03 55 04 08 13  ....US1.0...U...
0040: 02 43 41 31 12 30 10 06   03 55 04 07 13 09 43 75  .CA1.0...U....Cu
0050: 70 65 72 74 69 6E 6F 31   1F 30 1D 06 03 55 04 0A  pertino1.0...U..
0060: 13 16 53 75 6E 20 4D 69   63 72 6F 73 79 73 74 65  ..Sun Microsyste
0070: 6D 73 2C 20 49 6E 63 2E   31 16 30 14 06 03 55 04  ms, Inc.1.0...U.
0080: 0B 13 0D 4A 61 76 61 20   53 6F 66 74 77 61 72 65  ...Java Software
0090: 31 0D 30 0B 06 03 55 04   03 13 04 44 75 6B 65 30  1.0...U....Duke0
00A0: 1E 17 0D 30 31 30 35 32   32 32 33 34 36 34 36 5A  ...010522234646Z
00B0: 17 0D 31 31 30 35 32 32   32 33 34 36 34 36 5A 30  ..110522234646Z0
00C0: 76 31 0B 30 09 06 03 55   04 06 13 02 55 53 31 0B  v1.0...U....US1.
00D0: 30 09 06 03 55 04 08 13   02 43 41 31 12 30 10 06  0...U....CA1.0..
00E0: 03 55 04 07 13 09 43 75   70 65 72 74 69 6E 6F 31  .U....Cupertino1
00F0: 1F 30 1D 06 03 55 04 0A   13 16 53 75 6E 20 4D 69  .0...U....Sun Mi
0100: 63 72 6F 73 79 73 74 65   6D 73 2C 20 49 6E 63 2E  crosystems, Inc.
0110: 31 16 30 14 06 03 55 04   0B 13 0D 4A 61 76 61 20  1.0...U....Java
0120: 53 6F 66 74 77 61 72 65   31 0D 30 0B 06 03 55 04  Software1.0...U.
0130: 03 13 04 44 75 6B 65 30   81 9F 30 0D 06 09 2A 86  ...Duke0..0...*.
0140: 48 86 F7 0D 01 01 01 05   00 03 81 8D 00 30 81 89  H............0..
0150: 02 81 81 00 C0 33 77 E7   1F D0 CE CE BD 43 2F 8D  .....3w......C/.
0160: EB C6 D3 07 A9 00 F5 75   4D C8 4B 04 52 42 EE 69  .......uM.K.RB.i
0170: F3 30 E9 A0 C6 07 B7 C8   55 2D B9 5B 57 7A 4C AD  .0......U-.[WzL.
0180: 1A 30 63 5C 7D 6D 16 BF   ED 54 13 49 8A 1B E6 29  .0c\.m...T.I...)
0190: 26 20 85 F9 5E 2B 2F A7   12 9C 98 2D 83 F6 EE B1  &..^+/....-....
01A0: 85 68 DA B5 8E 4C 1D 2D   8E 21 97 B0 30 C8 3A 57  .h...L.-.!..0.:W
01B0: F4 E1 18 9E F6 98 B2 D5   3D 8E D5 2B 09 E2 E1 A0  ........=..+....
01C0: 49 C1 A6 43 CE EA 57 7F   3B 5C 3A C9 BA DB B7 F0  I..C..W.;\:.....
01D0: 89 69 BF 91 02 03 01 00   01 30 0D 06 09 2A 86 48  .i.......0...*.H
01E0: 86 F7 0D 01 01 04 05 00   03 81 81 00 5F B5 62 E9  ............_.b.
01F0: A0 26 1D 8E A2 7E 7C 02   08 36 3A 3E C9 C2 45 03  .&.......6:>..E.
0200: DD F9 BC 06 FC 25 CF 30   92 91 B1 4E 62 17 08 48  .....%.0...Nb..H
0210: 14 68 80 CF DD 89 11 EA   92 7F CE DD B4 FD 12 A8  .h..............
0220: 71 C7 9E D7 C3 D0 E3 BD   BB DE 20 92 C2 3B C8 DE  q......... ..;..
0230: CB 25 23 C0 8B B6 92 B9   0B 64 80 63 D9 09 25 2D  .%#......d.c..%-
0240: 7A CF 0A 31 B6 E9 CA C1   37 93 BC 0D 4E 74 95 4F  z..1....7...Nt.O
0250: 58 31 DA AC DF D8 BD 89   BD AF EC C8 2D 18 A2 BC  X1..........-...
0260: B2 15 4F B7 28 6F D3 00   E1 72 9B 6C 10 00 00 82  ..O.(o...r.l....
0270: 00 80 4E DD E7 77 F1 91   6B 31 4E FA D6 61 D9 69  ..N..w..k1N..a.i
0280: 82 BD 22 40 83 FD 76 E6   FF A7 18 95 A0 04 28 0D  .."@..v.......(.
0290: 0D F7 44 6F 0C 42 4F 17   77 A0 99 56 2A 13 77 28  ..Do.BO.w..V*.w(
02A0: 0B 09 48 C1 B9 8C 09 ED   9F C6 2E 32 18 DB BD ED  ..H........2....
02B0: AF C3 AB E7 AD 8F DF 9E   AB 07 43 B4 50 EF 74 98  ..........C.P.t.
02C0: EA FC E8 4D C9 DA FC B0   B2 C7 D4 83 50 B5 84 B8  ...M........P...
02D0: 44 86 7B 5D 8A C2 F8 04   80 06 E6 84 42 33 B2 EE  D..]........B3..
02E0: 05 E6 D3 48 0E 23 E5 1F   63 4C 53 98 B8 8C 45 BA  ...H.#..cLS...E.
02F0: C8 19                                              ..
main, WRITE: TLSv1 Handshake, length = 754
[Raw write]: length = 759
0000: 16 03 01 02 F2 0B 00 02   68 00 02 65 00 02 62 30  ........h..e..b0
0010: 82 02 5E 30 82 01 C7 02   04 3B 0A FA 66 30 0D 06  ..^0.....;..f0..
0020: 09 2A 86 48 86 F7 0D 01   01 04 05 00 30 76 31 0B  .*.H........0v1.
0030: 30 09 06 03 55 04 06 13   02 55 53 31 0B 30 09 06  0...U....US1.0..
0040: 03 55 04 08 13 02 43 41   31 12 30 10 06 03 55 04  .U....CA1.0...U.
0050: 07 13 09 43 75 70 65 72   74 69 6E 6F 31 1F 30 1D  ...Cupertino1.0.
0060: 06 03 55 04 0A 13 16 53   75 6E 20 4D 69 63 72 6F  ..U....Sun Micro
0070: 73 79 73 74 65 6D 73 2C   20 49 6E 63 2E 31 16 30  systems, Inc.1.0
0080: 14 06 03 55 04 0B 13 0D   4A 61 76 61 20 53 6F 66  ...U....Java Sof
0090: 74 77 61 72 65 31 0D 30   0B 06 03 55 04 03 13 04  tware1.0...U....
00A0: 44 75 6B 65 30 1E 17 0D   30 31 30 35 32 32 32 33  Duke0...01052223
00B0: 34 36 34 36 5A 17 0D 31   31 30 35 32 32 32 33 34  4646Z..110522234
00C0: 36 34 36 5A 30 76 31 0B   30 09 06 03 55 04 06 13  646Z0v1.0...U...
00D0: 02 55 53 31 0B 30 09 06   03 55 04 08 13 02 43 41  .US1.0...U....CA
00E0: 31 12 30 10 06 03 55 04   07 13 09 43 75 70 65 72  1.0...U....Cuper
00F0: 74 69 6E 6F 31 1F 30 1D   06 03 55 04 0A 13 16 53  tino1.0...U....S
0100: 75 6E 20 4D 69 63 72 6F   73 79 73 74 65 6D 73 2C  un Microsystems,
0110: 20 49 6E 63 2E 31 16 30   14 06 03 55 04 0B 13 0D   Inc.1.0...U....
0120: 4A 61 76 61 20 53 6F 66   74 77 61 72 65 31 0D 30  Java Software1.0
0130: 0B 06 03 55 04 03 13 04   44 75 6B 65 30 81 9F 30  ...U....Duke0..0
0140: 0D 06 09 2A 86 48 86 F7   0D 01 01 01 05 00 03 81  ...*.H..........
0150: 8D 00 30 81 89 02 81 81   00 C0 33 77 E7 1F D0 CE  ..0.......3w....
0160: CE BD 43 2F 8D EB C6 D3   07 A9 00 F5 75 4D C8 4B  ..C/........uM.K
0170: 04 52 42 EE 69 F3 30 E9   A0 C6 07 B7 C8 55 2D B9  .RB.i.0......U-.
0180: 5B 57 7A 4C AD 1A 30 63   5C 7D 6D 16 BF ED 54 13  [WzL..0c\.m...T.
0190: 49 8A 1B E6 29 26 20 85   F9 5E 2B 2F A7 12 9C 98  I...)& ..^+/....
01A0: 2D 83 F6 EE B1 85 68 DA   B5 8E 4C 1D 2D 8E 21 97  -.....h...L.-.!.
01B0: B0 30 C8 3A 57 F4 E1 18   9E F6 98 B2 D5 3D 8E D5  .0.:W........=..
01C0: 2B 09 E2 E1 A0 49 C1 A6   43 CE EA 57 7F 3B 5C 3A  +....I..C..W.;\:
01D0: C9 BA DB B7 F0 89 69 BF   91 02 03 01 00 01 30 0D  ......i.......0.
01E0: 06 09 2A 86 48 86 F7 0D   01 01 04 05 00 03 81 81  ..*.H...........
01F0: 00 5F B5 62 E9 A0 26 1D   8E A2 7E 7C 02 08 36 3A  ._.b..&.......6:
0200: 3E C9 C2 45 03 DD F9 BC   06 FC 25 CF 30 92 91 B1  >..E......%.0...
0210: 4E 62 17 08 48 14 68 80   CF DD 89 11 EA 92 7F CE  Nb..H.h.........
0220: DD B4 FD 12 A8 71 C7 9E   D7 C3 D0 E3 BD BB DE 20  .....q.........
0230: 92 C2 3B C8 DE CB 25 23   C0 8B B6 92 B9 0B 64 80  ..;...%#......d.
0240: 63 D9 09 25 2D 7A CF 0A   31 B6 E9 CA C1 37 93 BC  c..%-z..1....7..
0250: 0D 4E 74 95 4F 58 31 DA   AC DF D8 BD 89 BD AF EC  .Nt.OX1.........
0260: C8 2D 18 A2 BC B2 15 4F   B7 28 6F D3 00 E1 72 9B  .-.....O.(o...r.
0270: 6C 10 00 00 82 00 80 4E   DD E7 77 F1 91 6B 31 4E  l......N..w..k1N
0280: FA D6 61 D9 69 82 BD 22   40 83 FD 76 E6 FF A7 18  ..a.i.."@..v....
0290: 95 A0 04 28 0D 0D F7 44   6F 0C 42 4F 17 77 A0 99  ...(...Do.BO.w..
02A0: 56 2A 13 77 28 0B 09 48   C1 B9 8C 09 ED 9F C6 2E  V*.w(..H........
02B0: 32 18 DB BD ED AF C3 AB   E7 AD 8F DF 9E AB 07 43  2..............C
02C0: B4 50 EF 74 98 EA FC E8   4D C9 DA FC B0 B2 C7 D4  .P.t....M.......
02D0: 83 50 B5 84 B8 44 86 7B   5D 8A C2 F8 04 80 06 E6  .P...D..].......
02E0: 84 42 33 B2 EE 05 E6 D3   48 0E 23 E5 1F 63 4C 53  .B3.....H.#..cLS
02F0: 98 B8 8C 45 BA C8 19                               ...E...

この時点で、実際の秘密鍵を生成するために必要な情報がすべてそろいます。

SESSION KEYGEN:
PreMaster Secret:
0000: 03 01 84 54 F5 D6 EB F5   A8 08 BA FA 7A 22 61 2D  ...T........z"a-
0010: 75 DC 40 E8 98 F9 0E B2   87 80 B8 1A 8F 68 25 B8  u.@..........h%.
0020: 51 D0 54 45 61 8A 50 C9   BB 0E 39 53 45 78 BE 79  Q.TEa.P...9SEx.y
CONNECTION KEYGEN:
Client Nonce:
0000: 40 FC 30 AE 2D 63 84 BB   C5 4B 27 FD 58 21 CA 90  @.0.-c...K'.X!..
0010: 05 F6 A7 7B 37 BB 72 E1   FC 1D 1B 6A F5 1C C8 9F  ....7.r....j....
Server Nonce:
0000: 40 FC 31 10 79 AB 17 66   FA 8B 3F AA FD 5E 48 23  @.1.y..f..?..^H#
0010: FA 90 31 D8 3C B9 A3 2C   8C F5 E9 81 9B A2 63 6C  ..1.<..,......cl
Master Secret:
0000: B0 00 22 34 59 03 16 B7   7A 6C 56 9B 89 D2 7A CC  .."4Y..<.c.
Server MAC write Secret:
0000: 1E 4D D1 D3 0A 78 EE B7   4F EC 15 79 B2 59 18 40  .M...x..O..y.Y.@
Client write key:
0000: 10 D0 D6 C2 D9 B7 62 CB   2C 74 BF 5F 85 3C 6F E7  ......b.,t._.<o.
Server write key:
0000: 06 65 DF BD 16 4B A5 7D   8C 66 2A 80 C1 45 B4 F3  .e...K...f*..E..
... no IV for cipher
>

サーバーに簡単な確認メッセージを送信し、送信したばかりのクライアント証明書に対応する非公開鍵を持っていることを確証します。

*** CertificateVerify
[write] MD5 and SHA1 hashes:  len = 134
0000: 0F 00 00 82 00 80 45 41   43 4B 47 1D F0 EE D1 14  ......EACKG.....
0010: AE F9 B3 2C 1F B9 FE 7B   3E 91 50 C5 0F F1 57 4F  ...,....>.P...WO
0020: 55 F1 4B C3 79 16 A8 F1   72 6B 10 CA CC 83 02 FC  U.K.y...rk......
0030: 97 3D 04 29 44 4C 58 74   84 94 19 63 BB 8A 2C 78  .=.)DLXt...c..,x
0040: 43 A0 DD 5E 54 52 AA 97   15 92 1C 39 6B 10 2E BF  C..^TR.....9k...
0050: F2 DA AE 2D 8F FB 50 44   9E E2 1F 7D C9 C5 CB A0  ...-..PD........
0060: 31 A0 F9 AA 93 2D 1B 07   1B FA E0 EE 95 E7 88 D7  1....-..........
0070: AD 4A 3A 40 DC FB DF 9E   EB 75 04 14 E2 F2 BB DC  .J:@.....u......
0080: 1B 7E 6E D5 8C 62                                  ..n..b
main, WRITE: TLSv1 Handshake, length = 134
[Raw write]: length = 139
0000: 16 03 01 00 86 0F 00 00   82 00 80 45 41 43 4B 47  ...........EACKG
0010: 1D F0 EE D1 14 AE F9 B3   2C 1F B9 FE 7B 3E 91 50  ........,....>.P
0020: C5 0F F1 57 4F 55 F1 4B   C3 79 16 A8 F1 72 6B 10  ...WOU.K.y...rk.
0030: CA CC 83 02 FC 97 3D 04   29 44 4C 58 74 84 94 19  ......=.)DLXt...
0040: 63 BB 8A 2C 78 43 A0 DD   5E 54 52 AA 97 15 92 1C  c..,xC..^TR.....
0050: 39 6B 10 2E BF F2 DA AE   2D 8F FB 50 44 9E E2 1F  9k......-..PD...
0060: 7D C9 C5 CB A0 31 A0 F9   AA 93 2D 1B 07 1B FA E0  .....1....-.....
0070: EE 95 E7 88 D7 AD 4A 3A   40 DC FB DF 9E EB 75 04  ......J:@.....u.
0080: 14 E2 F2 BB DC 1B 7E 6E   D5 8C 62                 .......n..b

大部分が終了しました。新しく確立した符号化方式に変更することをサーバーに知らせます。それ以後のすべてのメッセージは、確立したばかりのパラメータを使用して暗号化されます。暗号化された終了メッセージを送信して、すべての処理が終了したことを確証します。

main, WRITE: TLSv1 Change Cipher Spec, length = 1
[Raw write]: length = 6
0000: 14 03 01 00 01 01                                  ......
*** Finished
verify_data:  { 242, 98, 66, 170, 124, 124, 204, 231, 73, 15, 237, 172 }
***
[write] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C F2 62 42 AA   7C 7C CC E7 49 0F ED AC  .....bB.....I...
Padded plaintext before ENCRYPTION:  len = 32
0000: 14 00 00 0C F2 62 42 AA   7C 7C CC E7 49 0F ED AC  .....bB.....I...
0010: FA 06 3C 9F 8C 41 1D ED   2B 06 D0 5A ED 31 F2 80  ..<..A..+..Z.1..
main, WRITE: TLSv1 Handshake, length = 32

上記メッセージが、5バイトのヘッダー情報に続いてraw出力デバイスに実際に書き込まれるときには、メッセージは暗号化されます。

[Raw write]: length = 37
0000: 16 03 01 00 20 15 8C 25   BA 4E 73 F5 27 79 49 B1  .... ..%.Ns.'yI.
0010: E9 F5 7E C8 48 A7 D3 A6   9B BD 6F 8E A5 8E 2B B7  ....H.....o...+.
0020: EE DC BD F4 D7                                     .....

サーバーが同じChange Cipher Spec/Finishedメッセージを送信するのを待ちます。これにより、ネゴシエーションが正常に完了したことがわかります。

[Raw read]: length = 5
0000: 14 03 01 00 01                                     .....
[Raw read]: length = 1
0000: 01                                                 .
main, READ: TLSv1 Change Cipher Spec, length = 1
[Raw read]: length = 5
0000: 16 03 01 00 20                                     ....
[Raw read]: length = 32
0000: 1F F5 FA C8 79 20 CE 91   AA 68 7F 6C F3 5A DB 7B  ....y ...h.l.Z..
0010: A5 1C 31 1F 6F 41 50 C5   C6 25 25 8D 48 50 3F F1  ..1.oAP..%%.HP?.
main, READ: TLSv1 Handshake, length = 32
Padded plaintext after DECRYPTION:  len = 32
0000: 14 00 00 0C 07 38 46 5F   62 AD 41 B3 DC 79 30 FD  .....8F_b.A..y0.
0010: 34 F2 3B 54 1C D4 68 0E   92 0B 9C 7E ED 47 9F 3B  4.;T..h......G.;
*** Finished
verify_data:  { 7, 56, 70, 95, 98, 173, 65, 179, 220, 121, 48, 253 }
***
[read] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C 07 38 46 5F   62 AD 41 B3 DC 79 30 FD  .....8F_b.A..y0.

すべてが正常に完了しました。この接続が切断されたあと、もう一度このセッションを確立する場合に備えて、確立されたセッションをキャッシュします。

この時点で、SSL/TLSクライアントはピアの資格を調べ、意図するサーバーと通信していることを確認します。HttpsURLConnectionは、問題が生じた場合にホスト名とcall HostnameVerifierをチェックしますが、raw SSLSocketはチェックしません。この検証動作は手動で行いますが、ここでは省略します。

ここまでで、アプリケーション・データを交換する準備が整います。Get /index.html HTTP1.1コマンドを送信します。

%% Cached client session: [Session-1, SSL_RSA_WITH_RC4_128_MD5]
Padded plaintext before ENCRYPTION:  len = 42
0000: 47 45 54 20 2F 69 6E 64   65 78 2E 68 74 6D 6C 20  GET /index.html
0010: 48 54 54 50 2F 31 2E 31   0A 0A CA CB D1 10 9D 0E  HTTP/1.1........
0020: 13 3C D9 66 6B 9E 36 87   ED 9B                    .<.fk.6...
main, WRITE: TLSv1 Application Data, length = 42

送信中のデータは、5バイトのヘッダー情報をスキップして暗号化されています。

[Raw write]: length = 47
0000: 17 03 01 00 2A 8A E9 EC   2C 8D 19 B6 E2 50 C1 E2  ....*...,....P..
0010: 22 1A C0 97 95 23 99 E1   20 DD F3 2A B4 DC 14 57  "....#.. ..*...W
0020: 32 71 58 98 01 BE 70 11   A3 FC 8E 3A 7C 8D BF     2qX...p....:...

アプリケーション・データを取り戻します。最初にHTTPSヘッダー、次に実際のデータが戻されます。

[Raw read]: length = 5
0000: 17 03 01 00 50                                     ....P
[Raw read]: length = 80
0000: 70 10 0D D6 FA ED 51 FC   C2 7E CE 24 2E F1 2F F7  p.....Q....$../.
0010: E7 CD A5 D6 2D 1B 10 FD   48 56 9C 1B B5 EC 8F 1E  ....-...HV......
0020: DB DA F9 83 62 52 15 38   70 4B C1 85 13 EF CA 17  ....bR.8pK......
0030: 89 37 D3 45 C0 88 BB 92   63 F6 9C DE 69 E6 60 3E  .7.E....c...i.`>
0040: 1F F7 4D C1 56 61 79 01   49 55 FB 38 6B 16 81 BC  ..M.Vay.IU.8k...
main, READ: TLSv1 Application Data, length = 80
Padded plaintext after DECRYPTION:  len = 80
0000: 48 54 54 50 2F 31 2E 30   20 32 30 30 20 4F 4B 0D  HTTP/1.0 200 OK.
0010: 0A 43 6F 6E 74 65 6E 74   2D 4C 65 6E 67 74 68 3A  .Content-Length:
0020: 20 35 38 0D 0A 43 6F 6E   74 65 6E 74 2D 54 79 70   58..Content-Typ
0030: 65 3A 20 74 65 78 74 2F   68 74 6D 6C 0D 0A 0D 0A  e: text/html....
0040: 40 18 A1 FF 1D 5A D4 55   98 DB E3 95 01 A0 91 AF  @....Z.U........
HTTP/1.0 200 OK
Content-Length: 58
Content-Type: text/html
[Raw read]: length = 5
0000: 17 03 01 00 4A                                     ....J
[Raw read]: length = 74
0000: 75 DA F2 58 C3 5E 58 DE   14 AD FE 71 A3 78 07 58  u..X.^X....q.x.X
0010: EB E9 2B A2 D7 82 5C 6E   C9 9C 58 84 D7 A8 C6 F8  ..+...\n..X.....
0020: DE C6 5B BA 10 59 DF CC   11 AE 35 F7 C7 0F F6 C2  ..[..Y....5.....
0030: 3E 67 4E 95 30 AA 91 0B   E4 4F 5C C7 BF 50 AC 61  >gN.0....O\..P.a
0040: 87 B7 80 75 F0 81 F1 00   63 C9                    ...u....c.
main, READ: TLSv1 Application Data, length = 74
Padded plaintext after DECRYPTION:  len = 74
0000: 3C 48 54 4D 4C 3E 0A 3C   48 31 3E 48 65 6C 6C 6F  <HTML>.<H1>Hello
0010: 20 57 6F 72 6C 64 3C 2F   48 31 3E 0A 54 68 65 20   World</H1>.The
0020: 74 65 73 74 20 69 73 20   63 6F 6D 70 6C 65 74 65  test is complete
0030: 21 0A 3C 2F 48 54 4D 4C   3E 0A 38 2E 68 72 F1 47  !.</HTML>.8.hr.G
0040: E8 56 D1 EA A6 FC 3C 30   6F F3                    .V....<0o.
<HTML>
<H1>Hello World<H1>
The test is complete!
<HTML>

ソケットからもう一度読み出し、さらにデータがあるかどうかを確認します。close_notifyメッセージを受け取ります。このメッセージは、この接続が正常に切断されることを意味します。応答メッセージを返し、ソケットを閉じます。

[Raw read]: length = 5
0000: 15 03 01 00 12                                     .....
[Raw read]: length = 18
0000: 09 AB 95 00 43 8D C8 7C   83 18 EB C4 8C 99 43 A6  ....C.........C.
0010: 76 49                                              vI
main, READ: TLSv1 Alert, length = 18
Padded plaintext after DECRYPTION:  len = 18
0000: 01 00 FA 44 D5 57 71 5C   CC C7 D9 D0 04 23 10 D8  ...D.Wq\.....#..
0010: 21 7B                                              !.
main, RECV TLSv1 ALERT:  warning, close_notify
main, called closeInternal(false)
main, SEND TLSv1 ALERT:  warning, description = close_notify
Padded plaintext before ENCRYPTION:  len = 18
0000: 01 00 8A 2C A2 36 9C 88   22 50 6E BC 95 3B B2 C4  ...,.6.."Pn..;..
0010: FE F2                                              ..
main, WRITE: TLSv1 Alert, length = 18
[Raw write]: length = 23
0000: 15 03 01 00 12 19 BE 10   8D FA F1 CA DD AB CC 91  ................
0010: 2E 49 08 71 2B C1 05                               .I.q+..
main, called close()
main, called closeInternal(true)
main, called close()
main, called closeInternal(true)
main, called close()
main, called closeInternal(true)

コード例

このセクションには次のコード例が含まれています。

トピック

- セキュアでないソケットからセキュアなソケットへの変換

- JSSEサンプル・コードの実行

- JSSEで使用するキーストアの作成

- Server Name Indication (SNI)拡張の使用

セキュアでないソケットからセキュアなソケットへの変換

JSSEを使用してセキュアでないソケット接続をセキュアなソケット接続に変換する方法を示すコード例です。このコード例は、『Java SE 6ネットワーク・セキュリティ』(Marco Pistoia他著)から引用したものです。

例8-26では、セキュアでないソケットを使用してクライアントとサーバーとの間の通信を設定するために使用できるサンプル・コードを示します。次に例8-27ではこのコードを変更し、JSSEを使用してセキュアなソケット通信を設定します。

例8-26 SSLを使用しないソケットの例

次の例では、セキュアでないソケット接続を設定するサーバー側およびクライアント側のコードを示します。

サーバーとして動作し、ソケットを使用してクライアントと通信するJavaプログラムでは、次のようなコードでソケット通信を設定します。

import java.io.*;
import java.net.*;

. . .

int port = availablePortNumber;

ServerSocket s;

try {
s = new ServerSocket(port);
Socket c = s.accept();

OutputStream out = c.getOutputStream();
InputStream in = c.getInputStream();

// Send messages to the client through
// the OutputStream
// Receive messages from the client
// through the InputStream
} catch (IOException e) { }

ソケットを使ってサーバーとの通信を設定するクライアント・コードの例を、次に示します。

import java.io.*;
import java.net.*;

. . .

int port = availablePortNumber;
String host = "hostname";

try {
s = new Socket(host, port);

OutputStream out = s.getOutputStream();
InputStream in = s.getInputStream();

// Send messages to the server through
// the OutputStream
// Receive messages from the server
// through the InputStream
} catch (IOException e) { }

例8-27 SSLを使用するソケットの例

次の例では、セキュアなソケット接続を設定するサーバー側およびクライアント側のコードを示します。

サーバーとして動作し、セキュアなソケットを使用してクライアントと通信するJavaプログラムでは、次のようなコードでソケット通信を設定します。セキュアでないソケットを使った通信のプログラムとこのプログラムとの違いは、太字で示されています。

import java.io.*;
import javax.net.ssl.*;

. . .

int port = availablePortNumber;

SSLServerSocket s;

try {
SSLServerSocketFactory sslSrvFact =
(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
s = (SSLServerSocket)sslSrvFact.createServerSocket(port);

SSLSocket c = (SSLSocket)s.accept();

OutputStream out = c.getOutputStream();
InputStream in = c.getInputStream();

// Send messages to the client through
// the OutputStream
// Receive messages from the client
// through the InputStream
}

catch (IOException e) {
}

セキュアなソケットを使ってサーバーとの通信を設定するクライアント・コードの例を、次に示します。セキュアでないソケットとの違いは、太字で示されています。

import java.io.*;
import javax.net.ssl.*;

. . .

int port = availablePortNumber;
String host = "hostname";

try {
SSLSocketFactory sslFact =
(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket s = (SSLSocket)sslFact.createSocket(host, port);

OutputStream out = s.getOutputStream();
InputStream in = s.getInputStream();

// Send messages to the server through
// the OutputStream
// Receive messages from the server
// through the InputStream
}

catch (IOException e) {
}

JSSEサンプル・コードの実行

JSSEサンプル・プログラムでは、JSSEの使用方法を示します。

- クライアントとサーバーのセキュアなソケット接続を表すサンプル・コード

- HTTPS接続を表すサンプル・コード

- セキュアなRMI接続を表すサンプル・コード

- SSLEngineの使用を表すサンプル・コード

サンプル・コードを使用する場合、サンプル・プログラムはJSSEの使用法を示すために設計されていることに注意してください。堅牢なアプリケーションを開発するためのものではありません。

注意:
セキュアな通信を設定すると、アルゴリズムが複雑になります。サンプル・プログラムでは、設定プロセス中のフィードバックがありません。プログラムの実行時は、少し待ってください。しばらく出力が表示されない場合があります。javax.net.debugシステム・プロパティをallに設定して、プログラムを実行すると、フィードバックが表示されます。このデバッグ情報の見方の説明は、ガイド「SSL/TLS接続のデバッグ」を参照してください。

サンプル・コードの場所

JDK 8ドキュメント内のJSSEサンプル・コードではすべてのサンプル・コード・ファイルとテキスト・ファイルをリストします。そのページには、すべてのサンプル・コード・ファイルをダウンロードできるZIPファイルへのリンクも記載されています。

次のセクションでは、サンプルについて説明します。JDK 8ドキュメント内のJSSEサンプル・コードでREADME.txtを参照してください。

クライアントとサーバーのセキュアなソケット接続を表すサンプル・コード

samples/socketsディレクトリにあるサンプル・プログラムは、クライアントとサーバーとの間でセキュアなソケット接続を設定する方法を示しています。

サンプルのクライアント・プログラムを実行中に、Webサーバーなどの既存のサーバーと通信できます。または、サンプル・サーバー・プログラムClassFileServerと通信することもできます。サンプルのクライアント・プログラムとサーバー・プログラムは、同じネットワークに接続された別個のマシンで実行することも、1台のマシンで、ただし別のターミナル・ウィンドウから両方を実行することもできます。

amples/sockets/clientディレクトリのサンプルSSLSocketClient*プログラム(および「HTTPS接続を表すサンプル・コード」で説明するURLReader*プログラム)は、すべてClassFileServerサンプル・サーバー・プログラムで実行できます。これを実行する方法の例は、「ClassFileServerを使用したSSLSocketClientWithClientAuthの実行」に示しています。同様の変更をして、URLReader、SSLSocketClientまたはSSLSocketClientWithTunnelingをClassFileServerで実行できます。

クライアントとサーバーとの間で通信中に認証エラーが発生する場合(WebサーバーとClassFileServerのどちらを使用しているか関係なく)、必要な鍵がトラストストア(トラスト鍵データベース)にないためである可能性があります。用語と定義を参照してください。たとえば、ClassFileServerは、SSLハンドシェーク中に必要なlocalhostの公開鍵を格納するtestkeysというキーストアを使用します。testkeysキーストアはClassFileServerソースと同じsamples/sockets/serverディレクトリにあります。参照するトラストストアで、localhostの対応する公開鍵の証明書をクライアントが見つけられない場合、認証エラーが発生します。次のセクションで説明するように、samplecacertsトラストストア(localhostの公開鍵と証明書を格納する)を使用してください。

構成要件

クライアントとサーバーとの間のセキュアなソケット接続を作成するサンプル・プログラムを実行する場合は、適切な証明書ファイル(トラストストア)を利用できるようにしておく必要があります。クライアント・プログラムとサーバー・プログラムの両方で、samplesディレクトリのsamplecacerts証明書ファイルを使用してください。この証明書ファイルを使用すると、クライアントがサーバーを認証できるようになります。このファイルには、JDK (cacertsファイルにある)に付属する一般的な証明書発行局(CA)のすべての証明書と、サンプルサーバーClassFileServerとの通信時にクライアントがlocalhostを認証するために必要なlocalhostの証明書が含まれています。ClassFileServerは、samplecacertsの公開鍵に対応するlocalhostの公開鍵を含むキーストアを使用します。

クライアントとサーバーの両方でsamplecacertsファイルを使用できるようにするには、それをjava-home/lib/security/jssecacertsファイルにコピーして名前をcacertsに変更し、java-home/lib/security/cacertsファイルと置き換えます。あるいは、クライアントとサーバーの両方に対してjavaコマンドを実行する場合は、次のオプションをコマンド行に追加します。

-Djavax.net.ssl.trustStore=path_to_samplecacerts_file

java-homeの詳細は、用語と定義を参照してください。

samplecacertsトラスト・ストアのパスワードはchangeitです。keytoolユーティリティを使用して、サンプルの独自の証明書を置き換えることができます。

Mozilla FirefoxやMicrosoft Internet Explorerなどのブラウザを使用して、ClassFileServerの例で提供されているサンプルのSSLサーバーにアクセスすると、ダイアログ・ボックスが開いて、証明書が認識されないというメッセージが表示されます。これは、サンプル・プログラムで使用する証明書は自己署名付きのもので、テスト用にすぎないためです。現在のセッションで証明書に同意できます。SSLサーバーのテストが終了したあと、ブラウザを終了し、ブラウザの名前空間からテスト用証明書を削除します。

クライアント認証の場合、適切なディレクトリの別のduke証明書を使用できます。公開鍵および証明書も、samplecacertsファイルに格納されています。

SSLSocketClientの実行

JDK 8ドキュメント内のJSSEサンプル・コードのSSLSocketClient.javaプログラムは、SSLSocketを使用してHTTP要求を送信しHTTPSサーバーから応答を受け取るクライアントの作成方法を示します。このプログラムの出力は、https://www.verisign.com/index.htmlのHTMLソースです。

ファイアウォールの内側から、このプログラムを提供されたとおりに実行しないでください。ファイアウォールの内側から実行すると、JSSEはファイアウォールを経由したwww.verisign.comへのパスを検出できないので、UnknownHostExceptionを受け取ります。ファイアウォールの外側から実行できる同等のクライアントを作成するには、サンプル・プログラムSSLSocketClientWithTunnelingで示すように、プロキシ・トンネリングを設定します。

SSLSocketClientWithTunnelingの実行

JDK 8ドキュメント内のJSSEサンプル・コードのSSLSocketClientWithTunneling.javaプログラムは、ファイアウォールの外側からセキュアなWebサーバーにアクセスするプロキシ・トンネリングの方法を示します。このプログラムを実行するには、次のJavaシステム・プロパティに適切な値を設定する必要があります。

java -Dhttps.proxyHost=webproxy
-Dhttps.proxyPort=ProxyPortNumber
SSLSocketClientWithTunneling

注意:
-Dオプションによるプロキシ指定はオプションです。webproxyは使用するプロキシ・ホスト名に、ProxyPortNumberは適切なポート番号に置き換えてください。

プログラムはhttps://www.verisign.com/index.htmlのHTMLソース・ファイルを返します。

SSLSocketClientWithClientAuthの実行

JDK 8ドキュメント内のJSSEサンプル・コードのSSLSocketClientWithClientAuth.javaプログラムは、サーバーから要求された場合にクライアント認証を行うように、キー・マネージャを設定する方法を示します。このプログラムも、クライアントがファイアウォールの外側にはいないことを前提にしています。SSLSocketClientWithTunnelingの例に従ってプログラムを変更すれば、ファイアウォールの内側から接続することもできます。

このプログラムを実行するには、ホスト、ポート、および要求されたファイル・パスの3つのパラメータを指定する必要があります。前の例を反映させるには、ホストにwww.verisign.com、ポート番号に443、要求されたファイル・パスにhttps://www.verisign.com/を設定することで、このプログラムをクライアント認証なしで実行します。これらのパラメータを使用したときの出力が、Webサイトhttps://www.verisign.com/のHTMLソースです。

SSLSocketClientWithClientAuthを実行してクライアント認証を行うには、クライアント認証を要求するサーバーにアクセスする必要があります。このサーバーには、サンプル・プログラムClassFileServerを使用できます。これについては、次のセクションで説明します。

ClassFileServerの実行

ここでClassFileServerと呼ばれるプログラムは、JDK 8ドキュメント内のJSSEサンプル・コードのClassFileServer.javaとClassServer.javaという2つのファイルで構成されています。

これらを実行するには、ClassFileServer.classを実行します。その際は次のパラメータが必要です。

- portは、利用できる未使用のポート番号です。たとえば、2001のような数字を使用できます。

- docrootは、取得するファイルを含むサーバーのディレクトリを表します。たとえば、Solarisでは、/home/userid/ (useridは特定のUIDを表す)を使用でき、Microsoft Windowsシステムではc:\を使用できます。

- TLSは、サーバーがSSLを使用するかTLSを使用するかを示すオプションのパラメータです。

- trueは、クライアント認証が必要であることを示すオプションのパラメータです。このパラメータは、TLSパラメータが設定されているかどうかだけを参照します。

注意:
TLSパラメータとtrueパラメータはオプションです。それらを省略した場合は、TLSではない通常のファイルサーバーを認証なしで使用し、何も起こらないことを示します。これは、一方の側(クライアント)がTLSとネゴシエーションを行おうとしても、もう一方の側(サーバー)は行おうとしないため、通信ができないからです。

注意:
サーバーは、GET /path_to_fileの形式でのGET要求を予期しています。

ClassFileServerによるSSLSocketClientWithClientAuthの実行

JDK 8ドキュメント内のJSSEサンプル・コードのサンプル・プログラムSSLSocketClientWithClientAuth.javaおよびClassFileServerを使用して、認証済の通信を設定できます。この通信では、クライアントとサーバーが相互に認証します。サンプルのプログラムは、同じネットワークに接続された別個のマシンで実行することも、1台のマシンで、ただし別のターミナル・ウィンドウまたはコマンド・プロンプト・ウィンドウから両方を実行することもできます。クライアントとサーバーの両方を設定するには、次を実行します。

1. ClassFileServerプログラムを1つのマシンまたはターミナル・ウィンドウから実行します。
ClassFileServerの実行を参照してください。

2. SSLSocketClientWithClientAuthプログラムを別のマシンやターミナル・ウィンドウで実行します。SSLSocketClientWithClientAuthには、次のパラメータが必要です。

- hostは、ClassFileServerを実行するために使用するマシンのホスト名です。

- portは、ClassFileServer用に指定したものと同じポートです。

- requestedfilepathは、サーバーから取得するファイルのパスを示します。このパラメータには、/filepathと指定する必要があります。GET文の一部として使用されるので、ファイル・パスにはフォワード・スラッシュが必要です。GET文には、稼動中のオペレーティング・システムの種類にかかわらず、フォワード・スラッシュが必要です。文の構成は次のようになります。

"GET " + requestedfilepath + " HTTP/1.0"

注意:
ClassFileServerが動作しているローカル・マシンに接続するように、他のSSLClient*アプリケーションのGETコマンドを変更できます。

HTTPS接続を表すサンプル・コード

JSSEを介してセキュアな通信にアクセスするためのプライマリAPIは2つあります。1つの方法は、SSLSocketClient、SSLSocketClientWithTunnelingおよびSSLSocketClientWithClientAuth (ClassFileServerを使用するか使用しないで)に示すように、任意のセキュアな通信に使用できるソケット・レベルのAPIを使用することです。

2つ目はもっと簡単な方法で、標準のJava URL APIを使用します。java.net.URLクラスを使用したHTTPS URLプロトコルまたはスキームを使用して、SSL対応のWebサーバーとセキュアに通信できます。

HTTPS URLスキームのサポートは一般的なブラウザの多くに実装されており、JSSEで提供されているソケット・レベルAPIを必要とせずにセキュアな通信にアクセスできます。

URLの例はhttps://www.verisign.comです。

HTTPS URL実装のトラストおよび鍵の管理は、環境に固有です。JSSE実装は、HTTPS URL実装を提供します。別のHTTPSプロトコル実装を使用するには、java.protocol.handler.pkgsを設定します。パッケージ名に対するjava.lang.Systemプロパティの指定方法を参照してください。詳細については、java.net.URLクラスのドキュメントを参照してください。

JSSEでダウンロードできるサンプルには、HTTPS接続の作成方法を示すサンプル・プログラムが2つ含まれています。これらのサンプル・プログラム(URLReader.javaとURLReaderWithOptions.java)は、どちらもsamples/urlsディレクトリにあります。

URLReaderの実行

JDK 8ドキュメント内のJSSEサンプル・コードのURLReader.javaプログラムは、セキュアなサイトにアクセスするURLクラスの使用方法を示します。このプログラムの出力は、https://www.verisign.com/のHTMLソースです。デフォルトで、JSSEに付属のHTTPSプロトコル実装が使用されます。別の実装を使用するには、システム・プロパティjava.protocol.handler.pkgsの値を、実装を含むパッケージ名に設定します。

ファイアウォールの内側でサンプル・コードを実行している場合は、https.proxyHostおよびhttps.proxyPortシステム・プロパティを設定する必要があります。たとえば、ポート8080でプロキシ・ホスト「webproxy」を使用する場合は、javaコマンドで次のオプションを使用できます。

-Dhttps.proxyHost=webproxy
-Dhttps.proxyPort=8080

または、java.lang.SystemのメソッドsetProperty()で、ソース・コード内のシステム・プロパティを設定することもできます。たとえば、コマンド行オプションを使用するかわりに、使用するプログラムに次の行を含めることができます。

System.setProperty("java.protocol.handler.pkgs", "com.ABC.myhttpsprotocol");
System.setProperty("https.proxyHost", "webproxy");
System.setProperty("https.proxyPort", "8080");

URLReaderWithOptionsの実行

JDK 8ドキュメント内のJSSEサンプル・コードのURLReaderWithOptions.javaプログラムは、基本的にはURLReader.javaプログラムと同じですが、実行時にプログラムの引数として次のシステム・プロパティのどれかまたはすべてをオプションで入力できる点が異なります。

- java.protocol.handler.pkgs

- https.proxyHost

- https.proxyPort

- https.cipherSuites

URLReaderWithOptionsを実行するには、次のコマンドを入力します。

java URLReaderWithOptions [-h proxyhost -p proxyport] [-k protocolhandlerpkgs] [-c ciphersarray]

注意:
複数のプロトコル・ハンドラを、縦線で区切った項目のリストでprotocolhandlerpkgs引数に含めることができます。複数のSSL暗号化方式群名を、カンマで区切った項目のリストでciphersarray引数に含めることができます。可能な暗号化方式群名はSSLSocket.getSupportedCipherSuites()メソッドで返されるものと同じです。暗号群はSSLおよびTLSプロトコルの仕様から命名されています。

protocolhandlerpkgs引数は、Oracleが提供するデフォルト以外のHTTPSプロトコル・ハンドラ実装を使用する場合にのみ必要です。

ファイアウォールの内側でサンプル・コード実行している場合は、プロキシ・ホストおよびプロキシ・ポートの引数を含める必要があります。また、使用できる暗号群のリストを含めることもできます。

次に、URLReaderWithOptionsの実行例と、ポート8080にプロキシ・ポート「webproxy」を指定する場合の例を示します。

java URLReaderWithOptions -h webproxy -p 8080

セキュアなRMI接続を表すサンプル・コード

samples/rmiディレクトリ内のサンプル・コードは、セキュアなJava Remote Method Invocation (RMI)接続の作成方法を示しています。このサンプル・コードは、基本的には、カスタムRMIソケット・ファクトリをインストールして使用するように変更されたHello Worldサンプルです。

SSLEngineの使用を表すサンプル・コード

SSLEngineは、アプリケーション開発者にI/Oおよび計算戦略を選択するときの柔軟性を提供します。SSLEngineは、SSL/TLS実装を特定のI/O抽象化(シングル・スレッドSSLSocketsなど)に結びつけるのではなく、I/Oおよび計算の制約をSSL/TLS実装から除外します。

前述したように、SSLEngineは高度なAPIであり、不用意に使用することはできません。ここでは、その使用を説明するのに役立つ入門用サンプル・コードを示します。最初のデモは、ほとんどのI/Oおよびスレッドの発行を除外し、SSLEngineメソッドの多くに重点を置きます。2番目のデモは、より現実的な例であり、SSLEngineがどのようにJava NIOと結合して基本的なHTTP/HTTPSサーバーを作成するかを示します。

SSLEngineSimpleDemoの実行

JDK 8ドキュメント内のJSSEサンプル・コードのSSLEngineSimpleDemo.javaプログラムは、入出力とスレッドの問題が簡素化されている、SSLEngineの操作に重点を置いた非常に単純なアプリケーションです。このアプリケーションは、一般的なByteBufferオブジェクトによってSSL/TLSメッセージを交換する2つのSSLEngineオブジェクトを作成します。1つのループがすべてのエンジン操作を順番に実行して、セキュアな接続の確立(ハンドシェーク)、アプリケーション・データの転送、およびエンジンのクローズを示します。

SSLEngineResultは、SSLEngineの現在の状態に関して多くの情報を提供します。この例では、すべての状態を調べているわけではありません。I/Oおよびスレッドの発行を適度に単純化しているため本番稼動環境に適した例ではありませんが、SSLEngineの全体的な機能の説明に有用です。

NIOベースのサーバーの実行

SSLEngineによって提供される柔軟性を十分に利用するには、最初にI/Oやスレッド・モデルなどの相補的なAPIを理解する必要があります。

大規模なアプリケーションの開発者が有用と考えるI/Oモデルは、NIO SocketChannelです。NIOは、java.net.Socket APIに内在するスケーリングの問題のいくつかを解決するために部分的に導入されました。SocketChannelには、次のような様々な操作モードがあります。

- ブロック

- 非ブロック

- セレクタによる非ブロック

新しい多くのNIO APIを示すだけでなく、セキュアなHTTPSサーバーを作成するためにSSLEngineを採用する方法も示す、基本的なHTTPサーバーのサンプル・コードが提供されています。サーバーは本番稼動の品質ではありませんが、これらの多くの新しいAPIの動作を示しています。

サンプル・ディレクトリには、サーバーを紹介するREADME.txtファイルがあり、これには、サーバーの構築および構成方法の説明、コード・レイアウトの簡潔な概要が含まれています。SSLEngineのユーザーにとってもっとも重要なファイルは、ChannelIO.javaおよびChannelIOSecure.javaです。

注意:
この項で説明するサーバー例は、JDKに含まれています。コードは、jdk-home/samples/nio/serverディレクトリにバンドルされています。

JSSEで使用するキーストアの作成

keytoolユーティリティを使用してJSSEでの使用に適した単純なPKCS12キーストアを作成する方法の手順を示します。

まずキーストア内に(公開鍵および秘密鍵を持つ) keyEntryを作成し、トラストストア内に対応するtrustedCertEntry (公開鍵のみ)を作成します。クライアント認証の場合、クライアントの証明書に対して同様の処理に従う必要があります。

注意:
PKCS12での信頼できるアンカーおよび秘密鍵の格納は、JDK 8以降でサポートされています。

注意:
ここでは、各ステップに関する詳しい解説は省略します。keytoolを参照してください。

ユーザー入力は太字で表示されます。

1. 対応する公開鍵および秘密鍵によって、新しいキーストアと自己署名付き証明書を作成します。

% keytool -genkeypair -alias duke -keyalg RSA -validity 7 -keystore keystore

Enter keystore password:  <password>
What is your first and last name?
[Unknown]:  Duke
What is the name of your organizational unit?
[Unknown]:  Java Software
What is the name of your organization?
[Unknown]:  Oracle, Inc.
What is the name of your City or Locality?
[Unknown]:  Palo Alto
What is the name of your State or Province?
[Unknown]:  CA
What is the two-letter country code for this unit?
[Unknown]:  US
Is CN=Duke, OU=Java Software, O="Oracle, Inc.",
L=Palo Alto, ST=CA, C=US correct?
[no]:  yes

2. キーストアを調べます。エントリの型はPrivatekeyEntryで、これは、このエントリに秘密鍵が関連付けられていることを示します。

% keytool -list -v -keystore keystore

Enter keystore password:  <password>

Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: duke
Creation date: Jul 25, 2016
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US
Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US
Serial number: 210cccfc
Valid from: Mon Jul 25 10:33:27 IST 2016 until: Mon Aug 01 10:33:27 IST 2016
Certificate fingerprints:
SHA1: 80:E5:8A:47:7E:4F:5A:70:83:97:DD:F4:DA:29:3D:15:6B:2A:45:1F
SHA256: ED:3C:70:68:4E:86:35:9C:63:CC:B9:59:35:58:94:1F:7E:B8:B0:EE:D2:
4B:9D:80:31:67:8A:D4:B4:7A:B5:12
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: RSA (2048)
Version: 3

Extensions:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 7F C9 95 48 42 8D 68 91   BA 1E E6 5C 2C 6B FF 75  ...HB.h....\,k.u
0010: 5F 19 78 43                                        _.xC
]
]

3. 自己署名付き証明書をエクスポートし、内容を調べます。

% keytool -export -alias duke -keystore keystore -rfc -file duke.cer
Enter keystore password:  <password>
Certificate stored in file <duke.cer>
% cat duke.cer
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEIQzM/DANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJV
UzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEVMBMGA1UEChMMT3Jh
Y2xlLCBJbmMuMRYwFAYDVQQLEw1KYXZhIFNvZnR3YXJlMQ0wCwYDVQQDEwREdWtl
MB4XDTE2MDcyNTA1MDMyN1oXDTE2MDgwMTA1MDMyN1owbDELMAkGA1UEBhMCVVMx
CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlQYWxvIEFsdG8xFTATBgNVBAoTDE9yYWNs
ZSwgSW5jLjEWMBQGA1UECxMNSmF2YSBTb2Z0d2FyZTENMAsGA1UEAxMERHVrZTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ7+Yeu6HDZgWwkGlG4iKH9w
vGKrxXVR57FaFyheMevrgj1ovVnQVFhfdMvjPkjWmpqLg6rfTqU4bKbtoMWV6+Rn
uQrCw2w9xNC93hX9PxRa20UKrSRDKnUSvi1wjlaxfj0KUKuMwbbY9S8x/naYGeTL
lwbHiiMvkoFkP2kzhVgeqHjIwSz4HRN8vWHCwgIDFWX/ZlS+LbvB4TSZkS0ZcQUV
vJWTocOd8RB90W3bkibWkWq166XYGE1Nq1L4WIhrVJwbav6ual69yJsEpVcshVkx
E1WKzJg7dGb03to4agbReb6+aoCUwb2vNUudNWasSrxoEFArVFGD/ZkPT0esfqEC
AwEAAaMhMB8wHQYDVR0OBBYEFH/JlUhCjWiRuh7mXCxr/3VfGXhDMA0GCSqGSIb3
DQEBCwUAA4IBAQAmcTm2ahsIJLayajsvm8yPzQsHA7kIwWfPPHCoHmNbynG67oHB
fleaNvrgm/raTT3TrqQkg0525qI6Cqaoyy8JA2fAp3i+hmyoGHaIlo14bKazaiPS
RCCqk0J8vwY3CY9nVal1XlHJMEcYV7X1sxKbuAKFoAJ29E/p6ie0JdHtQe31M7X9
FNLYzt8EpJYUtWo13B9Oufz/Guuex9PQ7aC93rbO32MxtnnCGMxQHlaHLLPygc/x
cffGz5Xe5s+NEm78CY7thgN+drI7icBYmv4navsnr2OQaD3AfnJ4WYSQyyUUCPxN
zuk+B0fbLn7PCCcQspmqfgzIpgbEM9M1/yav
-----END CERTIFICATE-----

また、-certreqによって証明書署名要求(CSR)を生成し、証明書発行局(CA)に送付して署名を求めることもできますが、それはこの例の範囲を超えています。

4. 証明書を新しいトラスト・ストアにインポートします。

% keytool -import -alias dukecert -file duke.cer -keystore truststore
Enter keystore password:  <password>
Re-enter new password:
Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US
Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US
Serial number: 210cccfc
Valid from: Mon Jul 25 10:33:27 IST 2016 until: Mon Aug 01 10:33:27 IST 2016
Certificate fingerprints:
SHA1: 80:E5:8A:47:7E:4F:5A:70:83:97:DD:F4:DA:29:3D:15:6B:2A:45:1F
SHA256: ED:3C:70:68:4E:86:35:9C:63:CC:B9:59:35:58:94:1F:7E:B8:B0:EE:D2:
4B:9D:80:31:67:8A:D4:B4:7A:B5:12
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: RSA (2048)
Version: 3

Extensions:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 7F C9 95 48 42 8D 68 91   BA 1E E6 5C 2C 6B FF 75  ...HB.h....\,k.u
0010: 5F 19 78 43                                        _.xC
]
]

Trust this certificate? [no]:  yes
Certificate was added to keystore

5. トラストストアを調べます。エントリの型はtrustedCertEntryで、これは、秘密鍵をこのエントリで使用できないことを示します。これは、このファイルがKeyManagerのキーストアとして適切でないことも意味します。

% keytool -list -v -keystore truststore
Enter keystore password:  <password>

Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: dukecert
Creation date: Jul 25, 2016
Entry type: trustedCertEntry

Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US
Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US
Serial number: 210cccfc
Valid from: Mon Jul 25 10:33:27 IST 2016 until: Mon Aug 01 10:33:27 IST 2016
Certificate fingerprints:
SHA1: 80:E5:8A:47:7E:4F:5A:70:83:97:DD:F4:DA:29:3D:15:6B:2A:45:1F
SHA256: ED:3C:70:68:4E:86:35:9C:63:CC:B9:59:35:58:94:1F:7E:B8:B0:EE:D2:
4B:9D:80:31:67:8A:D4:B4:7A:B5:12
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: RSA (2048)
Version: 3

Extensions:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 7F C9 95 48 42 8D 68 91   BA 1E E6 5C 2C 6B FF 75  ...HB.h....\,k.u
0010: 5F 19 78 43                                        _.xC
]
]

*******************************************
*******************************************

6. ここで、適切なキーストアを使用してアプリケーションを実行します。この例では、デフォルトのX509KeyManagerおよびX509TrustManagerを使用することを前提とするため、「JSSEのカスタマイズ」で説明したシステム・プロパティを使用してキーストアを選択します。

% java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password Server

% java -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword Client

注意:
この例で認証されるのは、サーバーのみです。クライアントの認証の場合、クライアントの鍵に対して同様のキーストアを用意し、サーバーに対して適切なトラストストアを提供する必要があります。

Server Name Indication (SNI)拡張の使用

これらの例では、クライアント側アプリケーションとサーバー側アプリケーションでServer Name Indication (SNI)拡張を使用する方法およびそれを仮想インフラストラクチャに適用する方法を示します。

このセクションのすべての例で、パラメータの設定後にそれらを適用するには、対応するSSLSocket、SSLEngineまたはSSLServerSocketオブジェクトで、setSSLParameters(SSLParameters)メソッドを呼び出します。

一般的なクライアント側使用例

次は、クライアント・アプリケーションを開発するためにSNI拡張の理解を必要とするユース・ケースのリストです。

- ケース1.クライアントはwww.example.comにアクセスする必要があります。

ホスト名を明示的に設定します。

SNIHostName serverName = new SNIHostName("www.example.com");
sslParameters.setServerNames(Collections.singletonList(serverName));

クライアントは常にホスト名を明示的に指定してください。

- ケース2.サーバーでサポートしていないため、クライアントはSNIを使用する必要はありません。

空のサーバー名リストでSNIを無効にします。

sslParameters.setServerNames(Collections.emptyList());

- ケース3.クライアントはURL https://www.example.comにアクセスする必要があります。

OracleプロバイダはデフォルトでSNI拡張にホスト名を設定しますが、サード・パーティ・プロバイダがデフォルトのサーバー名表示をサポートしていないことがあります。アプリケーションでプロバイダに依存しないようにするには、常にホスト名を明示的に設定します。

- ケース4.クライアントはソケットをサーバー・モードからクライアント・モードに切り替える必要があります。

まず、メソッドsslSocket.setUseClientMode(true)でモードを切り替えます。次に、ソケットでサーバー名表示パラメータをリセットします。

一般的なサーバー側使用例

次は、サーバー・アプリケーションを開発するためにSNI拡張の理解を必要とするユース・ケースのリストです。

- ケース1.サーバーはすべてのサーバー名表示タイプを受け付ける必要があります。

SNI拡張を処理するコードがない場合、サーバーはすべてのサーバー名表示型を無視します。

- ケース2.サーバーは型 host_nameのすべてのサーバー名表示型を拒否する必要があります。

host_nameに無効なサーバー名パターンを設定します。

SNIMatcher matcher = SNIHostName.createSNIMatcher("");
Collection<SNIMatcher> matchers = new ArrayList<>(1);
matchers.add(matcher);
sslParameters.setSNIMatchers(matchers);

他の方法は、matches()メソッドで、常にfalseを返すSNIMatcherサブクラスを作成することです。

class DenialSNIMatcher extends SNIMatcher {
DenialSNIMatcher() {
super(StandardConstants.SNI_HOST_NAME);
}

@Override
public boolean matches(SNIServerName serverName) {
return false;
}
}

SNIMatcher matcher = new DenialSNIMatcher();
Collection<SNIMatcher> matchers = new ArrayList<>(1);
matchers.add(matcher);
sslParameters.setSNIMatchers(matchers);

- ケース3.サーバーはexample.comドメイン内のすべてのホスト名への接続を受け付ける必要があります。

すべての*.example.comアドレスを含むパターンとして、host_nameの認識可能なサーバー名を設定します。

SNIMatcher matcher = SNIHostName.createSNIMatcher("(.*\\.)*example\\.com");
Collection<SNIMatcher> matchers = new ArrayList<>(1);
matchers.add(matcher);
sslParameters.setSNIMatchers(matchers);

- ケース4.サーバーはソケットをクライアント・モードからサーバー・モードに切り替える必要があります。

まず、メソッドsslSocket.setUseClientMode(false)でモードを切り替えます。次に、ソケットでサーバー名表示パラメータをリセットします。

仮想インフラストラクチャの操作

このセクションでは、仮想インフラストラクチャ内からServer Name Indication (SNI)拡張を使用する方法について説明します。ここでは、ソケットからのClientHelloメッセージのパーサーの作成方法を示し、SSLSocketおよびSSLEngineを使用した仮想サーバー・ディスパッチャの例を示し、SNI拡張を使用できない場合に何が発生するかを説明して、フェールオーバーSSLContextを作成する方法を示します。

ClientHelloパーサーの準備

アプリケーションはソケットからのClientHelloメッセージを解析するAPIを実装する必要があります。次の例に、これらの機能を実行できるSSLCapabilitiesクラスとSSLExplorerクラスを示します。

SSLSocketClient.javaはハンドシェーク中にSSL/TLS/DTLSセキュリティ機能をカプセル化します(つまり、SSL/TLS/DTLSハンドシェークで受け付けられる暗号化方式群のリスト、レコード・バージョン、helloバージョンおよびサーバー名表示)。それは、SSLExplorer.explore()メソッドで、SSL/TLS/DTLS接続のネットワーク・データを調べることによって取得できます。

SSLExplorer.javaはTLSクライアントからの初期ClientHelloメッセージを調査しますが、ハンドシェークを開始したり、ネットワーク・データを消費したりしません。SSLExplorer.explore()メソッドはClientHelloメッセージを解析し、SSLCapabilitiesにセキュリティ・パラメータを取得します。このメソッドは、TLS接続でハンドシェークが行われる前に呼び出す必要があります。

SSLSocketに基づいた仮想サーバー・ディスパッチャ

このセクションでは、SSLSocketに基づいて、仮想サーバー・ディスパッチャを使用する手順を説明します。

1. サーバー名ハンドラを登録します。

このステップで、アプリケーションは様々なサーバー名表示の様々なSSLContextオブジェクトを作成したり、特定のサーバー名表示を指定した仮想マシンまたは分散システムにリンクしたりできます。

たとえば、サーバー名がwww.example.orgの場合、登録されたサーバー名ハンドラはローカル仮想ホスティングWebサービス用などになります。ローカル仮想ホスティングWebサービスは指定したSSLContextを使用します。サーバー名がwww.example.comの場合、登録されたサーバー名ハンドラは10.0.0.36でホストしている仮想マシン用などになります。ハンドラはこの接続を仮想マシンにマッピングできます。

2. ServerSocketを作成し、新しい接続を受け付けます。

ServerSocket serverSocket = new ServerSocket(serverPort);
Socket socket = serverSocket.accept();

3. ソケット入力ストリームからバイトを読み取り、バッファして、バッファ済のバイトを調査します。

InputStream ins = socket.getInputStream();
byte[] buffer = new byte[0xFF];
int position = 0;
SSLCapabilities capabilities = null;

// Read the header of TLS record
while (position < SSLExplorer.RECORD_HEADER_SIZE) {
int count = SSLExplorer.RECORD_HEADER_SIZE - position;
int n = ins.read(buffer, position, count);
if (n < 0) {
throw new Exception("unexpected end of stream!");
}
position += n;
}

// Get the required size to explore the SSL capabilities
int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
if (buffer.length < recordLength) {
buffer = Arrays.copyOf(buffer, recordLength);
}

while (position < recordLength) {
int count = recordLength - position;
int n = ins.read(buffer, position, count);
if (n < 0) {
throw new Exception("unexpected end of stream!");
}
position += n;
}

// Explore
capabilities = SSLExplorer.explore(buffer, 0, recordLength);
if (capabilities != null) {
System.out.println("Record version: " + capabilities.getRecordVersion());
System.out.println("Hello version: " + capabilities.getHelloVersion());
}

4. 調査済の機能から要求されたサーバー名を取得します。

List<SNIServerName> serverNames = capabilities.getServerNames();

5. このサーバー名表示の登録済のサーバー名ハンドラを検索します。

ホスト名のサービスが仮想マシンまたは他の分散システムに存在する場合、アプリケーションは接続を転送先に転送する必要があります。アプリケーションは、ソケット・ストリームからのSSLアプリケーションよりも、rawインターネット・データを読取りおよび書込みする必要があります。

Socket destinationSocket = new Socket(serverName, 443);
// Forward buffered bytes and network data from the current socket to the destinationSocket.

ホスト名のサービスが同じプロセスに存在し、ホスト名サービスでSSLSocketを直接使用できる場合、アプリケーションはSSLSocketインスタンスをサーバーに設定する必要があります。

// Get service context from registered handler
// or create the context
SSLContext serviceContext = ...

SSLSocketFactory serviceSocketFac = serviceContext.getSSLSocketFactory();

// wrap the buffered bytes
ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, position);
SSLSocket serviceSocket = (SSLSocket)serviceSocketFac.createSocket(socket, bais, true);

// Now the service can use serviceSocket as usual.

SSLEngineに基づいた仮想サーバー・ディスパッチャ

このセクションでは、SSLEngineに基づいて、仮想サーバー・ディスパッチャを使用する手順を説明します。

1. サーバー名ハンドラを登録します。

このステップで、アプリケーションは様々なサーバー名表示の様々なSSLContextオブジェクトを作成したり、特定のサーバー名表示を指定した仮想マシンまたは分散システムにリンクしたりできます。

たとえば、サーバー名がwww.example.orgの場合、登録されたサーバー名ハンドラはローカル仮想ホスティングWebサービス用などになります。ローカル仮想ホスティングWebサービスは指定したSSLContextを使用します。サーバー名がwww.example.comの場合、登録されたサーバー名ハンドラは10.0.0.36でホストしている仮想マシン用などになります。ハンドラはこの接続を仮想マシンにマッピングできます。

2. ServerSocketまたはServerSocketChannelを作成し、新しい接続を受け付けます。

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(...);
...
SocketChannel socketChannel = serverSocketChannel.accept();

3. ソケット入力ストリームからバイトを読み取り、バッファして、バッファ済のバイトを調査します。

ByteBuffer buffer = ByteBuffer.allocate(0xFF);
SSLCapabilities capabilities = null;
while (true) {
// ensure the capacity
if (buffer.remaining() == 0) {
ByteBuffer oldBuffer = buffer;
buffer = ByteBuffer.allocate(buffer.capacity() + 0xFF);
buffer.put(oldBuffer);
}

int n = sc.read(buffer);
if (n < 0) {
throw new Exception("unexpected end of stream!");
}

int position = buffer.position();
buffer.flip();
capabilities = explorer.explore(buffer);
buffer.rewind();
buffer.position(position);
buffer.limit(buffer.capacity());
if (capabilities != null) {
System.out.println("Record version: " +
capabilities.getRecordVersion());
System.out.println("Hello version: " +
capabilities.getHelloVersion());
break;
}
}

buffer.flip();  // reset the buffer position and limitation

4. 調査済の機能から要求されたサーバー名を取得します。

List<SNIServerName> serverNames = capabilities.getServerNames();

5. このサーバー名表示の登録済のサーバー名ハンドラを検索します。

ホスト名のサービスが仮想マシンまたは他の分散システムに存在する場合、アプリケーションは接続を転送先に転送する必要があります。アプリケーションは、ソケット・ストリームからのSSLアプリケーションよりも、rawインターネット・データを読取りおよび書込みする必要があります。

Socket destinationSocket = new Socket(serverName, 443);
// Forward buffered bytes and network data from the current socket to the destinationSocket.

ホスト名のサービスが同じプロセスに存在し、ホスト名サービスでSSLEngineを直接使用できる場合、アプリケーションは単にネット・データをSSLEngineインスタンスに提供します。

// Get service context from registered handler
// or create the context
SSLContext serviceContext = ...

SSLEngine serviceEngine = serviceContext.createSSLEngine();
// Now the service can use the buffered bytes and other byte buffer as usual.

使用可能なSNI拡張がない

ClientHelloメッセージにサーバー名表示がない場合、SNIに従って正しいサービスを選択する方法がありません。そのような場合、アプリケーションは、サーバー名表示がない場合に、接続をそれに委譲できるように、デフォルトのサービスを指定する必要がある場合があります。

フェイルオーバーSSLContext

SSLExplorer.explore()メソッドはSSL/TLS/DTLSの内容の妥当性をチェックしません。レコード形式がSSL/TLS/DTLS仕様に準拠していない場合、またはハンドシェークの起動後に、explore()メソッドが呼び出された場合、メソッドはIOExceptionをスローすることがあり、ネットワーク・データを生成できません。そのような場合、SSL/TLS/DTLS接続のネゴシエーションに使用されないが適切な警告メッセージで接続を閉じるフェイルオーバーSSLContextを使用することで、SSLExplorer.explore()によってスローされた例外を処理します。次の例はフェールオーバーSSLContextを示しています。「一般的なサーバー側使用例」の「ケース2」で、DenialSNIMatcherクラスの例を参照できます。

byte[] buffer = ...       // buffered network data
boolean failed = true;    // SSLExplorer.explore() throws an exception

SSLContext context = SSLContext.getInstance("TLS");
// the failover SSLContext

context.init(null, null, null);
SSLSocketFactory sslsf = context.getSocketFactory();
ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, position);
SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);

SNIMatcher matcher = new DenialSNIMatcher();
Collection<SNIMatcher> matchers = new ArrayList<>(1);
matchers.add(matcher);
SSLParameters params = sslSocket.getSSLParameters();
params.setSNIMatchers(matchers);    // no recognizable server name
sslSocket.setSSLParameters(params);

try {
InputStream sslIS = sslSocket.getInputStream();
sslIS.read();
} catch (Exception e) {
System.out.println("Server exception " + e);
} finally {
sslSocket.close();
}

標準名

JDK Security APIは、アルゴリズム、証明書およびキーストアのタイプの一連の標準名を必要とし、これらを使用します。Javaセキュリティ標準アルゴリズム名を参照してください。特定のプロバイダ情報は、JDKプロバイダ・ドキュメントで参照できます。

プロバイダのプラグイン可能性

JSSEは完全にプラガブルであり、サード・パーティのJSSEプロバイダの使用には何の制限もありません。

JSSE暗号化方式群のパラメータ

表8-14には、JSSE暗号化方式群名に関連するその他のパラメータのリストが示されています。Javaセキュリティ標準アルゴリズム名を参照してください。

表8-14 JSSE暗号化方式群のパラメータ

標準名(異なる場合は、IANA名)

鍵交換アルゴリズム

バルクCipherアルゴリズム

メッセージ認証コード・アルゴリズム

SSL_NULL_WITH_NULL_NULL

IANA: TLS_NULL_WITH_NULL_NULL

K_NULL

B_NULL

M_NULL

SSL_RSA_WITH_NULL_MD5

IANA: TLS_RSA_WITH_NULL_MD5

RSA

B_NULL

MD5

SSL_RSA_WITH_NULL_SHA

IANA: TLS_RSA_WITH_NULL_SHA

RSA

B_NULL

SHA-1

SSL_RSA_EXPORT_WITH_RC4_40_MD5

IANA: TLS_RSA_EXPORT_WITH_RC4_40_MD5

RSA_EXPORT

RC4_40

MD5

SSL_RSA_WITH_RC4_128_MD5

IANA: TLS_RSA_WITH_RC4_128_MD5

RSA

RC4

MD5

SSL_RSA_WITH_RC4_128_SHA

IANA: TLS_RSA_WITH_RC4_128_SHA

RSA

RC4

SHA-1

SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5

IANA: TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5

RSA_EXPORT

RC2_CBC_40

MD5

SSL_RSA_WITH_IDEA_CBC_SHA

IANA: TLS_RSA_WITH_IDEA_CBC_SHA

RSA

IDEA_CBC

SHA-1

SSL_RSA_EXPORT_WITH_DES40_CBC_SHA

IANA: TLS_RSA_EXPORT_WITH_DES40_CBC_SHA

RSA_EXPORT

DES40_CBC

SHA-1

SSL_RSA_WITH_DES_CBC_SHA

IANA: TLS_RSA_WITH_DES_CBC_SHA

RSA

DES_CBC

SHA-1

SSL_RSA_WITH_3DES_EDE_CBC_SHA

IANA: TLS_RSA_WITH_3DES_EDE_CBC_SHA

RSA

3DES_EDE_CBC

SHA-1

SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA

IANA: TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA

DH_DSS

DES40_CBC

SHA-1

SSL_DH_DSS_WITH_DES_CBC_SHA

IANA: TLS_DH_DSS_WITH_DES_CBC_SHA

DH_DSS

DES_CBC

SHA-1

SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA

IANA: TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA

DH_DSS

3DES_EDE_CBC

SHA-1

SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA

IANA: TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA

DH_RSA_EXPORT

DES40_CBC

SHA-1

SSL_DH_RSA_WITH_DES_CBC_SHA

IANA: TLS_DH_RSA_WITH_DES_CBC_SHA

DH_RSA

DES_CBC

SHA-1

SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA

IANA: TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA

DH_RSA

3DES_EDE_CBC

SHA-1

SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA

IANA: TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA

DHE_DSS_EXPORT

DES40_CBC

SHA-1

SSL_DHE_DSS_WITH_DES_CBC_SHA

IANA: TLS_DHE_DSS_WITH_DES_CBC_SHA

DHE_DSS

DES_CBC

SHA-1

SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA

IANA: TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA

DHE_DSS

3DES_EDE_CBC

SHA-1

SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA

IANA: TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA

DHE_RSA_EXPORT

DES40_CBC

SHA-1

SSL_DHE_RSA_WITH_DES_CBC_SHA

IANA: TLS_DHE_RSA_WITH_DES_CBC_SHA

DHE_RSA

DES_CBC

SHA-1

SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA

IANA: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA

DHE_RSA

3DES_EDE_CBC

SHA-1

SSL_DH_anon_EXPORT_WITH_RC4_40_MD5

IANA: TLS_DH_anon_EXPORT_WITH_RC4_40_MD5

DH_anon_EXPORT

RC4_40

MD5

SSL_DH_anon_WITH_RC4_128_MD5

IANA: TLS_DH_anon_WITH_RC4_128_MD5

DH_anon

RC4

MD5

SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA

IANA: TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA

DH_anon

DES40_CBC

SHA-1

SSL_DH_anon_WITH_DES_CBC_SHA

IANA: TLS_DH_anon_WITH_DES_CBC_SHA

DH_anon

DES_CBC

SHA-1

SSL_DH_anon_WITH_3DES_EDE_CBC_SHA

IANA: TLS_DH_anon_WITH_3DES_EDE_CBC_SHA

DH_anon

3DES_EDE_CBC

SHA-1

TLS_KRB5_WITH_DES_CBC_SHA

KRB5

DES_CBC

SHA-1

TLS_KRB5_WITH_3DES_EDE_CBC_SHA

KRB5

3DES_EDE_CBC

SHA-1

TLS_KRB5_WITH_RC4_128_SHA

KRB5

RC4

SHA-1

TLS_KRB5_WITH_IDEA_CBC_SHA

KRB5

IDEA_CBC

SHA-1

TLS_KRB5_WITH_DES_CBC_MD5

KRB5

DES_CBC

MD5

TLS_KRB5_WITH_3DES_EDE_CBC_MD5

KRB5

3DES_EDE_CBC

MD5

TLS_KRB5_WITH_RC4_128_MD5

KRB5

RC4

MD5

TLS_KRB5_WITH_IDEA_CBC_MD5

KRB5

IDEA_CBC

MD5

TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA

KRB5_EXPORT

DES_CBC

SHA-1

TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA

KRB5_EXPORT

RC2_CBC_40

SHA-1

TLS_KRB5_EXPORT_WITH_RC4_40_SHA

KRB5_EXPORT

RC4_40

SHA-1

TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5

KRB5_EXPORT

DES_CBC

MD5

TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5

KRB5_EXPORT

RC2_CBC_40

MD5

TLS_KRB5_EXPORT_WITH_RC4_40_MD5

KRB5_EXPORT

RC4_40

MD5

TLS_PSK_WITH_NULL_SHA

PSK

B_NULL

SHA-1

TLS_DHE_PSK_WITH_NULL_SHA

DHE_PSK

B_NULL

SHA-1

TLS_RSA_PSK_WITH_NULL_SHA

RSA_PSK

B_NULL

SHA-1

TLS_RSA_WITH_AES_128_CBC_SHA

RSA

AES_128_CBC

SHA-1

TLS_DH_DSS_WITH_AES_128_CBC_SHA

DH_DSS

AES_128_CBC

SHA-1

TLS_DH_RSA_WITH_AES_128_CBC_SHA

DH_RSA

AES_128_CBC

SHA-1

TLS_DHE_DSS_WITH_AES_128_CBC_SHA

DHE_DSS

AES_128_CBC

SHA-1

TLS_DHE_RSA_WITH_AES_128_CBC_SHA

DHE_RSA

AES_128_CBC

SHA-1

TLS_DH_anon_WITH_AES_128_CBC_SHA

DH_anon

AES_128_CBC

SHA-1

TLS_RSA_WITH_AES_256_CBC_SHA

RSA

AES_256_CBC

SHA-1

TLS_DH_DSS_WITH_AES_256_CBC_SHA

DH_DSS

AES_256_CBC

SHA-1

TLS_DH_RSA_WITH_AES_256_CBC_SHA

DH_RSA

AES_256_CBC

SHA-1

TLS_DHE_DSS_WITH_AES_256_CBC_SHA

DHE_DSS

AES_256_CBC

SHA-1

TLS_DHE_RSA_WITH_AES_256_CBC_SHA

DHE_RSA

AES_256_CBC

SHA-1

TLS_DH_anon_WITH_AES_256_CBC_SHA

DH_anon

AES_256_CBC

SHA-1

TLS_RSA_WITH_NULL_SHA256

RSA

B_NULL

SHA-1

TLS_RSA_WITH_AES_128_CBC_SHA256

RSA

AES_128_CBC

SHA-256

TLS_RSA_WITH_AES_256_CBC_SHA256

RSA

AES_256_CBC

SHA-256

TLS_DH_DSS_WITH_AES_128_CBC_SHA256

DH_DSS

AES_128_CBC

SHA-256

TLS_DH_RSA_WITH_AES_128_CBC_SHA256

DH_RSA

AES_128_CBC

SHA-256

TLS_DHE_DSS_WITH_AES_128_CBC_SHA256

DHE_DSS

AES_128_CBC

SHA-256

TLS_RSA_WITH_CAMELLIA_128_CBC_SHA

RSA

CAMELLIA_128_CBC

SHA-1

TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA

DH_DSS

CAMELLIA_128_CBC

SHA-1

TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA

DH_RSA

CAMELLIA_128_CBC

SHA-1

TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA

DHE_DSS

CAMELLIA_128_CBC

SHA-1

TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA

DHE_RSA

CAMELLIA_128_CBC

SHA-1

TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA

DH_anon

CAMELLIA_128_CBC

SHA-1

TLS_DHE_RSA_WITH_AES_128_CBC_SHA256

DHE_RSA

AES_128_CBC

SHA-256

TLS_DH_DSS_WITH_AES_256_CBC_SHA256

DH_DSS

AES_256_CBC

SHA-256

TLS_DH_RSA_WITH_AES_256_CBC_SHA256

DH_RSA

AES_256_CBC

SHA-256

TLS_DHE_DSS_WITH_AES_256_CBC_SHA256

DHE_DSS

AES_256_CBC

SHA-256

TLS_DHE_RSA_WITH_AES_256_CBC_SHA256

DHE_RSA

AES_256_CBC

SHA-256

TLS_DH_anon_WITH_AES_128_CBC_SHA256

DH_anon

AES_128_CBC

SHA-256

TLS_DH_anon_WITH_AES_256_CBC_SHA256

DH_anon

AES_256_CBC

SHA-256

TLS_RSA_WITH_CAMELLIA_256_CBC_SHA

RSA

CAMELLIA_256_CBC

SHA-1

TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA

DH_DSS

CAMELLIA_256_CBC

SHA-1

TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA

DH_RSA

CAMELLIA_256_CBC

SHA-1

TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA

DHE_DSS

CAMELLIA_256_CBC

SHA-1

TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA

DHE_RSA

CAMELLIA_256_CBC

SHA-1

TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA

DH_anon

CAMELLIA_256_CBC

SHA-1

TLS_PSK_WITH_RC4_128_SHA

PSK

RC4

SHA-1

TLS_PSK_WITH_3DES_EDE_CBC_SHA

PSK

3DES_EDE_CBC

SHA-1

TLS_PSK_WITH_AES_128_CBC_SHA

PSK

AES_128_CBC

SHA-1

TLS_PSK_WITH_AES_256_CBC_SHA

PSK

AES_256_CBC

SHA-1

TLS_DHE_PSK_WITH_RC4_128_SHA

DHE_PSK

RC4

SHA-1

TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA

DHE_PSK

3DES_EDE_CBC

SHA-1

TLS_DHE_PSK_WITH_AES_128_CBC_SHA

DHE_PSK

AES_128_CBC

SHA-1

TLS_DHE_PSK_WITH_AES_256_CBC_SHA

DHE_PSK

AES_256_CBC

SHA-1

TLS_RSA_PSK_WITH_RC4_128_SHA

RSA_PSK

RC4

SHA-1

TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA

RSA_PSK

3DES_EDE_CBC

SHA-1

TLS_RSA_PSK_WITH_AES_128_CBC_SHA

RSA_PSK

AES_128_CBC

SHA-1

TLS_RSA_PSK_WITH_AES_256_CBC_SHA

RSA_PSK

AES_256_CBC

SHA-1

TLS_RSA_WITH_SEED_CBC_SHA

RSA

SEED_CBC

SHA-1

TLS_DH_DSS_WITH_SEED_CBC_SHA

DH_DSS

SEED_CBC

SHA-1

TLS_DH_RSA_WITH_SEED_CBC_SHA

DH_RSA

SEED_CBC

SHA-1

TLS_DHE_DSS_WITH_SEED_CBC_SHA

DHE_DSS

SEED_CBC

SHA-1

TLS_DHE_RSA_WITH_SEED_CBC_SHA

DHE_RSA

SEED_CBC

SHA-1

TLS_DH_anon_WITH_SEED_CBC_SHA

DH_anon

SEED_CBC

SHA-1

TLS_RSA_WITH_AES_128_GCM_SHA256

RSA

AES_128_GCM

SHA-256

TLS_RSA_WITH_AES_256_GCM_SHA384

RSA

AES_256_GCM

SHA-384

TLS_DHE_RSA_WITH_AES_128_GCM_SHA256

DHE_RSA

AES_128_GCM

SHA-256

TLS_DHE_RSA_WITH_AES_256_GCM_SHA384

DHE_RSA

AES_256_GCM

SHA-384

TLS_DH_RSA_WITH_AES_128_GCM_SHA256

DH_RSA

AES_128_GCM

SHA-256

TLS_DH_RSA_WITH_AES_256_GCM_SHA384

DH_RSA

AES_256_GCM

SHA-384

TLS_DHE_DSS_WITH_AES_128_GCM_SHA256

DHE_DSS

AES_128_GCM

SHA-256

TLS_DHE_DSS_WITH_AES_256_GCM_SHA384

DHE_DSS

AES_256_GCM

SHA-384

TLS_DH_DSS_WITH_AES_128_GCM_SHA256

DH_DSS

AES_128_GCM

SHA-256

TLS_DH_DSS_WITH_AES_256_GCM_SHA384

DH_DSS

AES_256_GCM

SHA-384

TLS_DH_anon_WITH_AES_128_GCM_SHA256

DH_anon

AES_128_GCM

SHA-256

TLS_DH_anon_WITH_AES_256_GCM_SHA384

DH_anon

AES_256_GCM

SHA-384

TLS_PSK_WITH_AES_128_GCM_SHA256

PSK

AES_128_GCM

SHA-256

TLS_PSK_WITH_AES_256_GCM_SHA384

PSK

AES_256_GCM

SHA-384

TLS_DHE_PSK_WITH_AES_128_GCM_SHA256

DHE_PSK

AES_128_GCM

SHA-256

TLS_DHE_PSK_WITH_AES_256_GCM_SHA384

DHE_PSK

AES_256_GCM

SHA-384

TLS_RSA_PSK_WITH_AES_128_GCM_SHA256

RSA_PSK

AES_128_GCM

SHA-256

TLS_RSA_PSK_WITH_AES_256_GCM_SHA384

RSA_PSK

AES_256_GCM

SHA-384

TLS_PSK_WITH_AES_128_CBC_SHA256

PSK

AES_128_CBC

SHA-256

TLS_PSK_WITH_AES_256_CBC_SHA384

PSK

AES_256_CBC

SHA-384

TLS_PSK_WITH_NULL_SHA256

PSK

B_NULL

SHA-256

TLS_PSK_WITH_NULL_SHA384

PSK

B_NULL

SHA-384

TLS_DHE_PSK_WITH_AES_128_CBC_SHA256

DHE_PSK

AES_128_CBC

SHA-256

TLS_DHE_PSK_WITH_AES_256_CBC_SHA384

DHE_PSK

AES_256_CBC

SHA-384

TLS_DHE_PSK_WITH_NULL_SHA256

DHE_PSK

B_NULL

SHA-256

TLS_DHE_PSK_WITH_NULL_SHA384

DHE_PSK

B_NULL

SHA-384

TLS_RSA_PSK_WITH_AES_128_CBC_SHA256

RSA_PSK

AES_128_CBC

SHA-256

TLS_RSA_PSK_WITH_AES_256_CBC_SHA384

RSA_PSK

AES_256_CBC

SHA-384

TLS_RSA_PSK_WITH_NULL_SHA256

RSA_PSK

B_NULL

SHA-256

TLS_RSA_PSK_WITH_NULL_SHA384

RSA_PSK

B_NULL

SHA-384

TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256

RSA

CAMELLIA_128_CBC

SHA-256

TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256

DH_DSS

CAMELLIA_128_CBC

SHA-256

TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256

DH_RSA

CAMELLIA_128_CBC

SHA-256

TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256

DHE_DSS

CAMELLIA_128_CBC

SHA-256

TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256

DHE_RSA

CAMELLIA_128_CBC

SHA-256

TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256

DH_anon

CAMELLIA_128_CBC

SHA-256

TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256

RSA

CAMELLIA_256_CBC

SHA-256

TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256

DH_DSS

CAMELLIA_256_CBC

SHA-256

TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256

DH_RSA

CAMELLIA_256_CBC

SHA-256

TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256

DHE_DSS

CAMELLIA_256_CBC

SHA-256

TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256

DHE_RSA

CAMELLIA_256_CBC

SHA-256

TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256

DH_anon

CAMELLIA_256_CBC

SHA-256

TLS_EMPTY_RENEGOTIATION_INFO_SCSV

該当なし

該当なし

該当なし

TLS_FALLBACK_SCSV

該当なし

該当なし

該当なし

TLS_ECDH_ECDSA_WITH_NULL_SHA

ECDH_ECDSA

B_NULL

SHA-1

TLS_ECDH_ECDSA_WITH_RC4_128_SHA

ECDH_ECDSA

RC4

SHA-1

TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA

ECDH_ECDSA

3DES_EDE_CBC

SHA-1

TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA

ECDH_ECDSA

AES_128_CBC

SHA-1

TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA

ECDH_ECDSA

AES_256_CBC

SHA-1

TLS_ECDHE_ECDSA_WITH_NULL_SHA

ECDHE_ECDSA

B_NULL

SHA-1

TLS_ECDHE_ECDSA_WITH_RC4_128_SHA

ECDHE_ECDSA

RC4

SHA-1

TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA

ECDHE_ECDSA

3DES_EDE_CBC

SHA-1

TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA

ECDHE_ECDSA

AES_128_CBC

SHA-1

TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA

ECDHE_ECDSA

AES_256_CBC

SHA-1

TLS_ECDH_RSA_WITH_NULL_SHA

ECDH_RSA

B_NULL

SHA-1

TLS_ECDH_RSA_WITH_RC4_128_SHA

ECDH_RSA

RC4

SHA-1

TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA

ECDH_RSA

3DES_EDE_CBC

SHA-1

TLS_ECDH_RSA_WITH_AES_128_CBC_SHA

ECDH_RSA

AES_128_CBC

SHA-1

TLS_ECDH_RSA_WITH_AES_256_CBC_SHA

ECDH_RSA

AES_256_CBC

SHA-1

TLS_ECDHE_RSA_WITH_NULL_SHA

ECDHE_RSA

B_NULL

SHA-1

TLS_ECDHE_RSA_WITH_RC4_128_SHA

ECDHE_RSA

RC4

SHA-1

TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA

ECDHE_RSA

3DES_EDE_CBC

SHA-1

TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

ECDHE_RSA

AES_128_CBC

SHA-1

TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA

ECDHE_RSA

AES_256_CBC

SHA-1

TLS_ECDH_anon_WITH_NULL_SHA

ECDH_anon

B_NULL

SHA-1

TLS_ECDH_anon_WITH_RC4_128_SHA

ECDH_anon

RC4

SHA-1

TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA

ECDH_anon

3DES_EDE_CBC

SHA-1

TLS_ECDH_anon_WITH_AES_128_CBC_SHA

ECDH_anon

AES_128_CBC

SHA-1

TLS_ECDH_anon_WITH_AES_256_CBC_SHA

ECDH_anon

AES_256_CBC

SHA-1

TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA

SRP_SHA

3DES_EDE_CBC

SHA-1

TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA

SRP_SHA

3DES_EDE_CBC

SHA-1

TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA

SRP_SHA

3DES_EDE_CBC

SHA-1

TLS_SRP_SHA_WITH_AES_128_CBC_SHA

SRP_SHA

AES_128_CBC

SHA-1

TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA

SRP_SHA

AES_128_CBC

SHA-1

TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA

SRP_SHA

AES_128_CBC

SHA-1

TLS_SRP_SHA_WITH_AES_256_CBC_SHA

SRP_SHA

AES_256_CBC

SHA-1

TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA

SRP_SHA

AES_256_CBC

SHA-1

TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA

SRP_SHA

AES_256_CBC

SHA-1

TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256

ECDHE_ECDSA

AES_128_CBC

SHA-256

TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384

ECDHE_ECDSA

AES_256_CBC

SHA-384

TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256

ECDH_ECDSA

AES_128_CBC

SHA-256

TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384

ECDH_ECDSA

AES_256_CBC

SHA-384

TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256

ECDHE_RSA

AES_128_CBC

SHA-256

TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384

ECDHE_RSA

AES_256_CBC

SHA-384

TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256

ECDH_RSA

AES_128_CBC

SHA-256

TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384

ECDH_RSA

AES_256_CBC

SHA-384

TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256

ECDHE_ECDSA

AES_128_GCM

SHA-256

TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384

ECDHE_ECDSA

AES_256_GCM

SHA-384

TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256

ECDH_ECDSA

AES_128_GCM

SHA-256

TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384

ECDH_ECDSA

AES_256_GCM

SHA-384

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

ECDHE_RSA

AES_128_GCM

SHA-256

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

ECDHE_RSA

AES_256_GCM

SHA-384

TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256

ECDH_RSA

AES_128_GCM

SHA-256

TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384

ECDH_RSA

AES_256_GCM

SHA-384

TLS_ECDHE_PSK_WITH_RC4_128_SHA

ECDHE_PSK

RC4

SHA-1

TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA

ECDHE_PSK

3DES_EDE_CBC

SHA-1

TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA

ECDHE_PSK

AES_128_CBC

SHA-1

TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA

ECDHE_PSK

AES_256_CBC

SHA-1

TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256

ECDHE_PSK

AES_128_CBC

SHA-256

TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384

ECDHE_PSK

AES_256_CBC

SHA-384

TLS_ECDHE_PSK_WITH_NULL_SHA

ECDHE_PSK

B_NULL

SHA-1

TLS_ECDHE_PSK_WITH_NULL_SHA256

ECDHE_PSK

B_NULL

SHA-256

TLS_ECDHE_PSK_WITH_NULL_SHA384

ECDHE_PSK

B_NULL

SHA-384

TLS_RSA_WITH_ARIA_128_CBC_SHA256

RSA

ARIA_128_CBC

SHA-256

TLS_RSA_WITH_ARIA_256_CBC_SHA384

RSA

ARIA_256_CBC

SHA-384

TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256

DH_DSS

ARIA_128_CBC

SHA-256

TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384

DH_DSS

ARIA_256_CBC

SHA-384

TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256

DH_RSA

ARIA_128_CBC

SHA-256

TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384

DH_RSA

ARIA_256_CBC

SHA-384

TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256

DHE_DSS

ARIA_128_CBC

SHA-256

TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384

DHE_DSS

ARIA_256_CBC

SHA-384

TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256

DHE_RSA

ARIA_128_CBC

SHA-256

TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384

DHE_RSA

ARIA_256_CBC

SHA-384

TLS_DH_anon_WITH_ARIA_128_CBC_SHA256

DH_anon

ARIA_128_CBC

SHA-256

TLS_DH_anon_WITH_ARIA_256_CBC_SHA384

DH_anon

ARIA_256_CBC

SHA-384

TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256

ECDHE_ECDSA

ARIA_128_CBC

SHA-256

TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384

ECDHE_ECDSA

ARIA_256_CBC

SHA-384

TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256

ECDH_ECDSA

ARIA_128_CBC

SHA-256

TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384

ECDH_ECDSA

ARIA_256_CBC

SHA-384

TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256

ECDHE_RSA

ARIA_128_CBC

SHA-256

TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384

ECDHE_RSA

ARIA_256_CBC

SHA-384

TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256

ECDH_RSA

ARIA_128_CBC

SHA-256

TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384

ECDH_RSA

ARIA_256_CBC

SHA-384

TLS_RSA_WITH_ARIA_128_GCM_SHA256

RSA

ARIA_128_GCM

SHA-256

TLS_RSA_WITH_ARIA_256_GCM_SHA384

RSA

ARIA_256_GCM

SHA-384

TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256

DHE_RSA

ARIA_128_GCM

SHA-256

TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384

DHE_RSA

ARIA_256_GCM

SHA-384

TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256

DH_RSA

ARIA_128_GCM

SHA-256

TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384

DH_RSA

ARIA_256_GCM

SHA-384

TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256

DHE_DSS

ARIA_128_GCM

SHA-256

TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384

DHE_DSS

ARIA_256_GCM

SHA-384

TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256

DH_DSS

ARIA_128_GCM

SHA-256

TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384

DH_DSS

ARIA_256_GCM

SHA-384

TLS_DH_anon_WITH_ARIA_128_GCM_SHA256

DH_anon

ARIA_128_GCM

SHA-256

TLS_DH_anon_WITH_ARIA_256_GCM_SHA384

DH_anon

ARIA_256_GCM

SHA-384

TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256

ECDHE_ECDSA

ARIA_128_GCM

SHA-256

TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384

ECDHE_ECDSA

ARIA_256_GCM

SHA-384

TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256

ECDH_ECDSA

ARIA_128_GCM

SHA-256

TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384

ECDH_ECDSA

ARIA_256_GCM

SHA-384

TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256

ECDHE_RSA

ARIA_128_GCM

SHA-256

TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384

ECDHE_RSA

ARIA_256_GCM

SHA-384

TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256

ECDH_RSA

ARIA_128_GCM

SHA-256

TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384

ECDH_RSA

ARIA_256_GCM

SHA-384

TLS_PSK_WITH_ARIA_128_CBC_SHA256

PSK

ARIA_128_CBC

SHA-256

TLS_PSK_WITH_ARIA_256_CBC_SHA384

PSK

ARIA_256_CBC

SHA-384

TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256

DHE_PSK

ARIA_128_CBC

SHA-256

TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384

DHE_PSK

ARIA_256_CBC

SHA-384

TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256

RSA_PSK

ARIA_128_CBC

SHA-256

TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384

RSA_PSK

ARIA_256_CBC

SHA-384

TLS_PSK_WITH_ARIA_128_GCM_SHA256

PSK

ARIA_128_GCM

SHA-256

TLS_PSK_WITH_ARIA_256_GCM_SHA384

PSK

ARIA_256_GCM

SHA-384

TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256

DHE_PSK

ARIA_128_GCM

SHA-256

TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384

DHE_PSK

ARIA_256_GCM

SHA-384

TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256

RSA_PSK

ARIA_128_GCM

SHA-256

TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384

RSA_PSK

ARIA_256_GCM

SHA-384

TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256

ECDHE_PSK

ARIA_128_CBC

SHA-256

TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384

ECDHE_PSK

ARIA_256_CBC

SHA-384

TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256

ECDHE_ECDSA

CAMELLIA_128_CBC

SHA-256

TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384

ECDHE_ECDSA

CAMELLIA_256_CBC

SHA-384

TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256

ECDH_ECDSA

CAMELLIA_128_CBC

SHA-256

TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384

ECDH_ECDSA

CAMELLIA_256_CBC

SHA-384

TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256

ECDHE_RSA

CAMELLIA_128_CBC

SHA-256

TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384

ECDHE_RSA

CAMELLIA_256_CBC

SHA-384

TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256

ECDH_RSA

CAMELLIA_128_CBC

SHA-256

TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384

ECDH_RSA

CAMELLIA_256_CBC

SHA-384

TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256

RSA

CAMELLIA_128_GCM

SHA-256

TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384

RSA

CAMELLIA_256_GCM

SHA-384

TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256

DHE_RSA

CAMELLIA_128_GCM

SHA-256

TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384

DHE_RSA

CAMELLIA_256_GCM

SHA-384

TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256

DH_RSA

CAMELLIA_128_GCM

SHA-256

TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384

DH_RSA

CAMELLIA_256_GCM

SHA-384

TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256

DHE_DSS

CAMELLIA_128_GCM

SHA-256

TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384

DHE_DSS

CAMELLIA_256_GCM

SHA-384

TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256

DH_DSS

CAMELLIA_128_GCM

SHA-256

TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384

DH_DSS

CAMELLIA_256_GCM

SHA-384

TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256

DH_anon

CAMELLIA_128_GCM

SHA-256

TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384

DH_anon

CAMELLIA_256_GCM

SHA-384

TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256

ECDHE_ECDSA

CAMELLIA_128_GCM

SHA-256

TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384

ECDHE_ECDSA

CAMELLIA_256_GCM

SHA-384

TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256

ECDH_ECDSA

CAMELLIA_128_GCM

SHA-256

TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384

ECDH_ECDSA

CAMELLIA_256_GCM

SHA-384

TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256

ECDHE_RSA

CAMELLIA_128_GCM

SHA-256

TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384

ECDHE_RSA

CAMELLIA_256_GCM

SHA-384

TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256

ECDH_RSA

CAMELLIA_128_GCM

SHA-256

TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384

ECDH_RSA

CAMELLIA_256_GCM

SHA-384

TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256

PSK

CAMELLIA_128_GCM

SHA-256

TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384

PSK

CAMELLIA_256_GCM

SHA-384

TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256

DHE_PSK

CAMELLIA_128_GCM

SHA-256

TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384

DHE_PSK

CAMELLIA_256_GCM

SHA-384

TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256

RSA_PSK

CAMELLIA_128_GCM

SHA-256

TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384

RSA_PSK

CAMELLIA_256_GCM

SHA-384

TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256

PSK

CAMELLIA_128_CBC

SHA-256

TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384

PSK

CAMELLIA_256_CBC

SHA-384

TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256

DHE_PSK

CAMELLIA_128_CBC

SHA-256

TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384

DHE_PSK

CAMELLIA_256_CBC

SHA-384

TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256

RSA_PSK

CAMELLIA_128_CBC

SHA-256

TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384

RSA_PSK

CAMELLIA_256_CBC

SHA-384

TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256

ECDHE_PSK

CAMELLIA_128_CBC

SHA-256

TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384

ECDHE_PSK

CAMELLIA_256_CBC

SHA-384

TLS_RSA_WITH_AES_128_CCM

RSA

AES_128_CCM

CCM

TLS_RSA_WITH_AES_256_CCM

RSA

AES_256_CCM

CCM

TLS_DHE_RSA_WITH_AES_128_CCM

DHE_RSA

AES_128_CCM

CCM

TLS_DHE_RSA_WITH_AES_256_CCM

DHE_RSA

AES_256_CCM

CCM

TLS_RSA_WITH_AES_128_CCM_8

RSA

AES_128_CCM

CCM_8

TLS_RSA_WITH_AES_256_CCM_8

RSA

AES_256_CCM

CCM_8

TLS_DHE_RSA_WITH_AES_128_CCM_8

DHE_RSA

AES_128_CCM

CCM_8

TLS_DHE_RSA_WITH_AES_256_CCM_8

DHE_RSA

AES_256_CCM

CCM_8

TLS_PSK_WITH_AES_128_CCM

PSK

AES_128_CCM

CCM

TLS_PSK_WITH_AES_256_CCM

PSK

AES_256_CCM

CCM

TLS_DHE_PSK_WITH_AES_128_CCM

DHE_PSK

AES_128_CCM

CCM

TLS_DHE_PSK_WITH_AES_256_CCM

DHE_PSK

AES_256_CCM

CCM

TLS_PSK_WITH_AES_128_CCM_8

PSK

AES_128_CCM

CCM_8

TLS_PSK_WITH_AES_256_CCM_8

PSK

AES_256_CCM

CCM_8

TLS_DHE_PSK_WITH_AES_128_CCM_8

DHE_PSK

AES_128_CCM

CCM_8

TLS_DHE_PSK_WITH_AES_256_CCM_8

DHE_PSK

AES_256_CCM

CCM_8

TLS_ECDHE_ECDSA_WITH_AES_128_CCM

ECDHE_ECDSA

AES_128_CCM

CCM

TLS_ECDHE_ECDSA_WITH_AES_256_CCM

ECDHE_ECDSA

AES_256_CCM

CCM

TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8

ECDHE_ECDSA

AES_128_CCM

CCM_8

TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8

ECDHE_ECDSA

AES_256_CCM

CCM_8

TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256

ECDHE_RSA

AEAD_CHACHA20_POLY1305

SHA-256

TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256

ECDHE_ECDSA

AEAD_CHACHA20_POLY1305

SHA-256

TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256

DHE_RSA

AEAD_CHACHA20_POLY1305

SHA-256

TLS_PSK_WITH_CHACHA20_POLY1305_SHA256

PSK

AEAD_CHACHA20_POLY1305

SHA-256

TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256

ECDHE_PSK

AEAD_CHACHA20_POLY1305

SHA-256

TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256

DHE_PSK

AEAD_CHACHA20_POLY1305

SHA-256

TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256

RSA_PSK

AEAD_CHACHA20_POLY1305

SHA-256

脚注の凡例

脚注4: これらのセキュリティ・プロパティで指定されている制限付きアルゴリズムのリストは、変更される可能性があります。最新の値については、JDKインストールのjava.securityファイルを参照してください。

9 Java PKIプログラマーズ・ガイド

Java Certification Path APIは、証明書パス(証明書チェーンとも呼ばれる)を扱うためのクラスとインタフェースで構成されます。証明書パスは、特定の検証規則を満たす場合に、公開鍵から主体へのマッピングを安全に確立するために使用されます。

トピック

PKIプログラマーズ・ガイドの概要

コア・クラスとインタフェース

サービス・プロバイダの実装

付録A: 標準名

付録B: SUNプロバイダでのCertPath実装

付録C: OCSPサポート

付録D: JdkLDAPプロバイダでのCertPath実装

付録E: 暗号化アルゴリズムの無効化

PKIプログラマーズ・ガイドの概要

Java Certification Path APIは、証明書パスを作成、構築および検証するためのインタフェースと抽象クラスを定義します。 実装は、プロバイダ・ベースのインタフェースを使ってプラグインされます。

このAPIは、Java暗号化アーキテクチャ・リファレンス・ガイドで説明されている暗号化サービス・プロバイダ・アーキテクチャに基づいており、PKIX標準に従ってX.509証明書パスを構築および検証するためのアルゴリズム固有のクラスを含んでいます。PKIX標準は、IETF PKIXワーキング・グループによって開発されています。

このAPIは、最初はJava Community Processプログラムを使用して指定されました(Java Specification Request (JSR) 000055)。このAPIは、Java SE Development Kit (JDK) 1.4からJava SDKに含まれました。JSR 55: Certification Path APIを参照してください。

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

このドキュメントは、次の2つの条件のいずれかに該当する、経験ある開発者を対象にしています。

1. 証明書パスを構築または検証する、セキュリティ保護されたアプリケーションを設計する人

2. 証明書パスを構築または検証するためのサービス・プロバイダ実装を記述する人

このドキュメントは、暗号化サービス・プロバイダをすでに読んでいることが前提となっています。

公開鍵証明書の概要

公開鍵アプリケーションおよびシステムのユーザーは、主体の公開鍵が本物であること、つまり、関連する非公開鍵が主体によって所有されていることを確信している必要があります。公開鍵証明書は、この信頼を確立するのに使用されます。

公開鍵(またはアイデンティティ)証明書は、アイデンティティへの公開鍵のバインディングです。アイデンティティは、別のエンティティ(多くの場合、証明書発行局(CA)と呼ばれる)の非公開鍵でデジタル署名されます。この項の残りの部分では、CAという用語は、証明書に署名するエンティティの意味で使用されます。

ユーザーは、主体の公開鍵証明書に署名したCAの公開鍵の信頼できるコピーを持っていない場合、署名しているCAを保証する別の公開鍵証明書が必要です。この論理は、証明書の連鎖(または証明書パス)が信頼できるアンカーまたは最も信頼できるCAから検出されるまで、再帰的にターゲットの主体(一般にエンド・エンティティと呼ばれる)に適用されます。通常、もっとも信頼できるCAは、ユーザーが直接信頼するCAに宛てて発行した証明書によって指定されます。一般に証明書パスは順序付けされた証明書のリストで、通常の場合、エンド・エンティティの公開鍵証明書と0個以上の付加的な証明書で構成されます。通常、証明書パスには1つ以上のエンコードがあります。これにより、証明書パスは、安全にネットワークを通じて伝送されたり、別のオペレーティング・システム・アーキテクチャへ送信されたりします。

次の図は、もっとも信頼できるCAの公開鍵(CA 1)からターゲットの主体(Alice)への証明書パスを示しています。証明書パスは、CA2という名前の中間CAを介して、Aliceの公開鍵との信頼を確立します。

図9-1 CAの公開鍵(CA 1)からターゲットの主体への証明書パス

証明書パスは、主体の公開鍵の信頼を確立するため、信頼する前に検証する必要があります。検証では、署名を検証したり、各証明書が取り消されていないことをチェックしたりするなど、証明書パスに含まれている証明書に対するさまざまなチェックを行います。PKIX標準は、X.509証明書で構成される証明書パスの検証に関するアルゴリズムを定義します。

ユーザーは、もっとも信頼できるCAから主体への証明書パスを持っていないことがあります。証明書パスを構築または検出するサービスの提供は、公開鍵に対応するシステムの重要な機能です。RFC 2587は、LDAP (Lightweight Directory Access Protocol)スキーマ定義を定義します。LDAPスキーマ定義により、LDAPディレクトリ・サービス・プロトコルを使用したX.509証明書パスの検出が容易になります。

証明書パスの構築および検証は、SSL/TLS/DTLS、S/MIME、IPsecなど、多くの標準セキュリティ・プロトコルの重要な一部です。Java Certification Path APIは、この機能をアプリケーションに統合する必要のある開発者に、クラスおよびインタフェースのセットを提供します。このAPIは、特定の証明書パスの構築、または検証アルゴリズム用にサービス・プロバイダ実装を記述する必要がある開発者、および実装に依存しない方式による証明書パスの作成、構築、検証のために標準アルゴリズムにアクセスする必要のある開発者にとって便利です。

X.509証明書と証明書失効リスト(CRL)

公開鍵証明書とは、あるエンティティが発行したデジタル署名のある文書で、別のエンティティの公開鍵(および他の情報)が特定の値であることを証明するものです。

次の表では、重要な用語をいくつか示します。

公開鍵

公開鍵は、特定のエンティティに関連付けられた数です。公開鍵は、該当するエンティティとの間に信頼できる関係を持つ必要があるすべての人に対して公開することを意図したものです。公開鍵は、署名を検証するのに使われます。

デジタル署名

データがデジタル署名されると、そのデータは、エンティティの「アイデンティティ」と、そのエンティティがデータの内容について知っていることを証明する署名とともに格納されます。エンティティの非公開鍵を使って署名することで、データを偽造できなくなります。

アイデンティティ

エンティティを特定するための既知の方法です。システムによっては、公開鍵をアイデンティティにするものがあります。公開鍵の他にも、UNIX UIDや電子メール・アドレス、X.509識別名など、様々なものをアイデンティティとすることができます。

署名

署名は、エンティティ(署名者)の非公開鍵を使い、あるデータに対して計算されるものです。

非公開鍵

非公開鍵とは、特定のエンティティのみが知っている数のことで、この数のことをそのエンティティの非公開鍵と呼びます。非公開鍵は、他に知られないように秘密にしておくことが前提になっています。非公開鍵と公開鍵は、すべての公開鍵暗号化システムで対になって存在しています。DSAなどの典型的な公開鍵暗号化システムの場合、1つの非公開鍵は厳密に1つの公開鍵に対応します。非公開鍵は、署名を計算するのに使われます。

エンティティ

エンティティは、人、組織、プログラム、コンピュータ、企業、銀行など、一定の度合いで信頼の対象となるさまざまなものを指します。

公開鍵暗号化では、その性質上、ユーザーの公開鍵にアクセスする必要があります。大規模なネットワーク環境では、互いに通信しているエンティティ間で以前の関係が引き続き確立されていると仮定したり、使われているすべての公開鍵を収めた信頼できるリポジトリが存在すると仮定したりすることは不可能です。このような公開鍵の配布に関する問題を解決するために証明書が考案されました。現在では、証明書発行局(CA)が、信頼できる第三者として機能します。CAは、信頼されて、ほかのエンティティのために証明書に署名する(証明書を発行する)エンティティ(ビジネスなど)です。CAだけが、法的な契約による義務の下で、有効かつ信頼できる証明書を作成するものと見なされています。VeriSign、Thawte、Entrustをはじめ、多くの公開CAが存在します。NetscapeやMicrosoftの認証サーバー、EntrustのCA製品などを所属組織内で利用すれば、独自の証明書発行局を運営することも可能です。

証明書を使うアプリケーション

現在、X.509証明書のアプリケーションでもっとも身近なものは、TLSプロトコルをサポートするWebブラウザ(Mozilla FirefoxやMicrosoft Internet Explorerなど)です。TLS (Transport Layer Security)は、ネットワーク・トラフィックにプライバシと認証機能を提供するセキュリティ・プロトコルです。TLSをサポートするブラウザは、TLSをサポートするWebサーバーとの間でだけ、このプロトコルを使うことができます。

X.509の証明書に依存する技術としては、ほかに次のものがあります。

- 署名されたJava ARchiveやMicrosoft Authenticodeなど、さまざまなコード署名方式。

- PEMやS/MIMEなど、機密保護された電子メールの各種標準。

証明書の取得方法

証明書を取得する基本的な方法としては、次の2つがあります。

- 自分で作成する(keytoolなどの適切なツールを使用する)

- 証明書発行局に証明書の発行を依頼する(直接要求する、またはkeytoolなどのツールを使用して要求を生成する)。

証明書の作成処理で必要になる主な入力には、次のものがあります。

- 対になった公開鍵と非公開鍵。専用のツール(keytoolなど)やブラウザを使って生成されたものです。公開鍵のみがだれに対しても提示されます。非公開鍵は、データへの署名に使われます。他人に非公開鍵を知られると、所有者になりすまされて、所有者に帰属する法的な文書を偽造されることにもなりかねません。

- 認定されるエンティティ(自分など)についての情報を提供する必要があります。一般に、名前や勤務先住所などの情報が含まれます。CAに証明書の発行を依頼する場合、通常、情報が正しいことを証明するものを提示しなければなりません。

CAに証明書の発行を依頼する場合は、自分の非公開鍵と自分についての情報を提出します。keytoolや、証明書署名要求の生成をサポートするブラウザなどのツールを使ってこの情報にデジタル署名し、CAに送ります。CAは証明書を生成して、送り返します。

自分で証明書を生成する場合は、同様の情報に加えて、さらに若干の情報(証明書の有効期間、シリアル番号など)を用意し、keytoolなどのツールを使って証明書を作成します。自己署名の証明書では受け入れられない場合があります。CAが提供する価値の1つは、中立で信頼できる紹介サービスを提供することであり、その一部は、認証サービス業務(CSP)の中で公開されている検証要件に基づいています。

X.509証明書の内容

X.509規格では、証明書に含める情報が定義されており、この情報を証明書に書き込む方法(データ形式)についても記述されています。すべてのX.509証明書は、署名のほかに次のデータを含んでいます。

バージョン

証明書に適用されるX.509規格のバージョンを特定します。証明書に指定できる情報は、バージョンによって異なります。これまでに、3つのバージョンが定義されています。

シリアル番号

証明書を作成したエンティティは、そのエンティティが発行する他の証明書と区別するために、証明書にシリアル番号を割り当てます。この情報は、様々な方法で使われます。たとえば、証明書が失効されると、シリアル番号が証明書失効リスト(CRL)に格納されます。

署名アルゴリズム識別子

証明書に署名を付けるときにCAが使ったアルゴリズムを特定します。

発行者名

証明書に署名したエンティティのX.500名です。エンティティは、通常はCAです。この証明書を使うことは、証明書に署名を付けたエンティティを信頼することを意味します。ルートまたはトップ・レベルのCAの証明書など、場合によっては発行者が自身の証明書に署名を付けることがある点に注意してください。

有効期間

各証明書は、かぎられた期間だけ有効になります。この期間は開始の日時と終了の日時によって指定され、数秒の短い期間から100年という長期にわたることもあります。選択される有効期間は、証明書への署名に使われる非公開鍵の強度や証明書に支払う金額など、さまざまな要因で異なります。有効期間は、使用する非公開鍵が損なわれない場合に、エンティティが公開鍵を信頼できると期待される期間です。

サブジェクト名

証明書で公開鍵が識別されているエンティティの名前です。この名前はX.500標準を使うので、インターネット全体で一意なものと想定されます。これは、エンティティの識別名(DN)で、次はその例です。

CN=Java Duke, OU=Java Software Division, O=Sun Microsystems Inc, C=US

これらはそれぞれサブジェクトの通称、組織単位、組織、国を表します。

Subjectの公開鍵情報

名前を付けられたエンティティの公開鍵とアルゴリズム識別子です。アルゴリズム識別子では、公開鍵に対して使われている公開鍵暗号化システムおよび関連する鍵パラメータが指定されています。

X.509 Version 1は、1988年から利用されて広く普及しており、もっとも一般的です。

X.509 Version 2では、サブジェクトや発行者の名前をあとで再利用できるようにするために、サブジェクトと発行者の一意識別子の概念が導入されました。ほとんどの証明書プロファイル文書では、名前を再使用しないことと、証明書で一意な識別子を使わないことが、強く推奨されています。Version 2の証明書は、広くは使われていません。

X.509 Version 3はもっとも新しい(1996年)規格で、拡張機能の概念をサポートしています。これにより、だれでも拡張機能を定義でき、証明書に含めることができます。現在使われている一般的な拡張機能としては、KeyUsage (「署名専用」など、鍵の使用を特定の目的に制限する)、AlternativeNames (DNS名、電子メール・アドレス、IPアドレスなど、他のアイデンティティを公開鍵に関連付けることができる)などがあります。拡張機能には、criticalというマークを付けて、その拡張機能のチェックと使用を義務付けることができます。たとえば、criticalとマークされ、KeyCertSignが設定されたKeyUsageエクステンションが証明書に含まれている場合、この証明書をSSL通信中に提示すると、証明書が拒否されます。これは、証明書のエクステンションによって、関連する非公開鍵が証明書の署名専用として指定されており、SSLでは使用できないためです。

証明書のすべてのデータは、ASN.1/DERと呼ばれる2つの関連規格を使ってエンコードされます。Abstract Syntax Notation 1はデータについて記述しています。Distinguished Encoding Rulesはデータの保存および転送の唯一の方法について記述しています。

証明書のアクセスと管理に使うJava API

java.security.certにあるCertificate APIには、次のクラスとインタフェースが含まれています。

- CertificateFactoryクラスは、証明書ファクトリの機能を定義します。証明書ファクトリは、エンコーディングから証明書のオブジェクト、証明書失効リスト(CRL)のオブジェクト、および証明書パスのオブジェクトを生成するために使用されます。

- Certificateクラスは、様々な証明書を管理するための抽象クラスです。形式は異なっていても重要な共通の使用方法を備えた、証明書のための抽象化です。たとえば、X.509とPGPなどは異なる種類の証明書ですが、一般的な証明書の機能(エンコードと検証など)と、公開鍵など一部の情報を共有しています。

- CRLクラスは、様々な証明書失効リスト(CRL)を管理するための抽象クラスです。

- X509Certificateクラスは、X.509証明書のための抽象クラスです。X.509証明書のすべての属性にアクセスするための標準的な手段を提供します。

- X509Extensionインタフェースは、X.509拡張情報のためのインタフェースです。X.509 v3の証明書およびv2のCRLに定義されている拡張情報は、付加属性をユーザーや公開鍵と関連付けるためのメカニズムを提供します。これは、証明書の階層やCRLの配布を管理するためのものです。

- X509CRLクラスは、X.509証明書失効リスト(CRL)のための抽象クラスです。CRLは、タイムスタンプの付いたリストで、取り消された証明書を示しています。これは証明書発行局(CA)によって署名され、公開のリポジトリで自由に利用できるようになっています。

- X509CRLEntryクラスは、CRLのエントリのための抽象クラスです。

X.509証明書の生成、表示、インポート、およびエクスポートのためのJavaツール

公開鍵/非公開鍵のペアとX.509 v3証明書の作成、およびキーストアの管理に使用できる、keytoolというツールがあります。鍵と証明書は、Javaアプリケーションおよびアプレットにデジタル署名を行うために使用されます(jarsignerを参照)。

キーストアとは、鍵と証明書を保持する保護されたデータベースです。キーストアへのアクセスは、パスワードによって保護されます。キーストアのパスワードは、キーストアの作成時に作成者が定義し、変更できるのは、現在のパスワードを指定した場合のみです。また、キーストアに含まれる各非公開鍵は、それぞれのパスワードによって保護できます。

keytoolを使用すると、ファイルとして保存されているX.509 v1、v2およびv3証明書の表示、インポートおよびエクスポートや、新しいv3証明書の生成ができます。例については、Java Platform, Standard Editionツール・リファレンスでkeytoolの「例」の項を参照してください。

コア・クラスとインタフェース

Java Certification Path APIのコア・クラスは、アルゴリズムおよび実装に依存しない方式で証明書パスの機能をサポートするインタフェースおよびクラスで構成されています。

APIは、証明書を処理する既存のjava.security.certパッケージに構築され、その機能を拡張します。コア・クラスは、次のように基本、検証、構築およびストレージという4つのクラス・カテゴリに分けることができます。

- 基本のCertification Pathクラス

- CertPath、CertificateFactoryおよびCertPathParameters

- Certification Path検証クラス

- CertPathValidator、CertPathValidatorResultおよびCertPathChecker

- Certification Path構築クラス

- CertPathBuilderおよびCertPathBuilderResult

- 証明書/CRLストレージ・クラス

- CertStore、CertStoreParameters、CertSelectorおよびCRLSelector

Java Certification Path APIには、RFC 5280 (Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile)で定義されているPKIX証明書パス検証アルゴリズムで使用するためにモデル化された、アルゴリズム固有クラスのセットも含まれます。PKIXクラスを次に示します。

- TrustAnchor

- PKIXParameters

- PKIXCertPathValidatorResult

- PKIXBuilderParameters

- PKIXCertPathBuilderResult

- PKIXCertPathChecker

- PKIXRevocationChecker

関連するCertification Path APIクラスをすべて説明するリファレンス・ドキュメントは、java.security.certにあります。

CertPath APIのクラスおよびインタフェースの大半は、スレッドセーフではありません。ただし、このガイドおよびAPI仕様で言及される例外もあります。スレッドに対して安全でない単一のオブジェクトに同時にアクセスする必要のある複数のスレッドは、互いに同期して必要なロックを行うものとします。複数のスレッドがそれぞれ個別のオブジェクトを処理する場合、それらのスレッドは同期する必要はありません。

トピック

基本のCertification Pathクラス

Certification Path検証クラス

Certification Path構築クラス

証明書/CRLストレージ・クラス

PKIXクラス

基本のCertification Pathクラス

基本の証明書パス・クラスは、証明書パスをエンコードおよび表示する基本的な機能を提供します。Java Certification Path APIの主要な基本クラスはCertPathです。このクラスは、すべての型の証明書パスで共有される汎用的な部分をカプセル化します。アプリケーションは、CertificateFactoryクラスのインスタンスを使ってCertPathオブジェクトを生成します。

トピック

CertPathクラス

CertificateFactoryクラス

CertPathParametersインタフェース

CertPathクラス

CertPathクラスは、証明書パスの抽象クラスです。すべての証明書パス・オブジェクトが共有する機能を定義します。様々な証明書パスの型は、それが異なる内容および順序付けスキームを持っていても、CertPathクラスをサブクラス化することによって実装できます。

すべてのCertPathオブジェクトは、直列化可能、不変、かつスレッドに対して安全です。さらに、次の特徴を備えています。

- 型

これは、証明書パスの証明書の型と対応しています。たとえば、X.509のようになります。CertPathの型は、次のメソッドを使って取得されます。

public String getType()

標準の証明書型については、CertificateFactoryの型を参照してください。

- 証明書リスト

getCertificatesメソッドは、証明書パスに含まれる証明書のリストを返します。

public abstract List<? extends Certificate> getCertificates()

このメソッドは、0個以上のjava.security.cert.CertificateオブジェクトのListを返します。返されるListおよびその中に含まれるCertificatesは、CertPathオブジェクトの内容を保護するため、変更できません。返される証明書の順序付けは、型に依存します。規則により、X.509型のCertPathオブジェクト内の証明書は、ターゲットとなる証明書から順に並べられ、信頼できるアンカーによって発行された証明書が最後に置かれます。つまり、証明書の発行者は、その次に続く証明書の主体になります。TrustAnchorを表す証明書を証明書パスに含めることはできません。ただし、検証されていないX.509のCertPathは、この規則に従っていないことがあります。PKIX CertPathValidatorは、この規則の違反を検出することにより、証明書パスが無効になりCertPathValidatorExceptionがスローされるのを防ぎます。

- 1つ以上のエンコード

各CertPathオブジェクトは、1つ以上のエンコードをサポートします。これらは証明書パスの外部エンコード形式で、ネットワークを通じてパスを別の組織に転送する際に、パスの標準表示がJava仮想マシンの外部で必要なときに使用します。各パスはデフォルトの形式でエンコードされ、そのバイトは次のメソッドを使って返されます。

public abstract byte[] getEncoded()

一方、getEncoded(String)メソッドは、エンコード形式をString (例: PKCS7)として指定することにより、サポートされる特定のエンコードを返します。標準のエンコード形式については、CertPathのエンコードを参照してください。

public abstract byte[] getEncoded(String encoding)

また、getEncodingsメソッドは、サポートされるエンコード形式Stringでもイテレータを返します(デフォルトのエンコード形式が最初に返される)。

public abstract Iterator<String> getEncodings()

すべてのCertPathオブジェクトはSerializableでもあります。直列化中にCertPathオブジェクトは代替CertPath.CertPathRepオブジェクトに解釈処理されます。これにより、基本的な実装にかかわらず、CertPathオブジェクトを同等の表現に直列化できます。

CertPathオブジェクトは、CertificateFactoryを使って、エンコードされたバイト配列またはCertificateのリストから生成されます。一方、CertPathBuilderは、もっとも信頼できるCAから特定の主体へのCertPathを探すために使用されます。CertPathオブジェクトが生成されると、それはCertPathValidatorのvalidateメソッドに渡され、検証されます。これらの概念の詳細については、続くセクションで説明します。

CertificateFactoryクラス

CertificateFactoryクラスは、証明書ファクトリの機能を定義するエンジン・クラスです。Certificate、CRLおよびCertPathオブジェクトを生成するために使用されます。

CertificateFactoryをCertPathBuilderと混同しないでください。CertPathBuilder (後述)は、証明書パスが存在しないときに、証明書パスの検出または発見に使用されます。それに対してCertificateFactoryは、証明書パスがすでに検出されていて、エンコードされたバイト配列、またはCertificateの配列など、異なる形式で存在する内容から呼出し側がCertPathオブジェクトのインスタンスを生成する必要があるときに使用されます。

CertificateFactoryオブジェクトの作成

CertificateFactoryオブジェクトの作成の詳細は、Java暗号化アーキテクチャ・リファレンス・ガイドのCertificateFactoryの項を参照してください。

CertPathオブジェクトの生成

CertificateFactoryのインスタンスは、CertificateオブジェクトのListから、またはCertPathのエンコードされた形式を含むInputStreamから、CertPathオブジェクトを生成します。CertPathと同様、それぞれのCertificateFactoryは、証明書パス(PKCS#7など)のデフォルトのエンコード形式をサポートします。CertPathオブジェクトを生成し、そのオブジェクトを入力ストリームから(デフォルトのエンコード形式で)読み込まれたデータを使って初期化するには、generateCertPathメソッドを使用します。

public final CertPath generateCertPath(InputStream inStream)

特定のエンコード形式から読み込まれたデータを使用する場合は次のようになります。

public final CertPath generateCertPath(InputStream inStream,
String encoding)

サポートされているエンコード形式を調べるには、getCertPathEncodingsメソッドを使用します(デフォルトのエンコードが最初に返される)。

public final Iterator<String> getCertPathEncodings()

証明書パス・オブジェクトをCertificateオブジェクトのListから生成するには、次のメソッドを使用します。

public final CertPath generateCertPath(List<? extends Certificate> certificates)

CertificateFactoryは、ファクトリと同じ型のCertificatesで構成されたCertPathオブジェクトを常に返します。たとえば、X.509型のCertificateFactoryは、java.security.cert.X509Certificateのインスタンスである証明書で構成されたCertPathオブジェクトを返します。

次のコード例は、PKCS#7でエンコードされた、ファイルに格納されている証明書応答から証明書パスを生成する方法を示しています。

// open an input stream to the file
FileInputStream fis = new FileInputStream(filename);
// instantiate a CertificateFactory for X.509
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// extract the certification path from
// the PKCS7 SignedData structure
CertPath cp = cf.generateCertPath(fis, "PKCS7");
// print each certificate in the path
List<Certificate> certs = cp.getCertificates();
for (Certificate cert : certs) {
System.out.println(cert);
}

次に、KeyStoreから証明書チェーンをフェッチして、CertificateFactoryを使ってCertPathに変換する別のコード例を示します。

// instantiate a KeyStore with type JKS
KeyStore ks = KeyStore.getInstance("JKS");
// load the contents of the KeyStore
ks.load(new FileInputStream("./keystore"),
"password".toCharArray());
// fetch certificate chain stored with alias "sean"
Certificate[] certArray = ks.getCertificateChain("sean");
// convert chain to a List
List certList = Arrays.asList(certArray);
// instantiate a CertificateFactory for X.509
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// extract the certification path from
// the List of Certificates
CertPath cp = cf.generateCertPath(certList);

generateCertificatesという名前のCertificateFactoryに、Certificatesのシーケンスを構文解析する既存のメソッドがあることに注意してください。複数の証明書から成るエンコードでは、互いに関連性がないと思われる証明書のコレクションを解析する場合に、generateCertificatesを使用します。それ以外では、CertPathを生成し、CertPathValidator (後述)で検証する場合に、generateCertPathを使用します。

CertPathParametersインタフェース

CertPathParametersインタフェースは、特定の証明書パス・ビルダーまたは検証アルゴリズムで使用される一連のパラメータの透明な表現です。

このインタフェースの主な目的は、すべての証明書パスのパラメータの仕様をグループ化すること(およびそれらのパラメータに安全な型を提供すること)です。CertPathParametersインタフェースは、Cloneableインタフェースを拡張し、例外をスローしないclone()メソッドを定義します。このインタフェースのすべての固定実装は、必要に応じてObject.clone()メソッドを実装し、オーバーライドします。これにより、アプリケーションは、CertPathParametersオブジェクトを複製できます。

CertPathParametersインタフェースを実装しているオブジェクトは、CertPathValidatorおよびCertPathBuilderクラスのメソッドに引数として渡されます。一般に、CertPathParametersインタフェースの固定実装は、特定の証明書パスの構築または検証アルゴリズムに固有の入力パラメータのセットを保持します。たとえば、PKIXParametersクラスは、PKIX証明書パス検証アルゴリズムの入力パラメータのセットを保持するCertPathParametersインタフェースの実装です。このようなパラメータの1つに、呼出し側が検証処理のアンカーについて信頼する、もっとも信頼できるCAのセットがあります。このパラメータについては特に、PKIXParametersクラスを扱ったセクションで詳しく説明します。

Certification Path検証クラス

Java Certification Path APIには、証明書パスを検証するクラスおよびインタフェースが含まれています。アプリケーションは、CertPathValidatorクラスのインスタンスを使用してCertPathオブジェクトを検証します。成功すると、CertPathValidatorResultインタフェースを実装するオブジェクトに、検証アルゴリズムの結果が返されます。

トピック

CertPathValidatorクラス

CertPathValidatorResultインタフェース

CertPathValidatorクラス

CertPathValidatorクラスは、証明書パスの検証に使用されるエンジン・クラスです。

CertPathValidatorオブジェクトの生成

他のエンジン・クラスと同様に、特定の検証アルゴリズム用のCertPathValidatorオブジェクトを取得するには、CertPathValidatorクラスのgetInstance staticファクトリ・メソッドの1つを呼び出します。

public static CertPathValidator getInstance(String algorithm)
public static CertPathValidator getInstance(String algorithm,
String provider)
public static CertPathValidator getInstance(String algorithm,
Provider provider)

algorithmパラメータは、証明書パス検証アルゴリズムの名前(「PKIX」など)です。標準のCertPathValidatorアルゴリズム名は、Javaセキュリティ標準アルゴリズム名仕様にリストされています。

証明書パスの検証

CertPathValidatorオブジェクトが生成されると、validateメソッドを呼び出して、検証する証明書パスおよびアルゴリズム固有のパラメータ・セットを渡すことによって、パスを検証できます。

public final CertPathValidatorResult
validate(CertPath certPath, CertPathParameters params)
throws CertPathValidatorException,
InvalidAlgorithmParameterException

検証アルゴリズムが成功すると、CertPathValidatorResultインタフェースを実装するオブジェクトに結果が返されます。そうでない場合は、CertPathValidatorExceptionがスローされます。CertPathValidatorExceptionには、CertPathを返すメソッドが含まれます。また、必要に応じて、アルゴリズムの失敗を引き起こした証明書のインデックスや、エラーの根本となる例外または原因を返すメソッドが含まれます。

validateメソッドに渡されるCertPathおよびCertPathParametersは、検証アルゴリズムによってサポートされた型である必要があります。それ以外の場合は、InvalidAlgorithmParameterExceptionがスローされます。たとえば、PKIXアルゴリズムを実装するCertPathValidatorインスタンスは、X.509型のCertPathオブジェクト、およびPKIXParametersのインスタンスであるCertPathParametersを検証します。

CertPathValidatorResultインタフェース

CertPathValidatorResultインタフェースは、証明書パス検証アルゴリズムの成功結果または出力の透明な表現です。

このインタフェースの主な目的は、すべての検証結果をグループ化し、型保証をすることです。CertPathParametersインタフェースと同様に、CertPathValidatorResultはCloneableを拡張し、例外をスローしないclone()メソッドを定義します。これにより、アプリケーションは、CertPathValidatorResultオブジェクトを複製できます。

CertPathValidatorResultインタフェースを実装しているオブジェクトは、成功時にCertPathValidatorResultインタフェースのvalidateメソッドによって返されます。成功しなかった場合は、失敗の説明とともにCertPathValidatorExceptionがスローされます。一般に、CertPathValidatorResultインタフェースの固定実装は、特定の証明書パス検証アルゴリズムに固有の出力パラメータのセットを保持します。たとえば、PKIXCertPathValidatorResultクラスは、PKIX証明書パス検証アルゴリズムの出力パラメータを取得するメソッドを含むCertPathValidatorResultインタフェースの実装です。このようなパラメータの1つに、有効なポリシー・ツリーがあります。このパラメータについては特に、PKIXCertPathValidatorResultクラスを扱ったセクションで詳しく説明します。

次のコード例では、CertPathValidatorを作成し、それを使用して証明書パスを検証する方法を示します。この例は、validateメソッドに渡されるCertPathおよびCertPathParametersオブジェクトが事前に生成されていることを前提としています。より詳しい例は、PKIXクラスを説明したセクションにあります。

// create CertPathValidator that implements the "PKIX" algorithm
CertPathValidator cpv = null;
try {
cpv = CertPathValidator.getInstance("PKIX");
} catch (NoSuchAlgorithmException nsae) {
System.err.println(nsae);
System.exit(1);
}
// validate certification path ("cp") with specified parameters ("params")
try {
CertPathValidatorResult cpvResult = cpv.validate(cp, params);
} catch (InvalidAlgorithmParameterException iape) {
System.err.println("validation failed: " + iape);
System.exit(1);
} catch (CertPathValidatorException cpve) {
System.err.println("validation failed: " + cpve);
System.err.println("index of certificate that caused exception: "
+ cpve.getIndex());
System.exit(1);
}

Certification Path構築クラス

Java Certification Path APIには、証明書パスを構築する(または検出する)ためのクラスが含まれています。アプリケーションは、CertPathBuilderクラスのインスタンスを使ってCertPathオブジェクトを構築します。成功すると、CertPathBuilderResultインタフェースを実装するオブジェクトに、構築の結果が返されます。

トピック

CertPathBuilderクラス

CertPathBuilderResultインタフェース

CertPathBuilderクラス

CertPathBuilderクラスは、証明書パスの構築に使用されるエンジン・クラスです。

CertPathBuilderオブジェクトの生成

他のエンジン・クラスと同様に、特定の構築アルゴリズム用のCertPathBuilderオブジェクトを取得するには、CertPathBuilderクラスのgetInstance staticファクトリ・メソッドの1つを呼び出します。

public static CertPathBuilder getInstance(String algorithm)
public static CertPathBuilder getInstance(String algorithm,
String provider)
public static CertPathBuilder getInstance(String algorithm,
Provider provider)

algorithmパラメータは、証明書パス構築アルゴリズムの名前(「PKIX」など)です。標準のCertPathBuilderアルゴリズム名は、Javaセキュリティ標準アルゴリズム名仕様にリストされています。

証明書パスの構築

CertPathBuilderオブジェクトが生成されると、buildメソッドを呼び出して、アルゴリズム固有のパラメータ仕様を渡すことによって、パスを構築できます。

public final CertPathBuilderResult build(CertPathParameters params)
throws CertPathBuilderException,
InvalidAlgorithmParameterException

構築アルゴリズムが成功すると、CertPathBuilderResultインタフェースを実装するオブジェクトに結果が返されます。失敗した場合は、たとえば、基になる例外(存在する場合)とエラー・メッセージなど、エラーについての情報を含むCertPathBuilderExceptionがスローされます。

buildメソッドに渡されるCertPathParametersは、構築アルゴリズムによってサポートされた型である必要があります。それ以外の場合は、InvalidAlgorithmParameterExceptionがスローされます。

CertPathBuilderResultインタフェース

CertPathBuilderResultインタフェースは、証明書パス構築アルゴリズムの結果または出力の透明な表現です。

このインタフェースには、次に示すように、正常に構築された証明書パスを返すメソッドが含まれます。

public CertPath getCertPath()

CertPathBuilderResultインタフェースの目的は、すべての構築結果をグループ化すること(およびそれらの構築結果に安全な型を提供すること)です。CertPathValidatorResultインタフェースと同様に、CertPathBuilderResultインタフェースは、Cloneableを拡張し、例外をスローしないclone()メソッドを定義します。これにより、アプリケーションは、CertPathBuilderResultオブジェクトを複製できます。

CertPathBuilderResultインタフェースを実装するオブジェクトは、CertPathBuilderのbuildメソッドによって返されます。

次のコード例では、CertPathBuilderを作成し、それを使用して証明書パスを構築する方法を示します。この例は、buildメソッドに渡されるCertPathParametersオブジェクトが事前に生成されていることを前提としています。より詳しい例は、PKIXクラスを説明したセクションにあります。

// create CertPathBuilder that implements the "PKIX" algorithm
CertPathBuilder cpb = null;
try {
cpb = CertPathBuilder.getInstance("PKIX");
} catch (NoSuchAlgorithmException nsae) {
System.err.println(nsae);
System.exit(1);
}
// build certification path using specified parameters ("params")
try {
CertPathBuilderResult cpbResult = cpb.build(params);
CertPath cp = cpbResult.getCertPath();
System.out.println("build passed, path contents: " + cp);
} catch (InvalidAlgorithmParameterException iape) {
System.err.println("build failed: " + iape);
System.exit(1);
} catch (CertPathBuilderException cpbe) {
System.err.println("build failed: " + cpbe);
System.exit(1);
}

証明書/CRLストレージ・クラス

Java Certification Path APIには、リポジトリから証明書およびCRLを取得するCertStoreクラスが含まれています。

このクラスを使用すると、呼出し側は、CertPathValidatorまたはCertPathBuilderの実装が証明書およびCRLの検出に使用するリポジトリを指定できます。PKIXParametersクラスのaddCertStoresメソッドを参照してください。

CertPathValidator実装は、呼出し側がコールバック・メカニズムとして指定したCertStoreオブジェクトを使用してCRLをフェッチし、失効チェックを行います。同様に、CertPathBuilder実装は、CertStoreをコールバック・メカニズムとして使用して証明書をフェッチします。また、失効チェックを行っている場合はCRLをフェッチします。

トピック

CertStoreクラス

CertStoreParametersインタフェース

CertSelectorおよびCRLSelectorインタフェース

CertStoreクラス

CertStoreクラスは、証明書および証明書失効リスト(CRL)のリポジトリとして機能するエンジン・クラスです。

このクラスは、CertPathBuilderおよびCertPathValidatorの実装によって、証明書およびCRLの検索のため、または汎用の証明書およびCRLの取得メカニズムとして使用されます。

CertStoreは、非公開鍵および信頼できる証明書のキャッシュへのアクセスを提供するjava.security.KeyStoreクラスとは異なり、非常に大きくなる可能性がある、信頼されない証明書およびCRLのリポジトリへのアクセスを提供するように設計されています。たとえば、CertStoreのLDAP実装は、1つ以上のディレクトリに格納されている証明書およびCRLに対するアクセスを、LDAPプロトコルを使って提供します。

CertStoreオブジェクトのすべてのpublicメソッドは、スレッドセーフです。つまり、単一の(または複数の) CertStoreオブジェクト上で、複数のスレッドがこれらのメソッドを並行して呼び出しても、悪影響はありません。これにより、たとえばCertPathBuilderは、CRLを検索しながら同時にほかの証明書を検索できます。

CertStoreオブジェクトの生成

他のエンジン・クラスと同様に、特定のリポジトリ型用のCertStoreオブジェクトを取得するには、CertStoreクラスのgetInstance staticファクトリ・メソッドの1つを呼び出します。

public static CertStore getInstance(String type,
CertStoreParameters params)
public static CertStore getInstance(String type,
CertStoreParameters params, String provider)
public static CertStore getInstance(String type,
CertStoreParameters params, Provider provider)

typeパラメータは、証明書リポジトリ型(「LDAP」など)の名前です。標準のCertStore型は、Javaセキュリティ標準アルゴリズム名仕様にリストされています。

初期化パラメータ(params)は、リポジトリ型に固有のものです。たとえば、サーバー・ベースのリポジトリの初期化パラメータは、サーバーのホスト名およびポートを含みます。パラメータがこのCertStore型について無効な場合、InvalidAlgorithmParameterExceptionがスローされます。getCertStoreParametersメソッドは、CertStoreを初期化するために使用されたCertStoreParametersを返します。

public final CertStoreParameters getCertStoreParameters()

証明書の取得

CertStoreオブジェクトを作成すると、getCertificatesメソッドを使用してリポジトリから証明書を取得できます。このメソッドは、CertSelectorオブジェクト(詳細は後述)を引数として取得します。この引数は、どの証明書が返されるかを決定する一連の選択条件を指定します。

public final Collection<? extends Certificate> getCertificates(CertSelector selector)
throws CertStoreException

このメソッドは、選択条件を満たすjava.security.cert.CertificateオブジェクトのCollectionを返します。一致するものがない場合は、空のCollectionが返されます。リモート・リポジトリとの通信障害など、予期しないエラー状態が生じた場合は、通常、CertStoreExceptionがスローされます。

ある種のCertStore実装では、指定した選択条件に一致する証明書またはCRLをリポジトリ全体で検索できません。これらのインスタンスでは、CertStore実装は、証明書およびCRLを検索するセレクタで指定された情報を使用します。たとえば、LDAP CertStoreは、ディレクトリ内のすべてのエントリを検索しない場合があります。その代わりに、探している証明書を含んでいる可能性のあるエントリだけを検索します。提供されたCertSelectorが、LDAP CertStoreがどのエントリを検索する必要があるかを判断するための十分な情報を提供しない場合、LDAP CertStoreはCertStoreExceptionをスローすることがあります。

CRLの取得

getCRLsメソッドを使ってリポジトリからCRLを取得することもできます。このメソッドは、CRLSelectorオブジェクト(詳細は後述)を引数として取得します。この引数は、どのCRLが返されるかを決定する一連の選択条件を指定します。

public final Collection<? extends CRL> getCRLs(CRLSelector selector)
throws CertStoreException

このメソッドは、選択条件を満たすjava.security.cert.CRLオブジェクトのCollectionを返します。一致するものがない場合は、空のCollectionが返されます。

CertStoreParametersインタフェース

CertStoreParametersインタフェースは、特定のCertStoreで使用されるパラメータのセットの透明な表現です。

このインタフェースの主な目的は、すべての証明書ストレージ・パラメータ仕様をグループ化し、型保証をすることです。CertStoreParametersインタフェースは、Cloneableインタフェースを拡張し、例外をスローしないcloneメソッドを定義します。このインタフェースの実装は、必要に応じてObject.clone()メソッドを実装し、オーバーライドします。これにより、アプリケーションは、CertStoreParametersオブジェクトを複製できます。

CertStoreParametersインタフェースを実装しているオブジェクトは、CertStoreクラスのgetInstanceメソッドに引数として渡されます。CertStoreParametersインタフェースを実装している、LDAPCertStoreParametersとCollectionCertStoreParametersという2つのクラスは、このAPIで定義されます。

LDAPCertStoreParametersクラス

LDAPCertStoreParametersクラスは、CertStoreParametersインタフェースの実装で、証明書およびCRLをLDAP型のCertStoreから取得するために最低限の初期化パラメータのセット(ディレクトリ・サーバーのホストおよびポート番号)を保持します。

LDAPCertStoreParametersを参照してください。

CollectionCertStoreParametersクラス

CollectionCertStoreParametersクラスは、CertStoreParametersインタフェースの実装で、証明書およびCRLをCollection型のCertStoreから取得するための初期化パラメータのセットを保持します。

CollectionCertStoreParametersを参照してください。

CertSelectorおよびCRLSelectorインタフェース

CertSelectorおよびCRLSelectorインタフェースは、証明書およびCRLのコレクションまたは大きなグループから、証明書およびCRLを選択するための一連の条件の仕様です。

インタフェースはグループ化され、すべてのセレクタの仕様に型の安全性を提供します。各セレクタ・インタフェースは、Cloneableを拡張し、例外をスローしないclone()メソッドを定義します。これにより、アプリケーションは、CertSelectorまたはCRLSelectorオブジェクトを複製できます。

CertSelectorおよびCRLSelectorインタフェースは、それぞれmatchという名前のメソッドを定義します。matchメソッドは、CertificateまたはCRLオブジェクトを引数として取得し、オブジェクトが選択条件を満たす場合、trueを返します。それ以外は、falseを返します。CertSelectorインタフェースのmatchメソッドは、次のようにして定義されます。

public boolean match(Certificate cert)

CRLSelectorインタフェースについては、次のようにして定義されます。

public boolean match(CRL crl)

一般に、これらのインタフェースを実装しているオブジェクトは、CertStoreクラスのgetCertificatesおよびgetCRLsメソッドにパラメータとして渡されます。これらのメソッドは、指定された選択条件に一致するCertStoreリポジトリから、CertificateまたはCRLのCollectionを返します。また、CertSelectorは、証明書パスのターゲットまたはエンド・エンティティ証明書で、検証の制約を指定するためにも使用されます(例については、PKIXParameters.setTargetCertConstraintsメソッドを参照)。

X509CertSelectorクラス

X509CertSelectorクラスは、X.509証明書を選択するための一連の条件を定義するCertSelectorインタフェースの実装です。

X509Certificateオブジェクトは、matchメソッドによって選択されるには、指定された条件のすべてを満たす必要があります。この選択条件は、CertPathBuilder実装がX.509証明書パスを構築する際に、潜在的な証明書を検出するために使用するよう設計されています。

たとえば、X509CertSelectorのsetSubjectメソッドを使用すると、PKIX CertPathBuilderは、部分的に完成された連鎖の中で、先行するX509Certificateの発行者名と一致しないX509Certificateをフィルタにかけることができます。X509CertSelectorオブジェクトで、この条件とともにその他の条件を設定することにより、CertPathBuilderは、無関係な証明書を破棄して、CertPathParametersオブジェクトで指定した要件を満たすX.509証明書パスをより簡単に探すことができます。

この項で説明したX.509証明書拡張機能の定義については、RFC 5280を参照してください。

X509CertSelectorオブジェクトの生成

X509CertSelectorオブジェクトは、次のようにしてデフォルトのコンストラクタを呼び出すことにより生成されます。

public X509CertSelector()

最初は、条件は何も設定されていません(どのX509Certificateも一致)。

選択条件の設定

呼出し側は、選択条件を使用してX.509証明書の異なるコンポーネントを照合できます。ここでは、選択条件を設定するいくつかのメソッドについて説明します。X509CertSelectorを参照してください。

setIssuerメソッドは、発行者の条件を設定します。

public void setIssuer(X500Principal issuer)
public void setIssuer(String issuerDN)
public void setIssuer(byte[] issuerDN)

指定された識別名(X500Principal、RFC 2253 String、またはASN.1 DERエンコード形式)は、証明書にある発行者の識別名と一致する必要があります。nullの場合、発行者の識別名は問われません。識別名の表現には、型定義が適切で効率的なため、X500Principalを使用することをお勧めします。

同様に、setSubjectメソッドは主体の条件を設定します。

public void setSubject(X500Principal subject)
public void setSubject(String subjectDN)
public void setSubject(byte[] subjectDN)

指定された識別名(X500Principal、RFC 2253 String、またはASN.1 DERエンコード形式)は、証明書にある主体の識別名と一致する必要があります。nullの場合、主体の識別名は問われません。

setSerialNumberメソッドは、serialNumberの条件を設定します。

public void setSerialNumber(BigInteger serial)

指定されたシリアル番号は、証明書にある証明書シリアル番号と一致する必要があります。nullの場合、証明書シリアル番号は問われません。

setAuthorityKeyIdentifierメソッドは、authorityKeyIdentifierの条件を設定します。

public void setAuthorityKeyIdentifier(byte[] authorityKeyID)

証明書には、指定された値と一致するAuthority Key Identifier拡張機能が含まれている必要があります。nullの場合、authorityKeyIdentifier条件に関するチェックは行われません。

setCertificateValidメソッドは、certificateValidの条件を設定します。

public void setCertificateValid(Date certValid)

指定された日付は、証明書の証明書有効期間内に収まる必要があります。nullの場合、どの日付も有効です。

setKeyUsageメソッドは、keyUsageの条件を設定します。

public void setKeyUsage(boolean[] keyUsage)

証明書のKey Usage Extensionは、指定された鍵使用法の値(trueに設定されている値)を許可する必要があります。nullの場合、keyUsageの確認は行われません。

選択条件の取得

各選択条件の現在の値は、該当するgetメソッドを使って取得できます。X509CertSelectorを参照してください。

ここでは、X509CertSelectorクラスを使ってLDAP CertStoreからX.509証明書を取得する例を取り上げます。

はじめに、LDAPサーバーのホスト名およびポートを含むCertStoreオブジェクトの初期化に使用するLDAPCertStoreParametersオブジェクトを生成します。

LDAPCertStoreParameters lcsp = new
LDAPCertStoreParameters("ldap.sun.com", 389);

次に、CertStoreオブジェクトを生成し、次の文のようにして、LDAPCertStoreParametersオブジェクトを渡します。

CertStore cs = CertStore.getInstance("LDAP", lcsp);

この呼出しは、RFC 2587で定義されたスキーマを使って、証明書およびCRLをLDAPリポジトリから取得するCertStoreオブジェクトを生成します。

次のコードのブロックは、有効期限内のエンド・エンティティの証明書をすべて取得するX509CertSelectorを確立します。この証明書は、1)デジタル署名を許可する鍵の利用法、および2)特定の電子メール・アドレスとともに主体の代替名を持つ特定の主体に発行されます。

X509CertSelector xcs = new X509CertSelector();

// select only unexpired certificates
xcs.setCertificateValid(new Date());

// select only certificates issued to
// 'CN=alice, O=xyz, C=us'
xcs.setSubject(new X500Principal("CN=alice, O=xyz, C=us"));

// select only end-entity certificates
xcs.setBasicConstraints(-2);

// select only certificates with a digitalSignature
// keyUsage bit set (set the first entry in the
// boolean array to true)
boolean[] keyUsage = {true};
xcs.setKeyUsage(keyUsage);

// select only certificates with a subjectAltName of
// 'alice@xyz.example.com' (1 is the integer value of
// an RFC822Name)
xcs.addSubjectAlternativeName(1, "alice@xyz.example.com");

次に、以前に生成したCertStoreオブジェクトのgetCertificatesメソッドにセレクタを渡します。

Collection<Certificate> certs = cs.getCertificates(xcs);

PKIX CertPathBuilderは、潜在的な証明書の検出およびソートを容易にするため、同様のコードを使用して、検証制約またはその他の条件を満たさない潜在的な証明書を破棄することがあります。

X509CRLSelectorクラス

X509CRLSelectorクラスは、X.509 CRLを選択する一連の条件を定義するCRLSelectorインタフェースの実装です。

X509CRLオブジェクトは、matchメソッドによって選択されるには、指定された条件のすべてを満たす必要があります。選択条件は、リポジトリからCRLを取得する必要のあるCertPathValidatorまたはCertPathBuilder実装が、X.509証明書パスにある証明書の失効ステータスをチェックするのに役立つよう設計されています。

たとえば、X509CRLSelectorのsetDateAndTimeメソッドを使用すると、PKIX CertPathValidatorは、指示された時間のあとに発行された、または指示された時間の前に期限が切れるX509CRLをフィルタにかけることができます。X509CRLSelectorオブジェクトで、この条件とともにその他の条件を設定することにより、CertPathValidatorは、無関係なCRLを破棄して、証明書が取り消されているかどうかをより簡単にチェックできます。

この項で説明したX.509 CRLフィールドおよび拡張機能の定義については、RFC 5280を参照してください。

X509CRLSelectorオブジェクトの生成

X509CRLSelectorオブジェクトは、次のようにしてデフォルトのコンストラクタを呼び出すことにより生成されます。

public X509CRLSelector()

最初は、条件は何も設定されていません(どのX509CRLも一致)。

選択条件の設定

呼出し側は、選択条件を使用してX.509 CRLの異なるコンポーネントを照合できます。ここでは、選択条件を設定するほとんどのメソッドについて説明します。残りのメソッドの詳細は、X509CRLSelectorクラスのAPIドキュメントを参照してください。

setIssuersおよびsetIssuerNamesメソッドは、issuerNamesの条件を設定します。

public void setIssuers(Collection<X500Principal> issuers)
public void setIssuerNames(Collection<?> names)

CRLにある発行者の識別名は、指定された識別名の少なくとも1つと一致する必要があります。X500Principalを使用する識別名の表現は型定義が適切で効率的なため、setIssuersメソッドをお勧めします。setIssuerNamesメソッドの場合、names引数の各エントリは、Stringまたはバイト配列(それぞれ、RFC 2253またはASN.1 DERエンコード形式の名前を表す)のどちらかです。nullの場合、発行者の識別名は問われません。

setMinCRLNumberおよびsetMaxCRLNumberメソッドは、minCRLNumberおよびmaxCRLNumberの条件を設定します。

public void setMinCRLNumber(BigInteger minCRL)
public void setMaxCRLNumber(BigInteger maxCRL)

CRLには、CRL Number拡張機能が必要です。この拡張機能は、setMinCRLNumberメソッドが呼び出された場合に指定された値以上になり、setMaxCRLNumberメソッドが呼び出された場合に指定された値以下になる値を持ちます。これらのメソッドの1つに渡された値がnullの場合、対応するチェックは行われません。

setDateAndTimeメソッドは、dateAndTimeの条件を設定します。

public void setDateAndTime(Date dateAndTime)

指定された日付は、CRLのthisUpdateコンポーネントの値と同じかまたはそれよりあとで、さらにnextUpdateコンポーネントの値より前である必要があります。nullの場合、dateAndTimeのチェックは行われません。

setCertificateCheckingメソッドは、失効ステータスがチェックされている証明書を設定します。

public void setCertificateChecking(X509Certificate cert)

これは、条件ではありません。特定の証明書の失効をチェックする際に、CertStoreが関連するCRLを検索するのに役立つオプション情報です。nullが指定された場合、このようなオプション情報は提供されません。アプリケーションは、特定の証明書の失効をチェックする際、常にこのメソッドを呼び出し、CertStoreが正しいCRLを検索して無関係なCRLをフィルタすることができるよう、より多くの情報を提供する必要があります。

選択条件の取得

各選択条件の現在の値は、該当するgetメソッドを使って取得できます。これらのメソッドの詳細は、X509CRLSelectorクラスのAPIドキュメントを参照してください。

CRLをLDAPリポジトリから取得するX509CRLSelectorの生成方法は、X509CertSelectorの例と同様です。特定のCAによって発行され、最小のCRL番号を持つ、現在(現在の日付および時間)のCRLすべてを取得すると仮定します。まず、X509CRLSelectorオブジェクトを生成し、適当なメソッドを呼び出して選択条件を設定します。

X509CRLSelector xcrls = new X509CRLSelector();
// select CRLs satisfying current date and time
xcrls.setDateAndTime(new Date());
// select CRLs issued by 'O=xyz, C=us'
xcrls.addIssuerName("O=xyz, C=us");
// select only CRLs with a CRL number at least '2'
xcrls.setMinCRLNumber(new BigInteger("2"));

次に、X509CertSelectorの例で生成されたCertStoreオブジェクトのgetCRLsメソッドにセレクタを渡します。

Collection<CRL> crls = cs.getCRLs(xcrls);

PKIXクラス

Java Certification Path APIには、PKIX証明書パス検証アルゴリズムで使用するためにモデル化されている、アルゴリズム固有の一連のクラスが含まれています。

PKIX証明書パス検証アルゴリズムは、RFC 5280: インターネットX.509公開鍵インフラストラクチャの証明書および証明書失効リスト(CRL)のプロファイルで定義されています。

トピック

TrustAnchorクラス

PKIXParametersクラス

CertPathValidatorResultインタフェース

PolicyNodeインタフェースおよびPolicyQualifierInfoクラス

PKIXBuilderParametersクラス

PKIXCertPathBuilderResultクラス

PKIXCertPathCheckerクラス

証明書パス検証でのPKIXCertPathCheckerの使用

TrustAnchorクラス

TrustAnchorクラスは、X.509証明書パスの検証のための信頼できるアンカーとして使用される、最も信頼できるCAを表します。

TrustAnchorには、CAの公開鍵、CAの名前、およびこの鍵を使用して検証できるパスのセットに対する制約が含まれます。これらのパラメータは、信頼できるX509Certificateの形式で、または個別のパラメータとして指定できます。

すべてのTrustAnchorオブジェクトは、不変で、スレッドに対して安全です。つまり、単一の(または複数の) TrustAnchorオブジェクト上で、このクラスに定義されたメソッドを複数のスレッドが同時に呼び出しても、悪影響はありません。TrustAnchorオブジェクトは、不変かつスレッドに対して安全でなければならないので、アクセスの調整を心配することなく、オブジェクトをさまざまなコードに渡すことができます。

注意:
このクラスはPKIXクラスとして説明されていますが、その他のX.509証明書パス検証アルゴリズムで使用されることもあります。

TrustAnchorオブジェクトの生成

TrustAnchorオブジェクトのインスタンスを生成するには、呼出し側は、信頼できるX509Certificateまたは公開鍵と識別名のペアとして、「もっとも信頼できるCA」を指定する必要があります。また、呼出し側はオプションで、初期化の際に検証アルゴリズムが信頼できるアンカーに適用する名前の制約を指定することもできます。PKIXアルゴリズムでは、信頼できるアンカーに適用する名前の制約をサポートする必要がないため、PKIX CertPathValidatorまたはCertPathBuilderは、このパラメータをサポートすることなく例外をスローすることもできます。次のコンストラクタのうちの1つを使って、TrustAnchorオブジェクトを生成します。

public TrustAnchor(X509Certificate trustedCert,
byte[] nameConstraints)
public TrustAnchor(X500Principal caPrincipal, PublicKey pubKey,
byte[] nameConstraints)
public TrustAnchor(String caName, PublicKey pubKey,
byte[] nameConstraints)

nameConstraintsパラメータは、NameConstraints拡張機能のASN.1 DERエンコードを含むバイト配列として指定されます。名前の制約がデコードできない(正しく書式設定されない)場合、IllegalArgumentExceptionがスローされます。

パラメータ値の取得

次のように、対応するgetメソッドを使って、各パラメータを取得できます。

public final X509Certificate getTrustedCert()
public final X500Principal getCA()
public final String getCAName()
public final PublicKey getCAPublicKey()
public final byte[] getNameConstraints()

注意:
信頼できるアンカーが公開鍵と名前のペアとして指定されている場合、getTrustedCertメソッドはnullを返します。同様に、信頼できるアンカーがX509Certificateとして指定されている場合、getCA、getCAName、およびgetCAPublicKeyメソッドはnullを返します。

PKIXParametersクラス

PKIXParametersClassクラスは、PKIX証明書パス検証アルゴリズムにより定義された入力パラメータのセットを指定します。また、いくつかの有用な追加パラメータも含んでいます。

このクラスは、CertPathParametersインタフェースを実装します。

X.509 CertPathオブジェクトおよびPKIXParametersオブジェクトは、PKIXアルゴリズムを実装しているCertPathValidatorインスタンスのvalidateメソッドに引数として渡されます。CertPathValidatorは、パラメータを使って、PKIX証明書パスの検証アルゴリズムを初期化します。

PKIXParametersオブジェクトの生成

PKIXParametersオブジェクトのインスタンスを生成するには、呼出し側は、PKIX検証アルゴリズムによる定義に従って「もっとも信頼できるCA」を指定する必要があります。もっとも信頼できるCAは、次のように、2つのコンストラクタのうちの1つを使って指定できます。

public PKIXParameters(Set<TrustAnchor> trustAnchors)
throws InvalidAlgorithmParameterException
public PKIXParameters(KeyStore keystore)
throws KeyStoreException, InvalidAlgorithmParameterException

最初のコンストラクタを使用すると、呼出し側は、もっとも信頼できるCAをTrustAnchorオブジェクトのSetとして指定できます。かわりに、呼出し側は2番目のコンストラクタを使って、信頼できる証明書のエントリを含むKeyStoreインスタンスを指定できます。その各エントリは、もっとも信頼できるCAとみなされます。

パラメータ値の設定

PKIXParametersオブジェクトを作成すると、呼出し側は、様々なパラメータを設定(またはその現在の値を置換)できます。ここでは、パラメータを設定するためのいくつかのメソッドについて説明します。その他のメソッドの詳細は、PKIXParametersのAPIドキュメントを参照してください。

setInitialPoliciesメソッドは、PKIX検証アルゴリズムによって指定されたとおりに、初期ポリシー識別子を設定します。Setの要素は、Stringとして表現されたオブジェクト識別子(OID)です。initialPoliciesパラメータがnullであるか、または設定されていない場合、どのポリシーも受入れ可能です。

public void setInitialPolicies(Set<String> initialPolicies)

setDateメソッドは、パスの妥当性を判定するための時間を設定します。dateパラメータが設定されていないか、またはnullである場合、現在の日付が使用されます。

public void setDate(Date date)

setPolicyMappingInhibitedメソッドは、ポリシー・マッピング禁止フラグの値を設定します。指定されていない場合、フラグのデフォルト値はfalseです。

public void setPolicyMappingInhibited(boolean val)

setExplicitPolicyRequiredメソッドは、明示的なポリシー要求フラグの値を設定します。指定されていない場合、フラグのデフォルト値はfalseです。

public void setExplicitPolicyRequired(boolean val)

setAnyPolicyInhibitedメソッドは、ポリシー禁止フラグの値を設定します。指定されていない場合、フラグのデフォルト値はfalseです。

public void setAnyPolicyInhibited(boolean val)

setTargetCertConstraintsメソッドを使用すると、呼出し側は、ターゲットまたはエンド・エンティティの証明書に制約を設定できます。たとえば、呼出し側は、ターゲットの証明書に特定の主体名を含むよう指定できます。制約は、CertSelectorオブジェクトとして指定されます。selectorパラメータがnullであるか、または設定されていない場合、ターゲットの証明書に制約は定義されません。

public void setTargetCertConstraints(CertSelector selector)

setCertStoresメソッドを使用すると、呼出し側は、CertPathValidatorのPKIX実装がパス検証用CRLを検索するために使用するCertStoreオブジェクトのListを指定できます。これにより、CRLの位置を指定する拡張可能なメカニズムが提供されます。setCertStoresメソッドは、CertStoreオブジェクトのListをパラメータとして取得します。リスト中の最初のCertStoreは、後のエントリに優先します。

public void setCertStores(List<CertStore> stores)

setCertPathCheckersメソッドを使用すると、呼出し側は、実装に固有の証明書パスのチェッカを生成することによってPKIX検証アルゴリズムを拡張できます。たとえば、このメカニズムは、非公開証明書の拡張情報を処理するために使用されます。setCertPathCheckersメソッドは、PKIXCertPathCheckerオブジェクト(後述)のリストをパラメータとして取得します。

public void setCertPathCheckers(List<PKIXCertPathChecker> checkers)

setRevocationEnabledメソッドを使用すると、呼出し側は失効チェックを無効にできます。失効チェックは、PKIX検証アルゴリズムに必要なチェックなので、デフォルトでは有効になっています。ただし、PKIXでは、失効のチェック方法は定義しません。たとえば、実装はCRLまたはOCSPを使用することがあります。このメソッドを使用すると、呼出し側は、実装でデフォルトに設定された失効チェック・メカニズムが適当でない場合に、それを無効にできます。そのあとで、setCertPathCheckersメソッドを呼び出し、代わりとなるメカニズムを実装するPKIXCertPathCheckerに渡すことにより、別の失効チェック・メカニズムを指定できます。

public void setRevocationEnabled(boolean val)

setPolicyQualifiersRejectedメソッドを使用すると、呼出し側は、ポリシー修飾子の処理を有効または無効に設定できます。PKIXParametersオブジェクトが生成されると、このフラグはtrueに設定されます。この設定は、ポリシー修飾子を処理するためのもっとも一般的な(かつ簡単な)方法を反映します。より複雑なポリシーを使用するアプリケーションでは、このフラグをfalseに設定する必要があります。

public void setPolicyQualifiersRejected(boolean qualifiersRejected)

パラメータ値の取得

各パラメータの現在の値は、該当するgetメソッドを使って取得できます。これらのメソッドの詳細は、Class PKIXParametersのAPIドキュメントを参照してください。

PKIXCertPathValidatorResultクラス

PKIXCertPathValidatorResultクラスは、PKIX証明書パス検証アルゴリズムの結果を表します。

このクラスは、CertPathValidatorResultインタフェースを実装します。 検証アルゴリズムの実行結果である有効なポリシー・ツリーおよび主体の公開鍵が保持され、それらを返すためのメソッド(getPolicyTree()およびgetPublicKey())が含まれます。PKIXCertPathValidatorResultのインスタンスは、PKIXアルゴリズムを実装するCertPathValidatorオブジェクトのvalidateメソッドによって返されます。

このクラスの詳細は、PKIXCertPathValidatorResultのAPIドキュメントを参照してください。

PolicyNodeインタフェースおよびPolicyQualifierInfoクラス

PKIX検証アルゴリズムは、証明書ポリシー処理に関連のあるいくつかの出力を定義します。ほとんどのアプリケーションは、これらの出力を使用する必要はありませんが、PKIX検証を実装しているかまたはアルゴリズムを構築しているプロバイダはすべて、それらの出力をサポートする必要があります。

PolicyNodeインタフェースは、PKIX証明書パス検証が正常に実行されると作成される有効なポリシー・ツリーのノードを表します。アプリケーションは、PKIXCertPathValidatorResultのgetPolicyTreeメソッドを使って、有効なポリシー・ツリーのルートを取得できます。ポリシー・ツリーの詳細は、RFC 5280を参照してください。

PolicyNodeのgetPolicyQualifiersメソッドは、PolicyQualifierInfoオブジェクトのSetを返します。その各オブジェクトは、このポリシーが適用される適切な証明書のCertificate Policies拡張情報に含まれるポリシー修飾子を表します。

ほとんどのアプリケーションでは、有効なポリシー・ツリーおよびポリシー修飾子を調べる必要はありません。PKIXParametersでポリシー関連のパラメータを設定することにより、アプリケーションのポリシー処理目標を実現できます。ただし、有効なポリシー・ツリーは、より複雑なアプリケーション、特にポリシー修飾子を処理するアプリケーションに利用できます。

これらのクラスの詳細は、Interface PolicyNodeおよびPolicyQualifierInfoのAPIドキュメントを参照してください。

例9-1 PKIXアルゴリズムを使った証明書パスの検証例

ここでは、PKIX検証アルゴリズムを使った証明書パスの検証の例を取り上げます。例では、ほとんどの例外処理を無視し、信頼できるアンカーの証明書パスおよび公開鍵がすでに生成されているものと仮定します。

まず、次の行のようにして、CertPathValidatorを生成します。

CertPathValidator cpv = CertPathValidator.getInstance("PKIX");

次の手順で、TrustAnchorオブジェクトを生成します。このオブジェクトは、証明書パスの検証にアンカーとして使用されます。この例では、もっとも信頼できるCAは公開鍵および名前(名前制約は適用されず、nullとして指定される)として指定されます。

TrustAnchor anchor = new TrustAnchor("O=xyz,C=us", pubkey, null);

次の手順で、PKIXParametersオブジェクトを生成します。これを使用して、PKIXアルゴリズムで使用されるパラメータを移入します。この例では、コンストラクタに、前の手順で生成した要素TrustAnchorを1つだけ含むSetを渡します。

PKIXParameters params = new PKIXParameters(Collections.singleton(anchor));

次に、検証アルゴリズムにより使用される制約またはその他のパラメータを持つ、パラメータ・オブジェクトを移入します。この例では、explicitPolicyRequiredフラグを有効にし、初期ポリシーOIDのセット(セットの内容は示されない)を指定します。

// set other PKIX parameters here
params.setExplicitPolicyRequired(true);
params.setInitialPolicies(policyIds);

最後の手順は、生成済みの入力パラメータ・セットを使った証明書パスの検証です。

try {
PKIXCertPathValidatorResult result =
(PKIXCertPathValidatorResult) cpv.validate(certPath, params);
PolicyNode policyTree = result.getPolicyTree();
PublicKey subjectPublicKey = result.getPublicKey();
} catch (CertPathValidatorException cpve) {
System.out.println("Validation failure, cert["
+ cpve.getIndex() + "] :" + cpve.getMessage());
}

検証アルゴリズムが成功したら、その検証アルゴリズムで生成されたポリシー・ツリーおよび主体の公開鍵を、PKIXCertPathValidatorResultのgetPolicyTreeおよびgetPublicKeyメソッドを使って取得します。

そうでない場合は、CertPathValidatorExceptionがスローされるため、呼出し側は例外をキャッチして、エラー・メッセージや障害を引き起こした証明書のインデックスなど、障害のいくつかの詳細について出力できます。

PKIXBuilderParametersクラス

PKIXBuilderParametersクラスは、 CertPathBuilderクラスで使用するパラメータのセットを指定します。

PKIXParametersクラスを拡張するこのクラスは、PKIX証明書パス検証アルゴリズムに従って検証される証明書パスを構築するCertPathBuilderで使用されるパラメータのセットを指定します。

PKIXBuilderParametersオブジェクトは、PKIXアルゴリズムを実装したCertPathBuilderインスタンスのbuildメソッドに引数として渡されます。すべてのPKIX CertPathBuilderは、PKIX証明書パスの検証アルゴリズムに従って検証されている証明書パスを返す必要があります。

PKIX CertPathBuilderが構築されたパスの検証に使用するメカニズムが、実装の詳細であることに注目してください。たとえば、実装はまず、最低限の検証を行ったパスを構築し、次にPKIXCertPathValidatorのインスタンスを使ってパスを完全に検証します。より効率的な実装は、パスの構築中に多くのパスを検証し、検証の障害または行き詰まりが発生した場合には、前の段階に戻ります。

PKIXBuilderParametersオブジェクトの生成

PKIXBuilderParametersオブジェクトの生成は、PKIXParametersオブジェクトの生成と同様です。ただし、PKIXBuilderParametersオブジェクトを生成するときに、呼出し側は、ターゲットまたはエンド・エンティティの証明書に制約を指定する必要があります。これらの制約は、ターゲットの証明書を探すのに十分な情報をCertPathBuilderに提供する必要があります。制約は、CertSelectorオブジェクトとして指定されます。次のコンストラクタのうちの1つを使って、PKIXBuilderParametersオブジェクトを生成します。

public PKIXBuilderParameters(Set<TrustAnchor> trustAnchors,
CertSelector targetConstraints)
throws InvalidAlgorithmParameterException
public PKIXBuilderParameters(KeyStore keystore,
CertSelector targetConstraints)
throws KeyStoreException, InvalidAlgorithmParameterException

パラメータ値の取得/設定

PKIXBuilderParametersクラスは、PKIXParametersクラスで設定できるすべてのパラメータを継承します。さらに、setMaxPathLengthメソッドを呼び出して、証明書パス内の最大数の証明書に制限を設定することもできます。

public void setMaxPathLength(int maxPathLength)

maxPathLengthパラメータは、証明書パスに存在できる非自動発行の中間証明書の最大数を指定します。PKIXアルゴリズムを実装しているCertPathBuilderインスタンスでは、指定された長さよりも長いパスを構築することはできません。値が0の場合、パスは単一の証明書だけを含むことができます。値が -1の場合、パスの長さは制約を受けません(つまり上限はない)。最大パス長を指定しなかった場合、デフォルトの5になります。このメソッドは、呼出し側の要求を満たすかどうかにかかわりなく、CertPathBuilderが長いパスを構築するのにリソースおよび時間を費やさないようにするために便利です。

パス内のCA証明書にBasic Constraints拡張機能が含まれている場合、結果がより短い証明書パスのときは常に、拡張機能のpathLenConstraintコンポーネントの値で maxPathLengthパラメータの値をオーバーライドします。また、対応するgetMaxPathLengthメソッドにより、このパラメータを取得することもできます。

public int getMaxPathLength()

また、PKIXParametersクラスから継承されたsetCertStoresメソッドは、通常、CertPathBuilderのPKIX実装によって、パスを検証するためのCRLの検索だけでなく、パスを構築するための証明書の検索にも使用されます。これにより、証明書およびCRLの位置を指定する拡張可能なメカニズムが提供されます。

PKIXCertPathBuilderResultクラス

PKIXCertPathBuilderResultクラスは、PKIX証明書パス構築アルゴリズムの成功結果を表します。

このクラスは、PKIXCertPathValidatorResultクラスを拡張し、CertPathBuilderインタフェースを実装します。 PKIXCertPathBuilderResultのインスタンスは、PKIXアルゴリズムを実装するCertPathBuilderオブジェクトのbuildメソッドによって返されます。

PKIXCertPathBuilderResultインスタンスのgetCertPathメソッドは、常にPKIX証明書パスの検証アルゴリズムを使って検証されたCertPathオブジェクトを返します。返されたCertPathオブジェクトには、パスの固定に使用されていたもっとも信頼できるCA証明書が含まれません。代わりに、getTrustAnchorメソッドを使って、もっとも信頼できるCAのCertificateを取得します。

このクラスの詳細は、PKIXCertPathBuilderResultのAPIドキュメントを参照してください。

例9-2 PKIXアルゴリズムを使った証明書パスの構築例

これは、PKIXアルゴリズムに従って検証される証明書パスの構築例です。例外処理、およびCertStoreの移入用に信頼できるアンカーおよび証明書を生成する場合など、除外されている詳細もあります。

まず、次の例にあるように、CertPathBuilderを生成します。

CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");

この呼出しは、PKIXアルゴリズムに従って検証されるパスを返すCertPathBuilderオブジェクトを生成します。

次の手順では、PKIXBuilderParametersオブジェクトを生成します。これは、CertPathBuilderにより使用されるPKIXパラメータの移入に使用されます。

// Create parameters object, passing it a Set of
// trust anchors for anchoring the path
// and a target subject DN.
X509CertSelector targetConstraints = new X509CertSelector();
targetConstraints.setSubject("CN=alice,O=xyz,C=us");
PKIXBuilderParameters params =
new PKIXBuilderParameters(trustAnchors, targetConstraints);

次の手順では、CertPathBuilderが証明書およびCRLの検索に使用するCertStoreを指定します。この例では、証明書およびCRLを使用してCollection CertStoreを移入します。

CollectionCertStoreParameters ccsp =
new CollectionCertStoreParameters(certsAndCrls);
CertStore store = CertStore.getInstance("Collection", ccsp);
params.addCertStore(store);

次の手順では、生成済みの入力パラメータ・セットを使って証明書パスを構築します。

try {
PKIXCertPathBuilderResult result =
(PKIXCertPathBuilderResult) cpb.build(params);
CertPath cp = result.getCertPath();
} catch (CertPathBuilderException cpbe) {
System.out.println("build failed: " + cpbe.getMessage());
}

CertPathBuilderは、指定されたパラメータを満たすパスを構築できない場合、CertPathBuilderExceptionをスローします。そうでない場合、検証された証明書パスは、getCertPathメソッドを使ってPKIXCertPathBuilderResultから取得できます。

PKIXCertPathCheckerクラス

PKIXCertPathCheckerクラスは、ユーザーがPKIX CertPathValidatorまたはCertPathBuilder実装を拡張できるようにします。これは、ほとんどのユーザーは理解する必要のない高度な機能です。ただし、PKIXサービス・プロバイダを実装している方は、このセクションをお読み下さい

PKIXCertPathCheckerクラスは、X.509証明書で1つ以上のチェックを実行する抽象クラスです。開発者は、実行時にPKIX CertPathValidatorまたはCertPathBuilder実装を動的に拡張する必要がある場合、PKIXCertPathCheckerクラスの固定実装を作成する必要があります。PKIXCertPathChecker実装が役立つ例のいくつかを次に示します。

- PKIX CertPathValidatorまたはCertPathBuilder実装によって提供される失効メカニズムが適さない場合。たとえば、PKIXRevocationChecker (JDK 8で導入。PKIXRevocationCheckerクラスを使った証明書の失効ステータスのチェックを参照)を使用して失効メカニズムをより詳細に制御したり、独自のPKIXCertPathCheckerを実装して証明書が失効していないことをチェックしたりできます。

- ユーザーが重要な非公開の拡張情報を含む証明書を認識したい場合。拡張情報は非公開なので、PKIX CertPathValidatorまたはCertPathBuilder実装によって認識されず、CertPathValidatorExceptionがスローされます。この場合、開発者は重要な非公開の拡張情報を認識して処理するPKIXCertPathCheckerを実装できます。

- 開発者が、デバッグ時に処理される各証明書についての情報を記録したい、または目的を表示したい場合。

- ユーザーが特定のポリシー修飾子を持つ証明書を拒否したい場合。

PKIXParametersクラスのsetCertPathCheckersメソッドを使用すると、ユーザーはPKIXCertPathCheckerオブジェクトのListをPKIX CertPathValidatorまたはCertPathBuilder実装に渡すことができます。各PKIXCertPathCheckerオブジェクトは、PKIX CertPathValidatorまたはCertPathBuilder実装によって処理される証明書ごとに、順次呼び出されます。

PKIXCertPathCheckerオブジェクトの生成および使用

PKIXCertPathCheckerクラスには、publicコンストラクタはありません。PKIXCertPathCheckerのインスタンスの生成は実装に固有の問題なので、あえてこのようになっています。たとえば、証明書の失効ステータスのチェックにOCSPを使用するPKIXCertPathChecker実装のコンストラクタは、OCSPサーバーのホスト名およびポートを要求できます。

PKIXCertPathChecker checker = new OCSPChecker("ocsp.sun.com", 1321);

チェッカのインスタンスが生成されると、PKIXParametersクラスのaddCertPathCheckerメソッドを使って、パラメータとして追加できます。

params.addCertPathChecker(checker);

また、チェッカのListは、PKIXParametersクラスのsetCertPathCheckersメソッドを使って追加できます。

PKIXCertPathCheckerオブジェクトの実装

PKIXCertPathCheckerクラスは、抽象クラスです。このクラスには4つのメソッド(check、getSupportedExtensions、init、およびisForwardCheckingSupported)があり、すべての固定サブクラスはこれらを実装する必要があります。

PKIXCertPathCheckerの実装は、簡単な場合と複雑な場合があります。PKIXCertPathChecker実装には、ステートレスとステートフルがあります。ステートレスな実装では、checkメソッドの連続する呼出しの間で、状態が維持されません。たとえば、特定のポリシー修飾子を含む各証明書を確認するPKIXCertPathCheckerはステートレスです。それに対して、ステートフルな実装では、checkメソッドの連続する呼出しの間で、状態を維持します。通常、ステートフルな実装のcheckメソッドは、証明書パス内の以前の証明書の内容に依存します。たとえば、NameConstraints拡張情報を処理するPKIXCertPathCheckerはステートフルです。

また、サービス・プロバイダ実装によって処理される証明書がPKIXCertPathCheckerに提供される(渡される)順序は、実装がステートフルである場合は特に重要です。証明書は、サービス・プロバイダが使用するアルゴリズムによって逆方向または順方向の順序で渡されます。逆方向とは、証明書がもっとも信頼できるCA (存在する場合)からターゲットの主体へと順序付けられていることを意味します。それに対して順方向とは、証明書がターゲットの主体からもっとも信頼できるCAへと順序付けられていることを意味します。PKIXCertPathChecker実装には、連続した証明書の処理方法を理解できるようにするため、順序を知らせる必要があります。

PKIXCertPathCheckerオブジェクトの初期化

initメソッドは、チェッカの内部状態を初期化します。

public abstract void init(boolean forward)

すべてのステートフルな実装は、チェッカの内部状態をクリアまたは初期化する必要があります。こうすれば、サービス・プロバイダ実装は、初期化されていない状態のチェッカを呼び出すことができません。また、ステートフルなチェッカは、インスタンスを再度生成しなくても、以降の操作でそれを再利用できます。forwardパラメータは、PKIXCertPathCheckerに渡される証明書の順序を示します。forwardがtrueである場合、証明書はターゲットから信頼できるアンカーに渡され、falseの場合、信頼できるアンカーからターゲットに渡されます。

順方向のチェック

isForwardCheckingSupportedメソッドは、PKIXCertPathCheckerが順方向のチェックをサポートするかどうかを示すbooleanを返します。

public abstract boolean isForwardCheckingSupported()

すべてのPKIXCertPathChecker実装は逆方向のチェックをサポートしている必要があります。PKIXCertPathChecker実装は順方向のチェックをサポートできます。

順方向のチェックをサポートすると、パスが構築時にチェックされるので、順方向に構築するCertPathBuilderの効率が向上します。ただし、ステートフルなPKIXCertPathCheckerでは、順方向チェックのサポートが難しいか、または不可能な場合があります。

サポートされる拡張情報

getSupportedExtensionsメソッドは、PKIXCertPathChecker実装がサポートする(つまり、認識し、処理できる) X.509拡張情報の不変のOID StringのSetを返します。

public abstract Set<String> getSupportedExtensions()

拡張情報が処理されない場合、メソッドはnullを返す必要があります。すべての実装は、checkメソッドが処理するOID StringのSetを返す必要があります。

CertPathBuilderは、この情報を使って、順方向のチェックをサポートしないPKIXCertPathCheckerで順方向の構築を実行しているときでも、認識されない重要な拡張情報を含む証明書を識別できます。

チェックの実行

次のメソッドは、証明書に対するチェックを実行します。

public abstract void
check(Certificate cert, Collection<String> unresolvedCritExts)
throws CertPathValidatorException

unresolvedCritExtsパラメータには、OIDのコレクションがStringとして含まれています。これらのOIDは、まだ証明書パスの検証アルゴリズムで解釈されていない証明書内の重要な拡張情報のセットを表します。checkメソッドの固定実装は、unresolvedCritExtsパラメータから処理するすべての重要な拡張情報を削除する必要があります。

証明書がチェックに合格しない場合、CertPathValidatorExceptionがスローされる必要があります。

PKIXCertPathCheckerの複製

PKIXCertPathCheckerクラスは、Cloneableインタフェースを実装します。ステートフルなPKIXCertPathChecker実装はすべて、必要に応じて、cloneメソッドをオーバーライドする必要があります。cloneメソッドのデフォルトの実装は、Object.cloneメソッドを呼び出します。このメソッドは、元のオブジェクトのすべてのフィールドを新しいオブジェクトにコピーすることにより、簡単な複製を実行します。ステートレスな実装は、cloneメソッドをオーバーライドしません。ただし、ステートフルな実装はすべて、デフォルトのcloneメソッドが正しいことを確認し、必要に応じてそれをオーバーライドする必要があります。たとえば、配列内の状態を格納するPKIXCertPathCheckerは、配列を参照するだけではなく、配列のコピーを作成するため、cloneメソッドをオーバーライドする必要があります。

PKIXCertPathCheckerオブジェクトがCloneableである理由は、潜在的な証明書パスが行き詰まったり、障害ポイントに到達した場合、CertPathBuilder実装が効率的に戻り、別のパスを試すことができるようにするためです。この場合、実装は、複製されたオブジェクトを復元することにより、以前のパスの検証状態を復元できます。

例9-3 非公開の拡張情報をチェックするサンプル・コード

これは、ステートレスなPKIXCertPathChecker実装の例です。非公開の拡張情報が証明書に存在するかどうかをチェックし、いくつかの規則に従って処理します。

import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.CertPathValidatorException;

public class MyChecker extends PKIXCertPathChecker {
private static Set supportedExtensions =
Collections.singleton("2.16.840.1.113730.1.1");

/*
* Initialize checker
*/
public void init(boolean forward)
throws CertPathValidatorException {
// nothing to initialize
}

public Set getSupportedExtensions() {
return supportedExtensions;
}

public boolean isForwardCheckingSupported() {
return true;
}

/*
* Check certificate for presence of Netscape's
* private extension
* with OID "2.16.840.1.113730.1.1"
*/
public void check(Certificate cert,
Collection unresolvedCritExts)
throws CertPathValidatorException
{
X509Certificate xcert = (X509Certificate) cert;
byte[] ext =
xcert.getExtensionValue("2.16.840.1.113730.1.1");
if (ext == null)
return;

//
// process private extension according to some
// rules - if check fails, throw a
// CertPathValidatorException ...
// {insert code here}

// remove extension from collection of unresolved
// extensions (if it exists)
if (unresolvedCritExts != null)
unresolvedCritExts.remove("2.16.840.1.113730.1.1");
}
}

PKIXサービス・プロバイダ実装によるPKIXCertPathCheckerの使用方法

各PKIXCertPathCheckerオブジェクトは、構築または検証アルゴリズムを開始する前に、サービス・プロバイダ実装によって初期化される必要があります。次に例を示します。

List<PKIXCertPathChecker> checkers = params.getCertPathCheckers();
for (PKIXCertPathChecker checker : checkers) {
checker.init(false);
}

検証する証明書ごとに、サービス・プロバイダ実装は各PKIXCertPathCheckerオブジェクトのcheckメソッドを順に呼び出し、証明書および残りの解釈されていない重要な拡張情報に渡します。

for (PKIXCertPathChecker checker : checkers) {
checker.check(cert, unresolvedCritExts);
}

checkのどれかがCertPathValidatorExceptionをスローする場合、CertPathValidator実装は、検証手順を終了する必要があります。ただし、CertPathBuilder実装は、単に障害を記録し、引き続きその他の潜在的なパスを探す場合があります。すべてのcheckが成功した場合、サービス・プロバイダ実装は、すべての重要な拡張情報が解釈されていることを確認します。そうでない場合、検証が失敗したとみなされます。次に例を示します。

if (unresolvedCritExts != null &&
!unresolvedCritExts.isEmpty())
{
// note that a CertPathBuilder may have an enclosing
// try block to catch the exception below and continue on error
throw new CertPathValidatorException
("Unrecognized Critical Extension");
}

前のセクションで説明したように、CertPathBuilder実装は、潜在的な証明書パスが行き詰まったり、障害ポイントに達した場合は、戻る必要があります。ここで戻るというのは、パス内の以前の証明書に戻り、その他の潜在的なパスを探すことを意味します。CertPathBuilder実装がパスの構築中にパスを検証する場合、各PKIXCertPathCheckerの以前の状態を復元する必要があります。これを行うには、各証明書が処理される前に、PKIXCertPathCheckerオブジェクトを複製します。次に例を示します。

/* clone checkers */
List newList = new ArrayList(checkers);
ListIterator li = newList.listIterator();
while (li.hasNext()) {
PKIXCertPathChecker checker = (PKIXCertPathChecker) li.next();
li.set(checker.clone());
}

証明書パス検証でのPKIXCertPathCheckerの使用

PKIXCertPathCheckerを使用して証明書パス検証をカスタマイズすることは、比較的簡単です。

基本の証明書パス検証

まず、証明書パスを検証するコードを考慮します。

Set<TrustAnchor> trustAnchors = getTrustAnchors();
CertPath cp = getCertPath();

PKIXParameters pkixp = new PKIXParameters(trustAnchors);
pkixp.setRevocationEnabled(false);

CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
PKIXCertPathValidatorResult pcpvr =
(PKIXCertPathValidatorResult)cpv.validate(cp, pkixp);

検証が失敗した場合、validate()メソッドは例外をスローします。

基本の手順は次のとおりです。

1. CAルート証明書および検証する証明書パスを取得します。

2. 信頼できるアンカーでPKIXParametersを作成します。

3. CertPathValidatorを使用して、証明書パスを検証します。

この例では、getTrustAnchors()およびgetCertPath()がCAルート証明書と証明書パスを取得するメソッドです。

この例のgetTrustAnchors()メソッドは、検証に使用したいCAルート証明書を表すTrustAnchorのSetを返す必要があります。これは、ファイルから単一のCAルート証明書をロードする1つの簡単な実装です。

public Set<TrustAnchor> getTrustAnchors()
throws IOException, CertificateException {

CertificateFactory cf = CertificateFactory.getInstance("X.509");

X509Certificate c;
try (InputStream in = new FileInputStream("x509_ca-certificate.cer")) {
c = (X509Certificate)cf.generateCertificate(in);
}

TrustAnchor anchor = new TrustAnchor(c, null);
return Collections.singleton(anchor);
}

同様に、これは、ファイルから証明書パスをロードするgetCertPath()の簡単な実装です。

public CertPath getCertPath() throws IOException, CertificateException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");

CertPath cp;
try (InputStream in = new FileInputStream("certpath.pkcs7")) {
cp = cf.generateCertPath(in, "PKCS7");
}
return cp;
}

PKCS#7では、ファイル内の証明書の特定の順番を必要としないため、このコードは、証明書が検証されるエンティティから始まり、CAルートに戻る順番になっている場合の証明書パス検証にのみ有効です。証明書の順番が正しくない場合、追加の処理が必要です。CertificateFactoryには、この種類の処理に使用できるCollectionを受け付けるgenerateCertPath()メソッドがあります。

PKIXCertPathCheckerへの追加

証明書パス検証をカスタマイズするには、次のようにPKIXCertPathCheckerを追加します。この例では、SimpleCheckerはPKIXCertPathCheckerサブクラスです。新しい行を太字で示します。

Set<TrustAnchor> trustAnchors = getTrustAnchors();
CertPath cp = getCertPath();

PKIXParameters pkixp = new PKIXParameters(trustAnchors);
pkixp.setRevocationEnabled(false);

SimpleChecker sc = new SimpleChecker();
pkixp.addCertPathChecker(sc);

CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
PKIXCertPathValidatorResult pcpvr =
(PKIXCertPathValidatorResult)cpv.validate(cp, pkixp);

SimpleCheckerはPKIXCertPathCheckerの基本的なサブクラスです。そのcheck()メソッドは、検証される証明書パス内のすべての証明書に対して呼び出されます。SimpleCheckerはAlgorithmConstraints実装を使用して、各証明書の署名アルゴリズムと公開鍵を調査します。

import java.security.AlgorithmConstraints;
import java.security.CryptoPrimitive;
import java.security.Key;
import java.security.cert.*;
import java.util.*;

public class SimpleChecker extends PKIXCertPathChecker {
private final static Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
EnumSet.of(CryptoPrimitive.SIGNATURE);

public void init(boolean forward) throws CertPathValidatorException {}

public boolean isForwardCheckingSupported() { return true; }

public Set<String> getSupportedExtensions() { return null; }

public void check(Certificate cert,
Collection<String> unresolvedCritExts)
throws CertPathValidatorException {
X509Certificate c = (X509Certificate)cert;
String sa = c.getSigAlgName();
Key key = c.getPublicKey();

AlgorithmConstraints constraints = new SimpleConstraints();

if (constraints.permits(SIGNATURE_PRIMITIVE_SET, sa, null) == false)
throw new CertPathValidatorException("Forbidden algorithm: " + sa);

if (constraints.permits(SIGNATURE_PRIMITIVE_SET, key) == false)
throw new CertPathValidatorException("Forbidden key: " + key);
}
}

最後に、SimpleConstraintsは、RSA 2048を必要とするAlgorithmConstraints実装です。

import java.security.AlgorithmConstraints;
import java.security.AlgorithmParameters;
import java.security.CryptoPrimitive;
import java.security.Key;
import java.security.interfaces.RSAKey;
import java.util.Set;

public class SimpleConstraints implements AlgorithmConstraints {
public boolean permits(Set<CryptoPrimitive> primitives,
String algorithm, AlgorithmParameters parameters) {
return permits(primitives, algorithm, null, parameters);
}

public boolean permits(Set<CryptoPrimitive> primitives, Key key) {
return permits(primitives, null, key, null);
}

public boolean permits(Set<CryptoPrimitive> primitives,
String algorithm, Key key, AlgorithmParameters parameters) {
if (algorithm == null) algorithm = key.getAlgorithm();

if (algorithm.indexOf("RSA") == -1) return false;

if (key != null) {
RSAKey rsaKey = (RSAKey)key;
int size = rsaKey.getModulus().bitLength();
if (size < 2048) return false;
}

return true;
}
}

PKIXRevocationCheckerクラスを使った証明書の失効ステータスのチェック

PKIXRevocationCheckerのインスタンスは、オンライン証明書ステータス・プロトコル(OCSP)または証明書の失効リスト(CRL)を使って証明書の失効ステータスをチェックします。

PKIXRevocationChecker (JDK 8で導入)は、PKIXCertPathCheckerのサブクラスであり、PKIXアルゴリズムを使用して証明書の失効ステータスをチェックします。

PKIXRevocationCheckerのインスタンスは、オンライン証明書ステータス・プロトコル(OCSP)または証明書の失効リスト(CRL)を使って証明書の失効ステータスをチェックします。OCSPは、RFC 2560に記述されている、証明書のステータスを判定するためのネットワーク・プロトコルです。CRLは、失効した証明書を識別するタイム・スタンプ付きのリストです。RFC 5280には、CRLを使って証明書の失効ステータスを判定するためのアルゴリズムが記述されています。

PKIX CertPathValidatorおよびCertPathBuilderの各インスタンスは、デフォルトで有効になるデフォルトの失効実装を提供します。その実装で使用される失効設定をより詳細に制御する必要がある場合は、PKIXRevocationCheckerクラスを使用します。

PKIXRevocationCheckerクラスを使って証明書パスの失効ステータスをチェックするには、これらの一般的な手順を実行します。

1. PKIX CertPathValidatorまたはCertPathBuilderインスタンスのgetRevocationCheckerメソッドを呼び出して、PKIXRevocationCheckerインスタンスを取得します。

2. PKIXRevocationCheckerクラスに含まれるメソッドを使って、証明書の失効に固有の追加パラメータおよびオプションを設定します。これらのメソッドには、OCSP応答者の場所を特定するURIを設定するsetOCSPResponder(URI)(ただし、通常このURIは証明書に含まれているため、設定する必要はありません)と、失効オプションを設定するsetOptions(Set<PKIXRevocationChecker.Option>)が含まれています。PKIXRevocationChecker.Optionは、次のオプションを指定するために使用される列挙型です。

- ONLY_END_ENTITY: エンドエンティティ証明書の失効ステータスのみをチェックします。

- PREFER_CRLS: デフォルトでは、OCSPが失効ステータスをチェックするための推奨メカニズムであり、CRLは代替メカニズムです。この設定を切り替えるには、このオプションを使用します。

- SOFT_FAIL: ネットワーク障害を無視します。

3. PKIXRevocationCheckerのインスタンスを取得したら、addCertPathCheckerまたはsetCertPathCheckersメソッドを使用してPKIXParametersまたはPKIXBuilderParametersオブジェクトにそれを追加します。

4. PKIX CertPathValidatorまたはCertPathBuilderインスタンスのどちらを使用するかに応じて、これらの手順のいずれかを実行します。

- PKIX CertPathValidatorインスタンスを使用する場合は、検証する証明書パスと、失効チェッカを含むPKIXParametersオブジェクトを引数として指定して、validateメソッドを呼び出します。

- PKIX CertPathBuilderインスタンスを使用する場合は、失効チェッカを含むPKIXBuilderParametersオブジェクトを引数として指定して、buildメソッドを呼び出します。

5. 検証する証明書パスと、失効チェッカを含むPKIXParametersまたはPKIXBuilderParametersオブジェクトを引数として指定して、PKIX CertPathValidatorまたはCertPathBuilderインスタンスのvalidateメソッドを呼び出します。

次の抜粋は、証明書パスに含まれる証明書の失効ステータスをチェックします。CertPathオブジェクトpathは証明書パスです。paramsはPKIXParameters型のオブジェクトです。

CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
PKIXRevocationChecker rc = (PKIXRevocationChecker)cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(Option.SOFT_FAIL));
params.addCertPathChecker(rc);
params.setRevocationEnabled(false);
CertPathValidatorResult res = cpv.validate(path, params);

この抜粋では、SOFT_FAILオプションによって、失効チェッカが失効ステータスをチェックするときにネットワーク障害(OCSPサーバーへの接続確立の失敗など)が無視されます。

サービス・プロバイダの実装

上級プログラマは、証明書パス・サービス実装を提供する独自のプロバイダ・パッケージを作成できます。

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

Java Certification Path APIでは、次のエンジン・クラスが定義されています。

- CertPathValidator - 証明書パスの検証に使用される

- CertPathBuilder - 証明書パスの構築に使用される

- CertStore - リポジトリから証明書とCRLを取得するために使用される

さらに、既存のCertificateFactoryエンジン・クラスは、証明書パスの生成をサポートしています。

エンジン・クラスが提供するアプリケーション・インタフェースは、「Service Provider Interface」(SPI)として実装されます。各SPIクラスの名前は、対応するエンジン・クラス名のあとに「Spi」を追加した名前になります。たとえば、CertPathValidatorエンジン・クラスに対応するSPIクラスは、CertPathValidatorSpiクラスです。各SPIクラスは、抽象クラスです。個々のアルゴリズムまたは型について、特定の型のサービスの実装を提供するには、プロバイダは対応するSPIクラスをサブクラス化して、すべての抽象メソッドの実装を提供する必要があります。たとえば、CertStoreクラスは、証明書およびCRLをリポジトリから取得する機能を利用できるようにします。CertStoreSpiサブクラスで提供される実際の実装は、LDAPなど、特定の型の証明書リポジトリのための実装です。

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

証明書パス・サービスのためにプロバイダを実装および統合する場合は、特定の情報が提供されていることを確認する必要があります。

開発者は、プロバイダの実装および統合までのステップに従う必要があります。次に、特定のステップで従う規則をさらにいくつか示します。

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

ステップ3: プロバイダのサブクラスであるマスター・クラスの記述では、これらが、証明書パス・サービス用に定義する必要があるプロパティです。ここではアルゴリズム名はalgNameに置き換えられ、certstore型はstoreTypeに置き換えられています。

- CertPathValidator.algName

- CertPathBuilder.algName

- CertStore.storeType

algNameおよびstoreTypeに対して定義されている標準名は、Javaセキュリティ標準アルゴリズム名仕様を参照してください。各プロパティの値は、指定されたアルゴリズムを実装するクラスの完全修飾名、またはcertstore型である必要があります。つまり、クラス名のあとにピリオドとパッケージ名が記述されていなければなりません。たとえば、プロバイダは、次のようにしてCertPathValidator.PKIXプロパティを設定し、値sun.security.provider.certpath.PKIXCertPathValidatorを保持します。

put("CertPathValidator.PKIX", "sun.security.provider.certpath.PKIXCertPathValidator")

さらに、サービス属性を証明書パス・サービス用に定義できます。これらの属性は、サービス・プロバイダを選択するためのフィルタとして使用できます。標準のサービス属性の定義については、付録Aを参照してください。たとえば、プロバイダがValidationAlgorithmサービス属性をPKIX検証アルゴリズムを定義する仕様またはRFCの名前に設定している場合があります。

put("CertPathValidator.PKIX ValidationAlgorithm", "RFC5280");

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

ステップ12: プロバイダおよびそのサポート対象サービスのドキュメント化では、証明書パス・サービス・プロバイダは、SPIごとに次の情報をドキュメント化する必要があります。

証明書ファクトリ

プロバイダは、ファクトリが作成できる証明書パスの種類(および必要に応じてパス内の証明書のバージョン番号)をドキュメント化する必要があります。プロバイダは、内容だけでなく、証明書パスの証明書の順序を記述する必要があります。

プロバイダは、サポートされているエンコード形式のリストをドキュメント化する必要があります。クライアントはgetCertPathEncodingsメソッドを呼び出してそれらを要求できるので、技術的にはこの作業は必要ありません。ただし、ドキュメントには各エンコード形式について詳細に記述し、該当する場合は、標準について言及する必要があります。

証明書パスの検証

プロバイダは、検証する証明書パスの種類など、CertPathValidator実装についての関連情報をドキュメント化する必要があります。特に、PKIX CertPathValidator実装は、次の情報をドキュメント化する必要があります。

- 準拠したRFCまたは仕様。

- 証明書が取り消されていないことをチェックするために使用するメカニズム。

- 認識するオプションの証明書またはCRL拡張機能、およびそれらを処理する方法。

証明書パスの構築

プロバイダは、作成する証明書パスの種類、およびそれらのパスが検証されているかどうかなど、CertPathBuilder実装の関連情報をドキュメント化する必要があります。特に、PKIX CertPathBuilder実装は、次の情報をドキュメント化する必要があります。

- 準拠したRFCまたは仕様。

- 証明書が取り消されていないことをチェックするために使用するメカニズム。

- 認識するオプションの証明書またはCRL拡張機能、およびそれらを処理する方法。

- 証明書パスの検索に使用するアルゴリズムの詳細。例: 深さ優先、幅優先、順方向(つまり、ターゲットから信頼できるアンカーへ)、逆方向(つまり、信頼できるアンカーからターゲットへ)。

- 潜在的な証明書の選択およびソートに使用されるアルゴリズム。たとえば、パス内に次の証明書の候補になりうる2つの証明書がある場合、一方を先に選択するためにどの条件を使用するか。証明書の拒否にどの条件を使用するか。

- 該当する場合、別のパスに戻る、または別のパスを構築する際に使用するアルゴリズム(つまり、潜在的なパスが制約に適合しない場合)。

- テスト済みのCertStore実装の型。実装は、どのCertStore型でも動作するように設計されている必要があるが、それでもなおこの情報が役立つことがある。

すべてのCertPathBuilder実装は、潜在的なパスの構築に関する問題を分析および修正するため、付加的なデバッグ・サポートを提供する必要があります。このデバッグ情報へのアクセス方法の詳細については、ドキュメント化されている必要があります。

証明書/CRLストア

プロバイダは、CertStoreによって取得される証明書およびCRLの種類(および必要に応じてバージョン番号)をドキュメント化する必要があります。

また、プロバイダは、CertStore実装に関するすべての情報 (使用されるプロトコルまたはサポートされる形式など)をドキュメント化する必要があります。たとえば、LDAP CertStore実装では、どのバージョンのLDAPがサポートされているのか、およびどの標準属性が証明書およびCRLの検索に使用されるのかを記述する必要があります。また、実装が結果をキャッシュするかどうか、およびその期間(つまりリフレッシュの条件)をドキュメント化する必要があります。

実装が、特定の順序で証明書およびCRLを返す場合、ソート・アルゴリズムを記述する必要があります。また、実装は、すべての追加またはデフォルトの初期化パラメータを記述する必要があります。最後に、実装は証明書およびCRLの検索にCertSelectorまたはCRLSelectorオブジェクトの情報を使用するかどうか、およびその方法をドキュメント化する必要があります。

サービスの相互依存性

証明書パス・サービス実装におけるアルゴリズムの相互依存の一般的な種類を示します。

次に、証明書パス・サービス実装におけるアルゴリズムの相互依存の一般的な種類を示します。

- 証明書パスの検証および署名アルゴリズム
CertPathValidator実装は、各証明書のデジタル署名を検証するため、署名アルゴリズムの使用を要求する場合があります。PKIXParametersクラスのsetSigProviderメソッドにより、ユーザーは特定のSignatureプロバイダを指定できます。

- 証明書パスの構築および証明書ファクトリ
CertPathBuilder実装は、証明書のリストから証明書パスを生成するため、CertificateFactoryを使用する場合があります。

- CertStoresおよび証明書ファクトリ
CertStore実装は、証明書およびCRLをエンコードから生成するため、CertificateFactoryを使用することがあります。たとえば、LDAP CertStore実装は、X.509 CertificateFactoryを使用して、ASN.1エンコード形式からX.509証明書およびCRLを生成します。

証明書パス・パラメータ仕様のインタフェース

Certification Path APIには、パラメータの透過的な仕様を表すCertPathParametersおよびCertStoreParametersという2つのインタフェースが含まれています。

CertPathParametersインタフェースには、PKIXParametersおよびPKIXBuilderParametersクラスという2つの実装が含まれています。PKIX証明書パスの検証およびアルゴリズム・パラメータを使って作業する場合、これらのクラスを利用できます。アルゴリズムごとにパラメータが必要な場合は、そのアルゴリズムに独自のCertPathParameters実装を提供する必要があります。

CertStoreParametersインタフェースには、LDAPCertStoreParametersおよびCollectionCertStoreParametersクラスという2つの実装が含まれています。これらのクラスは、それぞれLDAPおよびCollection CertStore実装で別々に使用されます。リポジトリ型ごとにパラメータが必要な場合は、その型に独自のCertStoreParameters実装を提供する必要があります。

CertPathParametersおよびCertStoreParametersインタフェースは、それぞれ実装がオーバーライドする必要のあるcloneメソッドを定義します。標準的な実装は、オブジェクトの「ディープ」コピーを実行し、それ以降コピーに対して行われる変更が元のオブジェクトに影響しないように(逆の場合も同様)します。ただし、これは、CertStoreParametersの実装の絶対的な要件ではありません。しかし、CertStoreParametersに含まれるパラメータへの参照を保持する必要があるアプリケーションでは、cloneのシャロー・コピー実装の方が適切です。たとえば、CertStore.getInstanceは指定されたCertStoreParametersの複製を作成するので、アプリケーションはガベージ・コレクション・メカニズムを待つのではなく、シャロー・コピーcloneを使用して特定のCertStore初期化パラメータのリソースへの参照を維持し、後でリリースできます。CertStoreがその他のスレッドによって使用されていることがあるので、この操作は細心の注意を払って行う必要があります。

証明書パスの結果の仕様インタフェース

Certification Path APIには、結果の透過的な仕様を表すCertPathValidatorResultおよびCertPathBuilderResultという2つのインタフェースが含まれています。

各インタフェースの実装には、それぞれPKIXCertPathValidatorResultおよびPKIXCertPathBuilderResultクラスが含まれます。PKIX証明書パス・サービス・プロバイダを実装する場合、これらのクラスを使用できます。アルゴリズムごとに証明書パスの結果が必要な場合は、そのアルゴリズムに独自のCertPathValidatorResultまたはCertPathBuilderResult実装を提供する必要があります。

CertPathValidatorまたはCertPathBuilderのPKIX実装では、デバッグ・トレースなど、PKIXCertPathValidatorResultまたはPKIXCertPathBuilderResultの追加情報を格納することが有用な場合があります。この場合、実装は、関連情報を取得するメソッドを持つ、適切な結果クラスのサブクラスを実装する必要があります。これらのクラスは、プロバイダ・クラスとともに(たとえば、プロバイダJARファイルの一部として)提供される必要があります。

証明書パスの例外クラス

Certification Path APIには、エラーを処理する例外クラスのセットが含まれています。CertPathValidatorException、CertPathBuilderException、およびCertStoreExceptionは、GeneralSecurityExceptionのサブクラスです。

サービス・プロバイダ実装では、これらのクラスの拡張が必要になる場合があります。

たとえば、CertPathBuilder実装は、CertPathBuilderExceptionがスローされるときに、デバッグ・トレースなどの追加情報を提供します。実装は、この情報を保持するCertPathBuilderExceptionのサブクラスをスローします。同様に、CertStore実装は、CertStoreException のサブクラスをスローすることにより障害が発生した場合、追加情報を提供できます。また、CertPathValidator実装の特定の障害モードについて記述するため、CertPathValidatorExceptionのサブクラスを実装することもできます。

どちらの場合も、新しい例外クラスはプロバイダ・クラスとともに(たとえば、プロバイダJARファイルの一部として)提供される必要があります。各プロバイダは、例外サブクラスをドキュメント化する必要があります。

付録A: 標準名

Java Certification Path APIでは、証明書パスの検証アルゴリズム、エンコードおよび証明書ストレージの型について、標準名のセットを必要とし、それらを使用します。

以前にこの付録Aおよびその他のセキュリティ仕様(JCA/JSSE/など)にあった標準名は、Javaセキュリティ標準アルゴリズム名仕様にまとめられました。特定のプロバイダの情報は、JDKプロバイダにあります。

サービス・プロバイダは、標準名のドキュメントに記述されていない独自のアルゴリズムまたは非標準のアルゴリズムに新しい名前を定義することもできます。ただし、名前の衝突を防止するため、プロバイダの組織のインターネット・ドメイン名を逆にしたもの(com.sun.MyCertPathValidatorなど)を名前の前に付けることをお薦めします。

付録B: SUNプロバイダでのCertPath実装

「SUN」プロバイダは、次の標準アルゴリズム、型、およびエンコードをサポートしています。

- CertificateFactory: X.509 CertPath型とPKCS7およびPkiPathエンコード

- CertPathValidator: PKIXアルゴリズム

- CertPathBuilder: PKIXアルゴリズム

- CertStore: Collection CertStore型

これらのサービス・プロバイダ・インタフェースの実装については、それぞれ次で詳しく説明します。

CertificateFactory

CertificateFactoryエンジン・クラス用のSUNプロバイダでは、X.509 CertPathオブジェクトの生成がサポートされています。PKCS7およびPkiPathエンコードがサポートされます。PKCS#7実装は、RFC 2315のサブセットをサポートします(SignedData ContentInfo型だけがサポートされる)。CertPathの証明書は、順方向に(ターゲットから信頼できるアンカーへ)順序付けされます。CertPathの各証明書はjava.security.cert.X509Certificate型であり、バージョン1、2および3がサポートされています。

CertPathValidator

「SUN」プロバイダは、CertPathValidatorエンジン・クラスのPKIX実装を提供します。この実装は、X 509型のCertPathを検証し、RFC 5280: PKIX証明書およびCRLプロファイルで定義されている証明書パス検証アルゴリズムを実装します。この実装では、ValidationAlgorithmサービス属性はRFC5280に設定されます。

弱い暗号化アルゴリズムは、jdk.certpath.disabledAlgorithmsセキュリティ・プロパティを使用して、SUNプロバイダで無効にできます。このプロパティの説明と例は、付録E: 暗号化アルゴリズムの無効化を参照してください。

PKIX証明書とCRLプロファイルには、多くのオプション機能があります。「SUN」プロバイダは、ポリシー・マッピング、Authority Information AccessおよびCRL配布ポイント証明書拡張機能、Issuing Distribution Point CRL拡張機能、原因コードおよびCertificate Issuer CRLエントリ拡張機能のサポートを実装しています。Freshest CRLまたはSubject Information Access証明書拡張機能のサポートは実装していません。また、Freshest CRLおよびDelta CRL Indicator CRL拡張機能とInvalidity DateおよびHold Instruction Code CRLエントリ拡張機能のサポートは含まれません。

実装は、PKIX証明書とCRLプロファイルのセクション6.3に準拠しているCRL失効チェック・メカニズムをサポートします。OCSP (RFC 2560)も、現在、組込みの失効チェック・メカニズムとしてサポートされています。実装と構成、およびCRLとどのように連携するかの詳細は、付録C: OCSPサポートを参照してください。

この実装は、TrustAnchorクラスのnameConstraintsパラメータをサポートしないため、このパラメータが指定されると、validateメソッドによりInvalidAlgorithmParameterExceptionがスローされます。

CertPathBuilder

「SUN」プロバイダは、CertPathBuilderエンジン・クラスのPKIX実装を提供します。実装は、X.509型のCertPathを構築します。各CertPathはRFC 5280: PKIX Certificate and CRL Profileに定義されているPKIXアルゴリズムに従って検証されます。この実装では、ValidationAlgorithmサービス属性はRFC5280に設定されます。

この実装では、PKIXBuilderParametersオブジェクトのtargetConstraintsパラメータがX509CertSelectorのインスタンスで、主体の条件がnull以外の値に設定されていることが必要です。そうでない場合、buildメソッドによりInvalidAlgorithmParameterExceptionがスローされます。

実装は、深さ優先アルゴリズムを使って、順方向にCertPathオブジェクトを構築します。潜在的なパスが無効であるか、またはPKIXBuilderParameters maxPathLengthパラメータを超えていると判定されると、前の状態に戻って代替パスを試みます。

パスの検証は、CertPathValidator実装と同じ方法で実行されます。実装は、プロセスの早い段階で無効なパスを削除するために、パスの構築時にほとんどのパスを検証します。順方向に順序付けされている証明書で実行できない検証チェックは延期され、パスが構築されてから(アプリケーションに返される前に)そのパス上で実行されます。

CertPathValidatorと同様に、jdk.certpath.disabledAlgorithmsセキュリティ・プロパティを使用して、安全とみなされない暗号化アルゴリズムを除外できます。

2つ以上の潜在的な証明書が検出され、指定された制約を満たすパスが見つかる可能性がある場合、実装は、次の条件を使って、証明書の優先順位を設定します(次の例では、TrustAnchorの識別名"ou=D,ou=C,o=B,c=A"が指定されていると仮定)。

1. 証明書の発行者DNが、指定されたTrustAnchorのうち1つのDNと一致(例: issuerDN = "ou=D,ou=C,o=B,c=A")。

2. 証明書の発行者DNがTrustAnchorのうち1つのDNの子孫で、アンカーに近いほうから順に順序付けられている(例: issuerDN = "ou=E,ou=D,ou=C,o=B,c=A")。

3. 証明書の発行者DNがTrustAnchorのうち1つのDNの祖先で、アンカーに近いほうから順に順序付けられている(例: issuerDN = "ou=C,o=B,c=A")。

4. 証明書の発行者DNがTrustAnchorのうち1つの同じ名前空間内にあり、アンカーに近いほうから順に順序付けられている(例: issuerDN = "ou=G,ou=C,o=B,c=A")。

5. 証明書の発行者DNが、証明書の主体DNの祖先で、主体に近いほうから順に順序付けられている。

これらのあとには、上記のどの条件も満たさない証明書が続きます。

この実装は、このリリースの「SUN」プロバイダに含まれるLDAPおよびCollection CertStore実装でテストされています。

デバッグのサポートは、java.security.debugプロパティをcertpathに設定すると有効になります。次に例を示します。

java -Djava.security.debug=certpath BuildCertPath

こうすると、追加のデバッグ情報が標準エラーに出力されます。

Collection CertStore

SUNプロバイダでは、CertStoreエンジン・クラスのCollection実装がサポートされています。

Collection CertStore実装は、java.security.cert.Certificateまたはjava.security.cert.CRLのインスタンスであるすべてのオブジェクトを保持できます。

証明書およびCRLは、特定の順序で返されることはなく、複製を含みません。

CRL配布ポイント拡張機能のサポート

CRL配布ポイント拡張機能に対して、サポートが提供されます。これは、デフォルトでは互換性を維持するため無効になっています。システム・プロパティcom.sun.security.enableCRLDPの値をtrueに設定すると、これが有効になります。

trueに設定した場合、SunのPKIX実装は、指定されるCertStoresに加えて証明書のCRL配布ポイント拡張機能の情報を使用して、CRLを検索します。ただし、配布ポイントがX.500識別名、またはldap、httpまたはftp型のURIである必要があります。

注意:
ネットワークとファイアウォールの設定によっては、ネットワーキング・プロキシ・サーバーも構成する必要がある場合があります。

Authority Information Access (AIA)拡張機能のサポート

Authority Information Access拡張機能のcaIssuersアクセス・メソッドに対して、サポートが提供されます。これは、デフォルトでは互換性を維持するため無効になっています。システム・プロパティcom.sun.security.enableAIAcaIssuersの値をtrueに設定すると、これが有効になります。

trueに設定した場合、SunのCertPathBuilder PKIX実装は、指定されるCertStoreに加えて証明書のAIA拡張機能の情報を使用して、発行するCA証明書を検索します。ただし、それがldap、httpまたはftp型のURIである必要があります。

注意:
ネットワークとファイアウォールの設定によっては、ネットワーキング・プロキシ・サーバーも構成する必要がある場合があります。

付録C: OCSPサポート

RFC 2560に定義されているOCSP (On-Line Certificate Status Protocol)のクライアント側サポートが提供されています。

OCSPチェックは、次の5つのセキュリティ・プロパティで制御されます。

プロパティ名

説明

ocsp.enable

このプロパティの値は、trueまたはfalseになる。trueの場合、OCSPチェックは証明書失効チェックの実行中に有効になる。falseまたは設定されていない場合、OCSPチェックは無効になる。

ocsp.responderURL

このプロパティの値は、OCSP応答者の場所を特定するURLである。次に例を示します

ocsp.responderURL=http://ocsp.example.net:80

デフォルトでは、OCSP応答者の場所は、検証される証明書から暗黙的に決定される。RFC 5280に定義されているAuthority Information Access拡張機能が証明書にない場合、またはオーバーライドが必要な場合に、このプロパティが使用される。

ocsp.responderCertSubjectName

このプロパティの値は、OCSP応答者の証明書の主体名である。次に例を示します

ocsp.responderCertSubjectName="CN=OCSP Responder, O=XYZ Corp"

デフォルトでは、OCSP応答者の証明書は、検証される証明書の発行者のものである。このプロパティは、デフォルトが適用されない場合に、OCSP応答者の証明書を特定する。この値はRFC 2253で定義された文字列の識別名で、証明書パスの検証中に取得した証明書セットの中から証明書を特定する。サブジェクト名だけでは証明書を一意に特定できない場合は、ocsp.responderCertIssuerNameとocsp.responderCertSerialNumberの両方のプロパティを代わりに使用する必要がある。このプロパティが設定されている場合は、この2つのプロパティは無視される。

ocsp.responderCertIssuerName

このプロパティの値は、OCSP応答者の証明書の発行者名である。次に例を示します

ocsp.responderCertIssuerName="CN=Enterprise CA, O=XYZ Corp"

デフォルトでは、OCSP応答者の証明書は、検証される証明書の発行者のものである。このプロパティは、デフォルトが適用されない場合に、OCSP応答者の証明書を特定する。この値はRFC 2253で定義された文字列の識別名で、証明書パスの検証中に取得した証明書セットの中から証明書を特定する。このプロパティが設定されている場合、ocsp.responderCertSerialNumberプロパティも設定されている必要がある。ocsp.responderCertSubjectNameプロパティが設定されている場合、このプロパティは無視される。

ocsp.responderCertSerialNumber

このプロパティの値は、OCSP応答者の証明書のシリアル番号である。次に例を示す

ocsp.responderCertSerialNumber=2A:FF:00

デフォルトでは、OCSP応答者の証明書は、検証される証明書の発行者のものである。このプロパティは、デフォルトが適用されない場合に、OCSP応答者の証明書を特定する。この値は16進数の文字列(コロンまたはスペースで区切られている)で、証明書パスの検証中に取得した証明書セットの中から証明書を特定する。このプロパティが設定されている場合、ocsp.responderCertIssuerNameプロパティも設定されている必要がある。ocsp.responderCertSubjectNameプロパティが設定されている場合、このプロパティは無視される。

これらのプロパティは、Javaランタイムの<java_home>/conf/security/java.securityファイル内で静的に設定されるか、またはjava.security.Security.setProperty()メソッドを使用して動的に設定されます。

デフォルトでは、OCSPチェックは有効ではありません。ocsp.enableプロパティをtrueに設定すると有効になります。その他のプロパティは、オプションで使用できます。OCSPチェックは、失効チェックも有効になっている場合にのみ有効になります。失効チェックは、PKIXParameters.setRevocationEnabled()メソッドを使用して有効にできます。

OCSPチェックは、失効チェック中に証明書失効リスト(CRL)と連動して機能します。次は、OCSPとCRLの相互作用のサマリーです。CRLでのフェイルオーバーは、OCSPに問題が発生した場合にかぎり、発生します。OCSP応答者が、証明書が取り消されたことまたは取り消されていないことを確認した場合は、フェイルオーバーは発生しません。

PKIXParameters RevocationEnabled (default=true)

ocsp.enable (default=false)

動作

true

true

OCSPを使用した失効チェック、CRLを使用したフェイルオーバー

true

false

CRLを使用した失効チェックのみ

false

true

失効チェックなし

false

false

失効チェックなし

付録D: JdkLDAPプロバイダでのCertPath実装

JdkLDAPプロバイダでは、CertStoreエンジン・クラスのLDAP実装がサポートされています。

LDAP CertStore

LDAP CertStore実装は、RFC 2587で定義されているLDAPスキーマを使って、証明書およびCRLをLDAPディレクトリから取得します。

LDAPSchemaサービス属性は「RFC2587」に設定されます。

実装は、主体の値、発行者、およびX509CertSelectorで指定されているbasicConstraints選択条件によって、証明書を別の位置からフェッチします。実装は、次の操作をできるだけ多く実行します。

1. Subject non-null, basicConstraints <= -1
主体DNの「userCertificate」属性で証明書を検索します。

2. Subject non-null, basicConstraints >= -1
主体DNの「crossCertificatePair」属性の順方向の要素内にあり、かつ主体の「caCertificate」属性内にある証明書を検索します。

3. Issuer non-null, basicConstraints >= -1
発行者DNの「crossCertificatePair」属性の逆方向要素内にあり、かつ発行者DNの「caCertificate」属性内にある証明書を検索します。

どの場合も、証明書は検索結果のコレクションに追加する前に、X509CertSelector.match()を使ってチェックされます。

上記に指定した条件のどれも当てはまらない場合、提供された条件を使って証明書をフェッチできなかったことを示す例外がスローされます。1つ以上の条件が適用されたとしても、ディレクトリに証明書がない場合、返されるCollectionは空のままであることに注意してください。

実装は、X509CRLSelectorクラスのsetCertificateChecking、addIssuerName、またはsetIssuerNamesメソッドで指定された発行者DNからCRLをフェッチします。いずれかのメソッドを使って発行者DNを指定しないと、実装により、指定された基準ではCRLをフェッチできなかったことを示す例外がスローされます。それ以外の場合、CRLは次のように検索されます。

1. 最初に、実装により発行者名のリストが作成されます。setCertificateCheckingメソッドに証明書が指定されている場合、その証明書の発行者が使用されます。それ以外の場合、addIssuerNameまたはsetIssuerNamesメソッドで指定された発行者名が使用されます。

2. 次に、発行者名のリストに対して反復処理が行われます。これは、発行者名ごとに、まず発行者のauthorityRevocationList属性内を検索し、一致するCRLが見つからなかった場合は、発行者のcertificateRevocationList属性内を検索します。ただし、setCertificateCheckingメソッドに指定された証明書から発行者名を取得した場合は例外であり、指定された証明書がCA証明書である場合は、発行者のauthorityRevocationList属性のみをチェックします。

3. すべてのCRLは検索結果のコレクションに追加する前に、X509CRLSelector.match()を使ってチェックされます。

4. 選択基準を満たすCRLが見つからない場合、空のCollectionが返されます。

キャッシュ

デフォルトでは、各LDAP CertStoreインスタンスは、検索を最大30秒キャッシュします。キャッシュの有効期間は、システム・プロパティsun.security.certpath.ldap.cache.lifetimeの値を秒単位で設定することで変更できます。値を0にすると、キャッシュは完全に無効になります。-1にすると、有効期間が無期限になります。

付録E: 暗号化アルゴリズムの無効化

jdk.certpath.disabledAlgorithmsセキュリティ・プロパティには、弱いか壊れているとみなされた暗号化アルゴリズムおよび鍵サイズ制約のリストが含まれています。これらのアルゴリズムまたは鍵サイズのいずれかを含む証明書およびその他のデータ(CRL、OCSPResponses)は、証明書パスの構築および検証中にブロックされます。このプロパティは、OracleのPKIX実装によって使用されます。他の実装では、検証および使用されない場合があります。

jdk.certpath.disabledAlgorithmsプロパティの正確な構文は、java.securityファイルで示されています。Java SE 9での、このプロパティのデフォルト値は次のとおりです。

jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \
RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224

この構文の説明は次のとおりです。

MD2

MD2ベースのアルゴリズムはブロックされます。
たとえば、MD2withRSA署名アルゴリズムを使用して署名された証明書、CRLまたはOCSPResponseです。

MD5

MD5ベースのアルゴリズムはブロックされます。
たとえば、MD5withRSA署名アルゴリズムを使用して署名された証明書、CRLまたはOCSPResponseです。

SHA1 jdkCA & usage TLSServer

cacertsキーストアに事前インストール済の信頼できるアンカーに連鎖する、TLSサーバーの認証に使用されるすべてのSHA1証明書です。JEP 288を参照してください。

RSA keySize < 1024

1024ビットより小さいRSA鍵はブロックされます。
たとえば、768ビットのRSA公開鍵を使用する証明書です。

DSA keySize < 1024

1024ビットより小さいDSA鍵はブロックされます。
たとえば、512ビットのDSA公開鍵を使用する証明書です。

EC keySize < 224

224ビットより小さいEC鍵はブロックされます。
たとえば、160ビットのEC公開鍵を使用する証明書です。

管理者またはユーザーは、その他のセキュリティ要件に対応するためにjdk.certpath.disabledAlgorithmsプロパティの値を変更できます。ただし、現在のアルゴリズムまたは鍵サイズを削除することはお薦めできません。

10 Java SASL APIプログラミングおよび配備ガイド

Simple Authentication and Security Layer (SASL)プロトコルは、認証を指定し、オプションでクライアント・アプリケーションとサーバー・アプリケーションの間のセキュリティ層の確立を指定します。

SASLは、認証、およびクライアント・アプリケーションとサーバー・アプリケーションの間のセキュリティ層の確立(オプション)を行うプロトコルを指定するインターネット標準(RFC 2222)です。SASLは認証データの交換方法を定義しますが、そのデータの内容は指定しません。認証データの内容およびセマンティックスを指定する特定の認証メカニズムが適合できるフレームワークです。

SASLは、Lightweight Directory Access Protocol、バージョン3 (LDAP v3)やInternet Message Access Protocol、バージョン4 (IMAP v4)などのプロトコルによって、プラガブル認証を有効にするために使用されます。認証方式をプロトコルに固定するかわりに、LDAP v3およびIMAP v4はSASLを使用して認証を実行するため、様々なSASLメカニズムによる認証が可能になります。

さまざまなセキュリティ・レベルおよび配備シナリオ用にインターネット・コミュニティによって定義された、多数の標準SASLメカニズムがあります。その範囲はセキュリティなし(匿名認証など)から高セキュリティ(Kerberos認証など)にいたるまで、さまざまなレベルがあります。

Java SASL API

Java SASL APIは、SASLメカニズムを使用するアプリケーション用のクラスおよびインタフェースを定義します。これは、メカニズムに依存しないものとして定義されています。APIを使用するアプリケーションを、特定のSASLメカニズムの使用に固定する必要はありません。このAPIは、クライアントとサーバーの両方のアプリケーションをサポートしています。アプリケーションは、受動的な辞書攻撃の影響を受けやすいかどうか、および匿名認証を受け入れるかどうかなど、必要なセキュリティ機能に基づいて使用するメカニズムを選択できます。

Java SASL APIでは、開発者は独自のカスタムSASLメカニズムを使用することもできます。SASLメカニズムは、Java暗号化アーキテクチャ(JCA)リファレンス・ガイド(JCA)を使用してインストールされます。

SASLをいつ使用するか

SASLは、プラガブルな認証とセキュリティ層をネットワーク・アプリケーションに提供します。Java SEには、Java Secure Socket Extension (JSSE)リファレンス・ガイドやJava Generic Security Serviceなど、同様の機能を提供するその他の機能があります。

Java GSSは、Generic Security Service Application Programming Interface GSS-API用のJava言語バインディングです。

Java SEでは、このAPIの下で現在サポートされているメカニズムは、Kerberos v5のみです。

JSSEおよびJava GSSと比較すると、SASLは相対的に軽量であり、最近のプロトコルの中で一般的です。一般的かつ軽量な(インフラストラクチャ・サポートの点で) SASLメカニズムがいくつか定義されているという利点もあります。一方、主要なJSSEおよびJava GSSメカニズムは相対的に重量のあるメカニズムを持ち、より精巧なインフラストラクチャ(それぞれ公開鍵インフラストラクチャおよびKerberos)を必要とします。

SASL、JSSEおよびJava GSSは、しばしば併用されます。たとえば、一般的なパターンでは、アプリケーションはセキュアなチャネルを確立するためにJSSEを使用し、クライアントのユーザー名/パスワードベースの認証にはSASLを使用します。GSS-APIメカニズムの上位層となるSASLメカニズムもあります。一般的な例は、LDAPで使用されるSASL GSS-API/Kerberos v5メカニズムです。

プロトコルをゼロから定義および構築する場合を除き、どのAPIを使用するかを決定する場合の最大要因は、多くの場合プロトコル定義です。たとえば、LDAPおよびIMAPはSASLを使用するように定義されているので、これらのプロトコルに関係するソフトウェアはJava SASL APIを使用するようにしてください。Kerberosアプリケーションおよびサービスを構築する場合、使用するAPIはJava GSSです。プロトコルとしてSSL/TLSを使用するアプリケーションおよびサービスを構築する場合、使用するAPIはJSSEです。

Java SASL APIの概要

SASLメカニズムをインストールおよび選択する方法

SunSASLプロバイダ

SASLセキュリティ・プロバイダの実装

Java SASL APIの概要

Java SASL APIには、インタフェースSaslClientおよびSaslServer があります。これらは、クライアント側APIとサーバー側APIを表します。

SASLはチャレンジ応答プロトコルです。サーバーはクライアントにチャレンジを発行し、クライアントはチャレンジに基づいて応答を送信します。この交換は、サーバーが充足してチャレンジを発行しなくなるまで続きます。これらのチャレンジおよび応答は、任意の長さのバイナリ・トークンです。カプセル化を行うプロトコル(LDAPやIMAPなど)は、これらのトークンのエンコードおよび交換方法を指定します。たとえば、LDAPは、SASLトークンがLDAPバインド要求および応答にどのようにカプセル化されるかを指定します。

Java SASL APIは、この相互作用と使用方法のスタイルに従ってモデル化されています。インタフェースSaslClientおよびSaslServerがあり、これらはそれぞれクライアント側およびサーバー側のメカニズムを表します。アプリケーションはチャレンジと応答を表すバイト配列によって、メカニズムと相互に作用します。サーバー側のメカニズムは、充足されるまでチャレンジの発行と応答の処理を繰り返します。一方、クライアント側のメカニズムは、サーバーが充足されるまでチャレンジの評価と応答の発行を繰り返します。メカニズムを使用しているアプリケーションが、それぞれの反復を制御します。つまり、それは、チャレンジまたは応答をプロトコル・パケットから抽出してメカニズムに渡し、メカニズムから返された応答またはチャレンジをプロトコル・パケットに入れて、ピアに送信します。

メカニズムの作成

メカニズムに入力を渡す

メカニズムの使用

ネゴシエーション済みのセキュリティ層の使用

メカニズムの作成

SASLメカニズムを使用するクライアント・コードおよびサーバー・コードは、特定のメカニズムを使用するように固定はされていません。SASLを使用する多くのプロトコルでは、サーバーは、サポートするSASLメカニズムのリストを(静的または動的に)通知します。クライアントは、セキュリティ要件に基づいて、これらの1つを選択します。

Saslクラスが、SaslClientおよびSaslServerのインスタンスの作成に使用されます。次に、使用可能なSASLメカニズムのリストを使用して、アプリケーションがどのようにSASLクライアント・メカニズムを作成するかの例を示します。

String[] mechanisms = new String[]{"DIGEST-MD5", "PLAIN"};
SaslClient sc = Sasl.createSaslClient(mechanisms, authzid, protocol,
serverName, props, callbackHandler);

プラットフォームによってサポートされるメカニズムの可用性およびパラメータで提供されるその他の構成情報に基づいて、Java SASLフレームワークは一覧されているメカニズムの1つを選択し、SaslClientのインスタンスを返します。

選択されたメカニズムの名前は、通常、アプリケーション・プロトコルによってサーバーに転送されます。メカニズム名を受信すると、サーバーは、クライアントから送信された応答を処理するために、対応するSaslServerオブジェクトを作成します。次に、サーバーがSaslServerのインスタンスを作成する方法の例を示します。

SaslServer ss = Sasl.createSaslServer(mechanism, protocol,
myName, props, callbackHandler);

メカニズムに入力を渡す

このAPIは、アプリケーションがメカニズムに入力を渡すための3つの方法を提供します。

Java SASL APIは汎用フレームワークなので、多数の異なるタイプのメカニズムに対応できる必要があります。各メカニズムは入力によって初期化される必要があり、先に進むために入力を必要とする場合があります。このAPIは、アプリケーションがメカニズムに入力を渡すための、次の3つの手段を提供します。

1. 共通入力パラメータ アプリケーションはあらかじめ定義されたパラメータを使用して、SASL仕様によって定義された、メカニズムによって共通に必要とされる情報を提供します。SaslClientのメカニズムの場合、入力パラメータは承認ID、プロトコルIDおよびサーバー名です。SaslServerのメカニズムの場合、共通入力パラメータはプロトコルIDおよび(完全修飾された固有の)サーバー名です。

2. プロパティ・パラメータ アプリケーションは、プロパティ値(文字列以外の場合もある)へのプロパティ名のマッピングであるプロパティ・パラメータを使用して、構成情報を提供します。Java SASL APIは、Sasl.QOP、Sasl.STRENGTHおよびSasl.MAX_BUFFERなど、いくつかの標準プロパティを定義します。パラメータは、特定のメカニズムに固有の非標準のプロパティを渡す場合にも使用できます。

3. コールバック アプリケーションは、Interface CallbackHandlerパラメータを使用して、事前に定めることができないかメカニズム間で共通でない可能性がある入力を提供します。メカニズムが入力データを必要とする場合、アプリケーションによって渡されたコールバック・ハンドラを使用してデータを収集します。アプリケーションのエンド・ユーザーから収集する場合もあります。たとえば、メカニズムは、アプリケーションのエンド・ユーザーに名前とパスワードを指定するように要求する場合があります。
メカニズムは、javax.security.auth.callbackパッケージで定義されたコールバックを使用できます。これらは、認証を実行するアプリケーションを構築するのに便利なジェネリック・コールバックです。メカニズムは、レルムおよび認証情報を収集するためのコールバックなどの、SASL固有のコールバック、または(標準化されていない)メカニズム固有のコールバックを必要とする場合もあります。アプリケーションは様々なメカニズムに対応できるようにする必要があります。したがって、そのコールバック・ハンドラは、メカニズムが要求する可能性があるすべてのコールバックに対応できることが必要です。これは、任意のメカニズムに対しては一般的には不可能ですが、通常は配備および使用されているメカニズムは数が限定されているため、実現可能です。

メカニズムの使用

アプリケーションがメカニズムを作成すると、アプリケーションはメカニズムを使用してSASLトークンを取得し、ピアと交換します。

一部のプロトコルでは、クライアントは初期応答を持つメカニズム用に、要求にオプションの初期応答を付加できます。この機能は、認証に必要なメッセージ交換の数を減らすために使用できます。例10-1では、クライアントで認証のためにどのようにSaslClientが使用されるかを示します。
クライアント・アプリケーションはメカニズム(sc)を使用して認証の各ステップを繰り返し、サーバーから取得したチャレンジを評価してサーバーに応答を返信します。クライアント・アプリケーションはメカニズムまたはアプリケーション・レベルのプロトコルが認証が完了したことを示すまで、またはメカニズムがチャレンジを評価できない場合に、このサイクルを続行します。メカニズムがチャレンジを評価できない場合、エラーを示す例外をスローし、認証を終了します。完了状態に関してメカニズムとプロトコルの間で不一致がある場合は、認証交換に障害があることを示す可能性があるため、エラーとして処理します。

例10-2では、サーバーでどのようにSaslServerが使用される可能性があるかを示します。

サーバー・アプリケーションはクライアントの応答をメカニズム(ss)に渡して処理することによって、認証の各ステップを繰り返します。応答が不正な場合、サーバーがエラーを報告して認証を終了できるように、メカニズムはSaslExceptionをスローしてエラーを示します。応答が正しい場合、メカニズムはクライアントに送信されるチャレンジ・データを返し、認証が完了したかどうかを示します。チャレンジ・データは「成功」を示すデータを伴うことができます。これは、たとえば、ネゴシエーションされた状態をクライアントに完結させるために使用される場合があります。

例10-1 認証にSASLクライアントを使用するサンプル・コード

// Get optional initial response
byte[] response =
(sc.hasInitialResponse() ? sc.evaluateChallenge(new byte[]) : null);

String mechanism = sc.getMechanismName();

// Send selected mechanism name and optional initial response to server
send(mechanism, response);

// Read response
msg = receive();
while (!sc.isComplete() && (msg.status == CONTINUE || msg.status == SUCCESS)) {
// Evaluate server challenge
response = sc.evaluateChallenge(msg.contents);

if (msg.status == SUCCESS) {
// done; server doesn't expect any more SASL data
if (response != null) {
throw new IOException(
"Protocol error: attempting to send response after completion");
}
break;
} else {
send(mechanism, response);
msg = receive();
}

例10-2 認証にSASLサーバーを使用するサンプル・コード

// Read request that contains mechanism name and optional initial response
msg.receive();

// Obtain a SaslServer to perform authentication
SaslServer ss = Sasl.createSaslServer(msg.mechanism,
protocol, myName, props, callbackHandler);

// Perform authentication steps until done
while (!ss.isComplete()) {
try {
// Process response
byte[] challenge = sc.evaluateResponse(msg.contents);

if (ss.isComplete()) {
send(mechanism, challenge, SUCCESS);
} else {
send(mechanism, challenge, CONTINUE);
msg.receive();
}
} catch (SaslException e) {
send(ERROR);
sc.dispose();
break;
}
}

ネゴシエーション済みのセキュリティ層の使用

認証のみをサポートするSASLメカニズムだけでなく、認証後にネゴシエーション済みのセキュリティ層の使用をサポートするSASLメカニズムもあります。セキュリティ層の機能はアプリケーションがSSL/TLSなどのほかの手段を使用してピアと安全に通信する場合は、使用されない場合がよくあります。

セキュリティ層がネゴシエーション済みの場合、ピアとの後続の通信はすべてセキュリティ層を使用して発生します。セキュリティ層がネゴシエーション済かどうかを判別するには、ネゴシエーション済のSasl.QOPをメカニズムから取得します。次に、セキュリティ層がネゴシエーション済みかどうかを判別する方法の例を示します。

String qop = (String) sc.getNegotiatedProperty(Sasl.QOP);
boolean hasSecurityLayer = (qop != null &&
(qop.equals("auth-int") || qop.equals("auth-conf")));

Sasl.QOPプロパティが整合性または機密性、あるはその両方がネゴシエーション済みであることを示している場合、セキュリティ層はネゴシエーション済みです。

ネゴシエーション済の層を使用してピアと通信するには、アプリケーションは最初にwrapメソッドを使用し、ピアに送信されるデータをエンコードして、ラップされたバッファを生成します。次に、ラップされたバッファ内のオクテットの数を表す長さフィールドとそれに続いてラップされたバッファの内容をピアに転送します。オクテットのストリームを受信するピアは長さフィールドを除くバッファをunwrapに渡し、ピアによって送信された復号化されたバイトを取得します。このプロトコルの詳細はRFC 2222で説明されています。例10-3では、クライアント・アプリケーションでセキュリティ層を使用してアプリケーション・データがどのように送受信されるかを示します。

例10-3 SASLクライアントのデータ送受信のサンプル・コード

// Send outgoing application data to peer
byte[] outgoing = ...;
byte[] netOut = sc.wrap(outgoing, 0, outgoing.length);

send(netOut.length, netOut);   // send to peer

// Receive incoming application data from peer
byte[] netIn = receive();      // read length and ensuing bytes from peer

byte[] incoming = sc.unwrap(netIn, 0, netIn.length);

SASLメカニズムをインストールおよび選択する方法

SASLメカニズムの実装は、SASLセキュリティ・プロバイダによって提供されます。各プロバイダは、1つ以上のSASLメカニズムをサポートでき、JCAを使用して登録されます。

J2SE 5では、デフォルトでSunSASLプロバイダがJCAプロバイダとして自動的に登録されます。このプロバイダを削除するか、JCAプロバイダとしての優先順位を変更するには、次の行を変更します。

security.provider.7=com.sun.security.sasl.Provider

Javaセキュリティ・プロパティ・ファイル(java-home/conf/security/java.security)内。

SASLプロバイダを追加または削除するには、セキュリティ・プロパティ・ファイルで対応する行を追加または削除します。たとえば、SASLプロバイダを追加し、そのメカニズムが、SunSASLプロバイダによって実装されている同じメカニズムよりも優先して選択されるようにする場合は、セキュリティ・プロパティ・ファイルに、より小さい番号で行を追加します。

security.provider.7=com.example.MyProvider
security.provider.8=com.sun.security.sasl.Provider

この場合、java.security.Securityクラスを使用して、プログラムで独自のプロバイダを追加することもできます。たとえば、次のサンプル・コードは、使用可能なSASLセキュリティ・プロバイダのリストにcom.example.MyProviderを登録します。

Security.addProvider(new com.example.MyProvider());

アプリケーションが1つ以上のメカニズム名を指定してSASLメカニズムを要求すると、SASLフレームワークは、登録済みプロバイダのリストを順に検索して、そのメカニズムをサポートする登録済みのSASLプロバイダを探します。次に、プロバイダは、要求されたメカニズムがSasl内の選択ポリシー・プロパティに一致するかどうかを判別し、一致する場合は、メカニズムの実装を返します。

選択ポリシー・プロパティは特定の攻撃の受けやすさなど、メカニズムのセキュリティ面を指定します。これらは、その実装というよりもメカニズム(定義)の特性であるため、すべてのプロバイダは特定のメカニズムについて同じ結果になるはずです。たとえば、PLAINメカニズムは、どのように実装されるかにかかわらず、平文攻撃を受けやすくなります。選択ポリシー・プロパティが指定されない場合、メカニズムの選択に制限はありません。これらのプロパティを使用して、アプリケーションは、実行環境に配備される可能性があるメカニズムについて、適していないものを使用しないようにすることができます。たとえば、平文攻撃を受けやすいメカニズムの使用を許可しない場合、アプリケーションは次のサンプル・コードを使用する場合があります。

Map props = new HashMap();
props.add(Sasl.POLICY_NOPLAINTEXT, "true");
SaslClient sc = Sasl.createSaslClient(mechanisms,
authzid, protocol, serverName, props, callbackHandler);

SunSASLプロバイダ

SunSASLプロバイダのクライアント・メカニズムおよびサーバー・メカニズムについての情報です。

SunSASLプロバイダは、次のクライアント・メカニズムおよびサーバー・メカニズムをサポートします。

- クライアント・メカニズム

- PLAIN (RFC 2595)。このメカニズムは、クリアテキスト・ユーザー名/パスワード認証をサポートしています。

- CRAM-MD5 (RFC 2195)。このメカニズムは、ハッシュされたユーザー名/パスワード認証方式をサポートしています。

- DIGEST-MD5 (RFC 2831)。HTTP Digest AuthenticationをSASLメカニズムとして使用する方法を定義します。

- EXTERNAL (RFC 2222)。このメカニズムは、TLSやIPsecなどの外部チャネルから認証情報を取得します。

- サーバー・メカニズム

- CRAM-MD5

- DIGEST-MD5

SunSASLプロバイダのクライアント・メカニズム

SunSASLプロバイダでは、LDAP、IMAPおよびSMTPなどの一般的なプロトコルで使用される複数のSASLクライアント・メカニズムがサポートされています。

次の表は、クライアント・メカニズムとそれらに必要な入力の概要です。

表10-1 SunSASLプロバイダのクライアント・メカニズム

クライアント・メカニズム名

パラメータ/入力

コールバック

構成プロパティ

選択ポリシー

CRAM-MD5

承認 ID (デフォルトのユーザー名として)

PasswordCallback

NameCallback

なし

Sasl.POLICY_NOANONYMOUS

Sasl.POLICY_NOPLAINTEXT

DIGEST-MD5

authorization id

protocol id

server name

NameCallback

PasswordCallback

RealmCallback

RealmChoiceCallback

RealmChoiceCallback

Sasl.QOP

Sasl.STRENGTH

Sasl.MAX_BUFFER

Sasl.SERVER_AUTH

"javax.security.sasl.sendmaxbuffer"

"com.sun.security.sasl.digest.cipher"

Sasl.POLICY_NOANONYMOUS

Sasl.POLICY_NOPLAINTEXT

EXTERNAL

authorization id

external channel

なし

なし

Sasl.POLICY_NOPLAINTEXT

Sasl.POLICY_NOACTIVE

Sasl.POLICY_NODICTIONARY

PLAIN

authorization id

NameCallback

PasswordCallback

なし

Sasl.POLICY_NOANONYMOUS

SunSASLプロバイダのこれらのメカニズムを使用するアプリケーションは、必要なパラメータ、コールバック、およびプロパティを提供する必要があります。プロパティには適切なデフォルト値が設定されているため、アプリケーションがデフォルト値をオーバーライドする場合にのみ設定する必要があります。ほとんどのパラメータ、コールバック、およびプロパティはAPIドキュメントで説明されています。次のセクションでは、メカニズム固有の動作、およびAPIドキュメントで説明されていないパラメータについて説明します。

Cram-MD5

Cram-MD5クライアント・メカニズムは、承認IDパラメータが指定された場合、それをNameCallbackのデフォルト・ユーザー名として使用して、アプリケーション/エンドユーザーに認証IDを求めます。それ以外の場合、承認IDはCram-MD5メカニズムによって使用されません。認証IDのみがサーバーと交換されます。

Digest-MD5

Digest-MD5メカニズムは、ダイジェスト認証およびオプションでセキュリティ層を確立する場合に使用されます。セキュリティ層とともに使用するTriple DES、DES、およびRC4 (128、56、および40ビット)の暗号を指定します。Digest-MD5メカニズムは、プラットフォームで使用可能な暗号のみをサポートできます。たとえば、プラットフォームがRC4暗号をサポートしない場合、Digest-MD5メカニズムはその暗号を使用しません。

Sasl.STRENGTHプロパティは、「high」、「medium」、および「low」設定をサポートしており、デフォルトは「high,medium,low」です。暗号は、次のように強度設定にマッピングされています。

表10-2 暗号強度

強度

暗号

暗号ID

high

トリプルDES
RC4 128 ビット

3des rc4

medium

DES
RC4 56 ビット

des rc4-56

low

RC4 40 ビット

rc4-40

特定の強度に複数の選択肢がある場合、選択される暗号は、基盤となるプラットフォームでの暗号の可用性によって決まります。使用する暗号を明示的に指定するには、com.sun.security.sasl.digest.cipherプロパティを、対応する暗号IDに設定します。このプロパティ設定は、Sasl.STRENGTHおよび基盤となるプラットフォームで利用可能な暗号と互換性を持たせてください。たとえば、lowに設定されているSasl.STRENGTHと3desに設定されているcom.sun.security.sasl.digest.cipherには、互換性がありません。com.sun.security.sasl.digest.cipherプロパティには、デフォルトはありません。

「javax.security.sasl.sendmaxbuffer」プロパティは、最大送信バッファ・サイズ(の文字列表現)をバイト単位で指定します。デフォルト値は65536です。実際の最大バイト数は、この数の最小値およびピアの最大受信バッファ・サイズになります。

SunSASLプロバイダのサーバー・メカニズム

SunSASLプロバイダでは、LDAP、IMAPおよびSMTPなどの一般的なプロトコルで使用される複数のSASLサーバー・メカニズムがサポートされています。

次の表は、サーバー・メカニズムとそれらに必要な入力の概要です。

表10-3 サーバー・メカニズム

サーバー・メカニズム名

パラメータ/入力

コールバック

構成プロパティ

選択ポリシー

CRAM-MD5

server name

AuthorizeCallback

NameCallback

PasswordCallback

なし

Sasl.POLICY_NOANONYMOUS

Sasl.POLICY_NOPLAINTEXT

DIGEST-MD5

protocol id
server name

AuthorizeCallback

NameCallback

PasswordCallback

RealmCallback

Sasl.QOP

Sasl.STRENGTH

Sasl.MAX_BUFFER

"javax.security.sasl.sendmaxbuffer"

"com.sun.security.sasl.digest.realm"

"com.sun.security.sasl.digest.utf8"

Sasl.POLICY_NOANONYMOUS

Sasl.POLICY_NOPLAINTEXT

SunSASLプロバイダのこれらのメカニズムを使用するアプリケーションは、必要なパラメータ、コールバック、およびプロパティを提供する必要があります。プロパティには適切なデフォルト値が設定されているため、アプリケーションがデフォルト値をオーバーライドする場合にのみ設定する必要があります。

サーバー・メカニズムのすべてのユーザーには、AuthorizeCallbackを処理するコールバック・ハンドラが必要です。これは、要求された承認IDに代わって認証済みのユーザーが操作できるかどうかを判別するため、および承認されたユーザーの正規化された名前を取得するために(正規化が適用可能な場合)、メカニズムによって使用されます。

ほとんどのパラメータ、コールバック、およびプロパティはAPIドキュメントで説明されています。次のセクションでは、メカニズム固有の動作、およびAPIドキュメントで説明されていないパラメータについて説明します。

Cram-MD5

Cram-MD5サーバー・メカニズムはNameCallbackおよびPasswordCallbackを使用して、SASLクライアントの応答の検証に必要なパスワードを取得します。コールバック・ハンドラは、パスワードを取得するための鍵としてNameCallback.getDefaultName()を使用します。

Digest-MD5

Digest-MD5サーバー・メカニズムはRealmCallback、NameCallbackおよびPasswordCallbackを使用して、SASLクライアントの応答の検証に必要なパスワードを取得します。コールバック・ハンドラは、パスワードを取得するための鍵としてRealmCallback.getDefaultText()およびNameCallback.getDefaultName()を使用します。

「javax.security.sasl.sendmaxbuffer」プロパティは、最大送信バッファ・サイズ(の文字列表現)をバイト単位で指定します。デフォルト値は65536です。実際の最大バイト数は、この数の最小値およびピアの最大受信バッファ・サイズになります。

「com.sun.security.sasl.digest.realm」プロパティは、サーバーがサポートするレルムの名前を空白で区切ったリストを指定するために使用されます。このリストは、チャレンジの一部としてクライアントに送信されます。このプロパティが設定されていない場合、デフォルトのレルムはサーバーの名前です(パラメータとして指定)。

「com.sun.security.sasl.digest.utf8」プロパティは、使用する文字エンコーディングを指定するために使用されます。「true」はUTF-8エンコーディングを使用することを意味し、「false」はISO Latin 1 (ISO-8859-1)を使用することを意味します。デフォルトは「true」です。

デバッグおよびモニタリング

SunSASLプロバイダとJdkSASLプロバイダは、ロギングAPIを使用して、実装のログを出力します。この出力は、ロギング構成ファイルおよびプログラム上のAPI (java.util.logging)を使用して制御できます。

SunSASLプロバイダによって使用されるロガー名は、javax.security.saslです。

次に、SunSASLプロバイダのFINESTロギング・レベルを有効にするサンプル・ロギング構成ファイルを示します。

javax.security.sasl.level=FINEST
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=FINEST

次の表に、メカニズムおよびそれらが生成するロギング出力を示します。

表10-4 ロギング出力

メカニズム

ロギング・レベル

ログ記録される情報

CRAM-MD5

FINE

構成プロパティ。チャレンジおよび応答メッセージ

DIGEST-MD5

INFO

エンコーディングの問題のために破棄されたメッセージ (不一致のMAC、不正なパディングなど)

DIGEST-MD5

FINE

構成プロパティ。チャレンジおよび応答メッセージ

DIGEST-MD5

FINER

チャレンジおよび応答メッセージに関するより詳細な情報

DIGEST-MD5

FINEST

セキュリティ層で交換されるバッファ

GSSAPI

FINE

構成プロパティ。チャレンジおよび応答メッセージ

GSSAPI

FINER

チャレンジおよび応答メッセージに関するより詳細な情報

GSSAPI

FINEST

セキュリティ層で交換されるバッファ

JdkSASLプロバイダ

JdkSASLプロバイダのクライアント・メカニズムおよびサーバー・メカニズムについての情報です。

JdkSASLプロバイダでは、次のクライアント・メカニズムおよびサーバー・メカニズムがサポートされています。

- クライアント・メカニズム

- GSSAPI (RFC 2222)。このメカニズムは、認証情報を取得するためにGSSAPIを使用します。Kerberos v5認証をサポートしています。

- サーバー・メカニズム

- GSSAPI (Kerberos v5)

JdkSASLプロバイダのクライアント・メカニズム

JdkSASLプロバイダでは、LDAP、IMAPおよびSMTPなどの一般的なプロトコルで使用されるGSSAPIクライアント・メカニズムがサポートされています。

次の表は、GSSAPIクライアント・メカニズムとそれらに必要な入力の概要です。

表10-5 JdkSASLプロバイダのクライアント・メカニズム

クライアント・メカニズム名

パラメータ/入力

コールバック

構成プロパティ

選択ポリシー

GSSAPI

JAAS Subject
authorization id

protocol id

server name

なし

Sasl.QOP

Sasl.MAX_BUFFER

Sasl.SERVER_AUTH

"javax.security.sasl.sendmaxbuffer"

Sasl.POLICY_NOACTIVE

Sasl.POLICY_NOANONYMOUS

Sasl.POLICY_NOPLAINTEXT

JdkSASLプロバイダのGSSAPIメカニズムを使用するアプリケーションは、必要なパラメータ、コールバックおよびプロパティを提供する必要があります。プロパティには適切なデフォルト値が設定されているため、アプリケーションがデフォルト値をオーバーライドする場合にのみ設定する必要があります。ほとんどのパラメータ、コールバック、およびプロパティはAPIドキュメントで説明されています。次の項では、GSSAPIのその他の動作、およびAPIドキュメントで説明されていないパラメータについて説明します。

GSSAPI

GSSAPIメカニズムは、Kerberos v5認証およびオプションでセキュリティ層の確立に使用されます。メカニズムは呼出し側スレッドのSubjectがクライアントのKerberos資格を含むこと、またはKerberosに暗黙的にログインすることによって資格が取得されることを想定しています。クライアントのKerberos資格を取得するには、Java Authentication and Authorization Service (JAAS)を使用し、Kerberosログイン・モジュールを使用してログインします。詳細および例は、JAASおよびJava GSS-APIチュートリアルを参照してください。JAAS認証を使用してKerberos資格を取得した後で、SASL GSSAPIメカニズムを使用するコードをdoAsまたはdoAsPrivileged内に配置します。

LoginContext lc = new LoginContext("JaasSample", new TextCallbackHandler());
lc.login();
lc.getSubject().doAs(new SaslAction());

class SaslAction implements java.security.PrivilegedAction {
public class run() {
...
String[] mechanisms = new String[]{"GSSAPI"};
SaslClient sc = Sasl.createSaslClient(mechanisms,
authzid, protocol, serverName, props, callbackHandler);
...
}
}

JAASプログラミングを明示的に行わずにKerberos資格を取得するには、JAASおよびJava GSS-APIチュートリアルを参照してください。この方法を使用する場合は、コードをdoAsまたはdoAsPrivileged内にラップする必要はありません

javax.security.sasl.sendmaxbufferプロパティは、最大送信バッファ・サイズ(の文字列表現)をバイト単位で指定します。デフォルト値は65536です。実際の最大バイト数は、この数の最小値およびピアの最大受信バッファ・サイズになります。

JdkSASLプロバイダのサーバー・メカニズム

JdkSASLプロバイダでは、LDAP、IMAPおよびSMTPなどの一般的なプロトコルで使用されるGSSAPIメカニズムがサポートされています。

次の表は、GSSAPIサーバー・メカニズムとそれらに必要な入力の概要です。

表10-6 サーバー・メカニズム

サーバー・メカニズム名

パラメータ/入力

コールバック

構成プロパティ

選択ポリシー

GSSAPI

Subject

protocol id

server name

AuthorizeCallback

Sasl.QOP

Sasl.MAX_BUFFER

"javax.security.sasl.sendmaxbuffer"

Sasl.POLICY_NOACTIVE

Sasl.POLICY_NOANONYMOUS

Sasl.POLICY_NOPLAINTEXT

JdkSASLプロバイダのGSSAPIメカニズムを使用するアプリケーションは、必要なパラメータ、コールバックおよびプロパティを提供する必要があります。プロパティには適切なデフォルト値が設定されているため、アプリケーションがデフォルト値をオーバーライドする場合にのみ設定する必要があります。

サーバー・メカニズムのすべてのユーザーには、AuthorizeCallbackを処理するコールバック・ハンドラが必要です。これは、要求された承認IDに代わって認証済のユーザーが操作できるかどうかを判別するため、および承認されたユーザーの正規化された名前を取得するために(正規化が適用可能な場合)、メカニズムによって使用されます。

ほとんどのパラメータ、コールバック、およびプロパティはAPIドキュメントで説明されています。次の項では、GSSAPIメカニズム固有の動作、およびAPIドキュメントで説明されていないパラメータについて説明します。

GSSAPI

GSSAPIメカニズムは、Kerberos v5認証およびオプションでセキュリティ層の確立に使用されます。メカニズムは呼出し側スレッドのSubjectがクライアントのKerberos資格を含むこと、またはKerberosに暗黙的にログインすることによって資格が取得されることを想定しています。クライアントのKerberos資格を取得するには、Java Authentication and Authorization Service (JAAS)を使用し、Kerberosログイン・モジュールを使用してログインします。詳細および例は、JAASおよびJava GSS-APIチュートリアルを参照してください。JAAS認証を使用してKerberos資格を取得した後で、SASL GSSAPIメカニズムを使用するコードをdoAsまたはdoAsPrivileged内に配置します。

LoginContext lc = new LoginContext("JaasSample", new TextCallbackHandler());
lc.login();
lc.getSubject().doAs(new SaslAction());

class SaslAction implements java.security.PrivilegedAction {
public class run() {
...
String[] mechanisms = new String[]{"GSSAPI"};
SaslClient sc = Sasl.createSaslClient(mechanisms,
authzid, protocol, serverName, props, callbackHandler);
...
}
}

JAASプログラミングを明示的に行わずにKerberos資格を取得するには、JAASおよびJava GSS-APIチュートリアルを参照してください。この方法を使用する場合は、コードをdoAsまたはdoAsPrivileged内にラップする必要はありません

javax.security.sasl.sendmaxbufferプロパティは、最大送信バッファ・サイズ(の文字列表現)をバイト単位で指定します。デフォルト値は65536です。実際の最大バイト数は、この数の最小値およびピアの最大受信バッファ・サイズになります。

SASLセキュリティ・プロバイダの実装

SASLセキュリティ・プロバイダを実装する手順です。

SASLセキュリティ・プロバイダの実装には、次の3つの基本的な手順があります。

1. SaslClientまたはSaslServerインタフェースを実装するクラスを記述します。

これには、SASLメカニズムの実装を提供することが含まれます。クライアント・メカニズムを実装するには、SaslClientインタフェースで宣言されたメソッドを実装する必要があります。同様に、サーバー・メカニズムの場合は、SaslServerインタフェースで宣言されたメソッドを実装する必要があります。この説明では、クラスcom.example.SampleMechClientによって実装されるクライアント・メカニズム「SAMPLE-MECH」の実装を開発するものとします。メカニズムが必要とする入力、およびそれらを実装が収集する方法を決定する必要があります。たとえば、メカニズムがユーザー名/パスワードベースの場合、実装ではその情報をコールバック・ハンドラ・パラメータ経由で収集する必要性が高くなります。

2. クラスのインスタンスを作成するファクトリ・クラス(SaslClientFactoryまたはSaslServerFactoryを実装)を記述します。
これには、com.example.SampleMechClientのインスタンスを作成するファクトリ・クラスの提供が含まれます。ファクトリはSasl.POLICY_*プロパティによって記述されるように、サポートするメカニズムの特性を決定する必要があります。そうすることにより、互換性のあるポリシー・プロパティを使用してAPIユーザーが要求したときに、メカニズムのインスタンスを返すことができます。ファクトリは、また、メカニズムを作成する前にパラメータの妥当性をチェックする場合もあります。この説明では、ファクトリ・クラスの名前をcom.example.MySampleClientFactoryとします。サンプル・ファクトリは1つのメカニズムのみを処理しますが、1つのファクトリは任意の数のメカニズムを処理できます。

3. ファクトリを登録するJCAプロバイダを記述します。

これには、JCAプロバイダの作成が含まれます。JCAプロバイダを作成する手順は、プロバイダの実装および統合までのステップに詳細に説明されています。SASLクライアント・ファクトリは、SaslClientFactory.mechNameという形式のプロパティ名を使用して登録されますが、SASLサーバー・ファクトリは、SaslServerFactory.mechNameという形式のプロパティ名を使用して登録されます。

mechNameは、SASLメカニズムの名前です。これは、SaslClient.getMechanismName()およびSaslServer.getMechanismName()によって返された名前です。この例で、プロバイダが「SAMPLE-MECH」メカニズムを登録する方法を次に示します。

put("SaslClientFactory.SAMPLE-MECH", "com.example.MySampleClientFactory");

1つのSASLプロバイダが、多数のメカニズムを処理する場合があります。そのため、関連するファクトリを登録するputの呼出しが多数ある場合があります。完了したSASLプロバイダは、前述の手順を使用して、アプリケーションで利用できます。

11 XMLデジタル署名

Java XMLデジタル署名API仕様では、XML署名を生成および検証するための標準APIについて説明します。また、XMLデジタル署名APIの固定実装を作成し、Java暗号化アーキテクチャ(JCA)プロバイダの暗号化サービスとして登録する方法について説明します。

XMLデジタル署名APIの概要とチュートリアルでは、APIを使用してXML署名を検証および生成する方法を示します。

Java XMLデジタル署名API仕様

このドキュメントでは、Java XMLデジタル署名API仕様(JSR 105)について説明します。このJSRの目的は、XML署名を生成および検証するための標準のJava APIを定義することです。

この仕様が完成すると、このAPIの機能について説明するリファレンス実装が作成されることになり、この仕様のオペレーションの定義が提供されます。この仕様の実装に対応しているかどうかを確認するためのTechnology Compatibility Kit (TCK)も使用可能になります。Java Community Process 2.1に従ってこれらが必要になります。

JSR 105 APIは、次の2種類のユーザーをターゲットにしています。

- JSR 105 APIを使用してXML署名を生成および検証するJavaプログラマ。

- JSR 105 APIの固定実装を作成し、JCAプロバイダの暗号化サービスとしてこの実装を登録するJavaプログラマ(Providerクラスを参照)。

謝辞

JSR 105エキスパート・グループ:

- Nicolas Catania、HP社

- Donald E.Eastlake 3rd、Motorola社

- Christian Geuer-Pollmann、Apache Software Foundation

- Hans Granqvist、VeriSign社

- Kazuyuki Harada、富士通

- Anthony Ho、DSTC社

- Merlin Hughes、Baltimore Technologies

- Joyce Leung、IBM社

- Gregor Karlinger、IAIK

- Serge Mister、Entrust Technologies

- Takuya Mori、NEC Corporation

- Sean Mullan、Sun Microsystems (共同仕様リード)

- Anthony Nadalin、IBM (共同仕様リード)

- Erwin van der Koogh、Apache Software Foundation

- Chris Yeung、XML Asia

また、次のメンバーにも感謝を捧げます。Sun MicrosystemsのValerie Peng、Vincent Ryan、Sharon Liu、Chok Poh、K. Venugopal Rao.、Paul Rank、Alexey Gavrilov、Bill Situ、Eric Jendrock、Andrew Fan、Manveen Kaur、Tom Amiro、Michael Mi、Dmitri Silaev、Roman Makarchuk、Vanitha Venkatraman, Arkadiy Sutchilin, Scott Fordin、ApacheのVishal Mahajan、IAIKのMartin Centner。

要件

このドキュメントのキーワード「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「MAY」、および「OPTIONAL」は、RFC 2119に説明されているとおりに解釈されます。

1. W3C勧告、XML署名の構文と処理。

- APIを使用して、プログラマはW3C勧告で規定されているSHOULDおよびMUSTのすべての要件を満たすXML署名を生成および検証できる必要があります。

- APIを使用して、W3C勧告で規定されているSHOULDおよびMUSTのすべての要件を満たすAPIの実装を作成できる必要があります。

2. 実装は、W3C Recommendation, XML-Signature XPath Filter Transform 2.0をサポートする必要があります。

3. 実装は、W3C Recommendation, Exclusive XML Canonicalization Version 1.0をサポートする必要があります。

4. DOMに依存しないAPI。APIは、DOMなどの特定のXML表現に依存していない必要があります。DOM、JDOM、dom4jなど、様々なXML処理およびメカニズム表現のためのAPIの実装を作成できる必要があります。

5. 拡張可能なプロバイダ・ベースのAPI。サード・パーティが、暗号化アルゴリズムや変換アルゴリズムの管理や作成、URIの間接参照、およびXMLのオブジェクトの整列化を行う実装を作成およびプラグインすることができる必要があります。

6. デフォルトのXMLメカニズム・タイプDOMのサポート。実装は、デフォルトのメカニズム・タイプDOMを最低限サポートする必要があります。これにより、JSR 105のすべての実装で最低レベルの機能性が保証されます。実装は、ほかのメカニズム・タイプもサポートできます。

7. デフォルトのXMLメカニズム・タイプDOMの相互運用性。APIは、DOM実装を使用するアプリケーションで移植性および相互運用性を保証する必要があります。

8. J2SEの要件。このテクノロジの実装はJ2SE 1.2以降をサポートできますが、最低限J2SEのバージョン1.4をサポートする必要があります。

APIの依存性

- J2SE (JDK) 1.2以降

- W3C DOMレベル2 API.この依存は、パッケージjavax.xml.crypto.domおよびjavax.xml.crypto.dsig.domのクラスで必要となります。

目標にしない事項

1. 非DOM実装のサポート。APIでは非DOM実装を作成できる必要がありますが、DOM以外の実装間での相互運用性を保証することは最初のバージョンの範囲外です。追加の標準サービス・プロバイダ・タイプが今後追加されたり、JSR 105の保守バージョンで必要なAPIの拡張機能が検討される可能性があります。

2. 高レベルのAPIのサポート。プログラマは、JSR 105 APIに基づいて構築される高レベルのAPIを設計して、低レベルの詳細を非表示にしたり、共通のユース・ケースに対処したり、プロファイリングの制限を適用できます。ただし、これらの要件をサポートすることは最初のバージョンの範囲外です。高レベルのAPIは、JSR 105の保守リリースで考慮される可能性があります。

3. ユーザープラガブル・アルゴリズム(javax.xml.crypto.dsig.TransformServiceクラスでサポートされている変換アルゴリズムおよび正規化アルゴリズム以外)のサポート。開発者が完全なJSR 105実装を作成せずに独自のXML署名アルゴリズム実装をプラグインできるようにすることは、有用なことだと考えられますが、JSR 105のこのリリースでは必要ありません。Java SEの後続リリースでは、基礎となるJCA/JCEを拡張して、XMLセキュリティ・アルゴリズム、パラメータ、鍵情報の登録、解析および処理のサポートを追加するソリューションを検討しています。

パッケージの概要

JSR 105 APIは、次の6つのパッケージで構成されています。

- javax.xml.crypto

- javax.xml.crypto.dom

- javax.xml.crypto.dsig

- javax.xml.crypto.dsig.dom

- javax.xml.crypto.dsig.keyinfo

- javax.xml.crypto.dsig.spec

javax.xml.cryptoパッケージには、XML署名の生成やXMLデータの暗号化など、XML暗号化操作を行う場合に使用する一般的なクラスが含まれています。このパッケージ内の主要なクラスには次の2つがあります。KeySelectorクラスを使用すると、開発者は、KeyInfoオブジェクトに含まれている情報を使用して鍵を検出して任意に検証する実装を提供できます。URIDereferencerクラスを使用すると、開発者は、実装を間接参照する独自のURIを作成および指定できます。

javax.xml.crypto.dsigパッケージには、W3C XMLデジタル署名仕様で定義されているコア要素を表すインタフェースが含まれています。最も重要なのはXMLSignatureクラスです。このクラスを使用すると、XMLデジタル署名の署名および検証を行うことができます。ほとんどのXML署名構造または要素は、対応するインタフェースによって表されます(KeyInfo構造を除きます。これは独自のパッケージに含まれており、次の段落で説明します)。これらのインタフェースには、SignedInfo、CanonicalizationMethod、SignatureMethod、Reference、Transform、DigestMethod、XMLObject、Manifest、SignaturePropertyおよびSignaturePropertiesなどがあります。XMLSignatureFactoryクラスは、これらのインタフェースを実装するオブジェクトを作成する場合に使用する抽象ファクトリです。

javax.xml.crypto.dsig.keyinfoパッケージには、KeyInfo、KeyName、KeyValue、X509Data、X509IssuerSerial、RetrievalMethodおよびPGPDataなど、W3C XMLデジタル署名勧告で定義されているほとんどのKeyInfo構造を表すインタフェースが含まれています。KeyInfoFactoryクラスは、これらのインタフェースを実装するオブジェクトを作成する場合に使用する抽象ファクトリです。

javax.xml.crypto.dsig.specパッケージには、ダイジェスト、署名、変換、またはXML署名の処理で使用される正規化アルゴリズム用の入力パラメータを表すインタフェースおよびクラスが含まれています。

最後に、javax.xml.crypto.domおよびjavax.xml.crypto.dsig.domパッケージには、javax.xml.cryptoおよびjavax.xml.crypto.dsigパッケージ用のDOM固有のクラスがそれぞれ含まれています。DOMベースのXMLSignatureFactoryまたはKeyInfoFactory実装を作成または使用する開発者およびユーザーのみが、これらのパッケージを直接使用する必要があります。

サービス・プロバイダ

JSR 105暗号化サービスは、XMLSignatureFactoryおよびKeyInfoFactory抽象クラスの固定実装であり、XML署名やKeyInfo構造を解析、生成および検証するオブジェクトやアルゴリズムを作成します。XMLSignatureFactoryの固定実装は、XML署名に関するW3C勧告に規定されているように、必要なそれぞれのアルゴリズムをサポートしている必要があります。この固定実装は、W3C勧告やほかの仕様に規定されているように、ほかのアルゴリズムをサポートできます。

JSR 105は、JCAプロバイダ・モデルを使用して(Providerクラスを参照)、XMLSignatureFactoryおよびKeyInfoFactory実装を登録およびロードします。

固定実装XMLSignatureFactoryまたはKeyInfoFactoryはそれぞれ、XML署名やKeyInfo構造を解析および生成するときに実装によって内部で使用されるXML処理メカニズムを識別する、特定のXMLメカニズム・タイプをサポートしています。このJSRは標準タイプDOMをサポートしています。新しい標準タイプ(JDOMなど)のサポートは、今後追加される可能性があります。

JSR 105実装は、java.security.Signatureやjava.security.MessageDigestなど、ベースとなるJCAエンジン・クラスを使用して暗号化操作を行う必要があります。

XMLSignatureFactoryおよびKeyInfoFactoryクラス以外に、JSR 105は、変換および正規化アルゴリズム用のサービス・プロバイダ・インタフェースもサポートしています。TransformServiceクラスを使用すると、特定のXMLメカニズム・タイプ用の固有の変換または正規化アルゴリズムの実装を開発およびプラグインできます。TransformServiceクラスは、実装を登録およびロードするときに標準JCAプロバイダ・モデルを使用します。各JSR 105実装はTransformServiceクラスを使用して、生成または検証しているXML署名で変換アルゴリズムおよび正規化アルゴリズムをサポートするプロバイダを見つける必要があります。

DOMメカニズムの要件

相互運用性の問題を最小限にするために、DOMベースのXMLSignatureFactory、KeyInfoFactory、またはTransformServiceを実装する場合は、次の要件に従う必要があります。

1. XMLSignatureFactoryのunmarshalXMLSignatureメソッドは、DOMValidateContext型をサポートする必要があります。型がDOMValidateContextの場合は、Signature型の要素を含む必要があります。また、unmarshalXMLSignatureメソッドは渡されるDOMValidateContextのID/要素マッピングを生成できます。

2. XMLSignatureFactoryによって生成されるXMLSignatureのsignメソッドはDOMSignContext型をサポートし、validateメソッドはDOMValidateContext型をサポートする必要があります。この要件は、SignatureValueのvalidateメソッド、およびReferenceのvalidateメソッドにも当てはまります。

3. 実装は、アプリケーションが拡張可能なコンテンツ(任意の要素または混合コンテンツ)を指定するためのメカニズムとして、DOMStructureをサポートする必要があります。

4. ユーザー指定のURIDereferencerのdereferenceメソッドがNodeSetDataオブジェクトを返す場合、iteratorメソッドは型org.w3c.dom.Nodeのオブジェクトの繰返しを返す必要があります。

5. ユーザー指定のURIDereferencersのdereferenceメソッドに渡されたURIReferenceオブジェクトは型DOMURIReferenceである必要があり、XMLCryptoContextオブジェクトはDOMCryptoContextを実装する必要があります。

6. 直前の2つの要件は、XMLSignatureFactoryおよびKeyInfoFactoryのgetURIDereferencerメソッドによって返されるURIDereferencersにも当てはまります。

7. KeyInfoFactoryのunmarshalKeyInfoメソッドはDOMStructure型をサポートする必要があります。型がDOMStructureの場合は、KeyInfo型の要素を含む必要があります。

8. TransformのtransformメソッドはDOMCryptoContextコンテキスト・パラメータ型をサポートする必要があります。

9. XMLSignatureFactoryのnewtransformおよびnewCanonicalizationMethodメソッドはDOMStructureパラメータ型をサポートする必要があります。

10. TransformServiceのinitおよびmarshalParamsメソッドはDOMStructureおよびDOMCryptoContext型をサポートする必要があります。

11. XMLSignatureFactoryのunmarshalXMLSignatureメソッドは、DOMStructure型をサポートする必要があります。型がDOMStructureの場合は、Signature型の要素を含む必要があります。

12. KeyInfoのmarshalメソッドはDOMStructureおよびDOMCryptoContextパラメータ型をサポートする必要があります。

DOM実装は、相互運用性に影響しない場合、DOM以外のXML解析APIを内部的に使用できます。たとえば、XMLSignatureFactoryのDOM実装は、内部的にSAXパーサーを使用してデータを正規化することができます。

オープンAPIの問題

オープンAPIの問題は、次のとおりです。

1. 外部XMLドキュメント参照のID属性の登録がサポートされていません。次の参照について検討してみます。
外部ドキュメントの間接参照によってオクテット・ストリームが生成され、それがJSR 105実装によってNodeSetに変換されています。ただし、APIでは外部ドキュメントのID属性を登録するためのメカニズムが提供されていないため、XPath変換実装は「foo」IDを識別できない可能性があります。

<Reference URI="document.xml">
<Transforms>
<Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
<XPath>id("foo")</XPath>
</Transform>
</Transforms>
</Reference>

プログラミング例

次の例1 - 3では、JSR 105 APIを使用してさまざまなタイプの単純なXMLデジタル署名を生成する方法について説明します。例1では、DSA署名アルゴリズムを使用して分離署名を生成する方法について説明します。例2では、エンベロープされた署名を生成する方法について説明します。例3では、エンベロープされた署名を生成する方法について説明します。例4では、XML署名を検証する方法について説明します。

例11-1 1.分離されたXMLデジタル署名の生成

import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.security.*;
import java.util.Collections;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;

/**
* This is a simple example of generating a Detached XML
* Signature using the JSR 105 API. The resulting signature will look
* like (key and signature values will be different):
*
* <pre><code>
* <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
*   <SignedInfo>
*     <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
*     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha256"/>
*     <Reference URI="http://www.w3.org/TR/xml-stylesheet">
*       <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
*       <DigestValue>60NvZvtdTB+7UnlLp/H24p7h4bs=</DigestValue>
*     </Reference>
*   </SignedInfo>
*   <SignatureValue>
*     DpEylhQoiUKBoKWmYfajXO7LZxiDYgVtUtCNyTgwZgoChzorA2nhkQ==
*   </SignatureValue>
*   <KeyInfo>
*     <KeyValue>
*       <DSAKeyValue>
*         <P>
*           rFto8uPQM6y34FLPmDh40BLJ1rVrC8VeRquuhPZ6jYNFkQuwxnu/wCvIAMhukPBL
*           FET8bJf/b2ef+oqxZajEb+88zlZoyG8g/wMfDBHTxz+CnowLahnCCTYBp5kt7G8q
*           UobJuvjylwj1st7V9Lsu03iXMXtbiriUjFa5gURasN8=
*         </P>
*         <Q>
*           kEjAFpCe4lcUOdwphpzf+tBaUds=
*         </Q>
*         <G>
*           oe14R2OtyKx+s+60O5BRNMOYpIg2TU/f15N3bsDErKOWtKXeNK9FS7dWStreDxo2
*           SSgOonqAd4FuJ/4uva7GgNL4ULIqY7E+mW5iwJ7n/WTELh98mEocsLXkNh24HcH4
*           BZfSCTruuzmCyjdV1KSqX/Eux04HfCWYmdxN3SQ/qqw=
*         </G>
*         <Y>
*           pA5NnZvcd574WRXuOA7ZfC/7Lqt4cB0MRLWtHubtJoVOao9ib5ry4rTk0r6ddnOv
*           AIGKktutzK3ymvKleS3DOrwZQgJ+/BDWDW8kO9R66o6rdjiSobBi/0c2V1+dkqOg
*           jFmKz395mvCOZGhC7fqAVhHat2EjGPMfgSZyABa7+1k=
*         </Y>
*       </DSAKeyValue>
*     </KeyValue>
*   </KeyInfo>
* </Signature>
* </code></pre>
*/
public class GenDetached {

//
// Synopsis: java GenDetached [output]
//
// where output is the name of the file that will contain the detached
// signature. If not specified, standard output is used.
//
public static void main(String[] args) throws Exception {

// First, create a DOM XMLSignatureFactory that will be used to
// generate the XMLSignature and marshal it to DOM.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

// Create a Reference to an external URI that will be digested
// using the SHA256 digest algorithm
Reference ref = fac.newReference("http://www.w3.org/TR/xml-stylesheet",
fac.newDigestMethod(DigestMethod.SHA256, null));

// Create the SignedInfo
SignedInfo si = fac.newSignedInfo(
fac.newCanonicalizationMethod
(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
(C14NMethodParameterSpec) null),
fac.newSignatureMethod("http://www.w3.org/2000/09/xmldsig#dsa-sha256", null),
Collections.singletonList(ref));

// Create a DSA KeyPair
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();

// Create a KeyValue containing the DSA PublicKey that was generated
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(kp.getPublic());

// Create a KeyInfo and add the KeyValue to it
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

// Create the XMLSignature (but don't sign it yet)
XMLSignature signature = fac.newXMLSignature(si, ki);

// Create the Document that will hold the resulting XMLSignature
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true); // must be set
Document doc = dbf.newDocumentBuilder().newDocument();

// Create a DOMSignContext and set the signing Key to the DSA
// PrivateKey and specify where the XMLSignature should be inserted
// in the target document (in this case, the document root)
DOMSignContext signContext = new DOMSignContext(kp.getPrivate(), doc);

// Marshal, generate (and sign) the detached XMLSignature. The DOM
// Document will contain the XML Signature if this method returns
// successfully.
signature.sign(signContext);

// output the resulting document
OutputStream os;
if (args.length > 0) {
os = new FileOutputStream(args[0]);
} else {
os = System.out;
}

TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
}
}

例11-2 2.エンベロープされたXMLデジタル署名の生成

import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.security.*;
import java.util.Collections;
import java.util.Iterator;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;

/**
* This is a simple example of generating an Enveloped XML
* Signature using the JSR 105 API. The resulting signature will look
* like (key and signature values will be different):
*
* <pre><code>
*<Envelope xmlns="urn:envelope">
* <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
*   <SignedInfo>
*     <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
*     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha256"/>
*     <Reference URI="">
*       <Transforms>
*         <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
*       </Transforms>
*       <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
*       <DigestValue>K8M/lPbKnuMDsO0Uzuj75lQtzQI=<DigestValue>
*     </Reference>
*   </SignedInfo>
*   <SignatureValue>
*     DpEylhQoiUKBoKWmYfajXO7LZxiDYgVtUtCNyTgwZgoChzorA2nhkQ==
*   </SignatureValue>
*   <KeyInfo>
*     <KeyValue>
*       <DSAKeyValue>
*         <P>
*           rFto8uPQM6y34FLPmDh40BLJ1rVrC8VeRquuhPZ6jYNFkQuwxnu/wCvIAMhukPBL
*           FET8bJf/b2ef+oqxZajEb+88zlZoyG8g/wMfDBHTxz+CnowLahnCCTYBp5kt7G8q
*           UobJuvjylwj1st7V9Lsu03iXMXtbiriUjFa5gURasN8=
*         </P>
*         <Q>
*           kEjAFpCe4lcUOdwphpzf+tBaUds=
*         </Q>
*         <G>
*           oe14R2OtyKx+s+60O5BRNMOYpIg2TU/f15N3bsDErKOWtKXeNK9FS7dWStreDxo2
*           SSgOonqAd4FuJ/4uva7GgNL4ULIqY7E+mW5iwJ7n/WTELh98mEocsLXkNh24HcH4
*           BZfSCTruuzmCyjdV1KSqX/Eux04HfCWYmdxN3SQ/qqw=
*         </G>
*         <Y>
*           pA5NnZvcd574WRXuOA7ZfC/7Lqt4cB0MRLWtHubtJoVOao9ib5ry4rTk0r6ddnOv
*           AIGKktutzK3ymvKleS3DOrwZQgJ+/BDWDW8kO9R66o6rdjiSobBi/0c2V1+dkqOg
*           jFmKz395mvCOZGhC7fqAVhHat2EjGPMfgSZyABa7+1k=
*         </Y>
*       </DSAKeyValue>
*     </KeyValue>
*   </KeyInfo>
* </Signature>
*</Envelope>
* </code></pre>
*/
public class GenEnveloped {

//
// Synopsis: java GenEnveloped [document] [output]
//
//    where "document" is the name of a file containing the XML document
//    to be signed, and "output" is the name of the file to store the
//    signed document. The 2nd argument is optional - if not specified,
//    standard output will be used.
//
public static void main(String[] args) throws Exception {

// Create a DOM XMLSignatureFactory that will be used to generate the
// enveloped signature
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

// Create a Reference to the enveloped document (in this case we are
// signing the whole document, so a URI of "" signifies that) and
// also specify the SHA256 digest algorithm and the ENVELOPED Transform.
Reference ref = fac.newReference
("", fac.newDigestMethod(DigestMethod.SHA256, null),
Collections.singletonList
(fac.newTransform
(Transform.ENVELOPED, (TransformParameterSpec) null)),
null, null);

// Create the SignedInfo
SignedInfo si = fac.newSignedInfo
(fac.newCanonicalizationMethod
(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
(C14NMethodParameterSpec) null),
fac.newSignatureMethod("http://www.w3.org/2000/09/xmldsig#dsa-sha256", null),
Collections.singletonList(ref));

// Create a DSA KeyPair
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();

// Create a KeyValue containing the DSA PublicKey that was generated
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(kp.getPublic());

// Create a KeyInfo and add the KeyValue to it
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

// Instantiate the document to be signed
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc =
dbf.newDocumentBuilder().parse(new FileInputStream(args[0]));

// Create a DOMSignContext and specify the DSA PrivateKey and
// location of the resulting XMLSignature's parent element
DOMSignContext dsc = new DOMSignContext
(kp.getPrivate(), doc.getDocumentElement());

// Create the XMLSignature (but don't sign it yet)
XMLSignature signature = fac.newXMLSignature(si, ki);

// Marshal, generate (and sign) the enveloped signature
signature.sign(dsc);

// output the resulting document
OutputStream os;
if (args.length > 1) {
os = new FileOutputStream(args[1]);
} else {
os = System.out;
}

TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
}
}

例11-3 3.エンベロープするXMLデジタル署名の生成

import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.security.*;
import java.util.Arrays;
import java.util.Collections;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

/**
* This is a simple example of generating an Enveloping XML
* Signature using the JSR 105 API. The signature in this case references a
* local URI that points to an Object element.
* The resulting signature will look like (certificate and
* signature values will be different):
*
* <pre><code>
* <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
*   <SignedInfo>
*     <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
*     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha256"/>
*     <Reference URI="#object">
*       <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
*       <DigestValue>7/XTsHaBSOnJ/jXD5v0zL6VKYsk=</DigestValue>
*     </Reference>
*   </SignedInfo>
*   <SignatureValue>
*     RpMRbtMHLa0siSS+BwUpLIEmTfh/0fsld2JYQWZzCzfa5kBTz25+XA==
*   </SignatureValue>
*   <KeyInfo>
*     <KeyValue>
*       <DSAKeyValue>
*         <P>
*           /KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0Imbz
*           RMqzVDZkVG9xD7nN1kuFw==
*         </P>
*         <Q>
*           li7dzDacuo67Jg7mtqEm2TRuOMU=
*         </Q>
*         <G>
*           Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/XPaF5Bpsy4pNWMOH
*           CBiNU0NogpsQW5QvnlMpA==
*         </G>
*         <Y>
*           wbEUaCgHZXqK4qLvbdYrAc6+Do0XVcsziCJqxzn4cJJRxwc3E1xnEXHscVgr1Cql9
*           i5fanOKQbFXzmb+bChqig==
*         </Y>
*       </DSAKeyValue>
*     </KeyValue>
*   </KeyInfo>
*   <Object Id="object">some text</Object>
* </Signature>
*
* </code></pre>
*/
public class GenEnveloping {

//
// Synopis: java GenEnveloping [output]
//
//   where "output" is the name of a file that will contain the
//   generated signature. If not specified, standard ouput will be used.
//
public static void main(String[] args) throws Exception {

// First, create the DOM XMLSignatureFactory that will be used to
// generate the XMLSignature
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

// Next, create a Reference to a same-document URI that is an Object
// element and specify the SHA256 digest algorithm
Reference ref = fac.newReference("#object",
fac.newDigestMethod(DigestMethod.SHA256, null));

// Next, create the referenced Object
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().newDocument();
Node text = doc.createTextNode("some text");
XMLStructure content = new DOMStructure(text);
XMLObject obj = fac.newXMLObject
(Collections.singletonList(content), "object", null, null);

// Create the SignedInfo
SignedInfo si = fac.newSignedInfo(
fac.newCanonicalizationMethod
(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
(C14NMethodParameterSpec) null),
fac.newSignatureMethod("http://www.w3.org/2000/09/xmldsig#dsa-sha256", null),
Collections.singletonList(ref));

// Create a DSA KeyPair
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();

// Create a KeyValue containing the DSA PublicKey that was generated
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(kp.getPublic());

// Create a KeyInfo and add the KeyValue to it
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

// Create the XMLSignature (but don't sign it yet)
XMLSignature signature = fac.newXMLSignature(si, ki,
Collections.singletonList(obj), null, null);

// Create a DOMSignContext and specify the DSA PrivateKey for signing
// and the document location of the XMLSignature
DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), doc);

// Lastly, generate the enveloping signature using the PrivateKey
signature.sign(dsc);

// output the resulting document
OutputStream os;
if (args.length > 0) {
os = new FileOutputStream(args[0]);
} else {
os = System.out;
}

TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
}
}

例11-4 4.XMLデジタル署名の検証

import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.*;
import java.io.FileInputStream;
import java.security.*;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

/**
* This is a simple example of validating an XML
* Signature using the JSR 105 API. It assumes the key needed to
* validate the signature is contained in a KeyValue KeyInfo.
*/
public class Validate {

//
// Synopsis: java Validate [document]
//
//    where "document" is the name of a file containing the XML document
//    to be validated.
//
public static void main(String[] args) throws Exception {

// Instantiate the document to be validated
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc =
dbf.newDocumentBuilder().parse(new FileInputStream(args[0]));

// Find Signature element
NodeList nl =
doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("Cannot find Signature element");
}

// Create a DOM XMLSignatureFactory that will be used to unmarshal the
// document containing the XMLSignature
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

// Create a DOMValidateContext and specify a KeyValue KeySelector
// and document context
DOMValidateContext valContext = new DOMValidateContext
(new KeyValueKeySelector(), nl.item(0));

// unmarshal the XMLSignature
XMLSignature signature = fac.unmarshalXMLSignature(valContext);

// Validate the XMLSignature (generated above)
boolean coreValidity = signature.validate(valContext);

// Check core validation status
if (coreValidity == false) {
System.err.println("Signature failed core validation");
boolean sv = signature.getSignatureValue().validate(valContext);
System.out.println("signature validation status: " + sv);
// check the validation status of each Reference
Iterator i = signature.getSignedInfo().getReferences().iterator();
for (int j=0; i.hasNext(); j++) {
boolean refValid =
((Reference) i.next()).validate(valContext);
System.out.println("ref["+j+"] validity status: " + refValid);
}
} else {
System.out.println("Signature passed core validation");
}
}

/**
* KeySelector which retrieves the public key out of the
* KeyValue element and returns it.
* NOTE: If the key algorithm doesn't match signature algorithm,
* then the public key will be ignored.
*/
private static class KeyValueKeySelector extends KeySelector {
public KeySelectorResult select(KeyInfo keyInfo,
KeySelector.Purpose purpose,
AlgorithmMethod method,
XMLCryptoContext context)
throws KeySelectorException {
if (keyInfo == null) {
throw new KeySelectorException("Null KeyInfo object!");
}
SignatureMethod sm = (SignatureMethod) method;
List list = keyInfo.getContent();

for (int i = 0; i < list.size(); i++) {
XMLStructure xmlStructure = (XMLStructure) list.get(i);
if (xmlStructure instanceof KeyValue) {
PublicKey pk = null;
try {
pk = ((KeyValue)xmlStructure).getPublicKey();
} catch (KeyException ke) {
throw new KeySelectorException(ke);
}
// make sure algorithm is compatible with method
if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
return new SimpleKeySelectorResult(pk);
}
}
}
throw new KeySelectorException("No KeyValue element found!");
}

//@@@FIXME: this should also work for key types other than DSA/RSA
static boolean algEquals(String algURI, String algName) {
if (algName.equalsIgnoreCase("DSA") &&
algURI.equalsIgnoreCase("http://www.w3.org/2000/09/xmldsig#dsa-sha256")) {
return true;
} else if (algName.equalsIgnoreCase("RSA") &&
algURI.equalsIgnoreCase("http://www.w3.org/2000/09/xmldsig#dsa-sha256")) {
return true;
} else {
return false;
}
}
}

private static class SimpleKeySelectorResult implements KeySelectorResult {
private PublicKey pk;
SimpleKeySelectorResult(PublicKey pk) {
this.pk = pk;
}

public Key getKey() { return pk; }
}
}

XMLデジタル署名APIの概要とチュートリアル

Java XMLデジタル署名APIは、XML署名を生成および検証するための標準のJava APIです。このAPIは、Javaコミュニティ・プロセスの下でJSR 105として定義されました。このJSRは最終的なものであり、このリリースのJava SEには、APIの最終バージョンのFCSアクセス実装が含まれています。

XML署名は、どのタイプ(XMLまたはバイナリ)のデータにも適用できます(XML署名の構文と処理を参照)。結果の署名は、XMLで表されます。XML署名は、データを保護するために使用でき、データの整合性、メッセージ認証、および署名者認証を提供します。

このドキュメントでは、XML署名とXMLデジタル署名APIの概要を説明したあとで、APIを使用してXML署名を検証および生成する方法を示す2つの例について説明します。このドキュメントは、読者に暗号化およびデジタル署名の基本的な知識があることを前提としています。

APIは、「XML-Signature Syntax and Processing」でのW3C勧告の必須機能または推奨機能をすべてサポートするように設計されています。APIは拡張可能およびプラガブルであり、Java暗号化サービス・プロバイダ・アーキテクチャに基づいてます。APIは、次の2種類の開発者向けに設計されています。

- XMLデジタル署名APIを使用してXML署名を生成および検証する開発者

- XMLデジタル署名APIの固定実装を作成し、JCAプロバイダの暗号化サービスとして登録する開発者(Providerクラスを参照)

パッケージ階層

次に示す6個のパッケージが、XMLデジタル署名APIを構成します。

- javax.xml.crypto

- javax.xml.crypto.dsig

- javax.xml.crypto.dsig.keyinfo

- javax.xml.crypto.dsig.spec

- javax.xml.crypto.dom

- javax.xml.crypto.dsig.dom

javax.xml.cryptoパッケージには、XML署名の生成やXMLデータの暗号化など、XML暗号化操作を行う場合に使用する一般的なクラスが含まれています。このパッケージで注意する必要がある2つのクラスのうちの1つはKeySelectorクラスで、開発者は、KeyInfoオブジェクトに含まれる情報を使用して鍵を見つけてオプションで検証する実装を提供できます。もう1つのクラスはURIDereferencerクラスで、開発者は、実装を間接参照する独自のURIを作成および指定できます。

javax.xml.crypto.dsigパッケージには、W3C XMLデジタル署名仕様で定義されているコア要素を表すインタフェースが含まれています。最も重要なのはXMLSignatureクラスです。このクラスを使用すると、XMLデジタル署名の署名および検証を行うことができます。XML署名構造または要素のほとんどは、対応するインタフェースによって表されます(KeyInfo構造を除く。これは独自のパッケージに含まれており、次の段落で説明します)。これらのインタフェースには、SignedInfo、CanonicalizationMethod、SignatureMethod、Reference、Transform、DigestMethod、XMLObject、Manifest、SignaturePropertyおよびSignaturePropertiesなどがあります。XMLSignatureFactoryクラスは、これらのインタフェースを実装するオブジェクトを作成する場合に使用する抽象ファクトリです。

javax.xml.crypto.dsig.keyinfoパッケージには、KeyInfo、KeyName、KeyValue、X509Data、X509IssuerSerial、RetrievalMethodおよびPGPDataなど、W3C XMLデジタル署名勧告で定義されているほとんどのKeyInfo構造を表すインタフェースが含まれています。KeyInfoFactoryクラスは、これらのインタフェースを実装するオブジェクトを作成する場合に使用する抽象ファクトリです。

javax.xml.crypto.dsig.specパッケージには、ダイジェスト、署名、変換、またはXML署名の処理で使用される正規化アルゴリズム用の入力パラメータを表すインタフェースおよびクラスが含まれています。

最後に、javax.xml.crypto.domおよびjavax.xml.crypto.dsig.domパッケージには、javax.xml.cryptoおよびjavax.xml.crypto.dsigパッケージ用のDOM固有のクラスがそれぞれ含まれています。DOMベースのXMLSignatureFactoryまたはKeyInfoFactory実装を作成または使用する開発者およびユーザーのみが、これらのパッケージを直接使用する必要があります。

サービス・プロバイダ

JSR 105暗号化サービスは、XMLSignatureFactoryおよびKeyInfoFactory抽象クラスの固定実装であり、XML署名やKeyInfo構造を解析、生成および検証するオブジェクトやアルゴリズムを作成します。XMLSignatureFactoryの固定実装は、XML署名についてW3C勧告で指定されているように、必須アルゴリズムそれぞれをサポートする必要があります。オプションで、W3C勧告またはその他の仕様で定義されているように、その他のアルゴリズムをサポートできます。

JSR 105は、JCAプロバイダ・モデルを使用してXMLSignatureFactoryおよびKeyInfoFactory実装の登録とロードを行います。

固定実装XMLSignatureFactoryまたはKeyInfoFactoryはそれぞれ、XML署名やKeyInfo構造を解析および生成するときに実装によって内部で使用されるXML処理メカニズムを識別する特定の「XMLメカニズム・タイプ」をサポートしています。このJSRは、1つの標準形式であるDOMをサポートします。Java SEにバンドルされているXMLデジタル署名プロバイダ実装は、DOMメカニズムをサポートします。JDOMなどの新しい標準形式のサポートが、将来的に追加される可能性があります。

XMLデジタル署名API実装は、java.security.Signatureやjava.security.MessageDigestなど、基盤となるJCAエンジン・クラスを使用して暗号化操作を実行する必要があります。

XMLSignatureFactoryおよびKeyInfoFactoryクラス以外に、JSR 105は、変換および正規化アルゴリズム用のサービス・プロバイダ・インタフェースもサポートしています。TransformServiceクラスを使用すると、特定のXMLメカニズム・タイプ用の固有の変換または正規化アルゴリズムの実装を開発およびプラグインできます。TransformServiceクラスは、実装を登録およびロードするときに標準JCAプロバイダ・モデルを使用します。各JSR 105実装は、TransformServiceクラスを使用して、生成または検証するXML署名内の変換および正規化アルゴリズムをサポートするプロバイダを見つけるようにしてください。

XML署名について

XML署名を使用すると、XMLかバイナリかにかかわらず、任意のデータに署名できます。データは1つ以上のReference要素内のURIによって識別されます。XML署名は分離、内包または包含の3つの形式のうち、1つ以上の形式で記述されます。分離署名は外部、つまり署名要素自体の外にあるデータに対するものです。内包署名は署名要素の内側にあるデータに対する署名です。包含署名は署名対象のデータ内に含まれる署名です。

XML署名の例

XML署名の内容を説明するもっとも簡単な方法は、実際のサンプルを示し、各コンポーネントについて詳細に説明することです。次に、XMLドキュメントの内容に対して生成される包含XML署名の例を示します。署名される前のドキュメントの内容は、次のとおりです。

<Envelope xmlns="urn:envelope">
</Envelope>

結果の包含XML署名は次のとおりです。読みやすくするために、インデントおよび形式設定されています。

<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="urn:envelope">
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod
Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>uooqbWYa5VCqcJCbuymBKqm17vY=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
KedJuTob5gtvYx9qM3k3gm7kbLBwVbEQRl26S2tmXjqNND7MRGtoew==
</SignatureValue>
<KeyInfo>
<KeyValue>
<DSAKeyValue>
<P>
/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxe
Eu0ImbzRMqzVDZkVG9xD7nN1kuFw==
</P>
<Q>li7dzDacuo67Jg7mtqEm2TRuOMU=</Q>
<G>
Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/
XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA==
</G>
<Y>
qV38IqrWJG0V/mZQvRVi1OHw9Zj84nDC4jO8P0axi1gb6d+475yhMjSc/
BrIVC58W3ydbkK+Ri4OKbaRZlYeRA==
</Y>
</DSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</Envelope>

Signature要素は署名対象の内容に挿入されており、それによって包含署名になっています。必須のSignedInfo要素には、実際に署名される次の情報が含まれています。

<SignedInfo>
<CanonicalizationMethod
Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>uooqbWYa5VCqcJCbuymBKqm17vY=</DigestValue>
</Reference>
</SignedInfo>

必須のCanonicalizationMethod要素は、署名または検証の前にSignedInfo要素を正規化するために使用されるアルゴリズムを定義します。正規化とは、そのデータに対する署名を無効化する可能性がある変更を考慮するために、XMLコンテンツを正規形に変換する処理のことを意味します。正規化は、XMLの性質および異なるプロセッサと中間的存在による解析のために必要です。正規化により、署名が無効になっても署名されたデータが論理的に同等であるようにデータを変更できます。

必須のSignatureMethod要素は、署名の生成に使用されるデジタル署名アルゴリズムを定義します。この場合は、SHA-1を使用するDSAです。

1つ以上のReference要素が、ダイジェストされるデータを識別します。各Reference要素は、URIによってデータを識別します。この例では、URIの値は空の文字列("")で、これはドキュメントのルートを示します。オプションのTransforms要素には、1つ以上のTransform要素のリストが含まれており、それぞれがダイジェスト前のデータの変換に使用される変換アルゴリズムを記述します。この例では、包含変換アルゴリズムのTransform要素が1つあります。包含変換は、署名値を計算する前に署名要素自体が削除されるようにするために、包含署名に必要です。必須のDigestMethod要素は、データのダイジェストに使用されるアルゴリズムを定義します。この場合は、SHA1です。最後に、必須のDigestValue要素には、実際のBase64でエンコードされたダイジェスト値が含まれています。

必須のSignatureValue要素には、SignedInfo要素に対する署名のBase64でエンコードされた署名値が含まれています。

オプションのKeyInfo要素には、署名の検証に必要な鍵に関する情報が含まれています。

<KeyInfo>
<KeyValue>
<DSAKeyValue>
<P>
/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxe
Eu0ImbzRMqzVDZkVG9xD7nN1kuFw==
</P>
<Q>li7dzDacuo67Jg7mtqEm2TRuOMU=</Q>
<G>
Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/
XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA==
</G>
<Y>
qV38IqrWJG0V/mZQvRVi1OHw9Zj84nDC4jO8P0axi1gb6d+475yhMjSc/
BrIVC58W3ydbkK+Ri4OKbaRZlYeRA==
</Y>
</DSAKeyValue>
</KeyValue>
</KeyInfo>

このKeyInfo要素にはKeyValue 要素が含まれています。この要素には、署名の検証に必要な公開鍵で構成されるDSAKeyValue要素が含まれています。KeyInfoには、X.509証明書およびPGP鍵識別子など、様々な内容を含めることができます。様々なKeyInfo型の詳細は、XML署名の構文と処理のKeyInfo要素を参照してください。

XMLデジタル署名APIの例

次のセクションでは、XMLデジタル署名APIを使用する方法を示す2つの例について説明します。

検証の例

この例をコンパイルし動作させるには、次のコマンドを実行します。

$ javac Validate.java
$ java Validate signature.xml

サンプル・プログラムは現在の作業ディレクトリ内のファイルsignature.xmlの署名を検証します。

例11-5 Validate.java

import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.*;
import java.io.FileInputStream;
import java.security.*;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

/**
* This is a simple example of validating an XML
* Signature using the JSR 105 API. It assumes the key needed to
* validate the signature is contained in a KeyValue KeyInfo.
*/
public class Validate {

//
// Synopsis: java Validate [document]
//
//    where "document" is the name of a file containing the XML document
//    to be validated.
//
public static void main(String[] args) throws Exception {

// Instantiate the document to be validated
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc =
dbf.newDocumentBuilder().parse(new FileInputStream(args[0]));

// Find Signature element
NodeList nl =
doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("Cannot find Signature element");
}

// Create a DOM XMLSignatureFactory that will be used to unmarshal the
// document containing the XMLSignature
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

// Create a DOMValidateContext and specify a KeyValue KeySelector
// and document context
DOMValidateContext valContext = new DOMValidateContext
(new KeyValueKeySelector(), nl.item(0));

// unmarshal the XMLSignature
XMLSignature signature = fac.unmarshalXMLSignature(valContext);

// Validate the XMLSignature (generated above)
boolean coreValidity = signature.validate(valContext);

// Check core validation status
if (coreValidity == false) {
System.err.println("Signature failed core validation");
boolean sv = signature.getSignatureValue().validate(valContext);
System.out.println("signature validation status: " + sv);
// check the validation status of each Reference
Iterator i = signature.getSignedInfo().getReferences().iterator();
for (int j=0; i.hasNext(); j++) {
boolean refValid =
((Reference) i.next()).validate(valContext);
System.out.println("ref["+j+"] validity status: " + refValid);
}
} else {
System.out.println("Signature passed core validation");
}
}

/**
* KeySelector which retrieves the public key out of the
* KeyValue element and returns it.
* NOTE: If the key algorithm doesn't match signature algorithm,
* then the public key will be ignored.
*/
private static class KeyValueKeySelector extends KeySelector {
public KeySelectorResult select(KeyInfo keyInfo,
KeySelector.Purpose purpose,
AlgorithmMethod method,
XMLCryptoContext context)
throws KeySelectorException {
if (keyInfo == null) {
throw new KeySelectorException("Null KeyInfo object!");
}
SignatureMethod sm = (SignatureMethod) method;
List list = keyInfo.getContent();

for (int i = 0; i < list.size(); i++) {
XMLStructure xmlStructure = (XMLStructure) list.get(i);
if (xmlStructure instanceof KeyValue) {
PublicKey pk = null;
try {
pk = ((KeyValue)xmlStructure).getPublicKey();
} catch (KeyException ke) {
throw new KeySelectorException(ke);
}
// make sure algorithm is compatible with method
if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
return new SimpleKeySelectorResult(pk);
}
}
}
throw new KeySelectorException("No KeyValue element found!");
}

//@@@FIXME: this should also work for key types other than DSA/RSA
static boolean algEquals(String algURI, String algName) {
if (algName.equalsIgnoreCase("DSA") &&
algURI.equalsIgnoreCase("http://www.w3.org/2009/xmldsig11#dsa-sha256")) {
return true;
} else if (algName.equalsIgnoreCase("RSA") &&
algURI.equalsIgnoreCase("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")) {
return true;
} else {
return false;
}
}
}

private static class SimpleKeySelectorResult implements KeySelectorResult {
private PublicKey pk;
SimpleKeySelectorResult(PublicKey pk) {
this.pk = pk;
}

public Key getKey() { return pk; }
}
}

例11-6 envelope.xml

<Envelope xmlns="urn:envelope">
</Envelope>

例11-7 signature.xml

<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="urn:envelope">
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<CanonicalizationMethod
Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
xmlns="http://www.w3.org/2000/09/xmldsig#"/>
<SignatureMethod
Algorithm="http://www.w3.org/2009/xmldsig11#dsa-sha256"
xmlns="http://www.w3.org/2000/09/xmldsig#"/>
<Reference URI="" xmlns="http://www.w3.org/2000/09/xmldsig#">
<Transforms xmlns="http://www.w3.org/2000/09/xmldsig#">
<Transform
Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"
xmlns="http://www.w3.org/2000/09/xmldsig#"/>
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"
xmlns="http://www.w3.org/2000/09/xmldsig#"/>
<DigestValue xmlns="http://www.w3.org/2000/09/xmldsig#">uooqbWYa5VCqcJCbuymBKqm17vY=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue xmlns="http://www.w3.org/2000/09/xmldsig#">eO7K1BdC0kzNvr1HpMf4hKoWsvl+oI04nMw55GO+Z5hyI6By3Oihow==</SignatureValue>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">
<DSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">
<P xmlns="http://www.w3.org/2000/09/xmldsig#">/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuFw==</P>
<Q xmlns="http://www.w3.org/2000/09/xmldsig#">li7dzDacuo67Jg7mtqEm2TRuOMU=</Q>
<G xmlns="http://www.w3.org/2000/09/xmldsig#">Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA==</G>
<Y xmlns="http://www.w3.org/2000/09/xmldsig#">OqFi0sGpvroi6Ut3m154QNWc6gavH3j2ZoRPDW7qVBbgk7XompuKvZe1owz0yvxq+1K+mWbL7ST+t5nr6UFBCg==</Y>
</DSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</Envelope>

XML署名の検証

この例では、JSR 105 APIを使用してXML署名を検証する方法を示します。この例はDOM (Document Object Model)を使用して、署名要素を含むXMLドキュメントおよびJSR 105 DOM実装を解析し、署名を検証します。

署名を含むドキュメントのインスタンス化

最初に、JAXP DocumentBuilderFactoryを使用して、署名を含むXMLドキュメントを解析します。アプリケーションは、次のコード行を呼び出すことによって、DocumentBuilderFactoryのデフォルト実装を取得します。

DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();

ファクトリ名前空間認識も作成します。

dbf.setNamespaceAware(true);

次に、このファクトリを使用してDocumentBuilderのインスタンスを取得します。これはドキュメントの解析で使用されます。

DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(new FileInputStream(argv[0]));

検証する署名要素の指定

ドキュメントに複数含まれている場合もあるため、検証するSignature要素を指定する必要があります。DOMメソッドDocument.getElementsByTagNameNSを使用して、次に示すように、Signature要素のXML署名名前空間URIおよびタグ名をメソッドに渡します。

NodeList nl = doc.getElementsByTagNameNS
(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("Cannot find Signature element");
}

これは、ドキュメント内にあるすべてのSignature要素のリストを返します。この例ではSignature要素は1つのみです。

検証コンテキストの作成

署名を検証するための入力パラメータを含むXMLValidateContextインスタンスを作成します。DOMを使用しているため、DOMValidateContextインスタンス(XMLValidateContextのサブクラス)をインスタンス化し、2つのパラメータを渡します。2つのパラメータとは、KeyValueKeySelectorオブジェクトと、先に生成したNodeListの最初のエントリである、検証するSignature要素の参照です。

DOMValidateContext valContext = new DOMValidateContext
(new KeyValueKeySelector(), nl.item(0));

KeyValueKeySelectorについては、「KeySelectorの使用」で詳細に説明します。

XML署名のアンマーシャリング

Signature要素の内容をXMLSignatureオブジェクトに抽出します。この処理は非整列化と呼ばれます。Signature要素はXMLSignatureFactoryオブジェクトを使用して非整列化されます。アプリケーションは次のコード行を呼び出すことによって、XMLSignatureFactoryのDOM実装を取得できます。

XMLSignatureFactory factory =
XMLSignatureFactory.getInstance("DOM");

次に、ファクトリのunmarshalXMLSignatureメソッドを呼び出してXMLSignatureオブジェクトを非整列化し、先に作成した検証コンテキストを渡します。

XMLSignature signature =
factory.unmarshalXMLSignature(valContext);

XML署名の検証

これで、署名を検証する準備が整いました。XMLSignatureオブジェクトでvalidateメソッドを呼び出して、次に示すように検証コンテキストを渡します。

boolean coreValidity = signature.validate(valContext);

W3C XML署名勧告のコア検証規則に従って署名が正常に検証されると、validateメソッドは「true」を返します。それ以外の場合はfalseを返します。

KeySelectorの使用

KeySelectorsは、XMLSignatureの検証に必要な鍵を見つけて選択するために使用されます。以前は、DOMValidateContextオブジェクトを作成したときは、KeySelectorオブジェクトを最初の引数として渡しました。

DOMValidateContext valContext = new DOMValidateContext
(new KeyValueKeySelector(), nl.item(0));

また、署名の検証に必要な鍵がすでにわかっている場合は、PublicKeyを最初の引数として渡すこともできました。ただし、多くの場合はわかりません。

KeyValueKeySelectorは、抽象KeySelectorクラスの固定実装です。KeyValueKeySelector実装は、XMLSignatureのKeyInfo要素のKeyValue要素に含まれるデータを使用して、適切な検証鍵を見つけようとします。鍵が信頼できるかどうかは判断しません。これは、単純なKeySelector実装であり、実際の使用というよりは説明のために設計されています。KeySelectorのより実用的な例は、KeyInfoに含まれるX509Data情報(X509SubjectName要素、X509IssuerSerial要素、X509SKI要素、X509Certificate要素など)に一致する信頼できる鍵でKeyStoreを検索する例です。

KeyValueKeySelectorの実装は、次のとおりです。

private static class KeyValueKeySelector extends KeySelector {

public KeySelectorResult select(KeyInfo keyInfo,
KeySelector.Purpose purpose,
AlgorithmMethod method,
XMLCryptoContext context)
throws KeySelectorException {

if (keyInfo == null) {
throw new KeySelectorException("Null KeyInfo object!");
}
SignatureMethod sm = (SignatureMethod) method;
List list = keyInfo.getContent();

for (int i = 0; i < list.size(); i++) {
XMLStructure xmlStructure = (XMLStructure) list.get(i);
if (xmlStructure instanceof KeyValue) {
PublicKey pk = null;
try {
pk = ((KeyValue)xmlStructure).getPublicKey();
} catch (KeyException ke) {
throw new KeySelectorException(ke);
}
// make sure algorithm is compatible with method
if (algEquals(sm.getAlgorithm(),
pk.getAlgorithm())) {
return new SimpleKeySelectorResult(pk);
}
}
}
throw new KeySelectorException("No KeyValue element
found!");
}

static boolean algEquals(String algURI, String algName) {
if (algName.equalsIgnoreCase("DSA") &&
algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
return true;
} else if (algName.equalsIgnoreCase("RSA") &&
algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
return true;
} else {
return false;
}
}
}

GenEnvelopedの例

このサンプルをコンパイルし動作させるには、次のコマンドを実行します。

$ javac GenEnveloped.java
$ java GenEnveloped envelope.xml envelopedSignature.xml

このサンプル・プログラムはファイルenvelope.xml内のドキュメントの包含署名を生成し、現在の作業ディレクトリ内のファイルenvelopedSignature.xmlに保存します。

例11-8 GenEnveloped.java

import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.security.*;
import java.util.Collections;
import java.util.Iterator;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;

/**
* This is a simple example of generating an Enveloped XML
* Signature using the JSR 105 API. The resulting signature will look
* like (key and signature values will be different):
*
* <pre><code>
*<Envelope xmlns="urn:envelope">
* <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
*   <SignedInfo>
*     <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
*     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha256"/>
*     <Reference URI="">
*       <Transforms>
*         <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
*       </Transforms>
*       <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
*       <DigestValue>K8M/lPbKnuMDsO0Uzuj75lQtzQI=<DigestValue>
*     </Reference>
*   </SignedInfo>
*   <SignatureValue>
*     DpEylhQoiUKBoKWmYfajXO7LZxiDYgVtUtCNyTgwZgoChzorA2nhkQ==
*   </SignatureValue>
*   <KeyInfo>
*     <KeyValue>
*       <DSAKeyValue>
*         <P>
*           rFto8uPQM6y34FLPmDh40BLJ1rVrC8VeRquuhPZ6jYNFkQuwxnu/wCvIAMhukPBL
*           FET8bJf/b2ef+oqxZajEb+88zlZoyG8g/wMfDBHTxz+CnowLahnCCTYBp5kt7G8q
*           UobJuvjylwj1st7V9Lsu03iXMXtbiriUjFa5gURasN8=
*         </P>
*         <Q>
*           kEjAFpCe4lcUOdwphpzf+tBaUds=
*         </Q>
*         <G>
*           oe14R2OtyKx+s+60O5BRNMOYpIg2TU/f15N3bsDErKOWtKXeNK9FS7dWStreDxo2
*           SSgOonqAd4FuJ/4uva7GgNL4ULIqY7E+mW5iwJ7n/WTELh98mEocsLXkNh24HcH4
*           BZfSCTruuzmCyjdV1KSqX/Eux04HfCWYmdxN3SQ/qqw=
*         </G>
*         <Y>
*           pA5NnZvcd574WRXuOA7ZfC/7Lqt4cB0MRLWtHubtJoVOao9ib5ry4rTk0r6ddnOv
*           AIGKktutzK3ymvKleS3DOrwZQgJ+/BDWDW8kO9R66o6rdjiSobBi/0c2V1+dkqOg
*           jFmKz395mvCOZGhC7fqAVhHat2EjGPMfgSZyABa7+1k=
*         </Y>
*       </DSAKeyValue>
*     </KeyValue>
*   </KeyInfo>
* </Signature>
*</Envelope>
* </code></pre>
*/
public class GenEnveloped {

//
// Synopsis: java GenEnveloped [document] [output]
//
//    where "document" is the name of a file containing the XML document
//    to be signed, and "output" is the name of the file to store the
//    signed document. The 2nd argument is optional - if not specified,
//    standard output will be used.
//
public static void main(String[] args) throws Exception {

// Create a DOM XMLSignatureFactory that will be used to generate the
// enveloped signature
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

// Create a Reference to the enveloped document (in this case we are
// signing the whole document, so a URI of "" signifies that) and
// also specify the SHA256 digest algorithm and the ENVELOPED Transform.
Reference ref = fac.newReference
("", fac.newDigestMethod(DigestMethod.SHA256, null),
Collections.singletonList
(fac.newTransform
(Transform.ENVELOPED, (TransformParameterSpec) null)),
null, null);

// Create the SignedInfo
SignedInfo si = fac.newSignedInfo
(fac.newCanonicalizationMethod
(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
(C14NMethodParameterSpec) null),
fac.newSignatureMethod("http://www.w3.org/2000/09/xmldsig#dsa-sha256", null),
Collections.singletonList(ref));

// Create a DSA KeyPair
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();

// Create a KeyValue containing the DSA PublicKey that was generated
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(kp.getPublic());

// Create a KeyInfo and add the KeyValue to it
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

// Instantiate the document to be signed
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc =
dbf.newDocumentBuilder().parse(new FileInputStream(args[0]));

// Create a DOMSignContext and specify the DSA PrivateKey and
// location of the resulting XMLSignature's parent element
DOMSignContext dsc = new DOMSignContext
(kp.getPrivate(), doc.getDocumentElement());

// Create the XMLSignature (but don't sign it yet)
XMLSignature signature = fac.newXMLSignature(si, ki);

// Marshal, generate (and sign) the enveloped signature
signature.sign(dsc);

// output the resulting document
OutputStream os;
if (args.length > 1) {
os = new FileOutputStream(args[1]);
} else {
os = System.out;
}

TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
}
}

例11-9 envelope.xml

<Envelope xmlns="urn:envelope">
</Envelope>

XML署名の生成

この例では、XMLデジタル署名APIを使用してXML署名を生成する方法を示します。より具体的には、この例はXMLドキュメントの包含XML署名を生成します。包含署名とは、署名対象の内容に含まれる署名です。この例はDOM (Document Object Model)を使用して、署名対象のXMLドキュメントおよびJSR 105 DOM実装を解析し、結果の署名を生成します。

XML署名およびそのさまざまなコンポーネントの基本的な知識が、このセクションの理解に役立ちます。詳細は、http://www.w3.org/TR/xmldsig-core/を参照してください。

署名するドキュメントのインスタンス化

最初に、JAXP DocumentBuilderFactoryを使用して、署名するXMLドキュメントを解析します。アプリケーションは、次のコード行を呼び出すことによって、DocumentBuilderFactoryのデフォルト実装を取得します。

DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();

ファクトリ名前空間認識も作成します。

dbf.setNamespaceAware(true);

次に、このファクトリを使用してDocumentBuilderのインスタンスを取得します。これはドキュメントの解析で使用されます。

DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(new FileInputStream(argv[0]));

公開鍵のペアの作成

公開鍵のペアを生成します。あとの例では、非公開鍵を使用して署名を生成します。KeyPairGeneratorを使用して、鍵のペアを作成します。この例では、2048バイトの長さのDSA KeyPairを作成します。

KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();

実際には、非公開鍵は通常は以前に生成されて、関連する公開鍵証明書とともにKeyStoreファイルに保存されています。

署名コンテキストの作成

署名を生成するための入力パラメータを含む、XMLデジタル署名XMLSignContextを作成します。DOMを使用しているので、DOMSignContext (XMLSignContextのサブクラス)をインスタンス化し、2つのパラメータを渡します。パラメータは、ドキュメントの署名に使用される秘密鍵と署名対象のドキュメントのルートです。

DOMSignContext dsc = new DOMSignContext
(kp.getPrivate(), doc.getDocumentElement());

XML署名の構築

Signature要素のさまざまな部分を組み合わせて、XMLSignatureオブジェクトにします。これらのオブジェクトはすべて、XMLSignatureFactoryオブジェクトを使用して、作成され組み合わせられます。アプリケーションは次のコード行を呼び出すことによって、XMLSignatureFactoryのDOM実装を取得します。

XMLSignatureFactory fac =
XMLSignatureFactory.getInstance("DOM");

次に示すように、さまざまなファクトリ・メソッドを呼び出してXMLSignatureオブジェクトのさまざまな部分を作成します。Referenceオブジェクトを作成して、そのオブジェクトに次のものを渡します。

- 署名対象のオブジェクトのURI (ここでは、ドキュメントのルートを示すURI ""を指定)

- DigestMethod (ここでは、SHA1を使用)

- 1つのTransform、包含Transform (署名値の計算の前に署名自体が削除されるように、包含署名で必要)

Reference ref = fac.newReference
("", fac.newDigestMethod(DigestMethod.SHA1, null),
Collections.singletonList
(fac.newTransform(Transform.ENVELOPED,
(TransformParameterSpec) null)), null, null);

次に、SignedInfoオブジェクトを作成します。これは、次に示すように、実際に署名されるオブジェクトです。SignedInfoを作成するときは、次のものをパラメータとして渡します。

- CanonicalizationMethod (ここでは、INCLUSIVEを使用してコメントを保持)

- SignatureMethod (ここでは、DSAを使用)

- Referencesのリスト(この場合は1つのみ)

SignedInfo si = fac.newSignedInfo
(fac.newCanonicalizationMethod
(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
(C14NMethodParameterSpec) null),
fac.newSignatureMethod(SignatureMethod.DSA_SHA1, null),
Collections.singletonList(ref));

次に、オプションのKeyInfoオブジェクトを作成します。このオブジェクトには、受け側が署名の検証に必要な鍵を見つけられるようにする情報が含まれています。この例では、公開鍵を含むKeyValueオブジェクトを追加します。KeyInfoおよびその様々なサブタイプを作成するには、KeyInfoFactoryオブジェクトを使用します。このオブジェクトは次に示すように、XMLSignatureFactoryのgetKeyInfoFactoryメソッドを呼び出すことによって取得できます。

KeyInfoFactory kif = fac.getKeyInfoFactory();

次に、KeyInfoFactoryを使用してKeyValueオブジェクトを作成し、KeyInfoオブジェクトに追加します。

KeyValue kv = kif.newKeyValue(kp.getPublic());
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

最後に、XMLSignatureオブジェクトを作成し、先に作成したSignedInfoおよびKeyInfoオブジェクトをパラメータとして渡します。

XMLSignature signature = fac.newXMLSignature(si, ki);

まだ実際には署名を生成していません。次のステップで生成します。

XML署名の生成

これで、署名を生成する準備が整いました。XMLSignatureオブジェクトでsignメソッドを呼び出して、次に示すように署名コンテキストを渡します。

signature.sign(dsc);

これで、結果のドキュメントには署名が含まれています。署名は、ルート要素の最後の子要素として挿入されました。

生成されるドキュメントの印刷または表示

次のコードを使用して、結果の署名済みドキュメントをファイルまたは標準出力に印刷できます。

OutputStream os;
if (args.length > 1) {
os = new FileOutputStream(args[1]);
} else {
os = System.out;
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));

12 セキュリティAPIの仕様

一般的なセキュリティ

- java.security

- javax.crypto

- javax.security.cert

- javax.crypto.spec

- java.security.spec

- java.security.interfaces

- javax.crypto.interfaces

- javax.rmi.ssl

証明書パス

java.security.cert

JAAS

- javax.security.auth

- javax.security.auth.callback

- javax.security.auth.kerberos

- javax.security.auth.login

- javax.security.auth.spi

- javax.security.auth.x500

Java GSS-API

- org.ietf.jgss

- com.sun.security.jgss

JSSE

- javax.net

- javax.net.ssl

- java.security.cert

Java SASL

javax.security.sasl

SSL/TLSベースのRMIソケット・ファクトリ

javax.rmi.ssl

XMLデジタル署名

- javax.xml.crypto

- javax.xml.crypto.dom

- javax.xml.crypto.dsig

- javax.xml.crypto.dsig.dom

- javax.xml.crypto.dsig.keyinfo

- javax.xml.crypto.dsig.spec

スマート・カードI/O

javax.smartcardio

13 削除対象としてマークされている推奨されていないセキュリティAPI

次のAPIは推奨されておらず、将来のリリースでの削除対象となっています。

jdeprscanツールを使用してこのAPIの依存性を確認できます。Java Platform, Standard Editionツール・リファレンスでjdeprscanを参照してください。

次のクラスは推奨されておらず、削除対象としてマークされています。

- com.sun.security.auth.PolicyFile

- com.sun.security.auth.SolarisNumericGroupPrincipal

- com.sun.security.auth.SolarisNumericUserPrincipal

- com.sun.security.auth.SolarisPrincipal

- com.sun.security.auth.X500Principal

- com.sun.security.auth.module.SolarisLoginModule

- com.sun.security.auth.module.SolarisSystem

次のメソッドは推奨されておらず、削除対象としてマークされています。

- java.lang.SecurityManager.getInCheck

- java.lang.SecurityManager.checkMemberAccess

- java.lang.SecurityManager.classDepth

- java.lang.SecurityManager.currentClassLoader

- java.lang.SecurityManager.currentLoadedClass

- java.lang.SecurityManager.inClass

- java.lang.SecurityManager.inClassLoader

- java.lang.SecurityManager.checkAwtEventQueueAccess

- java.lang.SecurityManager.checkTopLevelWindow

- java.lang.SecurityManager.checkSystemClipboardAccess

次のフィールドは推奨されておらず、削除対象としてマークされています。

- java.lang.SecurityManager.incheck

14 セキュリティ・ツール

Java Platform, Standard Editionツール・リファレンスでセキュリティのツールおよびコマンドを参照してください。

15 セキュリティのチュートリアル

- JavaチュートリアルのJava SEのセキュリティ機能トレール

- JAASチュートリアル

- Kerberosを使ったJava GSS-APIおよびJAASのチュートリアル

Oracleの法律上の注意点

コピーライト

Copyright © 1994-2018, Oracle and/or its affiliates. All rights reserved.

ライセンス制約の保証と結果的に生じる損害の免責

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

保証免責

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクルまでご連絡ください。

制限付権利について

このソフトウェアまたは関連ドキュメントを、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供する場合は、次の通知が適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs. No other rights are granted to the U.S. Government.

危険な用途への使用について

このソフトウェアまたはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアまたはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用する際、安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、Oracle Corporationおよびその関連会社は一切の責任を負いかねます。

商標と登録商標について

OracleおよびJavaはオラクルおよびその関連会社の登録商標です。その他の社名、商品名等は各社の商標または登録商標である場合があります。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

第三者のコンテンツ、製品、サービスに対する免責事項

このソフトウェアまたはハードウェア、そしてドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。適用されるお客様とOracle Corporationとの間の契約に別段の定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。適用されるお客様とOracle Corporationとの間の契約に定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても一切の責任を負いかねます。

Pre-General Availability (一般提供前)版のドキュメントについて

このドキュメントがPre-General Availability (一般提供前)版の場合:

このドキュメントはPre-General Availability (一般提供前)版であり、デモおよび暫定使用のみを目的としたものです。このソフトウェアを使用するハードウェアに限定するものではありません。Oracle Corporationおよびその関連会社は、このドキュメントに関して一切の責任を負わず、いかなる保証もいたしません。また、このドキュメントを使用したことによって損失、費用、あるいは損害が発生しても、一切の責任を負いかねます。

非公開のPre-General Availability (一般提供前)版のドキュメントについて

このドキュメントが非公開のPre-General Availability (一般提供前)版の場合:

このドキュメントに含まれる情報は、情報の共有のみを目的としたものであって、顧客諮問委員会(Customer Advisory Board)のメンバーとしてのお客様の立場でのみ、またはお客様のPre-General Availability Trial Agreement (Pre-GAトライアル契約)のみに従って検討されるべきものとします。マテリアル、コード、または機能の提供をお約束するものではなく、購入決定を行う際の判断材料とすべきではありません。このドキュメントに記載されている特徴または機能の開発、リリースおよび時期については、オラクルの裁量により決定されます。

このドキュメントには、ソフトウェアまたは印刷物などの形式を問わず、オラクルが独占的な権利を有する財産的情報が含まれています。この機密資料へのアクセスと使用は、お客様とオラクルとの間で締結され、お客様が遵守に同意した、Oracle Master Agreement、Oracle License and Oracle Services Agreement (オラクル・ライセンスおよびサービスに関する契約書)、Oracle Partner Network Agreement、Oracle Distribution Agreementまたはその他のライセンス契約の条件に従うものとします。このドキュメントとその内容の開示、コピー、複製および配布には、オラクルによる事前の承諾を必要とします。このドキュメントはライセンス契約の一部となるものではなく、オラクルおよびその子会社や関連会社との契約を構成するものではありません。

ドキュメントのアクセシビリティ

オラクルのアクセシビリティについての詳細情報は、Oracle Accessibility ProgramのWebサイト(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc)を参照してください。

Oracle Supportへのアクセス

サポートをご契約のお客様には、My Oracle Supportを通して電子支援サービスを提供しています。詳細情報は(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info)か、聴覚に障害のあるお客様は(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs)を参照してください。