SSL 証明書、サーバ証明書、ルート証明書、オレオレ証明書……その辺の話を咀嚼してまとめた

SSL 絡みのエラーが起きた時に「何が起きているのか」「どうすればいいのか」を理解し対処できるようになることを目標にしたい。言い換えると、対処法をググっておまじないとして「なんかこうしたら動いた」で解決するのではなく、その背景をちゃんと理解したい。

というわけで、SSL やら証明書やらの話題(特に仕組みや各用語の意味など)について雑多にまとめてみた。

SSL certificates(SSL証明書) とは

SSL認証を行うのに使うファイル。拡張子は .pem だったり .crt だったりする。内容は公開鍵の羅列。

例: GlobalSign Root CA(ルート1) と GlobalSign Root CA(ルート2) を並べた例

-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
...
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
...
-----END CERTIFICATE-----

参考:

CA_BUNDLE とは

ルート証明書中間証明書 をいっしょくたにした SSL 証明書ファイルのこと。

要するに SSL 認証で実際に使う公開鍵を列挙したもの。

CA_BUNDLE を指定する

ブラウザだけ使ってる場合は CA_BUNDLE を意識することはあまりない。元からブラウザに同梱されているし、追加手順が必要な場合(たとえば会社で一度くらいはやらされたことがあるのではなかろうか)も「証明書をインポートする」的な手順で、ブラウザから証明書ファイルをインポートするだけで済む。

問題は一部アプリやコマンドや、もっと低レベルではプログラミングで「HTTPS 通信を行う時に使うライブラリ」を使う時。これらはブラウザが持ってる証明書ファイルを見てくれない。じゃあどうするかというと「この証明書を使え」と我々が指示する必要がある。

  • 環境変数にて証明書ファイルのパスを指定する
    • curl コマンドは CURL_CA_BUNDLE 環境変数に
    • openssl コマンドは SSL_CERT_FILE 環境変数に
  • ライブラリのインタフェースにて証明書ファイルパスを指定する
    • Python の requests ライブラリの GET メソッド requests.get()verify 引数に

CA_BUNDLE の取得先

下記 URL から直接ダウンロードする。

公開鍵暗号方式(公開鍵と秘密鍵)

公開鍵暗号方式って何?公開鍵と秘密鍵の違いは?……などなど、SSL認証を知る上で避けて通れないこの暗号方式についてざっくりと解説する。知ってる人は飛ばせばいい。

登場人物

  • データ …… 暗号化したいデータ(誰かに見られちゃ困るデータ)
  • 公開鍵 …… なんか暗号化で使うやつ1
  • 秘密鍵 …… なんか暗号化で使うやつ2
  • 変換器 …… データと鍵を受け取って暗号化を行うプログラム
  • 暗号化されたデータ …… データを暗号化したもの。見ても意味不明で元のデータは取り出せない。

暗号化のルール

公開鍵暗号のルールを4つほど挙げる。これを頭に入れておく。

ルール1: 公開鍵で暗号化できる

[データ] + [公開鍵] ----> ★変換器★ ----> [公開鍵で暗号化されたデータ]

ルール2: 秘密鍵で暗号化できる

[データ] + [公開鍵] ----> ★変換器★ ----> [秘密鍵で暗号化されたデータ]

ルール3: 「公開鍵で暗号化されたデータ」は秘密鍵で元に戻せる

[公開鍵で暗号化されたデータ] + [秘密鍵] ----> ★変換器★ ----> [データ]

ルール4: 「秘密鍵で暗号化されたデータ」は公開鍵で元に戻せる

[秘密鍵で暗号化されたデータ] + [公開鍵] ----> ★変換器★ ----> [データ]

データを安全にやり取りする方法

上記4つのルールを使うと、二者間でデータを安全にやり取りできる。

例として(IDやパスワードなどデリケートなデータをやり取りする)ウェブサイトを想定しよう。

まず登場人物は二人。

  • ウェブサイト見る人
  • ウェブサイト

彼らの所持品はこうなっている。

  • ウェブサイト
    • 秘密鍵(誰にも教えない)
    • 公開鍵(一般公開する)

で、ウェブサイト見る人は、ウェブサイトが公開してる公開鍵を入手し、これを使ってデータを暗号化してからウェブサイトに送信する。このデータは 秘密鍵を持つウェブサイト運営元しか元に戻せない ので安全だ。たとえ通信途中で盗聴されたとしても問題無い。

まとめ

つまり公開鍵暗号方式とは、

  • 公開鍵で暗号化したデータは秘密鍵でしか元に戻せないよ

という絶対的なルールの下に成立する暗号技術。

Q: そんな都合の良い暗号技術ってホントにあるの?どうやって実現しているの?

Ans: 具体例を挙げると RSA暗号 など

理解するには数学と情報工学の知識が必要。いやはやこんなの考えるなんて先人は凄いですね。

サーバ証明書とルート証明書の関係や仕組み

いまさら聞けない、SSLサーバ証明書とルート証明書の関係.pdf

サーバ証明書って何?ルート証明書って何?違いは?どんな風に動作してるの?……その辺よくわからんかったのでまとめた。上記 PDF が詳しいが、勉強がてら自分の言葉であえてまとめた。

SSLサーバ証明書

  • 各ウェブサーバが持ってる「公開鍵と署名」の組データ
    • 公開鍵 = 各ウェブサーバが使う公開鍵暗号用の公開鍵
    • 署名 = 認証局が発行された「このウェブサーバーさんはこういう身元で信頼できますよ」的な太鼓判データ
  • このSSLサーバ証明書は「認証局の秘密鍵」によって暗号化されている

