SSLEngine
による非ブロック入出力ネットワークを通じてやり取りされるデータには、意図された受信者以外の人も、簡単にアクセスできます。データにパスワードやクレジットカード番号などの個人情報が含まれる場合、権限のない者がデータを理解できないよう、手段を講じる必要があります。また、意図的であるかどうかにかかわらず、通信中にデータが変更されないようにすることも重要です。Secure Sockets Layer (SSL) および Transport Layer Security (TLS) は、ネットワークを通じたデータの送信時に、データの機密性および整合性を保護するために設計されたプロトコルです。
Java Secure Socket Extension (JSSE) により、セキュアなインターネット通信が可能になります。JSSE では、Java バージョンの SSL および TLS プロトコルのフレームワークおよび実装が提供されます。また、データ暗号化、サーバー認証、メッセージの整合性のほか、オプションでクライアント認証の機能が含まれます。JSSE を使うと、開発者は Hypertext Transfer Protocol (HTTP)、Telnet、FTP など、TCP/IP のアプリケーションプロトコルを実行するクライアントとサーバーの間で、セキュアなデータのやり取りを実現できます。SSL の概要については、「Secure Sockets Layer (SSL) の概要」を参照してください。
JSSE は、基盤となる複雑なセキュリティーアルゴリズムや「ハンドシェーク」メカニズムを抽象化することにより、識別するのが難しく、しかし危険なセキュリティー上の弱点が生まれるリスクを最小限に抑えます。また、JSSE を開発者がアプリケーションに直接統合できる構築ブロックとして使うと、アプリケーション開発が簡単になります。
これまでの JSSE は、Java 2 SDK Standard Edition (J2SDK) v 1.3 のオプションパッケージでした。JSSE は、J2SDK 1.4 より Java Standard Edition Development Kit に統合されています。
JSSE は、アプリケーションプログラミングインタフェース (API) フレームワークと、その API の実装を提供します。JSSE API は、java.security
および java.net
パッケージによって定義された「コア」ネットワークおよび暗号化サービスを補い、拡張されたネットワークソケットクラス、トラストマネージャー、キーマネージャー、SSLContext、およびソケット作成動作をカプセル化するソケットファクトリのフレームワークを提供します。ソケット API は JDK 5.0 のブロック化入出力モデルを基にしているため、実装段階で固有の入出力メソッドを選択できるようにするために非ブロック SSLEngine
API が導入されました。
JSSE API では、SSL バージョン 2.0 および 3.0 のほか、Transport Layer Security (TLS) 1.0 をサポートします。これらのセキュリティープロトコルは、通常の双方向のストリームソケットをカプセル化し、JSSE API は認証、暗号化、および整合性保護の透過的なサポートを追加します。Oracle の JRE に付属している JSSE 実装は、SSL 3.0 と TLS 1.0 をサポートします。SSL 2.0 は実装しません。
すでに説明したように、JSSE は Java SE 6 プラットフォームのセキュリティーコンポーネントで、Java Cryptography Architecture (JCA) フレームワーク上の別の場所にあるものと同じ設計方針に基づいています。暗号化に関するセキュリティーコンポーネントのこのフレームワークにより、実装の独立性と、可能な場合にはアルゴリズムの独立性を実現できます。JSSE は、JCA で定義されたのと同じプロバイダアーキテクチャーを使用します。
Java SE 6 プラットフォームのほかのセキュリティーコンポーネントには、Java 認証・承認サービス (JAAS)、および Java セキュリティーツールがあります。JSSE は JCE と同じ概念およびアルゴリズムを多数含んでいますが、単純なストリームソケット API の下でこれらを自動的に適用します。
JSSE API は、その他の SSL/TLS プロトコルと公開鍵インフラストラクチャー (PKI) 実装をシームレスにプラグインできる設計になっています。開発者が、リモートホストの信頼性やリモートホストに送信する認証鍵データを決定するロジックを提供することもできます。
JSSE には次のような重要な特長があります。
SSLSocket
、SSLServerSocket
、および SSLEngine
)暗号化アルゴリズム脚注 1 | 暗号化処理 | 鍵の長さ (ビット) |
---|---|---|
RSA | 認証と鍵交換 | 512 以上 |
RC4 | バルク暗号化 | 128 128 (40 が有効) |
DES | バルク暗号化 | 64 (56 が有効) 64 (40 が有効) |
Triple DES | バルク暗号化 | 192 (112 が有効) |
AES | バルク暗号化 | 256脚注 2 128 |
Diffie-Hellman | 鍵合意 | 1024 512 |
DSA | 認証 | 1024 |
脚注 1 注: SunJSSE 実装では、暗号化アルゴリズムのすべてにおいて Java 暗号化拡張機能 (JCE) を使用します。
脚注 2 AES_256 を使用する暗号群では、強度が無制限の JCE 管轄ポリシーファイルがインストールされている必要があります。「Java SE ダウンロードページ」を参照してください。
JSSE 標準 API は、javax.net
および javax.net.ssl
パッケージで利用できる API で、次の点をカバーします。
SunJSSE
プロバイダOracle の Java SE の実装には、「SunJSSE
」という名前の JSSE プロバイダが含まれています。このプロバイダはあらかじめインストールされ、JCA に登録されています。このプロバイダが提供する暗号化サービスは次のとおりです。
SSL 3.0 および TLS 1.0 セキュリティープロトコルの実装。
もっとも一般的な SSL および TLS 暗号群の実装。一組の認証、鍵合意、暗号化、および整合性保護を含む。
X.509 ベースのキーマネージャーの実装。標準的な JCA キーストアから適切な認証キーを選択する。
証明書チェーンパスを検証する規則を実装する、X.509 ベースのトラストマネージャーの実装。
JCA キーストア型「pkcs12」として PKCS12 を実装。PKCS12 の信頼できるアンカーの格納は未サポート。ユーザーは、JKS 形式で信頼できるアンカーを格納し、PKCK12 形式で非公開鍵を保存する。
Java セキュリティーのホームページには、ホワイトペーパー、文書、セキュアコーディングガイドラインなどへのリンクがあります。
Java SE セキュリティー
Java Certification Path API プログラマーズガイド:CertPath
プログラマーズガイド
その他の Java SE 6 プラットフォームセキュリティードキュメント:
セキュリティーガイドのページ
Java プラットフォームセキュリティーのチュートリアル:
Java SE のセキュリティー機能
米国の暗号政策については、次のサイトを参照してください。
米国商務省:
http://www.commerce.gov
輸出規制に関するページ:
http://www.crypto.com/
Computer Systems Public Policy (CSPP):
http://www.cspp.org/
連邦情報処理標準出版物 (FIPS PUBS) のホームページ。データ暗号化規格 (DES) へのリンクあり:
http://www.itl.nist.gov/fipspubs/
米国暗号化輸出規制 (改訂版):
http://www.epic.org/crypto/export_controls/regs_1_00.html
オンラインリソース
書籍:
『Applied Cryptography, Second Edition』(Bruce Scheneier 著)、John Wiley and Sons, Inc., 1996
『Cryptography Theory and Practice』(Doug Stinson 著)、CRC Press, Inc., 1995
『Cryptography & Network Security:Principles & Practice』、William Stallings 著、Prentice Hall, 1998
オンラインリソース
Sun ONE ソフトウェアからの SSL の導入:
http://docs.sun.com/source/816-6156-10/contents.htm
SSL プロトコルバージョン 3.0 インターネットドラフト:
http://wp.netscape.com/eng/ssl3/ssl-toc.html
TLS プロトコルバージョン 1.0 RFC:
http://www.ietf.org/rfc/rfc2246.txt
"HTTP Over TLS" の RFC 情報:
http://www.ietf.org/rfc/rfc2818.txt
書籍:
『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』、Second Edition (Marco Pistoia, Duane F Reller, Deepak Gupta, Milind Nagnur, and Ashok K Ramani 著)、Prentice Hall, 1999. Copyright 1999 International Business Machines
このドキュメントでは、暗号化に関する用語が使用されます。このセクションでは、こうした用語を定義します。
キーマネージャーとトラストマネージャーは、それぞれの鍵データにキーストアを使用します。キーマネージャーはキーストアを管理し、ユーザー承認時など必要に応じて公開鍵を提供します。トラストマネージャーは、管理するトラストストアの情報に基づいて、トラストの対象者を決定します。
キーストアは、鍵データのデータベースです。鍵データにはさまざまな用途があり、それには認証やデータ整合性も含まれます。利用できるキーストアにはさまざまな型があり、その中には「PKCS12」や Oracle の「JKS」も含まれます。
一般に、キーストア情報は次の 2 つのカテゴリに分類できます。それは、鍵のエントリと、信頼される証明書のエントリです。鍵のエントリはエンティティーの識別情報とその非公開鍵から構成されており、さまざまな暗号化の用途に使用できます。これとは対照的に、信頼される証明書のエントリには、公開鍵とそのエンティティーの識別情報しか含まれていません。したがって、javax.net.ssl.KeyManager
の場合など、非公開鍵が必要な場合は、信頼される証明書のエントリを使用することはできません。JKS の JDK 実装では、キーストアに鍵のエントリと、信頼される証明書のエントリの両方を含めることができます。
トラストストアとは、トラストの対象を決めるときに使用するキーストアです。すでに信頼しているエンティティーからデータを受け取る場合、およびそのエンティティーが発信元を名乗るエンティティーであることを検証できる場合は、データは実際にそのエンティティーから届いたものであると仮定できます。
ユーザーがそのエンティティーを信頼すると決定した場合にかぎり、トラストストアにエントリが追加されます。ユーザーは、鍵のペアを生成するか、証明書をインポートすることにより、そのエントリにトラストを与えます。これにより、キーストアのエントリは信頼されたエントリとみなされます。
次のような 2 つのキーストアファイルがあると便利です。1 つは鍵のエントリだけのファイル、もう 1 つは信頼された証明書のエントリを含むファイルです。これには、証明書発行局 (CA) が発行する証明書が含まれます。前者には機密性のある情報が含まれますが、後者には機密性のある情報は含まれません。単独のキーストアファイルではなく、異なる 2 つのキーストアファイルを使用すると、独自の証明書 (および対応する非公開鍵) とほかの証明書を論理的に区別した明確な区分が提供されます。アクセス制限があるキーストアに非公開鍵を保存すると、非公開鍵をさらに保護することができます。一方、必要であれば、より自由にアクセスできるキーストアで信頼される証明書を提供することもできます。
暗号化ハッシュ機能に基づく MAC メカニズムは、HMAC と呼ばれます。HMAC は、共有する秘密鍵と組み合わせて、Message Digest 5 (MD5) や Secure Hash Algorithm (SHA) などの暗号化ハッシュ関数とともに使用できます。HMAC については、RFC 2104 で規定されています。
Secure Sockets Layer (SSL) は、Web で暗号化を実装する場合にもっともよく使用されるプロトコルです。SSL は、ネットワークでセキュアな通信を行うために暗号化プロセスを組み合わせて使用します。このセクションでは、SSL および SSL が使用する暗号化プロセスについて簡単に説明します。
SSL は、インターネットで使用される標準的な TCP/IP ソケットプロトコルの機能をセキュアに拡張します。次の表「SSL を使用する TCP/IP プロトコル」で示すように、Secure Sockets Layer は標準的な TCP/IP プロトコルのトランスポート層とアプリケーション層の間に追加されます。SSL とともにもっともよく使用されるアプリケーションは Hypertext Transfer Protocol (HTTP) です。これはインターネットの Web ページ用のプロトコルです。このほかにも、Net News Transfer Protocol (NNTP)、Telnet、Lightweight Directory Access Protocol (LDAP)、Interactive Message Access Protocol (IMAP)、File Transfer Protocol (FTP) などのアプリケーションがあり、やはり SSL とともに使用します。
注:現在のところ、セキュアな FTP の標準規格は存在しません。
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 を少しだけ変更したものです。SSL 3.0 と TLS 1.0 にはほとんど違いがありません。
SSL はこれらの問題に対応しています。最初の問題には、認証と呼ばれるプロセスを通じ、通信の当事者双方に互いの識別情報をオプションで確認させることで対応しています。両者が認証されると、SSL は両者を暗号化接続してセキュアな通信を行います。両者の通信を暗号化することで 2 番目の問題に対応し、機密性が保持されます。SSL で使用する暗号化アルゴリズムには、セキュアなハッシュ関数が含まれています。これはチェックサムに似ています。これにより、送信中にデータが変更されていないことが保証されます。セキュアなハッシュ関数により、3 番目の問題に対応し、データの整合性を確保します。
認証も暗号化もオプションであり、当事者間でネゴシエーションが行われた暗号群に基づいていることに注意してください。
もっとも端的な SSL の使用例は電子商取引です。電子商取引では、通信するサーバーの識別情報は保証されていると考えるべきではありません。クレジットカードの番号を入力するだけですばらしいサービスが受けられるという偽の Web サイトを作成するのは簡単なことです。SSL を使うと、クライアントがサーバーの識別情報を認証することができます。また、サーバーもクライアントの情報を認証できますが、インターネット上の取引では、この方法はあまり使われていません。
クライアントとサーバーが互いの情報を認証すると、SSL は暗号化アルゴリズムを使って機密性とデータの整合性を提供します。これにより、クレジットカード番号のような機密情報をインターネット上でセキュアに送信することができます。
SSL は認証、機密性、およびデータの整合性を提供しますが、非拒否サービスは提供しません。非拒否性とは、メッセージを送信したエンティティーは、後になって送信を拒否することができないということを意味します。メッセージとデジタル署名が関連付けられていると、後になって通信内容を証明することができます。SSL 単独では、非拒否性を提供しません。
秘密鍵暗号方式も公開鍵暗号方式も、合意に基づく暗号鍵または暗号鍵のペアを使用します。鍵は、データの暗号化プロセスおよび復号化プロセスで暗号化アルゴリズムが使用するビット文字列です。暗号化鍵は錠の鍵に似ています。錠を開けることができるのは、正しい鍵だけです。
通信の当事者が互いに鍵を安全に送信するのは、些細な問題ではありません。公開鍵証明書を使うと、公開鍵を安全に送信し、受信者に公開鍵の信頼性を保証することができます。公開鍵証明書については、後のセクションで説明します。
次で説明する暗号化プロセスでは、セキュリティーコミュニティーで使用する名称を使います。通信の当事者をそれぞれ Alice と Bob とし、権限のない第三者を Charlie とします。Charlie は攻撃者にもなります。
秘密鍵暗号方式では、通信する Alice と Bob はメッセージの暗号化と復号化に同じ鍵を使います。暗号化されたデータをネットワークで送信する前に、Alice と Bob は鍵を持っていることが必要で、暗号化と復号化に使用する暗号化アルゴリズムに同意している必要があります。
秘密鍵暗号方式で大きな問題になるのは、攻撃者にアクセスされずに一方から他方に鍵を渡す方法です。Alice と Bob が秘密鍵暗号方式でデータを暗号化しても、Charlie がその鍵にアクセスできれば Alice と Bob の非公開メッセージを傍受することができます。Charlie は Alice と Bob のメッセージを復号化できるだけではなく、Alice になりすまして暗号化データを Bob に送信することもできるのです。Bob には、メッセージが Charlie から届いたものか Alice から届いたものかはわかりません。
秘密鍵の配布方法の問題が解決すれば、秘密鍵暗号はたいへん貴重なツールになります。そのアルゴリズムにより、優れたセキュリティーと暗号化データが比較的迅速に提供できるからです。SSL セッションで送信される機密性の高いデータの多くは、秘密鍵暗号方式で送信されます。
秘密鍵暗号方式は、データの暗号化と復号化に同じ鍵を使用するので、対称暗号化方式とも呼ばれます。よく知られている秘密鍵暗号化アルゴリズムには、Data Encryption Standard (DES)、トリプル DES (3DES)、Rivest Cipher 2 (RC2)、および 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 とともに使用されることが多いハッシュ関数には、Message Digest 5 (MD5) と Secure Hash Algorithm (SHA) の 2 つがあります。SHA は、U.S. National Institute of Science and Technology (NIST) によって提案されました。
Alice は、Bob へのメッセージを Charlie が傍受していないことを確認したい場合、メッセージの HMAC を計算して元のメッセージに HMAC を追加できます。次に、Bob と共有している秘密鍵を使ってメッセージと HMAC を暗号化します。Bob は、メッセージを復号化して HMAC を計算すれば、送信中にメッセージが変更されたかどうかを知ることができます。SSL では、HMAC を使ってセキュアなデータを送信します。
メッセージに暗号化ハッシュが作成されると、ハッシュは送信者の非公開鍵で暗号化されます。このような暗号化ハッシュをデジタル署名と呼びます。
SSL を使った通信は、クライアントとサーバー間の情報交換から始まります。この情報交換を SSL ハンドシェークと呼びます。
SSL には、主に次のような 3 つの目的があります。
SSL セッションは、どの暗号群を使用するかについて、クライアントとサーバーがネゴシエーションを行うことから始まります。暗号群とは、コンピュータがデータを暗号化するために使用する暗号化アルゴリズムと鍵のサイズです。符号化方式には、公開鍵交換アルゴリズムまたは鍵合意アルゴリズム、および暗号化ハッシュ関数に関する情報が含まれます。クライアントは利用できる暗号群をサーバーに伝え、サーバーは、どちらにも適用できる暗号群を選択します。
SSL の認証ステップはオプションです。しかし、Web 上の電子商取引では、一般的にクライアントがサーバーを認証します。サーバーの認証により、サーバーが表すとクライアントが信じているエンティティーを、そのサーバーが実際に表していることをクライアントが確認できます。
サーバーは、自らが表すと唱える組織に属していることを証明するため、クライアントに公開鍵証明書を提示します。この証明書が有効であれば、クライアントはサーバーの識別情報について確信できます。
クライアントとサーバーは、同じ秘密鍵について同意できる情報を交換します。たとえば、RSA を使う場合、クライアントは公開鍵証明書で取得したサーバーの公開鍵を使用して、秘密鍵情報を暗号化します。クライアントは暗号化された秘密鍵情報をサーバーに送信します。復号化にはサーバーの非公開鍵が必要なので、サーバーでだけ、このメッセージを復号化できます。
クライアントとサーバーは、同じ秘密鍵にアクセスします。それぞれのメッセージでは、この処理の最初の段階で選択した、秘密情報を共有する暗号化ハッシュ関数を使って、メッセージに添付される HMAC を計算します。次に、秘密鍵と、この処理の最初の段階で設定した秘密鍵アルゴリズムを使い、セキュアなデータと HMAC を暗号化します。そのあと、クライアントとサーバーは、暗号化されハッシュ化されたデータを使ってセキュアに通信することができます。
前のセクションでは、SSL ハンドシェークを上位レベルで説明しました。つまり、暗号化されたメッセージを送信する前にクライアントとサーバーで行われる情報交換について説明しました。このセクションでは、さらに詳しく説明します。
次の「SSL メッセージ」の図には、SSL ハンドシェークで交換される一連のメッセージが示されています。特定の状況下でだけ送信されるメッセージには「Optional」と記されています。SSL メッセージの説明は次のとおりです。
SSL メッセージは、次の順序で送信されます。
注:クライアントに証明書を要求するのは、ごく一部のインターネットサーバーアプリケーションだけです。
close_notify message
を送信し、接続が終了したことをピアに伝えます。SSL セッションで生成したパラメータを保存しておけば、それ以降も SSL セッションでこれらのパラメータを利用することができます。SSL セッションのパラメータを保存しておけば、暗号化通信をすばやく開始できます。
初期のハンドシェークが完了してアプリケーションデータが流れているとき、いずれの側からでも新しいハンドシェークをいつでも開始できます。特に重要な操作について、アプリケーションで強力な暗号化方式群を使用したり、サーバーアプリケーションでクライアント認証が必要になる場合もあります。
理由は何であれ、新しいハンドシェークが既存の暗号化セッションに置き換わり、新しいセッションが確立されるまで、アプリケーションデータとハンドシェークメッセージが交互に配置されます。
アプリケーションで次のいずれかのメソッドを使用して、新しいハンドシェークを開始できます。
SSLSocket.startHandshake()
SSLEngine.beginHandshake()
再ネゴシエーションに関するプロトコルの問題が 2009 年に見つかりました。プロトコルおよび Java SE 実装はいずれも修正されています。詳細は、「Transport Layer Security (TLS) 再ネゴシエーションの問題」を参照してください。
raw SSLSockets/SSLEngines
を使用する場合は、データの送信前に必ずピアの資格をチェックしてください。SSLSocket
および SSLEngine
クラスは、URL のホスト名がピアの資格にあるホスト名と一致するかどうかを自動的に検証しません。ホスト名が検証されなければ、URL 不正行為によって被害を受ける可能性があります。
https などのプロトコルは、ホスト名の検証処理が不要です。アプリケーションは、HostnameVerifier
を使用してデフォルトの HTTPS ホスト名規則をオーバーライドできます。詳細は、HttpsURLConnection
を参照してください。
SSL についての参考資料は、「Secure Sockets Layer の関連ドキュメント」を参照してください。
セキュアな通信を行うには、接続の両方の側で SSL が使用できることが必要です。JSSE API の接続のエンドポイントクラスは、SSLSocket
および SSLEngine
です。次の図では、SSLSocket/SSLEngine
の作成に使用される主なクラスを論理的な順序で並べています。
SSLSocket
は SSLSocketFactory
またはインバウンド接続を受け取る SSLServerSocket
によって作成されます。SSLServerSocket
は SSLServerSocketFactory
で作成されます。SSLSocketFactory
および SSLServerSocketFactory
オブジェクトはどちらも SSLContext
で作成されます。SSLEngine
は、SSLContext によって直接作成され、アプリケーションに依存してすべての入出力を処理します。
SSLSockets/SSLEngines
を使用する場合は、データの送信前に必ずピアの資格をチェックしてください。たとえば SSLSocket/SSLEngine
クラスは、URL のホスト名がピアの資格にあるホスト名と一致するかどうかを自動的に検証しません。ホスト名が検証されなければ、URL 不正行為によって被害を受ける可能性があります。SSLContext
を取得して初期化するには、次の 2 つの方法があります。
SSLSocketFactory
または SSLServerSocketFactory
クラスで getDefault
static メソッドを呼び出すことです。これらのメソッドは、デフォルトの KeyManager
、TrustManager
、およびセキュアな乱数発生関数を使ってデフォルトの SSLContext
を作成します。デフォルトの KeyManagerFactory
および TrustManagerFactory
を使うと、KeyManager
および TrustManager
がそれぞれ作成されます。使用する鍵データはデフォルトのキーストアおよびトラストストアにあり、「デフォルトの鍵とトラストストア、ストア型、およびストアパスワードのカスタマイズ」で説明するシステムプロパティーで設定します。SSLContext
クラスで getInstance
static メソッドを呼び出し、さらにそのインスタンスの適切な init
メソッドを呼び出してコンテキストを初期化することです。init
メソッドの派生関数は、次の 3 つの引数を取ります。KeyManager
オブジェクトの配列、TrustManager
オブジェクトの配列、および SecureRandom
乱数発生関数です。適切なインタフェースを実装するか、KeyManagerFactory
クラスと TrustManagerFactory
クラスを使って実装を生成することにより、KeyManager
オブジェクトと TrustManager
オブジェクトが作成されます。次に、KeyManagerFactory
および TrustManagerFactory
クラスを、TrustManagerFactory/KeyManagerFactory
の init
メソッドで引数として渡された KeyStore
に含まれる鍵データで初期化します。最後に、TrustManagerFactory
の getTrustManagers
メソッドと KeyManagerFactory
の getKeyManagers
メソッドを呼び出して、トラストマネージャーまたはキーマネージャーの配列を取得します。どちらもトラストデータや鍵データの型の 1 つです。SSL 接続が確立されると、SSLSession
が作成されます。これには設定した識別情報、使用する暗号群などの情報が含まれます。次に、SSLSession
を使って両方のエンティティーの現在の関係と状態を表します。各 SSL 接続には、一度に 1 つのセッションが含まれますが、そのセッションがエンティティー間の接続に、同時に、または連続して何度も使用されることがあります。
コア JSSE クラスは、javax.net
および javax.net.ssl
パッケージの一部です。
SocketFactory
および ServerSocketFactory
クラス抽象クラス javax.net.SocketFactory
は、ソケットの作成に使われます。このクラスは、ほかのファクトリでサブクラス化する必要があります。ファクトリは、ソケットの特定のサブクラスを作成し、一般的なソケットレベルの機能を追加するフレームワークを提供します (たとえば、SSLSocketFactory
を参照)。
javax.net.ServerSocketFactory
クラスは SocketFactory
クラスに似ていますが、サーバーソケットの作成に特化して使われます。
ソケットファクトリを使うと、構築するソケットに関する一連のポリシーを簡単に取得し、ソケットを要求する特別なコード構成を必要としない方法でソケットを作成できます。
java.net.Socket
(または javax.net.ssl.SSLSocket
) のサブクラスにすることができます。そうすれば、圧縮、セキュリティー、レコード宣言、統計情報収集、ファイアウォールトンネリングなどの機能の新しい API を直接公開できます。SSLSocketFactory
および SSLServerSocketFactory
クラスjavax.net.ssl.SSLSocketFactory
は、セキュアなソケットを作成するファクトリとして動作します。このクラスは、javax.net.SocketFactory
の抽象サブクラスです。
セキュアなソケットファクトリは、セキュアなソケットの作成と初期設定の詳細情報をカプセル化します。これには、認証鍵、ピア証明書の検証、使用できる暗号群などが含まれます。
javax.net.ssl.SSLServerSocketFactory
クラスは SSLSocketFactory
クラスに似ていますが、サーバーソケットの作成に特化して使われます。
SSLSocketFactory
の取得SSLSocketFactory
を取得するには、次の 3 つの方法があります。
SSLSocketFactory.getDefault
static メソッドを呼び出してデフォルトのファクトリを取得する。SSLSocketFactory
パラメータでメソッドを含めることができます。これは、クライアントによって呼び出され、ソケットの作成時に使用する SSLSocketFactory
を指定するパラメータです (例: javax.net.ssl.HttpsURLConnection)。通常、デフォルトのファクトリはサーバー認証だけをサポートするように構成されています。このため、デフォルトのファクトリで作成されたソケットは、一般的な TCP ソケット以上にクライアントの情報を漏らすことはありません。
ソケットを作成して使用するクラスの多くは、ソケットの作成方法を詳しく知る必要はありません。パラメータとして渡されたソケットファクトリを介してソケットを作成するのは、ソケット構成の詳細を分離し、ソケットを作成して使用するクラスの再利用性を高めるよい方法です。
新しいソケットファクトリインスタンスを作成するには、独自のソケットファクトリサブクラスを実装するか、ソケットファクトリのファクトリとして動作するクラスをべつに使用します。このようなクラスの 1 つの例が SSLContext
で、これはプロバイダベースの構成クラスとして JSSE 実装に提供されます。
SSLSocket
および SSLServerSocket
クラスjavax.net.ssl.SSLSocket
クラスは標準の Java java.net.Socket
クラスのサブクラスです。標準的なソケットメソッドをすべてサポートし、セキュアなソケットに固有のメソッドを追加します。このクラスのインスタンスは、その作成に使用された SSLContext
をカプセル化します。ソケットインスタンスのセキュアなソケットセッションの作成を管理する API もありますが、トラストマネージャーおよびキーマネージャーは直接公開されません。
javax.net.ssl.SSLServerSocket
クラスは SSLSocket
クラスに似ていますが、サーバーソケットの作成に特化して使われます。
ピアの不正行為を防止するには、常に SSLSocket に示される資格を検証してください。
実装にあたっての注意:SSL と TLS プロトコルは複雑なので、接続時の入力バイトがハンドシェークのデータとアプリケーションデータのどちらなのかを予測し、現在の接続状態にどのような影響を与えるか (処理を中断させることもある) を予測するのは困難です。Oracle JSSE の実装では、SSLSocket.getInputStream()
によって取得されたオブジェクトの available()
メソッドは、SSL 接続で正常に復号化されても、アプリケーションではまだ読み込まれていないデータのバイト数を返します。
SSLSocket
の取得SSLSocket
のインスタンスは、次の 2 つの方法で取得できます。1 つ目は、そのクラスの複数の createSocket
メソッドのうちの 1 つを介して、SSLSocketFactory
のインスタンスで SSLSocket
を作成する方法です。2 つ目は、SSLServerSocket
クラスの accept
メソッドを介して SSLSocket
を取得する方法です。
SSLEngine
による非ブロック入出力Java SE 5 以前の JSSE API は、SSLSocket によるストリームベースソケットという 1 つのトランスポート抽象化のみをサポートしていました。ストリームベースソケットは多くのアプリケーションに適切でしたが、異なる入出力モデルやスレッドモデルを使用する必要があるアプリケーションの要求は満たせませんでした。1.6.0 では、新しい抽象化手法が導入され、トランスポートに依存せずに SSL/TLS プロトコルをアプリケーションで使用できるようになりました。これによりアプリケーションでは、アプリケーションの必要を満たす最善のトランスポートモデルや計算モデルを自由に選択することができます。この新しい抽象化により、非ブロック入出力チャネルやほかの入出力モデルをアプリケーションで使用できるだけでなく、異なるスレッドモデルにも対応することができます。事実上これは、入出力とスレッドの決定がアプリケーションに委ねられることになります。こうした柔軟性に対応するため、これからのアプリケーションは、それ自体が複雑な問題でもある入出力とスレッドを管理するとともに、SSL/TLS プロトコルをある程度理解する能力を持つ必要があります。このように、新しい抽象化は高度な API なので、初心者は引き続き SSLSocket を使用してください。
API をはじめて使用する人は、「java.nio.channels.SocketChannel
を拡張する SSLSocketChannel
だけにしたらどうか」と考えることもあるでしょう。そうしない主な理由は 2 つあります。
Selector
やほかのタイプの SocketChannel
との相互動作方法など、SSLSocketChannel
をどのような API にすべきかということに関して、難問が数多くありました。SSL/TLS に対応するために拡張された新しい API 抽象化は、どれも同じほど重要な分析が必要となり、大規模で複雑な API になる可能性のあることが判明しました。JGSS や SASL など、ほかの Java プログラミング言語 API のユーザーは、アプリケーションがデータをトランスポートする機能を果たすという類似点があることに気付くでしょう。
SSLEngine
SSLEngine は、全体として次に示す 5 つの状態のいずれかの状態を示します。
次に SSLEngine の作成例を示します。サーバー名とポート番号は、サーバーとの通信には使用されません。すべてのトランスポートはアプリケーションによって実行されます。次の作成例は、SSL セッションキャッシングの場合、および取得するサーバー資格を決定する Kerberos ベースの符号化方式が実装されている場合に使用する、JSSE プロバイダへのヒントです。
import javax.net.ssl.*; import java.security.*; // Create/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); // KeyManager's decide which key material to use. KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ksKeys, passphrase); // TrustManager's decide whether to allow connections. TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(ksTrust); sslContext = SSLContext.getInstance("TLS"); sslContext.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null); // We're ready for the engine. SSLEngine engine = sslContext.createSSLengine(hostname, port); // Use as client engine.setUseClientMode(true);
それぞれの SSLEngine には、ライフタイムを通じて数種類の段階があります。アプリケーションデータを送信/受信できるようにするには、SSL/TLS プロトコルに従ってハンドシェークを実行し、暗号化パラメータを確定する必要があります。このハンドシェークでは、SSLEngine による一連のデータやり取り手順が必要です。ハンドシェーク自体の詳細については、「SSL プロセス」を参照してください。
初期ハンドシェーク時に、wrap() と unwrap() はハンドシェークデータを生成および消費し、アプリケーションはデータをトランスポートする役割を担います。wrap()/unwrap() シーケンスは、ハンドシェークが終了するまで繰り返されます。それぞれの SSLEngine 動作により SSLEngineResult が生成され、SSLEngineResult の SSLEngineResult.HandshakeStatus フィールドは、ハンドシェークを続行するために実行する必要のある次の動作を決定するために使用されます。
標準的なハンドシェークでは次のような動作が行われます。
client |
SSL/TLS message |
HSStatus |
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 |
ピアにデータを送信するには、まずアプリケーションが送信するデータを SSLEngine.wrap() を介して SSLEngine に送信し、対応する SSL/TLS エンコードデータを取得します。次にアプリケーションは、選択したトランスポートメカニズムを使用してエンコードデータをピアに送信します。アプリケーションは、トランスポートメカニズムを介してピアから SSL/TLS エンコードデータを受け取ると、SSLEngine.unwrap() を介してそのデータを SSLEngine に送り、ピアによって送信されたプレーンテキストデータを取得します。
非ブロック SocketChannel を使用してピアと通信する SSL アプリケーションの例を次に示します。SSL アプリケーションは、非ブロック SocketChannel を組み込んだ Selector を使用することにより、堅牢性と拡張性を高めることができます。次のサンプルコードは、前の例で作成した SSLEngine を使用してエンコードすることにより、ピアに文字列「hello」を送信します。バイトバッファーのサイズは、SSLSession からの情報を使用して決定されます。
// Create a nonblocking socket channel SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress(hostname, port)); // Complete connection while (!socketChannel.finishedConnect()) { // do something until connect completed } // Create byte buffers to use for holding application and encoded data SSLSession session = engine.getSession(); ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize()); ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize()); ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize()); ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize()); // Do initial handshake doHandshake(socketChannel, engine, myNetData, peerNetData); myAppData.put("hello".getBytes()); myAppData.flip(); while (myAppData.hasRemaining()) { // Generate SSL/TLS encoded data (handshake or application data) SSLEngineResult res = engine.wrap(myAppData, myNetData); // Process status of call if (res.getStatus() == SSLEngineResult.Status.OK) { myAppData.compact(); // Send SSL/TLS encoded data to peer while(myNetData.hasRemaining()) { int num = socketChannel.write(myNetData); if (num == 0) { // no bytes written; try again later } } } // Handle other status: BUFFER_OVERFLOW, CLOSED ... }次のコードは、同じ非ブロック SocketChannel からデータを読み取り、前に作成した SSLEngine を使用して、そのデータからプレーンテキストを抽出します。このコードが反復されるごとに、ハンドシェーク処理が進行しているかどうかに応じて、プレーンテキストが生成されたり、生成されなかったりします。
// Read SSL/TLS encoded data from peer int num = socketChannel.read(peerNetData); if (num == -1) { // The channel has reached end-of-stream } else if (num == 0) { // No bytes read; try again ... } else { // Process incoming data peerNetData.flip(); res = engine.unwrap(peerNetData, peerAppData); if (res.getStatus() == SSLEngineResult.Status.OK) { peerNetData.compact(); if (peerAppData.hasRemaining()) { // Use peerAppData } } // Handle other status: BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED ... }
全体的なステータスは、SSLEngineResult.Status enum によって表されます。たとえばこのステータスには、エラーがないことを意味する OK、入力バッファーのデータが不足しているために、アプリケーションはネットワークからデータを読み込むなどしてピアからもっとデータを取得する必要があることを示す BUFFER_UNDERFLOW、出力バッファーの結果を保持する領域が不足しているために、アプリケーションは出力先バッファーをクリアまたは拡大する必要があることを示す BUFFER_OVERFLOW などがあります。
次に、SSLEngine.unwrap() の BUFFER_UNDERFLOW および BUFFER_OVERFLOW ステータスを処理する方法の例を示します。SSLSession.getApplicationBufferSize() および SSLSession.getPacketBufferSize() を使用して、バイトバッファーのサイズを決定します。
SSLEngineResult res = engine.unwrap(peerNetData, peerAppData); switch (res.getStatus()) { case BUFFER_OVERFLOW: // Maybe need to enlarge the peer application data buffer. if (engine.getSession().getApplicationBufferSize() > peerAppData.capacity()) { // enlarge the peer application data buffer } else { // compact or clear the buffer } // retry the operation break; case BUFFER_UNDERFLOW: // Maybe need to enlarge the peer network packet buffer if (engine.getSession().getPacketBufferSize() > peerNetData.capacity()) { // enlarge the peer network packet buffer } else { // compact or clear the buffer } // obtain more inbound network data and then retry the operation break; // Handle other status: CLOSED, OK ... }
ハンドシェークのステータスは、SSLEngineResult.HandshakeStatus enum によって表されます。このステータスにより、ハンドシェークが完了しているかどうか、発信側はピアからさらにハンドシェークデータを取得する必要があるかどうか、またピアにもっとハンドシェークデータを送信する必要があるかどうかなどの状態を表します。
動作結果ごとに 2 種類のステータスを示すことにより、エンジンはアプリケーションで実行することが必要な 2 つの動作、つまりハンドシェークへの応答における動作と wrap()/unwrap() メソッドの全体的なステータスを表す動作を示すことができます。たとえばエンジンは、1 回の SSLEngine.unwrap() 呼び出しの結果として、SSLEngineResult.Status.OK を返して入力データが正常に処理されたことを示し、SSLEngineResult.HandshakeStatus.NEED_UNWRAP を返すことにより、ハンドシェークを継続するためにアプリケーションがピアからさらに SSL/TLS エンコードデータを取得し、もう一度 SSLEngine.unwrap() に供給すべきことを示します。お気付きのとおり、先の例はかなり単純化されていますが、これらすべてのステータスを適正に処理するにはコードをかなり拡張する必要があります。
次に、wrap()/unwrap() メソッドのハンドシェークのステータスと全体的なステータスをチェックすることによって、ハンドシェークデータを処理する方法の例を示します。
void doHandshake(SocketChannel socketChannel, SSLEngine engine, ByteBuffer myNetData, ByteBuffer peerNetData) throws Exception { // Create byte buffers to use for holding application data int appBufferSize = engine.getSession().getApplicationBufferSize(); ByteBuffer myAppData = ByteBuffer.allocate(appBufferSize); ByteBuffer peerAppData = ByteBuffer.allocate(appBufferSize); // Begin handshake engine.beginHandshake(); SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); // Process handshaking message while (hs != SSLEngineResult.HandshakeStatus.FINISHED && hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { switch (hs) { case NEED_UNWRAP: // Receive handshaking data from peer if (socketChannel.read(peerNetData) < 0) { // The channel has reached end-of-stream } // Process incoming handshaking data peerNetData.flip(); SSLEngineResult res = engine.unwrap(peerNetData, peerAppData); peerNetData.compact(); hs = res.getHandshakeStatus(); // Check status switch (res.getStatus()) { case OK : // Handle OK status break; // Handle other status: BUFFER_UNDERFLOW, BUFFER_OVERFLOW, CLOSED ... } break; case NEED_WRAP : // Empty the local network packet buffer. myNetData.clear(); // Generate handshaking data res = engine.wrap(myAppData, myNetData); hs = res.getHandshakeStatus(); // Check status switch (res.getStatus()) { case OK : myNetData.flip(); // Send the handshaking data to peer while (myNetData.hasRemaining()) { socketChannel.write(myNetData); } break; // Handle other status: BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED ... } break; case NEED_TASK : // Handle blocking tasks break; // Handle other status: // FINISHED or NOT_HANDSHAKING ... } } // Processes after handshaking ... }
新しく作成されたスレッドでそれぞれのタスクを実行する例を次に示します。
if (res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { Runnable task; while ((task=engine.getDelegatedTask()) != null) { new Thread(task).start(); } }エンジンは、未処理のタスクすべてが完了するまで、
wrap/unwrap
呼び出しをブロックします。
// Indicate that application is done with engine engine.closeOutbound(); while (!engine.isOutboundDone()) { // Get close message SSLEngineResult res = engine.wrap(empty, myNetData); // Check res statuses // Send close message to peer while(myNetData.hasRemaining()) { int num = socketChannel.write(myNetData); if (num == 0) { // no bytes written; try again later } myNetData().compact(); } } // Close transport socketChannel.close();SSLEngine を明示的にクローズするアプリケーションに加え、SSLEngine は、ハンドシェークデータを処理している間にクローズメッセージを受け取ることにより、ピアによってクローズされます。または、アプリケーションデータまたはハンドシェークデータを処理している間に、SSLException をスローすることによって示される、エラーが発生した SSLEngine によってクローズされます。そのような場合、アプリケーションは SSLEngine.wrap() を呼び出してクローズメッセージを取得し、前の例に示されるように SSLEngine.isOutboundDone() が true 値を返すまで、または SSLEngineResult.getStatus() が CLOSED を返すまでそのメッセージをピアに送信します。
正常なシャットダウンに加え、クローズメッセージが交換される前にトランスポートリンクを切断する非常シャットダウン方法があります。前の例では、アプリケーションは非ブロック SocketChannel からの読み取りを実行しようとすると -1 または IOException を受け取り、非ブロック SocketChannel への書き込みを実行しようとすると IOException を受け取ります。 最後の入力データになったら、engine.closeInbound() を呼び出し、SSL/TLS の観点からはリモートのペアによって正常にクローズされた SSLEngine を検証してください。そのあとアプリケーションは、上述の手順に従って正常なシャットダウンを試みます。SSLSocket とは異なり、SSLEngine を使用するアプリケーションは、SSLEngine を使用する場合よりも明らかに多くの状態移行、ステータス、およびプログラミングに対処する必要があります。SSLEngine
ベースアプリケーションの作成方法については、「NIO ベースの HTTPS サーバー」を参照してください。
SSLSession
および ExtendedSSLSession
インタフェースjavax.net.ssl.SSLSession
は、SSLSocket
または SSLEngine
接続の 2 つのピアの間で取り決めたセキュリティーコンテキストを表します。一度確立されたセッションは、同じ 2 つのピアの間で接続されるそのあとの SSLSocket
または SSLEngine
オブジェクトによっても共有できます。
場合によっては、ハンドシェーク中に取り決めたパラメータが、後のハンドシェークでトラストについて判断を下す際に必要になることもあります。たとえば、有効な署名アルゴリズムのリストによって、認証に使用できる証明書タイプが制限されることがあります。Java SE 7 リリースでは、SSLSession
はハンドシェークを実行している間に、SSLSocket
または SSLEngine
の getHandshakeSession()
を呼び出すことによって取得できます。TrustManager
または KeyManager
の実装により、getHandshakeSession()
を使用して、それらが判断を下す際に役立つセッションパラメータに関する情報を取得できます。
完全に初期化された SSLSession
には暗号群が含まれます。これは、リモートピアのネットワークアドレスに関する権限のないヒントと同様、セキュアなソケットの通信でも使用され、作成や最後の使用の時点などで、管理情報としても使用されます。セッションには、SSLSocket
または SSLEngine
接続による通信を暗号化して整合性を保証する暗号鍵を作成するために使用される、ピア間で取り決めた共用マスターとなる秘密も含まれます。このマスターとなる秘密の値は、基盤となるセキュアなソケット実装にだけ伝えられ、SSLSession
API では公開されません。
Java SE 7 リリースでは、TLS 1.2 セッションは SSLSession
の実装である ExtendedSSLSession
によって表されます。ExtendedSSLSession
は、ローカル実装およびピアによってサポートされる署名アルゴリズムを記述するメソッドを追加します。
SSLSession.getPacketBufferSize() および SSLSession.getApplicationBufferSize() の呼び出しも、SSLEngine によって使用される適切なバッファーサイズを決定するために使用されます。
注: SSL/TLS プロトコルは、実装が最大 16K バイトのプレーンテキストを含むパケットを生成することを指定します。ただし、一部の実装はこの指定に違反し、32K バイトまでの大きいレコードを生成します。SSLEngine.unwrap() コードが大きいインバウンドパケットを検出した場合、SSLSession から返されるバッファーサイズは動的に更新されます。アプリケーションは常に、必要に応じて BUFFER_OVERFLOW/BUFFER_UNDERFLOW のステータスをチェックして、対応するバッファーを拡大する必要があります。SunJSSE 初値に標準に準拠した 16K バイトのレコードを送信し、32K バイトの着信レコードを許可します。回避策については、「カスタマイズ」のシステムプロパティー jsse.SSLEngine.acceptLargeFragments も参照してください。
HttpsURLConnection
クラスjavax.net.ssl.HttpsURLConnection
は java.net.HttpsURLConnection
クラスを拡張し、https に固有の機能をサポートします。https URL の構造と使用法の詳細については、java.net.URL
、java.net.URLConnection
、java.net.HttpURLConnection
、および javax.net.ssl.HttpURLConnection
クラスを参照してください。
HttpsURLConnection
を取得する際、URLConnection.connect
メソッドを使って実際にネットワーク接続を開始する前に、複数の http/https パラメータを構成できます。これらについては、次を参照してください。
SSLSocketFactory
の設定状況によっては、HttpsURLConnection
のインスタンスによって使用される SSLSocketFactory
を指定したほうがよい場合があります。たとえば、デフォルト実装ではサポートされないプロキシを使ってトンネリングを行う場合がこれに該当します。新しい SSLSocketFactory
は、すでに必要なトンネリングの完了したソケットを返すことができます。このため、HttpsURLConnection
は追加のプロキシを使用することができます。
HttpsURLConnection
クラスには、ロード時に割り当てられたデフォルトの SSLSocketFactory
があります (特に SSLSocketFactory.getDefault
によって返されたファクトリの場合)。以降、HttpsURLConnection
のインスタンスは、static メソッド HttpsURLConnection.setDefaultSSLSocketFactory
によってクラスに新しいデフォルトの SSLSocketFactory
が割り当てられるまで、現在のデフォルトの SSLSocketFactory
を継承します。HttpsURLConnection
のインスタンスが作成されたあと、setSSLSocketFactory
メソッドへの呼び出しにより、このインスタンス上の継承された SSLSocketFactory
をオーバーライドできます。
デフォルトの static メソッド SSLSocketFactory
を変更しても、既存の HttpsURLConnections
のインスタンスには何の影響もありません。既存のインスタンスを変更するには、setSSLSocketFactory
メソッドを呼び出す必要があります。
getSSLSocketFactory
メソッドまたは getDefaultSSLSocketFactory
メソッドを呼び出すことにより、インスタンスごと、またはクラスごとに SSLSocketFactory
を取得できます。
HostnameVerifier
の設定HostnameVerifier
のコールバックを実行します。こうすることで、より詳しいチェックを行うことができます。ホスト名識別子は、判定を下すために必要な処理を行います。その一環として、たとえばその他のホスト名パターンマッチングを実行したり、対話式のダイアログボックスを表示したりします。ホスト名ベリファイアによる検証に失敗した場合は、接続が切断されます。ホスト名の検証については、RFC 2818 を参照してください。
setHostnameVerifier
メソッドおよび setDefaultHostnameVerifier
メソッドは、setSSLSocketFactory
メソッドおよび setDefaultSSLSocketFactory
メソッドとよく似ています。これらの共通点は、インスタンスごと、またはクラスごとに HostnameVerifiers
が割り当てられ、現在の値が getHostnameVerifier
メソッドまたは getDefaultHostnameVerifier
メソッドによって取得されるという点です。
このセクションで説明するクラスとインタフェースは、SSLContext
オブジェクトの作成と初期化をサポートし、SSLSocketFactory、SSLServerSocketFactory
オブジェクトと SSLEngine
オブジェクトを作成するために提供されます。サポートクラスとインタフェースは javax.net.ssl
パッケージに含まれています。
このセクションで説明するクラスは SSLContext
、KeyManagerFactory
、および TrustManagerFactory
の 3 つで、いずれもエンジンクラスです。エンジンクラスとは、特定のアルゴリズムの API クラス (SSLContext
の場合はプロトコル) です。1 つまたは複数の暗号化サービスプロバイダ (プロバイダ) パッケージで実装が提供されることがあります。プロバイダとエンジンクラスの詳細は、「Java 暗号化アーキテクチャーリファレンスガイド」の「設計方針」と「概念」のセクションを参照してください。
JSSE に標準で付属する SunJSSE
プロバイダは、SSLContext
、KeyManagerFactory
、および TrustManagerFactory
実装を提供し、標準の Java セキュリティー (java.security
) API ではエンジンクラスの実装も提供します。SunJSSE
が提供する実装は次のとおりです。
実装される エンジンクラス |
アルゴリズムまたは プロトコル |
---|---|
KeyStore |
PKCS12 |
KeyManagerFactory |
PKIX , SunX509 |
TrustManagerFactory |
PKIX (X509 または SunPKIX ), SunX509 |
SSLContext |
SSLv3 (SSL ), TLSv1 (TLS ), TLSv1.1 , TLSv1.2 |
SSLContext
クラスjavax.net.ssl.SSLContext
は、セキュアなソケットプロトコルの実装のエンジンクラスです。このクラスのインスタンスは、SSL ソケットファクトリおよび SSL エンジンのファクトリとして動作します。SSLContext
は、そのコンテキストの下で作成されたすべてのオブジェクトで共有される状態情報をすべて保持します。たとえば、セッションの状態は、ソケットファクトリにより作成され、コンテキストにより提供されたソケットによってハンドシェークプロトコルが取り決められると、SSLContext
と関連付けられます。キャッシュに書き込まれたこれらのセッションは、同じコンテキストで作成された別のソケットで再利用したり共有することができます。
各インスタンスは、認証の実行に必要な鍵、証明書チェーン、および信頼されたルート CA 証明書を使って init
メソッドで構成されます。この構成は、鍵とトラストマネージャーの形で提供されます。これらのマネージャーは認証をサポートし、コンテキストによってサポートされる暗号群の鍵合意を提供します。
現在は、X.509 ベースのマネージャーだけがサポートされています。
SSLContext
オブジェクトの作成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);
プロトコル名だけを指定すると、システムは、要求されたプロトコルの実装がその環境で利用できるかどうかを判断します。複数の実装がある場合、より望ましいものがあるかどうかを判断します。
プロトコル名とプロバイダを指定すると、システムは、要求されたプロトコルの実装が要求されたプロバイダで利用できるかどうかを判断し、利用できない場合は例外をスローします。
プロトコルは、希望するセキュアなソケットプロトコルを記述する文字列 (「SSL」など) です。SSLContext
オブジェクトの一般的なプロトコル名は、付録 A で定義されています。
次に SSLContext
の取得例を示します。
SSLContext sc = SSLContext.getInstance("SSL");
新しく作成された SSLContext
は、init
メソッドを呼び出すことによって初期化する必要があります。
public void init(KeyManager[] km, TrustManager[] tm, SecureRandom random);
KeyManager[]
パラメータが NULL の場合、このコンテキストには空の KeyManager
が定義されます。TrustManager[]
パラメータが NULL の場合、インストールされたセキュリティープロバイダは、TrustManagerFactory
のもっとも優先度の高い実装で検索され、適切な TrustManager
が取得されます。同様に、SecureRandom パラメータも NULL にすることができます。その場合、デフォルト実装が使用されます。
内部のデフォルトコンテキストが使用される場合 (SSLContext
は SSLSocketFactory.getDefault()
または SSLServerSocketFactory.getDefault()
によって作成されるなど) は、デフォルトの KeyManager
と TrustManager
が作成されます。また、デフォルトの SecureRandom
実装も選択されます。
TrustManager
インタフェースTrustManager
は、提示された認証クレデンシャルの信頼性を判定します。信頼できないクレデンシャルの場合、接続は切断されます。セキュアなソケットピアのリモート識別情報を認証するには、1 つまたは複数の TrustManager
で SSLContext
オブジェクトを初期化する必要があります。サポートされる認証メカニズムのそれぞれに対し、TrustManager
を 1 つ渡す必要があります。SSLContext
の初期化中に NULL が渡されると、トラストマネージャーが作成されます。通常は、X.509 公開鍵証明書 (X509TrustManager
など) に基づく認証をサポートする単一のトラストマネージャーが存在しています。セキュアなソケット実装には、共有の秘密鍵、Kerberos、またはほかのメカニズムに基づく認証をサポートするものもあります。
TrustManager
は TrustManagerFactory
によって、またはインタフェースの具体的な実装を行うことによって作成されます。
TrustManagerFactory
クラスjavax.net.ssl.TrustManagerFactory
はプロバイダベースのサービスのエンジンクラスで、1 つまたは複数の型の TrustManager
オブジェクトのファクトリとして動作します。これはプロバイダベースなので、さらにファクトリを実装して構成し、より高度なサービスを提供するトラストマネージャーや、インストール専用の認証ポリシーを実装するトラストマネージャーを追加したり、べつに提供することができます。
TrustManagerFactory
の作成SSLContext
と同様の方法で作成しますが、getInstance
メソッドにプロトコル名ではなくアルゴリズム名の文字列を渡す点が異なります。
public static TrustManagerFactory getInstance(String algorithm); public static TrustManagerFactory getInstance(String algorithm, String provider); public static TrustManagerFactory getInstance(String algorithm, Provider provider);
アルゴリズム名文字列の例を次に示します。
"PKIX"呼び出しの例を次に示します。
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX", "SunJSSE");
上の呼び出しは、SunJSSE
プロバイダの PKIX トラストマネージャーファクトリのインスタンスを作成します。このファクトリを使用して、X.509 PKIX ベースの証明書パス妥当性検査を実行するトラストマネージャーを作成できます。
SSLContext
を初期化する場合は、トラストマネージャーファクトリから作成したトラストマネージャーを使用するか、CertPath
API などを使用して独自のトラストマネージャーを記述することができます。詳細については、「Java Certification Path API プログラマーズガイド」を参照してください。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 実装を使用します。「SUN」CertPath プロバイダは Java SE Development Kit 6 に付属しています。トラストマネージャーファクトリは、通常の init(KeyStore ks)
メソッドを使用するか、または新しく導入されたクラス javax.net.ssl.CertPathTrustManagerParameters を使用して CertPath パラメータを PKIX トラストマネージャーに渡すことにより初期化できます。
トラストマネージャーを取得して特定の LDAP 証明書ストアを使用する方法と、取り消し確認を有効にする方法の例を次に示します。
import javax.net.ssl.*; import java.security.cert.*; import java.security.KeyStore; ... // Create PKIX parameters KeyStore anchors = KeyStore.getInstance("JKS"); anchors.load(new FileInputStream(anchorsFile)); CertPathParameters 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 them as trust manager parameters ManagerFactoryParameters trustParams = new CertPathTrustManagerParameters(pkixParams); // Create TrustManagerFactory for PKIX-compliant trust managers TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX"); // Pass parameters to factory to be passed to CertPath implementation factory.init(trustParams); // Use factory SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, factory.getTrustManagers(), null);
init(KeyStore ks)
メソッドが使用される場合は、取り消し確認が無効にされる点を除いて、デフォルトの PKIXParameter が使用されます。有効にするには、システムプロパティー com.sun.net.ssl.checkRevocation
を true
に設定します。この設定では、CertPath 実装自身が取り消し情報の場所を検出する必要があります。SUN プロバイダの PKIX 実装では多くの場合にこの動作を実行できますが、システムプロパティー com.sun.security.enableCRLDP
を true
に設定する必要があります。
PKIX および CertPath API の詳細については、「Java Certificate Path API プログラミングガイド」を参照してください。
X509TrustManager
インタフェースjavax.net.ssl.X509TrustManager
インタフェースは、汎用の TrustManager
インタフェースを拡張したものです。X. 509 ベースの認証を行う際、トラストマネージャーは必ずこのインタフェースを実装します。
JSSE を使ったリモートソケットピアの X.509 認証をサポートするには、このインタフェースのインスタンスを SSLContext
オブジェクトの init
メソッドに渡す必要があります。
X509TrustManager
の作成SunJSSE
プロバイダなど、プロバイダベースの TrustManagerFactory
から取得することもできます。また、独自のインタフェースを実装して、ファクトリで生成されたトラストマネージャーに委譲することもできます。たとえば、結果として生じたトラストの決定をフィルタにかけ、GUI を使ってエンドユーザーに問い合わせる場合に、これを実行します。
注: null の KeyStore パラメータが SunJSSE
の「PKIX」または「SunX509」TrustManagerFactory
に渡される場合、ファクトリは次の手順でトラストデータを検索します。
javax.net.ssl.trustStoreが定義されている場合、
TrustManagerFactory
は、このシステムプロパティーで指定したファイル名を使ってファイルを検索し、このファイルをキーストアで使用します。システムプロパティー javax.net.ssl.trustStorePassword
が同様に定義されている場合は、ファイルを開く前に、その値を使ってトラストストアのデータの整合性をチェックします。
javax.net.ssl.trustStore
が定義されているものの、指定したファイルが存在しない場合、空のキーストアを使用するデフォルトの TrustManager
が作成されます。
javax.net.ssl.trustStore
システムプロパティーが指定されておらず、さらにファイル
<java-home>/lib/security/jssecacertsが存在すれば、そのファイルを使用します。(
<java-home>
の詳細については、「インストールディレクトリ <java-home>」を参照してください。)そうでない場合は、<java-home>/lib/security/cacertsが存在すれば、そのファイルを使用します。
どのファイルも存在しない場合、それは匿名の SSL 暗号群があるためと考えられます。この暗号群は認証を行わないので、トラストストアは必要ありません。
ファクトリは、セキュリティープロパティー javax.net.ssl.trustStore
経由で指定したファイル、または jssecacerts
ファイルを検索して cacerts
ファイルをチェックし、信頼されたルート証明書の JSSE 固有のセットを、コードに署名する目的で、cacerts
にあるものとはべつに提供できるようにします。
X509TrustManager
の作成X509TrustManager
の動作がニーズに合わない場合は、独自の X509TrustManager
を作成できます。方法としては、独自の TrustManagerFactory
を作成および登録する方法と、X509TrustManager
インタフェースを直接実装する方法があります。
次の MyX509TrustManager
クラスは、デフォルトの SunJSSE
X509
TrustManager
が失敗したとき、その他の認証ロジックを提供することによって、デフォルトの SunJSSE
X509
TrustManager
の動作を拡張します。
class MyX509TrustManager implements X509TrustManager { /* * The default PKIX X509TrustManager9. We'll delegate * decisions to it, and fall back to the logic in this class if the * default X509TrustManager doesn't 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 trustmanagers, look * for an instance of X509TrustManager. If found, * use that as our "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 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. */ } } /* * Merely pass this through. */ public X509Certificate[] getAcceptedIssuers() { return pkixTrustManager.getAcceptedIssuers(); } }
このようなトラストマネージャーを作成できたら、init
メソッドを使って、これを SSLContext
に割り当てます。以降、この SSLContext
から作成された SocketFactories
は、ユーザー独自の TrustManager
を使用して信頼性を判定するようになります。
TrustManager[] myTMs = new TrustManager [] { new MyX509TrustManager() }; SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, myTMs, null);
keyStore
の動的更新MyX509TrustManager
を拡張して、キーストアの動的更新処理を行うことができます。checkClientTrusted
または checkServerTrusted
のテストに失敗し、信頼できる証明書チェーンを確立できなかった場合、キーストアに対して、要求された信頼できる証明書を追加できます。更新されたキーストアを使用して初期化された TrustManagerFactory
から新しい pkixTrustManager
を作成する必要があります。以前に初期化した SSLContext
を使って新しい接続を確立すると、信頼性の判定を行うときに、新しく追加された証明書が使用されます。
X509ExtendedTrustManager Class
Java SE 7 リリースでは、X509ExtendedTrustManager
クラスは X509TrustManager
インタフェースの抽象実装です。これは、接続を考慮したトラスト管理のためのメソッドを追加します。さらに、TLS レイヤーでのエンドポイントの検証を可能にします。
TLS 1.2 以降では、クライアントとサーバーの両方が、自身が受け入れるハッシュアルゴリズムと署名アルゴリズムを指定することができます。リモート側を認証するには、認証の決定が、X509 証明書と、ローカルで受け入れられるハッシュアルゴリズムおよび署名アルゴリズムの両方に基づいていることが必要です。ローカルで受け入れられるハッシュアルゴリズムおよび署名アルゴリズムは ExtendedSSLSession.getLocalSupportedSignatureAlgorithms()
メソッドから取得できます。
ExtendedSSLSession
オブジェクトは、SSLSocket.getHandshakeSession()
メソッドまたは SSLEngine.getHandshakeSession()
メソッドを呼び出すことによって取得できます。
X509TrustManager
インタフェースは、接続を考慮しません。SSLSocket
または SSLEngine
セッションプロパティーにアクセスする方法を提供しません。
X509ExtendedTrustManager
クラスは TLS 1.2 サポート以外に、アルゴリズムの制約と SSL レイヤーのホスト名の検証もサポートします。JSSE プロバイダおよびトラストマネージャーの実装については、従来の X509TrustManager
インタフェースよりも X509ExtendedTrustManager
クラスの方が強く推奨されます。
X509ExtendedTrustManager
の作成X509ExtendedTrustManager
サブクラスを自分自身で作成するか (概略は次のセクションに記載)、プロバイダベースの TrustManagerFactory
からサブクラスを取得できます (SunJSSE
プロバイダで提供されるものなど)。Java SE 7 リリースでは、PKIX または SunX509 TrustManagerFactory
は X509ExtendedTrustManager
インスタンスを返します。
X509ExtendedTrustManager
の作成このセクションでは、X509TrustManager
について記載されているのとほぼ同じ方法でサブクラス X509ExtendedTrustManager
を作成する方法を示します。
次のクラスでは、「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. We'll delegate * decisions to it, and fall back to the logic in this class if the * default X509ExtendedTrustManager doesn't 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 trustmanagers, look * for an instance of X509TrustManager. If found, * use that as our "default" trust manager. */ for (int i = 0; i < tms.length; i++) { if (tms[i] instanceof X509ExtendedTrustManager) { pkixTrustManager = (X509ExtendedTrustManager) tms[i]; return; } } /* * Find some other way to initialize, or else we have to fail the * constructor. */ throw new Exception("Couldn't initialize"); } /* * Delegate to the default trust manager. */ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { pkixTrustManager.checkClientTrusted(chain, authType); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } /* * Delegate to the default trust manager. */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { pkixTrustManager.checkServerTrusted(chain, authType); } catch (CertificateException excep) { /* * Possibly pop up a dialog box asking whether to trust the * cert chain. */ } } /* * Connection-sensitive verification. */ public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { try { pkixTrustManager.checkClientTrusted(chain, authType, socket); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { try { pkixTrustManager.checkClientTrusted(chain, authType, engine); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { try { pkixTrustManager.checkServerTrusted(chain, authType, socket); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { try { pkixTrustManager.checkServerTrusted(chain, authType, engine); } catch (CertificateException excep) { // do any special handling here, or rethrow exception. } } /* * Merely pass this through. */ public X509Certificate[] getAcceptedIssuers() { return pkixTrustManager.getAcceptedIssuers(); } }
KeyManager
インタフェースKeyManager
は、最終的にリモートホストに送信される認証クレデンシャルを選択します。自分自身 (ローカルのセキュアなソケットピア) をリモートピアに認証させるには、1 つまたは複数の KeyManager
で SSLContext
オブジェクトを初期化する必要があります。サポートされる各認証メカニズムに、KeyManager
を 1 つ渡す必要があります。SSLContext
の初期化中に NULL が渡されると、空の KeyManager
が作成されます。内部のデフォルトコンテキストが使用される場合 (SSLContext
は SSLSocketFactory.getDefault()
または SSLServerSocketFactory.getDefault()
によって作成されるなど) は、デフォルトの KeyManager
が作成されます。通常は、X.509
公開鍵証明書に基づく認証をサポートするキーマネージャーが 1 つあります。セキュアなソケット実装には、共有の秘密鍵、Kerberos、またはほかのメカニズムに基づく認証をサポートするものもあります。
KeyManager
は KeyManagerFactory
によって、またはインタフェースの具体的な実装を行うことによって作成されます。
KeyManagerFactory
クラスjavax.net.ssl.KeyManagerFactory
はプロバイダベースのサービスのエンジンクラスで、1 つまたは複数の型の KeyManager
オブジェクトのファクトリとして動作します。SunJSSE
プロバイダは、基本となる X.509 キーマネージャーを返すことができるファクトリを実装します。これはプロバイダベースであるため、追加のファクトリを実装し、構成することにより、追加の、または代替のキーマネージャーを提供できます。
KeyManagerFactory
の作成SSLContext
と同様の方法で作成しますが、getInstance
メソッドにプロトコル名ではなくアルゴリズム名の文字列を渡す点が異なります。
public static KeyManagerFactory getInstance(String algorithm); public static KeyManagerFactory getInstance(String algorithm, String provider); public static KeyManagerFactory getInstance(String algorithm, Provider provider);
アルゴリズム名文字列の例を次に示します。
"SunX509"呼び出しの例を次に示します。
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
プロバイダは「SunX509」ファクトリをサポートします。ファクトリは、KeyStore パラメータで初期化する必要があります。
X509KeyManager
インタフェースjavax.net.ssl.X509KeyManager
インタフェースは、汎用の KeyManager
インタフェースを拡張したものです。X.509 ベースの認証を行うキーマネージャーで実装します。JSSE を使ったリモートソケットピアの X.509 認証をサポートするには、このインタフェースのインスタンスを SSLContext
オブジェクトの init
メソッドに渡す必要があります。
X509KeyManager
の作成SunJSSE
プロバイダなど、プロバイダベースの KeyManagerFactory
から取得することもできます。また、独自のインタフェースを実装して、ファクトリで生成されたキーマネージャーに委譲することもできます。たとえば、生成される鍵をフィルタにかけ、GUI を使ってエンドユーザーに問い合わせる場合に、これを実行します。
X509KeyManager
の作成X509KeyManager
のデフォルトの動作がニーズに合わない場合は、「独自の X509TrustManager
の作成」と同様の手順で独自の X509KeyManager
を作成できます。
X509ExtendedKeyManager
クラスX509ExtendedKeyManager
抽象クラスは、接続に固有の鍵の選択が可能な X509KeyManager
インタフェースの実装です。これは、鍵のタイプ、許可される発行者、および現在の SSLEngine
に基づいて、クライアントまたはサーバー用の鍵の別名を選択する 2 つのメソッドを追加します。
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
キーマネージャーが X509ExtendedKeyManager
クラスのインスタンスでない場合、SSLEngine
クラスと連携して動作しません。
JSSE プロバイダおよびキーマネージャーの実装については、従来の X509KeyManager
インタフェースよりも X509ExtendedKeyManager
クラスの方が強く推奨されます。
TLS 1.2 以降では、クライアントとサーバーの両方が、自身が受け入れるハッシュアルゴリズムと署名アルゴリズムを指定することができます。リモート側から要求される認証に合格するには、ローカルの鍵選択の決定が、X509 証明書と、リモート側で受け入れられるハッシュアルゴリズムおよび署名アルゴリズムの両方に基づいていることが必要です。リモート側で受け入れられるハッシュアルゴリズムおよび署名アルゴリズムは ExtendedSSLSession.getPeerSupportedSignatureAlgorithms()
メソッドから取得できます。
「独自の X509ExtendedTrustManager
の作成」に示す方法と同様の方法で、独自の X509ExtendedKeyManager
サブクラスを作成することができます。
TrustManager
と KeyManager
との関係TrustManager
と KeyManager
の機能がしばしば混同されてきました。ここでは、各マネージャー型のプライマリ機能を要約します。
型 | 機能 |
---|---|
TrustManager |
リモート認証クレデンシャルの信頼性 (すなわち接続の信頼性) を判定します。 |
KeyManager |
リモートホストに送信される認証クレデンシャルを決定します。 |
二次サポートクラスは、セキュアなソケットの作成、使用、および管理をサポートする JSSE API の一部として提供されます。このクラスは、セキュアなソケットアプリケーションでは、コアクラスやサポートクラスほどには使用されません。セカンダリサポートクラスおよびインタフェースは javax.net.ssl
および javax.security.cert
パッケージに含まれています。
SSLParameters
クラスSSLParameters
は、TLS 接続に影響するものをカプセル化します。
次のメソッドを使用して、SSLSocket
または SSLEngine
についての現在の SSLParameters
を取得できます。
SSLSocket
、SSLServerSocket
、および SSLEngine
の getSSLParameters()
SSLContext
の getDefaultSSLParameters()
および getSupportedSSLParamters()
SSLSocket
、SSLServerSocket
、または SSLEngine
の setSSLParameters()
メソッドを SSLParameters
に割り当てます。
SSLSessionContext
インタフェースjavax.net.ssl.SSLSessionContext
は、1 つのエンティティーに関連付けられている SSLSession
のグループです。たとえば、多数のセッションに同時に参加するサーバーやクライアントに関連付けることができます。このインタフェースのメソッドを使うと、コンテキストの全セッションを列挙したり、セッション ID で特定のセッションを検索したりすることができます。
SSLSessionContext
は、SSLSession の getSessionContext
メソッドを呼び出して SSLSession
からオプションで取得することもできます。一部の環境では、コンテキストが利用できません。それは、getSessionContext
メソッドが NULL を返す場合です。
SSLSessionBindingListener
インタフェースjavax.net.ssl.SSLSessionBindingListener
は、SSLSession
からバインドまたはアンバインドされるときに通知を受けるオブジェクトによって実装されるインタフェースです。
SSLSessionBindingEvent
クラスjavax.net.ssl.SSLSessionBindingEvent
は、SSLSession
からバインドまたはアンバインドされるときに、SSLSessionBindingListener
と通信するイベントです。
HandShakeCompletedListener
インタフェースjavax.net.ssl.HandShakeCompletedListener
は、SSLSocket
接続時に SSL プロトコルハンドシェークの完了通知を受け取る任意のクラスに実装されるインタフェースです。
HandShakeCompletedEvent
クラスjavax.net.ssl.HandShakeCompletedEvent
は、SSLSocket
接続の SSL プロトコルハンドシェークが終了したときに HandShakeCompletedListener
と通信するイベントです。
HostnameVerifier
インタフェースHttpsURLConnection
インスタンスに割り当てられたクラスの verify
メソッドを呼び出します。所定のパラメータがホスト名を受け付けることが明らかな場合、コールバッククラスは、接続が許可されることを報告する必要があります。応答が受け付けられない場合、接続は切断されます。
たとえば、
public class MyHostnameVerifier implements HostnameVerifier { public boolean verify(String hostname, SSLSession session) { // pop up an interactive dialog box // or insert additional matching logic if (good_address) { return true; } else { return false; } } } //...deleted... HttpsURLConnection urlc = (HttpsURLConnection) (new URL("https://www.sun.com/")).openConnection(); urlc.setHostnameVerifier(new MyHostnameVerifier());
HostnameVerifier
を HttpsURLConnection
に割り当てる方法については、「HttpsURLConnection
クラス」を参照してください。
X509Certificate
クラスセキュアなソケットプロトコルの多くは、X.509 証明書という公開鍵証明書を使って認証を行います。これは、SSL および TLS プロトコルのデフォルト認証メカニズムです。
java.security.cert.X509Certificate
抽象クラスは、X.509 証明書の属性にアクセスする標準的な方法を提供します。
注:javax.security.cert.X509Certificate
クラスは、以前のバージョンの JSSE (1.0.x、1.1.x) との後方互換性を確保するためのものです。新しいアプリケーションでは java.security.cert.X509Certificate
を使用し、javax.security.cert.X509Certificate
は使用しません。
AlgorithmConstraints
インタフェースJava SE 7 リリースには、許可される暗号化アルゴリズムを制御するための java.security.AlgorithmConstraints
インタフェースが含まれています。AlgorithmConstraints
は 3 つの permits()
メソッドを定義します。これらのメソッドは、ある暗号化関数について許可されるアルゴリズム名または鍵を指定します。暗号化関数は一連の CryptoPrimitive
で表現され、これは STREAM_CIPHER
、MESSAGE_DIGEST
、SIGNATURE
などのフィールドを含む列挙です。
したがって、AlgorithmConstraints
実装は、「この鍵とこのアルゴリズムを暗号化関数のために使用できるのか」といった質問に答えることができます。
新しいメソッド setAlgorithmConstraints()
を使用して、AlgorithmConstraints
オブジェクトを SSLParameters
オブジェクトと関連付けることができます。SSLParameters
オブジェクトに対する現在の AlgorithmConstraints
オブジェクトは、getAlgorithmConstraints()
を使用して取得できます。
JSSE の旧バージョン (1.0.x) にはリファレンス実装があり、その実装では、クラスおよびインタフェースが com.sun.net.ssl
パッケージで提供されました。
v 1.4 の時点で、JSSE は J2SDK に統合されています。従来 com.sun.net.ssl
パッケージにあったクラスは、javax.net.ssl
パッケージに移動し、現在では標準の JSSE API に含まれています。
com.sun.net.ssl
のクラスとインタフェースは下位互換のために存在しているため、非推奨となります。これらのクラスとインタフェースを使って記述したアプリケーションは、再コンパイルせずに v 1.4 以降の J2SDK で実行することができます。今後のリリースでは、このクラスとインタフェースは削除される可能性があります。そのため、新しいアプリケーションはすべて javax
のクラスとインタフェースを使って書くほうがよいでしょう。
現在のところ、com.sun.net.ssl
API を使用して作成したアプリケーションは、com.sun.net.ssl
を使用する JSSE 1.0.2 プロバイダ、または javax
API を使用する v 1.4 以降の J2SDK 用に記述された JSSE プロバイダのどちらでも利用できます。ただし、J2SDK v 1.4 以降の JSSE API を使って作成したアプリケーションは、v 1.4 以降の J2SDK 用に記述された JSSE プロバイダしか利用できません。新しいリリースには何らかの新機能が含まれるので、対応していないプロバイダではそうした新機能は利用できません。SunJSSE
は Oracle の JDK で提供されるプロバイダで、javax
API で作成されています。
JSSE 1.0.2 を使用している場合のように java.protocol.handler.pkgs
System
プロパティーを設定して URL 検索パスを更新すれば、引き続き com.sun.net.ssl.HttpsURLConnection
を取得できます。詳細については、トラブルシューティングのセクションの「HttpsURLConnection
クラスを使用するコード」を参照してください。
このドキュメントでは、<java-home>
は、Java SE 6 Runtime Environment (JRE) がインストールされているディレクトリを表します。ディレクトリは、JSSE を実行しているのが、Java SDK をインストールした JRE か、インストールしていない JRE かによって異なります。Java SE 6 SDK には JRE が含まれていますが、ファイル階層のレベルは異なります。
<java-home>
が表すディレクトリの例を、次に示します。
/home/user1/jdk1.6.0
にインストールされる場合、<java-home>
は次のようになります
/home/user1/jdk1.6.0/jre
/home/user1/jre1.6.0
にインストールされ、Java 2 SDK がインストールされない場合、<java-home>
は次のようになります
/home/user1/jre1.6.0
C:\jdk1.6.0
にインストールする場合、<java-home>
は次のようになります
C:\j2k1.6.0\jre
C:\jre1.6.0
にインストールされ、Java SE 6 SDK がインストールされない場合、<java-home>
は次のようになります
C:\jre1.6.0
JSSE には、すべてのユーザーが使用できる実装が含まれています。必要な場合、JSSE のいくつかの側面をカスタマイズすることも可能で、さまざまな実装をプラグインしたり、デフォルトのキーストアを指定したりできます。次に示す表では、カスタマイズが可能な側面、デフォルトの内容、およびカスタマイズを提供するために使用するメカニズムが要約されています。表の最初の列には、指定した機能と、カスタマイズ方法の詳細が説明されているサイトへのリンクが設定してあります。
一部の機能は、システムプロパティーやセキュリティープロパティーの値を設定してカスタマイズできます。表に続くセクションでは、プロパティー値の設定方法について説明します。
カスタマイズ項目 |
デフォルト |
カスタマイズ方法 |
---|---|---|
X509Certificate 実装 |
Oracle からの X509Certificate 実装 |
cert.provider.x509v1 セキュリティープロパティー |
HTTPS プロトコル実装 |
Oracle からの実装 |
java.protocol.handler.pkgs システムプロパティー |
プロバイダ実装 |
SunJSSE |
セキュリティープロパティーファイルの security.provider.n= 行。説明を参照。 |
デフォルトの SSLSocketFactory 実装 |
Oracle からの SSLSocketFactory 実装。 |
** ssl.SocketFactory.provider セキュリティープロパティー |
デフォルトの SSLServerSocketFactory 実装 |
Oracle からの SSLServerSocketFactory 実装。 |
** ssl.ServerSocketFactory.provider セキュリティープロパティー |
デフォルトのキーストア |
デフォルトは存在しない。 |
* javax.net.ssl.keyStore システムプロパティー |
デフォルトのキーストアパスワード |
デフォルトは存在しない。 |
* javax.net.ssl.keyStorePassword システムプロパティー |
デフォルトのキーストアプロバイダ |
デフォルトは存在しない。 |
* javax.net.ssl.keyStoreProvider システムプロパティー |
デフォルトのキーストア型 |
KeyStore.getDefaultType() |
* javax.net.ssl.keyStoreType システムプロパティー |
デフォルトのトラストストア |
存在する場合は、 |
* |
デフォルトのトラストストアパスワード |
デフォルトは存在しない。 |
* javax.net.ssl.trustStorePassword システムプロパティー |
デフォルトのトラストストアプロバイダ |
デフォルトは存在しない。 |
* javax.net.ssl.trustStoreProvider システムプロパティー |
デフォルトのトラストストア型 |
KeyStore.getDefaultType() |
* javax.net.ssl.trustStoreType システムプロパティー |
デフォルトのキーマネージャーファクトリのアルゴリズム名 |
|
ssl.KeyManagerFactory.algorithm セキュリティープロパティー |
デフォルトのトラストマネージャーファクトリのアルゴリズム名 |
|
ssl.TrustManagerFactory.algorithm セキュリティープロパティー |
無効化された証明書検証暗号化アルゴリズム |
|
jdk.certpath.disabledAlgorithms セキュリティープロパティー |
無効化された暗号化方式群暗号化アルゴリズム |
デフォルトは存在しない。 |
jdk.tls.disabledAlgorithms セキュリティープロパティー |
デフォルトのプロキシホスト |
デフォルトは存在しない。 |
* https.proxyHost システムプロパティー |
デフォルトのプロキシポート |
80 |
* https.proxyPort システムプロパティー |
Server Name Indication オプション |
|
* jsse.enableSNIExtension システムプロパティー。Server Name Indication (SNI) は TLS 拡張で、RFC 4366 で定義されています。これは仮想サーバーへの TLS 接続を可能にし、さまざまなネットワーク名に対して複数のサーバーが単一の基本ネットワークアドレスでホスティングされます。 かなり古い一部の SSL/TLS ベンダーは、SSL/TLS 拡張を処理できないことがあります。この場合、このプロパティーを false に設定して SNI 拡張を無効化します。 |
デフォルトの暗号群 |
ソケットファクトリによって決定。 |
* https.cipherSuites システムプロパティー。HttpsURLConnection で使用できる暗号群を指定する暗号群名リスト (カンマ区切り形式) を含む。SSLSocket setEnabledCipherSuites(String[]) メソッドを参照してください。 |
デフォルトのハンドシェークプロトコル |
ソケットファクトリによって決定 |
* https.protocols システムプロパティー。HttpsURLConnection で使用できるプロトコル群を指定するプロトコル群名リスト (カンマ区切り形式) を含む。SSLSocket setEnabledProtocols(String[]) メソッドを参照してください。 |
デフォルトの https ポート |
443 |
* https URL 内の port フィールドをカスタマイズ。 |
SunJSSE プロバイダが使用する JCE 暗号化アルゴリズム |
SunJCE 実装 |
代替 JCE アルゴリズムプロバイダに SunJCE プロバイダより高い優先順位を付与 |
大きい SSL/TLS パケット用のバッファーのデフォルトでのサイズ設定 |
デフォルトは存在しない。 |
* jsse.SSLEngine.acceptLargeFragments システムプロパティー |
安全でない SSL/TLS ネゴシエーションを許可 | false |
* sun.security.ssl.allowUnsafeRenegotiation システムプロパティー。このシステムプロパティーを true に設定すると、完全な (安全でない) レガシーの再ネゴシエーションが許可されます。 |
レガシーの Hello メッセージの許可 (再ネゴシエーション) | true |
* sun.security.ssl.allowLegacyHelloMessages システムプロパティー。このシステムプロパティーを true に設定すると、適切な RFC 5746 メッセージを必要とすることなくピアがハンドシェークを実行できます。 |
* このプロパティーは現在、JSSE 実装で使用されています。ほかの実装での検証および使用は保証されていません。ほかの実装で検証する場合は、JSSE 実装と同じ方法で処理されます。プロパティーが今後も存在すること、またはシステム型やセキュリティー型が将来のリリースでも変更されないことの保証はされません。
java.lang.System
プロパティーを設定してカスタマイズする項目と、java.security.Security
プロパティーを設定してカスタマイズする項目があります。次のセクションでは、両方のプロパティー型の値を設定する方法を説明します。
java.lang.System
プロパティーの指定方法JSSE の機能には、システムプロパティーを設定してカスタマイズするものがあります。次のいくつかの方法によって、これらのプロパティーを設定できます。
システムプロパティーを静的に設定するには、java
コマンドの -D
オプションを使用します。たとえば、アプリケーション MyApp
を実行してシステムプロパティー javax.net.ssl.trustStore
を設定し、「MyCacertsFile
」というトラストストアを指定するには、次のように入力します。
java -Djavax.net.ssl.trustStore=MyCacertsFile MyApp
システムプロパティーを動的に設定するには、次のコードで java.lang.System.setProperty
メソッドを呼び出します。
System.setProperty(propertyName, "propertyValue");
適切なプロパティー名と値を入力してください。たとえば、システムプロパティー javax.net.ssl.trustStore
を設定して「MyCacertsFile
」というトラストストアを指定する、上記の例に対応した setProperty
呼び出しでは、次のように入力します。
System.setProperty("javax.net.ssl.trustStore", "MyCacertsFile");
Java 配備環境 (プラグイン/Web Start) では、いくつかの方法でシステムプロパティーを設定できます。詳細情報については、「Java Rich Internet Application の開発および配備」を参照してください。
Java コントロールパネルを使用して、Runtime Environment Property をローカル/VM ごとのベースに設定します。これにより、ローカルで deployment.properties
ファイルが作成されます。配備担当開発者は deployment.config
メカニズムを使用して、企業全体の deployment.properties
ファイルを配布できます。「構成ファイルおよびプロパティーの配備」を参照してください。
特定のアプレットにプロパティーを設定するには、<APPLET>
タグ内の HTML サブタグ <PARAM>
"java_arguments" を使用します (java arguments を参照してください)。
特定の Java Web Start アプリケーションまたはアプレット内で新しい Plugin2 (6u10+) を使用してプロパティーを設定するには、"resources" 要素の JNLP "property" サブ要素を使用します (「resources
要素」を参照してください)。
java.security.Security
プロパティーの指定方法JSSE の機能には、セキュリティープロパティーを設定してカスタマイズするものがあります。セキュリティープロパティーは静的または動的に設定します。
<java-home>/lib/security/java.securityここで、<java-home> は、JRE ランタイムソフトウェアのインストールディレクトリです。インストールディレクトリ <java-home> を参照してください。
セキュリティープロパティーファイルでセキュリティープロパティー値を指定するには、次の行を追加します。
propertyName=propertyValue
たとえば、デフォルトの「SunX509」以外のキーマネージャーファクトリのアルゴリズム名を指定するとします。ssl.KeyManagerFactory.algorithm
のセキュリティープロパティー値をアルゴリズム名に指定します。たとえば、値を「MyX509」に設定するには、セキュリティープロパティーファイルを次のように変更します。
ssl.KeyManagerFactory.algorithm=MyX509
java.security.Security.setProperty
メソッドを呼び出します。
Security.setProperty(propertyName, "propertyValue");適切なプロパティー名と値を入力してください。たとえば、キーマネージャーファクトリのアルゴリズム名を指定する、上記の例に対応した
setProperty
呼び出しでは、次のように入力します。
Security.setProperty("ssl.KeyManagerFactory.algorithm", "MyX509");
X509Certificate.getInstance
メソッドで返された X509 証明書実装は、デフォルトでは JSSE 実装の実装です。
オプションで、別の実装を返すようにすることもできます。その場合は、新しい実装クラスの名前 (およびパッケージ) を、cert.provider.x509v1
というセキュリティープロパティー値に設定します。たとえば、MyX509CertificateImpl
というクラスが com.cryptox
パッケージにある場合、セキュリティープロパティーファイルに次の行を追加します。
cert.provider.x509v1=com.cryptox.MyX509CertificateImpl
java.net.URL
クラスに https で始まる URL スキームを使えば、SSL が利用できる Web サーバーでセキュアに通信できます。JDK では、https の URL 実装をデフォルトで提供します。
別の https プロトコル実装を使用する場合は、java.protocol.handler.pkgs
のシステムプロパティーに新しいクラス名を追加します。その結果、JDK のデフォルトクラスより前に、指定したクラスが検索され、ロードされます。詳細については、java.net.URL
クラスのドキュメントを参照してください。
旧バージョンの JSSE ユーザーへの注記: 旧バージョンの JSSE では、JSSE のインストール中に java.protocol.handler.pkgs
システムプロパティーを設定する必要がありました。このステップは、com.sun.net.ssl.HttpsURLConnection
のインスタンスを取得する場合以外は不要になりました。詳細については、「トラブルシューティング」セクションの「HttpsURLConnection
クラスを使用するコード」を参照してください。
v 1.4 以降の J2SDK リリースには、「SunJSSE
」という JSSE 暗号化サービスプロバイダ (プロバイダ) が標準で付属しています。基本的に、プロバイダは特定の暗号化アルゴリズムのエンジンクラスを実装するパッケージです。JSSE のエンジンクラスは SSLContext
、KeyManagerFactory
、および TrustManagerFactory
です。プロバイダとエンジンクラスの詳細は、「Java 暗号化アーキテクチャーリファレンスガイド」の「設計方針」と「概念」のセクションを参照してください。
プロバイダを使用するには、そのプロバイダを静的または動的に登録する必要があります。「SunJSSE」プロバイダは登録済みなので、新しく登録する必要はありません。ほかのプロバイダを使用する場合は、後述のセクションでプロバイダの登録方法を確認してください。
security.provider.n=providerClassName
これはプロバイダを宣言し、優先順位「n」を指定するものです。優先順位とは、特定プロバイダの指定がない場合に、要求されたアルゴリズムに関して検索されるプロバイダの順位です。順位は 1 から始まり、1 が最優先で次に 2、3...と続きます。
providerClassName は、プロバイダクラスの完全修飾名です。この名前は、プロバイダベンダーから取得します。
プロバイダを登録するには、セキュリティープロパティーに上記の行を追加して、providerClassName をプロバイダクラスの完全な修飾名、n をプロバイダに割り当てる優先順位にします。
標準のセキュリティープロバイダ、および SE 6 プラットフォームに付属する SunJSSE が自動的に登録されます。java.security
セキュリティープロパティーファイルに次の行が追加され、SunJCE セキュリティープロバイダの優先順位が 5、SunJSSE プロバイダの優先順位が 4 として登録されます。
security.provider.1=sun.security.pkcs11.SunPKCS11 \ ${java.home}/lib/security/sunpkcs11-solaris.cfg security.provider.2=sun.security.provider.Sun security.provider.3=sun.security.rsa.SunRsaSign security.provider.4=com.sun.net.ssl.internal.ssl.Provider security.provider.5=com.sun.crypto.provider.SunJCE security.provider.6=sun.security.jgss.SunProvider security.provider.7=com.sun.security.sasl.Provider
ほかの JSSE プロバイダを使う場合は、行を追加してプロバイダを登録し、優先順位を設定します。
複数の JSSE プロバイダを同時に登録できます。プロバイダには、異なるエンジンクラスの、異なるアルゴリズムの異なる実装が含まれる場合があり、同じ型のアルゴリズムおよびエンジンクラスの一部または全部をサポートする場合もあります。特定のアルゴリズムの特定のエンジンクラス実装を検索するとき、その条件に該当する特定のプロバイダが指定されていない場合、プロバイダは優先順位付きで検索され、指定したアルゴリズムを実装する最初のプロバイダの実装が使用されます。
プロバイダを静的に登録する代わりに、プログラム開始時に Security.addProvider
メソッドを呼び出して、実行時に動的に追加することができます。たとえば、プロバイダのクラス名が MyProvider
で、com.ABC
パッケージに MyProvider
クラスがある場合、次のようなメソッドを呼び出してプロバイダを動的に追加します。
Security.addProvider( new com.ABC.MyProvider());
Security.addProvider
メソッドは、次に利用できる優先順位で、指定したプロバイダを追加します。
この登録は恒久的ではなく、十分なアクセス権があるプログラムでしか実行できません。
SSLSocketFactory.getDefault
や SSLServerSocketFactory.getDefault
を呼び出すことでデフォルトの SSLSocketFactory
や SSLServerSocketFactory
が作成され、このデフォルトの SSLSocketFactory
(または SSLServerSocketFactory
) が JSSE リファレンス実装に由来するものであれば、デフォルトの SSLContext
は必ずソケットファクトリに関連付けられます。デフォルトのソケットファクトリは、JSSE 実装に由来します。
デフォルトの SSLContext
は、デフォルトの KeyManager
および TrustManager
で初期化されます。javax.net.ssl.keyStore
システムプロパティーおよび適切な javax.net.ssl.keyStorePassword
システムプロパティーでキーストアを指定すると、デフォルトの SSLContext
で作成した KeyManager
は、指定したキーストアを管理する KeyManager
実装になります。実際には「デフォルト鍵とトラストマネージャー」で説明したとおりに実装されます。システムプロパティーが指定されない場合は、KeyManager
が管理するキーストアは新しい空のキーストアです。
一般に、ハンドシェークでサーバーとして動作するピアには、クライアントへの認証の資格を取得するため、KeyManager のキーストアが必要です。ただし、匿名の暗号群を選択する場合、サーバーの KeyManager
キーストアは必要ありません。また、サーバーがクライアント認証を要求しないかぎり、クライアントとして動作するピアには、KeyManager
キーストアは必要ありません。したがって、このような状況では、javax.net.ssl.keyStore
のシステムプロパティー値が定義されていない場合もあります。
同様に、トラストストアを javax.net.ssl.trustStore
システムプロパティーで指定すると、デフォルトの SSLContext
で作成した TrustManager
が、指定したトラストストアを管理する TrustManager
実装になります。この場合、プロパティーが存在しても指定するファイルが存在しなければ、トラストストアは使用されません。javax.net.ssl.trustStore
プロパティーが存在しない場合は、デフォルトのトラストストアを検索します。<java-home>/lib/security/jssecacerts
という名前のトラストストアが見つかった場合、それが使用されます。このトラストストアが見つからない場合、<java-home>/lib/security/cacerts
というトラストストアを検索し、見つかればこれを使用します。<java-home>
の詳細については、「インストールディレクトリ <java-home>」を参照してください。トラストストアが見つからない場合、TrustManager
は新しい空のトラストストアを管理します。
<java-home>/lib/security/cacerts
ファイル内にある信頼されたルート証明書は、数がかぎられています。「keytool」に記載したとおり、このファイルをトラストストアとして使用する場合は、このファイルに含まれる証明書の管理 (追加または削除) を行う必要があります。
接続先のサーバーの証明書構成によっては、ルート証明書を新たに追加しなければならない場合もあります。適切なベンダーから必要なルート証明書を入手してください。
システムプロパティー javax.net.ssl.keyStoreType
と javax.net.ssl.keyStorePassword
の両方またはどちらか一方が指定されている場合、それぞれがデフォルトの KeyManager
キーストア型とパスワードとして扱われます。型が指定されていない場合、デフォルトの型は KeyStore.getDefaultType()
が返す keystore.type
セキュリティープロパティーの値です。また、そうしたセキュリティープロパティーが指定されていない場合は「jks」になります。キーストアのパスワードが指定されていない場合は "" とみなされます。
同様に、システムプロパティー javax.net.ssl.trustStoreType
と javax.net.ssl.trustStorePassword
の両方またはどちらか一方が指定されている場合、それぞれがデフォルトのトラストストア型とパスワードとして扱われます。型が指定されていない場合、KeyStore.getDefaultType()
によってデフォルト型が返されます。トラストストアのパスワードが指定されていない場合は "" とみなされます。
重要: このセクションでは、現在の JSSE リファレンス実装の動作を説明します。このセクションで説明するシステムプロパティーの名前と型 (システムかセキュリティーか) が今後も使用されるという保証はありません。また、今後のリリースで存在するという保証もありせん。また、ほかの JSSE 実装での検証や使用も保証されていません。実装で検証された場合、ここで説明した JSSE リファレンス実装と同じ方法で処理してください。
「デフォルトの鍵とトラストストア、ストア型、およびパスワードのカスタマイズ」で説明したように、デフォルトの SSLSocketFactory
や SSLServerSocketFactory
が作成され、このデフォルトの SSLSocketFactory
(または SSLServerSocketFactory
) が JSSE リファレンス実装に由来する場合は、常にデフォルトの SSLContext
はソケットファクトリに関連付けられます。
デフォルトの SSLContext
は、KeyManager
および TrustManager
で初期化されます。デフォルトの SSLContext
に提供された KeyManager
と TrustManager
の両方またはどちらか一方は、指定したキーストアまたはトラストストアを管理する KeyManager
または TrustManager
の実装になります。これについては、後のセクションで説明します。
選択される KeyManager
実装は、まず
ssl.KeyManagerFactory.algorithmセキュリティープロパティーを確認して決定されます。そのようなプロパティー値が指定されていると、指定したアルゴリズムの
KeyManagerFactory
実装が検索されます。実装を提供する最初のプロバイダの実装が使用されます。その getKeyManagers
メソッドが呼び出され、デフォルトの SSLContext
に提供する KeyManager
が決定されます。技術的には、getKeyManagers
は KeyManager
の配列を返します。これは鍵データの型ごとの KeyManager
です。そのようなセキュリティープロパティー値が指定されていない場合、「SunX509」のデフォルト値を使って検索します。注:「SunX509」アルゴリズムの KeyManagerFactory
実装は SunJSSE
プロバイダが提供します。それが指定する KeyManager
は javax.net.ssl.X509KeyManager
実装です。
同様に、選択される TrustManager
実装は、まず
ssl.TrustManagerFactory.algorithmセキュリティープロパティーを確認して決定されます。そのようなプロパティー値が指定されていると、指定したアルゴリズムの
TrustManagerFactory
実装が検索されます。実装を提供する最初のプロバイダの実装が使用されます。その getTrustManagers
メソッドが呼び出され、デフォルトの SSLContext
に提供する TrustManager
が決定されます。技術的には、getTrustManagers
は TrustManager
の配列を返します。これは信頼データの型ごとの TrustManager
です。そのようなセキュリティープロパティー値が指定されていない場合、「PKIX」のデフォルト値を使って検索します。注:「PKIX」アルゴリズムの TrustManagerFactory
実装は SunJSSE
プロバイダが提供します。それが指定する TrustManager
は javax.net.ssl.X509TrustManager
実装です。
重要: このセクションでは、現在の JSSE リファレンス実装の動作を説明します。このセクションで説明するシステムプロパティーの名前と型 (システムかセキュリティーか) が今後も使用されるという保証はありません。また、今後のリリースで存在するという保証もありせん。また、ほかの JSSE 実装での検証や使用も保証されていません。実装で検証された場合、ここで説明した JSSE リファレンス実装と同じ方法で処理してください。
暗号化ハッシュアルゴリズム MD2 は、もはやセキュアだと考えられていません。Java SE 7 リリースには、特定の暗号化アルゴリズムの無効化をサポートする 2 つの新規セキュリティープロパティーと 1 つの新しい API が含まれています。
jdk.tls.disabledAlgorithms
プロパティーは TLS ハンドシェークに適用され、jdk.certpath.disabledAlgorithms
プロパティーは証明書パスの処理に適用されます。
JDK 7u40 リリース以降では、jdk.certpath.disabledAlgorithms
のデフォルト値に RSA keySize < 1024
が含まれています。これは、1024 ビット未満の RSA 鍵サイズによる証明書の使用が制限されていることを意味します。jdk.certpath.disabledAlgorithms
のデフォルト値は現在次のようになっています。
jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024
これは、MD2 またはサイズが 1024 未満の RSA キーで署名されたすべての証明書が受け入れ不可であることを意味します。
各セキュリティープロパティーには、証明書パスの処理中に使用されない暗号化アルゴリズムのリストが含まれています。プロパティーの構文は jre/lib/security/java.security
ファイルに正しく記載されていますが、ここでは簡単に要約します。
セキュリティープロパティーには、使用してはいけない暗号化アルゴリズムのリストが含まれます。アルゴリズム名はコンマで区切られています。また、使用できない特定の鍵サイズも指定できます。
たとえば、java.security
内の次の行は、証明書パスの処理に MD2 および DSA アルゴリズムを使用してはいけないことを指定しています。さらに、2048 ビット未満の鍵サイズに対して RSA は無効になります。
jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048
Java SE 5 のリリースから、SunJSSE プロバイダは、暗号化の必要すべてを満たすために SunJCE 実装を使用しています。Sun プロバイダは通常の位置に置くことが推奨されていますが、SunJCE プロバイダより前に登録することにより、ほかの JCA/JCE プロバイダからの実装を使用することもできます。プロバイダの構成には、標準 JCA メカニズムを使用できます。まず、次のセキュリティープロパティーファイルを利用して静的に行う方法があります。
<java-home>/lib/security/java.security次に、
java.security.Security
クラスの addProvider
メソッドまたは insertProviderAt
メソッドを利用して動的に行う方法があります (<java-home>
の詳細については、「インストールディレクトリ <java-home>」を参照してください。)
SunJSSE が Cipher.getInstance()
を呼び出すとき、使用される変換文字列は "RSA/ECB/PKCS1Padding"、"RC4"、"DES/CBC/NoPadding"、"DESede/CBC/NoPadding" になります。Cipher クラスと変換文字列の詳細については、「Java 暗号化アーキテクチャーリファレンスガイド」を参照してください。
2009 年の秋、SSL/TLS プロトコルの問題が見つかりました。IETF TLS Working Group によってプロトコルの修正が開発され、JDK の現行バージョンにはこの修正が含まれています。このセクションでは、このプロトコル修正を含まない以前の実装との通信時における相互運用性の問題を含め、状況をさらに詳しく説明します。
この脆弱性により、選択されたプレーンテキストを接頭辞として TLS 接続に注入できるという Man-In-The-Middle (MITM) タイプの攻撃を許していました。この脆弱性は、クライアントとサーバーがセッションのネゴシエーションに成功したあと、傍受されたネットワーク通信を攻撃者が復号化または変更することを許すものではありません。この脆弱性は、次の資料で公開されています。
また、次の資料に追加情報があります。
この問題の修正は、2 つのフェーズに分けて扱われています。
フェーズ 1: プロトコル修正が開発されるまで、SSL/TLS の再ネゴシエーションをデフォルトで無効化する中間修正が、「March 30, 2010 Java SE and Java for Business Critical Patch Update」から利用できるようになりました。
フェーズ 2: IETF によって、再ネゴシエーションプロトコルの問題を扱う RFC 5746 が発表されました。RFC 5746 を実装してセキュアな再ネゴシエーションをサポートする修正は、次のリリースに組み込まれています。
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 |
SDK および 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 攻撃に対して脆弱です。
上記のモード区分は、アップグレードされていないピアとの接続にのみ影響します。すべてのクライアントおよびサーバーで厳密モード (完全な RFC 5746 モード) を使用することが望ましいのですが、配備されているすべての SSL/TLS 実装が RFC 5746 をサポートするようになるまである程度時間がかかるため、今のところは相互運用モードがデフォルトになっています。
相互運用性に関する追加情報を次に示します。
Client | Server | モード |
---|---|---|
更新済み | 更新済み | すべてのモードでセキュアな再ネゴシエーションが行われます。 |
レガシー [1] | 更新済み |
|
更新済み | レガシー [1] |
|
レガシー [1] | レガシー [1] | 既存の SSL/TLS 動作を行い、MITM 攻撃に対して脆弱です。 |
[1] レガシーとは元の SSL/TLS 仕様を意味します (RFC 5746 でない)。
[2] SunJSSE フェーズ 1 実装 (上記を参照) は、明示的にふたたび有効化されないかぎり再ネゴシエーションを拒否します。再ネゴシエーションがふたたび有効化された場合、再ネゴシエーションは適切な RFC 5746 メッセージを送信しないため、RFC 5746 に準拠したピアによってレガシーとして扱われます。
[3] SSL/TLS では、再ネゴシエーションをいずれの側からでも開始できます。フェーズ 1 修正のように、まだアップグレードされていないピアと相互運用モードで通信しているアプリケーションが (SSLSocket.startHandshake()
または SSLEngine.beginHandshake()
を使用して) 再ネゴシエーションを開始しようとすると、アプリケーションは SSLHandshakeException
(IOException
) を受け取り接続は停止されます (handshake_failure)。まだアップグレードされていないピアから再ネゴシエーション要求を受け取ったアプリケーションは、現在の接続のタイプに応じて応答します。
SSLHandshakeException
を受け取り、接続は終了します (handshake_failure)。(「no_renegotiation」は SSLv3 仕様では定義されていません。)これらのモードを設定するには、2 つのシステムプロパティーが使用されます。
sun.security.ssl.allowUnsafeRenegotiation
これはフェーズ 1 で導入されたもので、レガシー (安全でない) 再ネゴシエーションが許可されるかどうかを制御します。sun.security.ssl.allowLegacyHelloMessages
これはフェーズ 2 で導入され、適切な RFC 5746 メッセージを必要とすることなくピアがハンドシェークすることを許可します。mode | allowLegacyHelloMessages | allowUnsafeRenegotiation |
---|---|---|
厳密 | false | false |
相互運用 (デフォルト) | true | false |
セキュアでない | true | true |
警告: セキュアでない SSL/TLS 再ネゴシエーションをふたたび有効化することは、脆弱性がふたたび生じるため推奨されません。
システムプロパティーを設定することによって特定のモードを構成する方法については、「java.lang.System
プロパティーの設定方法」を参照してください。
すべてのピアは、できるだけ早く RFC 5746 準拠の実装に更新する必要があります。この RFC 5746 修正を実行しても、再ネゴシエーションが必要な場合は、まだアップグレードされていないピアとの通信に影響が生じます。推奨されるいくつかの方法を次に示します。
ピアを再構築して再ネゴシエーションを要求しないようにする。
再ネゴシエーションは通常、Web サーバーが最初は匿名のクライアントブラウズを許可するが、後で SSL/TLS 認証済みクライアントを要求する場合や、Web サーバーが最初は弱い暗号化方式群を許可するが、あとで強い暗号化方式群を必要とする場合に、Web サーバーによって使用されます。これに代わる方法として、最初のネゴシエーション中にクライアント認証および強い暗号化方式群を要求する方法があります。これを行うには、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 修正では同じシステムプロパティーを使用するほか、RFC 5746 メッセージの使用を求める sun.security.ssl.allowUnsafeRenegotiation
システムプロパティーも追加されています。
すべてのアプリケーションを、できるだけ早くフェーズ 2 RFC 5746 修正にアップグレードする必要があります。
Java 暗号化拡張機能 (JCE) は、暗号化、鍵生成と鍵合意、およびメッセージ認証コード (MAC) アルゴリズム用のフレームワークおよび実装となるパッケージセットです。Java SE 5 より前の SunJSSE プロバイダは、構成されていれば JCE プロバイダを利用することができましたが、引き続き JCE を使用しない内部暗号化コードが含まれていました。Java SE 6 の SunJSSE プロバイダは、すべての暗号化操作のために JCE を排他的に使用するので、JCE に新しく追加された PKCS#11 のサポートを含め、JCE の特長や拡張機能の利点を自動的に活用することができます。これにより Java SE 6 の SunJSSE プロバイダは、ハードウェア暗号化アクセラレータを使用してパフォーマンスを著しく向上させ、キーストアとしてスマートカードを使用し、鍵および信頼性管理の柔軟性を高めることができます。
基盤となるアクセラレータハードウェアを使用するように Oracle PKCS#11 プロバイダが構成され、その PKCS#11 プロバイダを使用するように JCE が構成されていれば、ハードウェア暗号化アクセラレータは自動的に使用されます。PKCS#11 プロバイダは、プロバイダリストにあるほかの JCE/JCA プロバイダより前に構成する必要があります。Oracle PKCS#11 プロバイダの構成方法については、「PKCS#11 リファレンスガイド」を参照してください。
JCE での PKCS#11 のサポートにより、キーストアとしてスマートカードにアクセスすることもできます。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 アプリケーションが存続する間には、異なるスマートカードがスマートカードリーダーに挿入され、それらのスマートカードは異なるパスワードを使用して保護されます。J2SE 5 以前の API および SunX509 キーマネージャーは、これらの要件を十分に満たしていませんでした。そのため、Java SE 5 には新しい API が組み込まれており、新しい X509KeyManager 実装が SunJSSE プロバイダに追加されました。
java.security.KeyStore.Builder クラスは、KeyStore オブジェクトの構造と初期化データを抽象化します。パスワードのプロンプトに CallbackHandlers を使用でき、サブクラス化してアプリケーションにとって望ましい追加機能をサポートできます。たとえば、ビルダーを実装して、個々の KeyStore エントリを異なるパスワードで保護することが可能です。そのあと、javax.net.ssl.KeyStoreBuilderParameters クラスを使用し、これらのビルダーオブジェクトを 1 つ以上使用して KeyManagerFactory を初期化することができます。
「NewSunX509」と呼ばれる、SunJSSE プロバイダの新しい X509KeyManager 実装は、これらのパラメータをサポートしています。複数の証明書が使用可能な場合は、鍵の使用法が適切な証明書を選択し、期限切れの証明書より有効な証明書を優先させます。
スマートカードを使用する PKCS#11 ファイルベースキーストアと PKCS#12 ファイルベースキーストアの両方を使用するよう JSSE に指示する方法の例を次に示します。
import javax.net.ssl.*; import java.security.KeyStore.*; ... // Specify keystore builder parameters for PKCS#11 keystores Builder scBuilder = Builder.newInstance("PKCS11", null, new CallbackHandlerProtection(myGuiCallbackHandler)); // Specify keystore builder parameters for a specific PKCS#12 keystore Builder fsBuilder = Builder.newInstance("PKCS12", null, new File(pkcsFileName), new PasswordProtection(pkcsKsPassword)); // Wrap them as key manager parameters ManagerFactoryParameters ksParams = new KeyStoreBuilderParameters( Arrays.asList(new Builder[] { scBuilder, fsBuilder })); // Create KeyManagerFactory KeyManagerFactory factory = KeyManagerFactory.getInstance("NewSunX509"); // Pass builder parameters to factory factory.init(ksParams); // Use factory SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(factory.getKeyManagers(), null, null);
TLS_KRB5_WITH_RC4_128_SHA TLS_KRB5_WITH_RC4_128_MD5 TLS_KRB5_WITH_3DES_EDE_CBC_SHA TLS_KRB5_WITH_3DES_EDE_CBC_MD5 TLS_KRB5_WITH_DES_CBC_SHA TLS_KRB5_WITH_DES_CBC_MD5 TLS_KRB5_EXPORT_WITH_RC4_40_SHA TLS_KRB5_EXPORT_WITH_RC4_40_MD5 TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5これらの符号化方式を使用可能にするには、明示的に指定する必要があります。詳細については、SSLEngine.setEnabledCipherSuites() および SSLSocket.setEnabledCipherSuites() を参照してください。その他のすべての SSL/TLS 符号化方式同様、符号化方式がピアの側でサポートされていない場合は、暗号のネゴシエーション時に選択されません。また、アプリケーションまたはサーバーが必要な Kerberos 資格を取得できない場合は、Kerberos 符号化方式も選択されません。
TLS_KRB5_WITH_DES_CBC_SHA 符号化方式のみを使用する TLS クライアントの例を示します。
// Create socket SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(tlsServer, serverPort); // Enable only one cipher suite String enabledSuites[] = { "TLS_KRB5_WITH_DES_CBC_SHA" }; sslSocket.setEnabledCipherSuites(enabledSuites);
アプリケーションは、Java 認証・承認サービス (JAAS) と Kerberos ログインモジュールを使用して、自身の Kerberos 資格を取得できます。Java SE Development Kit 6 は、Kerberos ログインモジュールに付属しています。JSSE での Kerberos 符号化方式は、Java Generic Security Services (Java GSS) の使用法と同様に、JAAS プログラムがある場合とない場合に使用できます。
JAAS プログラムなしで Kerberos 符号化方式を使用するには、TLS サーバー JAAS 構成入力用に「com.sun.net.ssl.server」または「other」、および TLS クライアント用に「com.sun.net.ssl.client」または「other」というインデックス名を使用し、システムプロパティー javax.security.auth.useSubjectCredsOnly を false に設定する必要があります。たとえば、JAAS プログラムを使用しない TLS サーバーには次の JAAS 構成ファイルがあります。
com.sun.net.ssl.server { com.sun.security.auth.module.Krb5LoginModule required principal="host/mach1.imc.org@IMC.ORG" useKeyTab=true keyTab=mach1.keytab storeKey=true; };JAAS プログラミングを行わずに Java GSS および Kerberos を使用する方法の例は、「Java GSS チュートリアル」で説明されています。Java GSS 呼び出しを JSSE 呼び出しに置き換えることにより、JSSE の使用例に適応できます。
JAAS プログラムによって Kerberos 符号化方式を使用するには、任意のインデックス名を使用できます。これは、アプリケーションに、インデックス名を使用して JAAS LoginContext を作成し、JSSE 呼び出しを Subject.doAs() または Subject.doAsPrivileged() 呼び出しの内部にラップする役割があるからです。Java GSS および Kerberos で JAAS を使用する方法の例は、「Java GSS チュートリアル」で説明されています。Java GSS 呼び出しを JSSE 呼び出しに置き換えることにより、JSSE の使用例に適応できます。
JSSE アプリケーションで Kerberos を使用する場合の使用法または構成方法に関する問題については、Java GSS チュートリアルの「トラブルシューティング」セクションを参照してください。
Java SE 5 より前は、JSSE アプリケーションは、getPeerCertificates() および同様のメソッドを javax.net.ssl.SSLSession、javax.net.ssl.HttpsURLConnection、および javax.net.HandshakeCompletedEvent で使用して、ピアに関する情報を取得しました。ピアに証明書がない場合、SSLPeerUnverifiedException がスローされます。これらのメソッドの動作は、Java SE 6 でも変わっていません。つまり、接続が Kerberos 暗号化方式を使用してセキュリティー保護された場合、これらのメソッドは SSLPeerUnverifiedException をスローします。
アプリケーションがピアのアイデンティティーまたはピアに送信されたアイデンティティーのみを判別する必要がある場合は、それぞれ getPeerPrincipal() および getLocalPrincipal() メソッドを使用する必要があります。getPeerCertificates() および getLocalCertificates() は、それらの証明書の内容を調べる必要がある場合にのみ使用します。また、認証されたピアに証明書がない場合の処理の準備が必要です。
javax.security.auth.kerberos.ServicePermission(serverPrincipal, "initiate");serverPrincipal は、TLS クライアントが通信する TLS サーバーの Kerberos 主体名です。host/mach1.imc.org@IMC.ORG などです。TLS サーバーアプリケーションには、次のアクセス権が必要です。
javax.security.auth.kerberos.ServicePermission(serverPrincipal, "accept");serverPrincipal は、TLS サーバーの Kerberos 主体名です。host/mach1.imc.org@IMC.ORG などです。サーバーまたはクライアントが KDC にコンタクトする必要がある場合 (その資格がローカルにキャッシュされていない場合など)、次のアクセス権も必要です。
javax.security.auth.kerberos.ServicePermission(tgtPrincipal, "initiate");tgtPrincipal は KDC の主体名です。krbtgt/IMC.ORG@IMC.ORG などです。
SunJSSE
プロバイダは、pkcs12 ファイルの読み書きのための PKCS12 java.security.KeyStore
形式の実装を提供します。この形式は、Netscape/Mozilla、Microsoft の Internet Explorer、OpenSSL などほかのツールキットやアプリケーションでもサポートされ、鍵と証明書をインポートおよびエクスポートします。たとえば、これらの実装は、クライアントの証明書と鍵を「.p12」ファイル名拡張子を使用してファイルにエクスポートできます。
SunJSSE
プロバイダでは、キーストア型「pkcs12」(または PKCS12、大文字と小文字は区別されません) を使って、KeyStore API を介して PKCS12 にアクセスできます。さらに keytool コマンドと、pkcs12
に設定された -storetype
オプションを使用して、インストールされた鍵および関連する証明書を表示できます。keytool については「セキュリティーツール」を参照してください。
問題: SSL 接続のネゴシエーション中に、クライアントまたはサーバーが CertificateException をスローします。
原因 1: 多くの場合、リモートサイドがローカルサイドにとって不明な証明書を送信することが原因です。
解決法 1: このような問題を解決するには、デバッグをオンにして、証明書のロード時、およびネットワーク接続からの受信時を観察します。デバッグについては、「デバッグユーティリティー」を参照してください。多くの場合、間違ったトラストファイルをロードしたため、受信した証明書がトラストメカニズムにとって不明です。詳細については、次のセクションを参照してください。
原因 2: システムクロックが正しく設定されていません。
解決法 2: クロックが正しく設定されていない場合、認識された時間が証明書の有効期間外になっている可能性があります。トラストストアの有効な証明書と置き換えないかぎり、システムはこの証明書を無効とみなし、例外をスローします。
問題: 信頼できる証明書を PKCS12 キーストアに格納しようとすると、java.security.KeyStoreException: TrustedCertEntry not supported.
がスローされます
原因 1: 信頼できる証明書の pkcs12 キーストアへの格納はサポートしていません。PKCS12 は、主に非公開鍵と関連する証明書チェーンの配信に使用されます。「信頼できる」証明書の概念はありません。相互運用性の観点から、ほかの pkcs12 ベンダーにも同じ制限があります。Mozilla や Internet Explorer などのブラウザは、信頼できる証明書のみを持つ pkcs12 ファイルは受け入れません。
解決法 1: 信頼できる証明書の格納には、JKS (または JCEKS) キーストアを使用します。
問題: JSSE を使うプログラムを実行すると、SSL サービスが利用できないという例外が発生します。たとえば、次のような例外が発生します。
Exception in thread "main" java.net.SocketException: no SSL Server Sockets Exception in thread "main": SSL implementation not available
原因: SSLContext
の初期化時に問題が発生しています。たとえば、キーストアに正しいパスワードが入力されなかったか、キーストアが破損している可能性があります。(注:この種のエラーが発生するのは、以前に未知の形式のキーストアを提供する JDK ベンダーがあったためです。)
解決法: 初期化パラメータを確認します。指定したキーストアが有効であり、指定したパスワードが正しいことを確認します。keytool を使うと、キーストアとその内容を調べることができます。
問題: 単純な SSL Server プログラムを実行しようとすると、次の例外がスローされます。
Exception in thread "main" javax.net.ssl.SSLException: No available certificate corresponding to the SSL cipher suites which are enabled...
原因: 必要な鍵データの型は暗号群によって異なります。たとえば、RSA 暗号群が有効になっている場合、キーストアで RSA の keyEntry
を有効にする必要があります。該当する鍵を使用できない場合、その暗号群を使用することはできません。有効になっているすべての暗号群の鍵のエントリが使用できない場合、この例外がスローされます。
解決法: 暗号群の型にあった鍵エントリを作成します。または匿名の暗号群を使用します。匿名の暗号群には、"man-in-the-middle" 攻撃に対して脆弱であるという潜在的な危険性があります。詳細については RFC 2246 を参照してください。正しいキーストアおよび証明書を渡す方法については、次のセクションを参照してください。
問題 1: ハンドシェーク時にクライアントやサーバーがこの例外をスローします。
原因 1: SSL 接続の両側が共通の暗号群について合意している必要があります。クライアントの暗号群セットとサーバーの暗号群セットの共通点がない場合、この例外がスローされます。
解決法 1: 有効な暗号群セットを構成し、共通の暗号群を追加します。さらに、非対称の暗号群に適切な keyEntry
が提供されるようにします。このセクションの「例外 "No available certificate..."」を参照してください。
問題 2: DSA ベースの証明書しかないサーバーのファイルに Netscape Navigator や Microsoft Internet Explorer (IE) でアクセスすると、共通の暗号群がないという実行時例外が発生します。
原因 2: デフォルトでは、keytool で作成された keyEntries
は DSA 公開鍵を使用します。キーストア内に DSA の keyEntries
が存在する場合、使用できるのは DSA ベースの暗号群のみです。デフォルトでは、Navigator と IE は RSA ベースの暗号群のみを送信します。クライアントとサーバーの暗号群セットの共通点がないため、この例外がスローされます。
解決法 2: Navigator や IE を使う場合は、RSA ベースの鍵を使う証明書を作成します。そのためには、keytool 使用時に -keyalg
RSA オプションを指定する必要があります。たとえば、
keytool -genkeypair -alias duke \ -keystore testkeys -keyalg rsa
問題: 最初のアクセスで JSSE が停止したように見えます。
原因: JSSE には乱数のセキュアなソースが必要です。初期化には時間がかかります。
解決法: 別の乱数発生関数を使用するか、オーバーヘッドが通知されない場合は次の方法で先に初期化します。
SecureRandom sr = new SecureRandom(); sr.nextInt(); SSLContext.init(..., ..., sr);
<java-home>/lib/security/java.security
ファイルは、SecureRandom のシードデータのソースを指定する方法も提供します。詳細は、ファイルを参照してください。
HttpsURLConnection
クラスを使用するコードが JSSE 1.0.x で ClassCastException
をスローする問題: 次のコード (抜粋) は、JSSE 1.0.x の com.sun.net.ssl.HttpsURLConnection
で作成されたものです。
import com.sun.net.ssl.*; ...deleted... HttpsURLConnection urlc = new URL("https://foo.com/").openConnection();現在のリリースで実行すると、このコードは
javax.net.ssl.HttpsURLConnection
を返し、ClassCastException
をスローします。
原因: デフォルトでは、"https" URL を開くと javax.net.ssl.HttpsURLConnection
が作成されます。
解決法: 以前のリリースの JDK (Java SE 6 SDK 以前) には、「https」URL の実装が付属していません。JSSE 1.0.x 実装は、"https" URL ハンドラを提供します。インストールガイドにも、URL ハンドラの検索パスを設定して JSSE 1.0.x の com.sun.net.ssl.HttpsURLConnection
実装を取得する方法が記載されています。
現在のリリースでは、URL ハンドラのデフォルトの検索パスに "https" ハンドラが指定されています。その結果、javax.net.ssl.HttpsURLConnection
のインスタンスが返されます。java.protocol.handler.pkgs
変数を使って、以前の JSSE 1.0.x 実装パスを URL 検索パスに追加することにより、com.sun.net.ssl.HttpsURLConnection
を取得できます。この場合、上記のコードが例外をスローすることはありません。
% java -Djava.protocol.handler.pkgs=\ com.sun.net.ssl.internal.www.protocol YourClassまたは
System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
ClientHello
メッセージの送信後ソケットが切断される問題: 接続するために ClientHello
メッセージを送信したあと、ただちにソケットが切断されます。
原因: SSL/TLS サーバーの中には、受信した ClientHello
メッセージの形式を判断できない場合や、このメッセージのプロトコルのバージョンがサポート対象外である場合、接続を切断するものがあります。
解決法: SSLSocket.setEnabledProtocols
でプロトコルの調整を試みてください。たとえば、従来のサーバー実装には、SSLv3 のみに対応し、TLS に対応していないものがあります。理論的に言えば、こうした実装は SSLv3 でネゴシエーションを行うはずですが、実際にはハングアップするだけです。後方互換性のため、一部のサーバー実装 (SunJSSE など) は SSLv3/TLS の ClientHello
を SSLv2 の ClientHello
パケットにカプセル化して送信します。SunJSSE プロバイダはこの機能をサポートします (「SSLv2Hello のデフォルト設定」を参照してください)。この機能を使用する場合は、必要に応じて setEnabledProtocols
を呼び出して SSLv2Hello
を有効にします。
NoSuchAlgorithmException
が発生する問題: ハンドシェークが試行され、必要なアルゴリズムが見つからない場合は失敗します。例は次のとおりです。
Exception in thread ...deleted... ...deleted... Caused by java.security.NoSuchAlgorithmException: Cannot find any provider supporting RSA/ECB/PKCS1Paddingまたは
Caused by java.security.NoSuchAlgorithmException: Cannot find any provider supporting AES/CBC/NoPadding
原因: SunJSSE は、その暗号化アルゴリズムすべてで JCE を使用します。デフォルトでは、Oracle JDK は Standard Extension ClassLoader を使用して、<java-home>/lib/ext/sunjce_provider.jar にある SunJCE プロバイダをロードします。ファイルが見つからないかロードできない場合、または SunJCE プロバイダが Provider
メカニズムから登録解除されており、JCE からの代替実装を利用できない場合、この例外が発生します。
解決法: ファイルがロード可能であることをチェックして SunJCE が使用可能であることを確認し、プロバイダが Provider
インタフェースに登録されていることを確認します。SSL 接続のコンテキストで次のコードを実行してみます。
import javax.crypto.*; System.out.println("=====Where did you get AES====="); Cipher c = Cipher.getInstance("AES/CBC/NoPadding"); System.out.println(c.getProvider());
問題: Web サーバーから SSL でアプリケーションリソースを取得しようとしたときに com.sun.deploy.net.FailedDownloadException
を受け取り、Web サーバーでは Server Name Indication (SNI) 拡張機能を持つ仮想ホストが使用されている場合 (Apache HTTP サーバーなど)、Web サーバーが正しく構成されていないことがあります。
原因: Java SE 7 以降では、JSSE クライアントの SNI 拡張がサポートされているため、要求された仮想サーバーのホスト名は、SSL ハンドシェーク中にクライアントからサーバーに送信された最初のメッセージに含まれています。要求されたホスト名 (Server Name Indication) が、仮想ホストの構成に指定されているはずの期待されるサーバー名と一致しない場合、サーバーはクライアントの接続要求を拒否することがあります。これにより、SSL ハンドシェークの認識されない名前の警告がトリガーされ、FailedDownloadException
がスローされます。
解決法: 問題を適切に診断するために、Java コンソールを使用してトレースを有効にします。詳細は、「トレースおよびロギング」を参照してください。問題の原因が javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
の場合、SNI のための仮想ホスト構成が正しくない可能性があります。Apache HTTP サーバーを使用している場合、仮想ホストの構成については、「Name-based Virtual Host Support」を参照してください。特に、<VirtualHost>
ブロック内で ServerName
指示が正しく構成されていることを確認してください。
詳細については、次の情報を参照してください。
JSSE には、動的デバッグのトレースをサポートする機能があります。これは、Java SE 6 プラットフォームでデバッグのアクセス制御に失敗した場合に使用するサポート機能に似ています。ジェネリック Java 動的デバッグトレースサポートにはシステムプロパティー java.security.debug
でアクセスしますが、JSSE 固有の動的デバッグトレースサポートにはシステムプロパティー javax.net.debug
でアクセスします。
注:デバッグユーティリティーは、公式にサポートされている JSSE 機能ではありません。
JSSE 動的デバッグユーティリティーのオプションを表示するには、java
コマンドで次のコマンド行オプションを使用します。
-Djavax.net.debug=help
注:デバッグ用に設計されたユーティリティーでクラスを使用しないプログラムを実行しているとき、どちらの動的デバッグユーティリティーで help
を指定しても、デバッグオプションは使用できません。
デバッグオプションのリストを表示させる方法の例を、次に示します。
java -Djavax.net.debug=help MyAppMyApp は、JSSE クラスを使用するアプリケーションです。MyApp は、デバッグのヘルプ情報が表示されると動作しなくなります。ヘルプコードによりアプリケーションが終了するためです。
現在のオプションは次のとおりです。
all turn on all debugging ssl turn on ssl debugging The following can be used with ssl: record enable per-record tracing handshake print each handshake message keygen print key generation data session print session activity defaultctx print default SSL initialization sslctx print SSLContext tracing sessioncache print session cache tracing keymanager print key manager tracing trustmanager print trust manager tracing handshake debugging can be widened with: data hex dump of each handshake message verbose verbose handshake message printing record debugging can be widened with: plaintext hex dump of record plaintext packet print raw SSL/TLS packets
javax.net.debug
プロパティー値は、all
または ssl
を指定する必要があり、デバッグ指定子がそのあとに続く場合もあります。1 つまたは複数のオプションが使用できます。オプションをセパレータで区切る必要はありませんが、「:」や「,」を使用すると読みやすくなります。どのセパレータも使用でき、オプションキーワードの順序も重要ではありません。
このデバッグ情報の見方の説明は、ガイド「SSL/TLS 接続のデバッグ」を参照してください。
java -Djavax.net.debug=all MyApp
java -Djavax.net.debug=ssl:handshake:data MyApp
java -Djavax.net.debug=SSL,handshake,data,trustmanager MyApp
次のセクションでは、次のコード例について説明します。
このセクションでは、JSSE を使って、セキュアでないソケット接続をセキュアなソケット接続に変換するソースコードの例を説明します。このセクションのコードは『Java SE 6 Network Security』(Marco Pistoia ほか著) から引用したものです。
まず、「SSL を使用しないソケットの例」で、セキュアでないソケットを使ってクライアントとサーバー間の通信を設定するサンプルコードを示します。次に、「SSL を使用するソケットの例」ではこのコードを変更し、JSSE を使用してセキュアなソケット通信を設定します。
サーバーとして動作し、ソケットを使ってクライアントと通信する Java プログラムを作成する場合、次のようなコードでソケット通信を設定します。
import java.io.*; import java.net.*; . . . int port = availablePortNumber; ServerSocket s; try { s = new ServerSocket(port); Socket c = s.accept(); OutputStream out = c.getOutputStream(); InputStream in = c.getInputStream(); // Send messages to the client through // the OutputStream // Receive messages from the client // through the InputStream } catch (IOException e) { }
ソケットを使ってサーバーとの通信を設定するクライアントコードの例を、次に示します。
import java.io.*; import java.net.*; . . . int port = availablePortNumber; String host = "hostname"; try { s = new Socket(host, port); OutputStream out = s.getOutputStream(); InputStream in = s.getInputStream(); // Send messages to the server through // the OutputStream // Receive messages from the server // through the InputStream } catch (IOException e) { }
サーバーとして動作し、セキュアなソケットでクライアントと通信する Java プログラムを作成する場合、次のようなコードでソケット通信を設定します。セキュアでないソケットを使った通信のプログラムとこのプログラムとの違いは、太字で示されています。
import java.io.*; import javax.net.ssl.*; . . . int port = availablePortNumber; SSLServerSocket s; try { SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); s =(SSLServerSocket)sslSrvFact.createServerSocket(port); SSLSocket c = (SSLSocket)s.accept(); OutputStream out = c.getOutputStream(); InputStream in = c.getInputStream(); // Send messages to the client through // the OutputStream // Receive messages from the client // through the InputStream } catch (IOException e) { }
セキュアなソケットを使ってサーバーとの通信を設定するクライアントコードの例を、次に示します。セキュアでないソケットとの違いは、太字で示されています。
import java.io.*; import javax.net.ssl.*; . . . int port = availablePortNumber; String host = "hostname"; try { SSLSocketFactory sslFact = (SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket s = (SSLSocket)sslFact.createSocket(host, port); OutputStream out = s.getOutputStream(); InputStream in = s.getInputStream(); // Send messages to the server through // the OutputStream // Receive messages from the server // through the InputStream } catch (IOException e) { }
JSSE サンプルプログラムでは、JSSE を使って次の処理を行う方法を示します。
サンプルコードを使用する場合、サンプルプログラムは JSSE の使い方を示すためのものである点に注意してください。堅牢なアプリケーションを開発するためのものではありません。
注:セキュアな通信を設定すると、アルゴリズムが複雑になります。サンプルプログラムでは、設定プロセス中のフィードバックがありません。プログラム実行時には、一定時間待っても結果が表示されない場合があります。システムプロパティー javax.net.debug
の設定を all
にして、プログラムを実行すると、フィードバックが表示されます。このデバッグ情報の見方の説明は、ガイド「SSL/TLS 接続のデバッグ」を参照してください。
ほとんどのサンプルコードは、このドキュメントと同じディレクトリの samples サブディレクトリに格納されています。次のリンクから、すべてのサンプルファイルのリストとテキストファイルを表示できます。このページから、すべてのサンプルファイルが含まれた zip ファイルもダウンロードできます。zip ファイルは、このドキュメントを Web 経由で表示している場合に便利です。
次のセクションでは、サンプルについて説明します。詳細については、README を参照してください。
samples/sockets
ディレクトリにあるサンプルプログラムは、クライアントとサーバーとの間でセキュアなソケット接続を設定する方法を示しています。
サンプルのクライアントプログラムを実行中に、商用 Web サーバーなどの既存のサーバーと通信できます。また、サンプルのプログラムサーバー ClassFileServer
と通信することもできます。サンプルのクライアントプログラムとサーバープログラムは、同じネットワークに接続された別個のマシンで実行することも、別々のターミナルウィンドウから同一マシン上で実行することもできます。
samples/sockets/client
ディレクトリのサンプル SSLSocketClient
* プログラム (および「HTTPS 接続を表すサンプルコード」で説明する URLReader* プログラム) は、すべて ClassFileServer
サンプルサーバープログラムで実行できます。この例は、「ClassFileServer
を使った SSLSocketClientWithClientAuth
の実行」に示されています。URLReader
、SSLSocketClient
、または SSLSocketClientWithTunneling
を ClassFileServer
で実行する場合も、同様の変更ができます。
クライアントとサーバーとの間でメッセージを送信しようとすると認証エラーが発生する場合、Web サーバーと ClassFileServer
のどちらを使用しているとしても、必要な鍵がトラストストア (トラスト鍵データベース) にない可能性があります。たとえば、ClassFileServer
は「testkeys」というキーストアを使用します。これには、SSL ハンドシェーク中に必要な「localhost」の公開鍵が含まれています。「testkeys」は ClassFileServer
ソースと同じディレクトリ samples/sockets/server
にあります。参照するトラストストアで、「localhost」の公開鍵に対応する証明書をクライアントが検索できない場合、認証エラーが発生します。次のセクションで説明するように、samplecacerts
トラストストア (「localhost」公開鍵がある) の扱いには注意してください。
クライアントとサーバーとの間のセキュアなソケット接続を作成するサンプルプログラムを実行する場合は、適切な証明書ファイル (トラストストア) を利用できるようにしておく必要があります。クライアントプログラムとサーバープログラムの両方で、samples
ディレクトリの samplecacerts
証明書ファイルを使用します。この証明書ファイルを使うと、クライアントがサーバーを認証できるようになります。このファイルには、JDK (cacerts
ファイルにある) に付属する、一般的な証明書発行局の発行した証明書がすべて含まれており、また、サンプルサーバー ClassFileServer
との通信時にクライアントが「localhost」を証明するのに必要な「localhost」の証明書も含まれています。(ClassFileServer
は、「localhost」の公開鍵を含むキーストアを使用します。これは、samplecacerts
の公開鍵に対応しています。 )
クライアントとサーバーの両方で samplecacerts
ファイルを使用するには、ファイルを <java-home>/lib/security/jssecacerts
ファイルにコピーして名前を cacerts
に変更し、<java-home>/lib/security/cacerts
ファイルと置き換えるか、クライアントとサーバーの java
コマンドを実行中に、次のオプションをコマンド行に追加します。
-Djavax.net.ssl.trustStore=path_to_samplecacerts_file
(<java-home>
の詳細については、「インストールディレクトリ <java-home>」を参照してください。)
samplecacerts
トラストストアのパスワードは changeit
です。keytool を使って、サンプルに独自の証明書を置き換えることができます。
Netscape Navigator や Internet Explorer などのブラウザを使用して ClassFileServer
にあるサンプルの SSL サーバーにアクセスすると、ダイアログボックスが開いて、証明書が認識されないというメッセージが表示されます。これは、サンプルプログラムで使用する証明書は自己署名付きのもので、テスト用にすぎないためです。現在のセッションで証明書に同意できます。SSL サーバーのテストが終了したあと、ブラウザを終了し、ブラウザの名前空間からテスト用証明書を削除します。
クライアント認証の場合、適切なディレクトリの別の「duke」証明書を使用できます。公開鍵/証明書も、samplecacerts ファイルに格納されています。
SSLSocketClient
の実行SSLSocketClient.java プログラムは、SSLSocket
を使うクライアントを作成し、HTTP 要求を送信して HTTPS サーバーから応答を受け取る方法を実際に示します。このプログラムの出力は、https://www.verisign.com/index.html
の HTML ソースです。
ファイアウオールの外側で、このプログラムを出荷時の状態で実行しないでください。ファイアウォールの外側で実行すると、JSSE はファイアウォールを通じた www.verisign.com
へのパスを検出できないので、UnknownHostException
を受け取ります。ファイアウォールの外側から実行できる同等のクライアントを作成するには、サンプルプログラム SSLSocketClientWithTunneling
で示すように、プロキシトンネリングを設定します。
SSLSocketClientWithTunneling
の実行SSLSocketClientWithTunneling.java プログラムは、ファイアウォールの外側からセキュアな Web サーバーにアクセスするプロキシトンネリングの方法を示します。このプログラムを実行するには、次の Java システムプロパティーに適切な値を設定する必要があります。
java -Dhttps.proxyHost=webproxy
-Dhttps.proxyPort=ProxyPortNumber
SSLSocketClientWithTunneling
注:プロキシを指定する際、-D
オプション (青字) はオプションです。webproxy
は使用するプロキシホスト名に、ProxyPortNumber
は適切なポート番号に置き換えてください。
プログラムは https://www.verisign.com/index.html
の HTML ソースファイルを返します。
SSLSocketClientWithClientAuth
の実行SSLSocketClientWithClientAuth.java プログラムは、サーバーから要求された場合にキーマネージャーを設定し、クライアント認証を行う方法を示します。このプログラムも、クライアントがファイアウォールの外側にはいないことを前提にしています。SSLSocketClientWithTunneling
の例に従ってプログラムを変更すれば、ファイアウォールの内側から接続することもできます。
このプログラムを実行するには、次の 3 つのパラメータを指定する必要があります。ホスト、ポート番号、および要求されたファイルパスです。前回の例を反映させるため、このプログラムをクライアント認証なしで実行できます。ホストに www.verisign.com
、ポート番号に 443
、要求されたファイルパスに https://www.verisign.com/
を設定します。これらのパラメータを使用したときの出力が、https://www.verisign.com/
の HTML ソースです。
SSLSocketClientWithClientAuth
を実行してクライアント認証を行うには、クライアント認証を要求するサーバーにアクセスする必要があります。このサーバーには、サンプルプログラム ClassFileServer
を使用できます。これについては、次のセクションで説明します。
ClassFileServer
の実行ここで ClassFileServer
と呼ばれているプログラムは、ClassFileServer.java と ClassServer.java の 2 つのファイルで構成されています。
これらを実行するには、ClassFileServer.class
を実行します。その際は次のパラメータが必要です。
port
- ポートのパラメータは、利用できる未使用のポート番号です。たとえば、2001 のような数字を使うことができます。docroot
- このパラメータは、検索するファイルを含むサーバーのディレクトリを表します。たとえば、Solaris では /home/userid/
(userid
は特定のユーザー ID)、Microsoft Windows では c:\
となります。TLS
- オプションのパラメータです。サーバーが SSL または TLS を使用することを表します。true
- オプションのパラメータです。クライアント認証が必要なことを表します。このパラメータは、TLS パラメータが設定されているかどうかだけを参照します。注意 1:TLS
および true
パラメータはオプションです。使用しない場合は、TLS ではない通常のファイルサーバーだけが認証なしで使用され、何も起こりません。これは、片方の側 (クライアント) が TLS とネゴシエーションを行おうとしても、もう一方の側 (サーバー) はネゴシエーションを行おうとしないため、通信ができないからです。
注意 2:サーバーは "GET /..." の形式で GET 要求を期待します。"..." はファイルへのパスを表します。
ClassFileServer
を使った SSLSocketClientWithClientAuth
の実行サンプルプログラム SSLSocketClientWithClientAuth および ClassFileServer
を使って、認証済みの通信を設定できます。この通信では、クライアントとサーバーが相互に認証します。両方のサンプルプログラムを同じネットワークに接続された別個のマシン上で実行することも、同じマシン上の別のターミナルウィンドウまたはコマンドプロンプトウィンドウから実行することもできます。クライアントとサーバーを設定するには、次の操作を実行します。
ClassFileServer
プログラムを、1 台のマシンやターミナルウィンドウから実行します。「ClassFileServer
の実行」を参照してください。SSLSocketClientWithClientAuth
プログラムを別のマシンやターミナルウィンドウで実行します。
SSLSocketClientWithClientAuth
には、次のパラメータが必要です。host
- ClassFileServer
を実行するマシンのホスト名です。port
- ClassFileServer
に指定したのと同じポートです。requestedfilepath
- サーバーから検索するファイルのパスです。このパラメータには、/filepath
を使用します。GET 文の一部として使用されるので、ファイルパスにはフォワードスラッシュが必要です。GET 文には、稼動中のオペレーティングシステムの種類にかかわらず、フォワードスラッシュが必要です。文の構成は次のようになります。
"GET " + requestedfilepath + " HTTP/1.0"
ClassFileServer
が動作しているローカルマシンに接続できます。
JSSE を介してセキュアな通信にアクセスするためのプライマリ API は 2 つあります。1 つは任意のセキュアな通信に使用できるソケットレベルの API で、SSLSocketClient
、SSLSocketClientWithTunneling
、および SSLSocketClientWithClientAuth
(ClassFileServer
を使用する場合と使用しない場合がある) のサンプルプログラムに示されています。
もう 1 つはもっと簡単な方法で、標準の Java URL API を使う方法です。java.net.URL
クラスを使った "https" URL プロトコルまたはスキームを使って、SSL が使用できる Web サーバーとセキュアに通信できます。
"https" URL スキームへのサポートは一般的なブラウザの多くに実装されており、JSSE に付属のソケットレベル API を必要とせずにセキュアな通信にアクセスできます。
URL の例を次に示します。
"https://www.verisign.com""https" URL 実装のトラストおよび鍵の管理は、環境に固有です。JSSE 実装は、"https" URL 実装を提供します。別の https プロトコル実装を使用する場合は、パッケージ名に java.protocol.handler.pkgs
システムプロパティーを設定できます。詳細については、java.net.URL
クラスのドキュメントを参照してください。
JSSE でダウンロードできるサンプルには、HTTPS 接続の作成方法を示すサンプルプログラムが 2 つ含まれています。サンプルプログラム URLReader.java
と URLReaderWithOptions.java
は、どちらも urls
ディレクトリにあります。
URLReader.java プログラムは、セキュアなサイトにアクセスする URL クラスの使い方を示します。このプログラムの出力は、https://www.verisign.com/
の HTML ソースです。デフォルトで、JSSE に付属の HTTPS プロトコル実装が使用されます。別の実装を使用する場合は、システムプロパティー java.protocol.handler.pkgs
の値を、実装を含むパッケージ名に設定する必要があります。
ファイアウォールの外側でサンプルコードを実行している場合は、システムプロパティー https.proxyHost
および https.proxyPort
を設定する必要があります。たとえば、ポート 8080 でプロキシホスト "webproxy" を使う場合は、java
コマンドで次のオプションを使用します。
-Dhttps.proxyHost=webproxy -Dhttps.proxyPort=8080
または、ソースコードのシステムプロパティーに java.lang.System
のメソッド setProperty
を設定することもできます。たとえば、オプションでコマンド行を使う代わりに、使用するプログラムに次の行を含めることができます。
System.setProperty("java.protocol.handler.pkgs", "com.ABC.myhttpsprotocol"); System.setProperty("https.proxyHost", "webproxy"); System.setProperty("https.proxyPort", "8080");
注:Windows 95 または Windows 98 で実行している場合、コマンド行オプションをすべて含めるには、MS-DOS プロンプトで使用できる文字数では足りない場合があります。その場合、entire コマンドを使用して .bat ファイルを作成するか、ソースコードにシステムプロパティーを追加して、ソースコードを再コンパイルします。
URLReaderWithOptions.java プログラムは基本的には URLReader と同じですが、実行時にプログラムの引数として次のシステムプロパティーのどれか、または全部をオプションで入力できる点が異なります。
URLReaderWithOptions を実行するには、次のコマンドを 1 行で入力します。
java URLReaderWithOptions [-h proxyhost -p proxyport] [-k protocolhandlerpkgs] [-c ciphersarray] myApp
注:複数のプロトコルハンドラを、縦線で区切った項目のリストで protocolhandlerpkgs
に含めることができます。複数の SSL 暗号群名を、カンマで区切った項目のリストで ciphersarray
に含めることができます。可能な暗号群名は SSLSocket.getSupportedCipherSuites()
呼び出しで返されたものと同じです。暗号群は SSL および TLS プロトコルの仕様から命名されています。
Oracle が提供するデフォルトのプロトコルハンドラ実装以外の HTTPS プロトコルハンドラ実装を使う場合は、protocolhandlerpkgs
引数だけが必要です。
ファイアウォールの外側で実行している場合は、プロキシホストおよびプロキシポートの引数を含める必要があります。また、使用できる暗号群のリストを含めることもできます。
次に、URLReaderWithOptions の実行例と、ポート 8080 にプロキシポート "webproxy" を指定する場合の例を示します。
java URLReaderWithOptions -h webproxy -p 8080
samples/rmi
ディレクトリのサンプルコードは、セキュアな RMI 接続の作成方法を示しています。サンプルコードは、RMI のサンプルに基づいています。基本的には "Hello World" のサンプルを変更し、カスタム RMI ソケットファクトリをインストールして使用します。
RMI については、Java RMI ドキュメントを参照してください。この Web ページは、RMI のチュートリアルと RMI に関するほかの情報を記載した Web ページです。
SSLEngine
の使用を表すサンプルコードSSLEngine
は、アプリケーション開発者に I/O および計算戦略を選択するときの柔軟性を提供するために、Java 2 プラットフォームの Java SE 5 リリースに導入されました。SSLEngine
は、SSL/TLS 実装を特定の I/O 抽象化 (シングルスレッド SSLSockets
など) に結びつけるのではなく、I/O および計算の制約を SSL/TLS 実装から除外します。
前述したように、SSLEngine
は高度な API であり、不用意に使用することはできません。ここでは、その使用を説明するのに役立つ入門用サンプルコードを示します。最初のデモは、ほとんどの I/O およびスレッドの発行を除外し、SSLEngine メソッドの多くに重点を置きます。2 番目のデモは、より現実的な例であり、SSLEngine
がどのように Java NIO と結合して基本的な HTTP/HTTPS サーバーを作成するかを示します。
SSLEngineSimpleDemo
の実行SSLEngine
の操作に重点を置いています。このアプリケーションは、一般的な ByteBuffer
によって SSL/TLS メッセージを交換する 2 つの SSLEngine
を作成します。1 つのループがすべてのエンジン操作を順番に実行して、セキュアな接続の確立 (ハンドシェーク)、アプリケーションデータの転送、およびエンジンのクローズを示します。
SSLEngineResult
は、SSLEngine
の現在の状態に関して多くの情報を提供します。この例では、すべての状態を調べてはいません。I/O およびスレッドの発行を適度に単純化しているため本番稼動環境に適した例ではありませんが、SSLEngine
の全体的な機能の説明に有用です。
NIO
ベースのサーバーの実行<jdk-home>/samples/nio/server
ディレクトリにバンドルされています。
SSLEngine
によって提供される柔軟性を十分に利用するには、最初に I/O やスレッドのモデルなどの相補的な API を理解します。
大規模なアプリケーションの開発者が有用と考える I/O モデルは、NIO SocketChannel
です。NIO は、java.net.Socket API に内在するスケーリングの問題のいくつかを解決するために部分的に導入されました。SocketChannel には、次のようなさまざまな操作モードがあります。
SSLEngine
を使用してセキュアな HTTPS サーバーを作成する方法も示します。サーバーは本番稼動の品質ではありませんが、これらの新しい API の多くを実際に示しています。
サンプルディレクトリには README.txt ファイルがあり、サーバーの紹介、構築および構成方法の説明、コードレイアウトの概要が含まれています。SSLEngine
のユーザーにとってもっとも重要なファイルは、ChannelIO.java
および ChannelIOSecure.java
です。
keytool
を使って、JSSE での使用に適した単純な JKS キーストアを作成します。キーストア内に (公開/非公開鍵を持つ) keyEntry
を作成し、トラストストア内に対応する trustedCertEntry
(公開鍵のみ) を作成します。クライアント認証の場合、クライアントの証明書に対して同じ処理を行う必要があります。注:信頼できるアンカーの PKCS12 での格納はサポートされていません。信頼できるアンカーの格納には JKS、非公開鍵の格納には PKCS12 を使用する必要があります。注: ここでは、各ステップに関する詳しい解説は省略します。詳細については、Solaris または Microsoft Windows の keytool に関するドキュメントを参照してください。ユーザー入力は太字で示します。
% keytool -genkeypair -alias duke -keyalg RSA \ -validity 7 -keystore keystore Enter keystore password: password What is your first and last name? [Unknown]: Duke What is the name of your organizational unit? [Unknown]: Java Software What is the name of your organization? [Unknown]: Oracle, Inc. What is the name of your City or Locality? [Unknown]: Palo Alto What is the name of your State or Province? [Unknown]: CA What is the two-letter country code for this unit? [Unknown]: US Is CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US correct? [no]: yes Enter key password for <duke> (RETURN if same as keystore password): <CR>これが、サーバーの使用するキーストアです。
keyEntry
(赤字) になっている点に注目してください。これは、このエントリに非公開鍵が関連付けられていることを示します。
% keytool -list -v -keystore keystore Enter keystore password: password Keystore type: jks Keystore provider: SUN Your keystore contains 1 entry Alias name: duke Creation date: Dec 20, 2001 Entry type: keyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Serial number: 3c22adc1 Valid from: Thu Dec 20 19:34:25 PST 2001 until: Thu Dec 27 19:34:25 PST 2001 Certificate fingerprints: MD5: F1:5B:9B:A1:F7:16:CF:25:CF:F4:FF:35:3F:4C:9C:F0 SHA1: B2:00:50:DD:B6:CC:35:66:21:45:0F:96:AA:AF:6A:3D:E4:03:7C:74
% keytool -export -alias duke -keystore keystore -rfc \ -file duke.cer Enter keystore password: password Certificate stored in file <duke.cer> % cat duke.cer -----BEGIN CERTIFICATE----- MIICXjCCAccCBDwircEwDQYJKoZIhvcNAQEEBQAwdjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNB MRIwEAYDVQQHEwlQYWxvIEFsdG8xHzAdBgNVBAoTFlN1biBNaWNyb3N5c3RlbXMsIEluYy4xFjAU BgNVBAsTDUphdmEgU29mdHdhcmUxDTALBgNVBAMTBER1a2UwHhcNMDExMjIxMDMzNDI1WhcNMDEx MjI4MDMzNDI1WjB2MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0 bzEfMB0GA1UEChMWU3VuIE1pY3Jvc3lzdGVtcywgSW5jLjEWMBQGA1UECxMNSmF2YSBTb2Z0d2Fy ZTENMAsGA1UEAxMERHVrZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1loObJzNXsi5aSr8 N4XzDksD6GjTHFeqG9DUFXKEOQetfYXvA8F9uWtz8WInrqskLTNzwXgmNeWkoM7mrPpK6Rf5M3G1 NXtYzvxyi473Gh1h9k7tjJvqSVKO7E1oFkQYeUPYifxmjbSMVirWZgvo2UmA1c76oNK+NhoHJ4qj eCUCAwEAATANBgkqhkiG9w0BAQQFAAOBgQCRPoQYw9rWWvfLPQuPXowvFmuebsTc28qI7iFWm6BJ TT/qdmzti7B5MHOt9BeVEft3mMeBU0CS2guaBjDpGlf+zsK/UUi1w9C4mnwGDZzqY/NKKWtLxabZ 5M+4MAKLZ92ePPKGpobM2CPLfM8ap4IgAzCbBKd8+CMp8yFmifze9Q== -----END CERTIFICATE-----この例では説明しませんが、
-certreq
を指定して証明書署名要求 (CSR) を生成し、証明書発行局 (CA) に送付して署名を求めることもできます。% keytool -import -alias dukecert -file duke.cer \ -keystore truststore Enter keystore password: trustword Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Serial number: 3c22adc1 Valid from: Thu Dec 20 19:34:25 PST 2001 until: Thu Dec 27 19:34:25 PST 2001 Certificate fingerprints: MD5: F1:5B:9B:A1:F7:16:CF:25:CF:F4:FF:35:3F:4C:9C:F0 SHA1: B2:00:50:DD:B6:CC:35:66:21:45:0F:96:AA:AF:6A:3D:E4:03:7C:74 Trust this certificate? [no]: yes Certificate was added to keystore
trustedCertEntry
(赤字) になっている点に注目してください。これは、このエントリに非公開鍵が関連付けられていることを示します。このエントリタイプから、このファイルが KeyManager
のキーストアとして適切でないこともわかります。
% keytool -list -v -keystore truststore Enter keystore password: trustword Keystore type: jks Keystore provider: SUN Your keystore contains 1 entry Alias name: dukecert Creation date: Dec 20, 2001 Entry type: trustedCertEntry Owner: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Issuer: CN=Duke, OU=Java Software, O="Oracle, Inc.", L=Palo Alto, ST=CA, C=US Serial number: 3c22adc1 Valid from: Thu Dec 20 19:34:25 PST 2001 until: Thu Dec 27 19:34:25 PST 2001 Certificate fingerprints: MD5: F1:5B:9B:A1:F7:16:CF:25:CF:F4:FF:35:3F:4C:9C:F0 SHA1: B2:00:50:DD:B6:CC:35:66:21:45:0F:96:AA:AF:6A:3D:E4:03:7C:74ここで、適切なキーストアを使ってアプリケーションを実行します。この例では、デフォルトの
X509KeyManager
および X509TrustManager
を使用するものとします。したがって、「カスタマイズ」で説明したシステムプロパティーを使用してキーストアを選択します。
% java -Djavax.net.ssl.keyStore=keystore \ -Djavax.net.ssl.keyStorePassword=password Server % java -Djavax.net.ssl.trustStore=truststore \ -Djavax.net.ssl.trustStorePassword=trustword Client
JDK Security API は、さまざまなアルゴリズム、証明書、およびキーストアのタイプの標準名を必要とし、これらを使用します。以前にこの付録 A の仕様およびほかのセキュリティー仕様 (JCA/CertPath/その他) にあった仕様名は、標準名のドキュメントにまとめられました。特定のプロバイダの情報は、「Oracle Provider Documentation」にあります。
Java SE 6 の JSSE はプラグイン可能であり、サードパーティーの JSSE プロバイダの使用には何の制限もありません。