ネットワークを通じてやり取りされるデータには、意図された受信者以外の人も、簡単にアクセスできます。 データにパスワードやクレジット・カード番号などの個人情報が含まれる場合、権限のない者がデータを理解できないよう、手段を講じる必要があります。 また、意図的であるかどうかにかかわらず、通信中にデータが変更されていないことを確認することも重要です。 Secure Sockets Layer (SSL)およびTransport Layer Security (TLS)は、ネットワークを通じたデータの送信時に、データの機密性および整合性を保護するために設計されたプロトコルです。
Java Secure Socket Extension (JSSE)により、セキュアなインターネット通信が可能になります。 これにより、JavaバージョンのSSLおよびTLSプロトコルのフレームワークおよび実装が提供されます。また、データ暗号化、サーバー認証、メッセージの整合性の他、オプションでクライアント認証の機能が含まれます。 JSSEを使用すると、開発者はHTTP、Telnet、FTPなど、TCP/IP上のアプリケーション・プロトコルを実行するクライアントとサーバーの間で、セキュアなデータのやり取りを実現できます。 TLSの概要は、「Transport Layer Security (TLS)プロトコルの概要」を参照してください。
JSSEは、基盤となる複雑なセキュリティ・アルゴリズムやハンドシェーク・メカニズムを抽象化することにより、識別するのが難しく、しかし危険なセキュリティ上の弱点が生まれるリスクを最小限に抑えます。 また、開発者がそれをアプリケーションに直接統合できる構築ブロックとして使用すると、アプリケーション開発が簡単になります。
JSSEは、アプリケーション・プログラミング・インタフェース(API)フレームワークと、そのAPIの実装を提供します。 JSSE APIは、java.security
およびjava.net
パッケージによって定義されたコア・ネットワークおよび暗号化サービスを補い、拡張されたネットワーク・ソケット・クラス、トラスト・マネージャ、キー・マネージャ、SSLコンテキストおよびソケット作成動作をカプセル化するソケット・ファクトリのフレームワークを提供します。 SSLSocket
クラスはブロック入出力モデルに基づいているため、Java Development Kit (JDK)には、実装で独自の入出力メソッドを選択できるようにするために非ブロックSSLEngine
クラスが含まれます。
JSSE APIでは、次のセキュリティ・プロトコルがサポートされています。
これらのセキュリティ・プロトコルは、通常の双方向のストリーム・ソケットをカプセル化し、JSSE APIは認証、暗号化および整合性保護の透過的なサポートを追加します。
JSSEはJava SE プラットフォームのセキュリティ・コンポーネントで、Java Cryptography Architecture (JCA)フレームワークの至るところで見られる同じ設計方針に基づいています。 暗号化に関するセキュリティ・コンポーネントのこのフレームワークにより、実装の独立性と、可能な場合にはアルゴリズムの独立性を実現できます。 JSSEはJCAフレームワークによって定義された暗号化サービス・プロバイダを使用します。
Java SE プラットフォームの他のセキュリティ・コンポーネントには、Java認証・承認サービス(JAAS)およびJavaセキュリティ・ツールがあります。 JSSEはJCAと同じ概念およびアルゴリズムを多く含んでいますが、単純なストリーム・ソケットAPIの下でこれらを自動的に適用します。
JSSE APIは、その他のSSL/TLSプロトコルと公開キー・インフラストラクチャ(PKI)実装をシームレスにプラグインできる設計になっています。 開発者が、リモート・ホストの信頼性やリモート・ホストに送信する認証キー・データを決定するロジックを提供することもできます。
JSSEには次のような重要な特長があります。
SSLSocket
、SSLServerSocket
、およびSSLEngine
)JSSE標準APIは、javax.net
およびjavax.net.ssl
パッケージで利用でき、次を提供します。
SSLEngine
)のストリームを生成および使用する非ブロッキング・エンジン。OracleのJava SEの実装には、SunJSSE
という名前のJSSEプロバイダが含まれています。このプロバイダは、JCAに事前インストールされ、事前登録されています。 このプロバイダが提供する暗号化サービスは次のとおりです。
このプロバイダの詳細は、Oracleプロバイダ・ドキュメントの「SunJSSE」セクションで説明されています。
次のリストにはオンライン・ドキュメントのリンクと、関連サブジェクトの文書名を示しています。
このドキュメントでは、暗号化に関するいくつかの用語が使用されています。 このセクションでは、こうした用語を定義します。
通信している相手側の識別情報を確認するプロセスです。
暗号化パラメータの組合わせで、認証、キー合意、暗号化および整合性保護に使用するセキュリティ・アルゴリズムおよびキーのサイズを定義します。
デジタル署名付きの文で、あるエンティティ(人や会社など)の識別情報および公開キーの内容を保証します。 証明書は、自己署名または認証局(CA)によって発行できます - 他のエンティティに対して有効な証明書を発行することが信頼できるエンティティ。 よく知られているCAには、Comodo、DigiCertおよびGoDaddyなどがあります。 X509は、JDKのkeytool
で管理できる一般的な証明書形式です。
データの任意のブロックから比較的小さな固定サイズのビットの文字列(ハッシュと呼ばれる)を生成するために使われるアルゴリズム。 暗号化ハッシュ関数はチェックサムに似ており、3つの主な特性があります。一方向の関数であるため、ハッシュからオリジナルデータを生成することはできません。オリジナルデータをわずかに変更しても、ハッシュでは大きな変更になります。暗号化キーは必要ありません。
短縮形としてプロバイダとだけ呼ばれることもあり、Java暗号化アーキテクチャ(JCA)ではそれを、特定の暗号化アルゴリズムの1つまたは複数のエンジン・クラスを実装するパッケージ(または一連のパッケージ)と定義しています。 エンジン・クラスは、具体的な実装のない抽象的な方法で暗号化サービスを定義します。
「暗号化/復号化」を参照してください。
デジタル署名とは、手書きの署名のデジタル版です。 これは、ネットワークで伝送されるデータが、それを送信したと主張する人物からのものであり、送信中にデータが変更されていないことを保証するものです。 たとえば、RSAベースのデジタル署名を計算するには、まずデータの暗号化ハッシュを計算し、次に送信者の非公開キーでハッシュを暗号化します。
暗号化は複雑なアルゴリズムを使用して、元のメッセージ(クリアテキスト)を、復号化しないかぎり、その内容を理解できないエンコードされたメッセージ(暗号テキスト)に変換するプロセスです。 復号化とは、暗号テキストからクリアテキストを生成する逆のプロセスです。
データの暗号化および復号化に使用するアルゴリズムは一般に、秘密キー(対称)暗号化と公開キー(非対称)暗号化の2つのカテゴリに分けられます。
2つのソケット同士が新しいセッションや既存のセッションの使用に同意するネゴシエーションのフェーズです。 ハンドシェーク・プロトコルは、レコード・プロトコルを介して交換される一連のメッセージです。 ハンドシェークの終了時に、セッションの接続に固有の暗号化キーや、整合性を保護するためのキーが、キー合意による秘密に基づいて新たに生成されます。
二者が協力して共通キーを確立するための方法です。 それぞれの側が一定のデータを生成して交換します。 そのあと、2つのデータが組み合わされて、1つのキーが生成されます。 適正な非公開初期化データを保持しているユーザーのみが、最終的なキーを取得できます。 Diffie-Hellman (DH)は、一般的なキー合意アルゴリズムの一例です。
キーを交換する方法です。 一方の側が秘密キーを生成し、標準的にはRSAにより、ピアの公開キーを使用して暗号化します。 データがピアに送信され、ピアは対応する秘密キーを使用してキーを復号化します。
キー・マネージャとトラスト・マネージャは、それぞれのキー・データにキーストアを使用します。 キー・マネージャはキーストアを管理し、ユーザーを他のユーザーに対して承認する場合に使用するなど必要に応じて、他のユーザーに公開キーを提供します。 トラスト・マネージャは、管理するトラストストアの情報に基づいて、トラストの対象者を決定します。
キーストアは、キー・データのデータベースです。 キー・データにはさまざまな用途があり、それには認証やデータ整合性も含まれます。 利用できるキーストアには様々なタイプがあり、その中にはPKCS12やOracleのJKSも含まれます。
一般に、キーストア情報は、キー・エントリと信頼される証明書エントリ2つのカテゴリに分類できます。 キー・エントリはエンティティの識別情報とその秘密キーから構成されており、様々な暗号化の目的で使用できます。 これとは対照的に、信頼される証明書のエントリには、公開キーとそのエンティティの識別情報しか含まれていません。 したがって、javax.net.ssl.KeyManager
の場合など、秘密キーが必要な場合は、信頼される証明書エントリを使用することはできません。 JKSのJDK実装では、キーストアにキーのエントリと、信頼される証明書エントリの両方を含めることができます。
トラストストアとは、トラストの対象を決めるときに使用するキーストアです。 すでに信頼しているエンティティからデータを受け取る場合、およびそのエンティティが発信元を名乗るエンティティであることを確認できる場合は、データは実際にそのエンティティから届いたものであると仮定できます。
ユーザーがそのエンティティを信頼する場合にのみ、エントリをトラストストアに追加する必要があります。 ユーザーは、キーのペアを生成するか、証明書をインポートすることにより、そのエントリにトラストを与えます。 トラストストア内のすべてのエントリは信頼されたエントリとみなされます。
2つの異なるキーストア・ファイルを持つと便利な場合があります。1つはキー・エントリのみのファイル、もう1つはCA証明書を含む信頼された証明書エントリを含むファイルです。 前者には機密性のある情報が含まれますが、後者には含まれません。 単独のキーストア・ファイルではなく、2つのファイルを使用すると、独自の証明書(および対応する秘密キー)と他の証明書を論理的に区別した明確な区分が提供されます。 秘密キーの保護を強化するには、アクセスが制限されたキーストアにそれらを保存し、必要に応じて、より公的にアクセスできるキーストアで信頼される証明書を提供することもできます。
信頼できない媒体に送信または格納された情報の整合性を、秘密キーに基づいてチェックする方法を提供します。 通常、MACは秘密キーを共有する2つの当事者間で、お互いが送信する情報を検証するために使用されます。
暗号化ハッシュ機能に基づくMACメカニズムは、HMACと呼ばれます。 HMACは、共有する秘密キーと組み合せて、Secure Hash Algorithm (SHA-256)などの暗号化ハッシュ関数とともに使用できます。 HMACについては、RFC 2104で規定されています。
2つのキーを生成する暗号化アルゴリズムを使用するキー暗号化システムです。 一方のキーは公開されますが、他方は秘密のままです。 公開キーと非公開キーでは、逆の暗号化処理がなされ、一方のキーで暗号化したものを他方のキーで復号化します。 公開キー暗号化は、非対称暗号化とも呼ばれます。
すべてのデータ(アプリケーション・レベルであるかハンドシェーク・プロセスの一部であるかに関係なく)を独立したデータのレコードにパッケージ化するプロトコルで、TCPストリーム・ソケットがアプリケーション・バイト・ストリームをネットワーク・パケットに変換するのとよく似ています。 個々のレコードは、現在の暗号化キーと整合性保護キーによって保護されます。
データの暗号化と復号化に同じキーを使用する暗号化アルゴリズムを使用する暗号化システム。 秘密キー暗号化は対称暗号化とも呼ばれます。
認証されたピア識別情報、暗号化方式群、キー合意の秘密を含む名前付きの状態情報のコレクションで、セキュアなソケット・ハンドシェークを通じてネゴシエーションが行われ、複数のセキュアなソケット・インスタンス間で共有できます。
「キー・マネージャ/トラスト・マネージャ」を参照してください。
「キーストア/トラストストア」を参照してください。
トランスポート・レイヤー・セキュリティ(TLS)は、Webで暗号化を実装する場合に最もよく使用されるプロトコルです。 TLSは、ネットワークでセキュアな通信を行うために暗号化プロセスを組み合わせて使用します。 「Transport Layer Security (TLS)プロトコルの概要」ページでは、TLSおよびそれが使用する暗号化プロセスの概要を示します。
セキュアな通信を行うには、接続の両側がSSL対応であることが必要です。 JSSE APIの接続のエンドポイント・クラスは、SSLSocket
およびSSLEngine
です。 図「SSLSocketおよびSSLEngineの作成に使用するJSSEクラス」では、SSLSocket
およびSSLEngine
の作成に使用されるメジャー・クラスが論理的な順序で配置されています。 図の後のテキストで、図の内容を説明しています。
SSLSocket
はSSLSocketFactory
またはイン・バウンド接続を受け取るSSLServerSocket
によって作成されます。 SSLServerSocket
はSSLServerSocketFactory
で作成されます。 SSLSocketFactory
およびSSLServerSocketFactory
オブジェクトはどちらもSSLContext
で作成されます。 SSLEngine
は、SSLContext
によって直接作成され、アプリケーションに依存してすべての入出力を処理します。
ノート: raw SSLSocket
またはSSLEngine
クラスを使用する場合は、データの送信前に必ずピアのクレデンシャルをチェックしてください。 JDK 7以降、SSL/TLSハンドシェイク中にエンドポイントの識別/検証手順を処理できます。 メソッドSSLParameters.setEndpointIdentificationAlgorithm
を参照してください。 たとえば、URL内のホスト名は、ピアのクレデンシャル内のホスト名と一致する必要があります。 ホスト名が検証されない場合、URL不正行為によってアプリケーションが悪用される可能性があります。
コアJSSEクラスは、javax.net
およびjavax.net.ssl
パッケージの一部です。
抽象クラスjavax.net.SocketFactory
は、ソケットの作成に使われます。 このクラスのサブクラスは、ソケットの特定のサブクラスを作成し、パブリック・ソケット・レベルの機能を追加するための汎用フレームワークを提供するファクトリです。 たとえば、「SSLSocketFactoryおよびSSLServerSocketFactory」を参照してください。
javax.net.ServerSocketFactory
抽象クラスはSocketFactory
クラスに似ていますが、サーバー・ソケットの作成に特化して使われます。
ソケット・ファクトリは、構築されるソケットに関する様々なポリシーを取得するための簡単な方法であり、ソケットを要求する特別なコード構成を必要としない方法でそれらのソケットを生成します。
java.net.Socket
(またはjavax.net.ssl.SSLSocket
)のサブクラスにすることができます。そうすれば、圧縮、セキュリティ、レコード宣言、統計情報収集、ファイアウォール・トンネリングなどの機能の新しいAPIを直接公開できます。javax.net.ssl.SSLSocketFactory
クラスは、セキュアなソケットを作成するファクトリとして動作します。 このクラスは、javax.net.SocketFactoryの抽象サブクラスです。
セキュアなソケット・ファクトリは、セキュアなソケットの作成と初期設定の詳細情報をカプセル化します。 これには、認証キー、ピア証明書の検証、使用できる暗号化方式群などが含まれます。
javax.net.ssl.SSLServerSocketFactory
クラスはSSLSocketFactory
クラスに似ていますが、サーバー・ソケットの作成に特化して使われます。
次の方法でSSLSocketFactory
を取得できます。
SSLSocketFactory.getDefault()
staticメソッドを呼び出してデフォルトのファクトリを取得します。SSLSocketFactory
を指定できるSSLSocketFactory
パラメータを持つメソッドを含めることができます(javax.net.ssl.HttpsURLConnection
など)。 通常、デフォルトのファクトリはサーバー認証だけをサポートするように構成されています。このため、デフォルトのファクトリで作成されたソケットは、一般的なTCPソケット以上にクライアントの情報を漏らすことはありません。
ソケットを作成して使用するクラスの多くは、ソケットの作成方法を詳しく知る必要はありません。 パラメータとして渡されたソケット・ファクトリを介してソケットを作成するのは、ソケット構成の詳細を分離し、ソケットを作成して使用するクラスの再利用性を高めるよい方法です。
新しいソケット・ファクトリ・インスタンスを作成するには、独自のソケット・ファクトリ・サブクラスを実装するか、ソケット・ファクトリのファクトリとして動作するクラスをべつに使用します。 このようなクラスの1つの例がSSLContext
で、これはプロバイダ・ベースの構成クラスとしてJSSE実装に提供されます。
javax.net.ssl.SSLSocket
クラスは標準のJava java.net.Socket
クラスのサブクラスです。 標準的なソケット・メソッドをすべてサポートし、セキュアなソケットに固有のメソッドを追加します。 このクラスのインスタンスは、その作成に使用されたSSLContextをカプセル化します。 ソケット・インスタンスのセキュアなソケット・セッションの作成を管理するAPIもありますが、トラストおよびキー管理は直接公開されません。
javax.net.ssl.SSLServerSocket
クラスはSSLSocket
クラスに似ていますが、サーバー・ソケットの作成に特化して使われます。
ピア・スプーフィングを防ぐには、SSLSocket
に提示された資格証明を常に確認する必要があります。 「暗号スイートの選択とリモート・エンティティの検証」を参照してください。
ノート: SSLとTLSプロトコルは複雑なので、接続時の受信バイトがハンドシェークのデータとアプリケーション・データのどちらなのかを予測し、現在の接続状態にどのような影響を与えるか(処理を中断させることもある)を予測するのは困難です。 Oracle JSSEの実装では、SSLSocket.getInputStream()
によって取得されたオブジェクトのavailable()
メソッドは、SSL接続で正常に復号化されても、アプリケーションではまだ読み込まれていないデータのバイト数を返します。
SSLSocket
のインスタンスは、次のいずれかの方法で取得できます。
SSLSocket
は、そのクラスの複数のcreateSocket()
メソッドのうちの1つを使用して、SSLSocketFactoryのインスタンスを作成できます。SSLSocket
はSSLServerSocket
クラスのaccept()
メソッドを使用して作成できます。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ホスト名ルールをオーバーライドできます。
TLSはますます人気が高まっています。 広範な計算プラットフォームやデバイスを包含するさまざまなアプリケーションで使用されています。 この人気に加えて、アプリケーションのパフォーマンス、スケーラビリティ、フットプリントおよびその他の要件を満たすために、異なるI/Oおよびスレッド・モデルでTLSを使用することが求められています。 I/Oチャネル、非同期I/Oチャネル、任意の入出力ストリームおよびバイト・バッファをブロックおよび非ブロックするTLSを使用する要求があります。 また、数千のネットワーク接続を管理することが必要な、非常に拡張性の高いパフォーマンス重視の環境で使用することも求められています。
Java SEのSSLEngine
クラスを使用したI/Oトランスポート・メカニズムの抽象化により、アプリケーションはトランスポートに依存しない方法でTLSプロトコルを使用できるため、アプリケーション開発者はニーズに最も適したトランスポート・モデルおよびコンピューティング・モデルを選択できます。 この新しい抽象化により、非ブロック入出力チャネルや他の入出力モデルをアプリケーションで使用できるだけでなく、異なるスレッド・モデルにも対応できます。 事実上これは、入出力とスレッドの決定がアプリケーション開発者に委ねられることになります。 こうした柔軟性のため、アプリケーション開発者は、それ自体が複雑な問題でもある入出力とスレッドを管理するとともに、SSL/TLSプロトコルをある程度理解する必要があります。 このように、新しい抽象は高度なAPIなので、初心者はSSLSocket
を使用してください。
Java Generic Security Services (Java GSS)やJava Simple Authentication Security Layer (Java SASL)などの他のJavaプログラミング言語APIのユーザーは、アプリケーションがデータをトランスポートする役割を担うことの類似点に気付くでしょう。
コア・クラスは、 javax.net.ssl.SSLEngine
です。 TLS状態マシンをカプセル化し、SSLEngine
クラスのユーザーが提供するインバウンドおよびアウトバウンドのバイト・バッファを操作します。 図「SSLEngineを介したデータのフロー」は、アプリケーションからSSLEngine
、トランスポート・メカニズムへのデータのフロー、およびトランスポート・メカニズムへのデータのフローを示しています。
左側に示されるアプリケーションは、アプリケーションのプレーンテキスト・データをアプリケーション・バッファに供給し、それをSSLEngine
に渡します。 SSLEngine
オブジェクトは、バッファまたはハンドシェーク・データに含まれるデータを処理して、TLSエンコード・データを生成し、アプリケーションによって提供されるネットワーク・バッファに配置します。 次にアプリケーションは、右側に示されている適切なトランスポートを使用して、ネットワーク・バッファの内容をピアに送信する役割を実行します。 ピア(輸送を介して)からTLSエンコード・データを受信すると、アプリケーションはデータをネットワーク・バッファに配置し、それをSSLEngine
に渡します。 SSLEngine
オブジェクトは、ネットワーク・バッファの内容を処理し、ハンドシェーク・データまたはアプリケーション・データを生成します。
SSLEngine
クラスのインスタンスは次のいずれかの状態になります。
SSLEngine
は作成および初期化されていますが、まだ使用されていません。 この段階では、アプリケーションにより、SSLEngine
固有のあらゆる設定(暗号化方式群の有効化、SSLEngine
がクライアント・モードとサーバー・モードのどちらでハンドシェークを行うかなど)を行うことができます。 ただし、ハンドシェイクが開始されると、新しい設定(クライアント/サーバー・モードを除く)が次のハンドシェイクに使用されます。 SSLSession
が確立されるまで、2つのピアが通信パラメータを交換するプロシージャです。 この段階では、アプリケーション・データは送信できません。 SSLEngine
を流れる可能性があります。 アウトバウンド・アプリケーション・メッセージは暗号化され、データの整合性が確保されます。インバウンド・メッセージでは、この逆の手続きが行われます。 SSLEngine
構成設定は、次のハンドシェイクまで使用されません。 SSLEngine
をクローズし、基礎となるトランスポート・メカニズムを閉じる前に残りのメッセージをピアに送信または受信する必要があります。 いったん閉じられたエンジンを再利用することはできません。新しいSSLEngine
を作成する必要があります。 エンジンのステータスおよびアプリケーションが実行するアクションを示すために、「非ブロッキングSocketChannelの使用」の例に示すように、SSLEngine.wrap()
およびSSLEngine.unwrap()
メソッドはSSLEngineResult
インスタンスを返します。 このSSLEngineResult
オブジェクトには、エンジンの全体的なステータスとハンドシェークのステータスの2つのステータス情報が格納されます。
全体的なステータスは、SSLEngineResult.Status
enumによって表されます。 次のステータスがあります。
OK
CLOSED
SSLEngine
が閉じられたか、操作はすでに閉じられていたために完了できませんでした。BUFFER_UNDERFLOW
BUFFER_OVERFLOW
「BUFFER_UNDERFLOWおよびBUFFER_OVERFLOWの処理」の例は、SSLEngine.unwrap()
メソッドのBUFFER_UNDERFLOW
およびBUFFER_OVERFLOW
ステータスを処理する方法を示しています。 SSLSession.getApplicationBufferSize()
およびSSLSession.getPacketBufferSize()
を使用して、バイト・バッファのサイズを決定します。
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 ... }
ハンドシェークのステータスは、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
はアプリケーションが2つのアクションを実行する必要があることを示すことができます: ハンドシェークに対応したものと、wrap()
およびunwrap()
メソッドの全体的なステータスを表すもの。 たとえば、エンジンは、単一のSSLEngine.unwrap()
コールの結果として、入力データが正常に処理されたことを示すSSLEngineResult.Status.OK
を返し、アプリケーションがピアからさらにTLSエンコードされたデータを取得し、ハンドシェイクを続行できるように再度SSLEngine.unwrap()
に指定する必要があることを示すSSLEngineResult.HandshakeStatus.NEED_UNWRAP
を返すことができます。 お気付きのとおり、先の例はかなり単純化されていますが、これらすべてのステータスを適正に処理するにはコードをかなり拡張する必要があります。
「ハンドシェイク・ステータスおよび全体ステータスのチェックと処理」の例は、ハンドシェイク・ステータス、および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 ... }
この項では、SSLEngineオブジェクトを作成し、それを使用してTLSデータを生成および処理する方法を示します。
SSLEngine
オブジェクトを作成するには、SSLContext.createSSLEngine()
メソッドを使用します。 次に、クライアントまたはサーバーとして動作するようにエンジンを構成し、使用する暗号化方式群やクライアント認証が必要かどうかなどの他の構成パラメータも設定します。 SSLContext.createSSLEngine
メソッドは、javax.net.ssl.SSLEngine
オブジェクトを作成します。
「JKSをキーストアとするTLS用のSSLEngineクライアントの作成」の例は、JKSをキーストアとして使用するTLS用のSSLEngine
クライアントを作成する方法を示しています。
ノート: サーバー名とポート番号は、サーバーとの通信には使用されません(すべてのトランスポートはアプリケーションが担当します)。 それらは、SSLセッションキャッシングおよび取得すべきサーバー・クレデンシャルを決定するために、Kerberosベースの暗号化方式群の実装に使用するJSSEプロバイダへのヒントです。
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 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);
2つの主要なSSLEngine
メソッドはwrap()
とunwrap()
です。 それらは、それぞれネットワーク・データの生成と消費を担当します。 SSLEngine
オブジェクトの状態に応じて、このデータはハンドシェーク・データかアプリケーション・データになります。
それぞれのSSLEngine
オブジェクトには、存続期間中に数種類の段階があります。 アプリケーション・データを送信または受信できるようにするには、SSL/TLSプロトコルで、暗号化パラメータを確立するためのハンドシェークが必要です。 このハンドシェークでは、SSLEngine
オブジェクトによる一連のやり取りのステップが必要です。
初期ハンドシェーク時に、wrap()
およびunwrap()
メソッドはハンドシェーク・データを生成および消費し、アプリケーションはデータのトランスポートを担当します。 wrap()
およびunwrap()
メソッド・シーケンスは、ハンドシェークが終了するまで繰り返されます。 それぞれのSSLEngine
操作によりSSLEngineResult
クラスのインスタンスが生成され、その中のSSLEngineResult.HandshakeStatus
フィールドは、ハンドシェークを進めるために実行する必要のある次の動作を決定するために使用されます。
図「TLSハンドシェイク中の状態マシン」は、典型的なTLSハンドシェイク中の状態マシンと、対応するメッセージおよびステータスを示しています:
ハンドシェイクのステータスが決定される前に、次のステップが実行されます:
この図は、可能なハンドシェイク・ステータスの一部を示しています。 セクション「SSLEngine操作ステータスの理解」では、これらのステータスについて詳細に説明します:
NEED_TASK
NEED_WRAP
NEED_UNWRAP
FINISHED
ハンドシェークの完了時に、wrap()
をさらに呼び出すと、アプリケーション・データおよびパッケージを消費してトランスポートしようとします。 unwrap()
メソッドはその逆を試みます。
ピアにデータを送信するには、まずアプリケーションがSSLEngine.wrap()
を介して送信するデータを提供し、対応するSSL/TLSエンコード・データを取得します。 次にアプリケーションは、選択したトランスポート・メカニズムを使用してエンコード・データをピアに送信します。 アプリケーションは、トランスポート・メカニズムを介してピアからSSL/TLSエンコード・データを受け取ると、SSLEngine.unwrap()
を介してそのデータをSSLEngine
に送り、ピアによって送信されたプレーン・テキスト・データを取得します。
「非ブロッキングSocketChannelの使用」の例は、非ブロッキングSocketChannel
を使用してピアと通信するSSLアプリケーションを示しています。
ノート: この例は、非ブロックSocketChannel
を組み込んだSelector
を使用することにより、堅牢性と拡張性を高めることができます。
例「非ブロッキングSocketChannelの使用」では、文字列hello
は、例「JKSをキーストアとするTLS用のSSLEngineクライアントの作成」で作成したSSLEngine
を使用してエンコードすることによってピアに送信されます。 これは、バイト・バッファの大きさを決定するために、SSLSession
からの情報を使用しています。
// 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 to use 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 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 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 ... }
「非ブロックSocketChannelからのデータの読取り」の例は、同じ非ブロッキングSocketChannel
からデータを読み取って、「JKSをキーストアとするTLS用のSSLEngineクライアントの作成」の例で作成したSSLEngine
を使用してプレーン・テキスト・データを抽出する方法を示しています。 このコードが反復されるごとに、ハンドシェーク処理が進行しているかどうかに応じて、プレーン・テキストが生成されたり、生成されなかったりします。
// Read SSL/TLS 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 ... }
ハンドシェーク時に、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接続を正しい順序でシャットダウンするため、SSL/TLSプロトコルではクローズ・メッセージを送信する必要があります。 したがって、アプリケーションがSSL/TLS接続を終了する場合は、最初にSSLEngine
からクローズ・メッセージを取得し、トランスポート・メカニズムを使用してそれらのメッセージをピアに送信して、最後にトランスポート・メカニズムをシャットダウンします。 例6にこれを示します。
// 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();
SSLEngine
を明示的に閉じるアプリケーションに加え、SSLEngine
は、ピアによって(ハンドシェーク・データを処理している間のクローズ・メッセージの受取りによる)、またはアプリケーション・データやハンドシェーク・データを処理している間に、SSLException
をスローすることによって示される、エラーが発生しているSSLEngine
によって閉じられることがあります。 そのような場合、アプリケーションはSSLEngine.wrap()
を呼び出してクローズメッセージを取得し、SSLEngine.isOutboundDone()
がtrue
値を返すまで(例6に示すように)、またはSSLEngineResult.getStatus()
がCLOSED
を返すまで、それをピアに送信すべきです。
正常なシャットダウンに加え、クローズメッセージが交換される前にトランスポート・リンクを切断する非常シャットダウンもあります。 前の例では、アプリケーションは非ブロックSocketChannel
からの読取りを実行しようとすると-1
またはIOExceptionを受け取り、非ブロックSocketChannelへの書込みを実行しようとするとIOExceptionを受け取ります。 入力データの最後に達したら、engine.closeInbound()
を呼び出して、SSLEngine
によって、リモートのピアがSSL/TLSの観点から正常に閉じたことを検証すべきです。 次にアプリケーションは、例6の手順を使用して、正常なシャットダウンを試みてください。 SSLSocket
と異なり、SSLEngine
を使用するアプリケーションは、多くの状態移行、ステータスおよびプログラミングを処理する必要があります。 SSLEngine
ベースのアプリケーションの作成の詳細は、「SSLEngineの使用を示すサンプル・コード」を参照してください。
javax.net.ssl.SSLSession
インタフェースは、SSLSocket
またはSSLEngine
接続の2つのピアの間でネゴシエーションされたセキュリティ・コンテキストを表します。 一度確立されたセッションは、同じ2つのピアの間で接続されるそのあとのSSLSocket
またはSSLEngine
オブジェクトによっても共有できます。
場合によっては、ハンドシェーク中に取り決めたパラメータが、後のハンドシェークでトラストについて判断を下す際に必要になることもあります。 たとえば、有効な署名アルゴリズムのリストによって、認証に使用できる証明書タイプが制限されることがあります。 SSLSession
はハンドシェーク時に、SSLSocket
またはSSLEngine
のgetHandshakeSession()
を呼び出すことによって取得できます。 TrustManager
またはKeyManager
の実装により、getHandshakeSession()
メソッドを使用して、それらが判断を下す際に役立つセッション・パラメータに関する情報を取得できます。
完全に初期化されたSSLSession
には暗号化方式群が含まれ、これは、リモート・ピアのネットワーク・アドレスに関する権限のないヒントと同様、セキュアなソケットの通信でも使用され、作成や最後の使用の時点などで、管理情報としても使用されます。 セッションには、SSLSocket
またはSSLEngine
接続による通信を暗号化して整合性を保証する暗号キーを作成するために使用される、ピア間でネゴシエーションされた共用マスターとなる秘密も含まれます。 このマスターとなる秘密の値は、基盤となるセキュアなソケット実装のみに伝えられ、SSLSession
APIによって公開されません。
Java SE では、TLS 1.2セッションはSSLSession
の実装であるExtendedSSLSession
によって表されます。 ExtendedSSLSession
クラスは、ローカル実装およびピアによってサポートされる署名アルゴリズムを記述するメソッドを追加します。 ExtendedSSLSession
インスタンスで呼び出されるgetRequestedServerNames()
メソッドは、要求されたServer Name Indication (SNI)拡張でSNIServerName
オブジェクトのリストを取得するために使用します。 サーバーは、要求されたサーバー名を使用して、適切な認証証明書の選択やセキュリティ・ポリシーのその他の側面をガイドする必要があります。 クライアントは、要求されたサーバー名を使用して、ピアの識別情報のエンドポイントの識別やセキュリティ・ポリシーのその他の側面をガイドする必要があります。
SSLSession
でのgetPacketBufferSize()
およびgetApplicationBufferSize()
メソッドの呼出しは、SSLEngine
によって使用される適切なバッファ・サイズを決定するために使用します。
ノート: SSL/TLSプロトコルは、実装が最大16Kバイト(KB)のプレーン・テキストを含むパケットを生成することを指定します。 ただし、一部の実装はこの指定に違反し、32Kバイトまでの大きいレコードを生成します。 SSLEngine.unwrap()
コードが大きいインバウンド・パケットを検出した場合、SSLSession
から返されるバッファ・サイズは動的に更新されます。 アプリケーションは常に、必要に応じてBUFFER_OVERFLOW
およびBUFFER_UNDERFLOW
のステータスをチェックして、対応するバッファを拡大するべきです。 SunJSSE
は、常に標準に準拠した16 KBレコードを送信し、着信32 KBレコードを許可します。 回避策については、「JSSEのカスタマイズ」のシステム・プロパティjsse.SSLEngine.acceptLargeFragments
を参照してください。
HTTPSプロトコルはHTTPプロトコルに似ていますが、データを要求または受信する前に、まずSSL/TLSソケットを利用してセキュアなチャネルを確立してピアの識別情報を検証します。 javax.net.ssl.HttpsURLConnection
クラスはjava.net.HttpsURLConnection
クラスを拡張し、HTTPSに固有の機能のサポートを追加します。 HTTPS URLの構築および使用方法の詳細は、java.net.URL
、java.net.URLConnection
、java.net.HttpURLConnection
およびjavax.net.ssl.HttpURLConnection
クラスに関するAPIの仕様のセクションを参照してください。
HttpsURLConnection
インスタンスを取得する際、URLConnection.connect()
メソッドを使用して実際にネットワーク接続を開始する前に、複数のHTTPおよびHTTPSパラメータを構成できます。 これらについては、次を参照してください。
状況によっては、HttpsURLConnection
のインスタンスによって使用されるSSLSocketFactory
を指定した方がよい場合があります。 たとえば、デフォルト実装ではサポートされないプロキシ・タイプを使用してトンネリングを行いたいと考える場合があります。 新しいSSLSocketFactory
は、すでに必要なトンネリングの完了したソケットを返すことができます。このため、HttpsURLConnection
は追加のプロキシを使用できます。
HttpsURLConnection
クラスには、ロード時に割り当てられたデフォルトのSSLSocketFactory
があります(これはSSLSocketFactory.getDefault()
メソッドによって返されるファクトリです)。 以降、HttpsURLConnection
のインスタンスは、staticメソッドHttpsURLConnection.setDefaultSSLSocketFactory
によってクラスに新しいデフォルトのSSLSocketFactory
が割り当てられるまで、現在のデフォルトのSSLSocketFactory
を継承します。 HttpsURLConnection
のインスタンスが作成された後、setSSLSocketFactory()
メソッドへの呼出しにより、このインスタンス上の継承されたSSLSocketFactory
をオーバーライドできます。
ノート: デフォルトのstatic SSLSocketFactory
の変更はHttpsURLConnection
の既存のインスタンスに影響しません。 既存のインスタンスを変更するには、setSSLSocketFactory()
メソッドの呼出しが必要です。
getSSLSocketFactory()
メソッドまたはgetDefaultSSLSocketFactory()
メソッドを呼び出すことにより、インスタンスごと、またはクラスごとにSSLSocketFactory
を取得できます。
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つまたは複数の暗号化サービス・プロバイダ(プロバイダ)パッケージで提供されることがあります。 プロバイダとエンジン・クラスの詳細は、「Java暗号化アーキテクチャ・リファレンス・ガイド」の「設計方針」と「概念」のセクションを参照してください。
JSSEに標準付属のSunJSSE
プロバイダは、SSLContext
、KeyManagerFactory
およびTrustManagerFactory
実装と、標準のjava.security
APIのエンジン・クラスの実装を提供します。 次の表に、SunJSSE
によって提供される実装を示します。
実装されるエンジン・クラス | アルゴリズムまたはプロトコル |
---|---|
KeyStore |
PKCS12 |
KeyManagerFactory |
PKIX、SunX509 |
TrustManagerFactory |
PKIX (X509またはSunPKIX)、SunX509 |
SSLContext |
SSLv3(1), TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 (JDK 8u261以降) |
javax.net.ssl.SSLContext
クラスは、セキュアなソケット・プロトコルの実装のエンジン・クラスです。 このクラスのインスタンスは、SSLソケット・ファクトリおよびSSLエンジンのファクトリとして動作します。 SSLContext
オブジェクトは、そのコンテキストの下で作成されたすべてのオブジェクトで共有される状態情報をすべて保持します。 たとえば、セッションの状態は、ソケット・ファクトリにより作成され、コンテキストにより提供されたソケットによってハンドシェーク・プロトコルが取り決められると、SSLContext
と関連付けられます。 キャッシュに書き込まれたこれらのセッションは、同じコンテキストで作成された別のソケットで再利用したり共有することができます。
各インスタンスは、認証の実行に必要なキー、証明書チェーン、および信頼されたルートCA証明書を使ってinit
メソッドで構成されます。 この構成は、キーとトラスト・マネージャの形で提供されます。 これらのマネージャは認証をサポートし、コンテキストによってサポートされる暗号群のキー合意を提供します。
現在は、X.509ベースのマネージャだけがサポートされています。
SSLContext
を取得して初期化するには、次の2つの方法があります。
SSLSocketFactory
またはSSLServerSocketFactory
クラスでgetDefault
staticメソッドを呼び出すことです。 このメソッドは、デフォルトのKeyManager
、TrustManager
およびSecureRandom
(セキュアな乱数ジェネレータ)を使用してデフォルトのSSLContext
を作成します。 デフォルトのKeyManagerFactory
およびTrustManagerFactory
を使用すると、KeyManager
およびTrustManager
がそれぞれ作成されます。 使用するキー・データは、「デフォルトのキーストアとトラストストア、ストア・タイプ、およびストア・パスワードのカスタマイズ」で説明するシステム・プロパティで指定されるデフォルトのキーストアおよびトラストストアにあります。 SSLContext
クラスでstaticメソッドgetInstance()
を呼び出し、さらにそのインスタンスの適切な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つのセッションが含まれますが、そのセッションがエンティティ間の接続に、同時に、または連続して何度も使用されることがあります。
他のJCAプロバイダ・ベースのエンジン・クラスと同様に、SSLContext
オブジェクトは、SSLContext
クラスのgetInstance()
ファクトリ・メソッドを使用して作成されます。 このようなstaticメソッドは、最低限要求されたセキュアなソケット・プロトコルを実装するインスタンスを返します。 返されるインスタンスはその他のプロトコルも実装できます。 たとえば、getInstance("TLSv1.2")
は、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
オブジェクトの一般的なプロトコル名は、付録Aで定義されています。
SSLContext
は次のように取得できます。
SSLContext sc = SSLContext.getInstance("TLS");
新しく作成されたSSLContext
は、init
メソッドを呼び出すことによって初期化すべきです。
public void init(KeyManager[] km, TrustManager[] tm, SecureRandom random);
KeyManager[]
パラメータがnullの場合、このコンテキストには空のKeyManager
が定義されます。 TrustManager[]
パラメータがnullの場合、インストールされたセキュリティ・プロバイダは、TrustManagerFactory
のもっとも優先度の高い実装で検索され、適切なTrustManager
が取得されます。 同様に、SecureRandom
パラメータもNULLにできます。その場合、デフォルト実装が使用されます。
内部のデフォルト・コンテキストが使用される場合(SSLContext
はSSLSocketFactory.getDefault()
またはSSLServerSocketFactory.getDefault()
によって作成されるなど)、デフォルトのKeyManager
とTrustManager
が作成されます。 また、デフォルトのSecureRandom
実装も選択されます。
TrustManager
は、提示された認証クレデンシャルの信頼性を判定します。 信頼できないクレデンシャルの場合、接続は切断されます。 セキュアなソケット・ピアのリモート識別情報を認証するには、1つまたは複数のTrustManager
オブジェクトでSSLContext
オブジェクトを初期化する必要があります。 サポートされる認証メカニズムのそれぞれに対し、TrustManager
を1つずつ渡す必要があります。 SSLContext
の初期化にnullが渡されると、自動的にトラスト・マネージャが作成されます。 通常、単一のトラスト・マネージャは、X.509公開キー証明書(X509TrustManager
など)に基づく認証をサポートしています。 セキュアなソケット実装には、共有の秘密キー、Kerberos、またはほかのメカニズムに基づく認証をサポートするものもあります。
TrustManager
オブジェクトはTrustManagerFactory
によって、またはインタフェースの具体的な実装を提供することによって作成されます。
javax.net.ssl.TrustManagerFactory
はプロバイダ・ベースのサービスのエンジン・クラスで、1つまたは複数の型のTrustManager
オブジェクトのファクトリとして動作します。 これはプロバイダ・ベースなので、追加のファクトリを実装して構成し、より高度なサービスを提供したり、インストール固有の認証ポリシーを実装する追加または代替のトラスト・マネージャを提供できます。
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()
メソッドを呼び出します。 不明な場合は、プロバイダのベンダーに問い合わせてください。
SunJSSE
プロバイダのSunX509 TrustManagerFactory
など、多くのファクトリでは、KeyStore
がTrustManagerFactory
の初期化に必要な唯一の情報であるため、最初の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」です。 これは、java.security
ファイルのssl.TrustManagerFactory.algorithm
プロパティを編集することによって変更できます。
PKIXトラスト・マネージャ・ファクトリはインストールされたセキュリティ・プロバイダのCertPath PKIX実装を使用します。 トラスト・マネージャ・ファクトリを初期化するには、通常のinit(KeyStore ks)
メソッドを使用するか、javax.net.ssl.CertPathTrustManagerParameters
クラスを使用して、CertPathパラメータをPKIXトラスト・マネージャに渡します。
次の例に、トラスト・マネージャを取得して特定の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("PKCS12"); 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);
init(KeyStore ks)
メソッドが使用される場合は、失効チェックが無効にされる点を除いて、デフォルトのPKIXパラメータが使用されます。 有効にするには、com.sun.net.ssl.checkRevocation
システム・プロパティをtrue
に設定します。 この設定では、CertPath実装自身が失効情報を見つけられる必要があります。 プロバイダのPKIX実装では多くの場合にこの動作を実行できますが、システム・プロパティcom.sun.security.enableCRLDP
をtrue
に設定する必要があります。
PKIXおよびCertPath APIの詳細は、「Java PKIプログラマーズ・ガイド」を参照してください。
javax.net.ssl.X509TrustManager
インタフェースは、汎用のTrustManager
インタフェースを拡張したものです。 X. 509ベースの認証を使用する場合、トラスト・マネージャによって実装される必要があります。
JSSEを使用したリモート・ソケット・ピアのX.509認証をサポートするには、このインタフェースのインスタンスをSSLContext
オブジェクトのinit
メソッドに渡す必要があります。
このインタフェースを自分で直接実装するか、プロバイダ・ベースのTrustManagerFactory
(たとえば、SunJSSE
プロバイダによって提供されます)から取得できます。 また、独自のインタフェースを実装して、ファクトリで生成されたトラスト・マネージャに委譲することもできます。 たとえば、結果の信頼性の判定をフィルタし、グラフィカル・ユーザー・インタフェースからエンド・ユーザーに問い合わせる場合に、これを実行できます。
ノート: NULLのKeyStoreパラメータがSunJSSE
PKIXまたはSunX509 TrustManagerFactory
に渡された場合、ファクトリは次のプロセスを使用して信頼資料の検索を試みます:
javax.net.ssl.trustStore
プロパティが定義されている場合、TrustManagerFactory
は、このシステム・プロパティで指定されたファイル名を使用してファイルを検索し、KeyStoreパラメータにそのファイルを使用しようとします。 javax.net.ssl.trustStorePassword
システム・プロパティも定義されている場合は、ファイルを開く前に、その値を使用してトラストストアのデータの整合性をチェックします。
javax.net.ssl.trustStore
プロパティが定義されているが、指定したファイルが存在しない場合、空のキーストアを使用するデフォルトのTrustManager
が作成されます。
javax.net.ssl.trustStore
システム・プロパティが指定されていない場合:
java-home/lib/security/jssecacerts
が存在する場合は、そのファイルが使用されますjava-home/lib/security/cacerts
が存在する場合は、そのファイルが使用されますファクトリは、cacerts
ファイルをチェックする前に、javax.net.ssl.trustStore
セキュリティ・プロパティまたはjssecacerts
ファイルで指定されたファイルを検索します。 そのため、コード署名の目的で、cacerts内に存在するものとは別に、JSSE固有の一連の信頼されたルート証明書を提供できます。
指定したX509TrustManager
の動作が状況に適していない場合は、独自のTrustManagerFactory
を作成して登録するか、X509TrustManager
インタフェースを直接実装して、独自の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("PKCS12"); 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(); } }
このようなトラスト・マネージャを作成できたら、次の例のように、init()
メソッドを使用して、これをSSLContext
に割り当てます。 以降、このSSLContext
から作成されたSocketFactories
は、ユーザー独自のTrustManager
を使用して信頼性を判定するようになります。
TrustManager[] myTMs = new TrustManager[] { new MyX509TrustManager() }; SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, myTMs, null);
MyX509TrustManager
を拡張して、キーストアの動的更新処理を行うことができます。 checkClientTrusted
またはcheckServerTrusted
のテストに失敗し、信頼できる証明書チェーンを確立できなかった場合、キーストアに対して、要求された信頼できる証明書を追加できます。 更新されたキーストアを使用して初期化されたTrustManagerFactory
から新しいpkixTrustManager
を作成する必要があります。 以前に初期化したSSLContext
を使って新しい接続を確立すると、信頼性の判定を行うときに、新しく追加された証明書が使用されます。
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
サブクラスを自分で(次のセクションで概説)に作成するか、プロバイダ・ベースのTrustManagerFactory
(たとえば、SunJSSE
プロバイダによって提供されます)から取得できます。 Java SE 7では、PKIXまたはSunX509 TrustManagerFactory
はX509ExtendedTrustManager
インスタンスを返します。
このセクションでは、X509TrustManager
について記載されているのとほぼ同じ方法でサブクラスX509ExtendedTrustManager
を作成する方法を示します。
次の例では、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
は、最終的にリモート・ホストに送信される認証クレデンシャルを選択します。 自分自身(ローカルのセキュアなソケット・ピア)をリモート・ピアに対して認証させるには、1つまたは複数のKeyManager
オブジェクトでSSLContext
オブジェクトを初期化する必要があります。 サポートされる認証メカニズムごとに、KeyManager
を1つ渡す必要があります。 SSLContext
の初期化中にnullが渡されると、空のKeyManager
が作成されます。 内部のデフォルト・コンテキストが使用される場合(SSLSocketFactory.getDefault()
またはSSLServerSocketFactory.getDefault()
によって作成されるSSLContext
など)は、デフォルトのKeyManager
が作成されます。 通常、単一のキー・マネージャは、X.509公開キー証明書に基づく認証をサポートしています。 セキュアなソケット実装には、共有の秘密キー、Kerberos、またはほかのメカニズムに基づく認証をサポートするものもあります。
KeyManager
オブジェクトはKeyManagerFactory
によって、またはインタフェースの固定実装を提供することによって作成されます。
javax.net.ssl.KeyManagerFactory
クラスはプロバイダ・ベースのサービスのエンジン・クラスで、1つまたは複数の型のKeyManager
オブジェクトのファクトリとして動作します。 SunJSSE
プロバイダは、基本的なX.509キー・マネージャを返すことができるファクトリを実装します。 これはプロバイダ・ベースであるため、追加のファクトリを実装し、構成することにより、追加の、または代替のキー・マネージャを提供できます。
このクラスのインスタンスは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");
前述のコールでは、基本的なX.509ベースの認証キーを提供する、SunJSSE
プロバイダのデフォルトのキー・マネージャ・ファクトリのインスタンスが作成されます。
新しく作成されたファクトリは、init
メソッドの1つを呼び出すことによって初期化してください。
public void init(KeyStore ks, char[] password); public void init(ManagerFactoryParameters spec);
使用するKeyManagerFactory
に適したinit
メソッドを呼び出します。 不明な場合は、プロバイダのベンダーに問い合わせてください。
SunJSSE
プロバイダからのデフォルトのSunX509 KeyManagerFactory
など、多くのファクトリでは、KeyManagerFactory
の初期化に必要な情報がKeyStore
とパスワードのみであるため、最初のinit
メソッドがコールする適切なメソッドです。 KeyManagerFactory
はKeyStore
に、リモートのソケット・ピアを認証するために使用すべき秘密キー、および対応する公開キー証明書について問い合わせます。 パスワード・パラメータは、KeyStore
のキーにアクセスするメソッドで使用するパスワードを指定します。 KeyStore
のキーはすべて、同じパスワードで保護する必要があります。
プロバイダでは、KeyStore
とパスワード以外の初期化パラメータを必要とすることがあります。 そのプロバイダのユーザーは、プロバイダによる定義に従って、適切なManagerFactoryParameters
の実装を渡すことが期待されます。 そのあと、プロバイダはManagerFactoryParameters
実装の特定のメソッドを呼び出し、必要な情報を取得できます。
一部のファクトリでは、KeyStore
オブジェクトやその他のパラメータで初期化されなくても、認証データにアクセスできます。 たとえば、Java認証・承認サービス(JAAS)などのログイン・メカニズムの一部としてキー・データにアクセスできる場合があります。
前述のように、SunJSSE
プロバイダは、KeyStore
パラメータを使用して初期化する必要があるSunX509ファクトリをサポートしています。
javax.net.ssl.X509KeyManager
インタフェースは、汎用のKeyManager
インタフェースを拡張したものです。 X.509ベースの認証を行うキー・マネージャで実装します。 JSSEを使用したリモート・ソケット・ピアのX.509認証をサポートするには、このインタフェースのインスタンスをSSLContext
オブジェクトのinit()
メソッドに渡す必要があります。
このインタフェースを自分で直接実装するか、プロバイダ・ベースのKeyManagerFactory
(たとえば、SunJSSE
プロバイダによって提供されます)から取得できます。 また、独自のインタフェースを実装して、ファクトリで生成されたキー・マネージャに委譲することもできます。 たとえば、結果のキーをフィルタし、グラフィカル・ユーザー・インタフェースを使用してエンド・ユーザーに問い合わせる場合に、これを実行できます。
X509KeyManager
のデフォルトの動作が状況に適していない場合は、「独自のX509TrustManagerの作成」に示す同様の方法で独自のX509KeyManager
を作成できます。
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()
メソッドを使用して取得できます。
「独自のX509ExtendedTrustManagerの作成」に示す同様の方法で、独自の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
はリモート・ホストに送信される認証クレデンシャルを決定します。
二次サポート・クラスは、セキュアなソケットの作成、使用、および管理をサポートするJSSE APIの一部として提供されます。 このクラスは、セキュアなソケット・アプリケーションでは、コア・クラスやサポート・クラスほどには使用されません。 セカンダリ・サポート・クラスおよびインタフェースはjavax.net.ssl
およびjavax.security.cert
パッケージに含まれています。
SSLParameters
クラスはTLS接続に影響する次のパラメータをカプセル化します。
次のメソッドを使用して、SSLSocket
またはSSLEngine
の現在のSSLParameters
を取得できます。
SSLSocket
、SSLServerSocket
およびSSLEngine
内のgetSSLParameters()
SSLContext
内のgetDefaultSSLParameters()
およびgetSupportedSSLParamters()
SSLSocket
、SSLServerSocket
およびSSLEngine
のsetSSLParameters()
メソッドで、SSLParameters
を割り当てることができます。
SSLParameters.setServerNames()
メソッドによって、サーバー名の表示を明示的に設定できます。 クライアント・モードでのサーバー名の表示はエンドポイントの識別にも影響します。 X509ExtendedTrustManager
の実装で、それはExtendedSSLSession.getRequestedServerNames()
メソッドによって取得されたサーバー名の表示を使用します。 次の例に、この機能を示します。
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);
前の例では、サーバー名表示のホスト名 (www.example.com
) は、エンド・エンティティのX.509証明書に提示されるピアのIDに対してエンドポイントを識別するために使用されます。
TLSハンドシェーク時に、クライアントは、それがサポートする暗号化オプションのリストの、最優先順位から暗号化方式群をネゴシエーションすることを要求します。 次に、サーバーはクライアントによって要求された暗号化方式群のリストから、単一の暗号化方式群を選択します。 この選択では、デフォルトで、最もセキュアな設定であるサーバーの優先順位に従います。 ただし、サーバーは、SSLParameters.setUseCipherSuitesOrder(false)
メソッドを呼び出すことで、クライアントのプリファレンスではなく、クライアントのプリファレンスを尊重することを選択できます。
javax.net.ssl.SSLSessionContext
インタフェースは、1つのエンティティに関連付けられているSSLSession
オブジェクトのグループです。 たとえば、多数のセッションに同時に参加するサーバーやクライアントに関連付けることができます。 このインタフェースのメソッドを使用すると、コンテキストの全セッションを列挙したり、セッションIDで特定のセッションを検索したりできます。
SSLSessionContext
は、SSLSessionのgetSessionContext()
メソッドを呼び出してSSLSession
からオプションで取得することもできます。 一部の環境では、コンテキストが使用できないことがあり、その場合、getSessionContext
メソッドはnullを返します。
javax.net.ssl.SSLSessionBindingListener
インタフェースは、SSLSession
からバインドまたはアンバインドされるときに通知を受けるオブジェクトによって実装されます。
javax.net.ssl.SSLSessionBindingEvent
クラスは、SSLSession
からバインドまたはアンバインドされるときに、SSLSessionBindingListener
に伝えられるイベントを定義します。
javax.net.ssl.HandShakeCompletedListener
インタフェースは、指定のSSLSocket
接続時にSSLプロトコル・ハンドシェークの完了通知を受け取る任意のクラスに実装されるインタフェースです。
javax.net.ssl.HandShakeCompletedEvent
クラスは、指定のSSLSocket
接続のSSLプロトコル・ハンドシェークの完了時にHandShakeCompletedListener
に伝えられるイベントを定義します。
SSL/TLS実装の標準ホスト名検証ロジックが失敗した場合、実装は、このインタフェースを実装し、このHttpsURLConnection
インスタンスに割り当てられたクラスのverify
メソッドを呼び出します。 コールバック・クラスは、指定のパラメータでホスト名が受け付け可能であると判断できる場合、接続を許可すべきであると報告します。 応答が受け付けられない場合、接続は切断されます。
たとえば、
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());
HostnameVerifier
をHttpsURLConnection
に割り当てる方法の詳細は、「HttpsURLConnectionクラス」を参照してください。
セキュアなソケット・プロトコルの多くは、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
クラスを使用してください。
java.security.AlgorithmConstraints
インタフェースは、許可される暗号化アルゴリズムを制御するために使用します。 AlgorithmConstraints
は3つのpermits()
メソッドを定義します。 これらのメソッドは、ある暗号化関数について許可されるアルゴリズム名またはキーを指定します。 暗号化関数は一連のCryptoPrimitive
で表現され、これはSTREAM_CIPHER
、MESSAGE_DIGEST
、SIGNATURE
などのフィールドを含む列挙です。
したがって、AlgorithmConstraints
実装は、「このキーとこのアルゴリズムを暗号化操作の目的で使用できるのか」といった質問に答えることができます。
新しいsetAlgorithmConstraints()
メソッドを使用して、AlgorithmConstraints
オブジェクトをSSLParameters
オブジェクトと関連付けることができます。 SSLParameters
オブジェクトに対する現在のAlgorithmConstraints
オブジェクトは、getAlgorithmConstraints()
メソッドを使用して取得します。
StandardConstants
クラスは、JSSEでの標準の定数定義を表すために使用します。
StandardConstants.SNI_HOST_NAME
Server Name Indication (SNI)拡張でのドメイン・ネーム・サーバー(DNS)ホスト名を表し、SNIServerName
またはSNIMatcher
オブジェクトのインスタンス化時に使用できます。
抽象SNIServerName
クラスのインスタンスはServer Name Indication (SNI)拡張のサーバー名を表します。 それは、指定したサーバー名のタイプとエンコード値を使用してインスタンス化されます。
getType()
およびgetEncoded()
メソッドを使用して、サーバー名のタイプとエンコードされたサーバー名の値のコピーをそれぞれ返します。 equals()
メソッドは他のオブジェクトがこのサーバー名と「等しい」かどうかをチェックするために使用できます。 hashCode()
メソッドはこのサーバー名のハッシュ・コード値を返します。 サーバー名(サーバー名のタイプとエンコードされたサーバー名の値)の文字列表現を取得するには、toString()
メソッドを使用します。
抽象SNIServerName
クラスのインスタンスはSNIServerName
オブジェクトの一致操作を実行します。 サーバーはServer Name Indication (SNI)拡張からの情報を使用して、特定のSSLSocket
またはSSLEngine
で接続を受け付けるべきであるかどうかを判定できます。 たとえば、単一の基礎となるネットワーク・アドレスで複数の「仮想」または「名前ベース」のサーバーがホストされている場合、サーバー・アプリケーションは、SNI情報を使用して、このサーバーが、クライアントがアクセスしようとしている正しいサーバーであるかどうかを判断できます。 このクラスのインスタンスは、サーバーによって、ホスト名などの特定のタイプの受け付け可能なサーバー名を確認するために使用できます。
SNIMatcher
クラスは、一致操作が実行される指定したサーバー名タイプを使用してインスタンス化されます。 指定のSNIServerName
を照合するには、matches()
メソッドを使用します。 指定のSNIMatcher
オブジェクトのサーバー名タイプを返すには、getType()
メソッドを使用します。
SNIHostName
クラス(SNIServerName
クラスを拡張する)のインスタンスは、Server Name Indication (SNI)拡張のタイプ「host_name」(「StandardConstantsクラス」を参照)のサーバー名を表します。 SNIHostName
をインスタンス化するには、String
引数で、サーバーの完全修飾DNSホスト名(クライアントから理解できる)を指定します。 この引数は、次の場合に不正です。
エンコードされたホスト名値をバイト配列で指定して、SNIHostName
をインスタンス化することもできます。 このメソッドは一般に、要求されたSNI拡張でエンコードされた名前値を解釈するために使用します。 そうでない場合、SNIHostName(String hostname)
コンストラクタを使用します。 encoded
引数は、次の場合に不正です。
ノート: 引数として渡されたencoded
バイト配列は、以降の変更から保護するために、クローンが作成されます。
US-ASCIIエンコーディングのSNIHostName
オブジェクトのホスト名を返すには、getAsciiName()
メソッドを使用します。 サーバー名を他のオブジェクトと比較するには、equals()
メソッドを使用します(比較は大文字と小文字が区別されません)。 SNIHostName
のハッシュ・コード値を返すには、hashCode()
メソッドを使用します。 DNSホスト名を含むSNIHostName
の文字列表現を返すには、toString()
メソッドを使用します。
createSNIMatcher()
メソッドに、1つ以上の照合するホスト名を表す正規表現を渡すことによって、SNIHostName
オブジェクトのSNIMatcher
オブジェクトを作成します。
JSSEには、様々な実装をプラグインしたり、デフォルトのキーストアを指定したりして、カスタマイズ可能な標準実装が含まれます。 次の表では、カスタマイズ可能な側面、デフォルトの概要、およびカスタマイズに使用するメカニズムについて説明します。
一部のカスタマイズは、セキュリティ・プロパティ値またはシステム・プロパティ値を設定することによって行われます。 表に続くセクションでは、プロパティ値の設定方法について説明します。
次の表に、java.security.Security
プロパティを設定してカスタマイズされる項目を示します:
セキュリティ・プロパティ | カスタマイズされたアイテム | デフォルト値 | ノート |
---|---|---|---|
cert.provider.x509v1 |
X509証明書実装のカスタマイズ | OracleからのX509Certificate実装 | なし |
SunJSSE プロバイダで使用されるJCE暗号化アルゴリズム |
SunJCEプロバイダよりも優先度の高い代替JCEアルゴリズム・プロバイダを指定します。「暗号化アルゴリズム・プロバイダのカスタマイズ」を参照してください。 | SunJCE実装 | なし |
jdk.certpath.disabledAlgorithms 1 |
無効化された証明書検証暗号化アルゴリズム(無効化された制限付き暗号化アルゴリズムを参照) | MD2、MD5、SHA1 jdkCA &使用方法TLSServer、RSA keySize < 1024、DSA keySize < 1024、EC keySize < 224、jdk.disabled.namedCurves、SHA1使用方法SignedJAR & denyAfter 2019-01-01 2 | なし |
jdk.tls.disabledAlgorithms 1 |
無効化された制限付き暗号化アルゴリズム | SSLv3、TLSv1、TLSv1.1、RC4、DES、MD5withRSA、DH keySize < 1024、EC keySize < 224、3DES_EDE_CBC、anon、NULL、ECDHにはjdk.disabled.namedCurves 2が含まれます | アプリケーションで明示的に有効化されている場合でも、TLS接続に対してネゴシエートされない特定のアルゴリズム(プロトコルのバージョン、暗号スイート、キー交換メカニズムなど)を無効にします |
jdk.tls.keyLimits 1 (JDK 8u261以降) |
アルゴリズムでキー・セットを使用して暗号化可能なデータ量の制限 | AES/GCM/NoPadding KeyUpdate 2^37 | アルゴリズムで特定のキー・セットを使用して暗号化可能なデータ量を制限します。この制限に達すると、KeyUpdateポストハンドシェーク・メッセージが送信され、現在のキー・セットを更新するように要求します。 |
jdk.tls.legacyAlgorithms 1 |
レガシーの暗号化アルゴリズム | K_NULL、C_NULL、M_NULL、DH_anon、ECDH_anon、RC4_128、RC4_40、DES_CBC、DES40_CBC、3DES_EDE_CBC2 | レガシー・アルゴリズムとみなされるアルゴリズムを指定します。これは、他の候補がないかぎり、TLSセキュリティ・パラメータのネゴシエーション中にネゴシエーションされません。 |
jdk.tls.maxCertificateChainLength 1 |
証明書チェーンの処理 | 10 | TLSハンドシェークでの証明書チェーンの最大許容長を指定します。 |
jdk.tls.maxHandshakeMessageSize 1 |
証明書チェーンの処理 | 32768 (32KB) | TLSハンドシェイクでのハンドシェイク・メッセージの最大許容サイズをバイト単位で指定します。 |
jdk.tls.server.defaultDHEParameters 1 |
Diffie-Hellmanグループ | JDK TLS実装における安全なプライムDiffie-Hellmanグループ | Transport Layer Security (TLS)処理のデフォルトの有限フィールドDiffie-Hellmanエフェメラル(DHE)パラメータを定義 |
ocsp.enable 1 |
クライアント駆動OCSPおよびOCSPプル | false | クライアント主導型オンライン証明書ステータス・プロトコル(OCSP)を有効にします。 失効チェックも有効にする必要があります(「クライアント主導型OCSPを使用するためのJavaクライアントの設定」を参照)。 |
security.provider.n |
暗号化サービス・プロバイダ。プロバイダ実装のカスタマイズと暗号化アルゴリズム・プロバイダのカスタマイズを参照してください。 | プラットフォームごとに異なります。java.security セキュリティ・プロパティ・ファイルを確認してください。 |
セキュリティ・プロパティ・ファイルのsecurity.provider.n= 行にプロバイダを指定します。n は、値が1以上の整数です。 |
ssl.KeyManagerFactory.algorithm |
デフォルトのキー・マネージャ・ファクトリ・アルゴリズム名(デフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズを参照) | SunX509 | なし |
ssl.ServerSocketFactory.provider 1 |
デフォルトのSSLServerSocketFactory 実装 |
OracleからのSSLServerSocketFactory 実装 |
なし |
ssl.SocketFactory.provider 1 |
デフォルトのSSLSocketFactory 実装 |
OracleからのSSLSocketFactory 実装 |
なし |
ssl.TrustManagerFactory.algorithm |
デフォルトのトラスト・マネージャ・ファクトリ・アルゴリズム名(デフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズを参照) | PKIX | なし |
1このセキュリティ・プロパティは、現在JSSE実装で使用されていますが、他の実装で検査および使用される保証はありません。 他の実装で調査する場合は、その実装で、JSSE実装と同じ方法でそれを処理すべきです。 今後のリリースでは、このプロパティが引き続き存在するか、同じタイプ(システムまたはセキュリティ)になる保証はありません。
2これらのセキュリティ・プロパティで指定された制限付き、無効およびレガシー・アルゴリズムのリストが変更される場合があります。最新の値については、JDKインストールのjava.security
ファイルを参照してください。
次の表に、java.lang.System
プロパティを設定してカスタマイズされる項目を示します。
システム・プロパティ | カスタマイズされたアイテム | デフォルト | ノート |
---|---|---|---|
com.sun.net.ssl.checkRevocation 1 |
失効チェック | false | クライアント主導型OCSPを有効化するには、失効チェックを有効にする必要があります(「クライアント主導型OCSPとOCSPステープリング」を参照)。 |
HTTPS URLのポート・フィールドを使用してカスタマイズ* | デフォルトのHTTPSポート | 443 | なし |
https.cipherSuites 1 |
HTTPS接続用のデフォルトの暗号化方式群1 | ソケット・ファクトリによって決定 | HttpsURLConnection で使用できる暗号群を指定する暗号群名リスト(カンマ区切り形式)を含む。 SSLSocket.setEnabledCipherSuites(String[]) メソッドを参照してください。 このメソッドは、渡されたString配列から直接、ClientHello暗号スイートの優先順位を設定します。 |
https.protocols 1 |
HTTPS接続用のデフォルトのハンドシェーク・プロトコル | ソケット・ファクトリによって決定 | HttpsURLConnection で有効にするプロトコルを指定するプロトコル名リスト(カンマ区切り形式)を含む。 SSLSocket.setEnabledProtocols(String[]) を参照してください。 |
https.proxyHost 1 |
デフォルトのプロキシ・ホスト | なし | なし |
https.proxyPort 1 |
デフォルトのプロキシ・ポート | 80 | なし |
java.protocol.handler.pkgs |
HTTPSプロトコルの代替実装の指定 | Oracleからの実装 | なし |
javax.net.ssl.keyStore 1 |
デフォルトのキーストア(「デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ」を参照) | NONE |
値NONE を指定できます。 この設定は、ハードウェア・トークンに存在する場合など、キーストアがファイルベースでない場合に適切。 |
javax.net.ssl.keyStorePassword 1 |
デフォルトのキーストア・パスワード(「デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ」を参照) | なし | 他のユーザーによる検出に公開する方法でパスワードを指定することはお勧めできません。 たとえば、コマンドラインでパスワードを指定します。 パスワードのセキュリティを維持するには、アプリケーションでパスワードの入力を求めるか、適切に保護されたオプション・ファイルにパスワードを指定します。 |
javax.net.ssl.keyStoreProvider 1 |
デフォルトのキーストア・プロバイダ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | なし | なし |
javax.net.ssl.keyStoreType 1 |
デフォルトのキーストア・タイプ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | KeyStore.getDefaultType() |
なし |
javax.net.ssl.sessionCacheSize (JDK 8u261以降) |
SSLセッション・キャッシュ内の最大エントリ数のデフォルト値 | 20480 | セッション・キャッシュ・サイズは、SSLSessionContext.setSessionCacheSize メソッドをコールするか、javax.net.ssl.sessionCachSize システム・プロパティを設定することで設定できます。 キャッシュ・サイズが未設定の場合はデフォルト値が使用されます。 |
javax.net.ssl.trustStore 1 |
デフォルトのトラストストア(「デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ」を参照) | jssecacerts (存在する場合)、それ以外の場合はcacerts |
値NONE を指定できます。 この設定は、ハードウェア・トークンに存在する場合など、トラストストアがファイルベースでない場合に適切。 |
javax.net.ssl.trustStorePassword 1 |
デフォルトのトラストストア・パスワード(「デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ」を参照) | なし | 他のユーザーによる検出に公開する方法でパスワードを指定することはお勧めできません。 たとえば、コマンドラインでパスワードを指定します。 パスワードのセキュリティを維持するには、アプリケーションでパスワードの入力を求めるか、適切に保護されたオプション・ファイルにパスワードを指定します。 |
javax.net.ssl.trustStoreProvider 1 |
デフォルトのトラストストア・プロバイダ(「デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ」を参照) | なし | なし |
javax.net.ssl.trustStoreType 1 |
デフォルトのトラストストア・タイプ(「デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズ」を参照) | KeyStore.getDefaultType() |
なし |
jdk.tls.acknowledgeCloseNotify 1 (JDK 8u261以降) |
close_notifyアラートの受信時の送信の指定 | false | システム・プロパティがtrueに設定されている場合、クライアントまたはサーバーがclose_notifyアラートを受信すると、対応するclose_notifyアラートが送信され、接続は二重クローズされます。 |
jdk.tls.client.cipherSuites 1 |
クライアント側の、デフォルトで有効な暗号化方式群。デフォルトで有効な暗号化方式群の指定を参照してください | このJDKリリースに対して現在実装されているSunJSSE 暗号スイートのリストを、優先順位でソートして表示するには、「SunJSSE暗号スイート」を参照してください。 |
注意: これらのシステム・プロパティを使用して弱い暗号スイートを構成したり、構成済の暗号スイートが将来弱い場合があります。 このリスクを理解せずにこれらのシステム・プロパティを使用することはお薦めしません。 |
jdk.tls.client.disableExtensions 1 |
デフォルトの拡張機能の構成 | なし | クライアント側で使用される拡張機能をブロックします。 |
jdk.tls.client.protocols 1 |
TLSクライアントのデフォルトのハンドシェーク・プロトコル。 「SunJSSEプロバイダ」を参照してください。 | なし | クライアントで特定のSunJSSE プロトコルを有効にするには、カンマ区切りのリストで引用符で囲んで指定します。サポートされている他のすべてのプロトコルは、クライアントでは有効になりません。 たとえば、jdk.tls.client.protocols="TLSv1,TLSv1.1" の場合、TLSv1およびTLSv1.1のクライアントでのデフォルトのプロトコル設定は有効ですが、SSLv3、TLSv1.2、TLSv1.3およびSSLv2Helloは有効ではありません。 |
jdk.tls.client.SignatureSchemes 1 |
クライアント側のTLS接続に使用できる署名スキームを指定する、サポートされている署名スキーム名のカンマ区切りリストが含まれます。 | なし | 認識されない、またはサポートされない署名スキーム名がプロパティに指定された場合は無視されます。 このシステム・プロパティが定義されていないか空である場合、プロバイダ固有のデフォルトが使用されます。 この名前は大/小文字が区別されません。 シグネチャ・スキーム名のリストについては、「付録D: シグネチャ・スキーム」を参照してください。 |
jdk.tls.ephemeralDHKeySize 1 |
エフェメラルDiffie-Hellmanキーのサイズのカスタマイズ | 2048ビット | なし |
jdk.tls.namedGroups 1 |
TLSキー交換用にサポートされている名前付きグループのカスタマイズ | このシステム・プロパティが定義されていないか値が空の場合は、実装のデフォルトのグループおよび設定が使用されます。 | これには、引用符で囲まれたカンマ区切りリスト形式で、有効な名前付きグループが優先順位に従って格納されています。 たとえば: jdk.tls.namedGroups="secp521r1,secp256r1,ffdhe2048" |
jdk.tls.rejectClientInitiatedRenegotiation 1 |
サーバー側でクライアントが開始した再ネゴシエーションを拒否します。 このシステム・プロパティがtrue の場合、サーバーはクライアントによって開始された再ネゴシエーションを受け入れず、致命的なhandshake_failure アラートで失敗します。 クライアント側で初期化されたサーバー側再ネゴシエーションを拒否します。 |
false |
なし |
jdk.tls.server.cipherSuites 1 |
サーバー側の、デフォルトで有効な暗号化方式群。 「デフォルトで有効な暗号化方式群の指定」を参照してください | デフォルトで有効になっている暗号化方式群を確認するには、「SunJSSE暗号化方式群」を参照してください | 注意: これらのシステム・プロパティを使用して弱い暗号スイートを構成したり、構成済の暗号スイートが将来弱い場合があります。 このリスクを理解せずにこれらのシステム・プロパティを使用することはお薦めしません。 |
jdk.tls.server.disableExtensions 1 |
デフォルトの拡張機能の構成 | なし | サーバー側で使用される拡張機能をブロックします。 |
jdk.tls.server.protocols 1 (JDK 8u261以降) |
TLSサーバーのデフォルトのハンドシェーク・プロトコル。 「SunJSSEプロバイダ」を参照してください。 | なし | SunJSSE プロバイダのサーバー側でデフォルトの有効なプロトコル・スイートを構成するには、カンマ区切りリストでプロトコルを引用符で囲んで指定します。 このリストのプロトコルは、「Javaセキュリティ標準アルゴリズム」で説明されている標準のSSLプロトコル名です。 このシステム・プロパティは、デフォルトのプロトコル・スイート(SSLおよびTLSアルゴリズムのSSLContext)にのみ影響します。 アプリケーションがバージョン固有のSSLContext (SSLv3、TLSv1、TLSv1.1、TLSv1.2またはTLSv1.3)を使用する場合、または有効なプロトコル・バージョンを明示的に設定する場合、このシステム・プロパティは影響しません。 |
jdk.tls.server.SignatureSchemes 1 |
サーバー側のTLS接続に使用できる署名スキームを指定する、サポートされている署名スキーム名のカンマ区切りリストが含まれます。 | なし | 認識されない、またはサポートされない署名スキーム名がプロパティに指定された場合は無視されます。 このシステム・プロパティが定義されていないか空である場合、プロバイダ固有のデフォルトが使用されます。 この名前は大/小文字が区別されません。 シグネチャ・スキーム名のリストについては、「付録D: シグネチャ・スキーム」を参照してください。 |
jsse.enableFFDHEExtension 1 (JDK 8u261以降) |
TLSキー交換のFinite Field Diffie-Hellman Ephemeral (FFDHE)パラメータを有効または無効にします | true | FFDHEは、RFC 7919で定義されたTLS拡張子です。 これにより、TLS接続で既知の有限フィールドDiffie-Hellmanグループを使用できます。 かなり古い一部のTLSベンダーは、TLS拡張を処理できないことがあります。 この場合、このプロパティをfalseに設定してFFDHE拡張を無効化します。 |
jsse.enableMFLNExtension 1 (JDK 8u261以降) |
最大フラグメント長ネゴシエーションのカスタマイズ(MFLN) Extension | false | なし |
jsse.enableSNIExtension 1 |
Server Name Indicationオプション | true | Server Name Indication (SNI)は、RFC 6066で定義されているTLS拡張です。 これは仮想サーバーへのTLS接続を可能にし、さまざまなネットワーク名に対して複数のサーバーが単一の基本ネットワーク・アドレスでホスティングされます。 かなり古い一部のTLSベンダーは、TLS拡張を処理できないことがあります。 この場合、SNI拡張を無効にするには、このプロパティをfalseに設定 |
jsse.SSLEngine.acceptLargeFragments 1 |
大きいTLSパケット用のバッファのデフォルトのサイズ設定 | なし | このシステム・プロパティをtrueに設定すると、SSLSession は、デフォルトで大きいデータ・パケットを処理するためにバッファのサイズを設定します(「SSLSessionおよびExtendedSSLSession」のノートを参照)。 これにより、アプリケーションが不要な大きいSSLEngine バッファを割り当てる場合があります。 かわりに、アプリケーションはバッファ・オーバーフロー条件を動的にチェックし、必要に応じてバッファのサイズを変更する必要があります。(「SSLEngine操作ステータスの理解」を参照してください)。 |
jdk.tls.client.enableStatusRequestExtension 1 |
クライアント主導のOCSPを使用するためのJavaクライアントの設定 | false | trueの場合、status_requestおよびstatus_request_v2拡張が有効になり、サーバーによって送信されるCertificateStatusメッセージの処理が有効になります。 |
jdk.tls.server.enableStatusRequestExtension 1 |
OCSPステイプルを使用するようにJavaサーバーを設定 | false | trueの場合、OCSPステッピングのサーバー側サポートが有効になります |
sun.security.ssl.allowLegacyHelloMessages |
レガシーHelloメッセージの許可 (再交渉) | true | trueの場合、適切なRFC 5746メッセージを必要とせずにピアにハンドシェイクを許可します。 詳細は、「Transport Layer Security (TLS)再ネゴシエーションの問題」の「フェーズ2の修正の説明」を参照してください。 |
sun.security.ssl.allowUnsafeRenegotiation |
安全でないSSL/TLS再ネゴシエーションの許可 | false | trueの場合、完全な(安全でない)レガシー・ネゴシエーションを許可します。 詳細は、「Transport Layer Security (TLS)再ネゴシエーションの問題」の「フェーズ2の修正の説明」を参照してください。 |
1このシステム・プロパティは、現在JSSE実装によって使用されていますが、他の実装によって検査および使用されることは保証されていません。 他の実装で調査する場合は、その実装で、JSSE実装と同じ方法でそれを処理すべきです。 今後のリリースでは、このプロパティが引き続き存在するか、同じタイプ(システムまたはセキュリティ)になる保証はありません。
JSSEの一部の側面は、セキュリティ・プロパティを設定してカスタマイズできます。 セキュリティ・プロパティは静的または動的に設定できます。
セキュリティ・プロパティを静的に設定するには、セキュリティ・プロパティ・ファイルに行を追加します。 セキュリティ・プロパティ・ファイルは、java-home/lib/security/java.security
にあります。
java-home
は、JREがインストールされているディレクトリを示します。
セキュリティ・プロパティ・ファイルでセキュリティ・プロパティ値を指定するには、次の形式の行を追加します:
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");
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 Platform, Standard Edition配備ガイドを参照してください。
Javaコントロール・パネルを使用して、Runtime Environment PropertyをローカルまたはVM単位で設定します。 これにより、ローカルでdeployment.properties
ファイルが作成されます。 配備担当開発者はdeployment.config
メカニズムを使用して、企業全体のdeployment.properties
ファイルを配布することもできます。 詳細については、「配備構成ファイルおよびプロパティ」を参照してください。
特定のアプレットにプロパティを設定するには、<APPLET>
タグ内のHTMLサブタグ<PARAM>
"java_arguments"を使用します。 詳細は、Java Platform, Standard Edition配備ガイドのコマンドライン引数に関する項を参照してください。
Java Plug-inを使用して特定のJava Web Startアプリケーションまたはアプレットでプロパティを設定するには、resources
要素のJNLP property
サブ要素を使用します。 詳細については、Java Web Startガイドのresources要素に関するセクションを参照してください。
JDK 8u261以降には、Transport Layer Security (TLS) 1.3仕様(RFC 8446)の実装が含まれています。
TLS 1.3は、クライアント・エンドポイントのデフォルトのSSLContext
(SSL
またはTLS
)に対して有効です。
TLS 1.3には、以前のバージョンとの直接的な互換性がありません。 TLS 1.3を下位互換性モードで実装することは可能ですが、TLS 1.3にアップグレードする際に考慮する互換性リスクはまだいくつかあります。
TLS 1.3ではhalf-closeポリシーを使用し、TLS 1.2以前ではduplex-closeポリシーを使用しています。 duplex-closeポリシーに依存しているアプリケーションでは、TLS 1.3へのアップグレード時に互換性の問題が発生する場合があります。
signature_algorithms_cert拡張では、証明書認証に事前定義済の署名アルゴリズムを使用することが求められます。 しかし実際には、サポートされていない署名アルゴリズムがアプリケーションで使用されている場合があります。
DSA署名アルゴリズムはTLS 1.3ではサポートされていません。 サーバーがDSA証明書のみを使用するように構成されている場合、サーバーではTLS 1.3接続をネゴシエートできません。
TLS 1.3でサポートされている暗号化方式群は、TLS 1.2以前のものとは異なります。 サポートされなくなった暗号化方式群がアプリケーションでハードコードされている場合、コードを修正せずにTLS 1.3を使用することはできない場合があります(たとえばTLS_AES_128_GCM_SHA256 (1.3以降)とTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (1.2以前))。
TLS 1.3のセッション再開およびキー更新の動作はTLS 1.2以前とは異なります。 互換性の影響はわずかですが、アプリケーションがTLSプロトコルのハンドシェークの詳細に依存している場合にはリスクになる可能性があります。
X509Certificate.getInstance()
メソッドで返されたX509証明書実装は、デフォルトでJSSE実装の実装です。
オプションで、別の実装を返すようにすることもできます。 これを行うには、cert.provider.x509v1
という名前の「セキュリティ・プロパティ」の値として、他の実装のクラスの(パッケージ)という名前を指定します。 たとえば、クラスがMyX509CertificateImpl
という名前で、com.cryptox
パッケージに表示されている場合は、セキュリティ・プロパティ・ファイルに次の行を追加する必要があります:
cert.provider.x509v1=com.cryptox.MyX509CertificateImpl
アプリケーションで、またはシステム・プロパティjdk.tls.client.cipherSuites
およびjdk.tls.server.cipherSuites
を使用して、デフォルトの有効な暗号スイートを指定できます。
ノート: 有効な暗号スイートの実際の使用は、アルゴリズムの制約によって制限されます。
デフォルトで有効化する暗号化方式群のセットは、この優先度順に従い、次のいずれかの方法で決定されます。
たとえば、アプリケーションでデフォルトの有効な暗号スイートを明示的に設定すると、jdk.tls.client.cipherSuites
またはjdk.tls.server.cipherSuites
で指定された設定およびJSSEプロバイダのデフォルトがオーバーライドされます。
次のいずれかのメソッドを使用して、どの暗号化方式群が有効であるかを設定できます。
SSLSocket.setEnabledCipherSuites(String[])
SSLEngine.setEnabledCipherSuites(String[])
SSLServerSocket.setEnabledCipherSuites(String[])
SSLParameters(String[] cipherSuites)
SSLParameters(String[] cipherSuites, String[] protocols)
SSLParameters.setCipherSuites(String[])
HttpsURLConnection
のhttps.cipherSuites
システム・プロパティシステム・プロパティjdk.tls.client.cipherSuites
は、クライアント側で有効になっているデフォルトの暗号スイートを指定します。jdk.tls.server.cipherSuites
は、サーバー側で有効になっている暗号スイートを指定します。
これら2つのシステム・プロパティの値の構文は、サポートされている暗号化方式群の名前をカンマで区切ったリストです。 これらのプロパティで指定された暗号化方式群名のうち、認識されていないまたはサポートされていないものは無視されます。 JSSE暗号化方式群の標準名は、Javaセキュリティ標準アルゴリズムを参照してください。
ノート: これらのシステム・プロパティは、現在Oracle JDKでサポートされています。 これらが他のJDK実装によってサポートされることは保証されていません。
注意: これらのシステム・プロパティを使用して弱い暗号スイートを構成したり、構成済の暗号スイートが将来弱い場合があります。 このリスクを理解せずにこれらのシステム・プロパティを使用することはお薦めしません。
各JSSEプロバイダは、デフォルトで有効な暗号化方式群を独自に設定しています。 SunJSSE
プロバイダでサポートされている暗号スイート名、およびデフォルトで有効になっている暗号スイート名については、「JDK 8のJava暗号化アーキテクチャOracleプロバイダ・ドキュメント」の「SunJSSEプロバイダ」を参照してください。
java.net.URL
クラスにHTTPS URLスキームを使用することで、SSL対応のWebサーバーでセキュアに通信できます。 JDKは、デフォルトのHTTPS URL実装を提供します。
別のHTTPSプロトコル実装を使用する場合は、新しいクラス名を含めるようにjava.protocol.handler.pkgs
システム・プロパティを設定します。 その結果、JDKのデフォルト・クラスより前に、指定したクラスが検索され、ロードされます。 詳細については、java.net.URL
クラスのドキュメントを参照してください。
ノート: 過去のJSSEリリースでは、JSSEのインストール中にjava.protocol.handler.pkgs
システム・プロパティを設定する必要がありました。 このステップは、com.sun.net.ssl.HttpsURLConnection
のインスタンスを取得する場合以外は不要になりました。 詳細については、「トラブルシューティング」セクションの「HttpsURLConnectionクラスを使用するコード」を参照してください。
JDK 1.4以降のリリースには、SunJSSEというJSSE暗号化サービス・プロバイダ(略称はプロバイダ)が標準で付属しています。 基本的に、プロバイダは特定の暗号化アルゴリズムのエンジン・クラスを実装するパッケージです。 JSSEのエンジン・クラスはSSLContext
、KeyManagerFactory
、およびTrustManagerFactory
です。 プロバイダとエンジン・クラスの詳細は、「Java暗号化アーキテクチャ・リファレンス・ガイド」を参照してください。
ノート: SunJSSEがCipher.getInstance()
を呼び出すとき、使用される変換文字列は「RSA/ECB/PKCS1Padding」、「RC4」、「DES/CBC/NoPadding」、「DESede/CBC/NoPadding」になります。 Cipher
クラスと変換文字列の詳細は、「Java暗号化アーキテクチャ・リファレンス・ガイド」を参照してください。
使用する前に、プロバイダを静的または動的に登録する必要があります。 SunJSSEプロバイダは登録済なので、登録する必要はありません。 ほかのプロバイダを使用する場合は、後述のセクションでプロバイダの登録方法を確認してください。
プロバイダを静的に登録するには、セキュリティ・プロパティ・ファイルに次の形式の行を追加します:
security.provider.n=providerClassName
これはプロバイダを宣言し、その優先順位n
を指定します。 優先順位とは、特定のプロバイダが要求されていない場合に、要求されたアルゴリズムについて検索されるプロバイダの順序です。「1」が最優先で次に「2」、というように続きます。
providerClassNameは、プロバイダ・クラスの完全修飾名です。 この名前は、プロバイダ・ベンダーから取得します。
JDK 6に同梱されている標準セキュリティ・プロバイダおよびSunJSSEプロバイダが自動的に登録されます。java.security
セキュリティ・プロパティ・ファイルに次の行が表示され、SunJCE
セキュリティ・プロバイダをプリファレンス順序5で登録し、SunJSSEプロバイダをプリファレンス順序4で登録します:
security.provider.1=sun.security.pkcs11.SunPKCS11 \ ${java.home}/lib/security/sunpkcs11-solaris.cfg security.provider.2=sun.security.provider.Sun security.provider.3=sun.security.rsa.SunRsaSign security.provider.4=com.sun.net.ssl.internal.ssl.Provider security.provider.5=com.sun.crypto.provider.SunJCE security.provider.6=sun.security.jgss.SunProvider security.provider.7=com.sun.security.sasl.Provider
他のJSSEプロバイダを使用する場合は、行を追加して他のプロバイダを登録し、目的の優先順位を指定します。
複数のJSSEプロバイダを同時に登録できます。 登録されたプロバイダには、様々なエンジン・クラスの、様々なアルゴリズムの様々な実装が含まれる場合があり、同じ型のアルゴリズムおよびエンジン・クラスの一部または全部をサポートする場合もあります。 特定のアルゴリズムの特定のエンジン・クラス実装を検索するとき、その検索に特定のプロバイダが指定されていない場合、プロバイダは優先順位で検索され、指定したアルゴリズムの実装を提供する最初のプロバイダの実装が使用されます。
プロバイダを静的に登録するかわりに、プログラム開始時にSecurity.addProvider()
メソッドを呼び出して、実行時に動的にプロバイダを追加できます。 たとえば、プロバイダのクラス名がMyProvider
で、そのMyProvider
クラスがcom.ABC
パッケージにあるプロバイダを動的に追加するには、次を呼び出します。
Security.addProvider(new com.ABC.MyProvider());
Security.addProvider
メソッドは、次に利用できる優先順位に、指定したプロバイダを追加します。
この登録は恒久的ではなく、十分なアクセス権があるプログラムでしか実行できません。
SSLSocketFactory.getDefault
やSSLServerSocketFactory.getDefault
を呼び出すことでデフォルトのSSLSocketFactory
やSSLServerSocketFactory
が作成され、このデフォルトのSSLSocketFactory
(またはSSLServerSocketFactory
)がJSSEリファレンス実装に由来するものであれば、デフォルトのSSLContext
は必ずソケット・ファクトリに関連付けられます。 デフォルトのソケット・ファクトリは、JSSE実装に由来します。
デフォルトのSSLContext
は、デフォルトのKeyManager
およびデフォルトのTrustManager
で初期化されます。 キーストアがjavax.net.ssl.keyStore
システム・プロパティおよび適切なjavax.net.ssl.keyStorePassword
システム・プロパティで指定されている場合、デフォルトの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
ファイルに限られた数の信頼できるルート証明書が付属しています。 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リファレンス実装と同じ方法でそれらを処理してください。
一部の環境では、TLSの使用時に特定のアルゴリズムまたはキー長が望ましくない場合があります。 Oracle JDKでは、jdk.certpath.disabledAlgorithms
およびjdk.tls.disabledAlgorithm
セキュリティ・プロパティを使用して、バージョン・ネゴシエーション、暗号スイートの選択、ピア認証、キー交換メカニズムなど、TLSプロトコル・ネゴシエーション中のアルゴリズムを無効化します。 これらのセキュリティ・プロパティが他のJDK実装によって使用される保証はないことに注意してください。 これらのセキュリティ・プロパティの構文とその現在アクティブな値については、<java-home>/lib/security/java.security
ファイルを参照してください。
jdk.certpath.disabledAlgorithms
プロパティ: CertPathコードは、このセキュリティ・プロパティを使用して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コードでは、このセキュリティ・プロパティを使用して、TLSプロトコル、暗号スイート、キーなどを無効にします。 この構文は、jdk.certpath.disabledAlgorithms
セキュリティ・プロパティに似ています。 たとえば、次の行は、SSLv3アルゴリズムおよびすべてのTLS_*_RC4_*暗号化方式群を無効にします。
jdk.tls.disabledAlgorithms=SSLv3, RC4
特定の条件が必要な場合は、java.security
ファイル内のセキュリティ・プロパティで関連する値を削除するか、JSSEの初期化前に正しいセキュリティ・プロパティを動的に設定することで、それを再アクティブ化できます。
これらのセキュリティ・プロパティでは暗号化方式群の3番目のセットDisabledが事実上作成されることに注意してください。 次のリストで、これら3つのセットを説明します。
jdk.tls.disabledAlgorithms
セキュリティ・プロパティで指定されている)、その暗号化方式群は無効になり、接続ハンドシェークに考慮されません。setEnabledCipherSuites
またはsetSSLParameters
メソッドを呼び出します。 一部の環境では、特定のアルゴリズムが望ましくない可能性があっても、それがレガシー・アプリケーションで使用されている場合は無効化できません。 レガシー・アルゴリズムは引き続きサポートされますが、通常、レガシー・アルゴリズムはセキュリティ強度が十分でないため、アプリケーションでは使用しないでください。 TLSセキュリティ・パラメータのネゴシエーション中、他の候補がないかぎり、レガシー・アルゴリズムはネゴシエーションされません。 セキュリティ・プロパティjdk.tls.legacyAlgorithms
では、Oracle JDKでレガシー・アルゴリズムとみなされるアルゴリズムを指定します。 このセキュリティ・プロパティの構文については、<java-home>/lib/security/java.security
ファイルを参照してください。
ノート:
jdk.tls.disabledAlgorithms
プロパティまたはjava.security.AlgorithmConstraints
API (javax.net.ssl.SSLParameters.setAlgorithmConstraints
メソッドを参照)でも制限されている場合、そのアルゴリズムは完全に無効化されてネゴシエートされません。jdk.tls.legacyAlgorithms
で指定されたアルゴリズムをアプリケーションで使用している場合は、できるだけ早く別のアルゴリズムを使用してください。将来のJDKリリースでは、レガシー・アルゴリズムが制限付きアルゴリズムに指定される可能性があります。SunJSSEプロバイダは、その暗号化のすべてニーズに対してSunJCE実装を使用します。 プロバイダは通常の位置に置くことが推奨されていますが、SunJCEプロバイダより前に登録することにより、他のJCAまたはJCEプロバイダからの実装を使用できます。 「標準JCAメカニズム」は、セキュリティ・プロパティ・ファイルjava-home/lib/security/java.security
を介して静的に、またはjava.security.Security
クラスのaddProvider()
またはinsertProviderAt()
メソッドを介して動的に、プロバイダを構成するために使用できます。
TLS接続では、ハンドシェーク中に一時的なDiffie-Hellmanの(DH)キーを内部的に使用できます。 SunJSSEプロバイダは、TLSハンドシェーク時のエフェメラルDHキー・サイズの強度をカスタマイズするための柔軟なアプローチを提供します。
2048ビット未満のサイズのDiffie-Hellman (DH)キーは強度が不十分なため、非推奨になりました。 システム・プロパティjdk.tls.ephemeralDHKeySize
を使用して、一時的なDHキー・サイズをカスタマイズできます。 このシステム・プロパティは、エクスポート可能な暗号スイートのServerKeyExchange
メッセージのDHキー・サイズには影響しません。 これは、JSSE OracleプロバイダのDHE_RSA、DHE_DSSおよびDH_anonベースの暗号化方式群にのみ影響します。
このプロパティには次のいずれかの値を指定できます。
未定義: 2048ビットのサイズのDHキーは、常にエクスポート不可能な暗号化方式群に使用されます。 これが、このプロパティのデフォルト値です。
legacy
: JSSE Oracleプロバイダは、JDK 7以前のリリースのレガシーの動作を保持しています(512ビットおよび768ビットのサイズのephemeral DHキーの使用など)。
matched
:
エクスポート不可能な匿名暗号スイートの場合、ServerKeyExchange
メッセージのDHキー・サイズは2048ビットです。
X.509証明書ベースの認証(エクスポート不可能な暗号化方式群の)の場合、対応する認証キーに一致するDHキーのサイズが使われます。ただし、1024ビット未満のすべてのキーでは固定サイズ1024ビットが使用され、2048ビットを超えるすべてのキーでは固定サイズ2048ビットが使用されます。
たとえば、認証証明書の公開キー・サイズが2048ビットの場合、暗号化方式群がエクスポート可能でないかぎり、ephemeral DHキー・サイズは2048ビットです。 このキー・サイズ設定スキームにより、認証キーとキー交換キー間の暗号化強度の整合性を確保しています。
1024以上8192以下の64の倍数である有効な整数: エクスポート不可能な暗号化方式群に、指定した値の固定のephemeral DHキー・サイズ(ビット単位)が使われます。
次の表は、システム・プロパティjdk.tls.ephemeralDHKeySize
に指定可能な各値の許容可能な最小および最大DHキー・サイズをまとめたものです:
jdk.tls.ephemeralDHKeySize の値 |
未定義 | legacy |
matched |
整数値(固定) |
---|---|---|---|---|
エクスポート可能なDHキー・サイズ | 512 | 512 | 512 | 512 |
エクスポート不可能な匿名暗号化方式群 | 2048 | 768 | 2048 | 1024以上8192以下の64の倍数である有効な整数: エクスポート不可能な暗号化方式群に、指定した値の固定のephemeral DHキー・サイズ(ビット単位)が使われます。 |
認証証明書 | 2048 | 768 | キーが1024ビット未満か2048ビットを超える場合を除き、キー・サイズは認証証明書と同じです。 キーが1024ビット未満の場合は、1024ビットのDHキーが使用されます。 キーが2048ビットを超える場合は、2048ビットのDHキーが使用されます。
結果として、使用できる値は1024または2048のみになります。 |
固定キー・サイズは有効な整数プロパティ値で指定し、1024以上8192以下の64の倍数である必要があります。 |
より小さい最大フラグメント長をネゴシエートするために、クライアントはClientHelloメッセージにmax_fragment_length型の拡張を含めるオプションを持っています。 システム・プロパティjsse.enableMFLNExtension
を使用して、TLSのMFLN拡張を有効または無効にできます。
メモリーの制限や帯域幅の制限により、制約されたTLSクライアントが最小のフラグメント長をネゴシエートすることが望ましい場合があります。 より小さい最大フラグメント長をネゴシエートするために、クライアントには、(拡張) ClientHelloメッセージにmax_fragment_length型の拡張子を含めるオプションがあります。 RFC 6066を参照してください。
最大フラグメント長が正常にネゴシエートされると、TLSクライアントおよびサーバーは、ネゴシエートされた長さを超えるフラグメントが送信されないように、メッセージ(ハンドシェイク・メッセージを含む)の断片化をすぐに開始できます。
システム・プロパティjsse.enableMFLNExtension
は、MFLN拡張を有効または無効にするために定義されます。 jsse.enableMFLNExtension
はデフォルトで無効になっています。
システム・プロパティの値は、次のように設定できます:
システム・プロパティ | 説明 |
---|---|
jsse.enableMFLNExtension=true |
MFLN拡張機能を有効にします。 SSLParameters.getMaximumPacketSize() の戻り値が(212 + header-size)より小さい場合、最大フラグメント長のネゴシエーション拡張が有効になります。 |
jsse.enableMFLNExtension=false |
MFLN拡張機能を無効にします。 |
jdk.tls.keyLimits
セキュリティ・プロパティを使用して、アルゴリズムが特定のキー・セットで暗号化できるデータの量に制限を指定できます。 この制限に達すると、KeyUpdateポストハンドシェーク・メッセージが送信され、現在のキー・セットを更新するように要求します。 このセキュリティ・プロバイダは、TLS 1.3での対称暗号化にのみ使用できます。
このプロパティの構文は次のとおりです。
jdk.tls.keyLimits=KeyLimit { , KeyLimit } KeyLimit: AlgorithmName KeyUpdate Length
AlgorithmName
: 完全なアルゴリズム変換Length
: KeyUpdateメッセージが送信される前のセッション内の暗号化されたデータの量。 この値は、バイト単位の整数値、または2の累乗値(2^37など)です。 たとえば、次の例では、AES/GCM/NoPaddingアルゴリズムで237バイトを暗号化するとKeyUpdateメッセージが送信されるように指定します。
jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37
jdk.tls.acknowledgeCloseNotify
システム・プロパティがtrueに設定されている場合、クライアントまたはサーバーがclose_notifyアラートを受信すると、対応するclose_notifyアラートが送信され、接続は二重クローズされます。
TLS 1.2以前のバージョンでは、duplex-closeポリシーが使用されています。 しかし、TLS 1.3では、half-closeポリシーが使用されます。つまり、インバウンドおよびアウトバウンドのclose_notifyアラートは独立しています。 TLS 1.3にアップグレードする場合、アプリケーションがSSLEngine.closeInbound()
またはSSLEngine.closeOutbound()
メソッドの1つのみを使用してTLS接続をシャットダウンし、接続の両側で両方を使用しないと、予期しない動作が発生する可能性があります。 基礎となるTLS転送が二重クローズされていないときにアプリケーションが予期せずハングまたはタイムアウトした場合、このプロパティをtrueに設定する必要があります。
TLS接続が不要になった場合、クライアント・アプリケーションとサーバー・アプリケーションはそれぞれそれぞれの接続の両側を閉じる必要があります。
TLS実装によっては、不明な拡張機能を正しく処理できないことがあります。 そのため、JDKで新しい拡張機能が導入されると、予期しない相互運用性の問題が発生する可能性があります。 2つのシステム・プロパティを使用すると、デフォルトの拡張機能をカスタマイズできます:
jdk.tls.client.disableExtensions
: クライアント側で使用される拡張機能をブロックします。jdk.tls.server.disableExtensions
: サーバー側で使用される拡張機能をブロックします。拡張機能が無効になっている場合、ハンドシェーク・メッセージで拡張機能は生成も処理もされません。
これらのシステム・プロパティの値は、カンマ区切りの標準TLS拡張名のリストです。 これらの名前のリストは、トランスポート・レイヤー・セキュリティ(TLS)の拡張を参照してください。 拡張名は大文字と小文字が区別され、不明な、サポートされていないスペルミスや重複した名前は無視されます。
ノート: jsse.enableMFLNExtension
、jsse.enableSNIExtension
、jsse.enableSNIExtension
などの特定のTLS拡張を有効または無効にするシステム・プロパティが存在しますが、対応するシステム・プロパティであっても、jdk.tls.client.disableExtensions
またはjdk.tls.server.disableExtensions
を使用して無効にすると、拡張は有効になりません。
トランスポート・レイヤー・セキュリティの(TLS)ハンドシェイク中にX.509証明書失効ステータスを確認するには、オンライン証明書ステータス・プロトコル(OCSP)を使用します。 クライアント主導型OCSPとOCSPステープリングを参照してください。
Java暗号化アーキテクチャ(JCA)は、暗号化、キー生成とキー合意およびメッセージ認証コード(MAC)アルゴリズム用のフレームワークと実装を提供するパッケージ・セットです。 SunJSSEプロバイダは、すべての暗号化操作にJCAのみを使用し、JCAによるPKCS#11のサポートなど、JCEの機能および拡張機能を自動的に利用できます。 このサポートによりSunJSSEプロバイダは、ハードウェア暗号化アクセラレータを使用してパフォーマンスを大幅に向上させ、キーストアとしてスマート・カードを使用し、キーおよび信頼性管理の柔軟性を高めることができます。
基盤となるアクセラレータ・ハードウェアを使用するようにOracle PKCS#11プロバイダが構成され、そのPKCS#11プロバイダを使用するようにJCAが構成されていれば、ハードウェア暗号化アクセラレータは自動的に使用されます。 プロバイダは、プロバイダ・リストにある他のJCAプロバイダより前に構成する必要があります。 Oracle PKCS#11プロバイダの構成方法の詳細は、PKCS#11のガイドを参照してください。
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アプリケーションの存続期間の間、異なるスマートカードをスマートカード・リーダーに入れることができ、それらのスマートカードは異なるパスワードを使用して保護できます。
java.security.KeyStore.Builder
クラスは、KeyStore
オブジェクトの構造と初期化データを抽象化します。 パスワードのプロンプト用にCallbackHandler
の使用をサポートし、そのサブクラスを使用して、アプリケーションに望まれる追加機能をサポートできます。 たとえば、Builder
を実装して、個々のKeyStore
エントリを異なるパスワードで保護するようにすることが可能です。 その後、 javax.net.ssl.KeyStoreBuilderParameters
クラスを使用し、これらのBuilder
オブジェクトを1つ以上使用してKeyManagerFactoryを初期化できます。
NewSunX509と呼ばれる、SunJSSEプロバイダのX509KeyManager
実装は、これらのパラメータをサポートしています。 複数の証明書が使用可能な場合は、適切なキーを使用する証明書を選択し、期限切れの証明書より有効な証明書を優先させます。
次の例に、PKCS#11キーストア(スマートカードを使用できる)とPKCS#12ファイルベース・キーストアの両方を使用するようJSSEに指示する方法を示します。
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);
SunJSSEプロバイダは、RFC 2712で規定されるKerberos暗号化方式群をサポートします。 次の暗号化方式群がサポートされていますが、デフォルトでは有効になっていません。
これらの暗号化方式群を使用できるようにするには、明示的に指定する必要があります。 詳細については、APIのドキュメントで、SSLEngine.setEnabledCipherSuites()
およびSSLSocket.setEnabledCipherSuites()
メソッドを参照してください。 その他のすべてのSSL/TLS暗号化方式群と同様、暗号化方式群がピアによってサポートされていない場合は、暗号化のネゴシエーション時に選択されません。 また、アプリケーションまたはサーバーが必要な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);
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を使用するように構成されている必要があります。 Java SEでのKerberosの使用の詳細は、「Kerberos要件」のドキュメントを参照してください。
アプリケーションは、Java認証・承認サービス(JAAS)とKerberosログイン・モジュールを使用して、自身のKerberos資格を取得できます。 JDKは、Kerberosログイン・モジュールに付属しています。 JSSEでのKerberos暗号化方式群は、JAASプログラミングがある場合とない場合のJava Generic Security Services (Java GSS)を使用する方法と同様に、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を使用する方法の例は、「Java GSSチュートリアル」で説明されています。 Java GSS呼出しをJSSE呼出しに置き換えることにより、JSSEの使用例に適応できます。
JAASプログラミングありでKerberos暗号化方式群を使用するには、任意のインデックス名を使用できます。これは、アプリケーションが、インデックス名を使用してJAAS LoginContext
を作成し、JSSE呼出しをSubject.doAs()
またはSubject.doAsPrivileged()
呼出しの内部にラップする役割を持つためです。 Java GSSおよびKerberosでJAASを使用する方法の例は、「Java GSSチュートリアル」で説明されています。 Java GSS呼出しをJSSE呼出しに置き換えることにより、JSSEの使用例に適応できます。
Kerberosを使用するようにJSSEアプリケーションを使用し、構成する場合の問題については、Java GSSチュートリアルの「トラブルシューティング」セクションを参照してください。
SSL接続のピアの識別情報を判別するには、次のクラスで、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
など)。
PKCS#12 (個人情報交換構文標準)は、ユーザーの秘密キー、証明書、その他のシークレットおよびその他のアイテムのストレージまたは転送(あるいはその両方)のための移植可能な形式を指定します。 SunJSSEプロバイダは、PKCS12ファイルの読取りおよび書込みのためのPKCS12 java.security.KeyStore
形式の完全な実装を提供します。 この形式は、Netscape/Mozilla、MicrosoftのInternet Explorer、OpenSSLなどほかのツールキットやアプリケーションでもサポートされ、キーと証明書をインポートおよびエクスポートします。 たとえば、これらの実装では、.p12
ファイル名拡張子を使用してクライアント証明書およびキーをファイルにエクスポートできます。
SunJSSEプロバイダでは、PKCS12のキーストア・タイプを使用して、KeyStore
APIを介してPKCS12キーにアクセスできます。 さらにkeytool
コマンドと、pkcs12
に設定された-storetype
オプションを使用して、インストールされたキーおよび関連する証明書を表示できます。 keytool
の詳細は、「セキュリティ・ツール」を参照してください。
ノート: JKSストア形式とは異なり、PKCS#12ストアにアクセスするには、パスワードを指定する必要があります。
SNI拡張は、SSL/TLSプロトコルを拡張し、クライアントがハンドシェーク時に接続を試みるサーバー名を示す機能です。 サーバーはServer Name Indicationの情報を使って、特定のSSLSocket
またはSSLEngine
インスタンスが接続を受け入れる必要があるかどうかを判定できます。 たとえば、単一の基礎となるネットワーク・アドレスで複数の仮想または名前ベースのサーバーがホストされている場合、サーバー・アプリケーションは、SNI情報を使用して、このサーバーが、クライアントがアクセスしようとしている正しいサーバーであるかどうかを判断できます。 このクラスのインスタンスは、サーバーによって、ホスト名などの特定のタイプの受け付け可能なサーバー名を確認するために使用できます。 詳細については、TLS拡張(RFC 6066)のセクション3を参照してください。
クライアント・アプリケーションの開発者は、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);
サーバー・アプリケーションの開発者はSNIMatcher
クラスを使用して、サーバー名の表示を認識する方法を決定できます。 次の2つの例に、この機能を示します。
例1
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);
例2
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
致命的エラーで拒否されます。www\\.invalid\\.com
に構成されたマッチャ:
www.example.com
の場合、それはunrecognized_name
致命的エラーで拒否されます。www.example.org
の場合、それは受け付けられ、ServerHelloメッセージで確認が送信されます。マッチャが構成されていない:
要求されたすべてのホスト名が受け付けられますが、ServerHelloメッセージで確認は送信されません。
SNI拡張を実装する新しいクラスの説明については、次を参照してください。
たとえば、「Server Name Indication (SNI)拡張の使用」を参照してください。
JDK 8u251以降では、Application Layer Protocol Negotiation (ALPN)を使用すると、TLS接続用のアプリケーション・プロトコルをネゴシエートできます。「TLSアプリケーション・レイヤー・プロトコルのネゴシエーション」を参照してください。
このセクションにはJSSEのトラブルシューティングに関する情報が含まれます。 まず、一般的な構成の問題とそれらの解決方法を説明し、次に、役に立つデバッグ・ユーティリティについて説明します。
このセクションでは、JSSEの使用時に発生する可能性のある一般的な構成の問題について説明します。
問題: SSL接続のネゴシエーション中に、クライアントまたはサーバーがCertificateException
をスローします。
原因1: 一般に、リモート側がローカル側に不明な証明書を送信することが原因です。
解決法1: このタイプの問題をデバッグするもっともよい方法は、デバッグをオンにして(「デバッグ・ユーティリティ」を参照)、証明書のロード時およびネットワーク接続経由での証明書の受信時に観察することです。 多くの場合、間違ったトラスト・ファイルをロードしたため、受信した証明書がトラスト・メカニズムにとって不明です。 詳細については、次のセクションを参照してください。
原因2: システム・クロックが正しく設定されていません。 この場合、認識された時間が証明書の有効期間外になっている可能性があります。トラストストアからの有効な証明書と置き換えないかぎり、システムはこの証明書を無効とみなすため、例外をスローします。
解決法2: システム・クロックの時間を修正します。
問題: 信頼できる証明書をPKCS12キーストアに格納しようとすると、java.security.KeyStoreException: TrustedCertEntry not supported
がスローされます。
原因: PKCS12キーストアへの信頼できる証明書の格納はサポートされていません。 PKCS12は、主に関連付けられた証明書チェーンによる秘密キーの配信に使用されます。 「信頼できる」証明書の概念はありません。 相互運用性の観点から、他のPKCS12ベンダーにも同じ制限があります。 MozillaやInternet Explorerなどのブラウザは、信頼できる証明書のみを含むPKCS12ファイルを受け付けません。
解決法: 信頼できる証明書の格納には、JKSキーストアを使用します。
問題: JSSEを使用するプログラムを実行すると、SSLサービスが利用できないという例外が発生します。 たとえば、次のような例外がスローされます。
Exception in thread "main" java.net.SocketException: no SSL Server Sockets Exception in thread "main": SSL implementation not available
原因: キーストア上の誤ったパスワードやキーストアの破損などのため、SSLContext
の初期化で問題が発生しました(かつてあるベンダーが不明な形式のキーストアを出荷し、それが原因でこのタイプのエラーが発生しました。)
解決法: 初期化パラメータを確認します。 指定したキーストアが有効であり、指定したパスワードが正しいことを確認します。 これをチェックできる1つの方法は、keytool
コマンド行ユーティリティを使用して、キーストアと関連の内容を調べることです。
問題: 単純な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を参照してください。
正しいキーストアおよび証明書を渡す方法を学習するには、次のセクションを参照してください。
問題1: ハンドシェーク時にクライアントやサーバーがこの例外をスローします。
原因1: SSL接続の両側が共通の暗号化方式群について合意している必要があります。 クライアントの暗号化方式群セットとサーバーの暗号化方式群セットの共通点がない場合、この例外がスローされます。
解決法1: 有効な暗号化方式群を構成し、共通の暗号化方式群を含めます。さらに、非対称の暗号化方式群に適切なkeyEntry
が提供されるようにします。 このセクションの「実行時例外: "No available certificate..."」を参照してください。)
問題2: DSAベースの証明書しかないサーバーのファイルにNetscape Navigatorや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
問題: 最初のアクセスでJSSEが停止したように見えます。
原因: JSSEには乱数のセキュアなソースが必要です。 初期化には時間がかかります。
解決法: 別の乱数ジェネレータを使用するか、オーバーヘッドが通知されない場合は前もって初期化します。
SecureRandom sr = new SecureRandom(); sr.nextInt(); SSLContext.init(..., ..., sr);
java-home/lib/security/java.security
ファイルには、SecureRandom
のシード・データのソースを指定する方法もあります。 詳細は、ファイルの内容を参照してください。
問題: 次のコード(抜粋)は、JSSE 1.0.xのcom.sun.net.ssl.HttpsURLConnection
を使用して作成されたものです。
import com.sun.net.ssl.*; ...deleted... HttpsURLConnection urlc = new URL("https://example.com/").openConnection();
JSSE 1.0.xで実行すると、このコードはjavax.net.ssl.HttpsURLConnection
オブジェクトを返し、ClassCastException
をスローします。
原因: デフォルトでは、HTTPS URLを開くとjavax.net.ssl.HttpsURLConnection
が作成されます。
解決法: 以前のリリースのJDK (リリース6以前)には、HTTPS URLの実装が付属していませんでした。 JSSE 1.0.x実装は、そのようなHTTPS URLハンドラを提供し、インストールガイドに、URLハンドラの検索パスを設定してJSSE 1.0.xのcom.sun.net.ssl.HttpsURLConnection
実装を取得する方法が記載されています。
JDKでは、URLハンドラのデフォルトの検索パスにHTTPSハンドラがあります。 これは、javax.net.ssl.HttpsURLConnection
のインスタンスを返します。 java.protocol.handler.pkgs
変数を使用して、以前のJSSE 1.0.x実装パスをURL検索パスの先頭に追加することにより、com.sun.net.ssl.HttpsURLConnection
を取得することもでき、前述のコードがキャスト例外をスローすることはなくなります。
% java -Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol YourClass
System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
問題: ソケットが接続を試み、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
など)は、「SSLv2 ClientHello」形式でカプセル化されたSSL/TLS ClientHelloメッセージを送信できます。 SunJSSE
プロバイダはこの機能をサポートしています。 この機能を使用する場合は、必要に応じて「SSLv2Hello」を有効なプロトコル・リストに追加します。 (SunJSSE
プロバイダに対してデフォルトで有効になっているプロトコルをリストする「プロトコル」セクションも参照してください。)
SSL/TLS RFC標準で、実装は、両側で使用されている最新バージョンとネゴシエーションする必要がありますが、認識できないバージョンを提示されると、一部の非準拠実装は単にハングアップします。 たとえば、SSLv3のみを認識する一部の古いサーバー実装は、TLSv1.2を要求されると、シャットダウンします。 この状況では、SSL/TLSバージョンのフォールバック・スキームを使用することを検討してください。
たとえば、クライアントの有効なプロトコル・リストがTLSv1、TLSv1.1およびTLSv1.2の場合、一般的なSSL/TLSバージョンのフォールバック・スキームが次のように見えます。
ノート: 以前のバージョンにフォールバックすることは、通常、セキュリティ強度が弱いプロトコルにダウングレードすることを意味します。 本当に必要で、サーバーが上位のプロトコル・バージョンをサポートしていないことが確実にわかっている場合でないかぎり、フォールバック・スキームを使用することはお薦めしません。
ノート: 一部のサーバーはSSLv3無効化の過程でSSLv2Helloも無効にしていますが、それは、SSLv2Helloアクティブなクライアント(たとえばJDK 1.5/6)との通信が失敗することを意味します。 JDK 7以降では、デフォルトで、SSLv2Helloはクライアント上では無効、サーバー上では有効です。
問題: ハンドシェークが試行され、必要なアルゴリズムが見つからない場合は失敗します。 例は次のとおりです。
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を使用します。 デフォルトでは、Oracle JDKは標準拡張ClassLoaderを使用して、java-home/lib/ext/sunjce_provider.jar
にあるSunJCEプロバイダをロードします。 ファイルが見つからないかロードできない場合、またはSunJCEプロバイダがProvider
メカニズムから登録解除されており、JCEからの代替実装を利用できない場合、この例外がスローされます。
解決法: ファイルがロード可能であることをチェックしてSunJCEが使用可能であることを確認し、プロバイダがProvider
インタフェースで登録されていることを確認します。 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でアプリケーション・リソースを取得しようとしたときに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コンソール、トレースおよびロギング」を参照してください。 問題の原因がjavax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
の場合、SNIのための仮想ホスト構成が正しくない可能性があります。 Apache HTTPサーバーを使用している場合、仮想ホストの構成については、「Name-based Virtual Host Support」を参照してください。 特に、<VirtualHost>
ブロック内でServerName
ディレクティブが正しく構成されていることを確認してください。
詳細については、次の情報を参照してください。
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
このセクションには次のコード例が含まれています。
このセクションでは、JSSEを使って、セキュアでないソケット接続をセキュアなソケット接続に変換するソース・コードの例を説明します。 このセクションのコードは『Java SE 6 Network Security』(Marco Pistoiaほか著)から引用したものです。
まず、「SSLを使用しないソケットの例」で、セキュアでないソケットを使ってクライアントとサーバー間の通信を設定するサンプル・コードを示します。 次に、「SSLを使用するソケットの例」ではこのコードを変更し、JSSEを使用してセキュアなソケット通信を設定します。
次の例では、セキュアでないソケット接続を設定するサーバー側およびクライアント側のコードを示します。
サーバーとして動作し、ソケットを使用してクライアントと通信する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) { }
次の例では、セキュアなソケット接続を設定するサーバー側およびクライアント側のコードを示します。
サーバーとして動作し、セキュアなソケットを使用してクライアントと通信する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の使用法を示すために設計されていることに注意してください。 堅牢なアプリケーションを開発するためのものではありません。
ノート: セキュアな通信を設定すると、アルゴリズムが複雑になります。 サンプル・プログラムでは、設定プロセス中のフィードバックがありません。 プログラムの実行時は、少し待ってください。しばらく出力が表示されない場合があります。 javax.net.debug
システム・プロパティをall
に設定して、プログラムを実行すると、フィードバックが表示されます。 このデバッグ情報の見方の説明は、ガイド「SSL/TLS接続のデバッグ」を参照してください。
ほとんどのサンプル・コードは、このドキュメントと同じディレクトリのsamplesサブディレクトリに格納されています。 そのリンクから、すべてのサンプル・コード・ファイルとテキスト・ファイルのリストを表示できます。 このページでは、すべてのサンプル・コード・ファイルを取得するためにダウンロードできるZIPファイルのリンクもあり、このドキュメントをWeb経由で表示している場合に便利です。
次のセクションでは、サンプルについて説明します。 詳細については、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
samplecacerts
トラスト・ストアのパスワードはchangeit
です。 keytool
ユーティリティを使用して、サンプルの独自の証明書を置き換えることができます。
Netscape NavigatorやMicrosoftのInternet Explorerなどのブラウザを使用して、ClassFileServer
の例で提供されているサンプルのSSLサーバーにアクセスすると、ダイアログ・ボックスが開いて、証明書が認識されないというメッセージが表示されます。 これは、サンプル・プログラムで使用する証明書は自己署名付きのもので、テスト用にすぎないためです。 現在のセッションで証明書に同意できます。 SSLサーバーのテストが終了したあと、ブラウザを終了し、ブラウザの名前空間からテスト用証明書を削除します。
クライアント認証の場合、適切なディレクトリの別のduke
証明書を使用できます。 公開キーおよび証明書も、samplecacerts
ファイルに格納されています。
SSLSocketClient.javaプログラムは、SSLSocket
を使用するクライアントを作成し、HTTP要求を送信してHTTPSサーバーから応答を受け取る方法を示します。 このプログラムの出力は、https://www.verisign.com/index.html
のHTMLソースです。
ファイアウォールの内側から、このプログラムを提供されたとおりに実行しないでください。 ファイアウォールの内側から実行すると、JSSEはファイアウォールを経由したwww.verisign.com
へのパスを検出できないので、UnknownHostException
を受け取ります。 ファイアウォールの外側から実行できる同等のクライアントを作成するには、サンプル・プログラムSSLSocketClientWithTunneling
で示すように、プロキシ・トンネリングを設定します。
SSLSocketClientWithTunneling.javaプログラムは、ファイアウォールの外側からセキュアなWebサーバーにアクセスするプロキシ・トンネリングの方法を示します。 このプログラムを実行するには、次のJavaシステム・プロパティに適切な値を設定する必要があります。
java -Dhttps.proxyHost=webproxy -Dhttps.proxyPort=ProxyPortNumber SSLSocketClientWithTunneling
ノート: -D
オプションによるプロキシ指定はオプションです。 webproxyは使用するプロキシ・ホスト名に、ProxyPortNumberは適切なポート番号に置き換えてください。
プログラムはhttps://www.verisign.com/index.html
のHTMLソース・ファイルを返します。
SSLSocketClientWithClientAuth.javaプログラムは、サーバーから要求された場合にキー・マネージャを設定し、クライアント認証を行う方法を示します。 このプログラムも、クライアントがファイアウォールの外側にはいないことを前提にしています。 SSLSocketClientWithTunneling
の例に従ってプログラムを変更すれば、ファイアウォールの内側から接続することもできます。
このプログラムを実行するには、ホスト、ポート、および要求されたファイル・パスの3つのパラメータを指定する必要があります。 前回の例を反映させるため、ホストにwww.verisign.com
、ポート番号に443
、要求されたファイル・パスにhttps://www.verisign.com/
を設定して、このプログラムをクライアント認証なしで実行できます。 これらのパラメータを使用したときの出力が、Webサイトhttps://www.verisign.com/
のHTMLソースです。
SSLSocketClientWithClientAuth
を実行してクライアント認証を行うには、クライアント認証を要求するサーバーにアクセスする必要があります。 このサーバーには、サンプル・プログラムClassFileServer
を使用できます。 これについては、次のセクションで説明します。
ここでClassFileServer
と呼ばれているプログラムは、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要求を期待します。
サンプル・プログラムSSLSocketClientWithClientAuthおよび ClassFileServer
を使用して、認証済の通信を設定できます。この通信では、クライアントとサーバーが相互に認証します。 サンプルのプログラムは、同じネットワークに接続された別個のマシンで実行することも、1台のマシンで、ただし別のターミナル・ウィンドウまたはコマンド・プロンプト・ウィンドウから両方を実行することもできます。 クライアントとサーバーの両方を設定するには、次を実行します。
ClassFileServer
プログラムを1台のマシンやターミナル・ウィンドウから実行します。SSLSocketClientWithClientAuth
プログラムを別のマシンやターミナル・ウィンドウで実行します。 SSLSocketClientWithClientAuth
には、次のパラメータが必要です。
host
は、ClassFileServer
を実行するために使用するマシンのホスト名です。port
は、ClassFileServer
に指定したものと同じポートです。requestedfilepath
は、サーバーから取得するファイルのパスを示します。 このパラメータには、/filepath
と指定する必要があります。 GET文の一部として使用されるので、ファイル・パスにはフォワード・スラッシュが必要です。GET文には、稼動中のオペレーティング・システムの種類にかかわらず、フォワード・スラッシュが必要です。 文の構成は次のようになります。
"GET " + requestedfilepath + " HTTP/1.0"
ノート: ClassFileServer
が動作しているローカル・マシンに接続するように、他のSSLClient*
アプリケーションのGET
コマンドを変更できます。
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.net.URL
クラスのドキュメントを参照してください。
JSSEでダウンロードできるサンプルには、HTTPS接続の作成方法を示すサンプル・プログラムが2つ含まれています。 これらのサンプル・プログラム(URLReader.java
とURLReaderWithOptions.java
)は、どちらもsamples/urlsディレクトリにあります。
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.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
samples/rmiディレクトリのサンプル・コードは、セキュアなJava Remote Method Invocation (RMI)接続の作成方法を示しています。 サンプル・コードは、RMIのサンプルに基づいており、基本的にはカスタムRMIソケット・ファクトリをインストールして使用するように変更された「Hello World」サンプルです。
Java RMIの詳細は、Java RMIドキュメントを参照してください。 このWebページは、Java RMIのチュートリアルとJava RMIに関する他の情報を示します。
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は単純なアプリケーションであり、I/Oおよびスレッドの発行を単純化してSSLEngine
の操作に重点を置いています。 このアプリケーションは、一般的なByteBuffer
オブジェクトによってSSL/TLSメッセージを交換する2つのSSLEngine
オブジェクトを作成します。 1つのループがすべてのエンジン操作を順番に実行して、セキュアな接続の確立(ハンドシェーク)、アプリケーション・データの転送、およびエンジンのクローズを示します。
SSLEngineResult
は、SSLEngine
の現在の状態に関して多くの情報を提供します。 この例では、すべての状態を調べているわけではありません。 I/Oおよびスレッドの発行を適度に単純化しているため本番稼動環境に適した例ではありませんが、SSLEngine
の全体的な機能の説明に有用です。
このセクションでは、keytool
ユーティリティを使用して、JSSEでの使用に適した単純なJKSキーストアを作成する方法を説明します。 まずキーストア内に(公開キーおよび秘密キーを持つ) keyEntry
を作成し、トラストストア内に対応するtrustedCertEntry
(公開キーのみ)を作成します。 クライアント認証の場合、クライアントの証明書に対して同様の処理に従う必要があります。
ノート: PKCS12での信頼できるアンカーおよび秘密キーの格納は、JDK 8以降でサポートされています。
ノート: ここでは、各ステップに関する詳しい解説は省略します。 詳細は、「Solaris、LinuxまたはmacOS」またはMicrosoft Windowsのkeytool
ドキュメントを参照してください。
ユーザー入力は太字で示します。
対応する公開キーおよび秘密キーによって、新しいキーストアと自己署名付き証明書を作成します。
% 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 Enter key password for <duke> (RETURN if same as keystore password): <CR>
キーストアを調べます。 エントリの型はkeyEntry
で、これは、このエントリに秘密キーが関連付けられていることを示します)。
% keytool -list -v -keystore keystore Enter keystore password: password Keystore type: jks Keystore provider: SUN Your keystore contains 1 entry Alias name: duke Creation date: Dec 20, 2001 Entry type: keyEntry 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: 3c22adc1 Valid from: Thu Dec 20 19:34:25 PST 2001 until: Thu Dec 27 19:34:25 PST 2001 Certificate fingerprints: MD5: F1:5B:9B:A1:F7:16:CF:25:CF:F4:FF:35:3F:4C:9C:F0 SHA1: B2:00:50:DD:B6:CC:35:66:21:45:0F:96:AA:AF:6A:3D:E4:03:7C:74
自己署名付き証明書をエクスポートし、内容を調べます。
% 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----- MIICXjCCAccCBDwircEwDQYJKoZIhvcNAQEEBQAwdjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNB MRIwEAYDVQQHEwlQYWxvIEFsdG8xHzAdBgNVBAoTFlN1biBNaWNyb3N5c3RlbXMsIEluYy4xFjAU BgNVBAsTDUphdmEgU29mdHdhcmUxDTALBgNVBAMTBER1a2UwHhcNMDExMjIxMDMzNDI1WhcNMDEx MjI4MDMzNDI1WjB2MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0 bzEfMB0GA1UEChMWU3VuIE1pY3Jvc3lzdGVtcywgSW5jLjEWMBQGA1UECxMNSmF2YSBTb2Z0d2Fy ZTENMAsGA1UEAxMERHVrZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1loObJzNXsi5aSr8 N4XzDksD6GjTHFeqG9DUFXKEOQetfYXvA8F9uWtz8WInrqskLTNzwXgmNeWkoM7mrPpK6Rf5M3G1 NXtYzvxyi473Gh1h9k7tjJvqSVKO7E1oFkQYeUPYifxmjbSMVirWZgvo2UmA1c76oNK+NhoHJ4qj eCUCAwEAATANBgkqhkiG9w0BAQQFAAOBgQCRPoQYw9rWWvfLPQuPXowvFmuebsTc28qI7iFWm6BJ TT/qdmzti7B5MHOt9BeVEft3mMeBU0CS2guaBjDpGlf+zsK/UUi1w9C4mnwGDZzqY/NKKWtLxabZ 5M+4MAKLZ92ePPKGpobM2CPLfM8ap4IgAzCbBKd8+CMp8yFmifze9Q== -----END CERTIFICATE-----
または、-certreq
を使用して証明書署名リクエスト(CSR)を生成し、署名のために認証局(CA)に送信することもできます。keytool
ドキュメントの"CAからの署名付き証明書のリクエスト" (「Solaris、LinuxまたはmacOS」またはWindows)の項を参照してください。
証明書を新しいトラスト・ストアにインポートします。
% keytool -import -alias dukecert -file duke.cer -keystore truststore Enter keystore password: trustword 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: 3c22adc1 Valid from: Thu Dec 20 19:34:25 PST 2001 until: Thu Dec 27 19:34:25 PST 2001 Certificate fingerprints: MD5: F1:5B:9B:A1:F7:16:CF:25:CF:F4:FF:35:3F:4C:9C:F0 SHA1: B2:00:50:DD:B6:CC:35:66:21:45:0F:96:AA:AF:6A:3D:E4:03:7C:74 Trust this certificate? [no]: yes Certificate was added to keystore
トラストストアを調べます。 エントリの型はtrustedCertEntry
で、これは、秘密キーをこのエントリで使用できないことを示します。 これは、このファイルがKeyManager
のキーストアとして適切でないことも意味します。
% keytool -list -v -keystore truststore Enter keystore password: trustword Keystore type: jks Keystore provider: SUN Your keystore contains 1 entry Alias name: dukecert Creation date: Dec 20, 2001 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: 3c22adc1 Valid from: Thu Dec 20 19:34:25 PST 2001 until: Thu Dec 27 19:34:25 PST 2001 Certificate fingerprints: MD5: F1:5B:9B:A1:F7:16:CF:25:CF:F4:FF:35:3F:4C:9C:F0 SHA1: B2:00:50:DD:B6:CC:35:66:21:45:0F:96:AA:AF:6A:3D:E4:03:7C:74
ここで、適切なキーストアを使用してアプリケーションを実行します。 この例では、デフォルトの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)拡張を使用する方法およびそれを仮想インフラストラクチャに適用する方法を示すコード例について説明します。
このセクションのすべての例で、パラメータの設定後にそれらを適用するには、対応するSSLSocket
、SSLEngine
またはSSLServerSocket
オブジェクトで、setSSLParameters(SSLParameters)
メソッドを呼び出します。
次は、クライアント・アプリケーションを開発するためにSNI拡張の理解を必要とするユース・ケースのリストです。
ケース1. クライアントはwww.example.com
にアクセスする必要があります。
ホスト名を明示的に設定します。
SNIHostName serverName = new SNIHostName("www.example.com"); List<SNIServerName> serverNames = new ArrayList<>(1); serverNames.add(serverName); sslParameters.setServerNames(serverNames);
クライアントは常にホスト名を明示的に指定してください。
ケース2. サーバーでサポートしていないため、クライアントはSNIを使用する必要はありません。
空のサーバー名リストでSNIを無効にします。
List<SNIServerName> serverNames = new ArrayList<>(1); sslParameters.setServerNames(serverNames);
ケース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メッセージを解析するAPIを実装する必要があります。 次の例に、これらの機能を実行できるSSLCapabilities
クラスとSSLExplorer
クラスを示します。
SSLCapabilities.javaはハンドシェーク中にSSL/TLSセキュリティ機能をカプセル化します(つまり、SSL/TLSハンドシェークで受け付けられる暗号化方式群のリスト、レコード・バージョン、helloバージョン、サーバー名表示)。 それは、SSLExplorer.explore()
メソッドによって、SSL/TLS接続のネットワーク・データを調べることによって取得できます。
SSLExplorer.javaはTLSクライアントからの初期ClientHelloメッセージを調査しますが、ハンドシェークを開始したり、ネットワーク・データを消費したりしません。 SSLExplorer.explore()
メソッドはClientHelloメッセージを解析し、SSLCapabilities
にセキュリティ・パラメータを取得します。 このメソッドは、TLS接続でハンドシェークが行われる前に呼び出す必要があります。
このセクションでは、SSLSocket
に基づいて、仮想サーバー・ディスパッチャを使用する手順を説明します。
サーバー名ハンドラを登録します。
このステップで、アプリケーションは様々なサーバー名表示の様々なSSLContext
オブジェクトを作成したり、特定のサーバー名表示を指定した仮想マシンまたは分散システムにリンクしたりできます。
たとえば、サーバー名がwww.example.org
の場合、登録されたサーバー名ハンドラはローカル仮想ホスティングWebサービス用などになります。 ローカル仮想ホスティングWebサービスは指定したSSLContext
を使用します。 サーバー名がwww.example.com
の場合、登録されたサーバー名ハンドラは10.0.0.36
でホストしている仮想マシン用などになります。 ハンドラはこの接続を仮想マシンにマッピングできます。
ServerSocket
を作成し、新しい接続を受け付けます。
ServerSocket serverSocket = new ServerSocket(serverPort); Socket socket = serverSocket.accept();
ソケット入力ストリームからバイトを読み取り、バッファして、バッファ済のバイトを調査します。
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()); }
調査済の機能から要求されたサーバー名を取得します。
List<SNIServerName> serverNames = capabilities.getServerNames();
このサーバー名表示の登録済のサーバー名ハンドラを検索します。
ホスト名のサービスが仮想マシンまたは他の分散システムに存在する場合、アプリケーションは接続を転送先に転送する必要があります。 アプリケーションは、ソケット・ストリームからの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
に基づいて、仮想サーバー・ディスパッチャを使用する手順を説明します。
サーバー名ハンドラを登録します。
このステップで、アプリケーションは様々なサーバー名表示の様々なSSLContext
オブジェクトを作成したり、特定のサーバー名表示を指定した仮想マシンまたは分散システムにリンクしたりできます。
たとえば、サーバー名がwww.example.org
の場合、登録されたサーバー名ハンドラはローカル仮想ホスティングWebサービス用などになります。 ローカル仮想ホスティングWebサービスは指定したSSLContext
を使用します。 サーバー名がwww.example.com
の場合、登録されたサーバー名ハンドラは10.0.0.36
でホストしている仮想マシン用などになります。 ハンドラはこの接続を仮想マシンにマッピングできます。
ServerSocket
またはServerSocketChannel
を作成し、新しい接続を受け付けます。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(...); ... SocketChannel socketChannel = serverSocketChannel.accept();
ソケット入力ストリームからバイトを読み取り、バッファして、バッファ済のバイトを調査します。
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
調査済の機能から要求されたサーバー名を取得します。
List<SNIServerName> serverNames = capabilities.getServerNames();
このサーバー名表示の登録済のサーバー名ハンドラを検索します。
ホスト名のサービスが仮想マシンまたは他の分散システムに存在する場合、アプリケーションは接続を転送先に転送する必要があります。 アプリケーションは、ソケット・ストリームからの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.
ClientHelloメッセージにサーバー名表示がない場合、SNIに従って正しいサービスを選択する方法がありません。 そのような場合、アプリケーションは、サーバー名表示がない場合に、接続をそれに委譲できるように、デフォルトのサービスを指定する必要がある場合があります。
SSLExplorer.explore()
メソッドはSSL/TLSの内容の妥当性をチェックしません。 レコード形式がSSL/TLS仕様に準拠していない場合、またはハンドシェークの起動後に、explore()
メソッドが呼び出された場合、メソッドはIOException
をスローすることがあり、ネットワーク・データを生成できません。 そのような場合、SSL/TLS接続のネゴシエーションに使用されませんが、適切な警告メッセージで接続を閉じるフェールオーバー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は、アルゴリズム、証明書およびキーストアのタイプの一連の標準名を必要とし、これらを使用します。 以前にこの付録Aの仕様および他のセキュリティ仕様(JCA、CertPath)にあった仕様名は、標準名のドキュメントにまとめられました。 特定のプロバイダの情報は、「Oracle Provider Documentation」にあります。
JSSEは完全にプラガブルであり、サード・パーティのJSSEプロバイダの使用には何の制限もありません。
2009年秋に、SSL/TLSプロトコルの問題が見つかりました。 IETF TLS Working Groupによってプロトコルの修正が開発され、JDKの現行バージョンにはこの修正が含まれています。 「Transport Layer Security (TLS)再ネゴシエーションの問題」ページでは、このプロトコル修正を含まない古い実装と通信する際の相互運用性の問題とともに、状況をより詳細に説明しています
次の表に、TLS接続のデジタル・シグネチャで使用されるアルゴリズムで、IANA TLSレジストリの「SignatureSchemeセクション」にも定義されている標準のシグネチャ・スキーム名を示します。
シグネチャ・スキーム | 仕様 |
---|---|
ecdsa_secp256r1_sha256 | RFC 8446 |
ecdsa_secp384r1_sha384 | RFC 8446 |
ecdsa_secp521r1_sha512 | RFC 8446 |
ecdsa_sha1 | RFC 8446 |
ed25519 | RFC 8446 |
ed448 | RFC 8446 |
rsa_pkcs1_sha1 | RFC 8446 |
rsa_pkcs1_sha256 | RFC 8446 |
rsa_pkcs1_sha384 | RFC 8446 |
rsa_pkcs1_sha512 | RFC 8446 |
rsa_pss_pss_sha256 | RFC 8446 |
rsa_pss_pss_sha384 | RFC 8446 |
rsa_pss_pss_sha512 | RFC 8446 |
rsa_pss_rsae_sha256 | RFC 8446 |
rsa_pss_rsae_sha384 | RFC 8446 |
rsa_pss_rsae_sha512 | RFC 8446 |