SSLサーバ証明書は「認証局の公開鍵」でしか復号できない。

ルート証明書

  • 各クライアントが持ってる「SSLサーバ証明書を復号する」ためのデータ
  • 実体は認証局の公開鍵
  • クライアント(ブラウザ)はルート証明書を複数所持している
  • ルート証明書は(普段はしないが)手作業で追加/削除することもできる

通信手順

  • 1 クライアントがサーバーにアクセス
  • 2 サーバーは「SSLサーバ証明書」をクライアントに返す
  • 3 クライアントは「ルート証明書」を使って「SSLサーバ証明書」を復号し、中身を調べる
    • 署名を取り出せなかった or 署名がおかしい → 相手は信用できないため 通信中止(ブラウザだったら警告出すとか)
  • 4 クライアントは復号した「公開鍵」を使ってデータを暗号化し、サーバーに送る
    • 暗号化したデータは秘密鍵を持つ相手サーバしか復号できないので安全

ざっくり言うとこんな感じ。本当は更に共通鍵をやり取りする手順があるけど割愛。

ルート証明書はどうやって入手&メンテナンスする?

  • ブラウザにデフォで組み込まれている
  • ブラウザをアップデートするとルート証明書も更新される

オレオレ証明書

オレオレ証明書はなぜ危険か。そもそもオレオレ証明書は何なのか。その辺よくわからなかったのでまとめた。例も二つ交えて、より具体的にイメージできるようにしたつもり。

オレオレ証明書の意義について

  • SSL認証という仕組みがある限り、安全でない相手と通信してしまう危険はほとんどない
  • SSL認証のせいで、社内で動かしてるサーバーAも「安全じゃないぞ」とブラウザに弾かれてしまう
  • この弾かれをてっとり早く解除する方法が、サーバーAのサーバ証明書Cert-A(これがオレオレ証明書)をつくった上でクライアント側にインポートすること
    • そうすればブラウザはサーバーAにアクセスする時、(既にインポートされてる)サーバ証明書Cert-Aを使って「サーバーAさんは信頼できる相手ですよ」と判別できるので弾かない
  • つまりブラウザ側が持ってる サーバ証明書という「通信先が信用できるかどうかの判断基準」に、オレオレな基準を追加しちゃう ということ

オレオレ証明書に潜む危険について

  • 無闇にオレオレ証明書を使ってると「ブラウザに弾かれてアクセスできない」 → 「そいつのサーバ証明書をインポートすればいいんでしょ?」と機械的に判断するようになってしまう
  • もし危ないサイトに対して同じことしちゃったらどうする?
  • 結局危ないサイトへのアクセスも許しちゃうことになるわけですよ

Q: 主要なサイトを問題無く閲覧できるのはなぜ?サーバ証明書をインポートした覚えはないよ?

Ans: 主要なサイトのサーバ証明書は 最初からブラウザに入っている から

これらサーバ証明書は 多額の献金と厳しい審査をクリアした組織のみ が入手でき、またブラウザへの同梱が認められている。そういう審査を行う第三者組織(認証局)がある。

例1: Amazon の偽サイト Amazan

Amazon と全く同じ見た目の詐欺サイト Amazan があるとして、利用者が Amazan のサーバ証明書を機械的にインポートしたとしたら?

  • 利用者が Amazan にアクセスしてもブラウザは何も弾かない
  • 利用者は Amazon だと思って普通に id/pass 打ってログインする
  • Amazan 運用者(悪者)から見たら id/pass 筒抜け。悪用しまくれる

もし利用者が「ブラウザに弾かれたサイトは使わない」「無闇にサーバ証明書をインポートしない」ことを徹底していたら、(たとえ Amazon と Amazan の違いに気づけなかったとしても)こんな自体は防げていた。もう少し鋭ければ「あれ?あの有名なサイト Amazon なのになんで SSL 認証が通らないんだ?」と疑い、「あ、よく見たら偽サイトじゃん!」と気付けただろう。

例2: Gmail を使いやすくする Web サービス Gmail+

Gmail を本家よりも圧倒的に使いやすくするラッパー的な Web サービス Gmail+ があるとしよう。この Gmail+ は最近誕生したばかりであり、開発したのは身元のよくわからない個人である。しかし見た目も紹介文もしっかりしており、見た感じ信用できそうである。

利用者が Gmail+ を信用して、Gmail+ からのサーバ証明書をインポートしたとしよう。どうなるか。

  • 利用者が Gmail+ にアクセスしてもブラウザは何も弾かない
  • Gmail+ が「Gmail と連携が必要なので Gmail の id/pass を打ってください」と言ってくる
  • 利用者は Gmail+ を信じているので打つ
    • 利用者(HTTPSで通信してるから打った内容が他者に漏れる恐れはないよね)
  • Gmail+ の運用者、実は悪者だった!
    • 入力された id/pass を使って悪用しまくる

もし利用者が「ブラウザに弾かれたサイトは使わない」「無闇にサーバ証明書をインポートしない」ことを徹底していたら、悪用されることはなかった。尤も利用者がこの徹底をすると、悪意のない Web サービスも使えないことになり、悪意無き Web サービス運用者は溜まったものではないが、そこは Web サービス運用者が「信用できるサーバーの一員」として認めてもらう(認証局に認めてもらう)よう手続きする しかない。頑張れ。