はじめに
Isuconに出場してみて、全くHTTPS
、SSL/TLS
について理解できていないことに気づいたので、まとめてみます。
SSL/TLSとは
通信を暗号化するためのプロトコルです。
HTTPS
通信はSSL/TLS
によって実現されています。
かつてはSSL
が使われていましたが、現在では非推奨になっており、主にTLS
が使われています。
その名残で、現在でもSSL
やSSL/TLS
などといったような参照のされ方をすることがあります。
SSL
はNetscape
社によって管理されていましたが、TLS
はIETF
によって管理されています。
-
TLS1.0
: https://datatracker.ietf.org/doc/html/rfc2246 -
TLS1.1
: https://datatracker.ietf.org/doc/html/rfc4346 -
TLS1.2
: https://datatracker.ietf.org/doc/html/rfc5246 -
TLS1.3
: https://datatracker.ietf.org/doc/html/rfc8446
現在ではTLS1.0
, TLS1.1
は非推奨になっており、TLS1.2
, TLS1.3
のいずれかを使う必要があります。(RFC 8996)
TLS通信の手順
主に「サーバ側の事前準備」、「TLSハンドシェイク」、「暗号化して通信」の3つの段階を踏みます。
サーバ側の事前準備
TLS通信を実現するにあたって、サーバ側は「サーバ証明書」を用意する必要があります。
この証明書によって、サーバの信頼性を保証します。
これには、サイトの主体者、証明書の発行者、有効期間、公開鍵などが含まれます。
Chromeだと、アドレスバーの左側のボタン -> 「この接続は保護されています」 -> 「証明書は有効です」からそのサイトの証明書を確認することができます。
証明書には、発行者のデジタル署名がついており、その発行者の証明書を取得することで、署名を検証することができます。
これはその発行者の証明書も同じで、木構造になっており、自己認証をすることができるRoot認証局に辿り着くまではこれを繰り返します。
こちらの記事が大変わかりやすかったです。
OSには、この信頼できるRoot認証局の証明書の一覧が入っているため、ここで整合性を確かめることができます。(/etc/ssl/cert.pem
などにあるかと思います。)
TLSハンドシェイク
TLSハンドシェイクでどんなことが行われているかは、curl
のverbose
オプションで確認することができます。
(※ TLS1.2
の例)
% curl -v https://qiita.com
* Trying 13.114.185.91:443...
* Connected to qiita.com (13.114.185.91) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/cert.pem
* CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
* subject: CN=qiita.com
* start date: Nov 4 00:00:00 2023 GMT
* expire date: Dec 2 23:59:59 2024 GMT
* subjectAltName: host "qiita.com" matched cert's "qiita.com"
* issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M03
* SSL certificate verify ok.
Client Hello (Client -> Server)
まず、client側からClient Hello
を送信します。
ここでは、clientが対応している
- TLSのバージョン
- 暗号スイート
- 鍵交換アルゴリズム・暗号化アルゴリズム・メッセージ認証符号・鍵認証方式の組み合わせ
- https://developer.mozilla.org/ja/docs/Glossary/Cipher_suite
- ランダム文字列
- アプリケーション層のプロトコル
などを格納して送信します。
Server Hello (Client <- Server)
サーバ側では、Client Hello
に含まれる情報と自身の設定を照らし合わせて、確定した情報をServer Hello
として返します。
ここでは、
- 使用することになった暗号スイート
- 使用するアプリケーション層のプロトコル
- ランダム文字列
などが格納されています。
Certificate (Client <- Server)
サーバ側から「サーバ証明書」を送信します。
サーバ証明書とその上の階層の中間証明書が含まれています。
Server Key Exchange (Client <- Server)
TLSによる暗号通信は、暗号化と復号が同じ鍵で行われる「共通鍵暗号方式」が採用されています。
なので共通鍵を共有する必要があり、ここで使われるのが暗号スイートです。
特定のアルゴリズムを使うことによって、第3者に盗み見られることなく共通鍵を共有することができるようになります。
そのアルゴリズムを実現するために必要なパラメータが、このSecret Key Exchange
で送信されます。
Server finished ・ Server Hello Done (Client <- Server)
Server Hello
から始まった、サーバからの一連の情報の送信が終わったことを示す情報を送信します。
Client Key Exchange (Client -> Server)
RSAで暗号化されている場合、client側で共通鍵を生成し、サーバから送信されている公開鍵で暗号化して送信します。
DH鍵交換アルゴリズムの場合は、どちらか一方が完全な共通鍵を作るのではなく、どちらともが共通鍵の要素を作り、お互いに交換し、計算することで共通の鍵を生成します。
Client Key Exchange
では、サーバ側が共通鍵を生成するために必要なパラメータをクライアント側から送信します。
Change Cipher Spec ・ Finished (Client -> Server)
client側から、暗号通信をするにあたって必要な情報が揃ったことを示す情報を送信します。
Change Cipher Spec ・ Finished (Client <- Server)
server側からも、暗号通信をするにあたって必要な情報が揃ったことを示す情報を送信します。
暗号化して通信
TLSハンドシェイクによって、サーバとクライアントのみが知る共通鍵が出来上がったので、以降はこれで、暗号化/復号をして通信をします。
参考