OpenSSLコマンドでオレオレ証明書を作り、ルート認証局としてサーバー証明書を発行する

こんにちは、最近あまりWebの話をしていないwakです。IISをあれこれ触ってリハビリをしようと試みていたのですが、同僚がSSL回りで苦しんでいたので復習がてら表題の件を試してみました。

f:id:nurenezumi:20150831224432j:plain

通信の盗み見、改竄、なりすまし、おいしくないカリカリを許さない鋭い視線の猫

1行でまとめると

こういう証明書を作成するのが目的です。

f:id:nurenezumi:20150831224058p:plain

まずは基礎知識

SSLの目的と機能

SSLには2つの機能があります。

  1. 通信を暗号化して、のぞき見や改竄を防ぐ
  2. 通信相手が本物であることを保証する

たとえば公衆Wi-Fiに紛れて偽Wi-Fiアクセスポイントを設置した悪者がいたとします。このアクセスポイントは悪者の支配下にありますから、ここに接続してしまった人は全ての通信内容を見られてしまいますし、通信内容は改竄され放題です。「mizuhobank.co.jp」にアクセスしていたつもりなのに、実際にはフィッシングサイトに接続させられていたということも起こり得ます(これではいくら通信が暗号化されていても意味がありません!)。こういった状況でも、

  • 暗号化によって通信をのぞき見を防ぐ
  • もし偽の相手と通信していたらそれを検知できる

ようにして安全にインターネットを利用できるようにするのがSSLの役割です。

SSLが安全を保証する仕組み

  1. ブラウザはサーバーと通信を開始すると、まず最初にサーバー証明書をチェックします。サーバー証明書には、「私は本物のexample.comです」とAさんの署名つきで書いてあります。
  2. そこでブラウザはAさんに「ああ言ってるけど君は誰? 信頼できる人なの?」と聞きにいきます。Aさんは「私は本物のAです。Bさんの署名もあります」という証明書を示します。
  3. そこでブラウザはBさんに「ああ言ってるけど君は誰? 信頼できる人なの?」と聞きに行きます。Bさんは「私は本物のBです。Cさんの署名もあります」という証明書を示します。
  4. そこでブラウザはCさんに「ああ言ってるけど君は誰? 信頼できる人なの?」と聞きに行きます。Cさんは「私は本物のCです。Dさんの署名もあります」という証明書を示します。
  5. そこでブラウザはDさんに「ああ言って

このたらい回しはいつ終わるのでしょうか。その答えは、OSまたはブラウザに内蔵された「この人は絶対に信用できる人だ」という署名のリスト=ルート証明局にあります(これはWindowsインストール時、Windows update、ブラウザのインストール時などに導入されます)。たらい回しをずっと続けていくと、いつかは必ず、このルート証明局の署名が入った証明書にぶつかります。続けてみましょう。

……そこでブラウザはDさんに「ああ言ってるけど君は誰? 信頼できる人なの?」と聞きに行きます。Dさんは「私は本物のDです。ルート証明局の署名もあります」という証明書を示します。するとブラウザは、

  • Dさんは信頼できる
  • Dさんが署名したCさんの証明書も信頼できる→Cさんは信頼できる
  • Cさんが署名したBさんの証明書も信頼できる→Bさんは信頼できる
  • Bさんが署名したAさんの証明書も信頼できる→Aさんは信頼できる
  • Aさんが署名したexample.comの証明書も信頼できる
  • じゃあこのサーバー証明書を持ってるexample.comは本物だ!

と結論づけることができます。めでたしめでたし。

公開鍵暗号

SSLでは公開鍵暗号というものを利用します。どんな暗号でも必ず鍵が必要ですが(例:アルファベットを2文字後ろにずらす、全てのデータに01101001でXORをかけるなど)、公開鍵暗号では鍵を決めるときに必ず「秘密鍵」「暗号鍵」のペアを作ります。この鍵には次の性質があります。

公開鍵を使って文書を暗号化することができる。暗号化された文書は秘密鍵だけで復号できる

つまり公開鍵だけを知っていても、文書を暗号化することができるだけです。暗号化された文書を復号することはできません。*1

証明書を作る

オレオレ証明書

まず「自己署名入り証明書」、通称「オレオレ証明書」を作りましょう。これは「俺は○○です。署名は自分でした。俺を信じろ」という全く信頼の置けない証明書です。とはいえ、全てのルート証明書はこのタイプですから重要です(他の誰かに署名してもらおうとすればまたさっきのたらい回しが始まってしまいます)。

まず、乱数を使って秘密鍵を作ります。これは誰にも秘密の鍵で、「この鍵を知っている」ことが「自分が自分である」ことの証明となります。また、秘密鍵があれば対応する公開鍵はいつでも取り出せます。

openssl genrsa 2048 > ca.key

