Java Secure Socket Extension (JSSE)により、セキュアなインターネット通信が可能になります。これは、JavaバージョンのSSL、TLSおよびDTLSプロトコルのフレームワークおよび実装を提供し、データ暗号化、サーバー認証、メッセージの整合性の他、オプションでクライアント認証の機能を含んでいます。
ネットワークを通じてやり取りされるデータには、意図された受信者以外の人も、簡単にアクセスできます。データにパスワードやクレジット・カード番号などの個人情報が含まれる場合、権限のない者がデータを理解できないよう、手段を講じる必要があります。また、意図的であるかどうかにかかわらず、通信中にデータが変更されていないことを確認することも重要です。Secure Sockets Layer (SSL)およびTransport Layer Security (TLS)は、ネットワークを通じたデータの送信時に、データの機密性および整合性を保護するために設計されたプロトコルです。
Java Secure Socket Extension (JSSE)により、セキュアなインターネット通信が可能になります。これにより、JavaバージョンのSSLおよびTLSプロトコルのフレームワークおよび実装が提供されます。また、データ暗号化、サーバー認証、メッセージの整合性の他、オプションでクライアント認証の機能が含まれます。JSSEを使用すると、開発者はHTTP、Telnet、FTPなど、TCP/IP上のアプリケーション・プロトコルを実行するクライアントとサーバーの間で、セキュアなデータのやり取りを実現できます。SSLの概要については、「Secure Sockets Layer (SSL)プロトコルの概要」を参照してください。
JSSEは、基盤となる複雑なセキュリティ・アルゴリズムやハンドシェーク・メカニズムを抽象化することにより、識別するのが難しく、しかし危険なセキュリティ上の弱点が生まれるリスクを最小限に抑えます。また、開発者がそれをアプリケーションに直接統合できる構築ブロックとして使用すると、アプリケーション開発が簡単になります。
JSSEは、アプリケーション・プログラミング・インタフェース(API)フレームワークと、そのAPIの実装を提供します。JSSE APIは、java.security
およびjava.net
パッケージによって定義されたコア・ネットワークおよび暗号化サービスを補い、拡張されたネットワーク・ソケット・クラス、トラスト・マネージャ、キー・マネージャ、SSLコンテキストおよびソケット作成動作をカプセル化するソケット・ファクトリのフレームワークを提供します。SSLSocket
クラスはブロック入出力モデルに基づいているため、Java Development Kit (JDK)には、実装で独自の入出力メソッドを選択できるようにするために非ブロックSSLEngine
クラスが含まれます。
JSSE APIでは、次のセキュリティ・プロトコルがサポートされています。
SSL: バージョン3.0
TLS: バージョン1.0、1.1および1.2
DTLS: バージョン1.0および1.2
これらのセキュリティ・プロトコルは、通常の双方向のストリーム・ソケットをカプセル化し、JSSE APIは認証、暗号化および整合性保護の透過的なサポートを追加します。JDKに付属のJSSE実装ではSSL 2.0は実装されないことに注意してください。
JSSEはJava SEプラットフォームのセキュリティ・コンポーネントであり、Java暗号化アーキテクチャ(JCA)リファレンス・ガイドのフレームワーク内の至るところで見られる同じ設計方針に基づいています。暗号化に関するセキュリティ・コンポーネントのこのフレームワークにより、実装の独立性と、可能な場合にはアルゴリズムの独立性を実現できます。JSSEはJCAフレームワークによって定義された暗号化サービス・プロバイダを使用します。
Java SEプラットフォーム内の他のセキュリティ・コンポーネントには、Java Authentication and Authorization Service (JAAS)リファレンス・ガイドおよびJavaセキュリティ・ツールがあります。JSSEはJCAと同じ概念およびアルゴリズムを多く含んでいますが、単純なストリーム・ソケットAPIの下でこれらを自動的に適用します。
JSSE APIは、その他のSSL/TLS/DTLSプロトコルと公開鍵インフラストラクチャ(PKI)実装をシームレスにプラグインできる設計になっています。開発者が、リモート・ホストの信頼性やリモート・ホストに送信する認証鍵データを決定するロジックを提供することもできます。
JSSEには、次のような重要な利点と特長があります。
SSLSocket
、SSLServerSocket
、およびSSLEngine
)JSSEは、次の暗号化アルゴリズムを使用します。
表8-1 JSSEで使用される暗号化アルゴリズム
暗号化機能 | 暗号化アルゴリズム脚注1 | 鍵の長さ(ビット)脚注2 |
---|---|---|
バルク暗号化 | Advanced Encryption Standard(AES) | 256脚注3 128 |
バルク暗号化 | データ暗号化規格(DES) | 64 (56が有効) 64 (40が有効) |
バルク暗号化 | Rivest Cipher 4 (RC4) | 128 128 (40が有効) |
バルク暗号化 | トリプルDES (3DES) | 192 (112が有効) |
ハッシュ・アルゴリズム | メッセージ・ダイジェスト・アルゴリズム(MD5) | 128 |
ハッシュ・アルゴリズム | Secure Hash Algorithm 1 (SHA1) | 160 |
ハッシュ・アルゴリズム | Secure Hash Algorithm 224 (SHA224) | 224 |
ハッシュ・アルゴリズム | Secure Hash Algorithm 256 (SHA256) | 256 |
ハッシュ・アルゴリズム | Secure Hash Algorithm 384 (SHA384) | 384 |
ハッシュ・アルゴリズム | Secure Hash Algorithm 512 (SHA512) | 512 |
認証 | デジタル署名アルゴリズム(DSA) | 1024, 2048, 3072 |
認証 | Elliptic Curve Digital Signature Algorithm | 160から512 |
認証と鍵交換 | Rivest-Shamir-Adleman (RSA) | 512以上 |
鍵交換 | Static Elliptic Curve Diffie-Hellman (ECDH) | 160から512 |
鍵交換 | Ephemeral Elliptic Curve Diffie-Hellman (ECDHE) | 160から512 |
鍵合意 | Diffie-Hellman (DH) | 512, 768, 1024, 2048, 3072, 4096, 6144, 8192 |
脚注1 SunJSSE実装では、そのすべての暗号化アルゴリズムでJava暗号化アーキテクチャ(JCA)が使用されています。
脚注2 JSSEプロバイダでは、弱いアルゴリズムおよび弱い鍵は無効化または非アクティブ化される場合があります。
脚注3 AES_256を使用する暗号群では、Java Cryptography Extension (JCE)の無制限強度の適切な管轄ポリシー・ファイル・セットが必要となります。これは、JDKに含まれています。デフォルトでは、アクティブな暗号化ポリシーは、無制限です。暗号強度の構成を参照してください。
JSSE標準APIは、javax.net
およびjavax.net.ssl
パッケージで利用でき、次を提供します。
SSLEngine
)。OracleのJava SEの実装には、SunJSSEという名前のJSSEプロバイダが含まれており、これはあらかじめインストールされ、JCAに登録されています。このプロバイダが提供する暗号化サービスは次のとおりです。
SunJSSEプロバイダを参照してください。
次のリストにはオンライン・ドキュメントのリンクと、関連サブジェクトの文書名を示しています。
JSSE APIドキュメント
javax.netパッケージ
javax.net.sslパッケージ
Java SEセキュリティ
Java SEセキュリティのホーム・ページ
JavaチュートリアルのJava SEのセキュリティ機能トレール
暗号化
暗号化とセキュリティに関するページ、Ronald L. Rivest博士著(保守されなくなりました)
『Applied Cryptography』(Bruce Schneier著)、第2版。John Wiley and Sons, Inc.、1996年
『Cryptography Theory and Practice』(Doug Stinson著)、CRC Press, Inc.、1995年。2005年発行第3版。
『Cryptography & Network Security: Principles & Practice』(William Stallings著)、Prentice Hall、1998年。2010年発行第5版。
Secure Sockets Layer(SSL)
『SSL and TLS: Designing and Building Secure Systems』(Eric Rescorla著)、Addison Wesley Professional、2000年。
『SSL and TLS Essentials: Securing the Web』(Stephen Thomas著)、John Wiley and Sons, Inc.、2000年。
『Java 2 Network Security』、第2版(Marco Pistoia,、Duane F Reller、Deepak Gupta、Milind Nagnur、Ashok K Ramani著)、Prentice Hall、1999年。
Transport Layer Security(TLS)
Datagram Transport Layer Security (DTLS)
米国の暗号化政策
次に、一般的に使用されている暗号化用語とその定義を示します。
通信している相手側の識別情報を確認するプロセスです。
デジタル署名付きの文で、あるエンティティ(人や会社など)の識別情報および公開鍵の内容を保証します。証明書は、自己署名されるか証明書発行局(CA)(他のエンティティのために有効な証明書を発行する信頼されているエンティティ)によって発行されます。よく知られているCAには、Comodo、EntrustおよびGoDaddyなどがあります。X509は証明書の一般的な形式であり、JDKのkeytool
で管理できます。
暗号化パラメータの組合わせで、認証、鍵合意、暗号化および整合性保護に使用するセキュリティ・アルゴリズムおよび鍵のサイズを定義します。
データの任意のブロックから比較的小さな固定サイズのビットの文字列(ハッシュと呼ばれる)を生成するために使われるアルゴリズム。暗号化ハッシュ関数はチェックサムに似ており、3つの主な特性があります。一方向の関数であるため、ハッシュからオリジナル・データを生成することはできません。オリジナル・データをわずかに変更しても、結果となるハッシュでは大きな変更になります。暗号化鍵は必要ありません。
短縮形としてプロバイダとだけ呼ばれることもあり、Java暗号化アーキテクチャ(JCA)ではそれを、特定の暗号化アルゴリズムの1つまたは複数のエンジン・クラスを実装するパッケージ(または一連のパッケージ)と定義しています。エンジン・クラスは、具体的な実装のない抽象的な方法で暗号化サービスを定義します。
クライアントとサーバーの認証、データ整合性、およびUDPなどの信頼できないトランスポート・チャネルに基づいたクライアントとサーバーとの間の暗号化通信を管理するプロトコルです。
暗号化/復号化を参照してください。
デジタル署名とは、手書きの署名のデジタル版です。これは、ネットワークで伝送されるデータが、それを送信したと主張する人物からのものであり、送信中にデータが変更されていないことを保証するものです。たとえば、RSAベースのデジタル署名を計算するには、まずデータの暗号化ハッシュを計算し、次に送信者の非公開鍵でハッシュを暗号化します。
暗号化は複雑なアルゴリズムを使用して、元のメッセージ(クリアテキスト)を、復号化しないかぎり、その内容を理解できないエンコードされたメッセージ(暗号テキスト)に変換するプロセスです。復号化とは、暗号テキストからクリアテキストを生成する逆のプロセスです。
データの暗号化および復号化に使用するアルゴリズムは一般に、秘密鍵(対称)暗号化と公開鍵(非対称)暗号化の2つのカテゴリに分けられます。
ネットワーク上のエンドポイントを識別するために使用されるIPv4アドレスまたはIPv6アドレスです。
エンドポイント識別の手順は、SSL/TLSハンドシェーク中に処理されます。
2つのソケット同士が新しいセッションや既存のセッションの使用に同意するネゴシエーションのフェーズです。ハンドシェーク・プロトコルは、レコード・プロトコルを介して交換される一連のメッセージです。ハンドシェークの終了時に、セッションの接続に固有の暗号化鍵や、整合性を保護するための鍵が、鍵合意による秘密に基づいて新たに生成されます。
このドキュメント全体でJava Development Kit (JDK)がインストールされているディレクトリを示すために使用される、可変プレースホルダです。
二者が協力して共通鍵を確立するための方法です。それぞれの側が一定のデータを生成して交換します。そのあと、2つのデータが組み合わされて、1つの鍵が生成されます。適正な非公開初期化データを保持しているユーザーのみが、最終的な鍵を取得できます。Diffie-Hellman (DH)は、一般的な鍵合意アルゴリズムの一例です。
鍵を交換する方法です。一方の側が秘密鍵を生成し、標準的にはRSAにより、ピアの公開鍵を使用して暗号化します。データがピアに送信され、ピアは対応する秘密鍵を使用して鍵を復号化します。
キー・マネージャとトラスト・マネージャは、それぞれの鍵データにキーストアを使用します。キー・マネージャはキーストアを管理し、ユーザーを他のユーザーに対して承認する場合に使用するなど必要に応じて、他のユーザーに公開鍵を提供します。トラスト・マネージャは、管理するトラストストアの情報に基づいて、トラストの対象者を決定します。
キーストアは、鍵データのデータベースです。鍵データにはさまざまな用途があり、それには認証やデータ整合性も含まれます。利用できるキーストアには様々なタイプがあり、その中にはPKCS12やOracleのJKSも含まれます。
一般に、キーストア情報は、鍵エントリと信頼される証明書エントリ2つのカテゴリに分類できます。鍵エントリはエンティティの識別情報とその秘密鍵から構成されており、様々な暗号化の目的で使用できます。これとは対照的に、信頼される証明書のエントリには、公開鍵とそのエンティティの識別情報しか含まれていません。したがって、javax.net.ssl.KeyManager
の場合など、秘密鍵が必要な場合は、信頼される証明書エントリを使用することはできません。JKSのJDK実装では、キーストアに鍵のエントリと、信頼される証明書エントリの両方を含めることができます。
トラストストアとは、トラストの対象を決めるときに使用するキーストアです。すでに信頼しているエンティティからデータを受け取る場合、およびそのエンティティが発信元を名乗るエンティティであることを確認できる場合は、データは実際にそのエンティティから届いたものであると仮定できます。
ユーザーがそのエンティティを信頼する場合にのみ、エントリをトラストストアに追加する必要があります。ユーザーは、鍵のペアを生成するか、証明書をインポートすることにより、そのエントリにトラストを与えます。トラストストア内のすべてのエントリは信頼されたエントリとみなされます。
2つの異なるキーストア・ファイルを持つと便利な場合があります。1つは鍵エントリのみのファイル、もう1つはCA証明書を含む信頼された証明書エントリを含むファイルです。前者には機密性のある情報が含まれますが、後者には含まれません。単独のキーストア・ファイルではなく、2つのファイルを使用すると、独自の証明書(および対応する秘密鍵)と他の証明書を論理的に区別した明確な区分が提供されます。秘密鍵の保護を強化するには、アクセスが制限されたキーストアにそれらを保存し、必要に応じて、より公的にアクセスできるキーストアで信頼される証明書を提供することもできます。
信頼できない媒体に送信または格納された情報の整合性を、秘密鍵に基づいてチェックする方法を提供します。通常、MACは秘密鍵を共有する2つの当事者間で、お互いが送信する情報を検証するために使用されます。
暗号化ハッシュ機能に基づくMACメカニズムは、HMACと呼ばれます。HMACは、共有する秘密鍵と組み合せて、Message Digest 5 (MD5)やSecure Hash Algorithm (SHA-256)などの暗号化ハッシュ関数とともに使用できます。HMACについては、RFC 2104で規定されています。
2つの鍵を生成する暗号化アルゴリズムを使用する鍵暗号化システムです。一方の鍵は公開されますが、他方は秘密のままです。公開鍵と非公開鍵では、逆の暗号化処理がなされ、一方の鍵で暗号化したものを他方の鍵で復号化します。公開鍵暗号化は、非対称暗号化とも呼ばれます。
すべてのデータ(アプリケーション・レベルであるかハンドシェーク・プロセスの一部であるかに関係なく)を独立したデータのレコードにパッケージ化するプロトコルで、TCPストリーム・ソケットがアプリケーション・バイト・ストリームをネットワーク・パケットに変換するのとよく似ています。個々のレコードは、現在の暗号化鍵と整合性保護鍵によって保護されます。
データの暗号化と復号化に同じ鍵を使用する暗号化アルゴリズムを使用する暗号化システム。秘密鍵暗号化は対称暗号化とも呼ばれます。
クライアントとサーバーの認証、データ整合性、およびクライアントとサーバーとの間の暗号化通信を管理するプロトコルです。
認証されたピア識別情報、暗号化方式群、鍵合意の秘密を含む名前付きの状態情報のコレクションで、セキュアなソケット・ハンドシェークを通じてネゴシエーションが行われ、複数のセキュアなソケット・インスタンス間で共有できます。
クライアントとサーバーの認証、データ整合性、およびTCPなどの信頼できるトランスポート・チャネルに基づいたクライアントとサーバーとの間の暗号化通信を管理するプロトコルです。
TLS 1は、SSL 3.0プロトコルの後継です。
キー・マネージャ/トラスト・マネージャを参照してください。
キーストア/トラストストアを参照してください。
Secure Sockets Layer (SSL)は、Webで暗号化を実装する場合にもっともよく使用されるプロトコルです。SSLは、ネットワークでセキュアな通信を行うために暗号化プロセスを組み合わせて使用します。このセクションでは、SSLおよびSSLが使用する暗号化プロセスについて簡単に説明します。
SSLは、インターネット通信で使用される標準的なTCP/IPソケットプロトコルをセキュアに拡張します。表8-2に示すように、Secure Sockets Layerは標準TCP/IPプロトコル・スタックのトランスポート層とアプリケーション層の間に追加されます。SSLとともにもっともよく使用されるアプリケーションは、インターネットWebページ用のプロトコルであるHypertext Transfer Protocol (HTTP)です。この他にも、Net News Transfer Protocol (NNTP)、Telnet、Lightweight Directory Access Protocol (LDAP)、Interactive Message Access Protocol (IMAP)、File Transfer Protocol (FTP)などのアプリケーションがあり、同様にSSLとともに使用します。
表8-2 SSLによるTCP/IPプロトコル・スタック
TCP/IPの層 | プロトコル |
---|---|
アプリケーション層 | HTTP、NNTP、Telnet、FTPなど |
Secure Sockets Layer | SSL |
トランスポート層 | TCP |
インターネット層 | IP |
SSLは1994年にNetscape社によって開発され、インターネットの世界で使用されるようになると、標準的な存在になりました。現在では、国際的な標準化機構であるInternet Engineering Task Force (IETF)が管理しています。IETFはSSLの名称をTransport Layer Security (TLS)に変更し、1999年1月に最初の仕様のバージョン1.0をリリースしました。TLS 1.0は、SSLの最新バージョン3.0を少しだけ変更したものです。このアップグレードでは、前のバージョンでの不具合が修正され、既知の弱いアルゴリズムの使用が禁止されました。TLS 1.1は2006年4月にリリースされ、TLS 1.2は2008年8月にリリースされました。
次の3つの理由から、機密情報をネットワークで送信する際に危険が伴う場合があります。
SSLはこれらの各問題に対処します。最初の問題には、認証と呼ばれるプロセスで、通信の当事者双方に相手側の識別情報をオプションで確認させることで対応しています。両者が認証されると、SSLはセキュアなメッセージ伝送のために両者間の暗号化接続を提供します。両者の通信を暗号化することで機密性が保持されるため、2番目の問題に対処します。SSLで使用する暗号化アルゴリズムには、セキュアなハッシュ関数が含まれており、これはチェックサムに似ています。これにより、送信中にデータが変更されていないことが保証されます。セキュアなハッシュ関数により、3番目のデータの整合性の問題に対処します。
注意:
認証も暗号化もオプションであり、2つのエンティティ間のネゴシエーションされた暗号化方式群に依存します。SSLを使用する場合の明確な例は電子商取引です。電子商取引では、通信するサーバーの識別情報は保証されていると考えるべきではありません。クレジット・カードの番号を入力するだけですばらしいサービスが受けられるという偽のWebサイトを作成するのは簡単なことです。SSLを使うと、クライアントがサーバーの識別情報を認証することができます。また、サーバーもクライアントの情報を認証できますが、インターネット上の取引では、この方法はあまり使われていません。
クライアントとサーバーが互いの情報を認証すると、SSLは暗号化アルゴリズムを使用して機密性とデータの整合性を提供します。これにより、クレジット・カード番号のような機密情報をインターネット上でセキュアに送信することができます。
SSLは認証、機密性およびデータの整合性を提供しますが、非拒否サービスは提供しません。非拒否性とは、メッセージを送信したエンティティは、後で送信を拒否できないということを意味します。メッセージとデジタル署名が関連付けられていると、後になって通信内容を証明することができます。SSL単独では、非拒否性を提供しません。
SSLが有効な理由の1つに、複数の暗号化プロセスを使用していることがあります。SSLは、公開鍵暗号化で認証を行い、秘密鍵暗号化とハッシュ関数で機密性とデータ整合性を提供します。SSLについて理解する前に、暗号化の処理方法を理解しておくと役立ちます。
暗号化の主な目的は、2者間の秘密の通信に権限のない第三者がアクセスしたり、その内容を理解するのを困難にすることです。暗号化のプロセスによって、データに対する権限のないすべてのアクセスを必ずしも制限できるわけではありませんが、権限のない者が秘密のデータを理解できないようにすることができます。暗号化では、複雑なアルゴリズムを使用して、元のメッセージ(クリアテキスト)をエンコードされたメッセージ(暗号テキスト)に変更します。ネットワーク上で転送されるデータの暗号化および復号化に使用するアルゴリズムは一般に、秘密鍵暗号化と公開鍵暗号化の2つのカテゴリに分けられます。
秘密鍵暗号化も公開鍵暗号化も、合意に基づく暗号鍵または暗号鍵のペアを使用します。鍵は、データの暗号化プロセスおよび復号化プロセスで暗号化アルゴリズムが使用するビット文字列です。暗号化鍵は、錠の鍵と同様、錠を開けることができるのは正しい鍵のみです。
通信の当事者が互いに鍵を安全に送信するのは、些細な問題ではありません。公開鍵証明書を使用すると、公開鍵を安全に送信し、受信者に公開鍵の信頼性を保証できます。公開鍵証明書を参照してください。
秘密鍵暗号化と公開鍵暗号化における暗号化処理の説明では、セキュリティのコミュニティで広く使用されている慣例(通信する2人の当事者はAliceとBobという名前で示される)に従います。権限のない第三者は攻撃者とも呼ばれ、Charlieと名付けられます。
秘密鍵暗号化では、通信するAliceとBobはメッセージの暗号化と復号化に同じ鍵を使用します。暗号化されたデータをネットワークで送信する前に、AliceとBobは鍵を持っていることが必要で、暗号化と復号化に使用する暗号化アルゴリズムに同意している必要があります
秘密鍵暗号化で大きな問題の1つが、攻撃者にアクセスされずに一方から他方に鍵を渡す方法の問題です。AliceとBobが秘密鍵暗号化でデータを保護しても、Charlieがその鍵にアクセスできればAliceとBobの間で傍受した秘密メッセージを理解できます。CharlieはAliceとBobのメッセージを復号化できるだけではなく、Aliceになりすまして暗号化データをBobに送信することもできるのです。Bobには、メッセージがCharlieから届いたものかAliceから届いたものかはわかりません。
秘密鍵の配布の問題が解決すれば、秘密鍵暗号化はたいへん貴重なツールになります。そのアルゴリズムにより、優れたセキュリティと暗号化データが比較的迅速に提供できるからです。SSLセッションで送信される機密性の高いデータの多くは、秘密鍵暗号化で送信されます。
秘密鍵暗号化は、データの暗号化と復号化の両方に同じ鍵を使用するため、対称暗号化とも呼ばれます。よく知られている秘密鍵暗号化アルゴリズムには、Advanced Encryption Standard (AES)、Triple Data Encryption Standard (3DES)およびRivest Cipher 4 (RC4)があります。
公開鍵暗号化は、公開鍵と秘密鍵の両方を使用することで鍵の配布方法の問題を解決しました。公開鍵はネットワークを通じて公開し、送信できますが、非公開鍵は通信の1人の当事者にしか公開されません。公開鍵と非公開鍵は暗号化方式が逆で、一方の鍵で暗号化したものをもう一方の鍵で復号化します。
Bobが公開鍵暗号化を使用して、Aliceに秘密のメッセージを送信するとします。Aliceは公開鍵と非公開鍵をどちらも持っているので、非公開鍵は安全な場所に保管しておき、公開鍵をBobに送信します。BobはAliceの公開鍵を使ってAliceへの秘密のメッセージを暗号化します。Aliceは非公開鍵を使ってメッセージを復号化します。
Aliceが自分の秘密鍵を使用してメッセージを暗号化し、その暗号化されたメッセージをBobに送信すれば、Bobが受信するデータはAliceから届いたものだと考えることができます。BobがAliceの公開鍵でデータを復号化できれば、そのメッセージはAliceが自分の秘密鍵で暗号化したものに間違いなく、Aliceの秘密鍵を持っているのはAliceのみです。問題は、Aliceの公開鍵が公開されているために、だれもがメッセージを読めてしまうことです。このシナリオは、セキュアなデータ通信を考慮に入れていませんが、デジタル署名の基本を示しています。デジタル署名とは公開鍵証明書のコンポーネントの1つで、SSLでクライアントやサーバーを認証するために使用します。公開鍵証明書とデジタル署名を参照してください。
公開鍵暗号化は、データの暗号化と復号化に別の鍵を使用するので、非対称暗号化とも呼ばれます。SSLでよく使われる、よく知られている公開鍵暗号化アルゴリズムは、Rivest Shamir Adleman (RSA)アルゴリズムです。このほかにも、秘密鍵を交換するために設計されたSSLを使う公開鍵暗号化アルゴリズムには、Diffie-Hellman (DH)があります。公開鍵暗号化には膨大な計算が必要なため、速度が大幅に遅くなります。そこで、この方式は暗号化データ通信全体に使用するよりもむしろ、秘密鍵など少量のデータを暗号化する場合にだけ使用します。
秘密鍵暗号化と公開鍵暗号化のどちらにも、長所と短所があります。秘密鍵暗号化では、データの暗号化や復号化に時間はかかりませんが、通信者同士が同じ秘密鍵情報を共有する必要があり、鍵の交換の方法が問題になります。公開鍵暗号化では、鍵を秘密にする必要がないので鍵の交換は問題になりませんが、データの暗号化と復号化に使用するアルゴリズムには膨大な計算が必要で、著しく遅くなります
公開鍵証明書を使用すると、エンティティは非対称暗号化で使用する公開鍵を安全に配布できます。公開鍵証明書は、次の状況を回避します。Charlieが自分の公開鍵と秘密鍵を作成すれば、自分はAliceだと名乗ってBobに公開鍵を送信できます。BobはCharlieと通信できますが、データをAliceに送信していると思い込んでしまいます。
公開鍵証明書は電子的なパスポートだと考えることができます。これは、信頼できる組織によって発行され、所有者に識別情報を提供します。公開鍵証明書を発行する信頼できる組織を、証明書発行局(CA)と呼びます。CAは公証人にたとえることができます。CAから証明書を取得するには、識別情報の証拠となるものを提供する必要があります。CAは、申請者が申し立てる組織の代表であるとの確証が得られたら、証明書に含まれる情報の妥当性を証明する証明書に署名します。
公開鍵証明書には、次のようなフィールドがあります。
AliceがBobに公開鍵証明書で自分の公開鍵を送信するとき、BobがAliceの公開鍵のみを有効として受け付ける場合、CharlieがAliceになりすましたとしても、BobがだまされてCharlieに秘密情報を送信することはありません。
複数の証明書を証明書チェーンでリンクすることもできます。証明書チェーンを使用する場合、最初の証明書は必ず送信者の証明書です。次は送信者の証明書を発行したエンティティの証明書です。チェーン内に他の証明書がある場合、それぞれ直前の証明書を発行した証明書発行局の証明書です。チェーンの最後の証明書は、ルートCAの証明書です。ルートCAは、広く信頼されている公開証明書発行局です。複数のルートCAの情報は、通常、クライアントのインターネット・ブラウザに保存されています。この情報には、CAの公開鍵が含まれています。よく知られているCAにはVeriSign、Entrust、およびGTE CyberTrustがあります。
暗号化されたデータを送信する場合、SSLは通常、暗号化ハッシュ関数を使ってデータの整合性を保証します。ハッシュ関数を使って、AliceがBobに送ったデータをCharlieが改ざんできないようにします。
暗号化ハッシュ関数はチェックサムに似ています。主な違いは、チェックサムがデータの偶発的変化を検出するのに対し、暗号化ハッシュ関数は故意による変更を検出するように設計されています。データが暗号化ハッシュ関数で処理されると、ハッシュと呼ばれる小さなビット文字列が生成されます。メッセージがごくわずかだけ変更された場合も、結果として生成されるハッシュは大きく変更されます。暗号化ハッシュ関数には、暗号化鍵が必要ありません。SSLとともによく使用されるハッシュ関数は、Secure Hash Algorithm (SHA)です。SHAは、U.S. National Institute of Standards and Technology (NIST)によって提案されました。
メッセージ認証コード(MAC)は暗号化ハッシュに似ていますが、秘密鍵をベースにしている点が異なります。秘密鍵情報が暗号化ハッシュ関数で処理したデータに含まれている場合、その結果生成されるハッシュはHMACと呼ばれます。
Aliceは、BobへのメッセージをCharlieが確実に改ざんしないようにする場合、メッセージのHMACを計算して元のメッセージにHMACを追加できます。次に、Bobと共有している秘密鍵を使用してメッセージとHMACを暗号化できます。Bobは、メッセージを復号化してHMACを計算すれば、送信中にメッセージが変更されたかどうかを知ることができます。SSLでは、HMACを使ってセキュアなデータを送信します。
SSLを使った通信は、クライアントとサーバー間の情報交換から始まります。この情報交換をSSLハンドシェークと呼びます。SSLハンドシェークには、次のステージがあります。
SSLセッションは、どの暗号群を使用するかについて、クライアントとサーバーがネゴシエーションを行うことから始まります。暗号化方式群とは、コンピュータがデータを暗号化するために使用する暗号化アルゴリズムと鍵のサイズのセットです。符号化方式には、公開鍵交換アルゴリズムまたは鍵合意アルゴリズム、および暗号化ハッシュ関数に関する情報が含まれます。クライアントは利用できる暗号群をサーバーに伝え、サーバーは、どちらにも適用できる暗号群を選択します。
SSLの認証ステップはオプションです。しかし、Web上の電子商取引の例では、一般にクライアントがサーバーを認証します。サーバーの認証により、サーバーが表すとクライアントが信じているエンティティを、そのサーバーが実際に表していることをクライアントが確認できます。
サーバーは、自らが表すと唱える組織に属していることを証明するため、クライアントに公開鍵証明書を提示します。この証明書が有効であれば、クライアントはサーバーの識別情報について確信できます。
クライアントとサーバーは、同じ秘密鍵について同意できる情報を交換します。たとえば、RSAを使う場合、クライアントは公開鍵証明書で取得したサーバーの公開鍵を使用して、秘密鍵情報を暗号化します。クライアントは暗号化された秘密鍵情報をサーバーに送信します。復号化にはサーバーの非公開鍵が必要なので、サーバーでだけ、このメッセージを復号化できます。
クライアントとサーバーは、同じ秘密鍵にアクセスします。それぞれのメッセージでは、ハンドシェークの最初のステップで選択した暗号化ハッシュ関数と、共有された秘密情報を使用して、メッセージに添付されるHMACを計算します。次に、秘密鍵と、ハンドシェークの最初のステップでネゴシエーションされた秘密鍵アルゴリズムを使用し、セキュアなデータとHMACを暗号化します。そのあと、クライアントとサーバーは、暗号化されハッシュ化されたデータを使ってセキュアに通信することができます。
SSLハンドシェークでは、SSLハンドシェークについて概要を説明しました。それは、暗号化されたメッセージを送信する前にクライアントとサーバーの間で行われる情報の交換です。図8-1に詳細を示します。これは、SSLハンドシェークで交換される一連のメッセージを示しています。特定の状況下でだけ送信されるメッセージには「optional」と記されています。各SSLメッセージについては、後で詳しく説明します。
図8-1 SSL/TLSハンドシェーク
SSLメッセージは、次の順序で送信されます。
注意:
クライアントに証明書を要求するのは、ごく一部のインターネット・サーバー・アプリケーションのみです。close_notify
アラートを送信し、接続が終了したことをピアに伝えます。SSLセッションで生成したパラメータを保存しておけば、将来のSSLセッションでこれらのパラメータを再利用できます。SSLセッションのパラメータを保存しておけば、暗号化通信をすばやく開始できます。
初期のハンドシェークが完了してアプリケーション・データが流れているとき、いずれの側からでも新しいハンドシェークをいつでも開始できます。特に重要な操作について、アプリケーションで強力な暗号化方式群を使用したり、サーバー・アプリケーションでクライアント認証が必要になる場合もあります。
理由は何であれ、新しいハンドシェークが既存の暗号化セッションに置き換わり、新しいセッションが確立されるまで、アプリケーション・データとハンドシェーク・メッセージが交互に配置されます。
アプリケーションで次のいずれかのメソッドを使用して、新しいハンドシェークを開始できます。
SSLSocket.startHandshake()
SSLEngine.beginHandshake()
注意:
再ネゴシエーションに関するプロトコルの問題が2009年に見つかりました。プロトコルおよびJava SE実装はいずれも修正されています。Transport Layer Security (TLS)再ネゴシエーションの問題を参照してください。SSL/TLSプロトコルは、保護された接続を確保するための一連の特定のステップを定義します。ただし、暗号化方式群の選択が、接続で確保するセキュリティのタイプに直接影響します。たとえば、匿名暗号化方式群を選択した場合、アプリケーションにはリモート・ピアの識別情報を検証する方法がありません。暗号化しない方式群が選択された場合は、データの機密性を保護できません。またSSL/TLSプロトコルでは、受信した資格と、ピアから送信されることが予期される資格が一致するようにとは規定していません。接続がなんらかの理由で悪意のあるピアにリダイレクトされたときに、悪意のあるピアのクレデンシャルが現在のトラスト・データに基づいて受け付けられた場合、その接続は有効とみなされてしまいます。
raw SSLSocket
およびSSLEngine
クラスを使用する場合は、データの送信前に必ずピアのクレデンシャルをチェックしてください。SSLSocket
およびSSLEngine
クラスは、URL内のホスト名がピアのクレデンシャル内のホスト名と一致することを自動的に検証しません。ホスト名が検証されない場合、URL不正行為によってアプリケーションが悪用される可能性があります。JDK 7以降は、エンドポイント識別/検証の手順は、SSL/TLSハンドシェーク中に処理できます。SSLParameters.getEndpointIdentificationAlgorithmメソッドを参照してください。
HTTPS (HTTP Over TLS)などのプロトコルでは、ホスト名検証が必要です。JDK 7以降、HTTPSエンドポイント識別は、HttpsURLConnectionのためのハンドシェーク中にはデフォルトで強制されます。SSLParameters.getEndpointIdentificationAlgorithmメソッドを参照してください。また、アプリケーションは、HostnameVerifierインタフェースを使用してデフォルトのHTTPSホスト名規則をオーバーライドできます。HostnameVerifierインタフェースおよびHttpsURLConnectionクラスを参照してください。
Transport Layer Security (TLS)のハンドシェーク中にX.509証明書失効ステータスを判定するには、オンライン証明書ステータス・プロトコル(OCSP)を使用します。
CRLは、失効した証明書を単純に示すリストです。証明書を受け取るアプリケーションは、CRLサーバーからCRLを取得し、受け取った証明書がリストにあるかどうかを確認します。CRLの使用で不便な点は2つあります。それは、証明書を失効させることができるが、その失効した証明書はCRLにリストされないということです。
CRLは非常に大きくなる可能性があるため、ネットワーク・トラフィックがかなり増加する可能性があります。
多くのCRLは長い有効期間で作成されます。これにより、証明書がその有効期間内に失効し次のCRL更新までリストに含まれなくなる可能性が高くなります。
Java PKIプログラマーズ・ガイドの証明書/CRLストレージ・クラスのトピックを参照してください。
クライアント主導型OCSPでは、クライアントはOCSPを使用してOCSPレスポンダに連絡し、証明書の失効ステータスを確認します。必要なデータの量は少なく、OCSPレスポンダは、CRLよりも最新の失効ステータスを把握している可能性があります。サーバーに接続する各クライアントは、確認する証明書ごとに1つのOCSP応答が必要となります。サーバーがよく使用されるものであり、クライアントの多くでクライアント主導型OCSPが使用されている場合、これらのOCSP要求は、OCSPレスポンダのパフォーマンスに悪影響を及ぼす可能性があります。
OCSPステープリングにより、クライアントではなくサーバーで、OCSPレスポンダへの要求を行うことができます。サーバーは、証明書へのOCSP応答をステープリングして、TLSハンドシェーク中にそれをクライアントに返します。この手法では、発行元のCAではなく、証明書の提示者が、OCSP応答の提供のリソース・コストを負担できます。また、サーバーでOCSP応答をキャッシュし、それらをすべてのクライアントに提供できるようになります。これにより、応答をキャッシュし各クライアントによってではなくサーバーによって定期的に更新できるため、OCSPレスポンダの負荷が大幅に軽減されます。
クライアント主導型オンライン証明書ステータス・プロトコル(OCSP)では、クライアントが、Transport Layer Security (TLS)ハンドシェーク中にOCSPレスポンダに接続することで、証明書失効ステータスを確認できます。
クライアント主導型OCSPの要求は、TLSハンドシェーク中の、クライアントがサーバーから証明書を受け取りそれを検証したすぐ後に発生します。SSLハンドシェークを参照してください。
クライアント主導型OCSPを使用するTLSハンドシェーク
クライアント主導型OCSPは、サーバーの証明書失効ステータスを確認するために、クライアントとサーバーとの間のTLSハンドシェーク中に使用されます。クライアントは証明書を受け取った後、証明書検証を実行します。検証が成功すると、クライアントは、証明書が発行者によって失効にされていないことを確認します。これは、OCSPレスポンダにOCSP要求を送信することで行われます。OCSP応答を受け取ると、クライアントは、TLSハンドシェークが完了する前にこの応答を確認します。
通常、クライアントは証明書のAuthority Information Access (AIA)拡張情報を調べてOCSPレスポンダのURLを見つけますが、システム・プロパティの使用によってそれを静的URLに設定できます。
オンライン証明書ステータス・プロトコル(OCSP)ステープリングでは、発行元の証明書発行局(CA)ではなく、証明書の提示者が、証明書の失効ステータスを含むOCSP応答の提供のリソース・コストを負担できます。
OCSPステープリングを使用するTLSハンドシェーク
OCSPステープリングは、サーバーの証明書失効ステータスを確認するために、クライアントとサーバーとの間のTransport Layer Security (TLS)ハンドシェーク中に使用されます。サーバーはOCSPレスポンダへのOCSP要求を行い、クライアントに返された証明書にOCSP要求をステープリングします。サーバーにOCSPレスポンダへの要求を行わせることで、応答をキャッシュし、多数のクライアントのために複数回使用できます。
TLSハンドシェークは、TLS ClientHello
メッセージで始まります。OCSPステープリングが使用される場合、このメッセージは、サーバーがOCSP要求を実行する必要があることを示すstatus_request
拡張情報とともにサーバーに送信されます。ClientHello
メッセージを処理した後、サーバーは、証明書ごとに適切なOCSPレスポンダにOCSP要求を送信します。サーバーはOCSPレスポンダからOCSP応答を受け取ると、TLSハンドシェークでOCSP応答が提供されることを示すstatus_request
拡張情報とともに、ServerHello
メッセージを送信します。次に、サーバーはサーバー証明書チェーンを提示し、その後にそれらの証明書の1つ以上のOCSP応答で構成されているメッセージが続きます。ステープリングされたOCSP応答とともに証明書を受け取るクライアントは、各証明書を検証してから、ハンドシェークを続行する前にそのOCSP応答を確認します。
クライアントの視点から述べると、証明書のための、サーバーからのステープリング済OCSP応答がない場合、クライアントはクライアント主導型OCSPまたはCRLを使用して失効情報を取得しようとします。これらのどちらかが有効になっている場合、失効チェックはtrue
に設定されます。
TLSハンドシェーク・メッセージの詳細は、SSLハンドシェークを参照してください。
ステータス要求と複数ステータス要求との比較
OCSPステープリング機能は、TLS Certificate Status Request拡張機能(RFC 6066の第8項)およびMultiple Certificate Status Request拡張機能(RFC 6961)を実装します。
TLS Certificate Status Request拡張機能は、証明書チェーン内のサーバー証明書のみのために失効情報を要求しますが、Multiple Certificate Status Request拡張機能は、証明書チェーン内のすべての証明書のために失効情報を要求します。サーバー証明書の失効情報のみがクライアントに送信される場合、チェーン内の他の証明書を、証明書失効リスト(CRL)またはクライアント主導型OCSPを使用して検証できます(ただし、クライアントを、これを行うように設定する必要があります)。
TLSによって、サーバーがクライアントの証明書も要求するようにできますが、クライアントが適切なOCSPレスポンダに連絡して、サーバーに送信された証明書に応答をステープリングできるようにする、OCSPステープリングでの用意はできていません。
OCSPの要求と応答
OCSPの要求メッセージと応答メッセージは、通常は、暗号化されていないHTTPを介して送信されます。応答は、CAによって署名されます。
必要な場合は、ステープリングされた応答は、クライアント・コードで、ExtendedSSLSession
オブジェクトでgetStatusResponses
メソッドを呼び出すことで取得できます。メソッド・シグネチャは次のとおりです。
public List<byte[]> getStatusResponses();
OCSP応答は、RFC 6960内のASN.1で示されている形式でDistinguished Encoding Rules (DER)を使用してエンコードされます。
オンライン証明書ステータス・プロトコル(OCSP)ステープリングは、クライアント側で、システム・プロパティjdk.tls.client.enableStatusRequestExtension
をtrue
(そのデフォルト値)に設定することで有効になります。
このトピックでは、オンライン証明書ステータス・プロトコル(OCSP)使用時の各種プロパティの設定の効果をリストします。クライアント主導型OCSPとOCSPステープリングの両方で使用されるプロパティを示します。
サーバー側のプロパティ
大部分のプロパティは、SSLContext
のインスタンス化時に読み取られます。これは、プロパティを設定した場合に、新しいSSLContext
オブジェクトを取得して、そのSSLContext
オブジェクトから取得するSSLSocket
またはSSLEngine
オブジェクトがそのプロパティ設定を反映するようにする必要があることを意味します。1つの例外は、jdk.tls.stapling.responseTimeout
プロパティです。このプロパティは、ServerHandshaker
オブジェクトの作成時に評価されます(基本的に、SSLSocket
またはSSLEngine
オブジェクトの作成と同時)。
表8-3 サーバー側のOCSPステープリングのプロパティ
プロパティ | 説明 | デフォルト値 |
---|---|---|
jdk.tls.server.enableStatusRequestExtension |
OCSPステープリングのサーバー側サポートを有効にします。 | False |
jdk.tls.stapling.responseTimeout |
キャッシュから取得するかOCSPレスポンダに連絡することで取得するかに関係なく、サーバーでOCSP応答の取得に使用される最長時間を制御します。 受取済の応答は、該当する場合は実行中のステープリングのタイプに基づいて、 |
5000 (ミリ秒単位の整数値) |
jdk.tls.stapling.cacheSize |
エントリ内の最大キャッシュ・サイズを制御します。 キャッシュがいっぱいで、新しい応答をキャッシュする必要がある場合は、最も長い間使用されていないキャッシュ・エントリが、新しいものに置き換えられます。このプロパティの値がゼロ以下の場合は、キャッシュに含めることができる応答の数に上限がないことを意味します。 |
256個のオブジェクト |
jdk.tls.stapling.cacheLifetime |
キャッシュされた応答の最長存続時間を制御します。 応答にキャッシュ存続時間より早く期限切れになるnextUpdateフィールドがある場合は、応答の存続時間を、このプロパティで設定されている値よりも短くできます。このプロパティの値がゼロ以下の場合は、キャッシュ存続時間が無効になります。オブジェクトにnextUpdate値がなく、キャッシュ存続時間が無効になっている場合、応答はキャッシュされません。 |
3600秒(1時間) |
jdk.tls.stapling.responderURI |
TLSのために使用される証明書にAuthority Info Access (AIA)拡張情報がない場合は、管理者がデフォルトURIを設定できるようになります。 |
未設定 |
jdk.tls.stapling.responderOverride |
|
False |
jdk.tls.stapling.ignoreExtensions |
|
False |
クライアント側の設定
表8-4 OCSPステープリングで使用されるクライアント側設定
PKIXBuilderParameters | checkRevocationプロパティ | PKIXRevocationChecker | 結果 |
---|---|---|---|
デフォルト | デフォルト | デフォルト | 失効チェックは無効です。 |
デフォルト | True | デフォルト | 失効チェックは有効です。[1] |
インスタンス化済 | デフォルト | デフォルト | 失効チェックは有効です。[1] |
インスタンス化済 | デフォルト | インスタンス化され、PKIXBuilderParameters オブジェクトに追加されています。 |
失効チェックは有効であり、[1]PKIXRevocationChecker の設定に従って動作します。 |
脚注1 ocsp.enable
セキュリティ・プロパティがtrue
に設定されている場合のみクライアント側のOCSPのフォールバックが起こるということに注意してください。
開発者は、OCSPステープリングによって提供された応答をいくらか柔軟に処理できます。OCSPステープリングによって、証明書パス・チェックおよび失効チェックに関わる現在の方法論が変更されることはありません。これは、クライアントとサーバーの両方でstatus_request
拡張情報をアサートし、CertificateStatus
メッセージによってOCSP応答を取得し、ユーザーが失効情報またはその不足に柔軟に対応できるようになることを意味します。
呼出し側によってPKIXBuilderParameters
が提供されない場合は、失効チェックは無効になります。呼出し側がPKIXBuilderParameters
オブジェクトを作成し、setRevocationEnabled
メソッドを使用して失効チェックを有効にする場合は、ステープリングされたOCSP応答が評価されます。これは、com.sun.net.ssl.checkRevocation
プロパティがtrue
に設定されている場合にも当てはまります。
セキュアな通信を行うには、接続の両側がSSL対応であることが必要です。JSSE APIの接続のエンドポイント・クラスは、SSLSocket
およびSSLEngine
です。図8-4では、SSLSocket
とSSLEngine
の作成に使用される主なクラスを論理的な順序で並べています。
SSLSocket
はSSLSocketFactory
またはイン・バウンド接続を受け取るSSLServerSocket
によって作成されます。SSLServerSocket
はSSLServerSocketFactory
で作成されます。SSLSocketFactory
およびSSLServerSocketFactory
オブジェクトはどちらもSSLContext
で作成されます。SSLEngine
は、SSLContext
によって直接作成され、アプリケーションに依存してすべての入出力を処理します。
注意:
rawSSLSocket
クラスまたはSSLEngine
クラスを使用する場合は、データの送信前に必ずピアのクレデンシャルをチェックしてください。JDK 7以降は、エンドポイント識別/検証の手順は、SSL/TLSハンドシェーク中に処理できます。メソッドSSLParameters.setEndpointIdentificationAlgorithmを参照してください。 たとえば、URL内のホスト名は、ピアのクレデンシャル内のホスト名と一致します。ホスト名が検証されない場合、URL不正行為によってアプリケーションが悪用される可能性があります。
抽象クラスjavax.net.SocketFactory
は、ソケットの作成に使われます。このクラスのサブクラスは、ソケットの特定のサブクラスを作成し、パブリック・ソケット・レベルの機能を追加するための汎用フレームワークを提供するファクトリです。たとえば、SSLSocketFactory and 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をカプセル化します。SSLContextクラスを参照してください。ソケット・インスタンスのセキュアなソケット・セッションの作成を管理するAPIもありますが、トラストおよび鍵管理は直接公開されません。
javax.net.ssl.SSLServerSocket
クラスはSSLSocket
クラスに似ていますが、サーバー・ソケットの作成に特化して使われます。
ピアの不正行為を防止するには、常にSSLSocket
に提示されるクレデンシャルを検証してください。符号化方式の選択とリモート・エンティティの検証を参照してください。
注意:
SSLプロトコルとTLSプロトコルは複雑なので、接続時の受信バイトがハンドシェークのデータとアプリケーション・データのどちらなのかを予測し、現在の接続状態にどのような影響を与えるか(処理を中断させることもある)を予測するのは困難です。Oracle JSSEの実装では、SSLSocket.getInputStream()
によって取得されたオブジェクトのavailable()
メソッドは、SSL接続で正常に復号化されても、アプリケーションではまだ読み込まれていないデータのバイト数を返します。SSLSocketのインスタンスは、次のいずれかの方法で取得できます。
SSL/TLS/DTLSが利用される機会はますます増えています。広範な計算プラットフォームやデバイスを包含するさまざまなアプリケーションで使用されています。この普及に伴い、アプリケーションのパフォーマンス、拡張性、サイズおよびその他の要件を満たすため、様々な入出力モデルやスレッド・モデルでSSL/TLS/DTLSを使用することが求められています。ブロックおよび非ブロック入出力チャネル、非同期入出力、任意の入力ストリームと出力ストリームおよびバイト・バッファでSSL/TLS/DTLSの使用が求められています。また、数千のネットワーク接続を管理することが必要な、非常に拡張性の高いパフォーマンス重視の環境で使用することも求められています。
Java SEのSSLEngine
クラスを使用する入出力トランスポート・メカニズムの抽象により、トランスポートに依存せずにSSL/TLS/DTLSプロトコルをアプリケーションで使用できるようになったため、アプリケーション開発者は最もニーズを満たすトランスポート・モデルや計算モデルを自由に選択できます。この新しい抽象化により、非ブロック入出力チャネルや他の入出力モデルをアプリケーションで使用できるだけでなく、異なるスレッド・モデルにも対応できます。事実上これは、入出力とスレッドの決定がアプリケーション開発者に委ねられることになります。こうした柔軟性のため、アプリケーション開発者は、それ自体が複雑な問題でもある入出力とスレッドを管理するとともに、SSL/TLS/DTLSプロトコルをある程度理解する必要があります。このように、新しい抽象は高度なAPIなので、初心者はSSLSocket
を使用してください。
Java Generic Security Services (Java GSS-API)やJava Simple Authentication Security Layer (Java SASL)などの他のJavaプログラミング言語APIのユーザーは、アプリケーションがデータをトランスポートする役割を担うことの類似点に気付くでしょう。
コア・クラスは、javax.net.ssl.SSLEngine
です。これは、SSL/TLS/DTLS状態マシンをカプセル化し、SSLEngine
クラスのユーザーによって供給されるインバウンドとアウトバウンドのバイト・バッファ上で動作します。図8-5は、アプリケーションから、SSLEngine
を経由して、トランスポート・メカニズムまで進み、戻ってくるデータのフローを示しています。
図8-5 SSLEngineを通るデータのフロー
左側に示されるアプリケーションは、アプリケーションのプレーンテキスト・データをアプリケーション・バッファに供給し、それをSSLEngineに渡します。SSLEngineオブジェクトは、バッファに格納されているデータ、またはハンドシェーク・データを処理してSSL/TLS/DTLSエンコード・データを生成し、アプリケーションによって提供されるネットワーク・バッファに格納します。次にアプリケーションは、右側に示されている適切なトランスポートを使用して、ネットワーク・バッファの内容をピアに送信する役割を実行します。トランスポートを介してピアからSSL/TLS/DTLSエンコード・データを受け取ると、アプリケーションはそのデータをネットワーク・バッファに格納し、SSLEngineに渡します。SSLEngineオブジェクトは、ネットワーク・バッファの内容を処理し、ハンドシェーク・データまたはアプリケーション・データを生成します。
SSLEngine
クラスのインスタンスは次のいずれかの状態になります。
SSLEngine
の作成と初期化は完了しましたが、まだ使用されてはいません。この段階では、アプリケーションにより、SSLEngine
固有のあらゆる設定(暗号化方式群の有効化、SSLEngine
がクライアント・モードとサーバー・モードのどちらでハンドシェークを行うかなど)を行うことができます。ハンドシェークが始まると、次のハンドシェークから新しい設定(クライアント/サーバー・モードの設定を除く)が使用されます。SSLSession
が確立されるまでの間、2つのピアが通信パラメータを交換する手続きです。この段階では、アプリケーション・データは送信できません。SSLEngine
からアプリケーション・データが送信されます。アウトバウンド・アプリケーション・メッセージは暗号化され、データの整合性が確保されます。インバウンド・メッセージでは、この逆の手続きが行われます。SSLEngine
構成設定は使用されません。SSLEngine
を終了し、ピアと送受信するメッセージが残っている場合は送受信を完了してから、配下の転送メカニズムを終了する必要があります。いったん閉じられたエンジンを再利用することはできません。新しいSSLEngine
を作成する必要があります。SSLContext.createSSLEngine()
メソッドを使用してSSLEngine
オブジェクトを作成します。
SSLEngine
オブジェクトを作成する前に、クライアントまたはサーバーとして動作するようにエンジンを構成し、使用する暗号化方式群やクライアント認証が必要かどうかなどの他の構成パラメータも設定します。SSLContext.createSSLEngine
メソッドはjavax.net.ssl.SSLEngine
オブジェクトを作成します。
注意:
サーバー名とポート番号は、サーバーとの通信には使用されません(すべてのトランスポートはアプリケーションが担当します)。それらは、SSLセッションキャッシングおよび取得すべきサーバー・クレデンシャルを決定するために、Kerberosベースの暗号化方式群の実装に使用するJSSEプロバイダへのヒントです。例8-1 キーストアとしてJKSを使用してTLSのためのSSLEngineクライアントを作成するサンプル・コード
次のサンプル・コードでは、キーストアとしてJKSを使用するTLSのためのSSLEngineクライアントを作成します。
import javax.net.ssl.*; import java.security.*; // Create and initialize the SSLContext with key material char[] passphrase = "passphrase".toCharArray(); // First initialize the key and trust material KeyStore ksKeys = KeyStore.getInstance("JKS"); ksKeys.load(new FileInputStream("testKeys"), passphrase); KeyStore ksTrust = KeyStore.getInstance("JKS"); ksTrust.load(new FileInputStream("testTrust"), passphrase); // KeyManagers decide which key material to use KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); kmf.init(ksKeys, passphrase); // TrustManagers decide whether to allow connections TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ksTrust); // Get an instance of SSLContext for SSL/TLS protocols sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); // Create the engine SSLEngine engine = sslContext.createSSLengine(hostname, port); // Use as client engine.setUseClientMode(true);
2つの主要なSSLEngine
メソッドはwrap()
とunwrap()
です。それらは、それぞれネットワーク・データの生成と消費を担当します。SSLEngine
オブジェクトの状態に応じて、このデータはハンドシェーク・データかアプリケーション・データになります。
それぞれのSSLEngine
オブジェクトには、存続期間中に数種類の段階があります。アプリケーション・データを送信または受信できるようにするには、SSL/TLSプロトコルで、暗号化パラメータを確立するためのハンドシェークが必要です。このハンドシェークでは、SSLEngine
オブジェクトによる一連のやり取りのステップが必要です。ハンドシェーク自体の詳細は、SSLハンドシェークを参照してください。
初期ハンドシェーク時に、wrap()
およびunwrap()
メソッドはハンドシェーク・データを生成および消費し、アプリケーションはデータのトランスポートを担当します。wrap()
およびunwrap()
メソッド・シーケンスは、ハンドシェークが終了するまで繰り返されます。それぞれのSSLEngine
操作によりSSLEngineResult
クラスのインスタンスが生成され、その中のSSLEngineResult.HandshakeStatus
フィールドは、ハンドシェークを進めるために実行する必要のある次の動作を決定するために使用されます。
表8-5に、一般的なハンドシェーク時に呼び出されるメソッドのシーケンスと、対応するメッセージおよびステータスを示します。
表8-5 一般的なハンドシェーク
クライアント | SSL/TLSメッセージ | HandshakeStatus |
---|---|---|
wrap() | ClientHello | NEED_UNWRAP |
unwrap() | ServerHello/Cert/ServerHelloDone | NEED_WRAP |
wrap() | ClientKeyExchange | NEED_WRAP |
wrap() | ChangeCipherSpec | NEED_WRAP |
wrap() | Finished | NEED_UNWRAP |
unwrap() | ChangeCipherSpec | NEED_UNWRAP |
unwrap() | Finished | FINISHED |
表8-6に、一般的なSSL/TLSハンドシェーク時の状態マシンと、対応するメッセージおよびステータスを示します。
ハンドシェークの完了時に、wrap()
をさらに呼び出すと、アプリケーション・データおよびパッケージを消費してトランスポートしようとします。unwrap()
メソッドはその逆を試みます。
ピアにデータを送信するには、まずアプリケーションがSSLEngine.wrap()
を介して送信するデータを提供し、対応するSSL/TLSエンコード・データを取得します。次にアプリケーションは、選択したトランスポート・メカニズムを使用してエンコード・データをピアに送信します。アプリケーションは、トランスポート・メカニズムを介してピアからSSL/TLSエンコード・データを受け取ると、SSLEngine.unwrap()
を介してそのデータをSSLEngine
に送り、ピアによって送信されたプレーン・テキスト・データを取得します。
例8-2 非ブロックSocketChannelの作成のサンプル・コード
次の例では、非ブロックSocketChannel
を使用してピアと通信するSSLアプリケーションを示します。これは、例8-1で作成したSSLEngine
を使用してエンコードすることにより、ピアに文字列helloを送信します。バイト・バッファの大きさを決定するために、SSLSession
からの情報を使用しています。
注意:
この例は、非ブロックSocketChannel
を組み込んだSelector
を使用することにより、堅牢性と拡張性を高めることができます。// Create a nonblocking socket channel SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress(hostname, port)); // Complete connection while (!socketChannel.finishedConnect()) { // do something until connect completed } //Create byte buffers for holding application and encoded data SSLSession session = engine.getSession(); ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize()); ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize()); ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize()); ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize()); // Do initial handshake doHandshake(socketChannel, engine, myNetData, peerNetData); myAppData.put("hello".getBytes()); myAppData.flip(); while (myAppData.hasRemaining()) { // Generate SSL/TLS/DTLS encoded data (handshake or application data) SSLEngineResult res = engine.wrap(myAppData, myNetData); // Process status of call if (res.getStatus() == SSLEngineResult.Status.OK) { myAppData.compact(); // Send SSL/TLS/DTLS encoded data to peer while(myNetData.hasRemaining()) { int num = socketChannel.write(myNetData); if (num == 0) { // no bytes written; try again later } } } // Handle other status: BUFFER_OVERFLOW, CLOSED ... }
例8-3 非ブロックSocketChannelからのデータの読取りのサンプル・コード
次のサンプル・コードでは、同じ非ブロックSocketChannel
からデータを読み取り、例8-1で作成したSSLEngine
を使用して、そのデータからプレーン・テキストを抽出する方法を示しています。このコードが反復されるごとに、ハンドシェーク処理が進行しているかどうかに応じて、プレーン・テキストが生成されたり、生成されなかったりします。// Read SSL/TLS/DTLS encoded data from peer int num = socketChannel.read(peerNetData); if (num == -1) { // The channel has reached end-of-stream } else if (num == 0) { // No bytes read; try again ... } else { // Process incoming data peerNetData.flip(); res = engine.unwrap(peerNetData, peerAppData); if (res.getStatus() == SSLEngineResult.Status.OK) { peerNetData.compact(); if (peerAppData.hasRemaining()) { // Use peerAppData } } // Handle other status: BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED ... }
Datagram Transport Layer Security (DTLS)プロトコルは、信頼できる順次のデータ配信を必要とせず提供しない、データグラムでのTLSのトラフィックを構築するように設計されています。Java Secure Socket Extension (JSSE) APIおよびSunJSSEセキュリティ・プロバイダでは、DTLSプロトコルがサポートされています。
TLSはTCPなどの透過的な信頼できるトランスポート・チャネルを必要とするため、信頼できないデータグラム・トラフィックの保護には使用できないためです。DTLSは、TLSのデータグラム互換型です。
JSSE APIでは、Secure Socket Layer (SSL)プロトコルとTransport Layer Security (TLS)プロトコルとともにDTLSバージョン1.0とDTLSバージョン1.2がサポートされるようになりました。
javax.net.ssl.SSLEngine
プログラミング・モデルは、DTLS用JSSE APIで使用されます。
アプリケーション・データを送信または受信できるようにするには、DTLSプロトコルで、暗号化パラメータを確立するためのハンドシェークが必要です。このハンドシェークでは、SSLEngine
オブジェクトによる、クライアントとサーバーとの間の一連のメッセージのやり取りが必要です。
DTLSハンドシェークでは、すべてのメッセージが正しく受け取られる必要があります。したがって、信頼できないデータグラム・トラフィックでは、不足または遅延しているパケットを再送信する必要があります。javax.net.ssl.SSLEngine
は入出力操作を担当しないため、アプリケーションが、タイマーを提供し、再送信が必要なときにSSLEngineに知らせる役目を担います。アプリケーションのためにタイマーおよび再送信の戦略を実装することが重要です。DTLS接続での再送信の処理を参照してください。
DTLSハンドシェークには、次のステージがあります。
DTLSセッションは、どの暗号群を使用するかについて、クライアントとサーバーがネゴシエーションを行うことから始まります。暗号化方式群とは、コンピュータがデータを暗号化するために使用する暗号化アルゴリズムと鍵のサイズのセットです。符号化方式には、公開鍵交換アルゴリズムまたは鍵合意アルゴリズム、および暗号化ハッシュ関数に関する情報が含まれます。クライアントは利用できる暗号群をサーバーに伝え、サーバーは、どちらにも適用できる暗号群を選択します。
Cookieは、サービス妨害攻撃(DoS)を防ぐために、暗号群とともに、クライアントとサーバーとの間ので交換されます。
認証ステップはオプションです。しかし、Web上の電子商取引の例では、クライアントがサーバーを認証します。サーバーの認証により、サーバーが表すとクライアントが信じているエンティティを、そのサーバーが実際に表していることをクライアントが確認できます。
サーバーは、自らが表すと唱える組織に属していることを証明するため、クライアントに公開鍵証明書を提示します。この証明書が有効であれば、クライアントはサーバーの識別情報について確信できます。
クライアントとサーバーは、同じ秘密鍵について同意できる情報を交換します。たとえば、RSAを使う場合、クライアントは公開鍵証明書で取得したサーバーの公開鍵を使用して、秘密鍵情報を暗号化します。クライアントは暗号化された秘密鍵情報をサーバーに送信します。復号化にはサーバーの非公開鍵が必要なので、サーバーでだけ、このメッセージを復号化できます。
クライアントとサーバーは、同じ秘密鍵にアクセスします。それぞれのメッセージでは、ハンドシェークの最初のステップで選択した暗号化ハッシュ関数と、共有された秘密情報を使用して、メッセージに添付されるHMACを計算します。次に、秘密鍵と、ハンドシェークの最初のステップでネゴシエーションされた秘密鍵アルゴリズムを使用し、セキュアなデータとHMACを暗号化します。そのあと、クライアントとサーバーは、暗号化されハッシュ化されたデータを使ってセキュアに通信することができます。
DTLSハンドシェークでは、SSLEngine
オブジェクトによって、クライアントとサーバーとの間で一連の送受信メッセージが交換されます。
図8-7に、DTLSハンドシェークで交換される一連のメッセージを示します。特定の状況下でだけ送信されるメッセージには「optional」と記されています。各メッセージをこの図の後に説明します。
DTLSハンドシェーク・メッセージの詳細は、DTLSバージョン1.0およびDTLSバージョン1.2を参照してください。
次のハンドシェーク・メッセージは、DTLSハンドシェーク中にクライアントとサーバーとの間で交換されます。
クライアントは、それがサポートする最上位バージョンのDTLSおよび暗号化方式群のリストを含むサーバー情報を送信します。暗号化方式群の情報には、暗号化アルゴリズムと鍵のサイズが含まれます。
サーバーは、CookieとともにクライアントからのClientHelloメッセージに応答します。
クライアントは、それがサポートする最上位バージョンのDTLSおよび暗号化方式群のリストとともに、2つ目のClientHelloメッセージをサーバーに送信します。HelloVerifyRequestで受け取ったCookieは、サーバーに送り返されます。
サーバーは、クライアントとサーバーの両方がサポートする最上位バージョンのDTLSと最適な暗号化方式群を選択し、この情報をクライアントに送信します。
サーバーはクライアントに証明書または証明書チェーンを送信します。証明書チェーンは通常、サーバーの公開鍵証明書で始まり、認証局のルート証明書で終わります。このメッセージはオプションで、サーバー認証を求められた場合に使用します
サーバーは、クライアントを認証する必要がある場合、クライアントに証明書要求を送信します。インターネット・アプリケーションでは、このメッセージが使われることはほとんどありません。
Certificateからの公開鍵情報が鍵交換を行うのに不十分な場合、サーバーはクライアントにサーバー鍵交換メッセージを送信します。たとえば、Diffie-Hellman (DH)に基づく暗号化方式群では、このメッセージにはサーバーのDH公開鍵が含まれています。
サーバーは、最初のネゴシエーション・メッセージを終了したことをクライアントに伝えます。
サーバーがクライアントに証明書を要求すると、クライアントはサーバーが前に行ったように、その証明書チェーンを送信します。
注意:
クライアントに証明書を要求するのは、ごく一部のインターネット・サーバー・アプリケーションのみです。クライアントは、対称暗号化で使用する鍵を作成するために使用される情報を生成します。RSAでは、クライアントはサーバーの公開鍵でこの鍵情報を暗号化してサーバーに送信します。DHに基づく暗号化方式群の場合、このメッセージにはクライアントのDH公開鍵が含まれています。
このメッセージは、上述のとおりクライアントが証明書を提示する場合にクライアントによって送信されます。このメッセージは、サーバーにクライアントの認証処理を完了させるためのものです。このメッセージが使用されると、クライアントは暗号化ハッシュ関数で電子的に署名した情報を送信します。サーバーがクライアントの公開鍵でこの情報を復号化すれば、サーバーはクライアントを認証できます。
クライアントは、新しくネゴシエーションされたCipherSpecおよび鍵によって後続のデータが保護されることと、そのデータが暗号化されることをサーバーに伝えるメッセージを送信します。
クライアントはサーバーに、セキュアなデータ通信を開始する準備ができたことを伝えます。
サーバーは、新しくネゴシエーションされたCipherSpecおよび鍵によって後続のデータが保護されることと、そのデータが暗号化されることをクライアントに伝えるメッセージを送信します。
サーバーはクライアントに、セキュアなデータ通信を開始する準備ができたことを伝えます。これでDTLSハンドシェークが終了します。
初期のハンドシェークが完了してアプリケーション・データが流れているとき、いずれの側からでも新しいハンドシェークをいつでも開始できます。特に重要な操作について、アプリケーションで強力な暗号化方式群を使用したり、サーバー・アプリケーションでクライアント認証が必要になる場合もあります。
理由は何であれ、新しいハンドシェークが既存の暗号化セッションに置き換わり、新しいセッションが確立されるまで、アプリケーション・データとハンドシェーク・メッセージが交互に配置されます。
アプリケーションでSSLEngine.beginHandshake()
メソッドを使用して、新しいハンドシェークを開始できます。
注意:
再ネゴシエーションに関するプロトコルの問題が2009年に見つかりました。プロトコルおよびJava SE実装はいずれも修正されています。Transport Layer Security (TLS)再ネゴシエーションの問題を参照してください。例8-4 DTLSハンドシェーク・ステータスおよび全体的ステータスの処理のサンプル・コード
void handshake(SSLEngine engine, DatagramSocket socket, SocketAddress peerAddr) throws Exception { boolean endLoops = false; // private static int MAX_HANDSHAKE_LOOPS = 60; int loops = MAX_HANDSHAKE_LOOPS; engine.beginHandshake(); while (!endLoops && (serverException == null) && (clientException == null)) { if (--loops < 0) { throw new RuntimeException("Too many loops to produce handshake packets"); } SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP || hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) { ByteBuffer iNet; ByteBuffer iApp; if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) { // Receive ClientHello request and other SSL/TLS/DTLS records byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); try { socket.receive(packet); } catch (SocketTimeoutException ste) { // Retransmit the packet if timeout List <Datagrampacket> packets = onReceiveTimeout(engine, peerAddr); for (DatagramPacket p : packets) { socket.send(p); } continue; } iNet = ByteBuffer.wrap(buf, 0, packet.getLength()); iApp = ByteBuffer.allocate(1024); } else { iNet = ByteBuffer.allocate(0); iApp = ByteBuffer.allocate(1024); } SSLEngineResult r = engine.unwrap(iNet, iApp); SSLEngineResult.Status rs = r.getStatus(); hs = r.getHandshakeStatus(); if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) { // The client maximum fragment size config does not work? throw new Exception("Buffer overflow: " + "incorrect client maximum fragment size"); } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) { // Bad packet, or the client maximum fragment size // config does not work? if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { throw new Exception("Buffer underflow: " + "incorrect client maximum fragment size"); } // Otherwise, ignore this packet } else if (rs == SSLEngineResult.Status.CLOSED) { endLoops = true; } // Otherwise, SSLEngineResult.Status.OK if (rs != SSLEngineResult.Status.OK) { continue; } } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) { // Call a function to produce handshake packets List <DatagramPacket> packets = produceHandshakePackets(engine, peerAddr); for (DatagramPacket p : packets) { socket.send(p); } } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) { runDelegatedTasks(engine); } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { // OK, time to do application data exchange endLoops = true; } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) { endLoops = true; } } SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { throw new Exception("Not ready for application data yet"); } }
信頼できる接続でのSSL/TLSでは、データは正しい順序で到着することが保証されており、再送信は不要です。ただし、DTLSの場合は、信頼できないメディアで機能することが多く、不足または遅延しているハンドシェーク・メッセージを再送信する必要があります。
SSLEngine
クラスはトランスポートにまったく依存しない方法で動作し、アプリケーション層がすべての入出力を実行します。SSLEngine
クラスは入出力を担当しないため、かわりにアプリケーションが、タイマーの提供、および再送信が必要なときのSSLEngine
への通知を担当します。アプリケーション層は、正しいタイムアウト値、およびタイムアウト・イベントをトリガーする時期を決定する必要があります。ハンドシェーク中にSSLEngine
オブジェクトがHandshakeStatus.NEED_UNWRAP
状態の場合、SSLEngine.wrap()の呼出しは、前のパケットが失われ、再送信する必要があることを意味します。そのような場合、SSLEngine
クラスのDTLS実装は、必要に応じて前の必要なハンドシェーク・メッセージをラップしなおす役割を担います。
注意:
DTLSエンジンでは、ハンドシェーク・メッセージのみを正しく交換する必要があります。アプリケーション・データは、タイマーを必要とせずにパケット損失に対応できます。SSLEngine.unwrap()
およびSSLEngine.wrap()
は、アプリケーションで再送信を処理するために一緒に使用できます。
図8-8では、DTLSハンドシェークの再送信を処理する一般的なシナリオを示します。
図8-8 DTLSハンドシェークの再送信の状態のフロー
データグラム・トランスポートでは、信頼できる順次のデータ配信は必要なく、提供されません。ハンドシェーク・メッセージは、失われることや、並替えが必要なことがあります。DTLS実装では、前のすべてのメッセージを受け取る前に、今後の処理のためにハンドシェーク・メッセージをバッファする必要があることがあります。
SSLEngine
のDTLS実装は、ハンドシェーク・メッセージを並べ替える役割を担います。ハンドシェーク・メッセージのバッファリングと並替えは、アプリケーションに対して透過的です。
ただし、アプリケーションは、HandshakeStatus.NEED_UNWRAP_AGAIN
ステータスを管理する必要があります。このステータスは、次のSSLEngine.unwrap()操作にはリモート側からのその他のデータが必要ないことを示します。
図8-9では、HandshakeStatus.NEED_UNWRAP_AGAIN
の使用の一般的なシナリオを示します。
図8-9 NEED_UNWRAP_AGAINでのDTLSバッファ済ハンドシェークの状態マシン
次の例では、DTLSのためのSSLEngine
オブジェクトの作成方法を示します。
注意:
サーバー名とポート番号は、サーバーとの通信には使用されません(すべてのトランスポートはアプリケーションが担当します)。それらは、DTLSセッション・キャッシングのためや、取得する必要があるサーバー・クレデンシャルを決定するためのKerberosベースの暗号化方式群の実装のために使用するJSSEプロバイダの手掛かりとなります。例8-5 キーストアとしてPKCS12を使用してDTLSのためのSSLEngineクライアントを作成するサンプル・コード
次のサンプル・コードでは、キーストアとしてPKCS12を使用するDTLSのためのSSLEngineクライアントを作成します。
import javax.net.ssl.*; import java.security.*; // Create and initialize the SSLContext with key material char[] passphrase = "passphrase".toCharArray(); // First initialize the key and trust material KeyStore ksKeys = KeyStore.getInstance("PKCS12"); ksKeys.load(new FileInputStream("testKeys"), passphrase); KeyStore ksTrust = KeyStore.getInstance("PKCS12"); ksTrust.load(new FileInputStream("testTrust"), passphrase); // KeyManagers decide which key material to use KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); kmf.init(ksKeys, passphrase); // TrustManagers decide whether to allow connections TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ksTrust); // Get an instance of SSLContext for DTLS protocols sslContext = SSLContext.getInstance("DTLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); // Create the engine SSLEngine engine = sslContext.createSSLengine(hostname, port); // Use engine as client engine.setUseClientMode(true);
例8-6 キーストアとしてPKCS12を使用してDTLSのためのSSLEngineサーバーを作成するサンプル・コード
次のサンプル・コードでは、キーストアとしてPKCS12を使用するDTLSのためのSSLEngineサーバーを作成します。import javax.net.ssl.*; import java.security.*; // Create and initialize the SSLContext with key material char[] passphrase = "passphrase".toCharArray(); // First initialize the key and trust material KeyStore ksKeys = KeyStore.getInstance("PKCS12"); ksKeys.load(new FileInputStream("testKeys"), passphrase); KeyStore ksTrust = KeyStore.getInstance("PKCS12"); ksTrust.load(new FileInputStream("testTrust"), passphrase); // KeyManagers decide which key material to use KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); kmf.init(ksKeys, passphrase); // TrustManagers decide whether to allow connections TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ksTrust); // Get an SSLContext for DTLS Protocol without authentication sslContext = SSLContext.getInstance("DTLS"); sslContext.init(null, null, null); // Create the engine SSLEngine engine = sslContext.createSSLeEngine(hostname, port); // Use the engine as server engine.setUseClientMode(false); // Require client authentication engine.setNeedClientAuth(true);
DTLSハンドシェークとSSL/TLSハンドシェークでは、データの生成と処理が同様に行われます。(SSL/TLSデータの生成と処理を参照。)それらは両方とも、それぞれSSLEngine.wrap()メソッドとSSLEngine.wrap()メソッドを使用してネットワーク・データを生成および使用します。
次の図では、一般的なDTLSハンドシェーク時の状態マシンと、対応するメッセージおよびステータスを示します。
SSL/TLSとDTLSのSSLEngine.wrap()メソッドの違い
SSLEngine.wrap()
メソッドは、SSL/TLSとは次のように異なります。 SSLEngine
のSSL/TLS実装では、SSLEngine.wrap()
の出力バッファに、1つ以上のTLSレコードが含まれます(TLSv1 BEAST暗号ブロック連鎖の脆弱性が原因)。
SSLEngine
のDTLS実装では、すべてのDTLSレコードをマーシャリングしデータグラム層に個別に配信できるようにSSLEngine.wrap()
の出力バッファに最大で1つのレコードが含まれます。
注意:
SSLEngine.wrap()
によって生成された各レコードは、SSLParameters.getMaximumPacketSize()
で指定された最大パケット・サイズ制限に従っている必要があります。SSLEngineのステータスは、SSLEngineResult.Status
によって表されます。
エンジンのステータスとアプリケーションの取る必要があるアクションを示すため、SSLEngine.wrap()
メソッドとSSLEngine.unwrap()
メソッドは、例8-2で示すようなSSLEngineResult
インスタンスを返します。このSSLEngineResult
オブジェクトには、エンジンの全体的なステータスとハンドシェークのステータスの2つのステータス情報が格納されます。
全体的なステータスは、SSLEngineResult.Status
enumによって表されます。次のステータスがあります。
OK
CLOSED
SSLEngine
が閉じられたか、操作はすでに閉じられていたために完了できませんでした。BUFFER_UNDERFLOW
BUFFER_OVERFLOW
例8-7は、SSLEngine.unwrap()
メソッドのBUFFER_UNDERFLOW
およびBUFFER_OVERFLOW
ステータスの処理方法を示しています。SSLSession.getApplicationBufferSize()
およびSSLSession.getPacketBufferSize()
を使用して、バイト・バッファのサイズを決定します。
ハンドシェークのステータスは、SSLEngineResult.HandshakeStatus
enumによって表されます。それらは、ハンドシェークが完了しているかどうか、発信側はピアから追加のハンドシェーク・データを取得する必要があるかどうか、またピアに追加のハンドシェーク・データを送信する必要があるかどうかなどを表します。使用可能なステータスは次のとおりです。
FINISHED
SSLEngine
がハンドシェークを完了したところです。NEED_TASK
SSLEngine
がハンドシェークを続行するには、事前に1つ(または複数の)委譲されたタスクの結果が必要です。NEED_UNWRAP
SSLEngine
がハンドシェークを続行するには、事前にリモート側からデータを受信する必要があります。NEED_UNWRAP_AGAIN
SSLEngine
は、ハンドシェークを続行するにはアンラップする必要があります。この値は、まだ解釈されていないデータをリモート側から以前に受け取っており再度受け取る必要がないことを示しています。このデータはJSSEフレームワークに受信されていますが、まだ処理されていません。NEED_WRAP
SSLEngine
がハンドシェークを続行するには、事前にリモート側にデータを送信する必要があるため、SSLEngine.wrap()を呼び出すようにしてください。NOT_HANDSHAKING
SSLEngine
は現在ハンドシェークしていません。結果ごとに2つのステータスがあることにより、SSLEngineは、アプリケーションがハンドシェークへの応答におけるアクションとwrap()
およびunwrap()
メソッドの全体のステータスを表すアクションという2つのアクションを取る必要があることを示すことができます。たとえばエンジンは、1回のSSLEngine.unwrap()
呼出しの結果として、SSLEngineResult.Status.OK
を返すことで、入力データが正常に処理されたことを示し、SSLEngineResult.HandshakeStatus.NEED_UNWRAP
を返すことで、ハンドシェークを継続するためにアプリケーションがピアからさらにSSL/TLS/DTLSエンコード・データを取得し、もう一度SSLEngine.unwrap()
に供給する必要があることを示します。お気付きのとおり、先の例はかなり単純化されていますが、これらすべてのステータスを適正に処理するにはコードをかなり拡張する必要があります。
例8-9と例8-8に、wrap()
メソッドおよびunwrap()
メソッドのハンドシェーク・ステータスと全体のステータスをチェックすることによってハンドシェーク・データを処理する方法を示します。
例8-7 BUFFER_UNDERFLOWおよびBUFFER_OVERFLOWの処理のサンプル・コード
次のコード例では、BUFFER_UNDERFLOWステータスとBUFFER_OVERFLOWステータスを処理する方法を示します。
SSLEngineResult res = engine.unwrap(peerNetData, peerAppData); switch (res.getStatus()) { case BUFFER_OVERFLOW: // Maybe need to enlarge the peer application data buffer. if (engine.getSession().getApplicationBufferSize() > peerAppData.capacity()) { // enlarge the peer application data buffer } else { // compact or clear the buffer } // retry the operation break; case BUFFER_UNDERFLOW: // Maybe need to enlarge the peer network packet buffer if (engine.getSession().getPacketBufferSize() > peerNetData.capacity()) { // enlarge the peer network packet buffer } else { // compact or clear the buffer } // obtain more inbound network data and then retry the operation break; // Handle other status: CLOSED, OK // ... }
例8-8 ハンドシェーク・ステータスと全体のステータスの確認と処理のサンプル・コード
次のコード例では、wrap()メソッドとunwrap()メソッドのハンドシェーク・ステータスと全体のステータスをチェックすることによって、ハンドシェーク・データを処理する方法を示します。
void doHandshake(SocketChannel socketChannel, SSLEngine engine, ByteBuffer myNetData, ByteBuffer peerNetData) throws Exception { // Create byte buffers to use for holding application data int appBufferSize = engine.getSession().getApplicationBufferSize(); ByteBuffer myAppData = ByteBuffer.allocate(appBufferSize); ByteBuffer peerAppData = ByteBuffer.allocate(appBufferSize); // Begin handshake engine.beginHandshake(); SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); // Process handshaking message while (hs != SSLEngineResult.HandshakeStatus.FINISHED && hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { switch (hs) { case NEED_UNWRAP: // Receive handshaking data from peer if (socketChannel.read(peerNetData) < 0) { // The channel has reached end-of-stream } // Process incoming handshaking data peerNetData.flip(); SSLEngineResult res = engine.unwrap(peerNetData, peerAppData); peerNetData.compact(); hs = res.getHandshakeStatus(); // Check status switch (res.getStatus()) { case OK : // Handle OK status break; // Handle other status: BUFFER_UNDERFLOW, BUFFER_OVERFLOW, CLOSED // ... } break; case NEED_WRAP : // Empty the local network packet buffer. myNetData.clear(); // Generate handshaking data res = engine.wrap(myAppData, myNetData); hs = res.getHandshakeStatus(); // Check status switch (res.getStatus()) { case OK : myNetData.flip(); // Send the handshaking data to peer while (myNetData.hasRemaining()) { socketChannel.write(myNetData); } break; // Handle other status: BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED // ... } break; case NEED_TASK : // Handle blocking tasks break; // Handle other status: // FINISHED or NOT_HANDSHAKING // ... } } // Processes after handshaking // ... }
例8-9 DTLSハンドシェーク・ステータスおよび全体的ステータスの処理のサンプル・コード
次のコード例では、DTLSハンドシェーク・ステータスを処理する方法を示します。
void handshake(SSLEngine engine, DatagramSocket socket, SocketAddress peerAddr) throws Exception { boolean endLoops = false; // private static int MAX_HANDSHAKE_LOOPS = 60; int loops = MAX_HANDSHAKE_LOOPS; engine.beginHandshake(); while (!endLoops && (serverException == null) && (clientException == null)) { if (--loops < 0) { throw new RuntimeException("Too many loops to produce handshake packets"); } SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP || hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) { ByteBuffer iNet; ByteBuffer iApp; if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) { // receive ClientHello request and other SSL/TLS/DTLS records byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); try { socket.receive(packet); } catch (SocketTimeoutException ste) { // retransmit the packet if timeout List <Datagrampacket> packets = onReceiveTimeout(engine, peerAddr); for (DatagramPacket p : packets) { socket.send(p); } continue; } iNet = ByteBuffer.wrap(buf, 0, packet.getLength()); iApp = ByteBuffer.allocate(1024); } else { iNet = ByteBuffer.allocate(0); iApp = ByteBuffer.allocate(1024); } SSLEngineResult r = engine.unwrap(iNet, iApp); SSLEngineResult.Status rs = r.getStatus(); hs = r.getHandshakeStatus(); if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) { // the client maximum fragment size config does not work? throw new Exception("Buffer overflow: " + "incorrect client maximum fragment size"); } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) { // bad packet, or the client maximum fragment size // config does not work? if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { throw new Exception("Buffer underflow: " + "incorrect client maximum fragment size"); } // otherwise, ignore this packet } else if (rs == SSLEngineResult.Status.CLOSED) { endLoops = true; } // otherwise, SSLEngineResult.Status.OK: if (rs != SSLEngineResult.Status.OK) { continue; } } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) { List <DatagramPacket> packets = // Call a function to produce handshake packets produceHandshakePackets(engine, peerAddr); for (DatagramPacket p : packets) { socket.send(p); } } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) { runDelegatedTasks(engine); } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { // OK, time to do application data exchange. endLoops = true; } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) { endLoops = true; } } SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { throw new Exception("Not ready for application data yet"); } }
ハンドシェーク時に、SSLEngine
はブロックしたり処理に長い時間がかかったりするタスクに直面することがあります。たとえば、TrustManager
は、リモート証明書検証サービスに接続する必要があることがあります。またはKeyManager
は、クライアント認証の一環として使用する証明書を決定するようにユーザーに求める必要があることがあります。SSLEngine
の非ブロック性を保持するため、エンジンはそれらのタスクに直面した場合にSSLEngineResult.HandshakeStatus.NEED_TASK
を返します。このステータスを受け取ると、アプリケーションはSSLEngine.getDelegatedTask()
を呼び出してタスクを取得し、その要件に適したスレッド・モデルを使用してタスクを処理すべきです。アプリケーションは、たとえばスレッド・プールからスレッドを入手してタスクを処理し、メイン・スレッドは他の入出力を処理できます。
次のコードは、新しく作成されたスレッドで各タスクを実行します。
if (res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { Runnable task; while ((task = engine.getDelegatedTask()) != null) { new Thread(task).start(); } }
SSLEngine
は、未処理のタスクすべてが完了するまで、将来のwrap()
およびunwrap()
呼出しをブロックします。
SSL/TLS/DTLS接続を正しい順序で停止するため、SSL/TLS/DTLSプロトコルではクローズ・メッセージを送信する必要があります。したがって、アプリケーションがSSL/TLS/DTLS接続を終了するときは、最初にSSLEngine
からクローズ・メッセージを取得し、そのトランスポート・メカニズムを使用してピアに送信して、最後にトランスポート・メカニズムを停止する必要があります。例8-10に、これを示します。
SSLEngine
を明示的に閉じるアプリケーションの他に、SSLEngine
は、ピアによって(ハンドシェーク・データを処理している間のクローズ・メッセージの受取りによる)、またはアプリケーション・データやハンドシェーク・データを処理している間に、SSLException
をスローすることによって示される、エラーが発生しているSSLEngine
によって閉じられることがあります。そのような場合、アプリケーションはSSLEngine.wrap()
を呼び出してクローズメッセージを取得し、SSLEngine.isOutboundDone()
がtrue
を返すまで(例8-10に示すように)、またはSSLEngineResult.getStatus()
がCLOSED
を返すまで、それをピアに送信する必要があります。
正常なシャットダウンに加え、クローズメッセージが交換される前にトランスポート・リンクを切断する非常シャットダウンもあります。前の例では、アプリケーションは非ブロックSocketChannel
からの読取りを実行しようとすると-1
またはIOException
を受け取り、非ブロックSocketChannel
への書込みを実行しようとするとIOException
を受け取ります。入力データの最後に達したら、engine.closeInbound()
を呼び出して、SSLEngine
によって、リモートのピアがSSL/TLS/DTLSの観点から正常に閉じたことを検証する必要があります。次に、アプリケーションが例8-10の手順を使用して正常な停止を試みる必要があります。SSLSocket
と異なり、SSLEngine
を使用するアプリケーションは、多くの状態移行、ステータスおよびプログラミングを処理する必要があります。SSLEngineの使用を表すサンプル・コードを参照してください。
例8-10 SSL/TLS/DTLS接続の停止のサンプル・コード
次のコード例では、SSL/TLS/DTLS接続の停止方法を示します。
// Indicate that application is done with engine engine.closeOutbound(); while (!engine.isOutboundDone()) { // Get close message SSLEngineResult res = engine.wrap(empty, myNetData); // Check res statuses // Send close message to peer while(myNetData.hasRemaining()) { int num = socketChannel.write(myNetData); if (num == 0) { // no bytes written; try again later } myNetData().compact(); } } // Close transport socketChannel.close();
javax.net.ssl.SSLSession
インタフェースは、SSLSocket
またはSSLEngine
接続の2つのピアの間でネゴシエーションされたセキュリティ・コンテキストを表します。一度確立されたセッションは、同じ2つのピアの間で接続されるそのあとのSSLSocket
またはSSLEngine
オブジェクトによっても共有できます。
場合によっては、ハンドシェーク中に取り決めたパラメータが、後のハンドシェークでトラストについて判断を下す際に必要になることもあります。たとえば、有効な署名アルゴリズムのリストによって、認証に使用できる証明書タイプが制限されることがあります。SSLSession
はハンドシェーク時に、SSLSocket
またはSSLEngine
のgetHandshakeSession()
を呼び出すことによって取得できます。TrustManager
またはKeyManager
の実装により、getHandshakeSession()
メソッドを使用して、それらが判断を下す際に役立つセッション・パラメータに関する情報を取得できます。
完全に初期化されたSSLSession
には暗号化方式群が含まれ、これは、リモート・ピアのネットワーク・アドレスに関する権限のないヒントと同様、セキュアなソケットの通信でも使用され、作成や最後の使用の時点などで、管理情報としても使用されます。セッションには、SSLSocket
またはSSLEngine
接続による通信を暗号化して整合性を保証する暗号鍵を作成するために使用される、ピア間でネゴシエーションされた共用マスターとなる秘密も含まれます。このマスターとなる秘密の値は、基盤となるセキュアなソケット実装のみに伝えられ、SSLSession
APIによって公開されません。
ExtendedSSLSession
は、追加のセッション属性をサポートするようにSSLSession
インタフェースを拡張します。ExtendedSSLSession
クラスは、ローカル実装およびピアによってサポートされる署名アルゴリズムを記述するメソッドを追加します。ExtendedSSLSession
インスタンスで呼び出されるgetRequestedServerNames()
メソッドは、要求されたServer Name Indication (SNI)拡張でSNIServerName
オブジェクトのリストを取得するために使用されます。サーバーは、要求されたサーバー名を使用して、適切な認証証明書の選択やセキュリティ・ポリシーのその他の側面をガイドする必要があります。クライアントは、要求されたサーバー名を使用して、ピアの識別情報のエンドポイントの識別やセキュリティ・ポリシーのその他の側面をガイドする必要があります。
SSLSession
でのgetPacketBufferSize()
およびgetApplicationBufferSize()
メソッドの呼出しは、SSLEngine
によって使用される適切なバッファ・サイズを決定するために使用します。
注意:
SSL/TLSプロトコルは、実装が最大16Kバイト(KB)のプレーン・テキストを含むパケットを生成することを指定します。ただし、一部の実装はこの指定に違反し、32Kバイトまでの大きいレコードを生成します。SSLEngine.unwrap()
コードが大きいインバウンド・パケットを検出した場合、SSLSession
から返されるバッファ・サイズは動的に更新されます。アプリケーションは常に、必要に応じてBUFFER_OVERFLOWおよびBUFFER_UNDERFLOWのステータスをチェックして、対応するバッファを拡大するべきです。SSLEngineの操作のステータスについてを参照してください。SunJSSE初値に標準に準拠した16Kバイトのレコードを送信し、32Kバイトの着信レコードを許可します。回避策については、「JSSEのカスタマイズ」のシステム・プロパティjsse.SSLEngine.acceptLargeFragments
を参照してください。javax.net.ssl.HttpsURLConnection
はjava.net.HttpURLConnection
クラスを拡張し、HTTPS固有の機能のサポートを追加します。
HTTPSプロトコルはHTTPプロトコルに似ていますが、データを要求または受信する前に、まずSSL/TLSソケットを利用してセキュアなチャネルを確立してピアの識別情報を検証します(符号化方式の選択とリモート・エンティティの検証を参照)。javax.net.ssl.HttpsURLConnection
はjava.net.HttpURLConnection
クラスを拡張し、HTTPS固有の機能のサポートを追加します。HTTPS URLの構築方法と使用方法の詳細は、java.net.URL、java.net.URLConnection、java.net.HttpURLConnectionおよびjavax.net.ssl.HttpsURLConnectionクラスを参照してください。
HttpsURLConnection
インスタンスを取得する際、URLConnection.connect()
メソッドを使用して実際にネットワーク接続を開始する前に、複数のHTTPおよびHTTPSパラメータを構成できます。これらについては、次を参照してください。
状況によっては、HttpsURLConnection
のインスタンスによって使用されるSSLSocketFactory
を指定した方がよい場合があります。たとえば、デフォルト実装ではサポートされないプロキシ・タイプを使用してトンネリングを行いたいと考える場合があります。新しいSSLSocketFactory
は、すでに必要なトンネリングの完了したソケットを返すことができます。このため、HttpsURLConnection
は追加のプロキシを使用できます。
HttpsURLConnection
クラスには、ロード時に割り当てられたデフォルトのSSLSocketFactory
があります(これはSSLSocketFactory.getDefault()
メソッドによって返されるファクトリです)。以降、HttpsURLConnection
のインスタンスは、staticメソッドHttpsURLConnection.setDefaultSSLSocketFactory
によってクラスに新しいデフォルトのSSLSocketFactory
が割り当てられるまで、現在のデフォルトのSSLSocketFactory
を継承します。HttpsURLConnection
のインスタンスが作成された後、setSSLSocketFactory()
メソッドへの呼出しにより、このインスタンス上の継承されたSSLSocketFactory
をオーバーライドできます。
注意:
デフォルトのstaticSSLSocketFactory
の変更は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つまたは複数の暗号化サービス・プロバイダ(プロバイダ)パッケージで提供されることがあります。JCAの設計の原則およびエンジン・クラスとアルゴリズムを参照してください。
JSSEに標準で付属するSunJSSEプロバイダは、SSLContext
、KeyManagerFactory
およびTrustManagerFactory
実装を提供し、標準のjava.security
APIではエンジン・クラスの実装も提供します。表8-6では、SunJSSEによって提供される実装をリストします。
表8-6 SunJSSEによって提供される実装
実装されるエンジン・クラス | アルゴリズムまたはプロトコル |
---|---|
KeyStore |
PKCS12 |
KeyManagerFactory |
PKIX、SunX509 |
TrustManagerFactory |
PKIX (X509またはSunPKIX)、SunX509 |
SSLContext |
SSLv3[1]、TLSv1、TLSv1.1、TLSv1.2、DTLSv1.0、DTLSv1.2 |
脚注1: JDK 8u31リリース以降、SSLv3プロトコル(Secure Socket Layer)は無効になっており、デフォルトでは使用できません。<java_home>/conf/security/java.security
ファイルのjava.security.Security
プロパティjdk.tls.disabledAlgorithms
を参照してください。SSLv3が絶対に必要な場合は、java.security
ファイルのjdk.tls.disabledAlgorithms
プロパティからSSLv3
を削除するか、JSSEが初期化される前にこのセキュリティ・プロパティを動的に設定すれば、プロトコルを再度アクティブにできます。デプロイ・レベルでSSLv3プロトコルを有効にするには、前の手順に従った後、deployment.security.SSLv3=true
という行をdeployment.properties
ファイルに追加します。
javax.net.ssl.SSLContext
クラスは、セキュアなソケット・プロトコルの実装のエンジン・クラスです。このクラスのインスタンスは、SSLSocket
、SSLServerSocket
およびSSLEngine
のファクトリの役目を果たします。SSLContext
オブジェクトは、そのコンテキストの下で作成されたすべてのオブジェクトで共有される状態情報をすべて保持します。たとえば、セッションの状態は、ソケット・ファクトリにより作成され、コンテキストにより提供されたソケットによってハンドシェーク・プロトコルが取り決められると、SSLContext
と関連付けられます。キャッシュに書き込まれたこれらのセッションは、同じコンテキストで作成された別のソケットで再利用したり共有することができます。
各インスタンスは、認証の実行に必要な鍵、証明書チェーン、および信頼されたルートCA証明書を使ってinit
メソッドで構成されます。この構成は、鍵とトラスト・マネージャの形で提供されます。これらのマネージャは認証をサポートし、コンテキストによってサポートされる暗号群の鍵合意を提供します。
現在は、X.509ベースのマネージャだけがサポートされています。
SSLContext
クラスは、SSLSocketFactory
クラスまたはSSLServerSocketFactory
クラスを作成するために使用されます。
SSLContext
を取得して初期化するには、次の2つの方法があります。
SSLSocketFactory
またはSSLServerSocketFactory
クラスでSSLContext.getDefault
staticメソッドを呼び出すことです。このメソッドは、デフォルトのKeyManager
、TrustManager
およびSecureRandom
(セキュアな乱数ジェネレータ)を使用してデフォルトのSSLContext
を作成します。デフォルトのKeyManagerFactory
およびTrustManagerFactory
を使用すると、KeyManager
およびTrustManager
がそれぞれ作成されます。使用する鍵データは、「デフォルトのキーストアとトラストストア、ストア・タイプ、およびストア・パスワードのカスタマイズ」で説明するシステム・プロパティで指定されるデフォルトのキーストアおよびトラストストアにあります。SSLContext
クラスでstaticメソッドSSLContext.getDefault
を呼び出してから、そのインスタンスの適切なinit()
メソッドを呼び出してコンテキストを初期化することです。init()
メソッドの1つのバリアントは、KeyManager
オブジェクトの配列、TrustManager
オブジェクトの配列、およびSecureRandom
オブジェクトの3つの引数を取ります。適切なインタフェースを実装するか、実装を生成するKeyManagerFactory
クラスとTrustManagerFactory
クラスを使用して、KeyManager
オブジェクトとTrustManager
オブジェクトが作成されます。その後、KeyManagerFactory
とTrustManagerFactory
を、TrustManagerFactory
またはKeyManagerFactory
クラスのinit()
メソッドへの引数として渡されたKeyStore
に含まれる鍵データでそれぞれ初期化できます。最後に、TrustManagerFactory
のgetTrustManagers()
メソッドとKeyManagerFactory
のgetKeyManagers()
メソッドを呼び出して、トラスト・データまたは鍵データの型ごとに1つずつトラスト・マネージャまたはキー・マネージャの配列を取得できます。SSL接続が確立されると、SSLSession
が作成され、これには設定した識別情報、使用する暗号化方式群などの様々な情報が含まれます。次にSSLSession
を使用して、2つのエンティティ間の進行中の関係と状態情報が記述されます。各SSL接続には、一度に1つのセッションが含まれますが、そのセッションがエンティティ間の接続に、同時に、または連続して何度も使用されることがあります。
他のJCAプロバイダ・ベースのエンジン・クラスと同様に、SSLContext
オブジェクトは、SSLContext
クラスのgetInstance()
ファクトリ・メソッドを使用して作成されます。このようなstaticメソッドは、最低限要求されたセキュアなソケット・プロトコルを実装するインスタンスを返します。返されるインスタンスはその他のプロトコルも実装できます。たとえば、getInstance("TLSv1")
はTLSv1、TLSv1.1およびTLSv1.2を実装するインスタンスを返すことができます。getSupportedProtocols()
メソッドは、このコンテキストからSSLSocket
、SSLServerSocket
またはSSLEngine
が作成されたときに、サポート対象のプロトコルのリストを返します。実際のSSL接続でどのプロトコルを有効にするかは、setEnabledProtocols(String[] protocols)
メソッドを使用して制御できます。
注意:
SSLSocketFactory.getDefault()
メソッドを呼び出すと、SSLContext
オブジェクトが自動的に作成され、初期化され、SSLSocketFactory
クラスに静的に割り当てられます。したがって、デフォルト動作をオーバーライドする場合を除き、SSLContextオブジェクトを直接作成したり初期化したりする必要はありません。getInstance()
ファクトリ・メソッドを呼び出してSSLContext
オブジェクトを作成するには、プロトコル名を指定する必要があります。または、要求されたプロトコルの実装を提供するプロバイダを次のように指定することもできます。
public static SSLContext getInstance(String protocol);
public static SSLContext getInstance(String protocol, String provider);
public static SSLContext getInstance(String protocol, Provider provider);
プロトコル名のみを指定すると、システムは、要求されたプロトコルの実装がその環境で利用できるかどうかを判断します。複数の実装がある場合、より望ましいものがあるかどうかを判断します。
プロトコル名とプロバイダの両方を指定すると、システムは、要求されたプロトコルの実装が要求されたプロバイダにあるかどうかを判断します。実装がない場合は、例外がスローされます。
プロトコルは、希望するセキュアなソケット・プロトコルを記述する文字列(TLS
など)です。SSLContext
オブジェクトの一般的なプロトコル名は、Javaセキュリティ標準アルゴリズム名仕様で定義されています。
SSLContext
は次のように取得できます。
SSLContext sc = SSLContext.getInstance("TLS");
新しく作成されたSSLContext
は、init
メソッドを呼び出すことによって初期化すべきです。
public void init(KeyManager[] km, TrustManager[] tm, SecureRandom random);
KeyManager[]
パラメータがnullの場合、このコンテキストには空のKeyManager
が定義されます。TrustManager[]
パラメータがnullの場合、インストールされたセキュリティ・プロバイダは、TrustManagerFactoryクラスの最も優先度の高い実装で検索され(TrustManagerFactoryクラスを参照)、そこから適切なTrustManager
が取得されます。同様に、SecureRandom
パラメータもNULLにできます。その場合、デフォルト実装が使用されます。
内部のデフォルト・コンテキストが使用される場合(SSLContext
はSSLSocketFactory.getDefault()
またはSSLServerSocketFactory.getDefault()
によって作成されるなど)、デフォルトのKeyManagerとTrustManagerが作成されます。また、デフォルトのSecureRandom
実装も選択されます。
TrustManager
は、提示された認証クレデンシャルの信頼性を判定します。信頼できないクレデンシャルの場合、接続は切断されます。セキュアなソケット・ピアのリモート識別情報を認証するには、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()
メソッドを呼び出します。不明な場合は、プロバイダのベンダーに問い合わせてください。
SunX509 TrustManagerFactory
など、SunJSSEプロバイダが提供するファクトリは多数ありますが、TrustManagerFactory
を初期化するために必要な情報はKeyStore
のみであるため、最初にinit
メソッドを呼び出すのが適切です。TrustManagerFactory
はKeyStore
に、認証チェック中に信頼すべきリモート証明書の情報を問い合わせます。
プロバイダでは、KeyStore
以外の初期化パラメータを必要とすることがあります。そのプロバイダのユーザーは、プロバイダによる定義に従って、適切なManagerFactoryParameters
の実装を渡すことが期待されます。そのあと、プロバイダはManagerFactoryParameters
実装の特定のメソッドを呼び出し、必要な情報を取得できます。
たとえば、TrustManagerFactory
プロバイダが、そのプロバイダを使用するアプリケーションからの初期化パラメータB、RおよびSを必要としているとします。KeyStore
以外の初期化パラメータを必要とするすべてのプロバイダと同様に、プロバイダはアプリケーションがManagerFactoryParameters
の特定のサブインタフェースを実装するクラスのインスタンスを提供することを必要とします。たとえば、プロバイダは呼出し側のアプリケーションがMyTrustManagerFactoryParams
のインスタンスを実装して作成し、2つ目のinit()
メソッドに渡すことを必要としているとします。次の例に、MyTrustManagerFactoryParams
がどのように見えるかを示します。
public interface MyTrustManagerFactoryParams extends ManagerFactoryParameters { public boolean getBValue(); public float getRValue(); public String getSValue(); }
一部のトラスト・マネージャでは、KeyStore
オブジェクトやその他のパラメータで明示的に初期化されなくても、信頼性を判定できます。たとえば、LDAP経由でローカル・ディレクトリ・サービスのトラスト・データにアクセスしたり、オンラインの証明書ステータスチェックサーバーをリモートで使用したり、または標準のローカル位置からデフォルトのトラスト・データにアクセスすることもできます。
デフォルトのトラスト・マネージャのアルゴリズムは「PKIX」です。これは、java.security
ファイルのssl.TrustManagerFactory.algorithm
プロパティを編集することによって変更できます。
PKIXトラスト・マネージャ・ファクトリは、インストールされたセキュリティ・プロバイダのCertPath PKIX実装(PKIプログラマーズ・ガイドの概要を参照)を使用します。トラスト・マネージャ・ファクトリを初期化するには、通常のinit(KeyStores)
メソッドを使用するか、CertPathTrustManagerParametersクラスを使用して、CertPathパラメータをPKIXトラスト・マネージャに渡します。
例8-11では、トラスト・マネージャを取得して特定のLDAP証明書ストアを使用する方法と、失効チェックを有効にする方法を示します。
TrustManagerFactory.init(KeyStore)
メソッドが使用される場合は、失効チェックが無効にされる点を除いて、デフォルトのPKIXパラメータが使用されます。有効にするには、com.sun.net.ssl.checkRevocation
システム・プロパティをtrue
に設定します。この設定では、CertPath実装自身が失効情報を見つけられる必要があります。プロバイダのPKIX実装では多くの場合にこの動作を実行できますが、システム・プロパティcom.sun.security.enableCRLDP
をtrue
に設定する必要があります。TrustManagerFactory.init(ManagerFactoryParameters)メソッドではデフォルトで失効チェックが有効になっているということに注意してください。
PKIXクラスおよびCertPathクラスを参照してください。
例8-11 LDAP証明書を使用して失効チェックを有効にするサンプル・コード
次の例に、トラスト・マネージャを取得して特定のLDAP証明書ストアを使用する方法と、失効チェックを有効にする方法を示します。
import javax.net.ssl.*; import java.security.cert.*; import java.security.KeyStore; import java.io.FileInputStream; ... // Obtain Keystore password char[] pass = System.console().readPassword("Password: "); // Create PKIX parameters KeyStore anchors = KeyStore.getInstance("JKS"); anchors.load(new FileInputStream(anchorsFile, pass)); PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(anchors, new X509CertSelector()); // Specify LDAP certificate store to use LDAPCertStoreParameters lcsp = new LDAPCertStoreParameters("ldap.imc.org", 389); pkixParams.addCertStore(CertStore.getInstance("LDAP", lcsp)); // Specify that revocation checking is to be enabled pkixParams.setRevocationEnabled(true); // Wrap PKIX parameters as trust manager parameters ManagerFactoryParameters trustParams = new CertPathTrustManagerParameters(pkixParams); // Create TrustManagerFactory for PKIX-compliant trust managers TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX"); // Pass parameters to factory to be passed to CertPath implementation factory.init(trustParams); // Use factory SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, factory.getTrustManagers(), null);
javax.net.ssl.X509TrustManager
インタフェースは、汎用のTrustManager
インタフェースを拡張したものです。X. 509ベースの認証を使用する場合、トラスト・マネージャによって実装される必要があります。
JSSEを使用したリモート・ソケット・ピアのX.509認証をサポートするには、このインタフェースのインスタンスをSSLContext
オブジェクトのinit
メソッドに渡す必要があります。
このインタフェースは、ユーザーが直接実装することも、SunJSSEプロバイダによって提供されているものなど、プロバイダ・ベースのTrustManagerFactory
から取得することもできます。また、独自のインタフェースを実装して、ファクトリで生成されたトラスト・マネージャに委譲することもできます。たとえば、結果の信頼性の判定をフィルタし、グラフィカル・ユーザー・インタフェースからエンド・ユーザーに問い合わせる場合に、これを実行できます。
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
システム・プロパティが指定されていない場合: /lib/security/jssecacerts
ファイルが存在すれば、そのファイルが使用されます。/lib/security/cacerts
ファイルが存在すれば、そのファイルが使用されます。java-homeが何を示すかについては、用語と定義を参照してください。
ファクトリは、cacerts
ファイルをチェックする前に、javax.net.ssl.trustStore
セキュリティ・プロパティによって指定されたファイルまたはjssecacerts
ファイルを検索します。そのため、コード署名の目的で、cacerts
内に存在するものとは別に、JSSE固有の一連の信頼されたルート証明書を提供できます。
指定したX509TrustManager
の動作が状況に適していない場合は、独自のTrustManagerFactory
を作成して登録するか、X509TrustManager
インタフェースを直接実装して、独自のX509TrustManager
を作成できます。
例8-12では、デフォルトのX509TrustManager
が失敗した場合に代替の認証ロジックを提供することによってデフォルトのSunJSSE X509TrustManager
の動作を拡張する、MyX509TrustManager
クラスを示します。
このようなトラスト・マネージャを作成できたら、次の例のように、init()
メソッドを使用して、これをSSLContext
に割り当てます。以降、このSSLContext
から作成されたSocketFactories
は、ユーザー独自のTrustManager
を使用して信頼性を判定するようになります。
TrustManager[] myTMs = new TrustManager[] { new MyX509TrustManager() }; SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, myTMs, null);
例8-12 X509TrustManagerの作成のサンプル・コード
次のコード例では、デフォルトのX509TrustManager
が失敗した場合に代替の認証ロジックを提供することによってデフォルトのSunJSSE X509TrustManager
の動作を拡張する、MyX509TrustManager
クラスを示します。
class MyX509TrustManager implements X509TrustManager { /* * The default PKIX X509TrustManager9. Decisions are delegated * to it, and a fall back to the logic in this class is performed * if the default X509TrustManager does not trust it. */ X509TrustManager pkixTrustManager; MyX509TrustManager() throws Exception { // create a "default" JSSE X509TrustManager. KeyStore ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream("trustedCerts"), "passphrase".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ks); TrustManager tms [] = tmf.getTrustManagers(); /* * Iterate over the returned trust managers, looking * for an instance of X509TrustManager. If found, * use that as the default trust manager. */ for (int i = 0; i < tms.length; i++) { if (tms[i] instanceof X509TrustManager) { pkixTrustManager = (X509TrustManager) tms[i]; return; } } /* * Find some other way to initialize, or else the * constructor fails. */ throw new Exception("Couldn't initialize"); } /* * Delegate to the default trust manager. */ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { pkixTrustManager.checkClientTrusted(chain, authType); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } /* * Delegate to the default trust manager. */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { pkixTrustManager.checkServerTrusted(chain, authType); } catch (CertificateException excep) { /* * Possibly pop up a dialog box asking whether to trust the * cert chain. */ } } /* * Merely pass this through. */ public X509Certificate[] getAcceptedIssuers() { return pkixTrustManager.getAcceptedIssuers(); } }
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
を作成する方法を示します。
例8-13では、PKIX TrustManagerFactory
を使用して、信頼性についての判定を下すために使用するデフォルトのX509ExtendedTrustManager
を見つけるクラスの作成方法を示します。
例8-13 PKIX TrustManagerFactoryの作成のサンプル・コード
次のコード例では、PKIX TrustManagerFactory
を使用して、信頼性についての判定を下すために使用するデフォルトのX509ExtendedTrustManager
を見つけるクラスの作成方法を示します。なんらかの理由でデフォルトのトラスト・マネージャに障害が発生した場合、サブクラスは他の動作を追加できます。このサンプルでは、これらの場所はcatch
節内のコメントによって示されています。
import java.io.*; import java.net.*; import java.security.*; import java.security.cert.*; import javax.net.ssl.*; public class MyX509ExtendedTrustManager extends X509ExtendedTrustManager { /* * The default PKIX X509ExtendedTrustManager. Decisions are * delegated to it, and a fall back to the logic in this class is * performed if the default X509ExtendedTrustManager does not * trust it. */ X509ExtendedTrustManager pkixTrustManager; MyX509ExtendedTrustManager() throws Exception { // create a "default" JSSE X509ExtendedTrustManager. KeyStore ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream("trustedCerts"), "passphrase".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ks); TrustManager tms [] = tmf.getTrustManagers(); /* * Iterate over the returned trust managers, looking * for an instance of X509ExtendedTrustManager. If found, * use that as the default trust manager. */ for (int i = 0; i < tms.length; i++) { if (tms[i] instanceof X509ExtendedTrustManager) { pkixTrustManager = (X509ExtendedTrustManager) tms[i]; return; } } /* * Find some other way to initialize, or else we have to fail the * constructor. */ throw new Exception("Couldn't initialize"); } /* * Delegate to the default trust manager. */ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { pkixTrustManager.checkClientTrusted(chain, authType); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } /* * Delegate to the default trust manager. */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { pkixTrustManager.checkServerTrusted(chain, authType); } catch (CertificateException excep) { /* * Possibly pop up a dialog box asking whether to trust the * cert chain. */ } } /* * Connection-sensitive verification. */ public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { try { pkixTrustManager.checkClientTrusted(chain, authType, socket); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { try { pkixTrustManager.checkClientTrusted(chain, authType, engine); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { try { pkixTrustManager.checkServerTrusted(chain, authType, socket); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { try { pkixTrustManager.checkServerTrusted(chain, authType, engine); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } /* * Merely pass this through. */ public X509Certificate[] getAcceptedIssuers() { return pkixTrustManager.getAcceptedIssuers(); } }
KeyManager
は、最終的にリモート・ホストに送信される認証クレデンシャルを選択します。自分自身(ローカルのセキュアなソケット・ピア)をリモート・ピアに対して認証させるには、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");
前の呼出しでSunJSSEプロバイダのデフォルトのキー・マネージャ・ファクトリのインスタンスが作成され、これは、基本となるX.509ベースの認証鍵を提供します。
新しく作成されたファクトリは、init
メソッドの1つを呼び出すことによって初期化してください。
public void init(KeyStore ks, char[] password); public void init(ManagerFactoryParameters spec);
使用するKeyManagerFactory
に適したinit
メソッドを呼び出します。不明な場合は、プロバイダのベンダーに問い合わせてください。
SunX509 KeyManagerFactory
など、SunJSSEプロバイダが提供するファクトリは多数ありますが、KeyManagerFactory
を初期化するために必要な情報はKeyStore
のみであるため、最初にinit
メソッドを呼び出すのが適切です。KeyManagerFactory
はKeyStore
に、リモートのソケット・ピアを認証するために使用すべき秘密鍵、および対応する公開鍵証明書について問い合わせます。パスワード・パラメータは、KeyStore
の鍵にアクセスするメソッドで使用するパスワードを指定します。KeyStore
の鍵はすべて、同じパスワードで保護する必要があります。
プロバイダでは、KeyStore
とパスワード以外の初期化パラメータを必要とすることがあります。そのプロバイダのユーザーは、プロバイダによる定義に従って、適切なManagerFactoryParameters
の実装を渡すことが期待されます。そのあと、プロバイダはManagerFactoryParameters
実装の特定のメソッドを呼び出し、必要な情報を取得できます。
一部のファクトリでは、KeyStore
オブジェクトやその他のパラメータで初期化されなくても、認証データにアクセスできます。たとえば、Java認証・承認サービス(JAAS)などのログイン・メカニズムの一部として鍵データにアクセスできる場合があります。
前に述べたように、SunJSSEプロバイダは、KeyStore
パラメータで初期化されている必要があるSunX509ファクトリをサポートします。
javax.net.ssl.X509KeyManager
インタフェースは、汎用のKeyManager
インタフェースを拡張したものです。X.509ベースの認証を行うキー・マネージャで実装します。JSSEを使用したリモート・ソケット・ピアのX.509認証をサポートするには、このインタフェースのインスタンスをSSLContext
オブジェクトのinit()
メソッドに渡す必要があります。
このインタフェースは、ユーザーが直接実装することも、SunJSSEプロバイダによって提供されているものなど、プロバイダ・ベースのKeyManagerFactory
から取得することもできます。また、独自のインタフェースを実装して、ファクトリで生成されたキー・マネージャに委譲することもできます。たとえば、結果の鍵をフィルタし、グラフィカル・ユーザー・インタフェースを使用してエンド・ユーザーに問い合わせる場合に、これを実行できます。
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()
メソッドを使用して取得できます。
独自のX509TrustManagerの作成に示す同様の方法で、独自のX509ExtendedKeyManager
サブクラスを作成できます。
サーバー側でのServer Name Indication (SNI)拡張のサポートにより、キー・マネージャはサーバー名をチェックし、それに従って適切な鍵を選択できます。たとえば、キーストアに証明書付きの鍵エントリが3つあるとします。
cn=www.example.com
cn=www.example.org
cn=www.example.net
ClientHelloメッセージで、SNI拡張のwww.example.net
に接続するように要求する場合、サーバーはサブジェクトcn=www.example.net
で証明書を選択できる必要があります。
二次サポート・クラスは、セキュアなソケットの作成、使用、および管理をサポートするJSSE APIの一部として提供されます。このクラスは、セキュアなソケット・アプリケーションでは、コア・クラスやサポート・クラスほどには使用されません。セカンダリ・サポート・クラスおよびインタフェースはjavax.net.ssl
およびjavax.security.cert
パッケージに含まれています。
SSLParameters
クラスは、SSL/TLS/DTLS接続に影響する次のパラメータをカプセル化します。
次のメソッドを使用して、SSLSocket
またはSSLEngine
の現在のSSLParameters
を取得できます。
SSLSocket
、SSLServerSocket
およびSSLEngine
内のgetSSLParameters()
SSLContext
内のgetDefaultSSLParameters()
およびgetSupportedSSLParamters()
SSLSocket
、SSLServerSocket
およびSSLEngine
のsetSSLParameters()
メソッドで、SSLParameters
を割り当てることができます。
SSLParameters.setServerNames()
メソッドによって、サーバー名の表示を明示的に設定できます。クライアント・モードでのサーバー名の表示はエンドポイントの識別にも影響します。X509ExtendedTrustManager
の実装で、それはExtendedSSLSession.getRequestedServerNames()
メソッドによって取得されたサーバー名の表示を使用します。例8-14を参照してください。
例8-14 Server Name Indicationを設定するサンプル・コード
この例では、サーバー名表示のホスト名(www.example.com
)を使用して、エンド・エンティティのX.509証明書に提示されるピアのIDに対してエンドポイントを識別します。
SSLSocketFactory factory = ... SSLSocket sslSocket = factory.createSocket("172.16.10.6", 443); // SSLEngine sslEngine = sslContext.createSSLEngine("172.16.10.6", 443); SNIHostName serverName = new SNIHostName("www.example.com"); List<SNIServerName> serverNames = new ArrayList<>(1); serverNames.add(serverName); SSLParameters params = sslSocket.getSSLParameters(); params.setServerNames(serverNames); sslSocket.setSSLParameters(params); // sslEngine.setSSLParameters(params);
javax.net.ssl.SSLSessionContext
インタフェースは、1つのエンティティに関連付けられているSSLSessionオブジェクトのグループです。たとえば、多数のセッションに同時に参加するサーバーやクライアントに関連付けることができます。このインタフェースのメソッドを使用すると、コンテキストの全セッションを列挙したり、セッションIDで特定のセッションを検索したりできます。
SSLSessionContext
は、オプションで、SSLSessionのgetSessionContext()
メソッドを呼び出してSSLSession
から取得することもできます。一部の環境では、コンテキストが使用できないことがあり、その場合、getSessionContext
メソッドはnullを返します。
javax.net.ssl.SSLSessionBindingListener
インタフェースは、SSLSessionからバインドまたはアンバインドされるときに通知を受けるオブジェクトによって実装されます。
javax.net.ssl.SSLSessionBindingEvent
クラスは、SSLSession (SSLSessionとExtendedSSLSessionを参照)からバインドまたはアンバインドされるときにSSLSessionBindingListener (SSLSessionBindingListenerインタフェースを参照)に伝えられるイベントを定義します。
javax.net.ssl.HandShakeCompletedListener
インタフェースは、指定のSSLSocket
接続時にSSLプロトコル・ハンドシェークの完了通知を受け取る任意のクラスに実装されるインタフェースです。
javax.net.ssl.HandShakeCompletedEvent
クラスは、指定のSSLSocket
接続のSSLプロトコル・ハンドシェークの完了時にHandShakeCompletedListener (HandShakeCompletedListenerインタフェースを参照)に伝えられるイベントを定義します。
SSL/TLS実装の標準ホスト名検証ロジックが失敗した場合、実装は、このインタフェースを実装し、このHttpsURLConnection
インスタンスに割り当てられたクラスのverify
メソッドを呼び出します。コールバック・クラスは、指定のパラメータでホスト名が受け付け可能であると判断できる場合、接続を許可すべきであると報告します。応答が受け付けられない場合、接続は切断されます。例8-15を参照してください。
HostnameVerifier
をHttpsURLConnection
に割り当てる方法の詳細は、HttpsURLConnection
を参照してください。
例8-15 HostnameVerifierインタフェース実装のサンプル・コード
次の例では、HostnameVerifier
インタフェースを実装するクラスを示します。
public class MyHostnameVerifier implements HostnameVerifier { public boolean verify(String hostname, SSLSession session) { // pop up an interactive dialog box // or insert additional matching logic if (good_address) { return true; } else { return false; } } } //...deleted... HttpsURLConnection urlc = (HttpsURLConnection) (new URL("https://www.example.com/")).openConnection(); urlc.setHostnameVerifier(new MyHostnameVerifier());
セキュアなソケット・プロトコルの多くは、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()
メソッドを使用します。
抽象SNIMatcher
クラスのインスタンスは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には、様々な実装をプラグインしたり、デフォルトのキーストアを指定したりして、カスタマイズ可能な標準実装が含まれます。
表8-7と表8-8では、カスタマイズが可能な側面、デフォルトの内容、およびカスタマイズを提供するために使用するメカニズムを要約しています。
一部の機能は、システム・プロパティやセキュリティ・プロパティの値を設定してカスタマイズできます。表に続くセクションでは、プロパティ値の設定方法について説明します。
注意:
この表に示すプロパティの多くは、現在JSSE実装で使用されていますが、それらの名前や型(システムまたはセキュリティ)が引き続き同じで、それらが将来のリリースにも存在するという保証はありません。それらのすべてのプロパティは「*」でフラグ付けされています。ここでは、JSSE実装で使用する場合の参考として、それらに言及しています。表8-7では、java.security.Security
プロパティの設定によってカスタマイズされる項目を示します。java.security.Securityプロパティの指定方法を参照してください。
表8-7 セキュリティ・プロパティとカスタマイズされる項目
セキュリティ・プロパティ | カスタマイズされる項目 | デフォルト値 | 注意 |
---|---|---|---|
cert.provider.x509v1 |
X509証明書実装のカスタマイズ | OracleからのX509Certificate実装 | なし |
jdk.tls.client.cipherSuites |
クライアント側の、デフォルトで有効な暗号化方式群。デフォルトで有効な暗号化方式群の指定を参照してください | 表4-11 SunJSSEでサポートされる暗号化方式群 | 注意: これらのシステム・プロパティを使用して弱い暗号化方式群を構成できる、つまり、構成された暗号化方式群は将来的に脆弱である可能性があります。このリスクを理解せずにこれらのシステム・プロパティを使用することはお薦めしません。 |
jdk.tls.server.cipherSuites |
サーバー側の、デフォルトで有効な暗号化方式群。デフォルトで有効な暗号化方式群の指定を参照してください | 表4-11 SunJSSEでサポートされる暗号化方式群 | 注意: これらのシステム・プロパティを使用して弱い暗号化方式群を構成できる、つまり、構成された暗号化方式群は将来的に脆弱である可能性があります。このリスクを理解せずにこれらのシステム・プロパティを使用することはお薦めしません。 |
security.provider.n |
暗号化サービス・プロバイダ。プロバイダ実装のカスタマイズと暗号化アルゴリズム・プロバイダのカスタマイズを参照してください。 | 優先度順の上位5つのプロバイダは、次のとおりです。
|
セキュリティ・プロパティ・ファイル内のsecurity.provider.n= の行でプロバイダを指定します。ここでは、n は1以上の整数値です。 |
*ssl.SocketFactory.provider |
デフォルトのSSLSocketFactory 実装 |
OracleからのSSLSocketFactory 実装 |
なし |
*ssl.ServerSocketFactory.provider |
デフォルトのSSLServerSocketFactory 実装 |
OracleからのSSLServerSocketFactory 実装 |
なし |
ssl.KeyManagerFactory.algorithm |
デフォルトのキー・マネージャ・ファクトリ・アルゴリズム名(デフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズを参照) | SunX509 | なし |
*jdk.certpath.disabledAlgorithms |
無効化された証明書検証暗号化アルゴリズム(無効化された制限付き暗号化アルゴリズムを参照) | MD2、MD5、SHA1 jdkCA & usage TLSServer、RSA keySize < 1024、DSA keySize < 1024、EC keySize < 224脚注4 |
なし |
ssl.TrustManagerFactory.algorithm |
デフォルトのトラスト・マネージャ・ファクトリ・アルゴリズム名(デフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズを参照) | PKIX | なし |
SunJSSEプロバイダが使用するJCE暗号化アルゴリズム | 代替のJCEアルゴリズム・プロバイダにSunJCEプロバイダより古い高い優先順位を与えます。 | SunJCE実装 | なし |
*jdk.tls.disabledAlgorithms |
無効化された制限付き暗号化アルゴリズム | SSLv3、RC4、MD5withRSA、DH keySize < 1024、EC keySize < 224脚注4 |
特定のアルゴリズム(プロトコル・バージョン、暗号化方式群、鍵交換メカニズムなど)を無効化し、アプリケーションで明示的に有効化されてもSSL/TLS/DTLS接続でネゴシエーションされないようにします。 |
jdk.tls.server.defaultDHEParameters |
Diffie-Hellmanグループ | OpenJDK SSL/TLS/DTLS実装での安全な主要Diffie-Hellmanグループ | Transport Layer Security (SSL/TLS/DTLS)処理のためにデフォルトの有限フィールドDiffie-Hellman ephemeral (DHE)パラメータを定義します。 |
脚注4 これらのセキュリティ・プロパティで指定されている制限付きアルゴリズムのリストは、変更される可能性があります。最新の値は、JDKインストールのjava.security
ファイルを参照してください。
* このシステム・プロパティは現在JSSE実装で使用されていますが、他の実装で調査され、使用されている保証はありません。他の実装で調査する場合は、その実装で、JSSE実装と同じ方法でそれを処理すべきです。プロパティが今後も存在すること、またはシステム型やセキュリティ型が将来のリリースでも変更されないことの保証はされません。
java.lang.System
プロパティの設定によってカスタマイズされる項目を示します。java.lang.Systemプロパティの指定方法を参照してください。表8-8 システム・プロパティとカスタマイズされる項目
システム・プロパティ | カスタマイズされる項目 | デフォルト | 注意 |
---|---|---|---|
java.protocol.handler.pkgs |
HTTPSプロトコルの代替実装の指定 | Oracleからの実装 | なし |
*javax.net.ssl.keyStore |
デフォルトのキーストア(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | なし |
値NONE を指定できます。この設定は、キーストアがファイルベースでない場合に適切です(ハードウェア・トークンに存在する場合など) |
*javax.net.ssl.keyStorePassword |
デフォルトのキーストア・パスワード(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | なし |
他のユーザーに見つかるような方法でパスワードを指定することはお薦めできません。 たとえば、コマンド行でのパスワードの指定です。パスワードのセキュリティを維持するには、アプリケーションでパスワードの入力を求めるか、適切に保護されたオプション・ファイルにパスワードを指定します |
*javax.net.ssl.keyStoreProvider |
デフォルトのキーストア・プロバイダ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | なし |
なし |
*javax.net.ssl.keyStoreType |
デフォルトのキーストア・タイプ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | KeyStore.getDefaultType() |
なし |
*javax.net.ssl.trustStore |
デフォルトのトラストストア(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | 存在する場合は、jssecacerts 。 そうでない場合、 |
なし |
*javax.net.ssl.trustStorePassword |
デフォルトのトラストストア・パスワード(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | なし |
他のユーザーに見つかるような方法でパスワードを指定することはお薦めできません。 たとえば、コマンド行でのパスワードの指定です。パスワードのセキュリティを維持するには、アプリケーションでパスワードの入力を求めるか、適切に保護されたオプション・ファイルにパスワードを指定します |
*javax.net.ssl.trustStoreProvider |
デフォルトのトラストストア・プロバイダ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | なし |
なし |
*javax.net.ssl.trustStoreType |
デフォルトのトラストストア・タイプ(デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズを参照) | KeyStore.getDefaultType() |
値NONE を指定できます。この設定は、トラストストアがファイルベースでない場合に適切です(ハードウェア・トークンに存在する場合など) |
*https.proxyHost |
デフォルトのプロキシ・ホスト | なし |
なし |
*https.proxyPort |
デフォルトのプロキシ・ポート | 80 | なし |
*jsse.enableSNIExtension |
Server Name Indicationオプション | true |
Server Name Indication (SNI)はTLS拡張で、RFC 6066で定義されています。これは仮想サーバーへのTLS接続を可能にし、さまざまなネットワーク名に対して複数のサーバーが単一の基本ネットワーク・アドレスでホスティングされます。かなり古い一部のSSL/TLSベンダーは、SSL/TLS拡張を処理できないことがあります。この場合、このプロパティをfalse に設定してSNI拡張を無効化します |
*https.cipherSuites |
デフォルトの暗号化方式群 | ソケット・ファクトリによって決定 |
|
*https.protocols |
デフォルトのハンドシェーク・プロトコル | ソケット・ファクトリによって決定 |
|
* HTTPS URL内のport フィールドによってカスタマイズします。 |
デフォルトのHTTPSポート | 443 | なし |
*jsse.SSLEngine.acceptLargeFragments |
大きいSSL/TLSパケット用のバッファのデフォルトのサイズ設定 | なし |
このシステム・プロパティを |
*sun.security.ssl.allowUnsafeRenegotiation |
安全ではないSSL/TLS再ネゴシエーションを許可します(フェーズ2修正の説明を参照) | false |
このシステム・プロパティを このシステム・プロパティは推奨されておらず、将来のJDKリリースで削除される可能性があります。 |
*sun.security.ssl.allowLegacyHelloMessages |
旧式のHelloメッセージを許可します(フェーズ2修正の説明を参照) | true |
このシステム・プロパティをtrueに設定すると、適切なRFC 5746メッセージを必要とすることなくピアがハンドシェークを実行できます。 このシステム・プロパティは推奨されておらず、将来のJDKリリースで削除される可能性があります。 |
jdk.tls.client.protocols |
SunJSSEプロバイダ | なし |
クライアント上で特定の 次に例を示します。
|
jdk.tls.ephemeralDHKeySize |
エフェメラルDiffie-Hellman鍵のサイズのカスタマイズ | 1024ビット | なし |
jsse.enableMFLNExtension |
最大断片長ネゴシエーション(MFLN)拡張のカスタマイズ | false | なし |
* このシステム・プロパティは現在JSSE実装で使用されていますが、他の実装で調査され、使用されている保証はありません。他の実装で調査する場合は、その実装で、JSSE実装と同じ方法でそれを処理すべきです。プロパティが今後も存在すること、またはシステム型やセキュリティ型が将来のリリースでも変更されないことの保証はされません。
X509Certificate.getInstance()
メソッドで返されたX509証明書実装は、デフォルトでJSSE実装の実装です。
cert.provider.x509v1
という名前のjava.security.Securityプロパティの指定方法の値として指定します。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プロバイダのデフォルトで指定された設定がオーバーライドされます。
アプリケーションによる明示的な設定
次のいずれかのメソッドを使用して、どの暗号化方式群が有効であるかを設定できます。
https.cipherSuites
システム・プロパティシステム・プロパティによる指定
システム・プロパティjdk.tls.client.cipherSuites
では、クライアント側でのデフォルトで有効な暗号化方式群を指定します。jdk.tls.server.cipherSuites
ではサーバー側のものを指定します。
これら2つのシステム・プロパティの値の構文は、サポートされている暗号化方式群の名前をカンマで区切ったリストです。これらのプロパティで指定された暗号化方式群名のうち、認識されていないまたはサポートされていないものは無視されます。JSSE暗号化方式群の標準名は、Javaセキュリティ標準アルゴリズムを参照してください。
注意:
これらのシステム・プロパティは、現在、Oracle JDKおよびOpenJDKでサポートされています。これらが他のJDK実装によってサポートされることは保証されていません。注意:
これらのシステム・プロパティを使用することで、弱い暗号化方式群が構成される、つまり、構成された暗号化方式群は将来的に脆弱になる可能性があります。このリスクを理解せずにこれらのシステム・プロパティを使用することはお薦めしません。JSSEプロバイダのデフォルトによる指定
各JSSEプロバイダは、デフォルトで有効な暗号化方式群を独自に設定しています。SunJSSEプロバイダでサポートされている暗号化方式群名、およびデフォルトで有効になっている暗号化方式群名は、JDKプロバイダ・ドキュメントのSunJSSEプロバイダを参照してください。
java.net.URL
クラスにHTTPS URLスキームを使用することで、SSL対応のWebサーバーでセキュアに通信できます。JDKは、デフォルトのHTTPS URL実装を提供します。
別のHTTPSプロトコル実装を使用する必要がある場合は、新しいクラス名を含めるようにjava.protocol.handler.pkgs
のjava.lang.Systemプロパティの指定方法を設定します。その結果、JDKのデフォルト・クラスより前に、指定したクラスが検索され、ロードされます。詳細は、URLクラスを参照してください。
注意:
過去のJSSEリリースでは、JSSEのインストール中にjava.protocol.handler.pkgs
システム・プロパティを設定する必要がありました。このステップは、com.sun.net.ssl.HttpsURLConnection
のインスタンスを取得する場合以外は不要になりました。JDKには、SunJSSEというJSSE暗号化サービス・プロバイダ(略称はプロバイダ)が付属しています。基本的に、プロバイダは特定の暗号化アルゴリズムのエンジン・クラスを実装するパッケージです。
JSSEのエンジン・クラスはSSLContext
、KeyManagerFactory
、およびTrustManagerFactory
です。プロバイダとエンジン・クラスの詳細は、Java暗号化アーキテクチャ(JCA)リファレンス・ガイドを参照してください。
使用する前に、プロバイダを静的または動的に登録する必要があります。SunJSSEプロバイダは登録済なので、登録する必要はありません。ほかのプロバイダを使用する場合は、後述のセクションでプロバイダの登録方法を確認してください。
セキュリティ・プロパティ・ファイル<java-home>/conf/security/java.security
に次のような1行を追加することで、プロバイダを静的に登録します。
security.provider.n=provName|className
これはプロバイダを宣言し、その優先順位n
を指定します。優先順位とは、特定プロバイダの指定がないときに、要求されたアルゴリズムについてプロバイダを検索する順序です。順序は1が基準になり、1が最も優先されます。その後、2、3、...と続きます。
provName
はプロバイダの名前であり、className
はプロバイダの完全修飾クラス名です。
標準のセキュリティ・プロバイダは、java.security
セキュリティ・プロパティ・ファイルに自動的に登録されます。
他のJSSEプロバイダを使用する場合は、行を追加して他のプロバイダを登録し、目的の優先順位を指定します。
複数のJSSEプロバイダを同時に登録できます。登録されたプロバイダには、様々なエンジン・クラスの、様々なアルゴリズムの様々な実装が含まれる場合があり、同じ型のアルゴリズムおよびエンジン・クラスの一部または全部をサポートする場合もあります。特定のアルゴリズムの特定のエンジン・クラス実装を検索するとき、その検索に特定のプロバイダが指定されていない場合、プロバイダは優先順位で検索され、指定したアルゴリズムの実装を提供する最初のプロバイダの実装が使用されます。
プロバイダの実装および統合までのステップでステップ8.1: プロバイダの構成を参照してください。
プロバイダを静的に登録するかわりに、SecurityクラスのaddProviderまたはinsertProviderAtメソッドを呼び出すことで実行時に動的にプロバイダを追加できます。こうした登録は持続的なものではなく、実行できるのはinsertProvider.<provider name>
権限を付与されたコードのみです。
一部のプロバイダは、構成が必要な場合があります。これは、Security
クラスのaddProvider
メソッドを呼び出す前に、Provider
クラスのconfigure
メソッドを使用して行われます。一例として、SunPKCS11の構成を参照してください。Provider.configure()
メソッドは、Java SE 9の新機能です。
jdk.security.provider.preferred
セキュリティ・プロパティで特定のアルゴリズムのための推奨プロバイダを指定します。推奨プロバイダを指定することで、特定のアルゴリズムのパフォーマンスを向上させるプロバイダを構成できますが、それらは、他のアルゴリズムにとっては最善のプロバイダではありません。security.provider.n
プロパティを使用して指定された順序付きプロバイダ・リストは、特定のアルゴリズムのパフォーマンスを向上させるが他のアルゴリズムにとっては最善ではないプロバイダを順序付けるには十分ではありません。パフォーマンスを向上させるには、プロバイダ・リストの順序付けの構成をより柔軟に行う必要があります。
jdk.security.provider.preferred
セキュリティ・プロパティでは、登録済プロバイダのリストにアクセスする前に、特定のアルゴリズム、またはサービスの型を一連の推奨プロバイダから選択できます。java.security.Securityプロパティの指定方法を参照してください。
jdk.security.provider.preferred
セキュリティ・プロパティでは、プロバイダは登録されません。順序付きプロバイダ・リストは、security.provider.n
プロパティを使用して暗号化プロバイダを静的に登録している必要があります。登録されていないプロバイダは無視されます。
1つのアルゴリズムのための推奨プロバイダの指定
jdk.security.provider.preferred
セキュリティ・プロパティで推奨プロバイダ文字列を指定するための構文は、ServiceType.Algorithm:Provider
のカンマ区切りのリストです。
この構文の説明は次のとおりです。
サービスの型の名前。(例: "MessageDigest"
)ServiceTypeはオプションです。指定しない場合は、そのアルゴリズムがすべてのサービス型に適用されます。
標準アルゴリズム名。Javaセキュリティ標準アルゴリズム名仕様を参照してください。アルゴリズムは、標準名全体(AES/CBC/PKCS5Padding)または一部分(AES、AES/CBC、AES//PKCS5Padding)として指定できます。
プロバイダの名前。登録済リストに示されていないプロバイダは無視されます。JDKプロバイダを参照してください。
java -Djava.security.debug=jca
を使用します。推奨プロバイダとFIPS
FIPSプロバイダをsecurity.provider.n
プロパティに追加し、jdk.security.provider.preferred
プロパティで推奨プロバイダの順序付けを指定する場合は、jdk.security.provider.preferred
で指定された推奨プロバイダが最初に選択されます。
したがって、FIPSプロバイダ構成のjdk.security.provider.preferred
プロパティは構成しないようにすることをお薦めします。
jdk.security.provider.preferredのデフォルト値
jdk.security.provider.preferred
プロパティは、デフォルトでは設定されておらず、アプリケーションのパフォーマンス・チューニングのためにのみ使用されます。
SSLSocketFactory.getDefault
またはSSLServerSocketFactory.getDefault
を呼び出すことでデフォルトのSSLSocketFactory
またはSSLServerSocketFactory
が作成され、このデフォルトのSSLSocketFactory
(またはSSLServerSocketFactory
)がJSSEリファレンス実装に由来するものであれば、デフォルトのSSLContext
は必ずソケット・ファクトリに関連付けられます。デフォルトのソケット・ファクトリは、JSSE実装に由来します。
デフォルトのSSLContext
は、デフォルトのKeyManager
およびデフォルトのTrustManager
で初期化されます。javax.net.ssl.keyStore
システム・プロパティおよび適切なjavax.net.ssl.keyStorePassword
システム・プロパティでキーストアを指定すると(java.lang.Systemプロパティの指定方法を参照)、デフォルトのSSLContext
で作成したKeyManager
は、指定したキーストアを管理するKeyManager
実装になります。(実際にはデフォルトのキー・マネージャおよびトラスト・マネージャのカスタマイズで説明したとおりに実装されます。)システム・プロパティが指定されない場合は、KeyManager
が管理するキーストアは新しい空のキーストアです。
一般に、ハンドシェークでサーバーとして動作するピアには、クライアントへの認証の資格を取得するため、KeyManagerのキーストアが必要です。ただし、匿名の暗号化方式群のいずれかを選択する場合、サーバーのKeyManager
キーストアは必要ありません。また、サーバーがクライアント認証を要求しないかぎり、クライアントとして動作するピアには、KeyManager
キーストアは必要ありません。したがって、このような状況では、javax.net.ssl.keyStore
のシステム・プロパティ値が定義されていなくてもかまわない場合があります。
同様に、トラスト・ストアをjavax.net.ssl.trustStore
システム・プロパティで指定すると、デフォルトのSSLContext
で作成したTrustManager
が、指定したトラスト・ストアを管理するTrustManager
実装になります。この場合、そのようなプロパティが存在しても指定するファイルが存在しなければ、トラストストアは使用されません。javax.net.ssl.trustStore
プロパティが存在しない場合は、デフォルトのトラスト・ストアを検索します。java-home/lib/security/jssecacerts
という名前のトラストストアが見つかった場合、それが使用されます。そうでない場合、java-home/lib/security/cacerts
というトラストストアが検索され、それが存在していれば使用されます。トラスト・ストアが見つからない場合、TrustManager
は新しい空のトラスト・ストアを管理します。
注意:
JDKには、java-home/lib/security/cacerts
ファイルで、限定された数の信頼されたルート証明書が付属しています。Java Platform, Standard Editionツール・リファレンスのkeytoolに記載したとおり、このファイルをトラストストアとして使用する場合は、ユーザーにこのファイルに含まれる証明書を管理(追加または削除)する責任があります。
接続先のサーバーの証明書構成によっては、ルート証明書を追加する必要がある場合もあります。適切なベンダーから必要な特定のルート証明書を入手してください。
javax.net.ssl.keyStoreType
またはjavax.net.ssl.keyStorePassword
システム・プロパティも指定されている場合、それぞれがデフォルトのKeyManager
キーストア・タイプとパスワードとして扱われます。タイプが指定されていない場合、デフォルトのタイプはKeyStore.getDefaultType()
メソッドが返すもので、keystore.type
セキュリティ・プロパティの値か、または、そうしたセキュリティ・プロパティが指定されていない場合はjksになります。キーストアのパスワードが指定されていない場合は空白の文字列「」とみなされます。
同様に、javax.net.ssl.trustStoreType
またはjavax.net.ssl.trustStorePassword
システム・プロパティも指定されている場合、それぞれがデフォルトのトラストストア・タイプとパスワードとして扱われます。タイプが指定されていない場合、デフォルトのタイプは、KeyStore.getDefaultType()
メソッドによって返されるタイプです。トラストストアのパスワードが指定されていない場合は空白の文字列「」とみなされます。
注意:
この項では、現在のJSSEリファレンス実装の動作を説明します。このセクションで説明するシステム・プロパティの名前と型(システムまたはセキュリティ)が引き続き同じで、将来のリリースにも存在するという保証はありせん。また、他のJSSE実装での検証や使用も保証されていません。実装で検証された場合、ここで説明するように、実装では、JSSEリファレンス実装と同じ方法でそれらを処理してください。デフォルトのキーストアとトラストストア、ストア・タイプおよびストア・パスワードのカスタマイズで説明したように、デフォルトのSSLSocketFactory
またはSSLServerSocketFactory
が作成され、このデフォルトのSSLSocketFactory
(またはSSLServerSocketFactory
)がJSSEリファレンス実装に由来する場合は、デフォルトのSSLContext
は常にソケット・ファクトリに関連付けられます。
デフォルトのSSLContext
は、KeyManager
およびTrustManager
で初期化されます。デフォルトのSSLContext
に提供されたKeyManager
とTrustManager
の両方またはどちらか一方は、前のセクションで説明したように、指定したキーストアまたはトラストストアを管理するための実装になります。
選択されるKeyManager
実装は、まずssl.KeyManagerFactory.algorithm
セキュリティ・プロパティを調査することによって決定されます。そのようなプロパティ値が指定されていると、指定したアルゴリズムのKeyManagerFactory
実装が検索されます。実装を提供する最初のプロバイダの実装が使用されます。そのgetKeyManagers()
メソッドが呼び出され、デフォルトのSSLContext
に提供するKeyManager
が決定されます。技術的には、getKeyManagers()
は、鍵データの型ごとに1つずつのKeyManager
で、KeyManager
オブジェクトの配列を返します。そのようなセキュリティ・プロパティ値が指定されていない場合、SunX509のデフォルト値を使用して検索を実行します。
注意:
SunX509アルゴリズムのKeyManagerFactory
実装はSunJSSEプロバイダによって提供されます。それが指定するKeyManager
はjavax.net.ssl.X509KeyManager
実装です。同様に、選択されるTrustManager
実装は、まずssl.TrustManagerFactory.algorithm
セキュリティ・プロパティを調査することによって決定されます。そのようなプロパティ値が指定されていると、指定したアルゴリズムのTrustManagerFactory
実装が検索されます。実装を提供する最初のプロバイダの実装が使用されます。そのgetTrustManagers
メソッドが呼び出され、デフォルトのSSLContext
に提供するTrustManager
が決定されます。技術的には、getTrustManagers()
は、トラスト・データの型ごとに1つずつのTrustManager
で、TrustManager
オブジェクトの配列を返します。そのようなセキュリティ・プロパティ値が指定されていない場合、PKIXのデフォルト値を使用して検索を実行します。
注意:
PKIXアルゴリズムのTrustManagerFactory
実装はSunJSSEプロバイダによって提供されます。それが指定するTrustManager
はjavax.net.ssl.X509TrustManager
実装です。注意:
この項では、現在のJSSEリファレンス実装の動作を説明します。このセクションで説明するシステム・プロパティの名前と型(システムまたはセキュリティ)が引き続き同じで、将来のリリースにも存在するという保証はありせん。また、他のJSSE実装での検証や使用も保証されていません。実装で検証された場合、ここで説明するように、実装では、JSSEリファレンス実装と同じ方法でそれらを処理してください。一部の環境では、SSL/TLS/DTLSの使用時は、特定のアルゴリズムまたは鍵の長さが不適切である場合があります。Oracle JDKでは、jdk.certpath.disabledAlgorithms
およびjdk.tls.disabledAlgorithm
セキュリティ・プロパティを使用して、バージョンのネゴシエーション、暗号化方式群の選択、ピア認証および鍵交換メカニズムなど、SSL/TLS/DTLSプロトコルのネゴシエーション中にアルゴリズムを無効にします。これらのセキュリティ・プロパティが他のJDK実装によって使用される保証はないことに注意してください。これらのセキュリティ・プロパティの構文とその現在アクティブな値については、<java-home>/conf/security/java.security
ファイルを参照してください。
jdk.certpath.disabledAlgorithms
プロパティ: CertPathコードでは、jdk.certpath.disabledAlgorithms
セキュリティ・プロパティを使用して、CertPathチェック中に許可しないようにする必要があるアルゴリズムが決定されます。たとえば、TLSサーバーによって識別証明書チェーンが送信される場合、受け取ったチェーンをCertPath実装を使用して検証するクライアントTrustManagerでは、示された条件は許可されません。たとえば、次の行は、cacaerts
キーストアにプレインストールされている信頼できるアンカーに連鎖するSHA1 TLSServer証明書と同様に、MD2ベースの証明書をブロックします。さらに、この行は、1024ビット未満のRSA鍵をブロックします。
jdk.certpath.disabledAlgorithms=MD2, SHA1 jdkCA & usage TLSServer, RSA keySize < 1024
jdk.tls.disabledAlgorithms
プロパティ: SunJSSEコードでは、jdk.tls.disabledAlgorithms
セキュリティ・プロパティを使用して、SSL/TLS/DTLSプロトコル、暗号化方式群、鍵などが無効にされます。この構文は、jdk.certpath.disabledAlgorithms
セキュリティ・プロパティに似ています。たとえば、次の行は、SSLv3アルゴリズムおよびすべてのTLS_*_RC4_*暗号化方式群を無効にします。
jdk.tls.disabledAlgorithms=SSLv3, RC4
特定の条件が必要な場合は、java.security
ファイル内のセキュリティ・プロパティで関連する値を削除するか、JSSEの初期化前に正しいセキュリティ・プロパティを動的に設定することで、それを再アクティブ化できます。
これらのセキュリティ・プロパティでは暗号化方式群の3番目のセットDisabledが事実上作成されることに注意してください。次のリストで、これら3つのセットを説明します。
Disabled: 暗号化方式群に無効リスト内のコンポーネント(RC4など)が含まれている場合(たとえば、RC4がjdk.tls.disabledAlgorithms
セキュリティ・プロパティで指定されている)、その暗号化方式群は無効になり、接続ハンドシェークに考慮されません。
Enabled: 接続に考慮される特定の暗号化方式群のリストです。
Not Enabled: 接続に考慮されない、無効でない暗号化方式群のリストです。これらの暗号化方式群を再度有効にするには、適切なsetEnabledCipherSuites()またはsetSSLParameters()メソッドを呼び出します。
SunJSSEプロバイダは、その暗号化のすべてニーズに対してSunJCE実装を使用します。プロバイダは通常の位置に置くことが推奨されていますが、SunJCEプロバイダより前に登録することにより、他のJCAまたはJCEプロバイダからの実装を使用できます。
セキュリティ・プロパティ・ファイル<java-home>/conf/security/java.security
を介して静的に、またはjava.security.Security
クラスのaddProvider()
またはinsertProviderAt()
メソッドを介して動的に、標準JCAメカニズム(プロバイダ実装の要求および獲得方法を参照)を使用してプロバイダを構成できます。
SSL/TLS/DTLS接続では、ハンドシェーク中に内部的にephemeral Diffie-Hellman (DH)鍵が使用される場合があります。SunJSSEプロバイダでは、SSL/TLS/DTLSハンドシェーク中にephemeral DH鍵サイズの強度を柔軟にカスタマイズできます。
1024ビット未満のサイズのDiffie-Hellman (DH)鍵は強度が不十分なため、非推奨になりました。システム・プロパティjdk.tls.ephemeralDHKeySize
によって、ephemeral DH鍵のサイズをカスタマイズできます。このシステム・プロパティは、エクスポート可能な暗号化方式群のServerKeyExchange
メッセージ内のDH鍵のサイズには影響しません。これは、JSSE OracleプロバイダのDHE_RSA、DHE_DSSおよびDH_anonベースの暗号化方式群にのみ影響します。
このプロパティには次のいずれかの値を指定できます。
legacy
: JSSE Oracleプロバイダは、JDK 7以前のリリースのレガシーの動作を保持しています(512ビットおよび768ビットのサイズのephemeral DH鍵の使用など)。matched
: エクスポート不可能な匿名の暗号化方式群の場合、ServerKeyExchangeメッセージ内のDH鍵のサイズは1024ビットです。X.509証明書ベースの認証(エクスポート不可能な暗号化方式群の)の場合、サイズが1024ビットから2048ビットでなければならないことを除けば、対応する認証鍵に一致するDH鍵のサイズが使われます。たとえば、認証証明書の公開鍵サイズが2048ビットの場合、暗号化方式群がエクスポート可能でないかぎり、ephemeral DH鍵サイズは2048ビットにすべきです。この鍵サイズ設定スキームにより、認証鍵と鍵交換鍵間の暗号化強度の整合性を確保しています。次の表に、システム・プロパティjdk.tls.ephemeralDHKeySize
の可能性のある各値について、DH鍵の最小および最大許容サイズをまとめています。
表8-9 システム・プロパティjdk.tls.ephemeralDHKeySize
のDH鍵サイズ
jdk.tls.ephemeralDHKeySizeの値 | 未定義 | レガシー | 一致 | 整数値(固定) |
---|---|---|---|---|
エクスポート可能なDH鍵サイズ | 512 | 512 | 512 | 512 |
エクスポート不可能な匿名暗号化方式群 | 1024 | 768 | 1024 | 固定鍵サイズは有効な整数プロパティ値で指定し、1024以上2048以下である必要があります。 |
認証証明書 | 1024 | 768 | 鍵サイズは認証証明書と同じですが、1024ビット以上2048ビット以下である必要があります。ただし、1024ビットより大きい、SunJCEプロバイダでサポートされている唯一のDH鍵サイズは、2048ビットです。 結果として、使用できる値は1024または2048のみになります。 |
固定鍵サイズは有効な整数プロパティ値で指定し、1024以上2048以下である必要があります。 |
より小さい最大断片長をネゴシエーションするために、クライアントは、ClientHelloメッセージにmax_fragment_length
タイプの拡張情報を含めることを選択できます。システム・プロパティjsse.enableMFLNExtension
を使用して、SSL/TLS/DTLS用のMFLN拡張を有効または無効にできます。
最大断片長ネゴシエーション
制約があるSSL/TLS/DTLSクライアントでは、メモリー制限や帯域幅制限が原因で、より小さい最大断片長をネゴシエーションしたほうが望ましい場合があります。より小さい最大断片長をネゴシエーションするために、クライアントは、(拡張された) ClientHelloメッセージにmax_fragment_length
タイプの拡張情報を含めることを選択できます。RFC 6066を参照してください。
最大断片長が正常にネゴシエーションされると、SSL/TLS/DTLSクライアントおよびサーバーで、すぐにメッセージ(ハンドシェーク・メッセージを含む)の断片化を開始して、ネゴシエーションされた長さより長い断片が送信されないようにすることができます。
システム・プロパティjsse.enableMFLNExtension
システム・プロパティjsse.enableMFLNExtension
は、MFLN拡張を有効または無効にするために定義されます。jsse.enableMFLNExtension
は、デフォルトでは無効になっています。
このシステム・プロパティの値は、次のように設定できます。
表8-10 jsse.enableMFLNExtensionシステム・プロパティ
システム・プロパティ | 説明 |
---|---|
jsse.enableMFLNExtension =true |
MFLN拡張を有効にします。SSLParameters.getMaximumPacketSize() で返された値が(2^12 + header-size)未満の場合、最大断片長ネゴシエーション拡張が有効になります。 |
jsse.enableMFLNExtension =false |
MFLN拡張を無効にします。 |
SSLParameters.setMaximumPacketSizeメソッドを使用して、SSL/TLS/DTLSレコードのための予期される最大ネットワーク・パケット・サイズ(バイト単位)を設定します。
HelloVerifyRequestsなど、小さいハンドシェーク・メッセージが断片化されないように、パケット・サイズは256バイト未満にしないようにすることをお薦めします。
2009年秋に、SSL/TLSプロトコルの問題が見つかりました。IETF TLS Working Groupによってプロトコルの修正が開発され、JDKの現行バージョンにはこの修正が含まれています。このセクションでは、このプロトコル修正を含まない以前の実装との通信時における相互運用性の問題を含め、状況をさらに詳しく説明します。
この脆弱性により、選択されたプレーン・テキストを接頭辞としてTLS接続に注入できるというMan-In-The-Middle (MITM)攻撃を許していました。この脆弱性は、クライアントとサーバーがセッションのネゴシエーションに成功した後、傍受されたネットワーク通信を攻撃者が復号化または変更することを許すものではありません。
CVE-2009-3555 (Mitreの共通脆弱性識別子リストに掲載、2009年)
TLS再ネゴシエーション攻撃の理解(Eric RescorlaのブログEducated Guessworkに掲載、2009年11月5日)。
この問題の修正は、2つのフェーズに分けて扱われています。
フェーズ1: プロトコル修正が開発されるまで、SSL/TLSの再ネゴシエーションをデフォルトで無効化する中間修正が、「March 30, 2010 Java SE and Java for Business Critical Patch Update」から利用できるようになりました。
フェーズ2: IETFによって、再ネゴシエーション・プロトコルの問題を扱うRFC 5746が発表されました。次の表に、RFC 5746を実装してセキュアな再ネゴシエーションをサポートする修正を含むJDKおよびJREリリースを示します。
表8-11 TLS再ネゴシエーション問題の修正を含むJDKおよびJREリリース
JDKファミリ | 脆弱性のあるリリース | フェーズ1の修正(再ネゴシエーションの無効化) | フェーズ2の修正(RFC 5746) |
---|---|---|---|
JDKおよびJRE 6 | Update 18以前 | Update 19-21 | Update 22 |
JDKおよびJRE 5.0 | Update 23以前 | Update 24-25 | Update 26 |
JDKおよびJRE 1.4.2 | Update 25以前 | Update 26-27 | Update 28 |
注意:
再ネゴシエーションを必要としないアプリケーションはフェーズ2のデフォルト構成に影響を受けません。ただし、再ネゴシエーションを必要とするアプリケーション(最初は匿名のクライアント・ブラウズを許可するが、後でSSL/TLS認証済のクライアントを要求するWebサーバーなど)は:SunJSSE実装は、RFC 5746に準拠したピアへの接続について、再ネゴシエーションをデフォルトで再び有効にします。つまり、セキュアに再ネゴシエーションを行うためには、クライアントとサーバーが両方ともRFC 5746をサポートする必要があります。まだアップグレードされていないピアとの接続について、SunJSSEではある程度の相互運用性モードが提供されていますが、ユーザーがクライアントとサーバーの両方の実装をできるだけ早く更新することを強く推奨します。
フェーズ2修正により、SunJSSEは現在3つの再ネゴシエーション相互運用性モードを用意しています。どのモードもRFC 5746のセキュアな再ネゴシエーションを完全にサポートしていますが、アップグレードされていないピアと通信する場合、次のような意味合いが加わります。
厳密モード: クライアントとサーバーの両方がRFC 5746にアップグレードされていること、および適切なRFC 5746メッセージを送信することが求められます。そうでない場合、初期の(または後続の)ハンドシェークが失敗して接続が切断されます。
相互運用モード(デフォルト): 正しいRFC 5746メッセージの使用はオプションですが、適切なメッセージが使用されない場合はレガシーの(元のSSL/TLS仕様の)再ネゴシエーションが無効になります。最初のレガシー接続は許可されますが、レガシーの再ネゴシエーションは無効化されます。これはセキュリティと相互運用性の最適な組み合わせであるため、デフォルト設定です。
セキュアでないモード: レガシーの再ネゴシエーションを完全に許可します。レガシーのピアとの相互運用性がもっとも高いですが、本来のMITM攻撃に対して脆弱です。
3つのモード区分は、アップグレードされていないピアとの接続にのみ影響します。すべてのクライアントおよびサーバーで厳密モード(完全なRFC 5746モード)を使用することが望ましいのですが、配備されているすべてのSSL/TLS実装がRFC 5746をサポートするようになるまである程度時間がかかるため、今のところは相互運用モードがデフォルトになっています。
次の表に、クライアントまたはサーバー(あるいはその両方)がRFC 5746をサポートするように更新されている場合と更新されていない場合の様々な例でのモードに関する相互運用性情報を示しています。
表8-12 相互運用性情報
クライアント | サーバー | モード |
---|---|---|
更新済 | 更新済 | すべてのモードで再ネゴシエーションがセキュリティ保護されます。 |
レガシー脚注5 | 更新済 | |
更新済 | レガシー脚注5 | |
レガシー脚注5 | レガシー脚注5 | 既存のSSL/TLS動作を行い、MITM攻撃に対して脆弱です。 |
脚注5 「レガシー」とは元のSSL/TLS仕様を意味します(つまり、RFC 5746でない)。
脚注6 SunJSSEフェーズ1実装は、明示的に再有効化されないかぎり再ネゴシエーションを拒否します。再ネゴシエーションが再有効化された場合、それらは正しいRFC 5746メッセージを送信しないため、RFC 5746に準拠したピアによって「レガシー」として扱われます。
脚注7 SSL/TLSでは、再ネゴシエーションをいずれの側からでも開始できます。フェーズ1修正のように、アップグレードされていないピアと相互運用モードで通信しているアプリケーションが(SSLSocket.startHandshake()
またはSSLEngine.beginHandshake()
を使用して)再ネゴシエーションを開始しようとすると、アプリケーションはSSLHandshakeException
(IOException
)を受け取り、接続は停止されます(handshake_failure
)。まだアップグレードされていないピアから再ネゴシエーション要求を受け取ったアプリケーションは、現在の接続のタイプに応じて応答します。
no_renegotiation(100)
タイプの警告メッセージがピアに送信され、接続は開いたままになります。古いバージョンのSunJSSEは、no_renegotiation
アラートを受け取ると接続を停止します。SSLHandshakeException
を受け取り、接続は閉じられます(handshake_failure
)。no_renegotiation
アラートはSSLv3仕様に定義されていません。次のシステム・プロパティを使用してモードを設定します(java.lang.Systemプロパティの指定方法を参照)。
sun.security.ssl.allowUnsafeRenegotiation
(フェーズ1で導入)は、レガシー(安全でない)再ネゴシエーションを許可するかどうかを制御します。sun.security.ssl.allowLegacyHelloMessages
(フェーズ2で導入)は、適切なRFC 5746メッセージを必要とすることなくピアがハンドシェーク・プロセスを実行することを許可します。注意:
システム・プロパティsun.security.ssl.allowUnsafeRenegotiation
およびsun.security.ssl.allowLegacyHelloMessages
は推奨されておらず、将来のJDKリリースで削除される可能性があります。
表8-13 相互運用性モードを設定するためのシステム・プロパティの値
モード | allowLegacyHelloMessages | allowUnsafeRenegotiation |
---|---|---|
厳密 | false | false |
相互運用(デフォルト) | true | false |
セキュアでない | true | true |
注意:
セキュアでないSSL/TLS再ネゴシエーションは、脆弱性が再確立されるため、再有効化しないでください。すべてのピアは、できるだけ早くRFC 5746準拠の実装に更新する必要があります。このRFC 5746修正を適用しても、再ネゴシエーションが必要な場合は、アップグレードされていないピアとの通信に影響が生じます。推奨されるいくつかの方法を次に示します。
ピアを再構築して再ネゴシエーションを要求しないようにする。
再ネゴシエーションは通常、Webサーバーが最初は匿名のクライアント・ブラウズを許可するが、後でSSL/TLS認証済クライアントを要求する場合や、Webサーバーが最初は弱い暗号化方式群を許可するが、後で強い暗号化方式群を必要とする場合に、Webサーバーによって使用されます。代替策は、最初のネゴシエーション中にクライアント認証および強い暗号化方式群を必要とすることです。これを行うには、2つの選択肢があります。
アプリケーションに、あるポイントに到達して、再ネゴシエーションが必要になるまでの「ブラウズ・モード」がある場合、「ブラウズ・モード」を削除し、すべての初期接続を強化するようにサーバーを再構築できます。
サーバーを2つのエンティティに分割して、1つのエンティティ上でブラウズ・モードを実行し、2つ目のエンティティをよりセキュアなモードで使用します。ネゴシエーション・ポイントに到達したら、関連情報をサーバー間で転送します。
これらのどちらのオプションも、ある程度の作業が必要ですが、元のセキュリティ上の欠陥が再度発生することはありません。
システム・プロパティを使用して、再ネゴシエーション相互運用性モードを「セキュアでない」に設定します。
フェーズ2修正の説明を参照してください。
RFC 5746では2つの新しいデータ構造が定義されており、上級ユーザーのために、ここで説明します
これらのいずれも、実装がRFC 5746に準拠していることと、セキュアな再ネゴシエーションが実行できることを通知するために使用できます。2009年11月から2010年2月までのIETF電子メール・ディスカッションを参照してください。
RFC 5746により、クライアントは最初のClientHello
でSCSVまたはRIを送信できます。相互運用性を最大限に高めるため、SunJSSEはデフォルトでSCSVを使用しますが、これは、いくつかのTLSサーバー/SSLサーバーが不明な拡張機能を正しく処理できないためです。有効化された暗号化方式群(SSLSocket.setEnabledCipherSuites()
またはSSLEngine.setEnabledCipherSuites()
)にSCSVが存在することによって、最初のClientHello
内でSCSVを送信するか、あるいはRIをかわりに送信すべきかどうかが判別されます。
SSLv2はSSL/TLS拡張機能をサポートしません。SSLv2Hello
プロトコルが有効化された場合、SCSVが最初のClientHello
内で送信されます。
前述のとおり、フェーズ1修正は、RFC 5746に準拠した修正が開発されるまでの間、再ネゴシエーションをデフォルトで無効にするためのものでした。再ネゴシエーションは、sun.security.ssl.allowUnsafeRenegotiation
システム・プロパティを設定することによって再び有効化できました。フェーズ2修正では同じsun.security.ssl.allowUnsafeRenegotiation
システム・プロパティを使用するだけでなく、それにRFC 5746メッセージを使用させる必要もあります。
すべてのアプリケーションを、できるだけ早くフェーズ2 RFC 5746修正にアップグレードする必要があります。
注意:
システム・プロパティsun.security.ssl.allowUnsafeRenegotiation
およびsun.security.ssl.allowLegacyHelloMessages
は推奨されておらず、将来のJDKリリースで削除される可能性があります。
次の場合、SSL/TLS再ネゴシエーションでサーバー証明書を変更することは、安全でないことがあります。
エンドポイント識別がSSL/TLSハンドシェークで有効でない場合、および
以前のハンドシェークがセッション再開省略初期ハンドシェークである場合、および
両方の証明書によって表されるアイデンティティが異なると見なせる場合。
2つの証明書は、次の場合に、同じアイデンティティを表すと見なせます。
IPアドレスのサブジェクトの代替名が両方の証明書に存在する場合、同一であるはずです。または、
DNS名のサブジェクトの代替名が両方の証明書に存在する場合、同一であるはずです。または、
サブジェクト・フィールドが両方の証明書に存在する場合、証明書のサブジェクトと発行人は同一であるはずです。
JDK 8u25以降では、SSL/TLS再ネゴシエーションでの安全でないサーバー証明書の変更は、デフォルトでできません。新しいシステム・プロパティjdk.tls.allowUnsafeServerCertChange
を使用して、SSL/TLS再ネゴシエーションにおける安全でないサーバー証明書変更を制限するかどうかを定義できます。
このシステム・プロパティのデフォルト値はfalseです。
注意:
どうしても必要でないかぎり、このシステム・プロパティをtrueに設定しないでください。安全でないサーバー証明書変更の脆弱性が再び確立される可能性があります。Java暗号化アーキテクチャ(JCA)は、暗号化、鍵生成と鍵合意およびメッセージ認証コード(MAC)アルゴリズム用のフレームワークと実装を提供するパッケージ・セットです。(Java暗号化アーキテクチャ(JCA)リファレンス・ガイドを参照。)SunJSSEプロバイダは、すべての暗号化操作にJCAを排他的に使用するので、JCAのRSA PKCS#11のサポートを含め、JCEの機能や拡張機能を自動的に利用できます。このサポートによりSunJSSEプロバイダは、ハードウェア暗号化アクセラレータを使用してパフォーマンスを大幅に向上させ、キーストアとしてスマート・カードを使用し、鍵および信頼性管理の柔軟性を高めることができます。
基盤となるアクセラレータ・ハードウェアを使用するようにOracle PKCS#11プロバイダが構成され、そのPKCS#11プロバイダを使用するようにJCAが構成されていれば、ハードウェア暗号化アクセラレータは自動的に使用されます。プロバイダは、プロバイダ・リストにある他のJCAプロバイダより前に構成する必要があります。Oracle PKCS#11プロバイダの構成方法の詳細は、PKCS#11リファレンス・ガイドを参照してください。
JCAでのPKCS#11のサポートにより、キーストアとしてスマートカードにアクセスすることもできます。JSSEによって使用されるキーストアのタイプと場所の構成方法の詳細は、JSSEのカスタマイズを参照してください。スマートカードをキーストアまたはトラストストアとして使用するには、javax.net.ssl.keyStoreType
およびjavax.net.ssl.trustStoreType
システム・プロパティをそれぞれpkcs11
に設定し、javax.net.ssl.keyStore
およびjavax.net.ssl.trustStore
システム・プロパティをそれぞれNONE
に設定します。特定のプロバイダを使用するように指定するには、javax.net.ssl.keyStoreProvider
およびjavax.net.ssl.trustStoreProvider
システム・プロパティを使用します(たとえば、それらをSunPKCS11-joe
に設定します)。これらのプロパティを使用することにより、以前はファイルベースのキーストアにアクセスするためにこれらのプロパティに依存していたアプリケーションを構成して、アプリケーションに変更を加えずにスマートカードのキーストアを使用できます。
アプリケーションによっては、キーストアをプログラムで使用する必要があります。こうしたアプリケーションでは、引き続き既存のAPIを使用してKeystore
をインスタンス化し、キー・マネージャとトラスト・マネージャに渡すことができます。Keystore
インスタンスがスマートカードに基づくPKCS#11キーストアを参照する場合は、JSSEアプリケーションがスマートカードの鍵にアクセスすることになります。
スマートカードおよびその他の取り外し可能トークンには、X509KeyManager
について追加の要件があります。Javaアプリケーションの存続期間の間、異なるスマートカードをスマートカード・リーダーに入れることができ、それらのスマートカードは異なるパスワードを使用して保護できます。
KeyStore.Builder
クラスは、KeyStore
オブジェクトの構造と初期化を抽象化します。パスワードのプロンプト用にCallbackHandler
の使用をサポートし、そのサブクラスを使用して、アプリケーションに望まれる追加機能をサポートできます。たとえば、Builder
を実装して、個々のKeyStore
エントリを異なるパスワードで保護するようにすることが可能です。その後、KeyStoreBuilderParameters
クラスを使用し、これらのBuilder
オブジェクトを1つ以上使用してKeyManagerFactory
を初期化できます。
NewSunX509と呼ばれる、SunJSSEプロバイダのX509KeyManager
実装は、これらのパラメータをサポートしています。複数の証明書が使用可能な場合は、適切な鍵を使用する証明書を選択し、期限切れの証明書より有効な証明書を優先させます。
例8-17では、PKCS#11キーストア(スマートカードを使用できる)とPKCS#12ファイルベース・キーストアの両方を使用するようJSSEに指示する方法を示します。
例8-17 PKCS#11およびPKCS#12ファイルベース・キーストアを使用するサンプル・コード
import javax.net.ssl.*; import java.security.KeyStore.*; // ... // Specify keystore builder parameters for PKCS#11 keystores Builder scBuilder = Builder.newInstance("PKCS11", null, new CallbackHandlerProtection(myGuiCallbackHandler)); // Specify keystore builder parameters for a specific PKCS#12 keystore Builder fsBuilder = Builder.newInstance("PKCS12", null, new File(pkcsFileName), new PasswordProtection(pkcsKsPassword)); // Wrap them as key manager parameters ManagerFactoryParameters ksParams = new KeyStoreBuilderParameters( Arrays.asList(new Builder[] { scBuilder, fsBuilder }) ); // Create KeyManagerFactory KeyManagerFactory factory = KeyManagerFactory.getInstance("NewSunX509"); // Pass builder parameters to factory factory.init(ksParams); // Use factory SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(factory.getKeyManagers(), null, null);
これらの暗号化方式群を使用できるようにするには、明示的に指定する必要があります。SSLEngine.setEnabledCipherSuites(String[])
メソッドとSSLSocket.setEnabledCipherSuites(String[])
メソッドについては、APIドキュメントを参照してください。その他のすべてのSSL/TLS/DTLS暗号化方式群と同様、暗号化方式群がピアによってサポートされていない場合は、暗号化のネゴシエーション時に選択されません。また、アプリケーションまたはサーバーが必要なKerberos資格を取得できない場合は、Kerberos符号化方式も選択されません。
次に、TLS_KRB5_WITH_DES_CBC_SHA
暗号化方式群のみを使用するTLSクライアントの例を示します。
// Create socket SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(tlsServer, serverPort); // Enable only one cipher suite String enabledSuites[] = { "TLS_KRB5_WITH_DES_CBC_SHA" }; sslSocket.setEnabledCipherSuites(enabledSuites);
JSSEでKerberos符号化方式を使用する前に、配備されている環境でKerberosインフラストラクチャを設定しておく必要があります。特に、TLSクライアントとサーバーの両方に、Kerberos Key Distribution Center (KDC)によるアカウントが設定されていことが必要です。実行時にKerberos暗号化方式群の1つ以上が有効化されると、TLSクライアントとサーバーは、それぞれのアカウントに関連付けられている自身のKerberosクレデンシャルをKDCから取得します。たとえば、Kerberos領域IMC.ORG
のマシンmach1.imc.org
で動作するTLSサーバーは、host/mach1.imc.org@IMC.ORG
という名前のアカウントを持ち、IMC.ORG
用のKDCを使用するように構成されている必要があります。Kerberos要件を参照してください。
アプリケーションは、Java Authentication and Authorization Service (JAAS)リファレンス・ガイドとKerberosログイン・モジュールを使用して、自身のKerberos資格を取得できます。JDKには、Krb5LoginModuleが付属しています。JSSEでのKerberos暗号化方式群は、JAASプログラミングありまたはなしでJAASおよびJava GSS-APIチュートリアルを使用する方法と同様に、JAASプログラミングありでもなしでも使用できます。
JAASプログラミングなしでJSSEでKerberos暗号化方式群を使用するには、TLSサーバーJAAS構成エントリ用にcom.sun.net.ssl.server
またはother
、およびTLSクライアント用にcom.sun.net.ssl.client
またはother
というインデックス名を使用し、javax.security.auth.useSubjectCredsOnly
システム・プロパティをfalseに設定する必要があります。たとえば、JAASプログラミングを使用しないTLSサーバーには次のJAAS構成ファイルを使用できます。
com.sun.net.ssl.server { com.sun.security.auth.module.Krb5LoginModule required principal="host/mach1.imc.org@IMC.ORG" useKeyTab=true keyTab=mach1.keytab storeKey=true; };
JAASプログラミングなしでJava GSSおよびKerberosを使用する方法の例は、JDK 8ドキュメント内のチュートリアルJAASプログラミングなしでの安全なメッセージ交換のためのJava GSS-APIの使用で説明されています。Java GSS呼出しをJSSE呼出しに置き換えることにより、JSSEの使用例に適応できます。
JAASプログラミングありでKerberos暗号化方式群を使用するには、任意のインデックス名を使用できます。これは、アプリケーションが、インデックス名を使用してJAAS LoginContext
を作成し、JSSE呼出しをSubject.doAs()
またはSubject.doAsPrivileged()
呼出しの内部にラップする役割を持つためです。Java GSSおよびKerberosでJAASを使用する方法の例は、JDK 8ドキュメント内のチュートリアル安全なメッセージ交換のためのJAASログイン・ユーティリティおよびJava GSS-APIの使用で説明されています。Java GSS呼出しをJSSE呼出しに置き換えることにより、JSSEの使用例に適応できます。
Kerberosを使用するJSSEアプリケーションの使用または構成について問題がある場合は、JAASおよびJava GSS-APIチュートリアルの紹介のトラブルシューティングを参照してください。
SSL/TLS/DTLS接続のピアの識別情報を判別するには、次のクラスで、getPeerPrincipal()
メソッドを使用します。
javax.net.ssl.SSLSession
javax.net.ssl.HttpsURLConnection
javax.net.HandshakeCompletedEvent
同様に、(ローカル・エンティティを識別するために)ピアに送信された識別情報を取得するには、これらのクラスでgetLocalPrincipal()
メソッドを使用します。X509ベースの暗号化方式群の場合、これらのメソッドはjavax.security.auth.x500.X500Principal
のインスタンスを返します。Kerberos暗号化方式群の場合、これらのメソッドはjavax.security.auth.kerberos.KerberosPrincipal
のインスタンスを返します。
JSSEアプリケーションは、getPeerCertificates()
と、javax.net.ssl.SSLSession
、javax.net.ssl.HttpsURLConnection
、javax.net.HandshakeCompletedEvent
の類似のメソッドを使用して、ピアに関する情報を取得します。ピアに証明書がない場合、SSLPeerUnverifiedException
がスローされます。
アプリケーションでピアの識別情報またはピアに送信された識別情報のみを判別する必要がある場合は、それぞれgetPeerPrincipal()
およびgetLocalPrincipal()
メソッドを使用してください。getPeerCertificates()
およびgetLocalCertificates()
メソッドは、それらの証明書の内容を調べる必要がある場合にのみ使用してください。また、アプリケーションでは、認証されたピアに証明書がない場合の処理を準備しておく必要があります。
セキュリティ・マネージャが有効な場合、ピアとの通信に必要なSocketPermission
に加えて、Kerberos暗号化方式群を使用するTLSクライアント・アプリケーションには次のアクセス権も必要です。
javax.security.auth.kerberos.ServicePermission(serverPrincipal, "initiate");
説明は次のとおりです。
TLSサーバー・アプリケーションには、次のアクセス権が必要です。
javax.security.auth.kerberos.ServicePermission(serverPrincipal, "accept");
説明は次のとおりです。
サーバーまたはクライアントがKDCに接続する必要がある場合(そのクレデンシャルがローカルにキャッシュされていない場合など)、次のアクセス権も必要です。
javax.security.auth.kerberos.ServicePermission(tgtPrincipal, "initiate");
説明は次のとおりです。
PKCS#12 (Personal Information Exchange Syntax Standard)では、移植可能な保存形式、およびユーザーの非公開鍵、証明書、その他の秘密およびほかの項目の転送について規定されています。SunJSSEプロバイダは、PKCS12ファイルの読取りおよび書込みのためのPKCS12 java.security.KeyStore
形式の完全な実装を提供します。この形式は、鍵と証明書のインポートおよびエクスポートのために、Mozilla Firefox、Microsoft Internet ExplorerおよびOpenSSLなど、他のツールキットやアプリケーションでもサポートされています。たとえば、これらの実装は、クライアントの証明書と鍵を.p12ファイル名拡張子を使用してファイルにエクスポートできます。
SunJSSEプロバイダでは、PKCS12のキーストア・タイプを使用して、KeyStore
APIを介してPKCS12鍵にアクセスできます。さらにkeytool
コマンドと、pkcs12
に設定された-storetype
オプションを使用して、インストールされた鍵および関連する証明書を表示できます。Java Platform, Standard Editionツール・リファレンスでkeytoolを参照してください。
SNI拡張は、SSL/TLS/DTLSプロトコルを拡張し、クライアントがハンドシェーク時に接続を試みるサーバー名を示す機能です。サーバーはServer Name Indicationの情報を使って、特定のSSLSocket
またはSSLEngine
インスタンスが接続を受け入れる必要があるかどうかを判定できます。たとえば、単一の基礎となるネットワーク・アドレスで複数の仮想または名前ベースのサーバーがホストされている場合、サーバー・アプリケーションは、SNI情報を使用して、このサーバーが、クライアントがアクセスしようとしている正しいサーバーであるかどうかを判断できます。このクラスのインスタンスは、サーバーによって、ホスト名などの特定のタイプの受け付け可能なサーバー名を確認するために使用できます。TLS拡張(RFC 6066)の第3項を参照してください。
クライアント・アプリケーションの開発者は、SSLParameters.setServerNames(List<SNIServerName> serverNames)
メソッドを使用して、サーバー名の表示を明示的に設定できます。例8-18を参照してください。
サーバー・アプリケーションの開発者はSNIMatcher
クラスを使用して、サーバー名の表示を認識する方法を決定できます。例8-19と例8-20では、この機能を示します。
例8-18 Server Name Indicationを設定するサンプル・コード
次のコード例では、メソッドSSLParameters.setServerNames(List<SNIServerName> serverNames)を使用してサーバー名表示を設定する方法を示します。
SSLSocketFactory factory = ... SSLSocket sslSocket = factory.createSocket("172.16.10.6", 443); // SSLEngine sslEngine = sslContext.createSSLEngine("172.16.10.6", 443); SNIHostName serverName = new SNIHostName("www.example.com"); List<SNIServerName> serverNames = new ArrayList<>(1); serverNames.add(serverName); SSLParameters params = sslSocket.getSSLParameters(); params.setServerNames(serverNames); sslSocket.setSSLParameters(params); // sslEngine.setSSLParameters(params);
例8-19 SSLSocketクラスを使用してSNIを認識するサンプル・コード
次のコード例では、サーバー・アプリケーションでSNIMatcher
クラスを使用してサーバー名表示の認識方法を決定する方法を示します。
SSLSocket sslSocket = sslServerSocket.accept(); SNIMatcher matcher = SNIHostName.createSNIMatcher("www\\.example\\.(com|org)"); Collection<SNIMatcher> matchers = new ArrayList<>(1); matchers.add(matcher); SSLParameters params = sslSocket.getSSLParameters(); params.setSNIMatchers(matchers); sslSocket.setSSLParameters(params);
例8-20 SSLServerSocketクラスを使用してSNIを認識するサンプル・コード
次のコード例では、サーバー・アプリケーションでSNIMatcher
クラスを使用してサーバー名表示の認識方法を決定する方法を示します。
SSLServerSocket sslServerSocket = ...; SNIMatcher matcher = SNIHostName.createSNIMatcher("www\\.example\\.(com|org)"); Collection<SNIMatcher> matchers = new ArrayList<>(1); matchers.add(matcher); SSLParameters params = sslServerSocket.getSSLParameters(); params.setSNIMatchers(matchers); sslServerSocket.setSSLParameters(params); SSLSocket sslSocket = sslServerSocket.accept();
次のリストは、ClientHelloメッセージで様々なサーバー名表示要求を受け取った場合のSNIMatcher
の動作の例を示しています。
www\\.example\\.com
に構成されたマッチャ:
www.example.com
の場合、それは受け付けられ、ServerHelloメッセージで確認が送信されます。www.example.org
の場合、それはunrecognized_name
致命的エラーで拒否されます。www\\.invalid\\.com
に構成されたマッチャ:
www.example.com
の場合、それはunrecognized_name
致命的エラーで拒否されます。www.example.org
の場合、それは受け付けられ、ServerHelloメッセージで確認が送信されます。マッチャが構成されていない:
要求されたすべてのホスト名が受け付けられますが、ServerHelloメッセージで確認は送信されません。
SNI拡張を実装する新しいクラスの説明については、次を参照してください。
たとえば、「Server Name Indication (SNI)拡張の使用」を参照してください。
アプリケーション層プロトコル・ネゴシエーション(ALPN)を使用してTLS接続のためにアプリケーション・プロトコルをネゴシエーションします。
ALPNとは
アプリケーションによっては、TLSハンドシェークが完了する前に、共有アプリケーション・レベル値のネゴシエーションが必要な場合があります。たとえば、HTTP/2では、特定のTCPまたはUDPポートで使用されるか使用できるHTTPバージョン(h2、spdy/3、http/1.1)の確立に役立つように、アプリケーション層プロトコル・ネゴシエーションのメカニズムが使用されます。ALPN (RFC 7301)では、クライアントとサーバーとの間のネットワーク・ラウンドトリップを増やすことなく、これが行われます。HTTP/2の場合、プロトコルは、接続をネゴシエーションする前に確立される必要があります。これは、クライアントとサーバーが、通信を開始する前に、使用するHTTPのバージョンを把握する必要があるためです。ALPNを使用しないと、アプリケーション・プロトコルHTTP/1とHTTP/2を同じポートで使用できません。
クライアントは、TLSハンドシェークの開始時にALPN拡張を使用して、サポートされているアプリケーション・プロトコルのリストをClientHello
の一部としてサーバーに送信します。サーバーは、ClientHello
内のサポートされているアプリケーション・プロトコルのリストを読み取り、サポートされているプロトコルのうちどれが望ましいかを判断します。次に、ネゴシエーション結果とともにServerHello
メッセージをクライアントに送り返します。メッセージには、選択されているプロトコルの名前、またはプロトコルが選択されていないことが示されている場合があります。
そのようにして、アプリケーション・プロトコル・ネゴシエーションは、ネットワーク・ラウンドトリップを増やすことなく、TLSハンドシェーク内で行うことができ、必要に応じてサーバーで異なる証明書を各アプリケーション・プロトコルに関連付けることができるようにします。
他の多くのTLS拡張と異なり、この拡張では、セッションのプロパティは確立されず、接続のプロパティのみとなります。それが、ネゴシエーションした値がSSLSession
ではなくSSLSocket
/SSLEngine
で見つかる理由です。セッション再開またはセッション・チケットが使用される場合(サーバー側の状態なしでのTLSセッション再開を参照)、前にネゴシエーションした値は無関係であり、新しいハンドシェーク・メッセージ内の値のみが考慮されます。
クライアントによってサポートされているアプリケーション層プロトコル・ネゴシエーション(ALPN)値を設定します。サーバーとのハンドシェーク中に、サーバーは、クライアントのアプリケーション・プロトコル・リストを読み取り、どれが最も適切かを判断します。
クライアントの場合は、SSLParameters.setApplicationProtocols(String[])
メソッドの次にSSLSocket
またはSSLEngine
のsetSSLParameters
メソッドを使用して、サーバーに送信するためのアプリケーション・プロトコルを設定します。
例8-21 JavaクライアントでのALPN値の設定および取得のサンプル・コード
たとえば、ここでは、クライアントでthree
とtwo
というALPN値を設定する手順を示します。
コードを実行するには、プロパティjavax.net.ssl.trustStore
を有効なルート証明書に設定する必要があります。(これはコマンド行で実行できます)。
import java.io.*; import java.util.*; import javax.net.ssl.*; public class SSLClient { public static void main(String[] args) throws Exception { // Code for creating a client side SSLSocket SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket("localhost", 9999); // Get an SSLParameters object from the SSLSocket SSLParameters sslp = sslSocket.getSSLParameters(); // Populate SSLParameters with the ALPN values // On the client side the order doesn't matter as // when connecting to a JDK server, the server's list takes priority String[] clientAPs = {"three", "two"}; sslp.setApplicationProtocols(clientAPs); // Populate the SSLSocket object with the SSLParameters object // containing the ALPN values sslSocket.setSSLParameters(sslp); sslSocket.startHandshake(); // After the handshake, get the application protocol that has been negotiated String ap = sslSocket.getApplicationProtocol(); System.out.println("Application Protocol client side: \"" + ap + "\""); // Do simple write/read InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); sslOS.write(280); sslOS.flush(); sslIS.read(); sslSocket.close(); } }
one
、two
およびthree
が設定されているJavaサーバーにClientHello
が送信されると、出力は次のようになります。Application Protocol client side: twoハンドシェークの詳細は、SSLハンドシェークを参照してください。ハンドシェーク中にネゴシエーションの結果を確認することもできます。ハンドシェーク中のネゴシエーション済ALPN値の特定を参照してください。
サーバーでALPN値を設定することで、デフォルトのALPNメカニズムを使用して、適切なアプリケーション・プロトコルを決定します。
SSLParameters
オブジェクトに移入してから、このSSLParameters
オブジェクトを使用してSSLSocket
オブジェクトかSSLEngine
オブジェクトにこれらのパラメータを移入します。サーバーで設定されたALPN値のうち、ClientHello
に含まれているALPN値のいずれかに一致する最初の値が選択され、ServerHello
の一部としてクライアントに返されます。例8-22 サーバーでのデフォルトALPN値ネゴシエーションのサンプル・コード
次に、プロトコル・ネゴシエーションにデフォルトの手法を使用するJavaサーバー用コードを示します。コードを実行するには、プロパティjavax.net.ssl.keyStore
を有効なキーストアに設定する必要があります。(これはコマンド行で実行できます。JSSEで使用するキーストアの作成を参照)。
import java.util.*; import javax.net.ssl.*; public class SSLServer { public static void main(String[] args) throws Exception { // Code for creating a server side SSLSocket SSLServerSocketFactory sslssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(9999); SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); // Get an SSLParameters object from the SSLSocket SSLParameters sslp = sslSocket.getSSLParameters(); // Populate SSLParameters with the ALPN values // As this is server side, put them in order of preference String[] serverAPs ={ "one", "two", "three" }; sslp.setApplicationProtocols(serverAPs); // If necessary at any time, get the ALPN values set on the // SSLParameters object with: // String serverAPs = sslp.setApplicationProtocols(); // Populate the SSLSocket object with the ALPN values sslSocket.setSSLParameters(sslp); sslSocket.startHandshake(); // After the handshake, get the application protocol that // has been negotiated String ap = sslSocket.getApplicationProtocol(); System.out.println("Application Protocol server side: \"" + ap + "\""); // Continue with the work of the server InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); sslIS.read(); sslOS.write(85); sslOS.flush(); sslSocket.close(); } }
three
およびtwo
を含むClientHello
が送信されると、出力は次のようになります。Application Protocol server side: twoハンドシェークの詳細は、SSLハンドシェークを参照してください。ハンドシェーク中にネゴシエーションの結果を確認することもできます。ハンドシェーク中のネゴシエーション済ALPN値の特定を参照してください。
コールバック・メソッドを設定することで、カスタムのALPNメカニズムを使用して、適切なアプリケーション・プロトコルを決定します。
サーバーのデフォルト・ネゴシエーション・プロトコルを使用しない場合は、SSLEngine
またはSSLSocket
のsetHandshakeApplicationProtocolSelector
メソッドを使用して、現在までのハンドシェーク状態を確認できるBiFunction
(ラムダ)コールバックを登録し、クライアントのアプリケーション・プロトコル・リストおよびその他の関連情報に基づいて選択できます。たとえば、推奨されている暗号化方式群、または選択に当たって取得できるServer Name Indication (SNI)やその他のデータの使用を検討できます。カスタム・ネゴシエーションを使用する場合は、setApplicationProtocols
メソッドによって設定された値(デフォルト・ネゴシエーション)は無視されます。
例8-23 サーバーでのカスタムALPN値ネゴシエーションのサンプル・コード
次に、プロトコル・ネゴシエーションにカスタム・メカニズムを使用するJavaサーバー用コードを示します。コードを実行するには、プロパティjavax.net.ssl.keyStore
を有効な証明書に設定する必要があります。(これはコマンド行で実行できます。JSSEで使用するキーストアの作成を参照)。
import java.util.*; import javax.net.ssl.*; public class SSLServer { public static void main(String[] args) throws Exception { // Code for creating a server side SSLSocket SSLServerSocketFactory sslssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(9999); SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); // Code to set up a callback function // Pass in the current SSLSocket to be inspected and client AP values sslSocket.setHandshakeApplicationProtocolSelector( (serverSocket, clientProtocols) -> { SSLSession handshakeSession = serverSocket.getHandshakeSession(); // callback function called with current SSLSocket and client AP values // plus any other useful information to help determine appropriate // application protocol. Here the protocol and ciphersuite are also // passed to the callback function. return chooseApplicationProtocol( serverSocket, clientProtocols, handshakeSession.getProtocol(), handshakeSession.getCipherSuite()); }); sslSocket.startHandshake(); // After the handshake, get the application protocol that has been // returned from the callback method. String ap = sslSocket.getApplicationProtocol(); System.out.println("Application Protocol server side: \"" + ap + "\""); // Continue with the work of the server InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); sslIS.read(); sslOS.write(85); sslOS.flush(); sslSocket.close(); } // The callback method. Note how the parameters match the call within // the setHandshakeApplicationProtocolSelector method above. public static String chooseApplicationProtocol(SSLSocket serverSocket, List<String> clientProtocols, String protocol, String cipherSuite ) { // For example, check the cipher suite and return an application protocol // value based on that. if (cipherSuite.equals("<--a_particular_ciphersuite-->")) { return "three"; } else { return ""; } } }
暗号化方式群が、このコードの実行時に条件文で指定されたものと一致する場合は、値three
が返されます。そうでない場合は、空の文字列が返されます。
BiFunction
オブジェクトの戻り値がString
であることに注意してください。これは、アプリケーション・プロトコル名になるか、通知された値をどれも受け入れられないことを示すnullになります。戻り値が空のString
である場合は、アプリケーション・プロトコルの指示は使用されません。戻り値がnull (選択値なし)であるか、ピアによって通知されていない値である場合は、基になるプロトコルで、実行するアクションが決定されます。(たとえば、サーバー・コードでno_application_protocolアラートが送信され、接続が終了されます。)
クライアントとサーバーの両方でハンドシェークが完了した後、SSLSocket
オブジェクトまたはSSLEngine
オブジェクトでgetApplicationProtocol
メソッドを呼び出すことで、ネゴシエーションの結果を確認できます。ハンドシェークの詳細は、SSLハンドシェークを参照してください。
ハンドシェーク中にネゴシエーションされたALPN値を特定するには、カスタムのKeyManager
またはTrustManager
クラスを作成し、このカスタム・クラスにgetHandshakeApplicationProtocol
メソッドの呼出しを含めます。
選択したALPN値とSNI値がKeyManager
またはTrustManager
による選択内容に影響するユースケースがいくつかあります。たとえば、アプリケーションが、サーバーの属性および選択されたALPN/SNI/暗号化方式群値に応じて、異なる証明書/公開鍵のセットを選択する必要がある場合があります。
与えられたサンプル・コードでは、KeyManager
オブジェクトとして作成し登録するカスタムX509ExtendedKeyManager
内から、getHandshakeApplicationProtocol
メソッドを呼び出す方法を示します。
例8-24 カスタムKeyManagerのサンプル・コード
この例では、X509ExtendedKeyManager
を拡張するカスタムKeyManager
のコード全体を示します。大部分のメソッドは、このMyX509ExtendedKeyManager
クラスによってラップされているKeyManager
クラスから返された値を返すのみです。ただし、chooseServerAlias
メソッドは、SSLSocket
オブジェクトでgetHandshakeApplicationProtocol
を呼び出し、それにより、ネゴシエーションされた現在のALPN値を特定できます。
import java.net.Socket; import java.security.*; import javax.net.ssl.*; public class MyX509ExtendedKeyManager extends X509ExtendedKeyManager { // X509ExtendedKeyManager is an abstract class so your new class // needs to implement all the abstract methods in this class. // The easiest way to do this is to wrap an existing KeyManager // and call its methods for each of the methods you need to implement. X509ExtendedKeyManager akm; public MyX509ExtendedKeyManager(X509ExtendedKeyManager akm) { this.akm = akm; } @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return akm.getClientAliases(keyType, issuers); } @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { return akm.chooseClientAlias(keyType, issuers, socket); } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { // This method has access to a Socket, so it is possible to call the // getHandshakeApplicationProtocol method here. Note the cast from // a Socket to an SSLSocket String ap = ((SSLSocket) socket).getHandshakeApplicationProtocol(); System.out.println("In chooseServerAlias, ap is: " + ap); return akm.chooseServerAlias(keyType, issuers, socket); } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return akm.getServerAliases(keyType, issuers); } @Override public X509Certificate[] getCertificateChain(String alias) { return akm.getCertificateChain(alias); } @Override public PrivateKey getPrivateKey(String alias) { return akm.getPrivateKey(alias); } }
KeyManager
として登録されており、Javaクライアントによって、ALPN値を含むClientHello
が送信される場合、出力は次のようになります。In chooseServerAlias, ap is: <negotiated value>
例8-25 JavaサーバーでのカスタムKeyManagerの使用のサンプル・コード
この例では、デフォルトのALPNネゴシエーション戦略、および前のコード例で示されたカスタムKeyManager
、MyX509ExtendedKeyManager
を使用する単純なJavaサーバーを示します。
import java.io.*; import java.util.*; import javax.net.ssl.*; import java.security.KeyStore; public class SSLServerHandshake { public static void main(String[] args) throws Exception { SSLContext ctx = SSLContext.getInstance("TLS"); // You need to explicitly create a create a custom KeyManager // Keystores KeyStore keyKS = KeyStore.getInstance("PKCS12"); keyKS.load(new FileInputStream("serverCert.p12"), "password".toCharArray()); // Generate KeyManager KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); kmf.init(keyKS, "password".toCharArray()); KeyManager[] kms = kmf.getKeyManagers(); // Code to substitute MyX509ExtendedKeyManager if (!(kms[0] instanceof X509ExtendedKeyManager)) { throw new Exception("kms[0] not X509ExtendedKeyManager"); } // Create a new KeyManager array and set the first index // of the array to an instance of MyX509ExtendedKeyManager. // Notice how creating this object is done by passing in the // existing default X509ExtendedKeyManager kms = new KeyManager[] { new MyX509ExtendedKeyManager((X509ExtendedKeyManager) kms[0])}; // Initialize SSLContext using the new KeyManager ctx.init(kms, null, null); // Instead of using SSLServerSocketFactory.getDefault(), // get a SSLServerSocketFactory based on the SSLContext SSLServerSocketFactory sslssf = ctx.getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(9999); SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); SSLParameters sslp = sslSocket.getSSLParameters(); String[] serverAPs ={"one","two","three"}; sslp.setApplicationProtocols(serverAPs); sslSocket.setSSLParameters(sslp); sslSocket.startHandshake(); String ap = sslSocket.getApplicationProtocol(); System.out.println("Application Protocol server side: \"" + ap + "\""); InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); sslIS.read(); sslOS.write(85); sslOS.flush(); sslSocket.close(); sslServerSocket.close(); } }
カスタムX509ExtendedKeyManager
の準備が整い、ハンドシェーク中にchooseServerAlias
が呼び出されると、KeyManager
は、ネゴシエーションされたアプリケーション・プロトコル値を確認できるようになります。示されている例の場合、この値はコンソールに出力されます。
three
およびtwo
を含むClientHello
が送信されると、出力は次のようになります。Application Protocol server side: two
これらのクラスとメソッドは、アプリケーション層プロトコル・ネゴシエーション(ALPN)の使用時に使用されます。
使用するクラスとメソッド
SSLEngine
とSSLSocket
には同じALPN関連メソッドが含まれており、それらには同じ機能があります。
クラス | メソッド | 用途 |
---|---|---|
SSLParameters |
public String[] getApplicationProtocols(); |
クライアント側とサーバー側: 各プロトコル・セットを含むString 配列を返すには、このメソッドを使用します。 |
SSLParameters |
public void setApplicationProtocols([] protocols); |
クライアント側: サーバーに選択可能なプロトコルを設定するには、このメソッドを使用します。 サーバー側: サーバーで使用可能なプロトコルを設定するには、このメソッドを使用します。String配列には、プロトコルが優先度順に含まれている必要があります。 |
SSLEngine
SSLSocket |
public String getApplicationProtocol(); |
クライアント側とサーバー側: 接続のために選択されているプロトコルを含むString を返すには、TLSプロトコル・ネゴシエーションが完了した後に、このメソッドを使用します。 |
SSLEngine
SSLSocket |
public String getHandshakeApplicationProtocol(); |
クライアント側とサーバー側: 接続のために選択されているプロトコルを含むString を返すには、ハンドシェーク中にこのメソッドを使用します。ハンドシェークの前または後にこのメソッドが呼び出された場合は、nullが返されます。このメソッドの呼出し方法の詳細は、ハンドシェーク中のネゴシエーション済ALPN値の特定を参照してください。 |
SSLEngine
SSLSocket |
public void setHandshakeApplicationProtocolSelector(BiFunction,String> selector) |
サーバー側: コールバック関数を登録するには、このメソッドを使用します。その後、プロトコルや暗号化方式群など、利用可能な任意の情報に基づいて、アプリケーション・プロトコル値をコールバック内で設定できます。このメソッドの使用方法の詳細は、サーバーでのカスタムALPNの設定を参照してください。 |
このセクションにはJSSEのトラブルシューティングに関する情報が含まれます。構成に関する一般的な問題の解決策を提供します。
まず、一般的な構成の問題とそれらの解決方法を説明し、次に、役に立つデバッグ・ユーティリティについて説明します。
構成に関するいくつかの一般的問題のための解決策。
問題: SSL/TLS/DTLS接続のネゴシエーション中に、クライアントまたはサーバーがCertificateException
をスローします。
原因1: 一般に、リモート側がローカル側に不明な証明書を送信することが原因です。
解決法1: このタイプの問題をデバッグするもっともよい方法は、デバッグをオンにして(「デバッグ・ユーティリティ」を参照)、証明書のロード時およびネットワーク接続経由での証明書の受信時に観察することです。多くの場合、間違ったトラスト・ファイルをロードしたため、受信した証明書がトラスト・メカニズムにとって不明です。
次の項を参照してください。
原因2: システム・クロックが正しく設定されていません。この場合、認識された時間が証明書の有効期間外になっている可能性があります。トラストストアからの有効な証明書と置き換えないかぎり、システムはこの証明書を無効とみなすため、例外をスローします。
解決法2: システム・クロックの時間を修正します。
問題: JSSEを使用するプログラムを実行すると、SSLサービスが利用できないという例外が発生します。たとえば、次のような例外がスローされます。
Exception in thread "main" java.net.SocketException: no SSL Server Sockets Exception in thread "main": SSL implementation not available
原因: キーストア上の誤ったパスワードやキーストアの破損などのため、SSLContext
の初期化で問題が発生しました(かつてあるベンダーが不明な形式のキーストアを出荷し、それが原因でこのタイプのエラーが発生しました。)
解決法: 初期化パラメータを確認します。指定したキーストアが有効であり、指定したパスワードが正しいことを確認します。これをチェックできる1つの方法は、keytool
を使用して、キーストアと関連の内容を調べることです。Java Platform, Standard Editionツール・リファレンスでkeytoolを参照してください。
問題: 単純な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 corresponding to the SSL cipher suites which are enabled"も参照してください。
問題2: DSAベースの証明書しかないサーバーのファイルにMozilla FirefoxやMicrosoft Internet Explorerでアクセスすると、共通の暗号化方式群がないことを示す実行時例外が発生します。
原因2: デフォルトでは、keytool
で作成されたkeyEntries
はDSA公開鍵を使用します。キーストア内にDSAのkeyEntries
のみが存在する場合、使用できるのはDSAベースの暗号化方式群のみです。デフォルトでは、NavigatorとInternet ExplorerはRSAベースの暗号化方式群のみを送信します。クライアントとサーバーの暗号化方式群セットの共通点がないため、この例外がスローされます。
解決法2: NavigatorやInternet Explorerと対話するには、RSAベースの鍵を使用する証明書を作成してください。そのためには、keytool使用時に-keyalg
RSAオプションを指定します。次に例を示します。
keytool -genkeypair -alias duke -keystore testkeys -keyalg rsa
問題: ソケットが接続を試み、ClientHelloメッセージを送信すると、ただちに切断されます。
原因: SSL/TLSサーバーの中には、理解できない形式や、サポートしていないプロトコル・バージョン番号でClientHelloメッセージを受信した場合、接続を切断するものがあります。
解決法: クライアント側で、有効なプロトコルの調整を試みてください。これには、次のシステム・プロパティやメソッドの変更や呼び出しが含まれます。
HttpsURLConnection
クラスのシステム・プロパティhttps.protocols
SSLContext.getInstance
メソッドSSLEngine.setEnabledProtocols
メソッドSSLSocket.setEnabledProtocols
メソッドSSLParameters.setProtocols
およびSSLEngine.setSSLParameters
メソッドSSLParameters.setProtocols
およびSSLSocket.setSSLParameters
メソッド後方互換性のため、一部のSSL/TLS実装(SunJSSEなど)はSSL/TLSのClientHelloメッセージをSSLv2のClientHello形式にカプセル化して送信できます。SunJSSEプロバイダはこの機能をサポートしています。この機能を使用する場合は、必要に応じて「SSLv2Hello」を有効なプロトコル・リストに追加します。(「JDKプロバイダ」で「プロトコル」を参照してください。ここでは、SunJSSEプロバイダでデフォルトで有効になっているプロトコルがリストされています。)
SSL/TLS RFC標準で、実装は、両側で使用されている最新バージョンとネゴシエーションする必要がありますが、認識できないバージョンを提示されると、一部の非準拠実装は単にハングアップします。たとえば、SSLv3のみを認識する一部の古いサーバー実装は、TLSv1.2を要求されると、シャットダウンします。この状況では、SSL/TLSバージョンのフォールバック・スキームを使用することを検討してください。
たとえば、クライアントの有効なプロトコル・リストがTLSv1、TLSv1.1およびTLSv1.2の場合、一般的なSSL/TLSバージョンのフォールバック・スキームが次のように見えます。
注意:
以前のバージョンにフォールバックすることは、通常、セキュリティ強度が弱いプロトコルにダウングレードすることを意味します。本当に必要で、サーバーが上位のプロトコル・バージョンをサポートしていないことが確実にわかっている場合でないかぎり、フォールバック・スキームを使用することはお薦めしません。注意:
一部のサーバーはSSLv3無効化の過程でSSLv2Helloも無効にしていますが、それは、SSLv2Helloがアクティブなクライアント(JDK 6u95)との通信が失敗することを意味します。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を使用します。SunJCEプロバイダがProvider
メカニズムから登録解除されており、JCEからの代替実装を利用できない場合、この例外がスローされます。
解決法: プロバイダがProvider
インタフェースで登録されていることをチェックして、SunJCEが使用可能であることを確認します。SSL接続のコンテキストで次のコードを実行してみます。
import javax.crypto.*; System.out.println("=====Where did you get AES====="); Cipher c = Cipher.getInstance("AES/CBC/NoPadding"); System.out.println(c.getProvider());
問題: WebサーバーからSSLでアプリケーション・リソースを取得しようとしたときにcom.sun.deploy.net.FailedDownloadException
を受け取り、WebサーバーではServer Name Indication (SNI)拡張を持つ仮想ホストが使用されている場合(Apache HTTPサーバーなど)、Webサーバーが正しく構成されていないことがあります。
原因: Java SE 7では、JSSEクライアントのSNI拡張がサポートされているため、要求された仮想サーバーのホスト名は、SSLハンドシェーク中にクライアントからサーバーに送信された最初のメッセージに含まれています。要求されたホスト名(Server Name Indication)が、仮想ホストの構成に指定されているはずの期待されるサーバー名と一致しない場合、サーバーはクライアントの接続要求を拒否することがあります。これにより、SSLハンドシェークの認識されない名前の警告がトリガーされ、FailedDownloadException
がスローされます。
解決法: 問題を適切に診断するために、Javaコンソールを使用してトレースを有効にします。Java Platform, Standard Editionデプロイメント・ガイドでデバッグおよびJavaコンソールを参照してください。問題の原因がjavax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
の場合、SNIのための仮想ホスト構成が正しくない可能性があります。Apache HTTPサーバーを使用している場合、仮想ホストの構成については、名前ベースの仮想ホストのサポートを参照してください。特に、<VirtualHost
ブロック内でServerName
ディレクティブが正しく構成されていることを確認してください。
次を参照してください。
問題: SSLEngine.setEnabledCipherSuites(String[] suites)
メソッドでRC4暗号化方式群アルゴリズムが指定されており、SSLEngineがDTLSエンジンである場合に、IllegalArgumentException
例外がスローされます。
sslContext = SSLContext.getInstance("DTLS"); // Create the engine SSLEngine engine = sslContext.createSSLengine(hostname, port); String enabledSuites[] = { "SSL_RSA_WITH_RC4_128_SHA" }; engine.setEnabledCipherSuites(enabledSuites);
原因: DTLSバージョン1.0とDTLSバージョン1.2によると、RC4暗号化方式群はDTLSとは使用できません。
解決法: DTLS接続にはRC4ベースの暗号化方式群は使用しないでください。Javaセキュリティ標準アルゴリズム名仕様でJSSE暗号化方式群の名前を参照してください。
JSSEには、動的デバッグのトレースをサポートする機能があります。これは、Java SE プラットフォームでデバッグのアクセス制御に失敗した場合に使用するサポート機能に似ています。一般的なJava動的デバッグ・トレースのサポートにはjava.security.debug
システム・プロパティを使用してアクセスしますが、JSSE固有の動的デバッグ・トレースのサポートにはjavax.net.debug
システム・プロパティを使用してアクセスします。
注意:
debug
ユーティリティは、JSSEの公式にサポートされている機能ではありません。JSSE動的デバッグ・ユーティリティのオプションを表示するには、java
コマンドで次のコマンド行オプションを使用します。
-Djavax.net.debug=help
注意:
ユーティリティがデバッグするように設計されたクラスを使用しないプログラムの実行中に、いずれかの動的デバッグ・ユーティリティで値help
を指定しても、デバッグ・オプションは得られません。次の完全な例では、いくつかのJSSEクラスを使用するMyApp
というアプリケーションのデバッグ・オプションのリストを取得する方法を示しています。
java -Djavax.net.debug=help MyApp
MyApp
アプリケーションは、デバッグのヘルプ情報が表示されると動作しなくなりますが、これはヘルプコードによりアプリケーションが終了するためです。
現在のオプション:
all
: すべてのデバッグを有効にしますssl
: SSLデバッグを有効にしますssl
オプションとともに次のオプションを使用できます:
record
: レコードごとのトレースを有効にしますhandshake
: 各ハンドシェーク・メッセージを出力しますkeygen
: 鍵生成データを出力しますsession
: セッション・アクティビティを出力しますdefaultctx
: デフォルトのSSL初期化を出力しますsslctx
: SSLContext
のトレースを出力しますsessioncache
: セッション・キャッシュのトレースを出力しますkeymanager
: キー・マネージャのトレースを出力しますtrustmanager
: トラスト・マネージャのトレースを出力しますhandshake
オプションによって生成されるメッセージは、次のオプションで拡張できます:
data
: 各ハンドシェイク・メッセージの16進ダンプverbose
: ハンドシェイク・メッセージの詳細出力record
オプションによって生成されるメッセージは、次のオプションで拡張できます:
plaintext
: レコードのプレーン・テキストの16進ダンプpacket
: raw SSL/TLSパケットを出力しますjavax.net.debug
プロパティ値は、all
またはssl
を指定する必要があり、オプションでデバッグ指定子をその後に続けます。1つまたは複数のオプションが使用できます。オプションをセパレータで区切る必要はありませんが、コロン(:)やカンマ(,)などの区切り文字を使用すると読みやすくなります。どのセパレータでも使用でき、オプション・キーワードの順序も重要ではありません。
このデバッグ情報の見方の説明は、ガイド「SSL/TLS接続のデバッグ」を参照してください。
次に、javax.net.debug
プロパティの使用例を示します。
デバッグ・メッセージをすべて表示する場合は、次のように入力します。
java -Djavax.net.debug=all MyApp
各ハンドシェーク・メッセージを16進ダンプで表示するには、次のように入力します(コロンはオプションです)。
java -Djavax.net.debug=ssl:handshake:data MyApp
各ハンドシェーク・メッセージを16進ダンプで表示し、さらにトラスト・マネージャのトレース状態を表示するには、次のように入力します(カンマはオプションです)。
java -Djavax.net.debug=SSL,handshake,data,trustmanager MyApp
SSL/TLS接続における問題は、理解するのが難しい場合があり、実際に送受信されたメッセージが明確でない場合は特に面倒です。JSSEにはデバッグ機能が組み込まれており、javax.net.debug
システム・プロパティによって起動されます。
javax.net.debug
システム・プロパティの詳細は、デバッグ・ユーティリティを参照してください。
デバッグ出力ファイルの読み方を例示する短いサンプルを次に示します。出力は標準的なものではなく、リリースによって変化することがあります。例では、デフォルトのJSSE X509KeyManagerとX509TrustManagerを使用してデバッグ情報を出力しています。
この例では、SSL/TLSプロトコルの基礎を理解していることを前提としています。プロトコルの詳細(ハンドシェーク・メッセージなど)は、Secure Sockets Layer (SSL)プロトコルの概要を参照してください。
この例では、まずJDK 8ドキュメントのJSSEサンプル・コードからClassFileServer
サンプル・アプリケーションを実行します。これは、クライアント認証を必要とする簡単なHTTPSサーバーです。この例では、ホストmyremoteserver.example.com
上でClassFileServer
を実行します。
java -Djavax.net.ssl.trustStore=/my_home_directory/jssesamples/samples/samplecacerts -Djavax.net.ssl.trustStorePassword=changeit ClassFileServer 2002 /my_home_directory/jssesamples/samples/ TLS true
次に、JDK 8ドキュメントのJSSEサンプル・コードからSSLSocketClientWithClientAuth
サンプル・アプリケーションを(myremoteserver.example.com
以外のホスト上で)使用してこのHTTPSサーバーに接続し、HTTPS要求を送信して次の応答を受信します。
java SSLSocketClientWithClientAuth -Djavax.nex.debug=all -Djavax.net.ssl.trustStore=/my_home_directory/jssesamples/samples/samplecacerts myremoteserver.example.com 2002 /index.html
まずX509KeyManagerが初期化され、「duke」と呼ばれる被認証者のKeyStoreに1つのkeyEntryがあることを発見します。サーバーがクライアントに対してクライアント自体の認証を要求すると、X509KeyManagerはkeyEntryのリストを検索して適切な資格を見つけます。
***
found key for : duke
chain [0] = [
[
Version: V1
Subject: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.", L=Cupertino, ST=CA, C=US
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4
Key: Sun RSA public key, 1024 bits
modulus: 134968166047563266914058280571444028986498087544923991226919517667593269213420979048109900052353578998293280426361122296881234393722020704208851688212064483570055963805034839797994154526862998272017486468599962268346037652120279791547218281230795146025359480589335682217749874703510467348902637769973696151441
public exponent: 65537
Validity: [From: Tue May 22 19:46:46 EDT 2001,
To: Sun May 22 19:46:46 EDT 2011]
Issuer: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.", L=Cupertino, ST=CA, C=US
SerialNumber: [ 3b0afa66]
]
Algorithm: [MD5withRSA]
Signature:
0000: 5F B5 62 E9 A0 26 1D 8E A2 7E 7C 02 08 36 3A 3E _.b..&.......6:>
0010: C9 C2 45 03 DD F9 BC 06 FC 25 CF 30 92 91 B1 4E ..E......%.0...N
0020: 62 17 08 48 14 68 80 CF DD 89 11 EA 92 7F CE DD b..H.h..........
0030: B4 FD 12 A8 71 C7 9E D7 C3 D0 E3 BD BB DE 20 92 ....q......... .
0040: C2 3B C8 DE CB 25 23 C0 8B B6 92 B9 0B 64 80 63 .;...%#......d.c
0050: D9 09 25 2D 7A CF 0A 31 B6 E9 CA C1 37 93 BC 0D ..%-z..1....7...
0060: 4E 74 95 4F 58 31 DA AC DF D8 BD 89 BD AF EC C8 Nt.OX1..........
0070: 2D 18 A2 BC B2 15 4F B7 28 6F D3 00 E1 72 9B 6C -.....O.(o...r.l
]
***
X509TrustManagerが次に初期化され、localhostという名前のものを含む、認証局(CA)の複数の証明書が検索されます。これらのCAの署名がある有効なクレデンシャルを提示するサーバーはすべて信頼されます。
***
trustStore is: /my_home_directory/jssesamples/samples/samplecacerts
trustStore type is: pkcs12
trustStore provider is:
the last modified time is: Tue Dec 11 06:43:38 EST 2012
Reload the trust store
Reload trust certs
Reloaded 32 trust certs
...
adding as trusted cert:
Subject: CN=localhost, OU=Widget Development Group, O="Ficticious Widgets, Inc.", L=Sunnyvale, ST=CA, C=US
Issuer: CN=localhost, OU=Widget Development Group, O="Ficticious Widgets, Inc.", L=Sunnyvale, ST=CA, C=US
Algorithm: RSA; Serial number: 0x41004446
Valid from Thu Jul 22 18:48:38 EDT 2004 until Sun May 22 18:48:38 EDT 2011
...
付加的な初期化コードの一部が終了しました。このあと、いよいよサーバーに接続することができます。
trigger seeding of SecureRandom done seeding SecureRandom Allow unsafe renegotiation: false Allow legacy hello messages: true Is initial handshake: true Is secure renegotiation: false Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 for TLSv1 Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 for TLSv1 Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 for TLSv1 ... Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 for TLSv1.1 Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 for TLSv1.1 Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 for TLSv1.1 %% No cached client session update handshake state: client_hello[1] upcoming handshake states: server_hello[2]
サーバーへの接続が確立されて、初期ClientHelloメッセージが表示されます。メッセージには次の情報が含まれています。
この後、SSLv2Helloヘッダー形式でのTLSv1.2ヘッダーのカプセル化(setEnabledProtocols()参照)など、様々なフィルタ出力が続きます。
update handshake state: client_hello[1] upcoming handshake states: server_hello[2] *** ClientHello, TLSv1.2 RandomCookie: random_bytes = {BA FA 1D F1 56 A3 7C FF B9 43 76 71 98 F5 A9 B4 0E 6B DF 7B 52 B8 F3 92 CC F4 C1 8A AA 71 8A 3F} Session ID: {} Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV] Compression Methods: { 0 } Extension elliptic_curves, curve names: {secp256r1, secp384r1, secp521r1, sect283k1, sect283r1, sect409k1, sect409r1, sect571k1, sect571r1, secp256k1} Extension ec_point_formats, formats: [uncompressed] Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA Extension server_name, server_name: [type=host_name (0), value=sc11152716.us.oracle.com] Extension status_request_v2 CertStatusReqItemV2: ocsp_multi, OCSPStatusRequest ResponderIds: <EMPTY> Extensions: <EMPTY> CertStatusReqItemV2: ocsp, OCSPStatusRequest ResponderIds: <EMPTY> Extensions: <EMPTY> Extension status_request: ocsp, OCSPStatusRequest ResponderIds: <EMPTY> Extensions: <EMPTY> *** main, WRITE: TLSv1.2 Handshake, length = 265
「[Raw write]」というラベルのセクションは、raw出力オブジェクト(この場合はOutputStream)に送信される実際のデータを表します。
[Raw write]: length = 270
0000: 16 03 03 01 09 01 00 01 05 03 03 BA FA 1D F1 56 ...............V
0010: A3 7C FF B9 43 76 71 98 F5 A9 B4 0E 6B DF 7B 52 ....Cvq.....k..R
0020: B8 F3 92 CC F4 C1 8A AA 71 8A 3F 00 00 64 C0 2C ........q.?..d.,
...
初期ClientHelloを送信したあと、サーバーの応答であるServerHelloを待ちます。「[Raw read]」は、処理が実行される前に、入力デバイス(InputStream)から読み取ったrawデータを表示します。
[Raw read]: length = 5 0000: 16 03 03 16 A5 ..... [Raw read]: length = 1024 0000: 02 00 00 4D 03 03 5A 4E F9 E3 0C C5 C3 FE B6 50 ...M..ZN.......P 0010: ED 3E 40 2D 5D 75 27 12 B7 C0 FB CA C5 DD 6E 79 .>@-]u'.......ny 0020: DB FF AE C8 32 63 20 5A 4E F9 E3 35 7F A1 8D A1 ....2c ZN..5.... ... main, READ: TLSv1.2 Handshake, length = 5797
データは展開され、メッセージがSSL/TLS形式の場合はServerHelloに解析されます。非SSL/TLSソケット(プレーン・テキスト)に接続した場合、受信データはSSL/TLS形式ではないので、正常に接続できません。
ServerHelloは、次の事柄を指定します。
最後に、ServerHelloは、接続において「SSLv3」ではなく「TLSv1.2」を使用するように指定しています。
check handshake state: server_hello[2]
*** ServerHello, TLSv1.2
RandomCookie: random_bytes = {5A 4E F9 E3 0C C5 C3 FE B6 50 ED 3E 40 2D 5D 75 27 12 B7 C0 FB CA C5 DD 6E 79 DB FF AE C8 32 63}
Session ID: {90, 78, 249, 227, 53, 127, 161, 141, 161, 33, 124, 107, 167, 128, 131, 252, 2, 170, 193, 168, 50, 40, 183, 150, 161, 217, 57, 214, 248, 78, 138, 158}
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Initialized: [Session-2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
** TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
update handshake state: server_hello[2]
upcoming handshake states: server certificate[11]
upcoming handshake states: server_key_exchange[12](optional)
upcoming handshake states: certificate_request[13](optional)
upcoming handshake states: server_hello_done[14]
upcoming handshake states: client certificate[11](optional)
upcoming handshake states: client_key_exchange[16]
upcoming handshake states: certificate_verify[15](optional)
upcoming handshake states: client change_cipher_spec[-1]
upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished[20]
check handshake state: certificate[11]
update handshake state: certificate[11]
upcoming handshake states: server_key_exchange[12](optional)
upcoming handshake states: certificate_request[13](optional)
upcoming handshake states: server_hello_done[14]
upcoming handshake states: client certificate[11](optional)
upcoming handshake states: client_key_exchange[16]
upcoming handshake states: certificate_verify[15](optional)
upcoming handshake states: client change_cipher_spec[-1]
upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished[20]
次にサーバーは、証明書チェーンを渡すことによってクライアントに自身の身元を証明します。この例では、発行者「Ficticious Widgets, Inc.」によって署名された、サブジェクト「localhost」の証明書を持っています。「Ficticious Widgets, Inc.」は信頼されるCAであることがわかっているので、証明書チェーンがX509TrustManagerによって正常に検証されれば、この接続を受け入れることができます。
信頼関係を確立する方法は数多く存在するので、デフォルトのX509TrustManagerが必要とする信頼管理を実行しない場合は、独自のX509TrustManagerをSSLContextに設定できます。
*** Certificate chain chain [0] = [ [ Version: V1 Subject: CN=localhost, OU=Widget Development Group, O="Ficticious Widgets, Inc.", L=Sunnyvale, ST=CA, C=US Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4 Key: Sun RSA public key, 1024 bits modulus: 143033747930138472247486887271140056230073515145002178273967677351609615363370290762769276637999437478831560066065849659757291378862662902990827165994331807749557591488695685710009935444457827978925129834206313905790623318272163063103561366855275108040861594659236689181320854743400330625121610498756440722549 public exponent: 65537 Validity: [From: Thu Jul 22 18:48:38 EDT 2004, To: Sun May 22 18:48:38 EDT 2011] Issuer: CN=localhost, OU=Widget Development Group, O="Ficticious Widgets, Inc.", L=Sunnyvale, ST=CA, C=US SerialNumber: [ 41004446] ] Algorithm: [MD5withRSA] Signature: 0000: BB 83 25 91 F3 8F 20 B5 C3 BD E4 FE B1 AB E8 CD ..%... ......... 0010: 6F 5C C4 13 86 E9 AB 3E 97 DC AC BF D6 5A 38 42 o\.....>.....Z8B 0020: 39 70 CD 56 F8 82 7B B7 D7 9F 2C 40 52 2B 32 37 9p.V......,@R+27 0030: 5E B0 ED 50 5D D4 F2 5A 80 C6 8D FD 01 FA 8E 2B ^..P]..Z.......+ 0040: 4E 4E DB D8 96 75 CC BE B7 69 49 22 EC 8A B1 58 NN...u...iI"...X 0050: E6 7E A9 9F 0B 4F 77 4E EF 89 B0 8D 98 B9 2E E0 .....OwN........ 0060: 4D 08 26 13 C5 2E 12 6B 7D 64 4A C9 89 C5 A6 D9 M.&....k.dJ..... 0070: CF 85 AB 27 D3 C9 CE 4C 85 0A 5E B7 B8 0E B1 63 ...'...L..^....c ]
この証明書を認識します。証明書を信頼してハンドシェークを続行できます。
*** Found trusted certificate: [ [ Version: V1 Subject: CN=localhost, OU=Widget Development Group, O="Ficticious Widgets, Inc.", L=Sunnyvale, ST=CA, C=US Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4 Key: Sun RSA public key, 1024 bits modulus: 143033747930138472247486887271140056230073515145002178273967677351609615363370290762769276637999437478831560066065849659757291378862662902990827165994331807749557591488695685710009935444457827978925129834206313905790623318272163063103561366855275108040861594659236689181320854743400330625121610498756440722549 public exponent: 65537 Validity: [From: Thu Jul 22 18:48:38 EDT 2004, To: Sun May 22 18:48:38 EDT 2011] Issuer: CN=localhost, OU=Widget Development Group, O="Ficticious Widgets, Inc.", L=Sunnyvale, ST=CA, C=US SerialNumber: [ 41004446] ] Algorithm: [MD5withRSA] Signature: 0000: BB 83 25 91 F3 8F 20 B5 C3 BD E4 FE B1 AB E8 CD ..%... ......... 0010: 6F 5C C4 13 86 E9 AB 3E 97 DC AC BF D6 5A 38 42 o\.....>.....Z8B 0020: 39 70 CD 56 F8 82 7B B7 D7 9F 2C 40 52 2B 32 37 9p.V......,@R+27 0030: 5E B0 ED 50 5D D4 F2 5A 80 C6 8D FD 01 FA 8E 2B ^..P]..Z.......+ 0040: 4E 4E DB D8 96 75 CC BE B7 69 49 22 EC 8A B1 58 NN...u...iI"...X 0050: E6 7E A9 9F 0B 4F 77 4E EF 89 B0 8D 98 B9 2E E0 .....OwN........ 0060: 4D 08 26 13 C5 2E 12 6B 7D 64 4A C9 89 C5 A6 D9 M.&....k.dJ..... 0070: CF 85 AB 27 D3 C9 CE 4C 85 0A 5E B7 B8 0E B1 63 ...'...L..^....c ] check handshake state: server_key_exchange[12] update handshake state: server_key_exchange[12] upcoming handshake states: certificate_request[13](optional) upcoming handshake states: server_hello_done[14] upcoming handshake states: client certificate[11](optional) upcoming handshake states: client_key_exchange[16] upcoming handshake states: certificate_verify[15](optional) upcoming handshake states: client change_cipher_spec[-1] upcoming handshake states: client finished[20] upcoming handshake states: server change_cipher_spec[-1] upcoming handshake states: server finished[20] *** ECDH ServerKeyExchange Signature Algorithm SHA512withRSA Server key: Sun EC public key, 256 bits public x coord: 63946569761028817730470946709618245239665208702310658540834640572976024458986 public y coord: 95117373197468111348087774631874920178563710193262620110729461870051247081115 parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7) check handshake state: certificate_request[13]
サーバーは、共通名(CN=)「Duke」を持つX509証明書の被認証者により、自身の身元を証明するようクライアントに要求しています。サーバーのX509TrustManagerには、クライアントによって提供される資格、または資格がない場合を拒否するオプションがあります。実際の使用状況では、CAによって署名された証明書を使用することがほとんどであり、代わりに、信頼されるCAのリストがこのメッセージに含まれています。
*** CertificateRequest Cert Types: RSA, DSS, ECDSA Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, Unknown (hash:0x3, signature:0x3), Unknown (hash:0x3, signature:0x1), SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA Cert Authorities: <CN=VeriSign Class 3 Public Primary Certification Authority - G3, OU="(c) 1999 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US> <CN=VeriSign Class 2 Public Primary Certification Authority - G3, OU="(c) 1999 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US> <OU=Equifax Secure eBusiness CA-2, O=Equifax Secure, C=US> <CN=Equifax Secure eBusiness CA-1, O=Equifax Secure Inc., C=US> <EMAILADDRESS=server-certs@thawte.com, CN=Thawte Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA> <CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.", L=Cupertino, ST=CA, C=US> <CN=VeriSign Class 1 Public Primary Certification Authority - G3, OU="(c) 1999 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US> <OU=Class 2 Public Primary Certification Authority, O="VeriSign, Inc.", C=US> <CN=Baltimore CyberTrust Code Signing Root, OU=CyberTrust, O=Baltimore, C=IE> <EMAILADDRESS=personal-freemail@thawte.com, CN=Thawte Personal Freemail CA, OU=Certification Services Division, O=Thawte Consulting, L=Cape Town, ST=Western Cape, C=ZA> <OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 3 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US> <CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US> <EMAILADDRESS=personal-premium@thawte.com, CN=Thawte Personal Premium CA, OU=Certification Services Division, O=Thawte Consulting, L=Cape Town, ST=Western Cape, C=ZA> <EMAILADDRESS=personal-basic@thawte.com, CN=Thawte Personal Basic CA, OU=Certification Services Division, O=Thawte Consulting, L=Cape Town, ST=Western Cape, C=ZA> <CN=GTE CyberTrust Root, O=GTE Corporation, C=US> <OU=Class 3 Public Primary Certification Authority, O="VeriSign, Inc.", C=US> <CN=localhost, OU=Widget Development Group, O="Ficticious Widgets, Inc.", L=Sunnyvale, ST=CA, C=US> <CN=Entrust.net Client Certification Authority, OU=(c) 2000 Entrust.net Limited, OU=www.entrust.net/GCCA_CPS incorp. by ref. (limits liab.), O=Entrust.net> <CN=GTE CyberTrust Global Root, OU="GTE CyberTrust Solutions, Inc.", O=GTE Corporation, C=US> <CN=Entrust.net Client Certification Authority, OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/Client_CA_Info/CPS incorp. by ref. limits liab., O=Entrust.net, C=US> <OU=Equifax Secure Certificate Authority, O=Equifax, C=US> <CN=Entrust.net Secure Server Certification Authority, OU=(c) 2000 Entrust.net Limited, OU=www.entrust.net/SSL_CPS incorp. by ref. (limits liab.), O=Entrust.net> <OU=Secure Server Certification Authority, O="RSA Data Security, Inc.", C=US> <OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 2 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US> <EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA> <CN=GeoTrust Global CA, O=GeoTrust Inc., C=US> <OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 1 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US> <CN=Baltimore CyberTrust Root, OU=CyberTrust, O=Baltimore, C=IE> <CN=Entrust.net Secure Server Certification Authority, OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), O=Entrust.net, C=US> <OU=Class 1 Public Primary Certification Authority, O="VeriSign, Inc.", C=US> <CN=Entrust.net Certification Authority (2048), OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), O=Entrust.net> <CN=GTE CyberTrust Root 5, OU="GTE CyberTrust Solutions, Inc.", O=GTE Corporation, C=US> update handshake state: certificate_request[13] upcoming handshake states: server_hello_done[14] upcoming handshake states: client certificate[11](optional) upcoming handshake states: client_key_exchange[16] upcoming handshake states: certificate_verify[15](optional) upcoming handshake states: client change_cipher_spec[-1] upcoming handshake states: client finished[20] upcoming handshake states: server change_cipher_spec[-1] upcoming handshake states: server finished[20] check handshake state: server_hello_done[14] update handshake state: server_hello_done[14] upcoming handshake states: client certificate[11](optional) upcoming handshake states: client_key_exchange[16] upcoming handshake states: certificate_verify[15](optional) upcoming handshake states: client change_cipher_spec[-1] upcoming handshake states: client finished[20] upcoming handshake states: server change_cipher_spec[-1] upcoming handshake states: server finished[20] *** ServerHelloDone
クライアントのクレデンシャルをサーバーに返信し、クライアントのX509KeyManagerが調べられるようにする必要があります。受け入れられる発行者のリスト(上述)と、KeyStoreに保管された証明書が一致することが期待されます。この場合は(運良く)一致しました。これで、「duke」としてクレデンシャルを有することになります。これらのクレデンシャルが受け入れられるかどうかは、サーバーのX509TrustManagerしだいです。
matching alias: duke *** Certificate chain chain [0] = [ [ Version: V1 Subject: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.", L=Cupertino, ST=CA, C=US Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4 Key: Sun RSA public key, 1024 bits modulus: 134968166047563266914058280571444028986498087544923991226919517667593269213420979048109900052353578998293280426361122296881234393722020704208851688212064483570055963805034839797994154526862998272017486468599962268346037652120279791547218281230795146025359480589335682217749874703510467348902637769973696151441 public exponent: 65537 Validity: [From: Tue May 22 19:46:46 EDT 2001, To: Sun May 22 19:46:46 EDT 2011] Issuer: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.", L=Cupertino, ST=CA, C=US SerialNumber: [ 3b0afa66] ] Algorithm: [MD5withRSA] Signature: 0000: 5F B5 62 E9 A0 26 1D 8E A2 7E 7C 02 08 36 3A 3E _.b..&.......6:> 0010: C9 C2 45 03 DD F9 BC 06 FC 25 CF 30 92 91 B1 4E ..E......%.0...N 0020: 62 17 08 48 14 68 80 CF DD 89 11 EA 92 7F CE DD b..H.h.......... 0030: B4 FD 12 A8 71 C7 9E D7 C3 D0 E3 BD BB DE 20 92 ....q......... . 0040: C2 3B C8 DE CB 25 23 C0 8B B6 92 B9 0B 64 80 63 .;...%#......d.c 0050: D9 09 25 2D 7A CF 0A 31 B6 E9 CA C1 37 93 BC 0D ..%-z..1....7... 0060: 4E 74 95 4F 58 31 DA AC DF D8 BD 89 BD AF EC C8 Nt.OX1.......... 0070: 2D 18 A2 BC B2 15 4F B7 28 6F D3 00 E1 72 9B 6C -.....O.(o...r.l ] *** update handshake state: certificate[11] upcoming handshake states: client_key_exchange[16] upcoming handshake states: certificate_verify[15](optional) upcoming handshake states: client change_cipher_spec[-1] upcoming handshake states: client finished[20] upcoming handshake states: server change_cipher_spec[-1] upcoming handshake states: server finished[20] *** ECDHClientKeyExchange ECDH Public value: { 4, 122, 237, 144, 182, 238, 209, 254, 65, 55, 177, 247, 57, 161, 72, 21, 29, 94, 215, 195, 63, 129, 193, 247, 74, 136, 229, 16, 8, 243, 189, 119, 9, 138, 167, 148, 227, 237, 217, 160, 220, 105, 193, 152, 164, 23, 104, 56, 164, 67, 49, 118, 182, 237, 49, 113, 35, 239, 236, 170, 58, 208, 168, 84, 90 } update handshake state: client_key_exchange[16] upcoming handshake states: certificate_verify[15](optional) upcoming handshake states: client change_cipher_spec[-1] upcoming handshake states: client finished[20] upcoming handshake states: server change_cipher_spec[-1] upcoming handshake states: server finished[20]
この暗号化方式群の場合は、サーバーとクライアントの間で共有する秘密鍵を確立するために使用される、ECDHClientKeyExchangeと呼ばれるメッセージを渡す必要があります。
このデータ全体は最終的に収集され、rawデバイスに書き込まれます。
*** ECDHClientKeyExchange
ECDH Public value: { 4, 122, 237, 144, 182, 238, 209, 254, 65, 55, 177, 247, 57, 161, 72, 21, 29, 94, 215, 195, 63, 129, 193, 247, 74, 136, 229, 16, 8, 243, 189, 119, 9, 138, 167, 148, 227, 237, 217, 160, 220, 105, 193, 152, 164, 23, 104, 56, 164, 67, 49, 118, 182, 237, 49, 113, 35, 239, 236, 170, 58, 208, 168, 84, 90 }
update handshake state: client_key_exchange[16]
upcoming handshake states: certificate_verify[15](optional)
upcoming handshake states: client change_cipher_spec[-1]
upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished[20]
main, WRITE: TLSv1.2 Handshake, length = 690
[Raw write]: length = 695
0000: 16 03 03 02 B2 0B 00 02 68 00 02 65 00 02 62 30 ........h..e..b0
0010: 82 02 5E 30 82 01 C7 02 04 3B 0A FA 66 30 0D 06 ..^0.....;..f0..
0020: 09 2A 86 48 86 F7 0D 01 01 04 05 00 30 76 31 0B .*.H........0v1.
...
この時点で、実際の秘密鍵を生成するために必要な情報がすべてそろいます。
SESSION KEYGEN:
PreMaster Secret:
0000: 7B 30 E5 1B 16 56 24 A8 48 A1 14 22 18 80 6B 37 .0...V$.H.."..k7
0010: 33 87 5B AC 88 7E 3A AF 75 62 30 48 DA 6F 52 4E 3.[...:.ub0H.oRN
CONNECTION KEYGEN:
Client Nonce:
0000: BA FA 1D F1 56 A3 7C FF B9 43 76 71 98 F5 A9 B4 ....V....Cvq....
0010: 0E 6B DF 7B 52 B8 F3 92 CC F4 C1 8A AA 71 8A 3F .k..R........q.?
Server Nonce:
0000: 5A 4E F9 E3 0C C5 C3 FE B6 50 ED 3E 40 2D 5D 75 ZN.......P.>@-]u
0010: 27 12 B7 C0 FB CA C5 DD 6E 79 DB FF AE C8 32 63 '.......ny....2c
Master Secret:
0000: A0 DD C7 3A 6C CD CF 01 9E 8D 38 E6 74 D0 93 51 ...:l.....8.t..Q
0010: CD E4 DB AE 3C BB 64 5D 3D 9D 1C 24 14 A0 9E B8 ....<.d]=..$....
0020: 97 6B 8E 74 C5 6A 07 53 8D B0 5F CE 63 E6 2A B2 .k.t.j.S.._.c.*.
... no MAC keys used for this cipher
Client write key:
0000: 03 81 01 4E 4B 73 AC 71 A7 FC EF 32 99 11 39 D7 ...NKs.q...2..9.
Server write key:
0000: C0 41 60 66 69 0A E0 62 AE CC CA 59 55 84 13 BE .A`fi..b...YU...
Client write IV:
0000: 92 A8 F6 29 ...)
Server write IV:
0000: A3 81 9D 8A
サーバーに簡単な確認メッセージを送信し、送信したばかりのクライアント証明書に対応する非公開鍵を持っていることを確証します。
*** CertificateVerify
Signature Algorithm SHA512withRSA
update handshake state: certificate_verify[15]
upcoming handshake states: client change_cipher_spec[-1]
upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished[20]
main, WRITE: TLSv1.2 Handshake, length = 136
[Raw write]: length = 141
0000: 16 03 03 00 88 0F 00 00 84 06 01 00 80 21 D7 EB .............!..
0010: 85 55 D2 18 10 34 4A 67 62 35 67 E0 FD D7 76 0F .U...4Jgb5g...v.
0020: 72 4D 12 C5 9D 9D 2A DF AD 61 8D 8E E0 4D 20 47 rM....*..a...M G
0030: F9 19 D5 AC A8 40 25 8E C3 71 E9 07 E1 04 C8 A8 .....@%..q......
0040: 42 20 2D 9C EB 4A 64 8B 20 BD 72 B5 BB 75 88 8B B -..Jd. .r..u..
0050: FF 63 13 B5 20 8B F3 93 3C 55 BD A0 E4 9B B1 C0 .c.. ...<U......
0060: 26 14 32 79 29 C9 C4 63 4F 95 F5 8F B5 56 B6 96 &.2y)..cO....V..
0070: B3 E3 72 CC 6A A2 C7 3F 7D DD 54 9B FF 1E 66 2D ..r.j..?..T...f-
0080: 2C 8F 87 3E 6D 95 A0 61 B3 72 6B C9 36 ,..>m..a.rk.6
update handshake state: change_cipher_spec
upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished[20]
大部分が終了しました。新しく確立した符号化方式に変更することをサーバーに知らせます。それ以後のすべてのメッセージは、確立したばかりのパラメータを使用して暗号化されます。暗号化された終了メッセージを送信して、すべての処理が終了したことを確証します。
main, WRITE: TLSv1.2 Change Cipher Spec, length = 1 [Raw write]: length = 6 0000: 14 03 03 00 01 01 ...... *** Finished verify_data: { 203, 239, 43, 159, 111, 5, 204, 127, 182, 48, 181, 121 } *** update handshake state: finished[20] upcoming handshake states: server change_cipher_spec[-1] upcoming handshake states: server finished[20] main, WRITE: TLSv1.2 Handshake, length = 24 Padded plaintext before ENCRYPTION: len = 16 0000: 14 00 00 0C CB EF 2B 9F 6F 05 CC 7F B6 30 B5 79 ......+.o....0.y
上記メッセージが、5バイトのヘッダー情報に続いてraw出力デバイスに実際に書き込まれるときには、メッセージは暗号化されます。
[Raw write]: length = 45 0000: 16 03 03 00 28 00 00 00 00 00 00 00 00 A9 56 B7 ....(.........V. 0010: 91 8D A5 4E 3E AD 9F 68 7D 8F 5D 69 C3 B6 63 C9 ...N>..h..]i..c. 0020: 83 CC D5 1C 1E A2 09 A4 5F 19 38 2B 35 ........_.8+5
サーバーが同じChange Cipher Spec/Finishedメッセージを送信するのを待ちます。これにより、ネゴシエーションが正常に完了したことがわかります。
[Raw read]: length = 5 0000: 14 03 03 00 01 ..... [Raw read]: length = 1 0000: 01 . main, READ: TLSv1.2 Change Cipher Spec, length = 1 update handshake state: change_cipher_spec upcoming handshake states: server finished[20] [Raw read]: length = 5 0000: 16 03 03 00 28 ....( [Raw read]: length = 40 0000: 00 00 00 00 00 00 00 00 7C 67 B6 32 AC 15 50 98 .........g.2..P. 0010: FE A6 4E E1 A0 4B E8 D3 84 DE 82 02 32 DF 54 13 ..N..K......2.T. 0020: 57 24 66 55 4D 49 92 FB W$fUMI.. main, READ: TLSv1.2 Handshake, length = 40 Padded plaintext after DECRYPTION: len = 16 0000: 14 00 00 0C 3A 78 03 E5 A4 9A 9D ED 9D 0E 78 55 ....:x........xU check handshake state: finished[20] update handshake state: finished[20] *** Finished verify_data: { 58, 120, 3, 229, 164, 154, 157, 237, 157, 14, 120, 85 } ***
すべてが正常に完了しました。この接続が切断されたあと、もう一度このセッションを確立する場合に備えて、確立されたセッションをキャッシュします。
この時点で、SSL/TLSクライアントはピアの資格を調べ、意図するサーバーと通信していることを確認します。HttpsURLConnectionは、問題が生じた場合にホスト名とcall HostnameVerifierをチェックしますが、raw SSLSocketはチェックしません。この検証動作は手動で行いますが、ここでは省略します。
ここまでで、アプリケーション・データを交換する準備が整います。GET /index.html HTTP1.0コマンドを送信します。
%% Cached client session: [Session-2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256] main, WRITE: TLSv1.2 Application Data, length = 36 Padded plaintext before ENCRYPTION: len = 28 0000: 47 45 54 20 2F 69 6E 64 65 78 2E 68 74 6D 6C 20 GET /index.html 0010: 48 54 54 50 2F 31 2E 30 0D 0A 0D 0A HTTP/1.0....
送信中のデータは、5バイトのヘッダー情報をスキップして暗号化されています。
[Raw write]: length = 57
0000: 17 03 03 00 34 00 00 00 00 00 00 00 01 62 BA 68 ....4........b.h
0010: 66 BB 0E FC 6D C4 13 43 C5 98 A1 EC 73 96 0C 28 f...m..C....s..(
0020: 72 4F BB FD 44 85 D5 03 40 82 0B F8 D3 9B B2 4A rO..D...@......J
0030: 07 6B 0A B8 90 8F DC 82 41 .k......A
アプリケーション・データを取り戻します。最初にHTTPSヘッダー、次に実際のデータが戻されます。
[Raw read]: length = 5 0000: 17 03 03 00 5A ....Z [Raw read]: length = 90 0000: 00 00 00 00 00 00 00 01 C4 87 18 10 4F 64 C2 B0 ............Od.. 0010: A3 CE 53 C6 3D 0C 0C 5E E6 FE D2 05 64 39 C6 5C ..S.=..^....d9.\ 0020: BC BE 1B B2 C3 5D E0 5C B9 34 AD B7 E3 2D 79 08 .....].\.4...-y. 0030: B0 88 D6 33 89 46 23 4C 4D A0 9D 6C AA 79 3C 61 ...3.F#LM..l.y<a 0040: 27 39 65 7D 91 6C 08 C4 FB DD 1F 27 3D 3F 53 D1 '9e..l.....'=?S. 0050: 2A 7C 26 5F 6A 11 05 F4 FF 6B *.&_j....k main, READ: TLSv1.2 Application Data, length = 90 Padded plaintext after DECRYPTION: len = 66 0000: 48 54 54 50 2F 31 2E 30 20 32 30 30 20 4F 4B 0D HTTP/1.0 200 OK. 0010: 0A 43 6F 6E 74 65 6E 74 2D 4C 65 6E 67 74 68 3A .Content-Length: 0020: 20 32 35 37 37 0D 0A 43 6F 6E 74 65 6E 74 2D 54 2577..Content-T 0030: 79 70 65 3A 20 74 65 78 74 2F 68 74 6D 6C 0D 0A ype: text/html.. 0040: 0D 0A .. HTTP/1.0 200 OK Content-Length: 2577 Content-Type: text/html [Raw read]: length = 5 0000: 17 03 03 0A 29 ....) [Raw read]: length = 1024 ... [Raw read]: length = 1024 ... [Raw read]: length = 553 ... main, READ: TLSv1.2 Application Data, length = 2601 Padded plaintext after DECRYPTION: len = 2577 0000: 3C 21 44 4F 43 54 59 50 45 20 68 74 6D 6C 20 50 <!DOCTYPE html P 0010: 55 42 4C 49 43 20 22 2D 2F 2F 57 33 43 2F 2F 44 UBLIC "-//W3C//D 0020: 54 44 20 58 48 54 4D 4C 20 31 2E 30 20 54 72 61 TD XHTML 1.0 Tra 0030: 6E 73 69 74 69 6F 6E 61 6C 2F 2F 45 4E 22 0A 20 nsitional//EN". 0040: 20 20 20 22 68 74 74 70 3A 2F 2F 77 77 77 2E 77 "http://www.w 0050: 33 2E 6F 72 67 2F 54 52 2F 78 68 74 6D 6C 31 2F 3.org/TR/xhtml1/ 0060: 44 54 44 2F 78 68 74 6D 6C 31 2D 74 72 61 6E 73 DTD/xhtml1-trans 0070: 69 74 69 6F 6E 61 6C 2E 64 74 64 22 3E 0A 3C 68 itional.dtd">.<h 0080: 74 6D 6C 20 6C 61 6E 67 3D 22 65 6E 2D 55 53 22 tml lang="en-US" 0090: 20 78 6D 6C 6E 73 3D 22 68 74 74 70 3A 2F 2F 77 xmlns="http://w 00A0: 77 77 2E 77 33 2E 6F 72 67 2F 31 39 39 39 2F 78 ww.w3.org/1999/x 00B0: 68 74 6D 6C 22 20 78 6D 6C 3A 6C 61 6E 67 3D 0A html" xml:lang=. 00C0: 22 65 6E 2D 55 53 22 3E 0A 3C 68 65 61 64 3E 0A "en-US">.<head>. 00D0: 3C 74 69 74 6C 65 3E 4A 53 53 45 20 53 61 6D 70 <title>JSSE Samp 00E0: 6C 65 20 43 6F 64 65 3C 2F 74 69 74 6C 65 3E 0A le Code</title>. ... 0A00: 0A 3C 2F 62 6F 64 79 3E 0A 3C 2F 68 74 6D 6C 3E .</body>.</html> 0A10: 0A . <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html lang="en-US" xmlns="http://www.w3.org/1999/xhtml" xml:lang= "en-US"> <head> <title>JSSE Sample Code</title> </head> <body> <center> <h1>JSSE Sample Code</h1> </center> <p>This directory contains subdirectories with JSSE sample files.</p> ... </body> </html>
ソケットからもう一度読み出し、さらにデータがあるかどうかを確認します。close_notifyメッセージを受け取ります。このメッセージは、この接続が正常に切断されることを意味します。応答メッセージを返し、ソケットを閉じます。
[Raw read]: length = 5 0000: 15 03 03 00 1A ..... [Raw read]: length = 26 0000: 00 00 00 00 00 00 00 03 6E D8 FA E8 B7 A7 01 7F ........n....... 0010: EB C1 88 DC 30 34 BC 57 31 6D ....04.W1m main, READ: TLSv1.2 Alert, length = 26 Padded plaintext after DECRYPTION: len = 2 0000: 01 00 .. main, RECV TLSv1.2 ALERT: warning, close_notify main, called closeInternal(false) main, SEND TLSv1.2 ALERT: warning, description = close_notify main, WRITE: TLSv1.2 Alert, length = 10 Padded plaintext before ENCRYPTION: len = 2 0000: 01 00 .. [Raw write]: length = 31 0000: 15 03 03 00 1A 00 00 00 00 00 00 00 02 AA B8 0A ................ 0010: A1 3F 72 58 22 71 07 0B 83 DD 65 8B 6F 78 3B .?rX"q....e.ox; main, called closeSocket(false) main, called close() main, called closeInternal(true) main, called close() main, called closeInternal(true) main, called close() main, called closeInternal(true)
このセクションには次のコード例が含まれています。
JSSEを使用してセキュアでないソケット接続をセキュアなソケット接続に変換する方法を示すコード例です。このコード例は、『Java SE 6ネットワーク・セキュリティ』(Marco Pistoia他著)から引用したものです。
例8-26では、セキュアでないソケットを使用してクライアントとサーバーとの間の通信を設定するために使用できるサンプル・コードを示します。次に例8-27ではこのコードを変更し、JSSEを使用してセキュアなソケット通信を設定します。
例8-26 SSLを使用しないソケットの例
次の例では、セキュアでないソケット接続を設定するサーバー側およびクライアント側のコードを示します。
サーバーとして動作し、ソケットを使用してクライアントと通信するJavaプログラムでは、次のようなコードでソケット通信を設定します。
import java.io.*; import java.net.*; . . . int port = availablePortNumber; ServerSocket s; try { s = new ServerSocket(port); Socket c = s.accept(); OutputStream out = c.getOutputStream(); InputStream in = c.getInputStream(); // Send messages to the client through // the OutputStream // Receive messages from the client // through the InputStream } catch (IOException e) { }
ソケットを使ってサーバーとの通信を設定するクライアント・コードの例を、次に示します。
import java.io.*; import java.net.*; . . . int port = availablePortNumber; String host = "hostname"; try { s = new Socket(host, port); OutputStream out = s.getOutputStream(); InputStream in = s.getInputStream(); // Send messages to the server through // the OutputStream // Receive messages from the server // through the InputStream } catch (IOException e) { }
例8-27 SSLを使用するソケットの例
次の例では、セキュアなソケット接続を設定するサーバー側およびクライアント側のコードを示します。
サーバーとして動作し、セキュアなソケットを使用してクライアントと通信するJavaプログラムでは、次のようなコードでソケット通信を設定します。セキュアでないソケットを使った通信のプログラムとこのプログラムとの違いは、太字で示されています。
import java.io.*; import javax.net.ssl.*; . . . int port = availablePortNumber; SSLServerSocket s; try { SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); s = (SSLServerSocket)sslSrvFact.createServerSocket(port); SSLSocket c = (SSLSocket)s.accept(); OutputStream out = c.getOutputStream(); InputStream in = c.getInputStream(); // Send messages to the client through // the OutputStream // Receive messages from the client // through the InputStream } catch (IOException e) { }
セキュアなソケットを使ってサーバーとの通信を設定するクライアント・コードの例を、次に示します。セキュアでないソケットとの違いは、太字で示されています。
import java.io.*; import javax.net.ssl.*; . . . int port = availablePortNumber; String host = "hostname"; try { SSLSocketFactory sslFact = (SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket s = (SSLSocket)sslFact.createSocket(host, port); OutputStream out = s.getOutputStream(); InputStream in = s.getInputStream(); // Send messages to the server through // the OutputStream // Receive messages from the server // through the InputStream } catch (IOException e) { }
JSSEサンプル・プログラムでは、JSSEの使用方法を示します。
サンプル・コードを使用する場合、サンプル・プログラムはJSSEの使用法を示すために設計されていることに注意してください。堅牢なアプリケーションを開発するためのものではありません。
注意:
セキュアな通信を設定すると、アルゴリズムが複雑になります。サンプル・プログラムでは、設定プロセス中のフィードバックがありません。プログラムの実行時は、少し待ってください。しばらく出力が表示されない場合があります。javax.net.debug
システム・プロパティをall
に設定して、プログラムを実行すると、フィードバックが表示されます。このデバッグ情報の参照方法の概要は、SSL/TLS接続のデバッグを参照してください。サンプル・コードの場所
JDK 8ドキュメント内のJSSEサンプル・コードではすべてのサンプル・コード・ファイルとテキスト・ファイルをリストします。そのページには、すべてのサンプル・コード・ファイルをダウンロードできるZIPファイルへのリンクも記載されています。
次のセクションでは、サンプルについて説明します。
クライアントとサーバーのセキュアなソケット接続を表すサンプル・コード
samples/sockets
ディレクトリにあるサンプル・プログラムは、クライアントとサーバーとの間でセキュアなソケット接続を設定する方法を示しています。
サンプルのクライアント・プログラムを実行中に、Webサーバーなどの既存のサーバーと通信できます。または、サンプル・サーバー・プログラムClassFileServer
と通信することもできます。サンプルのクライアント・プログラムとサーバー・プログラムは、同じネットワークに接続された別個のマシンで実行することも、1台のマシンで、ただし別のターミナル・ウィンドウから両方を実行することもできます。
amples/sockets/clientディレクトリのサンプルSSLSocketClient*
プログラム(および「HTTPS接続を表すサンプル・コード」で説明するURLReader*
プログラム)は、すべてClassFileServer
サンプル・サーバー・プログラムで実行できます。これを実行する方法の例は、「ClassFileServerを使用したSSLSocketClientWithClientAuthの実行」に示しています。同様の変更をして、URLReader
、SSLSocketClient
またはSSLSocketClientWithTunneling
をClassFileServer
で実行できます。
クライアントとサーバーとの間で通信中に認証エラーが発生する場合(WebサーバーとClassFileServer
のどちらを使用しているか関係なく)、必要な鍵がトラストストア(トラスト鍵データベース)にないためである可能性があります。用語と定義を参照してください。たとえば、ClassFileServer
は、SSLハンドシェーク中に必要なlocalhost
の公開鍵を格納するtestkeys
というキーストアを使用します。testkeys
キーストアはClassFileServer
ソースと同じsamples/sockets/serverディレクトリにあります。参照するトラストストアで、localhost
の対応する公開鍵の証明書をクライアントが見つけられない場合、認証エラーが発生します。次のセクションで説明するように、samplecacerts
トラストストア(localhost
の公開鍵と証明書を格納する)を使用してください。
構成要件
クライアントとサーバーとの間のセキュアなソケット接続を作成するサンプル・プログラムを実行する場合は、適切な証明書ファイル(トラストストア)を利用できるようにしておく必要があります。クライアント・プログラムとサーバー・プログラムの両方で、samples
ディレクトリのsamplecacerts
証明書ファイルを使用してください。この証明書ファイルを使用すると、クライアントがサーバーを認証できるようになります。このファイルには、JDK (cacertsファイルにある)に付属する一般的な証明書発行局(CA)のすべての証明書と、サンプルサーバーClassFileServer
との通信時にクライアントがlocalhost
を認証するために必要なlocalhost
の証明書が含まれています。ClassFileServer
は、samplecacerts
の公開鍵に対応するlocalhost
の公開鍵を含むキーストアを使用します。
クライアントとサーバーの両方でsamplecacerts
ファイルを使用できるようにするには、それをjava-home/lib/security/jssecacerts
ファイルにコピーして名前をcacertsに変更し、java-home/lib/security/cacerts
ファイルと置き換えます。あるいは、クライアントとサーバーの両方に対してjava
コマンドを実行する場合は、次のオプションをコマンド行に追加します。
-Djavax.net.ssl.trustStore=path_to_samplecacerts_file
java-homeの詳細は、用語と定義を参照してください。
samplecacerts
トラスト・ストアのパスワードはchangeit
です。keytool
ユーティリティを使用して、サンプルの独自の証明書を置き換えることができます。
Mozilla FirefoxやMicrosoft Internet Explorerなどのブラウザを使用して、ClassFileServer
の例で提供されているサンプルのSSLサーバーにアクセスすると、ダイアログ・ボックスが開いて、証明書が認識されないというメッセージが表示されます。これは、サンプル・プログラムで使用する証明書は自己署名付きのもので、テスト用にすぎないためです。現在のセッションで証明書に同意できます。SSLサーバーのテストが終了したあと、ブラウザを終了し、ブラウザの名前空間からテスト用証明書を削除します。
クライアント認証の場合、適切なディレクトリの別のduke
証明書を使用できます。公開鍵および証明書も、samplecacerts
ファイルに格納されています。
SSLSocketClientの実行
JDK 8ドキュメント内のJSSEサンプル・コードのSSLSocketClient.java
プログラムは、SSLSocket
を使用してHTTP要求を送信しHTTPSサーバーから応答を受け取るクライアントの作成方法を示します。このプログラムの出力は、https://www.verisign.com/index.html
のHTMLソースです。
ファイアウォールの内側から、このプログラムを提供されたとおりに実行しないでください。ファイアウォールの内側から実行すると、JSSEはファイアウォールを経由したwww.verisign.com
へのパスを検出できないので、UnknownHostException
を受け取ります。ファイアウォールの外側から実行できる同等のクライアントを作成するには、サンプル・プログラムSSLSocketClientWithTunneling
で示すように、プロキシ・トンネリングを設定します。
SSLSocketClientWithTunnelingの実行
JDK 8ドキュメント内のJSSEサンプル・コードのSSLSocketClientWithTunneling.java
プログラムは、ファイアウォールの外側からセキュアなWebサーバーにアクセスするプロキシ・トンネリングの方法を示します。このプログラムを実行するには、次のJavaシステム・プロパティに適切な値を設定する必要があります。
java -Dhttps.proxyHost=webproxy -Dhttps.proxyPort=ProxyPortNumber SSLSocketClientWithTunneling
注意:
-D
オプションによるプロキシ指定はオプションです。webproxyは使用するプロキシ・ホスト名に、ProxyPortNumberは適切なポート番号に置き換えてください。プログラムはhttps://www.verisign.com/index.html
のHTMLソース・ファイルを返します。
SSLSocketClientWithClientAuthの実行
JDK 8ドキュメント内のJSSEサンプル・コードのSSLSocketClientWithClientAuth.java
プログラムは、サーバーから要求された場合にクライアント認証を行うように、キー・マネージャを設定する方法を示します。このプログラムも、クライアントがファイアウォールの外側にはいないことを前提にしています。SSLSocketClientWithTunneling
の例に従ってプログラムを変更すれば、ファイアウォールの内側から接続することもできます。
このプログラムを実行するには、ホスト、ポート、および要求されたファイル・パスの3つのパラメータを指定する必要があります。前の例を反映させるには、ホストにwww.verisign.com
、ポート番号に443
、要求されたファイル・パスにhttps://www.verisign.com/
を設定することで、このプログラムをクライアント認証なしで実行します。これらのパラメータを使用したときの出力が、Webサイトhttps://www.verisign.com/
のHTMLソースです。
SSLSocketClientWithClientAuth
を実行してクライアント認証を行うには、クライアント認証を要求するサーバーにアクセスする必要があります。このサーバーには、サンプル・プログラムClassFileServer
を使用できます。これについては、次のセクションで説明します。
ClassFileServerの実行
ここでClassFileServer
と呼ばれるプログラムは、JDK 8ドキュメント内のJSSEサンプル・コードのClassFileServer.java
とClassServer.java
という2つのファイルで構成されています。
これらを実行するには、ClassFileServer.class
を実行します。その際は次のパラメータが必要です。
port
は、利用できる未使用のポート番号です。たとえば、2001
のような数字を使用できます。docroot
は、取得するファイルを含むサーバーのディレクトリを表します。たとえば、Solarisでは、/home/userid/ (useridは特定のUIDを表す)を使用でき、Microsoft Windowsシステムではc:\を使用できます。TLS
は、サーバーがSSLを使用するかTLSを使用するかを示すオプションのパラメータです。true
は、クライアント認証が必要であることを示すオプションのパラメータです。このパラメータは、TLSパラメータが設定されているかどうかだけを参照します。注意:
TLS
パラメータとtrue
パラメータはオプションです。それらを省略した場合は、TLSではない通常のファイルサーバーを認証なしで使用し、何も起こらないことを示します。これは、一方の側(クライアント)がTLSとネゴシエーションを行おうとしても、もう一方の側(サーバー)は行おうとしないため、通信ができないからです。注意:
サーバーは、GET /path_to_file
の形式でのGET要求を予期しています。ClassFileServerによるSSLSocketClientWithClientAuthの実行
JDK 8ドキュメント内のJSSEサンプル・コードのサンプル・プログラムSSLSocketClientWithClientAuth.java
およびClassFileServer
を使用して、認証済の通信を設定できます。この通信では、クライアントとサーバーが相互に認証します。サンプルのプログラムは、同じネットワークに接続された別個のマシンで実行することも、1台のマシンで、ただし別のターミナル・ウィンドウまたはコマンド・プロンプト・ウィンドウから両方を実行することもできます。クライアントとサーバーの両方を設定するには、次を実行します。
ClassFileServer
プログラムを1つのマシンまたはターミナル・ウィンドウから実行します。 ClassFileServerの実行を参照してください。
SSLSocketClientWithClientAuth
プログラムを別のマシンやターミナル・ウィンドウで実行します。SSLSocketClientWithClientAuth
には、次のパラメータが必要です。 host
は、ClassFileServer
を実行するために使用するマシンのホスト名です。port
は、ClassFileServer
用に指定したものと同じポートです。requestedfilepath
は、サーバーから取得するファイルのパスを示します。このパラメータには、/filepath
と指定する必要があります。GET文の一部として使用されるので、ファイル・パスにはフォワード・スラッシュが必要です。GET文には、稼動中のオペレーティング・システムの種類にかかわらず、フォワード・スラッシュが必要です。文の構成は次のようになります。
"GET " + requestedfilepath + " HTTP/1.0"
注意:
ClassFileServer
が動作しているローカル・マシンに接続するように、他のSSLClient*
アプリケーションのGET
コマンドを変更できます。HTTPS接続を表すサンプル・コード
JSSEを介してセキュアな通信にアクセスするためのプライマリAPIは2つあります。1つの方法は、SSLSocketClient
、SSLSocketClientWithTunneling
およびSSLSocketClientWithClientAuth
(ClassFileServer
を使用するか使用しないで)に示すように、任意のセキュアな通信に使用できるソケット・レベルのAPIを使用することです。
2つ目はもっと簡単な方法で、標準のJava URL APIを使用します。java.net.URL
クラスを使用したHTTPS URLプロトコルまたはスキームを使用して、SSL対応のWebサーバーとセキュアに通信できます。
HTTPS URLスキームのサポートは一般的なブラウザの多くに実装されており、JSSEで提供されているソケット・レベルAPIを必要とせずにセキュアな通信にアクセスできます。
URLの例はhttps://www.verisign.com
です。
HTTPS URL実装のトラストおよび鍵の管理は、環境に固有です。JSSE実装は、HTTPS URL実装を提供します。別のHTTPSプロトコル実装を使用するには、java.protocol.handler.pkgs
を設定します。パッケージ名に対するjava.lang.Systemプロパティの指定方法を参照してください。詳細については、java.net.URL
クラスのドキュメントを参照してください。
JSSEでダウンロードできるサンプルには、HTTPS接続の作成方法を示すサンプル・プログラムが2つ含まれています。これらのサンプル・プログラム(URLReader.java
とURLReaderWithOptions.java
)は、どちらもsamples/urls
ディレクトリにあります。
URLReaderの実行
JDK 8ドキュメント内のJSSEサンプル・コードのURLReader.java
プログラムは、セキュアなサイトにアクセスするURLクラスの使用方法を示します。このプログラムの出力は、https://www.verisign.com/
のHTMLソースです。デフォルトで、JSSEに付属のHTTPSプロトコル実装が使用されます。別の実装を使用するには、システム・プロパティjava.protocol.handler.pkgs
の値を、実装を含むパッケージ名に設定します。
ファイアウォールの内側でサンプル・コードを実行している場合は、https.proxyHost
およびhttps.proxyPort
システム・プロパティを設定する必要があります。たとえば、ポート8080でプロキシ・ホスト「webproxy」を使用する場合は、java
コマンドで次のオプションを使用できます。
-Dhttps.proxyHost=webproxy -Dhttps.proxyPort=8080
または、java.lang.System
のメソッドsetProperty()
で、ソース・コード内のシステム・プロパティを設定することもできます。たとえば、コマンド行オプションを使用するかわりに、使用するプログラムに次の行を含めることができます。
System.setProperty("java.protocol.handler.pkgs", "com.ABC.myhttpsprotocol"); System.setProperty("https.proxyHost", "webproxy"); System.setProperty("https.proxyPort", "8080");
URLReaderWithOptionsの実行
JDK 8ドキュメント内のJSSEサンプル・コードのURLReaderWithOptions.java
プログラムは、基本的にはURLReader.java
プログラムと同じですが、実行時にプログラムの引数として次のシステム・プロパティのどれかまたはすべてをオプションで入力できる点が異なります。
java.protocol.handler.pkgs
https.proxyHost
https.proxyPort
https.cipherSuites
URLReaderWithOptions
を実行するには、次のコマンドを入力します。
java URLReaderWithOptions [-h proxyhost -p proxyport] [-k protocolhandlerpkgs] [-c ciphersarray]
注意:
複数のプロトコル・ハンドラを、縦線で区切った項目のリストでprotocolhandlerpkgs
引数に含めることができます。複数のSSL暗号化方式群名を、カンマで区切った項目のリストでciphersarray
引数に含めることができます。可能な暗号化方式群名はSSLSocket.getSupportedCipherSuites()
メソッドで返されるものと同じです。暗号群はSSLおよびTLSプロトコルの仕様から命名されています。protocolhandlerpkgs
引数は、Oracleが提供するデフォルト以外のHTTPSプロトコル・ハンドラ実装を使用する場合にのみ必要です。
ファイアウォールの内側でサンプル・コード実行している場合は、プロキシ・ホストおよびプロキシ・ポートの引数を含める必要があります。また、使用できる暗号群のリストを含めることもできます。
次に、URLReaderWithOptions
の実行例と、ポート8080にプロキシ・ポート「webproxy」を指定する場合の例を示します。
java URLReaderWithOptions -h webproxy -p 8080
セキュアなRMI接続を表すサンプル・コード
samples/rmi
ディレクトリ内のサンプル・コードは、セキュアなJava Remote Method Invocation (RMI)接続の作成方法を示しています。このサンプル・コードは、基本的には、カスタムRMIソケット・ファクトリをインストールして使用するように変更されたHello Worldサンプルです。
SSLEngineの使用を表すサンプル・コード
SSLEngine
は、アプリケーション開発者にI/Oおよび計算戦略を選択するときの柔軟性を提供します。SSLEngine
は、SSL/TLS実装を特定のI/O抽象化(シングル・スレッドSSLSockets
など)に結びつけるのではなく、I/Oおよび計算の制約をSSL/TLS実装から除外します。
前述したように、SSLEngine
は高度なAPIであり、不用意に使用することはできません。ここでは、その使用を説明するのに役立つ入門用サンプル・コードを示します。最初のデモは、ほとんどのI/Oおよびスレッドの発行を除外し、SSLEngineメソッドの多くに重点を置きます。2番目のデモは、より現実的な例であり、SSLEngine
がどのようにJava NIOと結合して基本的なHTTP/HTTPSサーバーを作成するかを示します。
SSLEngineSimpleDemoの実行
JDK 8ドキュメント内のJSSEサンプル・コードのSSLEngineSimpleDemo.java
プログラムは、入出力とスレッドの問題が簡素化されている、SSLEngine
の操作に重点を置いた非常に単純なアプリケーションです。このアプリケーションは、一般的なByteBuffer
オブジェクトによってSSL/TLSメッセージを交換する2つのSSLEngine
オブジェクトを作成します。1つのループがすべてのエンジン操作を順番に実行して、セキュアな接続の確立(ハンドシェーク)、アプリケーション・データの転送、およびエンジンのクローズを示します。
SSLEngineResult
は、SSLEngine
の現在の状態に関して多くの情報を提供します。この例では、すべての状態を調べているわけではありません。I/Oおよびスレッドの発行を適度に単純化しているため本番稼動環境に適した例ではありませんが、SSLEngine
の全体的な機能の説明に有用です。
NIOベースのサーバーの実行
SSLEngine
によって提供される柔軟性を十分に利用するには、最初にI/Oやスレッド・モデルなどの相補的なAPIを理解する必要があります。
大規模なアプリケーションの開発者が有用と考えるI/Oモデルは、NIO SocketChannel
です。NIOは、java.net.Socket
APIに内在するスケーリングの問題のいくつかを解決するために部分的に導入されました。SocketChannel
には、次のような様々な操作モードがあります。
新しい多くのNIO APIを示すだけでなく、セキュアなHTTPSサーバーを作成するためにSSLEngine
を採用する方法も示す、基本的なHTTPサーバーのサンプル・コードが提供されています。サーバーは本番稼動の品質ではありませんが、これらの多くの新しいAPIの動作を示しています。
サンプル・ディレクトリには、サーバーを紹介するREADME.txt
ファイルがあり、これには、サーバーの構築および構成方法の説明、コード・レイアウトの簡潔な概要が含まれています。SSLEngine
のユーザーにとってもっとも重要なファイルは、ChannelIO.java
およびChannelIOSecure.java
です。
注意:
この項で説明するサーバー例は、JDKに含まれています。コードは、jdk-home/samples/nio/server
ディレクトリにバンドルされています。keytool
ユーティリティを使用してJSSEでの使用に適した単純なPKCS12キーストアを作成する方法の手順を示します。
まずキーストア内に(公開鍵および秘密鍵を持つ) keyEntry
を作成し、トラストストア内に対応するtrustedCertEntry
(公開鍵のみ)を作成します。クライアント認証の場合、クライアントの証明書に対して同様の処理に従う必要があります。
注意:
PKCS12での信頼できるアンカーおよび秘密鍵の格納は、JDK 8以降でサポートされています。注意:
ここでは、各ステップに関する詳しい解説は省略します。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
キーストアを調べます。エントリの型はPrivatekeyEntry
で、これは、このエントリに秘密鍵が関連付けられていることを示します。
% keytool -list -v -keystore keystore Enter keystore password: <password> Keystore type: PKCS12 Keystore provider: SUN Your keystore contains 1 entry Alias name: duke Creation date: Jul 25, 2016 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Serial number: 210cccfc Valid from: Mon Jul 25 10:33:27 IST 2016 until: Mon Aug 01 10:33:27 IST 2016 Certificate fingerprints: SHA1: 80:E5:8A:47:7E:4F:5A:70:83:97:DD:F4:DA:29:3D:15:6B:2A:45:1F SHA256: ED:3C:70:68:4E:86:35:9C:63:CC:B9:59:35:58:94:1F:7E:B8:B0:EE:D2: 4B:9D:80:31:67:8A:D4:B4:7A:B5:12 Signature algorithm name: SHA256withRSA Subject Public Key Algorithm: RSA (2048) Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 7F C9 95 48 42 8D 68 91 BA 1E E6 5C 2C 6B FF 75 ...HB.h....\,k.u 0010: 5F 19 78 43 _.xC ] ]
自己署名付き証明書をエクスポートし、内容を調べます。
% keytool -export -alias duke -keystore keystore -rfc -file duke.cer Enter keystore password: <password> Certificate stored in file <duke.cer> % cat duke.cer -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEIQzM/DANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJV UzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEVMBMGA1UEChMMT3Jh Y2xlLCBJbmMuMRYwFAYDVQQLEw1KYXZhIFNvZnR3YXJlMQ0wCwYDVQQDEwREdWtl MB4XDTE2MDcyNTA1MDMyN1oXDTE2MDgwMTA1MDMyN1owbDELMAkGA1UEBhMCVVMx CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlQYWxvIEFsdG8xFTATBgNVBAoTDE9yYWNs ZSwgSW5jLjEWMBQGA1UECxMNSmF2YSBTb2Z0d2FyZTENMAsGA1UEAxMERHVrZTCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ7+Yeu6HDZgWwkGlG4iKH9w vGKrxXVR57FaFyheMevrgj1ovVnQVFhfdMvjPkjWmpqLg6rfTqU4bKbtoMWV6+Rn uQrCw2w9xNC93hX9PxRa20UKrSRDKnUSvi1wjlaxfj0KUKuMwbbY9S8x/naYGeTL lwbHiiMvkoFkP2kzhVgeqHjIwSz4HRN8vWHCwgIDFWX/ZlS+LbvB4TSZkS0ZcQUV vJWTocOd8RB90W3bkibWkWq166XYGE1Nq1L4WIhrVJwbav6ual69yJsEpVcshVkx E1WKzJg7dGb03to4agbReb6+aoCUwb2vNUudNWasSrxoEFArVFGD/ZkPT0esfqEC AwEAAaMhMB8wHQYDVR0OBBYEFH/JlUhCjWiRuh7mXCxr/3VfGXhDMA0GCSqGSIb3 DQEBCwUAA4IBAQAmcTm2ahsIJLayajsvm8yPzQsHA7kIwWfPPHCoHmNbynG67oHB fleaNvrgm/raTT3TrqQkg0525qI6Cqaoyy8JA2fAp3i+hmyoGHaIlo14bKazaiPS RCCqk0J8vwY3CY9nVal1XlHJMEcYV7X1sxKbuAKFoAJ29E/p6ie0JdHtQe31M7X9 FNLYzt8EpJYUtWo13B9Oufz/Guuex9PQ7aC93rbO32MxtnnCGMxQHlaHLLPygc/x cffGz5Xe5s+NEm78CY7thgN+drI7icBYmv4navsnr2OQaD3AfnJ4WYSQyyUUCPxN zuk+B0fbLn7PCCcQspmqfgzIpgbEM9M1/yav -----END CERTIFICATE-----
また、-certreq
によって証明書署名要求(CSR)を生成し、証明書発行局(CA)に送付して署名を求めることもできますが、それはこの例の範囲を超えています。
証明書を新しいトラスト・ストアにインポートします。
% keytool -import -alias dukecert -file duke.cer -keystore truststore Enter keystore password: <password> Re-enter new password: Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Serial number: 210cccfc Valid from: Mon Jul 25 10:33:27 IST 2016 until: Mon Aug 01 10:33:27 IST 2016 Certificate fingerprints: SHA1: 80:E5:8A:47:7E:4F:5A:70:83:97:DD:F4:DA:29:3D:15:6B:2A:45:1F SHA256: ED:3C:70:68:4E:86:35:9C:63:CC:B9:59:35:58:94:1F:7E:B8:B0:EE:D2: 4B:9D:80:31:67:8A:D4:B4:7A:B5:12 Signature algorithm name: SHA256withRSA Subject Public Key Algorithm: RSA (2048) Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 7F C9 95 48 42 8D 68 91 BA 1E E6 5C 2C 6B FF 75 ...HB.h....\,k.u 0010: 5F 19 78 43 _.xC ] ] Trust this certificate? [no]: yes Certificate was added to keystore
トラストストアを調べます。エントリの型はtrustedCertEntry
で、これは、秘密鍵をこのエントリで使用できないことを示します。これは、このファイルがKeyManager
のキーストアとして適切でないことも意味します。
% keytool -list -v -keystore truststore Enter keystore password: <password> Keystore type: PKCS12 Keystore provider: SUN Your keystore contains 1 entry Alias name: dukecert Creation date: Jul 25, 2016 Entry type: trustedCertEntry Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Serial number: 210cccfc Valid from: Mon Jul 25 10:33:27 IST 2016 until: Mon Aug 01 10:33:27 IST 2016 Certificate fingerprints: SHA1: 80:E5:8A:47:7E:4F:5A:70:83:97:DD:F4:DA:29:3D:15:6B:2A:45:1F SHA256: ED:3C:70:68:4E:86:35:9C:63:CC:B9:59:35:58:94:1F:7E:B8:B0:EE:D2: 4B:9D:80:31:67:8A:D4:B4:7A:B5:12 Signature algorithm name: SHA256withRSA Subject Public Key Algorithm: RSA (2048) Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 7F C9 95 48 42 8D 68 91 BA 1E E6 5C 2C 6B FF 75 ...HB.h....\,k.u 0010: 5F 19 78 43 _.xC ] ] ******************************************* *******************************************
ここで、適切なキーストアを使用してアプリケーションを実行します。この例では、デフォルトの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"); sslParameters.setServerNames(Collections.singletonList(serverName));
クライアントは常にホスト名を明示的に指定してください。
ケース2.サーバーでサポートしていないため、クライアントはSNIを使用する必要はありません。
空のサーバー名リストでSNIを無効にします。
sslParameters.setServerNames(Collections.emptyList());
ケース3.クライアントはURL https://www.example.com
にアクセスする必要があります。
OracleプロバイダはデフォルトでSNI拡張にホスト名を設定しますが、サード・パーティ・プロバイダがデフォルトのサーバー名表示をサポートしていないことがあります。アプリケーションでプロバイダに依存しないようにするには、常にホスト名を明示的に設定します。
ケース4.クライアントはソケットをサーバー・モードからクライアント・モードに切り替える必要があります。
まず、メソッドsslSocket.setUseClientMode(true)
でモードを切り替えます。次に、ソケットでサーバー名表示パラメータをリセットします。
次は、サーバー・アプリケーションを開発するためにSNI拡張の理解を必要とするユース・ケースのリストです。
ケース1.サーバーはすべてのサーバー名表示タイプを受け付ける必要があります。
SNI拡張を処理するコードがない場合、サーバーはすべてのサーバー名表示型を無視します。
ケース2.サーバーは型 host_name
のすべてのサーバー名表示型を拒否する必要があります。
host_name
に無効なサーバー名パターンを設定します。
SNIMatcher matcher = SNIHostName.createSNIMatcher(""); Collection<SNIMatcher> matchers = new ArrayList<>(1); matchers.add(matcher); sslParameters.setSNIMatchers(matchers);
他の方法は、matches()
メソッドで、常にfalse
を返すSNIMatcher
サブクラスを作成することです。
class DenialSNIMatcher extends SNIMatcher { DenialSNIMatcher() { super(StandardConstants.SNI_HOST_NAME); } @Override public boolean matches(SNIServerName serverName) { return false; } } SNIMatcher matcher = new DenialSNIMatcher(); Collection<SNIMatcher> matchers = new ArrayList<>(1); matchers.add(matcher); sslParameters.setSNIMatchers(matchers);
ケース3.サーバーはexample.com
ドメイン内のすべてのホスト名への接続を受け付ける必要があります。
すべての*.example.com
アドレスを含むパターンとして、host_name
の認識可能なサーバー名を設定します。
SNIMatcher matcher = SNIHostName.createSNIMatcher("(.*\\.)*example\\.com"); Collection<SNIMatcher> matchers = new ArrayList<>(1); matchers.add(matcher); sslParameters.setSNIMatchers(matchers);
ケース4.サーバーはソケットをクライアント・モードからサーバー・モードに切り替える必要があります。
まず、メソッドsslSocket.setUseClientMode(false)
でモードを切り替えます。次に、ソケットでサーバー名表示パラメータをリセットします。
このセクションでは、仮想インフラストラクチャ内からServer Name Indication (SNI)拡張を使用する方法について説明します。ここでは、ソケットからのClientHelloメッセージのパーサーの作成方法を示し、SSLSocket
およびSSLEngine
を使用した仮想サーバー・ディスパッチャの例を示し、SNI拡張を使用できない場合に何が発生するかを説明して、フェールオーバーSSLContext
を作成する方法を示します。
ClientHelloパーサーの準備
アプリケーションはソケットからのClientHelloメッセージを解析するAPIを実装する必要があります。次の例に、これらの機能を実行できるSSLCapabilities
クラスとSSLExplorer
クラスを示します。
SSLSocketClient.javaはハンドシェーク中にSSL/TLS/DTLSセキュリティ機能をカプセル化します(つまり、SSL/TLS/DTLSハンドシェークで受け付けられる暗号化方式群のリスト、レコード・バージョン、helloバージョンおよびサーバー名表示)。それは、SSLExplorer.explore()
メソッドで、SSL/TLS/DTLS接続のネットワーク・データを調べることによって取得できます。
SSLExplorer.javaはTLSクライアントからの初期ClientHelloメッセージを調査しますが、ハンドシェークを開始したり、ネットワーク・データを消費したりしません。SSLExplorer.explore()
メソッドはClientHelloメッセージを解析し、SSLCapabilities
にセキュリティ・パラメータを取得します。このメソッドは、TLS接続でハンドシェークが行われる前に呼び出す必要があります。
SSLSocketに基づいた仮想サーバー・ディスパッチャ
このセクションでは、SSLSocket
に基づいて、仮想サーバー・ディスパッチャを使用する手順を説明します。
サーバー名ハンドラを登録します。
このステップで、アプリケーションは様々なサーバー名表示の様々な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に基づいた仮想サーバー・ディスパッチャ
このセクションでは、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.
使用可能なSNI拡張がない
ClientHelloメッセージにサーバー名表示がない場合、SNIに従って正しいサービスを選択する方法がありません。そのような場合、アプリケーションは、サーバー名表示がない場合に、接続をそれに委譲できるように、デフォルトのサービスを指定する必要がある場合があります。
フェイルオーバーSSLContext
SSLExplorer.explore()
メソッドはSSL/TLS/DTLSの内容の妥当性をチェックしません。レコード形式がSSL/TLS/DTLS仕様に準拠していない場合、またはハンドシェークの起動後に、explore()
メソッドが呼び出された場合、メソッドはIOException
をスローすることがあり、ネットワーク・データを生成できません。そのような場合、SSL/TLS/DTLS接続のネゴシエーションに使用されないが適切な警告メッセージで接続を閉じるフェイルオーバーSSLContext
を使用することで、SSLExplorer.explore()
によってスローされた例外を処理します。次の例はフェールオーバーSSLContext
を示しています。「一般的なサーバー側使用例」の「ケース2」で、DenialSNIMatcher
クラスの例を参照できます。
byte[] buffer = ... // buffered network data boolean failed = true; // SSLExplorer.explore() throws an exception SSLContext context = SSLContext.getInstance("TLS"); // the failover SSLContext context.init(null, null, null); SSLSocketFactory sslsf = context.getSocketFactory(); ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, position); SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true); SNIMatcher matcher = new DenialSNIMatcher(); Collection<SNIMatcher> matchers = new ArrayList<>(1); matchers.add(matcher); SSLParameters params = sslSocket.getSSLParameters(); params.setSNIMatchers(matchers); // no recognizable server name sslSocket.setSSLParameters(params); try { InputStream sslIS = sslSocket.getInputStream(); sslIS.read(); } catch (Exception e) { System.out.println("Server exception " + e); } finally { sslSocket.close(); }
表8-14には、JSSE暗号化方式群名に関連するその他のパラメータのリストが示されています。Javaセキュリティ標準アルゴリズム名を参照してください。
表8-14 JSSE暗号化方式群のパラメータ
標準名(異なる場合は、IANA名) | 鍵交換アルゴリズム | バルクCipherアルゴリズム | メッセージ認証コード・アルゴリズム |
---|---|---|---|
SSL_NULL_WITH_NULL_NULL IANA: TLS_NULL_WITH_NULL_NULL |
K_NULL |
B_NULL |
M_NULL |
SSL_RSA_WITH_NULL_MD5 IANA: TLS_RSA_WITH_NULL_MD5 |
RSA |
B_NULL |
MD5 |
SSL_RSA_WITH_NULL_SHA IANA: TLS_RSA_WITH_NULL_SHA |
RSA |
B_NULL |
SHA-1 |
SSL_RSA_EXPORT_WITH_RC4_40_MD5 IANA: TLS_RSA_EXPORT_WITH_RC4_40_MD5 |
RSA_EXPORT |
RC4_40 |
MD5 |
SSL_RSA_WITH_RC4_128_MD5 IANA: TLS_RSA_WITH_RC4_128_MD5 |
RSA |
RC4 |
MD5 |
SSL_RSA_WITH_RC4_128_SHA IANA: TLS_RSA_WITH_RC4_128_SHA |
RSA |
RC4 |
SHA-1 |
SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 IANA: TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 |
RSA_EXPORT |
RC2_CBC_40 |
MD5 |
SSL_RSA_WITH_IDEA_CBC_SHA IANA: TLS_RSA_WITH_IDEA_CBC_SHA |
RSA |
IDEA_CBC |
SHA-1 |
SSL_RSA_EXPORT_WITH_DES40_CBC_SHA IANA: TLS_RSA_EXPORT_WITH_DES40_CBC_SHA |
RSA_EXPORT |
DES40_CBC |
SHA-1 |
SSL_RSA_WITH_DES_CBC_SHA IANA: TLS_RSA_WITH_DES_CBC_SHA |
RSA |
DES_CBC |
SHA-1 |
SSL_RSA_WITH_3DES_EDE_CBC_SHA IANA: TLS_RSA_WITH_3DES_EDE_CBC_SHA |
RSA |
3DES_EDE_CBC |
SHA-1 |
SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA IANA: TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA |
DH_DSS |
DES40_CBC |
SHA-1 |
SSL_DH_DSS_WITH_DES_CBC_SHA IANA: TLS_DH_DSS_WITH_DES_CBC_SHA |
DH_DSS |
DES_CBC |
SHA-1 |
SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA IANA: TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA |
DH_DSS |
3DES_EDE_CBC |
SHA-1 |
SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA IANA: TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA |
DH_RSA_EXPORT |
DES40_CBC |
SHA-1 |
SSL_DH_RSA_WITH_DES_CBC_SHA IANA: TLS_DH_RSA_WITH_DES_CBC_SHA |
DH_RSA |
DES_CBC |
SHA-1 |
SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA IANA: TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA |
DH_RSA |
3DES_EDE_CBC |
SHA-1 |
SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA IANA: TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA |
DHE_DSS_EXPORT |
DES40_CBC |
SHA-1 |
SSL_DHE_DSS_WITH_DES_CBC_SHA IANA: TLS_DHE_DSS_WITH_DES_CBC_SHA |
DHE_DSS |
DES_CBC |
SHA-1 |
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA IANA: TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA |
DHE_DSS |
3DES_EDE_CBC |
SHA-1 |
SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA IANA: TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA |
DHE_RSA_EXPORT |
DES40_CBC |
SHA-1 |
SSL_DHE_RSA_WITH_DES_CBC_SHA IANA: TLS_DHE_RSA_WITH_DES_CBC_SHA |
DHE_RSA |
DES_CBC |
SHA-1 |
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA IANA: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA |
DHE_RSA |
3DES_EDE_CBC |
SHA-1 |
SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 IANA: TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 |
DH_anon_EXPORT |
RC4_40 |
MD5 |
SSL_DH_anon_WITH_RC4_128_MD5 IANA: TLS_DH_anon_WITH_RC4_128_MD5 |
DH_anon |
RC4 |
MD5 |
SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA IANA: TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA |
DH_anon |
DES40_CBC |
SHA-1 |
SSL_DH_anon_WITH_DES_CBC_SHA IANA: TLS_DH_anon_WITH_DES_CBC_SHA |
DH_anon |
DES_CBC |
SHA-1 |
SSL_DH_anon_WITH_3DES_EDE_CBC_SHA IANA: TLS_DH_anon_WITH_3DES_EDE_CBC_SHA |
DH_anon |
3DES_EDE_CBC |
SHA-1 |
TLS_KRB5_WITH_DES_CBC_SHA |
KRB5 |
DES_CBC |
SHA-1 |
TLS_KRB5_WITH_3DES_EDE_CBC_SHA |
KRB5 |
3DES_EDE_CBC |
SHA-1 |
TLS_KRB5_WITH_RC4_128_SHA |
KRB5 |
RC4 |
SHA-1 |
TLS_KRB5_WITH_IDEA_CBC_SHA |
KRB5 |
IDEA_CBC |
SHA-1 |
TLS_KRB5_WITH_DES_CBC_MD5 |
KRB5 |
DES_CBC |
MD5 |
TLS_KRB5_WITH_3DES_EDE_CBC_MD5 |
KRB5 |
3DES_EDE_CBC |
MD5 |
TLS_KRB5_WITH_RC4_128_MD5 |
KRB5 |
RC4 |
MD5 |
TLS_KRB5_WITH_IDEA_CBC_MD5 |
KRB5 |
IDEA_CBC |
MD5 |
TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA |
KRB5_EXPORT |
DES_CBC |
SHA-1 |
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA |
KRB5_EXPORT |
RC2_CBC_40 |
SHA-1 |
TLS_KRB5_EXPORT_WITH_RC4_40_SHA |
KRB5_EXPORT |
RC4_40 |
SHA-1 |
TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 |
KRB5_EXPORT |
DES_CBC |
MD5 |
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 |
KRB5_EXPORT |
RC2_CBC_40 |
MD5 |
TLS_KRB5_EXPORT_WITH_RC4_40_MD5 |
KRB5_EXPORT |
RC4_40 |
MD5 |
TLS_PSK_WITH_NULL_SHA |
PSK |
B_NULL |
SHA-1 |
TLS_DHE_PSK_WITH_NULL_SHA |
DHE_PSK |
B_NULL |
SHA-1 |
TLS_RSA_PSK_WITH_NULL_SHA |
RSA_PSK |
B_NULL |
SHA-1 |
TLS_RSA_WITH_AES_128_CBC_SHA |
RSA |
AES_128_CBC |
SHA-1 |
TLS_DH_DSS_WITH_AES_128_CBC_SHA |
DH_DSS |
AES_128_CBC |
SHA-1 |
TLS_DH_RSA_WITH_AES_128_CBC_SHA |
DH_RSA |
AES_128_CBC |
SHA-1 |
TLS_DHE_DSS_WITH_AES_128_CBC_SHA |
DHE_DSS |
AES_128_CBC |
SHA-1 |
TLS_DHE_RSA_WITH_AES_128_CBC_SHA |
DHE_RSA |
AES_128_CBC |
SHA-1 |
TLS_DH_anon_WITH_AES_128_CBC_SHA |
DH_anon |
AES_128_CBC |
SHA-1 |
TLS_RSA_WITH_AES_256_CBC_SHA |
RSA |
AES_256_CBC |
SHA-1 |
TLS_DH_DSS_WITH_AES_256_CBC_SHA |
DH_DSS |
AES_256_CBC |
SHA-1 |
TLS_DH_RSA_WITH_AES_256_CBC_SHA |
DH_RSA |
AES_256_CBC |
SHA-1 |
TLS_DHE_DSS_WITH_AES_256_CBC_SHA |
DHE_DSS |
AES_256_CBC |
SHA-1 |
TLS_DHE_RSA_WITH_AES_256_CBC_SHA |
DHE_RSA |
AES_256_CBC |
SHA-1 |
TLS_DH_anon_WITH_AES_256_CBC_SHA |
DH_anon |
AES_256_CBC |
SHA-1 |
TLS_RSA_WITH_NULL_SHA256 |
RSA |
B_NULL |
SHA-1 |
TLS_RSA_WITH_AES_128_CBC_SHA256 |
RSA |
AES_128_CBC |
SHA-256 |
TLS_RSA_WITH_AES_256_CBC_SHA256 |
RSA |
AES_256_CBC |
SHA-256 |
TLS_DH_DSS_WITH_AES_128_CBC_SHA256 |
DH_DSS |
AES_128_CBC |
SHA-256 |
TLS_DH_RSA_WITH_AES_128_CBC_SHA256 |
DH_RSA |
AES_128_CBC |
SHA-256 |
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 |
DHE_DSS |
AES_128_CBC |
SHA-256 |
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA |
RSA |
CAMELLIA_128_CBC |
SHA-1 |
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA |
DH_DSS |
CAMELLIA_128_CBC |
SHA-1 |
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA |
DH_RSA |
CAMELLIA_128_CBC |
SHA-1 |
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA |
DHE_DSS |
CAMELLIA_128_CBC |
SHA-1 |
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA |
DHE_RSA |
CAMELLIA_128_CBC |
SHA-1 |
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA |
DH_anon |
CAMELLIA_128_CBC |
SHA-1 |
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 |
DHE_RSA |
AES_128_CBC |
SHA-256 |
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 |
DH_DSS |
AES_256_CBC |
SHA-256 |
TLS_DH_RSA_WITH_AES_256_CBC_SHA256 |
DH_RSA |
AES_256_CBC |
SHA-256 |
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 |
DHE_DSS |
AES_256_CBC |
SHA-256 |
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 |
DHE_RSA |
AES_256_CBC |
SHA-256 |
TLS_DH_anon_WITH_AES_128_CBC_SHA256 |
DH_anon |
AES_128_CBC |
SHA-256 |
TLS_DH_anon_WITH_AES_256_CBC_SHA256 |
DH_anon |
AES_256_CBC |
SHA-256 |
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA |
RSA |
CAMELLIA_256_CBC |
SHA-1 |
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA |
DH_DSS |
CAMELLIA_256_CBC |
SHA-1 |
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA |
DH_RSA |
CAMELLIA_256_CBC |
SHA-1 |
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA |
DHE_DSS |
CAMELLIA_256_CBC |
SHA-1 |
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA |
DHE_RSA |
CAMELLIA_256_CBC |
SHA-1 |
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA |
DH_anon |
CAMELLIA_256_CBC |
SHA-1 |
TLS_PSK_WITH_RC4_128_SHA |
PSK |
RC4 |
SHA-1 |
TLS_PSK_WITH_3DES_EDE_CBC_SHA |
PSK |
3DES_EDE_CBC |
SHA-1 |
TLS_PSK_WITH_AES_128_CBC_SHA |
PSK |
AES_128_CBC |
SHA-1 |
TLS_PSK_WITH_AES_256_CBC_SHA |
PSK |
AES_256_CBC |
SHA-1 |
TLS_DHE_PSK_WITH_RC4_128_SHA |
DHE_PSK |
RC4 |
SHA-1 |
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA |
DHE_PSK |
3DES_EDE_CBC |
SHA-1 |
TLS_DHE_PSK_WITH_AES_128_CBC_SHA |
DHE_PSK |
AES_128_CBC |
SHA-1 |
TLS_DHE_PSK_WITH_AES_256_CBC_SHA |
DHE_PSK |
AES_256_CBC |
SHA-1 |
TLS_RSA_PSK_WITH_RC4_128_SHA |
RSA_PSK |
RC4 |
SHA-1 |
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA |
RSA_PSK |
3DES_EDE_CBC |
SHA-1 |
TLS_RSA_PSK_WITH_AES_128_CBC_SHA |
RSA_PSK |
AES_128_CBC |
SHA-1 |
TLS_RSA_PSK_WITH_AES_256_CBC_SHA |
RSA_PSK |
AES_256_CBC |
SHA-1 |
TLS_RSA_WITH_SEED_CBC_SHA |
RSA |
SEED_CBC |
SHA-1 |
TLS_DH_DSS_WITH_SEED_CBC_SHA |
DH_DSS |
SEED_CBC |
SHA-1 |
TLS_DH_RSA_WITH_SEED_CBC_SHA |
DH_RSA |
SEED_CBC |
SHA-1 |
TLS_DHE_DSS_WITH_SEED_CBC_SHA |
DHE_DSS |
SEED_CBC |
SHA-1 |
TLS_DHE_RSA_WITH_SEED_CBC_SHA |
DHE_RSA |
SEED_CBC |
SHA-1 |
TLS_DH_anon_WITH_SEED_CBC_SHA |
DH_anon |
SEED_CBC |
SHA-1 |
TLS_RSA_WITH_AES_128_GCM_SHA256 |
RSA |
AES_128_GCM |
SHA-256 |
TLS_RSA_WITH_AES_256_GCM_SHA384 |
RSA |
AES_256_GCM |
SHA-384 |
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 |
DHE_RSA |
AES_128_GCM |
SHA-256 |
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 |
DHE_RSA |
AES_256_GCM |
SHA-384 |
TLS_DH_RSA_WITH_AES_128_GCM_SHA256 |
DH_RSA |
AES_128_GCM |
SHA-256 |
TLS_DH_RSA_WITH_AES_256_GCM_SHA384 |
DH_RSA |
AES_256_GCM |
SHA-384 |
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 |
DHE_DSS |
AES_128_GCM |
SHA-256 |
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 |
DHE_DSS |
AES_256_GCM |
SHA-384 |
TLS_DH_DSS_WITH_AES_128_GCM_SHA256 |
DH_DSS |
AES_128_GCM |
SHA-256 |
TLS_DH_DSS_WITH_AES_256_GCM_SHA384 |
DH_DSS |
AES_256_GCM |
SHA-384 |
TLS_DH_anon_WITH_AES_128_GCM_SHA256 |
DH_anon |
AES_128_GCM |
SHA-256 |
TLS_DH_anon_WITH_AES_256_GCM_SHA384 |
DH_anon |
AES_256_GCM |
SHA-384 |
TLS_PSK_WITH_AES_128_GCM_SHA256 |
PSK |
AES_128_GCM |
SHA-256 |
TLS_PSK_WITH_AES_256_GCM_SHA384 |
PSK |
AES_256_GCM |
SHA-384 |
TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 |
DHE_PSK |
AES_128_GCM |
SHA-256 |
TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 |
DHE_PSK |
AES_256_GCM |
SHA-384 |
TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 |
RSA_PSK |
AES_128_GCM |
SHA-256 |
TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 |
RSA_PSK |
AES_256_GCM |
SHA-384 |
TLS_PSK_WITH_AES_128_CBC_SHA256 |
PSK |
AES_128_CBC |
SHA-256 |
TLS_PSK_WITH_AES_256_CBC_SHA384 |
PSK |
AES_256_CBC |
SHA-384 |
TLS_PSK_WITH_NULL_SHA256 |
PSK |
B_NULL |
SHA-256 |
TLS_PSK_WITH_NULL_SHA384 |
PSK |
B_NULL |
SHA-384 |
TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 |
DHE_PSK |
AES_128_CBC |
SHA-256 |
TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 |
DHE_PSK |
AES_256_CBC |
SHA-384 |
TLS_DHE_PSK_WITH_NULL_SHA256 |
DHE_PSK |
B_NULL |
SHA-256 |
TLS_DHE_PSK_WITH_NULL_SHA384 |
DHE_PSK |
B_NULL |
SHA-384 |
TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 |
RSA_PSK |
AES_128_CBC |
SHA-256 |
TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 |
RSA_PSK |
AES_256_CBC |
SHA-384 |
TLS_RSA_PSK_WITH_NULL_SHA256 |
RSA_PSK |
B_NULL |
SHA-256 |
TLS_RSA_PSK_WITH_NULL_SHA384 |
RSA_PSK |
B_NULL |
SHA-384 |
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 |
RSA |
CAMELLIA_128_CBC |
SHA-256 |
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 |
DH_DSS |
CAMELLIA_128_CBC |
SHA-256 |
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 |
DH_RSA |
CAMELLIA_128_CBC |
SHA-256 |
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 |
DHE_DSS |
CAMELLIA_128_CBC |
SHA-256 |
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 |
DHE_RSA |
CAMELLIA_128_CBC |
SHA-256 |
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 |
DH_anon |
CAMELLIA_128_CBC |
SHA-256 |
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 |
RSA |
CAMELLIA_256_CBC |
SHA-256 |
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 |
DH_DSS |
CAMELLIA_256_CBC |
SHA-256 |
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 |
DH_RSA |
CAMELLIA_256_CBC |
SHA-256 |
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 |
DHE_DSS |
CAMELLIA_256_CBC |
SHA-256 |
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 |
DHE_RSA |
CAMELLIA_256_CBC |
SHA-256 |
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 |
DH_anon |
CAMELLIA_256_CBC |
SHA-256 |
TLS_EMPTY_RENEGOTIATION_INFO_SCSV |
該当なし |
該当なし |
該当なし |
TLS_FALLBACK_SCSV |
該当なし |
該当なし |
該当なし |
TLS_ECDH_ECDSA_WITH_NULL_SHA |
ECDH_ECDSA |
B_NULL |
SHA-1 |
TLS_ECDH_ECDSA_WITH_RC4_128_SHA |
ECDH_ECDSA |
RC4 |
SHA-1 |
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA |
ECDH_ECDSA |
3DES_EDE_CBC |
SHA-1 |
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA |
ECDH_ECDSA |
AES_128_CBC |
SHA-1 |
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA |
ECDH_ECDSA |
AES_256_CBC |
SHA-1 |
TLS_ECDHE_ECDSA_WITH_NULL_SHA |
ECDHE_ECDSA |
B_NULL |
SHA-1 |
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA |
ECDHE_ECDSA |
RC4 |
SHA-1 |
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA |
ECDHE_ECDSA |
3DES_EDE_CBC |
SHA-1 |
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA |
ECDHE_ECDSA |
AES_128_CBC |
SHA-1 |
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA |
ECDHE_ECDSA |
AES_256_CBC |
SHA-1 |
TLS_ECDH_RSA_WITH_NULL_SHA |
ECDH_RSA |
B_NULL |
SHA-1 |
TLS_ECDH_RSA_WITH_RC4_128_SHA |
ECDH_RSA |
RC4 |
SHA-1 |
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA |
ECDH_RSA |
3DES_EDE_CBC |
SHA-1 |
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA |
ECDH_RSA |
AES_128_CBC |
SHA-1 |
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA |
ECDH_RSA |
AES_256_CBC |
SHA-1 |
TLS_ECDHE_RSA_WITH_NULL_SHA |
ECDHE_RSA |
B_NULL |
SHA-1 |
TLS_ECDHE_RSA_WITH_RC4_128_SHA |
ECDHE_RSA |
RC4 |
SHA-1 |
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA |
ECDHE_RSA |
3DES_EDE_CBC |
SHA-1 |
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA |
ECDHE_RSA |
AES_128_CBC |
SHA-1 |
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA |
ECDHE_RSA |
AES_256_CBC |
SHA-1 |
TLS_ECDH_anon_WITH_NULL_SHA |
ECDH_anon |
B_NULL |
SHA-1 |
TLS_ECDH_anon_WITH_RC4_128_SHA |
ECDH_anon |
RC4 |
SHA-1 |
TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA |
ECDH_anon |
3DES_EDE_CBC |
SHA-1 |
TLS_ECDH_anon_WITH_AES_128_CBC_SHA |
ECDH_anon |
AES_128_CBC |
SHA-1 |
TLS_ECDH_anon_WITH_AES_256_CBC_SHA |
ECDH_anon |
AES_256_CBC |
SHA-1 |
TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA |
SRP_SHA |
3DES_EDE_CBC |
SHA-1 |
TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA |
SRP_SHA |
3DES_EDE_CBC |
SHA-1 |
TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA |
SRP_SHA |
3DES_EDE_CBC |
SHA-1 |
TLS_SRP_SHA_WITH_AES_128_CBC_SHA |
SRP_SHA |
AES_128_CBC |
SHA-1 |
TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA |
SRP_SHA |
AES_128_CBC |
SHA-1 |
TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA |
SRP_SHA |
AES_128_CBC |
SHA-1 |
TLS_SRP_SHA_WITH_AES_256_CBC_SHA |
SRP_SHA |
AES_256_CBC |
SHA-1 |
TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA |
SRP_SHA |
AES_256_CBC |
SHA-1 |
TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA |
SRP_SHA |
AES_256_CBC |
SHA-1 |
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 |
ECDHE_ECDSA |
AES_128_CBC |
SHA-256 |
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 |
ECDHE_ECDSA |
AES_256_CBC |
SHA-384 |
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 |
ECDH_ECDSA |
AES_128_CBC |
SHA-256 |
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 |
ECDH_ECDSA |
AES_256_CBC |
SHA-384 |
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 |
ECDHE_RSA |
AES_128_CBC |
SHA-256 |
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 |
ECDHE_RSA |
AES_256_CBC |
SHA-384 |
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 |
ECDH_RSA |
AES_128_CBC |
SHA-256 |
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 |
ECDH_RSA |
AES_256_CBC |
SHA-384 |
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 |
ECDHE_ECDSA |
AES_128_GCM |
SHA-256 |
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 |
ECDHE_ECDSA |
AES_256_GCM |
SHA-384 |
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 |
ECDH_ECDSA |
AES_128_GCM |
SHA-256 |
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 |
ECDH_ECDSA |
AES_256_GCM |
SHA-384 |
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
ECDHE_RSA |
AES_128_GCM |
SHA-256 |
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 |
ECDHE_RSA |
AES_256_GCM |
SHA-384 |
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 |
ECDH_RSA |
AES_128_GCM |
SHA-256 |
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 |
ECDH_RSA |
AES_256_GCM |
SHA-384 |
TLS_ECDHE_PSK_WITH_RC4_128_SHA |
ECDHE_PSK |
RC4 |
SHA-1 |
TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA |
ECDHE_PSK |
3DES_EDE_CBC |
SHA-1 |
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA |
ECDHE_PSK |
AES_128_CBC |
SHA-1 |
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA |
ECDHE_PSK |
AES_256_CBC |
SHA-1 |
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 |
ECDHE_PSK |
AES_128_CBC |
SHA-256 |
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 |
ECDHE_PSK |
AES_256_CBC |
SHA-384 |
TLS_ECDHE_PSK_WITH_NULL_SHA |
ECDHE_PSK |
B_NULL |
SHA-1 |
TLS_ECDHE_PSK_WITH_NULL_SHA256 |
ECDHE_PSK |
B_NULL |
SHA-256 |
TLS_ECDHE_PSK_WITH_NULL_SHA384 |
ECDHE_PSK |
B_NULL |
SHA-384 |
TLS_RSA_WITH_ARIA_128_CBC_SHA256 |
RSA |
ARIA_128_CBC |
SHA-256 |
TLS_RSA_WITH_ARIA_256_CBC_SHA384 |
RSA |
ARIA_256_CBC |
SHA-384 |
TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 |
DH_DSS |
ARIA_128_CBC |
SHA-256 |
TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 |
DH_DSS |
ARIA_256_CBC |
SHA-384 |
TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 |
DH_RSA |
ARIA_128_CBC |
SHA-256 |
TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 |
DH_RSA |
ARIA_256_CBC |
SHA-384 |
TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 |
DHE_DSS |
ARIA_128_CBC |
SHA-256 |
TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 |
DHE_DSS |
ARIA_256_CBC |
SHA-384 |
TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 |
DHE_RSA |
ARIA_128_CBC |
SHA-256 |
TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 |
DHE_RSA |
ARIA_256_CBC |
SHA-384 |
TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 |
DH_anon |
ARIA_128_CBC |
SHA-256 |
TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 |
DH_anon |
ARIA_256_CBC |
SHA-384 |
TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 |
ECDHE_ECDSA |
ARIA_128_CBC |
SHA-256 |
TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 |
ECDHE_ECDSA |
ARIA_256_CBC |
SHA-384 |
TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 |
ECDH_ECDSA |
ARIA_128_CBC |
SHA-256 |
TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 |
ECDH_ECDSA |
ARIA_256_CBC |
SHA-384 |
TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 |
ECDHE_RSA |
ARIA_128_CBC |
SHA-256 |
TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 |
ECDHE_RSA |
ARIA_256_CBC |
SHA-384 |
TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 |
ECDH_RSA |
ARIA_128_CBC |
SHA-256 |
TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 |
ECDH_RSA |
ARIA_256_CBC |
SHA-384 |
TLS_RSA_WITH_ARIA_128_GCM_SHA256 |
RSA |
ARIA_128_GCM |
SHA-256 |
TLS_RSA_WITH_ARIA_256_GCM_SHA384 |
RSA |
ARIA_256_GCM |
SHA-384 |
TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 |
DHE_RSA |
ARIA_128_GCM |
SHA-256 |
TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 |
DHE_RSA |
ARIA_256_GCM |
SHA-384 |
TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 |
DH_RSA |
ARIA_128_GCM |
SHA-256 |
TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 |
DH_RSA |
ARIA_256_GCM |
SHA-384 |
TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 |
DHE_DSS |
ARIA_128_GCM |
SHA-256 |
TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 |
DHE_DSS |
ARIA_256_GCM |
SHA-384 |
TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 |
DH_DSS |
ARIA_128_GCM |
SHA-256 |
TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 |
DH_DSS |
ARIA_256_GCM |
SHA-384 |
TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 |
DH_anon |
ARIA_128_GCM |
SHA-256 |
TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 |
DH_anon |
ARIA_256_GCM |
SHA-384 |
TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 |
ECDHE_ECDSA |
ARIA_128_GCM |
SHA-256 |
TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 |
ECDHE_ECDSA |
ARIA_256_GCM |
SHA-384 |
TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 |
ECDH_ECDSA |
ARIA_128_GCM |
SHA-256 |
TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 |
ECDH_ECDSA |
ARIA_256_GCM |
SHA-384 |
TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 |
ECDHE_RSA |
ARIA_128_GCM |
SHA-256 |
TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 |
ECDHE_RSA |
ARIA_256_GCM |
SHA-384 |
TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 |
ECDH_RSA |
ARIA_128_GCM |
SHA-256 |
TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 |
ECDH_RSA |
ARIA_256_GCM |
SHA-384 |
TLS_PSK_WITH_ARIA_128_CBC_SHA256 |
PSK |
ARIA_128_CBC |
SHA-256 |
TLS_PSK_WITH_ARIA_256_CBC_SHA384 |
PSK |
ARIA_256_CBC |
SHA-384 |
TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 |
DHE_PSK |
ARIA_128_CBC |
SHA-256 |
TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 |
DHE_PSK |
ARIA_256_CBC |
SHA-384 |
TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 |
RSA_PSK |
ARIA_128_CBC |
SHA-256 |
TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 |
RSA_PSK |
ARIA_256_CBC |
SHA-384 |
TLS_PSK_WITH_ARIA_128_GCM_SHA256 |
PSK |
ARIA_128_GCM |
SHA-256 |
TLS_PSK_WITH_ARIA_256_GCM_SHA384 |
PSK |
ARIA_256_GCM |
SHA-384 |
TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 |
DHE_PSK |
ARIA_128_GCM |
SHA-256 |
TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 |
DHE_PSK |
ARIA_256_GCM |
SHA-384 |
TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 |
RSA_PSK |
ARIA_128_GCM |
SHA-256 |
TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 |
RSA_PSK |
ARIA_256_GCM |
SHA-384 |
TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 |
ECDHE_PSK |
ARIA_128_CBC |
SHA-256 |
TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 |
ECDHE_PSK |
ARIA_256_CBC |
SHA-384 |
TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 |
ECDHE_ECDSA |
CAMELLIA_128_CBC |
SHA-256 |
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 |
ECDHE_ECDSA |
CAMELLIA_256_CBC |
SHA-384 |
TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 |
ECDH_ECDSA |
CAMELLIA_128_CBC |
SHA-256 |
TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 |
ECDH_ECDSA |
CAMELLIA_256_CBC |
SHA-384 |
TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 |
ECDHE_RSA |
CAMELLIA_128_CBC |
SHA-256 |
TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 |
ECDHE_RSA |
CAMELLIA_256_CBC |
SHA-384 |
TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 |
ECDH_RSA |
CAMELLIA_128_CBC |
SHA-256 |
TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 |
ECDH_RSA |
CAMELLIA_256_CBC |
SHA-384 |
TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 |
RSA |
CAMELLIA_128_GCM |
SHA-256 |
TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 |
RSA |
CAMELLIA_256_GCM |
SHA-384 |
TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 |
DHE_RSA |
CAMELLIA_128_GCM |
SHA-256 |
TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 |
DHE_RSA |
CAMELLIA_256_GCM |
SHA-384 |
TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 |
DH_RSA |
CAMELLIA_128_GCM |
SHA-256 |
TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 |
DH_RSA |
CAMELLIA_256_GCM |
SHA-384 |
TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 |
DHE_DSS |
CAMELLIA_128_GCM |
SHA-256 |
TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 |
DHE_DSS |
CAMELLIA_256_GCM |
SHA-384 |
TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 |
DH_DSS |
CAMELLIA_128_GCM |
SHA-256 |
TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 |
DH_DSS |
CAMELLIA_256_GCM |
SHA-384 |
TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 |
DH_anon |
CAMELLIA_128_GCM |
SHA-256 |
TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 |
DH_anon |
CAMELLIA_256_GCM |
SHA-384 |
TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 |
ECDHE_ECDSA |
CAMELLIA_128_GCM |
SHA-256 |
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 |
ECDHE_ECDSA |
CAMELLIA_256_GCM |
SHA-384 |
TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 |
ECDH_ECDSA |
CAMELLIA_128_GCM |
SHA-256 |
TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 |
ECDH_ECDSA |
CAMELLIA_256_GCM |
SHA-384 |
TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 |
ECDHE_RSA |
CAMELLIA_128_GCM |
SHA-256 |
TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 |
ECDHE_RSA |
CAMELLIA_256_GCM |
SHA-384 |
TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 |
ECDH_RSA |
CAMELLIA_128_GCM |
SHA-256 |
TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 |
ECDH_RSA |
CAMELLIA_256_GCM |
SHA-384 |
TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 |
PSK |
CAMELLIA_128_GCM |
SHA-256 |
TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 |
PSK |
CAMELLIA_256_GCM |
SHA-384 |
TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 |
DHE_PSK |
CAMELLIA_128_GCM |
SHA-256 |
TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 |
DHE_PSK |
CAMELLIA_256_GCM |
SHA-384 |
TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 |
RSA_PSK |
CAMELLIA_128_GCM |
SHA-256 |
TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 |
RSA_PSK |
CAMELLIA_256_GCM |
SHA-384 |
TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 |
PSK |
CAMELLIA_128_CBC |
SHA-256 |
TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 |
PSK |
CAMELLIA_256_CBC |
SHA-384 |
TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 |
DHE_PSK |
CAMELLIA_128_CBC |
SHA-256 |
TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 |
DHE_PSK |
CAMELLIA_256_CBC |
SHA-384 |
TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 |
RSA_PSK |
CAMELLIA_128_CBC |
SHA-256 |
TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 |
RSA_PSK |
CAMELLIA_256_CBC |
SHA-384 |
TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 |
ECDHE_PSK |
CAMELLIA_128_CBC |
SHA-256 |
TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 |
ECDHE_PSK |
CAMELLIA_256_CBC |
SHA-384 |
TLS_RSA_WITH_AES_128_CCM |
RSA |
AES_128_CCM |
CCM |
TLS_RSA_WITH_AES_256_CCM |
RSA |
AES_256_CCM |
CCM |
TLS_DHE_RSA_WITH_AES_128_CCM |
DHE_RSA |
AES_128_CCM |
CCM |
TLS_DHE_RSA_WITH_AES_256_CCM |
DHE_RSA |
AES_256_CCM |
CCM |
TLS_RSA_WITH_AES_128_CCM_8 |
RSA |
AES_128_CCM |
CCM_8 |
TLS_RSA_WITH_AES_256_CCM_8 |
RSA |
AES_256_CCM |
CCM_8 |
TLS_DHE_RSA_WITH_AES_128_CCM_8 |
DHE_RSA |
AES_128_CCM |
CCM_8 |
TLS_DHE_RSA_WITH_AES_256_CCM_8 |
DHE_RSA |
AES_256_CCM |
CCM_8 |
TLS_PSK_WITH_AES_128_CCM |
PSK |
AES_128_CCM |
CCM |
TLS_PSK_WITH_AES_256_CCM |
PSK |
AES_256_CCM |
CCM |
TLS_DHE_PSK_WITH_AES_128_CCM |
DHE_PSK |
AES_128_CCM |
CCM |
TLS_DHE_PSK_WITH_AES_256_CCM |
DHE_PSK |
AES_256_CCM |
CCM |
TLS_PSK_WITH_AES_128_CCM_8 |
PSK |
AES_128_CCM |
CCM_8 |
TLS_PSK_WITH_AES_256_CCM_8 |
PSK |
AES_256_CCM |
CCM_8 |
TLS_DHE_PSK_WITH_AES_128_CCM_8 |
DHE_PSK |
AES_128_CCM |
CCM_8 |
TLS_DHE_PSK_WITH_AES_256_CCM_8 |
DHE_PSK |
AES_256_CCM |
CCM_8 |
TLS_ECDHE_ECDSA_WITH_AES_128_CCM |
ECDHE_ECDSA |
AES_128_CCM |
CCM |
TLS_ECDHE_ECDSA_WITH_AES_256_CCM |
ECDHE_ECDSA |
AES_256_CCM |
CCM |
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 |
ECDHE_ECDSA |
AES_128_CCM |
CCM_8 |
TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 |
ECDHE_ECDSA |
AES_256_CCM |
CCM_8 |
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 |
ECDHE_RSA |
AEAD_CHACHA20_POLY1305 |
SHA-256 |
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 |
ECDHE_ECDSA |
AEAD_CHACHA20_POLY1305 |
SHA-256 |
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 |
DHE_RSA |
AEAD_CHACHA20_POLY1305 |
SHA-256 |
TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 |
PSK |
AEAD_CHACHA20_POLY1305 |
SHA-256 |
TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 |
ECDHE_PSK |
AEAD_CHACHA20_POLY1305 |
SHA-256 |
TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 |
DHE_PSK |
AEAD_CHACHA20_POLY1305 |
SHA-256 |
TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 |
RSA_PSK |
AEAD_CHACHA20_POLY1305 |
SHA-256 |