プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

Public Key Infrastructureについて

Web API開発においては認証や暗号化のために証明書を利用することは多い。

こういうコードと関係ない部分はセキュリティ担当のインフラ屋さんに丸投げしたくなる気持ちはわかる。

しかしつまらないところではまって時間を無駄にしないよう最低限の知識は身につけておこう。


Public Key Infrastructureとは

PKIとは公開鍵暗号方式電子署名方式で用いる公開鍵とその公開鍵の持ち主の対応関係を保証するための仕組みである。


公開鍵と秘密鍵

PKIでは公開鍵と秘密鍵という2つの鍵を使用する。この2つの鍵は数学的に奇妙な特性を持っていて、公開鍵によって生成された暗号文は秘密鍵によってのみ復号化でき、秘密鍵によって生成された暗号文は公開鍵によってのみ復号化できる。この鍵の特性を利用して「守秘性」、「完全性」、「認証」、「否認防止」を実現できる。

公開鍵は電子証明書に含まれており、通常は通信時に相手から送られてくる。

秘密鍵は公開鍵の持ち主が厳重に保管している。


Certificate Authority

「銀行の証明書だと思って振り込み情報を暗号化して送ったら実は悪意の第3者の公開鍵だった」ということになると困ってしまう。そこで証明書と持ち主の関係を保証する仕組みが存在する。

Comodo, IdenTrust, GoDaddy, Let's Encryptといった信頼ある会社(Certificate Authority、CA)が証明書と持ち主の関係を確認し、その証明書に署名している。受け取った銀行の証明書に信頼あるCertificate Authorityの署名が含まれていればその証明書は信用できると判断できる。

ブラウザにはデフォルトでいくつかの著名なCAが「信頼するCA」として登録されている。さらにユーザ自身が「信頼する」と選択したそれ以外のCAも登録される。その署名が本当にそのCAのものかはCAの公開鍵で署名を復号化して確認する。


Certificate Chain

CAは階層構造になっており、最上位のCAから保証されたCAがまた証明書を発行することができる。最上位のCAをRoot CAと呼び、Root CAより下位階層のCAをIntermediary CAと呼ぶ。

自分の証明書の署名欄には証明書を発行したIntermediary CAのDN(名前)と署名が入っている。そしてIssuerの証明書にはまたそのIssuerのDNと署名が入っている。そして階層の頂点であるRoot CAのRoot Certificateの署名欄はRoot CA自身が署名してある(Self Sign)。このような証明の連鎖をCertificate Chainと呼ぶ。

自分の証明書はend-entity certificateと呼ばれる。Intermediary CAの証明書はintermediary certificatesと呼ばれる。Root CAの証明書はroot certificatesと呼ばれる。


証明書の中身

証明書の中身はX.509というところで規定されている。以下に証明書に含まれているものの例を示す。

  • 持ち主の公開鍵
  • 持ち主のDN(名前)
  • 有効期限
  • シリアル番号
  • 発行者(CA)のDN
  • 発行者(CA)の署名

証明書の種類

証明書には証明される範囲と、CAが実施する審査の厳格さによっていくつか種類がある。

証明される範囲による種別

1. Single-domain
  • 特定の1つのホスト名にのみ適用される
  • 例:www.example.com
2. Wildcard
3. Multi-domain

審査の厳格さによる種別

1. Domain Validation
  • CAはドメイン名が正しいことを審査、保証する。
    • ある証明書がwww.google.comの証明書であることは保証する
    • しかし、www.google.comが本当にGoogleであることは保証しない
  • 最も簡素な審査で、数分程度で完了する。
  • フィッシングサイトでも利用されており信頼性は低い
2. Organization Validation
  • ドメイン名に加え、会社名が正しいことを審査、保証する。
    • www.google.comが本当にGoogleであることを保証する。
  • 審査には1〜2日程度かかる。
3. Extended Validation
  • CAはドメイン名、会社名に加えてその会社が実在することを審査、保証する。
  • 最も厳格な審査で1週間以上かかる。
  • ブラウザのアドレスバーに組織名が表示されることが多い。

価格

種類によって価格が異なる。

最も安いSingle domainのDVなら年間3万円強。

Muti-domainのEVなら年間20万円強である。

All HTTPS化を推進している「Let's Encrypt」からは無償で証明書を入手できるが種別はDVのみで有効期限は最長90日となる。


証明書取得の流れ

Step 1: 秘密鍵の作成

$ openssl genrsa -aes192 -out mykey.key 4096

Step 2: Certificate Signing Requestを作成

$ openssl req -new -key mykey.key -out mycsr.csr

Step 3: CSRの確認