次にこの秘密鍵(に対応する公開鍵)と、組織名・サーバー名(コモンネーム)などの情報とを混ぜ合わせて「CSR(Certificate Signing Request)」を作成します。これは次の手続きのために一時的な必要なだけの情報です。また、-subjオプションは指定しなければ対話式で聞かれます。

openssl req -new -key ca.key -subj "/C=JP/ST=Tokyo-to/L=Minato-ku/O=Sanwa System/OU=IT dept./CN=Sanwa System CA" > ca.csr

最後に、-sinkeyオプションを使ってこのCSRに自己署名を追加してサーバー証明書を作成します。

openssl x509 -days 365 -req -signkey ca.key < ca.csr > ca.crt

本来であれば最後のステップは信頼できる証明局にお願いして実施してもらう作業です。そのことにより、

証明局は信頼できる
  ↓
証明局によって署名されたサーバー証明書も信頼できる(偽造されていない)
  ↓
そのサーバー証明書に含まれる公開鍵で何か適当なフレーズを暗号化する
  ↓
通信相手がその暗号を解読できるか試してみる
  ↓
解読できたなら通信相手のサーバーは秘密鍵を知っている
  ↓
信頼できる

というチェーンが完成します。

WindowsPCに証明書をインストール

これで生成された証明書をWindows PCに持ってきてダブルクリックで開くとインストールするかどうかを聞かれます。

f:id:nurenezumi:20150831221505p:plain

インストールしましょう。

f:id:nurenezumi:20150831221745p:plain

これでこのPCはこの秘密鍵によって署名された証明書を無条件で信頼するようになり、

などが全部通るようになります。そしてこの証明書の中核はさっき作った秘密鍵(つまりca.keyというファイルの中身)ですから、これが誰かにバレたら大変なことになります。

サーバー証明書を作る

続いてサーバー証明書を作り、ルート証明局、つまりさっきの証明書の秘密鍵で署名してみましょう。CSRを作るところまでは同じです。

openssl genrsa 2048 > server.key
openssl req -new -key server.key -subj "/C=JP/ST=Tokyo-to/L=Minato-ku/O=Sanwa System/OU=IT dept./CN=*.ssc-aws.com" > server.csr

普通はこれをルート証明局に送付して署名してもらうのですが、今回は自分で署名します。

CSRに署名する

手順が若干面倒です。まずOpenSSLの設定ファイル、/usr/lib/ssl/openssl.cnfを開くと次のような行があるはずです。

dir             = ./demoCA              # Where everything is kept

OpenSSLコマンドは単にCSRファイルに署名を行うだけではなく、その証明書を管理する機能も持ち合わせています。そのためのファイルを配置する場所がこの行です。そこでこの行に従い、どこかに次のようなディレクトリを作成してください。(なぜか相対パスで書いてあるので、気持ちが悪ければ絶対パスに書き換えても構いません)

-./
 +-demoCA <dir>
   +-private   <dir>
   +-crl       <dir>
   +-certs     <dir>
   +-newcerts  <dir>
   +-index.txt
   +-serial

いずれも設定ファイルに書いてある通りです。ファイル2つは手で作ります。

echo "01" > serial
touch index.txt

こうした上で、demoCAの親ディレクトリで署名コマンドを実行します。

openssl ca -days 365 -cert ca.crt -keyfile ca.key -in server.csr > server.crt

これでサーバーの証明書が生成されました。この証明書は自作のルート証明局にぶら下がっているので、問題なく検証が成功します。

f:id:nurenezumi:20150831223737p:plain
f:id:nurenezumi:20150831223743p:plain

証明書の管理

署名を行うと、demoCAディレクトリ以下のファイルがいくつか更新されます。これはOpenSSLが認証局としての仕事を行うため、いつ、どんな証明書に署名したかを管理しておく必要があるからです。たとえば先ほど作成したindex.txtはこんな内容で更新されます。

V    160830133444Z       01  unknown /C=JP/ST=Tokyo-to/O=Sanwa System/OU=IT dept./CN=*.ssc-aws.com

このように、現在どのような証明書が有効なのか、その証明書の内容はどんなものか、などが分かるようになっています。必要があれば失効させ、問い合わせを受けたとき*2には「もう無効になっていますよ」と答えて利用者の安全を確保するわけです。

まとめ

初歩的な内容のはずでしたが意外に知識に抜けがあって手間取ってしまいました。ときには足元を見直すのも大切です。

*1:なお、「この逆のこともできる。つまり秘密鍵でも暗号化ができ、暗号化された文書は公開鍵だけで復号できる」という説明がなされることもありますが、そのようなタイプの公開鍵暗号があるというだけで、公開鍵暗号すべてに当てはまるわけではありません。

*2:SSLサーバーとして動作させないといけません