エンジニアをやっていると、必ずぶち当たるのが暗号化のための「鍵」なのですが、この鍵というのが厄介で、ふとした瞬間にえーとなんでこっちが秘密鍵なんだっけ、なんで素因数分解が非対称なんだっけ、ということが思い出せなくなります。
そんな悩みを撲滅すべくリファレンスを作成します。あくまでデファクトで流行ってるのはこんな概念なんだよというものです。
ここで、MITM(Man In The Middle)は通信を傍受できてなんかしてやろうと思っている犯罪者と定義します。
先に結論という事で手書きの汚い図(シーケンス無視)
H:秘密鍵 K:公開鍵 S:署名 C:証明書 CSRR:CSRのレスポンス
①一番偉いCAが自分の公開鍵に自己署名して証明書を作成しOSやブラウザベンダー等に配布します
②OSベンダー等はその証明書を信頼される証明書ストアに保管し、同梱してOSやブラウザを配布します
③その次に偉いCAが公開鍵、秘密鍵などを偉いCAにCSRで提出して署名して証明書を作成してもらいます
④HTTPSをしたいサーバーが公開鍵、秘密鍵などをCAにCSRで提出して署名して証明書を作成してもらいます
この時、CAの証明書と偉いCAの証明書ももらいます
⑤HTTPSの通信時に、サーバー証明書として偉いCAとCAと自分の証明書をクライアントに送りつけます
⑥クライアントは署名をCAの公開鍵で検証します。
さらにCAの公開鍵の署名を偉いCAの公開鍵で検証します。
さらに偉いCAの公開鍵を偉いCAの公開鍵で検証します。
その証明書を信頼されたルート証明書と突合して検証します
⑦クライアント証明書が必要な場合は、サーバー側から要求してクライアントを検証します。
⑧鍵交換がRSAの場合、サーバ証明書の公開鍵でPre-Master Secretを暗号化して送りつけます
⑨鍵交換がDHの場合、公開鍵を交換します
それぞれのやり方で共通鍵を作ります
HTTPS通信(SSL/TLS)での鍵と経路の暗号化
非対称(asymmetry)
これは基本的に秘密鍵で作成した情報がもうひとつの対となっている公開鍵で検証でき、なおかつ秘密鍵は対となった公開鍵から推定不可能だということを表します。
でもこの方式は数学の素因数分解(RSA)とか楕円(謎)の計算を伴うため、処理に時間がかかるわけですね。
対称(symmetry)
共通鍵です。これは鍵を使って情報を暗号化し、同じ鍵で復号化します。もちろん鍵がばれると途端に復号できてしまいますから、鍵が配れず、というか秘密裏に書留かなんかで鍵を送る必要があるわけです。3DES(トリプルデス=3回死ぬ=でももう流行ってない)とかAESですね。
でもこちらの方が非対称の暗号よりも速いわけです。
ハイブリッド
そしたら非対称と対称を組み合わせて、非対称の仕組みを使って秘密裏に対称の共通鍵を作ればいいじゃん。
となります。
これで作った共通鍵で通信の内容を暗号化します。
非対称で共通鍵を作る仕組
TLSでは最初のネゴシエーション時にやり方を選べます。
事前共有鍵(Pre Shared Key=PSK)
共有鍵を事前に取得しておくやりかたです。ここでは公開鍵暗号の出番はありません。
暗号化(RSA)
サーバーの証明書をもらって、その中の公開鍵で共通鍵作成用の情報を暗号化して相手に送り付けます。この時、サーバー側しか秘密鍵でその情報を復号できません。なのでMITMは読めません。
これはTLS1.3で廃止になりました。
鍵交換(DH)
サーバーとクライアント双方で秘密鍵と公開鍵のペアを作成し、公開鍵とその他の情報を交換し合って共通鍵をつくる方式です。秘密鍵は双方秘密にして持っているため推定不可能であり、MITMは公開鍵だけ知っても秘密鍵を知らないので共通鍵が作れません。
この時作成される秘密鍵と公開鍵のペアは揮発性が一般的です(DHE・Ephemeral=一時的)。
この時に作られる(公開鍵と秘密鍵)×2は認証で使われる公開鍵と秘密鍵とは関係ありません。
通信先の認証
サーバー認証
署名
ハイブリッドで暗号化通信する事にしたのはいいけど、通信相手が正当なサーバーである事を誰が証明するの?
じゃあ、署名で通信相手の情報を確認しよう、となります。
秘密鍵でしかつくれないものが、署名。それを配られた公開鍵で検証すると、署名対象の情報と合致して検証できます。
ハッシュ
署名するのに元の文書より署名が長くなったら本末転倒なので元の文書を短くし、その情報を使って署名を作成します。
この短くする手段をハッシュ関数と呼びます。
長いものを短くするので当然複数のものをハッシュ化すると同じハッシュ値になる可能性があります。これをコリジョン(衝突)と呼びますが、極めて低い確率なので無視します。その確率は普段日本人が使う漢数字の単位を大幅に超える単位分の1です。古くはMD5、それからSHA-256とかです。
証明書(CERTIFICATE)
署名がついた公開鍵が、証明書です。今X.509v3がスタンダードです。
この時、証明書には署名する人と、署名方式、証明される対象(ドメイン)などが書かれています。
証明機関(Certificate Authority)
サーバーが自分で署名した証明書を信用しても意味ないよね。オレオレ詐欺みたいなもんでしょ。
じゃあ通信する人たちと関係ない第三者の証明機関(CA)を作って署名してもらって証明しよう! となります。
CSR (Certificate Signing Request)
で、サーバー立てる時に証明機関にこの公開鍵に署名してね! とお願いするときの書類がCSRです。
署名対象のサーバーの公開鍵と、対象のドメインや、メールアドレス、署名方法などを登録します。
中間証明書
証明機関が自分の秘密鍵で署名した証明書で通信者を保証したはいいのですが、DNSとかADと同じでひとつの機関で証明書発行したら証明書発行業務が追いつかないわ、ヒエラルキー作って多段で証明書発行してよ!
となります。
致し方ないので公開鍵を署名で証明した機関の公開鍵に、さらに署名をつけてどっちかというと偉い証明機関が証明します。
ルート証明書
ここで、公開鍵に署名した機関の公開鍵に署名した機関の公開鍵に署名した機関の公開鍵の証明書を辿っていくと、
証明機関と、証明される対象(ドメイン)が同じ証明書にぶちあたります。
これが自己署名証明書です。
え?さっきのサーバーが自分で署名付けたオレオレ詐欺と何が違うの、と思うかもしれませんがその辺の自己署名証明書とは違って全世界的に有名な証明機関で監査も査察も入ってしっかりしているとみなして最初から信用して通信する側に登録しておきます。
で、登録された信頼された証明書に行き着いた場合、通信者は通信相手が正当とみなします。結局突合で信頼するわけですね。
これをルート証明書といいます。
自己署名証明書
で、なんでブラウザに怒られるかというと大した証明機関でもないのに自分で自分の公開鍵に署名するって何様?
と怒られているわけです。
もしかしたらオレオレ詐欺グループかもしれません。いえいえ私はローカルの検証環境に単にTLS張りたかっただけなんです…
証明書チェーン
公開鍵の中間証明書を含めたルート証明書までの証明書は、最近のTLS1.2などでは通信の煩雑さを避けるため、いっぺんにcertificate_listで送られて来ます。
クライアント認証
基本的にサーバー認証と一緒
TLSでは、クライアント認証させることもできます。サーバーの要求によりクライアントが証明書をサーバーと同じく差し出さないとサーバー側が信頼せずに拒絶します。
ここで、サーバーの証明書は不特定多数に正当な情報機関であることを確認する手段であるのに対して、クライアント証明書はサーバーにアクセスしてよいかを認証しています。ところがどちらも公開鍵への署名でその要件を実現している…
どちらも省略すると証明書なわけです。公開鍵をくるんだ。
なので、証明書を誤って発行してしまい、不正にシステムにアクセスされてしまいました!!などという時はクライアント証明書、
証明書を誤って発行してしまい、偽サイトでフィッシングサイトを作成されて大変な被害が出ました!!などという時はサーバー証明書
という風に読み替えないといけない訳ですね。
改ざん検知
Finished
非暗号化で行ったハンドシェイク通信の内容をハッシュ関数でハッシュ化し、Finishedメッセージとして送り付けることによって、MITMが改ざんしていないかを検知します。
その他TLS関連
アプリケーションサーバーからのTLS接続
アプリケーションサーバーから TLSを使った SMTPポートにメールを飛ばしたいというような事も考えられます。
すると、TLS接続先から送られて来たサーバ証明書のルート証明書がアプリケーションサーバー側の信頼された証明書に登録されていないと通信が失敗するでしょう。信用できませんからね。
CRL(Certificate Revocation List)
Revokeといったら失効です。証明書の有効期限内でも、なんらかの事情でその証明書が使えなくなった(先ほどのクライアント証明書の流出等)場合に証明書を使えなくします。RevokeはOAuth2とかでお馴染みですが、このための通信(OCSP等)でブラウザの表示に影響が出る事もあります。
ブラウザの実装によっては、このチェックを無視するようにするオプションがあった事もありました。 高速化を阻害しますからね。
証明書の形式
証明書? よくある証明書を見ると一つしかないじゃん…これでしょ?どこに署名があるの?
X.509v3のpemの例をあげます。
これは、人間に読めなくてもいいから機械で勘違いしないように符号化されているわけですね。
これを符号化から戻すと、
証明機関名:
証明される対象:
公開鍵:
署名:
が出て来ます。
-----BEGIN CERTIFICATE-----
MIIFjDCCBHSgAwIBAgIQCSDPJ+UsiWwnhy6MowjAmjANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUwEwYDVQQLEwxTZXJ2ZXIg
Q0EgMUIxDzANBgNVBAMTBkFtYXpvbjAeFw0xODEwMjkwMDAwMDBaFw0xOTEwMjkx
(中略)
Ubx7XQhrqEL9qsnBzceyt5wocCkAqjzZ3G1F2TLy0MvUCzqxIDQZdUwOq0NVfGKy
aW6WZo1YGlt5RA58wDZjMorEzQRtoK/PIMPxMM66Chw=
-----END CERTIFICATE-----
sh-4.2$ openssl x509 -text < AmazonSES.crt AWSのSMTPエンドポイントの中間証明書例
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
a7:0e:4a:4c:34:82:b7:7f
Signature Algorithm: sha256WithRSAEncryption #署名の方式
Issuer: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority #証明書発行者
Validity
Not Before: Sep 2 00:00:00 2009 GMT
Not After : Jun 28 17:39:16 2034 GMT
Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Services Root Certificate Authority - G2
Subject Public Key Info: #公開鍵情報
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus: #実際の公開鍵 RSAの乗数結果
00:d5:0c:3a:c4:2a:f9:4e:e2:f5:be:19:97:5f:8e:
(中略)
be:2a:2f:c4:55:1c:75:40:60:17:85:02:55:39:8b:
7f:05
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Key Usage: critical
Digital Signature, Certificate Sign, CRL Sign
X509v3 Subject Key Identifier:
9C:5F:00:DF:AA:01:D7:30:2B:38:88:A2:B8:6D:4A:9C:F2:11:91:83
X509v3 Authority Key Identifier:
keyid:BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7
Authority Information Access:
OCSP - URI:http://o.ss2.us/ #失効確認用
CA Issuers - URI:http://x.ss2.us/x.cer
X509v3 CRL Distribution Points:
Full Name:
URI:http://s.ss2.us/r.crl
X509v3 Certificate Policies:
Policy: X509v3 Any Policy
Signature Algorithm: sha256WithRSAEncryption #署名
23:1d:e3:8a:57:ca:7d:e9:17:79:4c:f1:1e:55:fd:cc:53:6e:
(後略)
シーケンス
TLS1.2の場合
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
TLS1.3の場合
Client Server
Key ^ ClientHello
Exch | + key_share*
| + signature_algorithms*
| + psk_key_exchange_modes*
v + pre_shared_key* -------->
ServerHello ^ Key
+ key_share* | Exch
+ pre_shared_key* v
{EncryptedExtensions} ^ Server
{CertificateRequest*} v Params
{Certificate*} ^
{CertificateVerify*} | Auth
{Finished} v
<-------- [Application Data*]
^ {Certificate*}
Auth | {CertificateVerify*}
v {Finished} -------->
[Application Data] <-------> [Application Data]
お気づきのように、TLS1.3ではシーケンスが減っています。
Client Hello 時に鍵交換体制に入っていますね。
でもRSAの場合はサーバー証明書がないとシーケンス的に共通鍵作成用の情報を暗号化できませんから、選べなくなったんでしょうね。
SSHの場合
公開鍵認証
SSHでも、公開鍵認証ができます。
公開鍵認証をする場合、公開鍵をサーバー側に登録して暗号化します。
これは、TLSのクライアント認証とある意味似ています。
クライアント側が、秘密鍵でもって署名した公開鍵証明書を提示、その証明書をサーバー側で公開鍵で検証する訳ですね。
この設定は、自分の端末で鍵ペアを作成し、セキュアではない経路を使って、公開鍵をアップロードしてサーバー側のSSHデーモンを設定するというのがセオリーでした。MITMは公開鍵を知っても署名した証明書を作成できません。
でも近年は、クラウドサービスなどで、平気で秘密鍵をダウンロードしますね。経路がTLSで暗号化されているからって…
証明書認証
さらにSSHでは、証明書認証も追加されました。これはTLSのようにCAを噛ませて認証する訳です。
クライアントが提示した証明書の署名がCAの公開鍵で検証できれば認証完了です。
指紋
SSHの公開鍵認証では、サーバーの真正性を指紋で確認します。
え?あれだけTLSでは認証機関を多段で構築しておいて急に指紋?単なるハッシュでしょ?いやまあ小規模なSSHではそれでも良いのか…。
結論
ここまで来て確信するのは、あー覚えている情報はもう全部時代遅れだったんだ、もはやわからなくて当たり前という事です… 悩んで当たり前…
というのは、TLSが、柔軟に方式をネゴシエーションできる方式だからです。通信相手とネゴった方式により、やることが違い、結局署名に使った秘密鍵公開鍵ペアは経路の暗号化では使わなかったりする…
この記事を書いて得るものが大きかったものの、キリがない事に気づきました。随時補筆します…
参考文献
2つの公開鍵暗号(公開鍵暗号の基礎知識)
鍵の確認方法
RFC5246(TLS1.2)
RFC8446(TLS1.3)
SSHの鍵交換
RSA鍵、証明書のファイルフォーマットについて