トランスポート・レイヤー・セキュリティ(TLS)プロトコルの概要
トランスポート・レイヤー・セキュリティ(TLS)は、Webで暗号化を実装する場合に最もよく使用されるプロトコルです。TLSは、ネットワークでセキュアな通信を行うために暗号化プロセスを組み合わせて使用します。このセクションでは、TLSおよびTLSが使用する暗号化プロセスについて簡単に説明します。
TLSは、インターネット通信で使用される標準的なTCP/IPソケットプロトコルをセキュアに拡張します。表8-11に示すように、Secure Sockets Layerは標準TCP/IPプロトコル・スタックのトランスポート層とアプリケーション層の間に追加されます。TLSとともにもっともよく使用されるアプリケーションは、インターネットWebページ用のプロトコルであるHypertext Transfer Protocol (HTTP)です。この他にも、Net News Transfer Protocol (NNTP)、Telnet、Lightweight Directory Access Protocol (LDAP)、Interactive Message Access Protocol (IMAP)、File Transfer Protocol (FTP)などのアプリケーションがあり、同様にTLSとともに使用します。
表8-11 TLSによるTCP/IPプロトコル・スタック
TCP/IP層 | プロトコル |
---|---|
アプリケーション層 | HTTP、NNTP、Telnet、FTPなど |
トランスポート・レイヤー・セキュリティ | TLS |
伝送制御プロトコル | TCP |
インターネット層 | IP |
Secure Socket Layer (SSL)は、1994年にNetscape社によって開発され、インターネットの世界で使用されるようになると、標準的な存在になりました。現在では、国際的な標準化機構であるInternet Engineering Task Force (IETF)が管理しています。IETFはSSLの名称をTLSに変更し、1999年1月に最初の仕様のバージョン1.0をリリースしました。TLS 1.0は、SSLの最新バージョン3.0を少しだけ変更したものです。このアップグレードでは、前のバージョンでの不具合が修正され、既知の弱いアルゴリズムの使用が禁止されました。TLS 1.1は2006年4月、TLS 1.2は2008年8月、TLS 1.3は2018年8月にリリースされました。TLS 1.3はTLSプロトコルを全面的に見直したもので、以前のバージョンよりもセキュリティとパフォーマンスが大幅に向上しています。
TLSの仕組み
TLSが有効な理由の1つに、複数の暗号化プロセスを使用していることがあります。TLSは、公開キー暗号化で認証を行い、秘密キー暗号化とハッシュ関数で機密性とデータ整合性を提供します。TLSについて理解する前に、暗号化の処理方法を理解しておくと役立ちます。
暗号化処理
暗号化の主な目的は、2者間の秘密の通信に権限のない第三者がアクセスしたり、その内容を理解するのを困難にすることです。暗号化のプロセスによって、データに対する権限のないすべてのアクセスを必ずしも制限できるわけではありませんが、権限のない者が秘密のデータを理解できないようにすることができます。暗号化では、複雑なアルゴリズムを使用して、元のメッセージ(クリアテキスト)をエンコードされたメッセージ(暗号テキスト)に変更します。ネットワーク上で転送されるデータの暗号化および復号化に使用するアルゴリズムは一般に、秘密キー暗号化と公開キー暗号化の2つのカテゴリに分けられます。
秘密キー暗号化も公開キー暗号化も、合意に基づく暗号キーまたは暗号キーのペアを使用します。キーは、データの暗号化プロセスおよび復号化プロセスで暗号化アルゴリズムが使用するビット文字列です。暗号化キーは、錠のキーと同様、錠を開けることができるのは正しいキーのみです。
通信の当事者が互いにキーを安全に送信するのは、些細な問題ではありません。公開キー証明書を使用すると、公開キーを安全に送信し、受信者に公開キーの信頼性を保証できます。公開キー証明書を参照してください。
秘密キー暗号化と公開キー暗号化における暗号化処理の説明では、セキュリティのコミュニティで広く使用されている慣例(通信する2人の当事者はAliceとBobという名前で示される)に従います。権限のない第三者は攻撃者とも呼ばれ、Charlieと名付けられます。
秘密キー暗号化
秘密キー暗号化では、通信するAliceとBobはメッセージの暗号化と復号化に同じキーを使用します。暗号化されたデータをネットワークで送信する前に、AliceとBobはキーを持っていることが必要で、暗号化と復号化に使用する暗号化アルゴリズムに同意している必要があります
秘密キー暗号化で大きな問題の1つが、攻撃者にアクセスされずに一方から他方にキーを渡す方法の問題です。AliceとBobが秘密キー暗号化でデータを保護しても、CharlieがそのキーにアクセスできればAliceとBobの間で傍受した秘密メッセージを理解できます。CharlieはAliceとBobのメッセージを復号化できるだけではなく、Aliceになりすまして暗号化データをBobに送信することもできるのです。Bobには、メッセージがCharlieから届いたものかAliceから届いたものかはわかりません。
秘密キーの配布の問題が解決すれば、秘密キー暗号化はたいへん貴重なツールになります。そのアルゴリズムにより、優れたセキュリティと暗号化データが比較的迅速に提供できるからです。TLSセッションで送信される機密性の高いデータの多くは、秘密キー暗号化で送信されます。
秘密キー暗号化は、データの暗号化と復号化の両方に同じキーを使用するため、対称暗号化とも呼ばれます。よく知られている秘密キー暗号化アルゴリズムには、Advanced Encryption Standard (AES)、Triple Data Encryption Standard (3DES)およびRivest Cipher 4 (RC4)があります。
公開キー暗号化
公開キー暗号化は、公開キーと秘密キーの両方を使用することでキーの配布方法の問題を解決しました。公開キーはネットワークを通じて公開し、送信できますが、秘密キーは通信の1人の当事者にしか公開されません。公開キーと秘密キーは暗号化方式が逆で、一方のキーで暗号化したものをもう一方のキーで復号化します。
Bobが公開キー暗号化を使用して、Aliceに秘密のメッセージを送信するとします。Aliceは公開キーと秘密キーをどちらも持っているので、秘密キーは安全な場所に保管しておき、公開キーをBobに送信します。BobはAliceの公開キーを使ってAliceへの秘密のメッセージを暗号化します。Aliceは秘密キーを使ってメッセージを復号化します。
Aliceが自分の秘密キーを使用してメッセージを暗号化し、その暗号化されたメッセージをBobに送信すれば、Bobが受信するデータはAliceから届いたものだと考えることができます。BobがAliceの公開キーでデータを復号化できれば、そのメッセージはAliceが自分の秘密キーで暗号化したものに間違いなく、Aliceの秘密キーを持っているのはAliceのみです。問題は、Aliceの公開キーが公開されているために、だれもがメッセージを読めてしまうことです。このシナリオは、セキュアなデータ通信を考慮に入れていませんが、デジタル署名の基本を示しています。デジタル署名とは公開キー証明書のコンポーネントの1つで、TLSでクライアントやサーバーを認証するために使用します。公開キー証明書とデジタル署名を参照してください。
公開キー暗号化は、データの暗号化と復号化に別のキーを使用するので、非対称暗号化とも呼ばれます。TLSでよく使われる、よく知られている公開キー暗号化アルゴリズムは、Rivest Shamir Adleman (RSA)アルゴリズムです。このほかにも、秘密キーを交換するために設計されたTLSを使う公開キー暗号化アルゴリズムには、Diffie-Hellman (DH)があります。公開キー暗号化には膨大な計算が必要なため、速度が大幅に遅くなります。そこで、この方式は暗号化データ通信全体に使用するよりもむしろ、秘密キーなど少量のデータを暗号化する場合にだけ使用します。
秘密キー暗号化と公開キー暗号化の比較
秘密キー暗号化と公開キー暗号化のどちらにも、長所と短所があります。秘密キー暗号化では、データの暗号化や復号化に時間はかかりませんが、通信者同士が同じ秘密キー情報を共有する必要があり、キーの交換の方法が問題になります。公開キー暗号化では、キーを秘密にする必要がないのでキーの交換は問題になりませんが、データの暗号化と復号化に使用するアルゴリズムには膨大な計算が必要で、著しく遅くなります。
公開キー証明書
公開キー証明書を使用すると、エンティティは非対称暗号化で使用する公開キーを安全に配布できます。公開キー証明書は、次の状況を回避します。Charlieが自分の公開キーと秘密キーを作成すれば、自分はAliceだと名乗ってBobに公開キーを送信できます。BobはCharlieと通信できますが、データをAliceに送信していると思い込んでしまいます。
公開キー証明書は電子的なパスポートだと考えることができます。これは、信頼できる組織によって発行され、所有者に識別情報を提供します。公開キー証明書を発行する信頼できる組織を、証明書発行局(CA)と呼びます。CAは公証人にたとえることができます。CAから証明書を取得するには、識別情報の証拠となるものを提供する必要があります。CAは、申請者が申し立てる組織の代表であるとの確証が得られたら、証明書に含まれる情報の妥当性を証明する証明書に署名します。
公開キー証明書には、次のようなフィールドがあります。
- 発行者
- 証明書を発行した認証局(CA)。証明書を発行したCAが信頼でき、証明書が有効であれば、証明書は信頼できます。
- 有効期間
- 証明書には有効期限があります。証明書の有効性を検証する場合は、この日付をチェックしてください。
- サブジェクト
- 証明書が表すエンティティに関する情報が含まれます。
- サブジェクトの公開キー
- 証明書が提供する情報の主要な部分は、サブジェクトの公開キーです。その他のフィールドは、このキーの妥当性を確認するためのものです。
- 署名
- 証明書は、証明書を発行したCAによってデジタルで署名されます。署名はCAの秘密キーを使って作成され、証明書の妥当性を保証するものです。証明書のみに署名され、TLSトランザクションで送信されるデータには署名されないため、TLSには非拒否性がありません。
AliceがBobに公開キー証明書で自分の公開キーを送信するとき、BobがAliceの公開キーのみを有効として受け付ける場合、CharlieがAliceになりすましたとしても、BobがだまされてCharlieに秘密情報を送信することはありません。
複数の証明書を証明書チェーンでリンクすることもできます。証明書チェーンを使用する場合、最初の証明書は必ず送信者の証明書です。次は送信者の証明書を発行したエンティティの証明書です。チェーン内に他の証明書がある場合、それぞれ直前の証明書を発行した証明書発行局の証明書です。チェーンの最後の証明書は、ルートCAの証明書です。ルートCAは、広く信頼されている公開証明書発行局です。複数のルートCAの情報は、通常、クライアントのインターネット・ブラウザに保存されています。この情報には、CAの公開キーが含まれています。よく知られているCAには、DigiCert、EntrustおよびGlobalSignがあります。
暗号化ハッシュ関数
暗号化されたデータを送信する場合、TLSは通常、暗号化ハッシュ関数を使ってデータの整合性を保証します。ハッシュ関数を使って、AliceがBobに送ったデータをCharlieが改ざんできないようにします。
暗号化ハッシュ関数はチェックサムに似ています。主な違いは、チェックサムがデータの偶発的変化を検出するのに対し、暗号化ハッシュ関数は故意による変更を検出するように設計されています。データが暗号化ハッシュ関数で処理されると、ハッシュと呼ばれる小さなビット文字列が生成されます。メッセージがごくわずかだけ変更された場合も、結果として生成されるハッシュは大きく変更されます。暗号化ハッシュ関数には、暗号化キーが必要ありません。TLSとともによく使用されるハッシュ関数は、Secure Hash Algorithm (SHA)です。SHAは、U.S. National Institute of Standards and Technology (NIST)によって提案されました。
メッセージ認証コード
メッセージ認証コード(MAC)は暗号化ハッシュに似ていますが、秘密キーをベースにしている点が異なります。秘密キー情報が暗号化ハッシュ関数で処理したデータに含まれている場合、その結果生成されるハッシュはHMACと呼ばれます。
Aliceは、BobへのメッセージをCharlieが確実に改ざんしないようにする場合、メッセージのHMACを計算して元のメッセージにHMACを追加できます。次に、Bobと共有している秘密キーを使用してメッセージとHMACを暗号化できます。Bobは、メッセージを復号化してHMACを計算すれば、送信中にメッセージが変更されたかどうかを知ることができます。TLSでは、HMACを使ってセキュアなデータを送信します。
TLS 1.3ハンドシェーク
TLS 1.3を使用した通信によってTLSハンドシェークが開始されます。これは、クライアントとサーバー間の初期ネゴシエーションで、TLS内で以降の相互作用のパラメータを確立します。これは、キー交換、サーバー・パラメータおよび認証という3つのフェーズで構成されます:
-
キー交換: このフェーズでは、共有キーが属することができる名前付きグループ(楕円曲線グループ(ECDHE)または有限フィールド・グループ(DHE))などの共有キー・データを確立し、対称暗号化オプションなどの暗号化パラメータを選択します。
-
サーバー・パラメータ: このフェーズでは、証明書ベースのクライアント認証が必要かどうかなど、他のハンドシェーク・パラメータを確立します。
-
認証: このフェーズでは、サーバー(およびオプションでクライアント)を認証し、キーの確認およびハンドシェークの整合性を提供します。
TLS 1.3プロトコル
次の図に、TLSハンドシェーク全体の一連のメッセージを示します。
図8-7 TLS 1.3ハンドシェーク
![TLS 1.3ハンドシェーク TLS 1.3ハンドシェーク](img/tls13handshake.png)
-
キー交換:
-
クライアントは、サーバーにClientHelloメッセージを送信します。
-
サーバーはClientHelloメッセージを処理し、接続に適した暗号化パラメータを決定します。その後、ネゴシエーションされた接続パラメータを示す独自のServerHelloメッセージで応答します。TLS 1.3の場合、ServerHelloメッセージによってキーおよび暗号オプションのみが決定されます。その他のハンドシェーク・パラメータは、後で決定できます。
-
-
サーバー・パラメータ: サーバーは、次の2つのメッセージを送信してサーバー・パラメータを確立します:
-
EncryptedExtensions: このメッセージには、個々の証明書に固有のものを除いて、暗号化パラメータの決定に必要とされないClientHello拡張への応答が含まれています。
-
CertificateRequest (オプション): 証明書ベースのクライアント認証が必要な場合、サーバーはその証明書に必要なパラメータを含むメッセージを送信します。クライアント認証が不要な場合、このメッセージは省略されます。
-
-
認証:
-
サーバーは次の認証メッセージを送信します:
-
Certificate (オプション): このメッセージには、認証証明書と、証明書チェーン内のサポートするその他の証明書が含まれます。サーバーが証明書で認証されていない場合、このメッセージは省略されます。
ノート:
Certificateメッセージには、証明書のかわりに未加工のキーを含めることができます。 -
CertificateVerify (オプション): このメッセージには、Certificateメッセージ内の公開キーに対応する秘密キーを使用したハンドシェーク全体に対する署名が含まれています。サーバーが証明書で認証されていない場合、このメッセージは省略されます。
-
Finished: ハンドシェーク全体のMAC (メッセージ認証コード)。
-
-
クライアントは、独自のCertificate、CertificateVerifyおよびFinishedメッセージで応答します。サーバーがCertificateRequestメッセージを送信しなかった場合、Certificateメッセージは省略されます。クライアントが証明書で認証されていない場合、CertificateVerifyメッセージは省略されます。
-
クライアントとサーバーは、アプリケーション・データを相互に安全に送信できるようになりました。
キー交換
キー交換メッセージ、ClientHelloおよびServerHelloは、クライアントとサーバーのセキュリティ機能を決定し、残りのハンドシェークおよびアプリケーション・データの保護に使用されるトラフィック・キーを含む共有シークレットを確立します。
ClientHello
TLSハンドシェークは、クライアントがClientHelloメッセージをサーバーに送信することから始まります。このメッセージには次のフィールドがあります:
ノート:
TLSメッセージには、ここにリストされている以外の追加フィールドが含まれている場合があります。TLSメッセージとそのフィールドの詳細は、TLS 1.3仕様を参照してください。-
cipher_suites: このフィールドには、クライアントでサポートされている対称暗号オプションのリストが含まれます。具体的には、レコード保護アルゴリズム(秘密キーの長さを含む)およびキー付きハッシュ・メッセージ・コード(HMAC)ベースを抽出して展開キー導出関数(HKDF)で使用されるハッシュが含まれます。
-
拡張機能: 拡張機能は、既存のクライアントへの影響を最小限に抑えながら、TLSプロトコルへの新機能の追加を容易にします。ClientHelloメッセージには次の拡張機能が含まれる場合がありますが、これらに限定されるわけではありません:
-
supported_versions: この拡張機能は、クライアントでサポートされているTLSのバージョンを示します。ClientHelloメッセージにこのメッセージが含まれている必要があります。
-
status_request: この拡張機能は、クライアントが証明書ステータス・プロトコルを使用することを示します。サーバーが証明書ステータス・プロトコルの使用に同意しない場合があります。証明書ステータス・プロトコルの例として、オンライン証明書ステータス・プロトコル(OCSP)があります。クライアント主導型OCSPとOCSPステープリングを参照してください。
-
supported_groups: この拡張機能は、クライアントがキー交換でサポートしている名前付きグループを示します。これらの名前付きグループには、楕円曲線(ECDHE)グループと有限フィールド(DHE)グループが含まれます。ECDHEまたはDHEキー交換を使用している場合、ClientHelloメッセージにはこのメッセージが含まれている必要があります。
-
key_share: この拡張機能には、キー交換用の暗号化パラメータのリストが含まれます。client_sharesという名前のフィールドに、このリストが格納されています。このリストの各項目には、次のフィールドが含まれています:
-
group: キー交換暗号化方式の基になるグループの名前。JDKプロバイダ・ドキュメントのSunJSSEプロバイダを参照してください。
-
key_exchange: グループ・フィールドの値によって決定されるキー交換情報。
-
-
pre_shared_key: 事前共有キー(PSK)は、使用する前になんらかのセキュアなチャネルを使用して以前に2者間で共有された共有シークレットです。PSKは、以前の接続で確立し、新しい接続の確立に使用できます。ハンドシェークが完了すると、サーバーは、初期ハンドシェークから派生した一意のキーに対応するPSK IDをクライアントに送信できます。事前共有キーを使用したセッション再開を参照してください。
-
cookie: サーバーがHelloRetryRequestメッセージを送信するときに、この拡張機能をクライアントに含めることができます。(受入れ可能なパラメータ・セットが見つかった場合、サーバーはClientHelloメッセージに応答してHelloRetryRequestメッセージを送信しますが、ハンドシェークを続行するための十分な情報がClientHelloメッセージにありません。)この拡張機能の目的の1つは、サーバーがクライアントに見かけのネットワーク・アドレスでの到達可能性を示すことを強制できるようにすることです(これにより、サービス拒否攻撃(DoS)保護が提供されます)。クライアントは、新しいClientHelloメッセージを送信するときに、HelloRetryRequestで受信したコンテンツをこの新しいClientHelloメッセージのCookie拡張にコピーする必要があります。
-
server_name: TLS 1.3は、クライアントが接続しているサーバーの名前をサーバーに通知するメカニズムを提供しません。クライアントは、この拡張機能を使用してこの情報を提供することで、単一のネットワーク・アドレスで複数の仮想サーバーをホストするサーバーへ容易に接続できます。一部のサーバーでは、クライアントがこの拡張機能を送信する必要がある場合があります。
-
ServerHello
受け入れ可能なハンドシェーク・パラメータのセットをネゴシエートできる場合、サーバーはServerHelloメッセージでクライアントのClientHelloメッセージに応答します。このメッセージには次のフィールドがあります:
-
cipher_suite: このフィールドには、ClientHello.cipher_suitesフィールドのリストからサーバーによって選択された単一の暗号スイートが含まれます。
-
extensions: このフィールドには、暗号化コンテキストの確立およびプロトコル・バージョンのネゴシエーションに必要な拡張機能が含まれています。SeverHelloに含まれる可能性のある拡張機能には、次のものがあります:
-
supported_versions: 使用しているTLSのバージョンを示します。ServerHelloメッセージには、この拡張機能が含まれている必要があります。
-
key_share: この拡張機能には、キー交換用の暗号化パラメータのリストが含まれます。
-
pre_shared_key: この拡張機能には、サーバーが使用することに同意した事前共有キーが含まれます。事前共有キーの詳細は、事前共有キーを使用したセッション再開を参照してください。
サーバーは、EncryptedExtensionsメッセージで他の拡張機能を個別に送信します。
-
サーバー・パラメータ
サーバーは、ServerHelloメッセージをクライアントに送信した後、EncryptedExtensionsとCertificateRequestの2つのメッセージを送信してサーバー・パラメータを確立します:
-
EncryptedExtensions: このメッセージには、個々の証明書に固有のものを除いて、暗号化パラメータの決定に必要とされないClientHello拡張への応答が含まれています。
-
CertificateRequest: 証明書ベースのクライアント認証が必要な場合、このメッセージが送信されます。これには、クライアントからリクエストされた証明書のパラメータが含まれます。次のフィールドが含まれます。
-
certificate_request_context: このフィールドには、証明書リクエストを識別する識別子が含まれます。
-
extensions: このフィールドには、リクエストされた証明書のパラメータを説明する拡張機能が含まれます。次の拡張機能が含まれる場合があります:
-
signature_algorithms: この拡張機能は、CertificateVerifyメッセージで使用できる署名アルゴリズムを示します。ServerHelloメッセージには、この拡張機能が含まれている必要があります。
-
signature_algorithms_cert: この拡張機能は、デジタル署名で使用できる署名アルゴリズムを示します。このメッセージが送信されない場合は、signature_algorithms拡張で指定された値が使用されます。
-
certificate_authorities: この拡張機能は、サーバーが受け入れる認証局を示します。
-
supported_groups: このメッセージには、サーバーが優先する名前付きグループが含まれています。クライアントは、この情報を使用して、後続の接続でkey_share拡張で使用するグループを変更できます。
-
認証
TLSハンドシェークでサーバーとクライアントが相互に送信する最後の3つのメッセージは、Certificate、CertificateVerifyおよびFinishedです。
Certificate
このメッセージには、認証証明書と、証明書チェーン内のサポートするその他の証明書が含まれます。キー交換方法で認証に証明書を使用する場合、サーバーはこのメッセージを送信する必要があります。サーバーがCertificateRequestメッセージを介してクライアント認証をリクエストした場合にのみ、クライアントはこれを送信する必要があります。Certificateメッセージには、次のフィールドが含まれます:
-
certificate_list: このフィールドには一連のCertificateEntry構造が含まれ、それぞれに単一の証明書と一連の拡張機能が含まれます。
-
extensions: Certificateメッセージに含まれる可能性のある拡張機能には、次のものがあります:
-
status_request: クライアント主導型OCSPとOCSPステープリングを参照
-
signed_certificate_timestamp: TLSクライアントは、証明書がログに記録されないかぎり、証明書を受け入れません。有効な証明書がログに送信されると、ログは署名付き証明書タイムスタンプ(SCT)を返す必要があります。RFC 6962: Certificate Transparencyを参照してください。
-
CertificateVerify
このメッセージには、Certificateメッセージ内の公開キーに対応する秘密キーを使用したハンドシェーク全体に対する署名が含まれています。クライアントまたはサーバーが証明書に対応する秘密キーを持っていることを証明します。このメッセージには、次のフィールドが含まれます:
-
algorithm: このフィールドには、使用される署名アルゴリズムが含まれます。サポートされているアルゴリズムについては、JDKプロバイダ・ドキュメントのSunJSSEプロバイダを参照してください。
-
signature: このフィールドには、アルゴリズムを使用したデジタル署名が含まれます。
Finished
このメッセージには、ハンドシェーク全体に対するMessage Authentication Code (MAC)が含まれています。クライアントとサーバーがそれぞれのピアから受信したFinishedメッセージを検証すると、両側が接続を介してアプリケーション・データを送受信できるようになります。
事前共有キーを使用したセッション再開
事前共有キー(PSK)は、使用する前になんらかのセキュアなチャネルを使用している2者間で共有されていた共有シークレットです。あるTLSハンドシェーク中にPSKを確立し、それを使用して別のハンドシェークで新しい接続を確立できます。これはPSKによるセッション再開と呼ばれます。PSKは、初期ハンドシェークから導出された一意のキーに対応します。新しい接続の確立時にサーバーがPSKを受け入れる場合、この接続のセキュリティ・コンテキストは暗号により元の接続に関連付けられ、完全なTLSハンドシェークではなく、最初のハンドシェークから導出されたキーを使用して暗号化状態がブートストラップされます。
次の図は、2つのハンドシェークを示しています。1つ目はPSKを確立し、もう1つはPSKを使用します。
図8-8 PSKを確立するTLS 1.3ハンドシェーク
![PSKを確立するTLS 1.3ハンドシェーク PSKを確立するTLS 1.3ハンドシェーク](img/tls13handshakeestablishpsk.png)
図8-9 PSKを使用するTLS 1.3ハンドシェーク
![PSKを使用するTLS 1.3ハンドシェーク PSKを使用するTLS 1.3ハンドシェーク](img/tls13handshakeusepsk.png)
-
クライアントは、key_share拡張を含むClientHelloメッセージをサーバーに送信します。この拡張機能は、クライアントがサポートするキー交換暗号化方式を一覧表示します。
-
サーバーは、key_share拡張を含むServerHelloメッセージで応答します。この拡張機能には、キー交換に使用する暗号化方式が含まれています。
-
サーバーはそのサーバー・パラメータをクライアントに送信します。
-
サーバーとクライアントの両方が認証メッセージを交換します。
-
サーバーはNewSessionTicketメッセージをクライアントに送信します。このメッセージにはPSKが含まれており、クライアントはこのPSKをClientHelloメッセージのpre_shared_key拡張に含めることで、将来のハンドシェークに使用できます。
-
クライアントとサーバーは、暗号化されたアプリケーション・データを交換できるようになりました。
-
将来のハンドシェークでは、クライアントは、key_shareおよびpre_shared_key拡張を含むClientHelloメッセージをサーバーに送信します。pre_shared_key拡張には、NewSessionTicketメッセージで送信されるPSKが含まれています。
-
サーバーは、pre_shared_keyおよびkey_share拡張を含むServerHelloメッセージで応答します。pre_shared_key拡張には、サーバーが使用に同意したPSKが含まれます。
-
サーバーはそのパラメータをクライアントに送信します。
-
サーバーとクライアントは、互いにFinishedメッセージを送信します。この接続のセキュリティ・コンテキストは暗号により元の接続に結び付けられるため、認証フェーズは実行されません。
-
クライアントとサーバーは、暗号化されたアプリケーション・データを交換できるようになりました。
ノート:
次のものは、JDK 11ではサポートされません:-
PSKのみを使用した再開: PSKを(EC) DHEキー交換とともに使用する必要があります。これにより、前方秘匿性が共有キーと組み合せて提供されます。PSKのみを使用した再開は、前方秘匿性および後方秘匿性に関して安全性が低くなります。
-
ラウンドトリップ時間ゼロの再開(0-RTT): これにより、クライアントとサーバーは最初のメッセージ(ClientHelloとServerHello)のアプリケーション・データを相互に送信できます。クライアントはPSKを使用して、最初にClientHelloで送信するアプリケーション・データを暗号化し、サーバーを認証します。これには、PSKのみを使用した再開およびリプレイ攻撃の可能性があるというセキュリティ上の問題があります。
-
ステートレス・サーバーPSK: 「RFC5077: Transport Layer Security (TLS) Session Resumption without Server-Site State」では、サーバーがセッションを再開し、クライアントごとのセッション状態を維持しないようにするメカニズムについて説明しています。このメカニズムを使用すると、PSKのみを使用した再開のための前方秘匿性を犠牲にして、サーバーのメモリー使用量が減少します。
-
帯域外PSKの確立: これは、NewSessionTicketメッセージ以外を使用したPSKの生成を意味します。
ポストハンドシェーク・メッセージ
クライアントとサーバーは、ハンドシェーク後に他のメッセージ(新規セッション・チケット・メッセージ、ポストハンドシェーク認証およびキー更新)を送信できます。
新規セッション・チケット・メッセージ
Finishedメッセージの受信後にサーバーによって送信されるNewSessionTicketメッセージには、クライアントが将来のハンドシェークに使用できる事前共有キーが含まれています。事前共有キーを使用したセッション再開を参照してください。
ポストハンドシェーク認証
クライアントがpost_handshake_auth拡張を送信した場合、サーバーはハンドシェーク後いつでもCertificateRequestメッセージを送信してクライアント認証をリクエストできます。クライアントが認証を受ける場合は、Certificate、CertificateVerifyおよびFinishedメッセージを送信する必要があります。クライアントが拒否した場合は、証明書を含まないCertificateメッセージおよびFinishedメッセージを送信する必要があります。
KeyUpdateメッセージ
KeyUpdateハンドシェーク・メッセージは、送信者が送信暗号キーを更新していることを示すために使用されます。これはTLS 1.2のChangeCipherSpecメッセージを置き換えます。
jdk.tls.keyLimits
セキュリティ・プロパティを使用すると、アルゴリズムで特定のキー・セットを使用して暗号化可能なデータ量に制限を指定できます。アルゴリズムでキー・セットを使用して暗号化可能なデータ量の制限を参照してください。
互換性のリスクと既知の問題
この項では、JSSEの機能拡張によって生じる可能性がある互換性の問題やその他の既知の問題について説明します。
TLS 1.3には以前のバージョンとの直接的な互換性がない
TLS 1.3には、以前のバージョンとの直接的な互換性がありません。TLS 1.3を下位互換性モードで実装することは可能ですが、TLS 1.3にアップグレードする際に考慮する互換性リスクはまだいくつかあります。
-
TLS 1.3ではhalf-closeポリシーを使用し、TLS 1.2以前ではduplex-closeポリシーを使用しています。duplex-closeポリシーに依存しているアプリケーションでは、TLS 1.3へのアップグレード時に互換性の問題が発生する場合があります。
-
signature_algorithms_cert拡張では、証明書認証に事前定義済の署名アルゴリズムを使用することが求められます。しかし実際には、サポートされていない署名アルゴリズムがアプリケーションで使用されている場合があります。
-
DSA署名アルゴリズムはTLS 1.3ではサポートされていません。サーバーがDSA証明書のみを使用するように構成されている場合、サーバーではTLS 1.3接続をネゴシエートできません。
-
TLS 1.3でサポートされている暗号化方式群は、TLS 1.2以前のものとは異なります。サポートされなくなった暗号化方式群がアプリケーションでハードコードされている場合、コードを修正せずにTLS 1.3を使用することはできない場合があります(たとえばTLS_AES_128_GCM_SHA256 (1.3以降)とTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (1.2以前))。
-
TLS 1.3のセッション再開およびキー更新の動作はTLS 1.2以前とは異なります。互換性の影響はわずかですが、アプリケーションがTLSプロトコルのハンドシェークの詳細に依存している場合にはリスクになる可能性があります。
TLS 1.2ハンドシェーク
SSLを使った通信は、クライアントとサーバー間の情報交換から始まります。この情報交換をSSLハンドシェークと呼びます。SSLハンドシェークには、次のステージがあります。
- 暗号化方式群のネゴシエーション
SSLセッションは、どの暗号群を使用するかについて、クライアントとサーバーがネゴシエーションを行うことから始まります。暗号化方式群とは、コンピュータがデータを暗号化するために使用する暗号化アルゴリズムとキーのサイズのセットです。符号化方式には、公開キー交換アルゴリズムまたはキー合意アルゴリズム、および暗号化ハッシュ関数に関する情報が含まれます。クライアントは利用できる暗号群をサーバーに伝え、サーバーは、どちらにも適用できる暗号群を選択します。
- サーバーの識別情報の認証(オプション)
SSLの認証ステップはオプションです。しかし、Web上の電子商取引の例では、一般にクライアントがサーバーを認証します。サーバーの認証により、サーバーが表すとクライアントが信じているエンティティを、そのサーバーが実際に表していることをクライアントが確認できます。
サーバーは、自らが表すと唱える組織に属していることを証明するため、クライアントに公開キー証明書を提示します。この証明書が有効であれば、クライアントはサーバーの識別情報について確信できます。
クライアントとサーバーは、同じ秘密キーについて同意できる情報を交換します。たとえば、RSAを使う場合、クライアントは公開キー証明書で取得したサーバーの公開キーを使用して、秘密キー情報を暗号化します。クライアントは暗号化された秘密キー情報をサーバーに送信します。復号化にはサーバーの秘密キーが必要なので、サーバーでだけ、このメッセージを復号化できます。
- 暗号化メカニズムの合意
クライアントとサーバーは、同じ秘密キーにアクセスします。それぞれのメッセージでは、ハンドシェークの最初のステップで選択した暗号化ハッシュ関数と、共有された秘密情報を使用して、メッセージに添付されるHMACを計算します。次に、秘密キーと、ハンドシェークの最初のステップでネゴシエーションされた秘密キー・アルゴリズムを使用し、セキュアなデータとHMACを暗号化します。そのあと、クライアントとサーバーは、暗号化されハッシュ化されたデータを使ってセキュアに通信することができます。
TLS 1.2プロトコル
TLS 1.2ハンドシェークでは、SSLハンドシェークについて概要を説明しました。これは、暗号化されたメッセージを送信する前にクライアントとサーバーの間で行われる情報の交換です。図8-10に詳細を示します。これは、SSLハンドシェークで交換される一連のメッセージを示しています。特定の状況下でだけ送信されるメッセージには「optional」と記されています。各SSLメッセージについては、後で詳しく説明します。
図8-10 SSL/TLSハンドシェーク
![この図は、SSLハンドシェークで交換される一連のメッセージを示しています。これらのメッセージの詳細は、その後のテキストで説明します。 この図は、SSLハンドシェークで交換される一連のメッセージを示しています。これらのメッセージの詳細は、その後のテキストで説明します。](img/ssl-client.png)
SSLメッセージは、次の順序で送信されます。
- Client hello: クライアントは、それがサポートする最上位バージョンのSSLと暗号化方式群のリスト(TLS 1.0はSSL 3.1と示される)を含むサーバー情報を送信します。暗号化方式群の情報には、暗号化アルゴリズムとキーのサイズが含まれます。
- Server hello: サーバーは、クライアントとサーバーの両方がサポートする最上位バージョンのSSLと最適な暗号化方式群を選択し、この情報をクライアントに送信します。
- (オプション) Certificate: サーバーはクライアントに証明書または証明書チェーンを送信します。証明書チェーンは通常、サーバーの公開キー証明書で始まり、認証局のルート証明書で終わります。このメッセージはオプションで、サーバー認証を求められた場合に使用します。
- (オプション) Certificate request: サーバーがクライアントを認証する必要がある場合、クライアントに証明書要求を送信します。インターネット・アプリケーションでは、このメッセージが使われることはほとんどありません。
- (オプション) Server key exchange: Certificateからの公開キー情報がキー交換を行うのに不十分な場合、サーバーはクライアントにサーバー・キー交換メッセージを送信します。たとえば、Diffie-Hellman (DH)に基づく暗号化方式群では、このメッセージにはサーバーのDH公開キーが含まれています。
- Server hello done: サーバーは、最初のネゴシエーション・メッセージを終了したことをクライアントに伝えます。
- (オプション) Certificate: サーバーがクライアントに証明書を要求すると、クライアントはサーバーが前に行ったように、その証明書チェーンを送信します。
ノート:
クライアントに証明書を要求するのは、ごく一部のインターネット・サーバー・アプリケーションのみです。 - Client key exchange: クライアントは、対称暗号化で使用するキーを作成するために使用される情報を生成します。RSAでは、クライアントはサーバーの公開キーでこのキー情報を暗号化してサーバーに送信します。DHに基づく暗号化方式群の場合、このメッセージにはクライアントのDH公開キーが含まれています。
- (オプション) Certificate verify: このメッセージは、上述のとおりクライアントが証明書を提示する場合にクライアントによって送信されます。このメッセージは、サーバーにクライアントの認証処理を完了させるためのものです。このメッセージが使用されると、クライアントは暗号化ハッシュ関数で電子的に署名した情報を送信します。サーバーがクライアントの公開キーでこの情報を復号化すれば、サーバーはクライアントを認証できます。
- Change cipher spec: クライアントはメッセージを送信し、暗号化モードに変更するようサーバーに伝えます。
- Finished クライアントはサーバーに、セキュアなデータ通信を開始する準備ができたことを伝えます。
- Change cipher spec: サーバーはメッセージを送信し、暗号化モードを変更するようクライアントに伝えます。
- Finished: サーバーはクライアントに、セキュアなデータ通信を開始する準備ができたことを伝えます。SSLハンドシェークが終了します。
- Encrypted data: クライアントとサーバーは、client helloとserver hello時にネゴシエーションされた対称暗号化アルゴリズムと暗号化ハッシュ関数およびclient key exchange時にクライアントがサーバーに送信した秘密キーを使用して通信します。このときハンドシェークの再ネゴシエーションを行うことができます。ハンドシェークの再実行(再ネゴシエーション)を参照してください。
- Close Messages: 接続の終わりに、それぞれの側が
close_notify
アラートを送信し、接続が終了したことをピアに伝えます。
SSLセッションで生成したパラメータを保存しておけば、将来のSSLセッションでこれらのパラメータを再利用できます。SSLセッションのパラメータを保存しておけば、暗号化通信をすばやく開始できます。
ハンドシェークの再実行(再ネゴシエーション)
初期のハンドシェークが完了してアプリケーション・データが流れているとき、いずれの側からでも新しいハンドシェークをいつでも開始できます。特に重要な操作について、アプリケーションで強力な暗号化方式群を使用したり、サーバー・アプリケーションでクライアント認証が必要になる場合もあります。
理由は何であれ、新しいハンドシェークが既存の暗号化セッションに置き換わり、新しいセッションが確立されるまで、アプリケーション・データとハンドシェーク・メッセージが交互に配置されます。
アプリケーションで次のいずれかのメソッドを使用して、新しいハンドシェークを開始できます。
SSLSocket.startHandshake()
SSLEngine.beginHandshake()
符号化方式の選択とリモート・エンティティの検証
SSL/TLSプロトコルは、保護された接続を確保するための一連の特定のステップを定義します。ただし、暗号化方式群の選択が、接続で確保するセキュリティのタイプに直接影響します。たとえば、匿名暗号化方式群を選択した場合、アプリケーションにはリモート・ピアの識別情報を検証する方法がありません。暗号化しない方式群が選択された場合は、データの機密性を保護できません。またSSL/TLSプロトコルでは、受信した資格と、ピアから送信されることが予期される資格が一致するようにとは規定していません。接続がなんらかの理由で悪意のあるピアにリダイレクトされたときに、悪意のあるピアのクレデンシャルが現在のトラスト・データに基づいて受け付けられた場合、その接続は有効とみなされてしまいます。
raw SSLSocket
およびSSLEngine
クラスを使用する場合は、データの送信前に必ずピアのクレデンシャルをチェックしてください。SSLSocket
およびSSLEngine
クラスは、URL内のホスト名がピアのクレデンシャル内のホスト名と一致することを自動的に検証しません。ホスト名が検証されない場合、URL不正行為によってアプリケーションが悪用される可能性があります。JDK 7以降は、エンドポイント識別/検証の手順は、SSL/TLSハンドシェーク中に処理できます。SSLParameters.getEndpointIdentificationAlgorithmメソッドを参照してください。
HTTPS (HTTP Over TLS)などのプロトコルでは、ホスト名検証が必要です。JDK 7以降、HTTPSエンドポイント識別は、HttpsURLConnectionのためのハンドシェーク中にはデフォルトで強制されます。SSLParameters.getEndpointIdentificationAlgorithmメソッドを参照してください。また、アプリケーションは、HostnameVerifierインタフェースを使用してデフォルトのHTTPSホスト名規則をオーバーライドできます。「HostnameVerifierインタフェース」および「HttpsURLConnectionクラス」を参照してください。
Datagram Transport Layer Security (DTLS)プロトコル
Datagram Transport Layer Security (DTLS)プロトコルは、信頼できる順次のデータ配信を必要とせず提供しない、データグラムでのTLSのトラフィックを構築するように設計されています。Java Secure Socket Extension (JSSE) APIおよびSunJSSEセキュリティ・プロバイダでは、DTLSプロトコルがサポートされています。
TLSはTCPなどの透過的な信頼できるトランスポート・チャネルを必要とするため、信頼できないデータグラム・トラフィックの保護には使用できないためです。DTLSは、TLSのデータグラム互換型です。
JSSE APIでは、Secure Socket Layer (SSL)プロトコルとTransport Layer Security (TLS)プロトコルとともにDTLSバージョン1.0とDTLSバージョン1.2がサポートされるようになりました。
javax.net.ssl.SSLEngine
プログラミング・モデルは、DTLS用JSSE APIで使用されます。
DTLSハンドシェーク
アプリケーション・データを送信または受信できるようにするには、DTLSプロトコルで、暗号化パラメータを確立するためのハンドシェークが必要です。このハンドシェークでは、SSLEngine
オブジェクトによる、クライアントとサーバーとの間の一連のメッセージのやり取りが必要です。
DTLSハンドシェークでは、すべてのメッセージが正しく受け取られる必要があります。したがって、信頼できないデータグラム・トラフィックでは、不足または遅延しているパケットを再送信する必要があります。javax.net.ssl.SSLEngine
は入出力操作を担当しないため、アプリケーションが、タイマーを提供し、再送信が必要なときにSSLEngineに知らせる役目を担います。アプリケーションのためにタイマーおよび再送信の戦略を実装することが重要です。DTLS接続での再送信の処理を参照してください。
DTLSハンドシェークには、次のステージがあります。
- 暗号化方式群のネゴシエーション
DTLSセッションは、どの暗号群を使用するかについて、クライアントとサーバーがネゴシエーションを行うことから始まります。暗号化方式群とは、コンピュータがデータを暗号化するために使用する暗号化アルゴリズムとキーのサイズのセットです。符号化方式には、公開キー交換アルゴリズムまたはキー合意アルゴリズム、および暗号化ハッシュ関数に関する情報が含まれます。クライアントは利用できる暗号群をサーバーに伝え、サーバーは、どちらにも適用できる暗号群を選択します。
Cookieは、サービス妨害攻撃(DoS)を防ぐために、暗号群とともに、クライアントとサーバーとの間ので交換されます。
- サーバーの識別情報の認証(オプション)
認証ステップはオプションです。しかし、Web上の電子商取引の例では、クライアントがサーバーを認証します。サーバーの認証により、サーバーが表すとクライアントが信じているエンティティを、そのサーバーが実際に表していることをクライアントが確認できます。
サーバーは、自らが表すと唱える組織に属していることを証明するため、クライアントに公開キー証明書を提示します。この証明書が有効であれば、クライアントはサーバーの識別情報について確信できます。
クライアントとサーバーは、同じ秘密キーについて同意できる情報を交換します。たとえば、RSAを使う場合、クライアントは公開キー証明書で取得したサーバーの公開キーを使用して、秘密キー情報を暗号化します。クライアントは暗号化された秘密キー情報をサーバーに送信します。復号化にはサーバーの秘密キーが必要なので、サーバーでだけ、このメッセージを復号化できます。
- 暗号化メカニズムの合意
クライアントとサーバーは、同じ秘密キーにアクセスします。それぞれのメッセージでは、ハンドシェークの最初のステップで選択した暗号化ハッシュ関数と、共有された秘密情報を使用して、メッセージに添付されるHMACを計算します。次に、秘密キーと、ハンドシェークの最初のステップでネゴシエーションされた秘密キー・アルゴリズムを使用し、セキュアなデータとHMACを暗号化します。そのあと、クライアントとサーバーは、暗号化されハッシュ化されたデータを使ってセキュアに通信することができます。
DTLSハンドシェークのメッセージの交換
DTLSハンドシェークでは、SSLEngine
オブジェクトによって、クライアントとサーバーとの間で一連の送受信メッセージが交換されます。
図8-11に、DTLSハンドシェークで交換される一連のメッセージを示します。特定の状況下でだけ送信されるメッセージには「optional」と記されています。各メッセージをこの図の後に説明します。
DTLSハンドシェーク・メッセージの詳細は、DTLSバージョン1.0およびDTLSバージョン1.2を参照してください。
次のハンドシェーク・メッセージは、DTLSハンドシェーク中にクライアントとサーバーとの間で交換されます。
- ClientHello:
クライアントは、それがサポートする最上位バージョンのDTLSおよび暗号化方式群のリストを含むサーバー情報を送信します。暗号化方式群の情報には、暗号化アルゴリズムとキーのサイズが含まれます。
- HelloVerifyRequest:
サーバーは、CookieとともにクライアントからのClientHelloメッセージに応答します。
- ClientHello:
クライアントは、それがサポートする最上位バージョンのDTLSおよび暗号化方式群のリストとともに、2つ目のClientHelloメッセージをサーバーに送信します。HelloVerifyRequestで受け取ったCookieは、サーバーに送り返されます。
- ServerHello:
サーバーは、クライアントとサーバーの両方がサポートする最上位バージョンのDTLSと最適な暗号化方式群を選択し、この情報をクライアントに送信します。
- (オプション) Certificate:
サーバーはクライアントに証明書または証明書チェーンを送信します。証明書チェーンは通常、サーバーの公開キー証明書で始まり、認証局のルート証明書で終わります。このメッセージはオプションで、サーバー認証を求められた場合に使用します
- (オプション) CertificateRequest:
サーバーは、クライアントを認証する必要がある場合、クライアントに証明書要求を送信します。インターネット・アプリケーションでは、このメッセージが使われることはほとんどありません。
- (オプション) ServerKeyExchange:
Certificateからの公開キー情報がキー交換を行うのに不十分な場合、サーバーはクライアントにサーバー・キー交換メッセージを送信します。たとえば、Diffie-Hellman (DH)に基づく暗号化方式群では、このメッセージにはサーバーのDH公開キーが含まれています。
- ServerHelloDone:
サーバーは、最初のネゴシエーション・メッセージを終了したことをクライアントに伝えます。
- (オプション) Certificate:
サーバーがクライアントに証明書を要求すると、クライアントはサーバーが前に行ったように、その証明書チェーンを送信します。
ノート:
クライアントに証明書を要求するのは、ごく一部のインターネット・サーバー・アプリケーションのみです。 - ClientKeyExchange:
クライアントは、対称暗号化で使用するキーを作成するために使用される情報を生成します。RSAでは、クライアントはサーバーの公開キーでこのキー情報を暗号化してサーバーに送信します。DHに基づく暗号化方式群の場合、このメッセージにはクライアントのDH公開キーが含まれています。
- (オプション) CertificateVerify:
このメッセージは、上述のとおりクライアントが証明書を提示する場合にクライアントによって送信されます。このメッセージは、サーバーにクライアントの認証処理を完了させるためのものです。このメッセージが使用されると、クライアントは暗号化ハッシュ関数で電子的に署名した情報を送信します。サーバーがクライアントの公開キーでこの情報を復号化すれば、サーバーはクライアントを認証できます。
- ChangeCipherSpec:
クライアントは、新しくネゴシエーションされたCipherSpecおよびキーによって後続のデータが保護されることと、そのデータが暗号化されることをサーバーに伝えるメッセージを送信します。
- Finished:
クライアントはサーバーに、セキュアなデータ通信を開始する準備ができたことを伝えます。
- ChangeCipherSpec:
サーバーは、新しくネゴシエーションされたCipherSpecおよびキーによって後続のデータが保護されることと、そのデータが暗号化されることをクライアントに伝えるメッセージを送信します。
- Finished:
サーバーはクライアントに、セキュアなデータ通信を開始する準備ができたことを伝えます。これでDTLSハンドシェークが終了します。
ハンドシェークの再実行(再ネゴシエーション)
初期のハンドシェークが完了してアプリケーション・データが流れているとき、いずれの側からでも新しいハンドシェークをいつでも開始できます。特に重要な操作について、アプリケーションで強力な暗号化方式群を使用したり、サーバー・アプリケーションでクライアント認証が必要になる場合もあります。
理由は何であれ、新しいハンドシェークが既存の暗号化セッションに置き換わり、新しいセッションが確立されるまで、アプリケーション・データとハンドシェーク・メッセージが交互に配置されます。
アプリケーションでSSLEngine.beginHandshake()
メソッドを使用して、新しいハンドシェークを開始できます。