Step 4: CAに申請する

  • CAは持ち主と証明書の関係性を審査するために電話やメールなどで必要な確認を行う
  • 審査が終了すると署名済みの証明書が送られてくる
  • 送り方はCAによって方法は異なる
    • ダウンロードリンク
    • メール添付
    • Simple Certificate Enrollment Protocol (SCEP)
  • 本来、禁じ手だがSelf Signするなら以下の通り
# openssl x509 -days 365000 -req -signkey mykey.key -in mycsr.csr > mycert.crt
  • 発行された証明書をテキスト形式で確認する
# openssl x509 -text -noout -in mycert.crt

Step 5: 証明書をインストールする

  • インストール対象や方法は用途によって異なる
  • Apacheならこんな感じ
  SSLCertificateFile /PATH_TO_CRT/mycert.crt
  SSLCertificateKeyFile /PATH_TO_KEY/private.key  
  SSLCertificateChainFile /PATH_TO_BUNDLE/bundle.crt
  • nginxならこんな感じ
ssl_certificate /etc/nginx/conf.d/ssl/cert.pem;
ssl_certificate_key /etc/nginx/conf.d/ssl/key.pem;
  • Javaで使うならこんな感じ
$ sudo keytool -import -alias publiccrt -file /PATH_TO_CRT/public.crt -keystore /etc/pki/java/cacerts

不要となった証明書の破棄

  • 期限失効前に不要になったCertificateはRevokeできる
  • Revokeする理由には以下のようなものがある
    • 秘密鍵が漏洩した
    • 秘密鍵を紛失した
    • 登録内容に変更があった
    • 誤って発行してしまった
    • 申請者に規則違反があった
  • Revokeは発行元のCAに申請して行う
  • RevokeされたCertificateの情報は伝搬される
  • 伝搬される仕組みは2つある
  • CRLやOCSPで情報が得られなかった場合、ブラウザはCertificateを信用する

Certificate Revocation List

  • RevokeされたCertificateのシリアル番号一覧
  • CRLはCertificateに含まれているCRL Repository URLをPollingして取得する
  • CRLは「openssl crl -inform der -in XXX.crl -text」で読むことができる
  • 破棄されたことが反映されるまでに少しタイムラグがある
  • クライアントにCRL取得、シリアル番号検索の処理負荷がかかる

Online Certificate Status Protocol (OCSP)

  • On demandにRevocation情報をPushしたりPullしたりできるプロトコル
  • クライアントはCertificateに含まれているOCSP Responder URLにそのCertificateが有効か問い合わせる
  • OSCP Responderはシリアル番号をもとに有効状況をチェックして有効、無効、不明のいずれかを応答する
  • CRLよりリアルタイム性が高い
  • Requestが多い環境ではOCSP Responderは大量の問い合わせに応える必要がある

考察

  • 発行、失効が多い場所ではCAを増やしてCRLのサイズを抑える工夫が必要。
  • SCEPを使う環境ならroot CAはsubordinate CA以外からは遮断してもよい。

Certificate Bundling

  • セキュリティプロトコルによってはCertificate Chain全体のチェックが必要なときがある
    • 例:IKEv2など
  • そのようなときはCertificate Chain全体をCertificate Bundleとして1つにまとめて相手に渡すとチェックが迅速に実施できる
    • TLS handshakeなどで有効

Certificate Bundle含まれるもの

  • 含まれる署名を作成した電子証明書
  • Intermediate CA certificates
  • Root CA Certificate(必須ではない。非推奨。)
  • CRL(bundleが不安定になるので非推奨)

Certificate Bundleの形式

  • 形式は複数ある
  • Privacy-Enhanced Mail (PEM) certificateが一般的
  • テキストファイルに発行と逆の順にCertificateをコピーする
    • root certificateが一番下になる
    • 自身は含めない
    • 記載の例:
cat Intermediate2.crt  Intermediate1.crt Root.crt > mychain.pem

よくある証明書のエラー

  • Hostname/Identity mismatch
    • URLとServer Certificateが一致しない
  • Validity date range
    • 有効期限と一致しない
    • 現在時刻がnot beforeとnot afterの間でなければ有効期限外とみなされる
  • Signature Validation Error
    • Self Signedは警告やエラーが出る実装が多い

よくある証明書の攻撃

  • Man in the middle
    • 通信の間に入って傍受して情報を得る
  • HTTPS Spoofing
    • ニセの証明書を送って本来の通信相手と誤認させる
  • SSL Hijacking
    • ニセの証明書をクライントとサーバに信頼させて通信を完全に乗っ取る
  • SSL Stripping
    • TLS handshakeを邪魔してHTTPSからHTTPへ変更する
  • Downgrade Attacks
  • Phishing Attacks
    • ニセの証明書を信頼させる
  • バグを利用する
    • 例)Heartbleed bug(OpenSSL libraryの不具合が原因)

対策としては一般的に不審と思われるものを信頼せず、 信頼できる証明書のみ信頼すること。

  • 不審な添付ファイル
  • 不審なRedirect