きっかけは「なんとなく使ってるファイル」への違和感
これらのファイル。
xxxx.crt xxxx.p12
クライアント認証を設定するとき、HTTPS化するときも、気づけば当たり前のようにこの手のファイルを扱っていますよね。
「証明書を配置してください」「証明書をインポートしてください」という手順書通りにやれば動くし、それで済ませがちですよね。
証明書を使うと、なぜか完全性の証明になったり、認証ができたりしますが、「らしい」というのが正直なところで、少しあやふやなまま過ごしていて、仕組みを人に説明したこともありませんでした。
というわけで、X.509(RFC 5280)に沿って、証明書の中身を解釈してみることにしました。
証明書の中身は、3つ
証明書の中身は、
- 公開鍵の文字列
- 属性情報(公開鍵の主体者情報や有効期限、シリアル等)
- 1と2に対する、第三者による署名
だけみたいです。それぞれ確認していきます。
1. 公開鍵
まず、公開鍵は公開鍵暗号方式におけるキーペアの片割れです。秘密鍵と公開鍵のうち、みんなに知られてもいい方(知らせる方)ですね。
生成したキーペアのうち、公開鍵がべたーっと書かれます。それと公開鍵のアルゴリズムの情報(キーペアを生成したときのアルゴリズム)も書かれるようです。
公開鍵が単体で存在しているとき、拡張子は.pubや.pemで見かけることが多いです。ただし.pemは「公開鍵専用の形式」というわけではなく、Base64でエンコードされたコンテナ形式そのものの名前で、公開鍵・秘密鍵・証明書・証明書チェーンのどれもが.pemになり得ます。中身が何かは、ファイルを開いて-----BEGIN ...-----というヘッダー部分を見ないと判別できない、というのが実態に近いです。
.p12もよく見かけますが、これは秘密鍵+証明書(場合によっては中間証明書のチェーンも)をパスワードで保護しつつひとつにまとめたファイル形式です。公開鍵を持ってるっちゃ持ってるけど、.pubや.pemとは用途が違う、と考えたほうが実態に近いと思います。
.p12は、クライアント端末に秘密鍵とそれに対応する証明書をインポートするためのファイル形式(PKCS#12)です。
2. 属性情報・メタ情報
バージョン、シリアル番号、有効期限、発行者(署名した人)、主体者(使う人・ドメイン名等)などが書かれます。
この属性情報がどう使われるのかは、単体で見てもいまいちピンと来ないと思いますが、RADIUSのEAP-TLSでのクライアント証明や、TLS(HTTPSとか)でのサーバ証明の流れを追ってみると、急に「あ、ここで使うのか」と繋がってくると思います。対話式でない認証でも、証明書が活躍している場面は意外と多いです。
それだけだと「言ってるだけ」なのでは
証明書を用いた処理では主に①と②の部分を使うため、中身としてはこれで充分に思えますが、問題があります。それは、証明書の中身が改ざんされていないかがわからないことです。
状況で言うと、「身分証として免許証を提出したが、客が自分で書いたものなので本物か不安である」ということです。本人性の認定を行う認証の文脈で、これはキツいですよね。
なので、証明書には①と②のほかに
- 改ざんされていませんよ
- この証明書を認めます
という要素が必要になります。これが3つ目の要素、署名です。
3. 第三者による署名
署名による効果は先述の2点です。
- 改ざんされていませんよという印
- この証明書を認めますという印
それぞれ確認していきましょう。
改ざんされていませんよという印
まず、改ざんされていない、つまり完全性について確認していきます。
署名の仕組みでは、公開鍵と属性情報に対してハッシュ化と数学処理を行った結果を用いて担保します。公開鍵か属性情報が1バイトでもズレると、ハッシュ化・数学処理ともに結果が大きく異なることになります。
なお、数学処理の際には、これまで用意したキーペアのほかに、発行者側の秘密鍵(CA鍵と呼ぶ)を用います。これが署名の肝です。
ざっくりとした署名の作り方はこんな感じです。 (アルゴリズムによって計算方法は異なります。下記はRSAの例です)
- 公開鍵と属性情報をバイト列になおす
- バイト列のハッシュ値を出す(SHA-256など)
- ハッシュ値をもとに、発行者のCA秘密鍵と署名アルゴリズムを使って署名値を計算する
- できた署名を証明書にくっつける(と、
.crt/.pemになる)
ここで一点補足です。RSAの場合は「ハッシュ値を秘密鍵で暗号化する」という説明がよくされ、実際そのイメージに近い処理です。ただしECDSAの場合はこのモデルは当てはまらず、まったく別の処理になります。「秘密鍵を使って署名値を計算する(アルゴリズムごとに計算方法は異なる)」くらいに捉えておくのが正確です。
公開鍵や属性情報が1バイトでも改ざんされていたらハッシュ値が変わってしまうので、署名検証時に不一致となり、すぐわかる仕組みになっています。
署名検証はこんな流れです。主に、送られてきた証明書と、署名に用いたCA秘密鍵とキーペアの関係にある公開鍵を用いて検証を行います。
- 受け取った証明書を「公開鍵と属性情報の本体部」「署名アルゴリズム」「署名値」の3つに分解する
- 「公開鍵と属性情報」からハッシュ値を計算する(Aとする)
- 証明書の発行者情報を確認し、対応するCAの公開鍵を用意する(すでに信頼しているCA証明書から取り出す)
- 証明書についている「署名値」とCAの公開鍵を使って検証用の値を計算する(Bとする)。RSAの場合は署名値をCAの公開鍵でべき乗演算すると、秘密鍵で暗号化する前のハッシュ値そのものが復元できます。ECDSAの場合は「復号してハッシュを復元する」というモデルにはならず、署名値
(r, s)と公開鍵とメッセージハッシュを使った検証式を計算し、その結果が一致するかを見る、という違うプロセスになります - AとBが一致すれば、改ざんされていない証明書であるということが担保される
なお、このCA公開鍵は事前に署名検証する側に登録が必要です。基本的に事前インストールされているのは「ルート証明書」で、その中の公開鍵を使います。中間証明書は通常、TLSハンドシェイクの際にサーバー側が送ってくるので、あらかじめ端末に入っているわけではありません(ブラウザやOSの実装によっては、一度見た中間証明書をキャッシュすることもありますが、それは補助的な挙動です)。もしルート証明書も中間証明書もない場合(自分で作ったCAで署名した場合など)は、手動で証明書をインポートする必要があります。
ハッシュを突き合わせるだけで改ざんの有無と第三者の認証が同時に証明できるっていうのはよくできてるなと思う。数学者たちが積み重ねてきた努力やチャレンジ、神秘を感じます。
認めますという印
次に、証明書の発行プロセスに工夫を入れると、たとえば「ドメインの所有者であることを認めます」だとか「この組織は実在します」といったことも証明書で表現できます。ドメイン名だけを確認するDV、組織の実在性まで確認するOV、さらに厳格な審査を経るEVといった段階があり、確認済みの内容は証明書のSubjectフィールドに実際の属性として書き込まれます。
つまり証明書とは
証明書は、キーペアのうち、公開鍵の完全性や真正性を証明することに用いることができます。 キーペアが数学的につながっていることを利用し、特定の計算を仕掛けることで検証できるからです。
また、この記事では触れませんでしたが、認証にも用いることができます。 証明書にメールアドレスとかユーザー名を書けるのでIDになるし、秘密鍵つまりシークレットな情報を「所有」しているという点で、ID/PWを入力しているのに近い状態と言えます。 ただし決定的に違う点もあります。ID/PW認証では秘密(パスワード)そのものをネットワーク越しに送信しますが、証明書ベースの認証(TLSクライアント認証など)では秘密鍵そのものは一切送信されません。サーバーから送られたチャレンジに対して秘密鍵で署名を返すだけで、「秘密鍵を持っていること」を証明します。盗聴されても秘密鍵自体が漏れることはない、という点でパスワード認証より本質的に安全な仕組みです。
これは公開鍵認証方式(チャレンジレスポンス)というものです。そのうち書きます。
とはいえ、秘密鍵と証明書のファイル自体を盗まれると、それだけでとてもラクチンに不正アクセスが可能になってしまいます。
なので、いろいろな対策が必要になります。それはまた別のお話。













