オレオレ証明書をopensslで作る(詳細版)

前回のopensslでRSA暗号と遊ぶRSA暗号秘密鍵について中身を色々といじってみた。続いて今回は、Apacheで使うオレオレ証明書を作ってみる。

細かいことはいいから、オレオレ証明書を作るコマンドだけ知りたい

お急ぎの方は、以下3つだけやれば良い。これで10年間(3650日)有効なオレオレ証明書ができあがる。

$ openssl genrsa 2048 > server.key
$ openssl req -new -key server.key > server.csr
$ openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt

できあがったserver.crtとserver.keyを、例えば/etc/httpd/conf/ 配下のssl.crt/ と ssl.key/ ディレクトリに設置し、以下のようにApachessl.confに記述する。

SSLCertificateFile /etc/httpd/conf/ssl.crt/server.crt
SSLCertificateKeyFile /etc/httpd/conf/ssl.key/server.key  

なお途中に作ったserver.csrは不要なので消してしまって良い。

以下はこの解説なので、とりあえず動けばいい人はこれ以上読む必要ありません。。。

SSL証明書の概要

オレオレ証明書を作るにはopensslコマンドを使えばいいのだが、オプションが複雑なことと、途中に色々なファイルができることから、なかなか全体像を把握することが難しい。

ということで、まずは証明書の仕組みを解説する図を描いてみた。なお以下の図は、オレオレではない、通常のSSL証明書を取得する場合の図です。

ファイル名 機能
server.key 秘密鍵 (Private Key)
server.csr 証明書署名要求 (CSR)
server.crt サーバ証明書(CRT)


証明書の発行手順は、以下の通りである。今回作ろうとしているオレオレ証明書では、「サーバ管理者」と「認証局」が同一となることに注意。

  1. サーバ管理者が秘密鍵ファイル(server.key)を作成する。
  2. サーバ管理者が秘密鍵ファイルから、公開鍵(Public Key)と、秘密鍵のハッシュを組み合わせて、証明書署名要求ファイル(server.csr)を作成する。サーバ管理者はこの証明書署名要求ファイルを認証局に送付する。
  3. 認証局は自分の秘密鍵で証明書署名要求ファイルに署名してサーバ証明書(server.crt)を作成し、返却する。

ポイントとしては、全ての根幹はサーバの秘密鍵(Private Key)である。秘密鍵から公開鍵を作れるし、証明書署名要求も作れるし、秘密鍵を用いて暗号化・復号もおこなえる。

というわけでまずは(1)秘密鍵の作成を見てみよう……と言いたいのだが、その前にまず、「なぜ鍵ファイルだけではダメで証明書が必要なのか?」をちょっと確認しておきたい。

なぜ鍵だけではダメなのか

そもそもRSA暗号秘密鍵と公開鍵のペアさえあればいいはずなのに、なぜ証明書が必要なのだろうか? それは、「鍵が本当に通信相手のものかを確認する」ためである。

AliceがBobに通信を送る例を考える。BobはAliceから、「Aliceの公開鍵」を受け取りたいが、これをどうもらえばいいだろう? もちろん直接Aliceから手渡しされれば確実だが、現実にはメールでもらったり、AliceのWebからダウンロードするのが普通だろう。

となると、全くの第三者がAliceのふりをして、「これ、私(Alice)の公開鍵だから。使ってね」と鍵を渡してくるかもしれない。本物のAliceの鍵だと信じ込んだBobが、その鍵で暗号化して通信した場合、第三者がその通信を復号できてしまう。そのため鍵を受け取ったBobは、それが本当にAliceの鍵なのかを確かめる必要がある。

ここで出てくるのが認証局である。BobはAliceの鍵の中に、この鍵の正当性は執事のセバスチャン(認証局)に聞いてみろという記述を見つける。セバスチャンは、もともとBobもよく知っており、信頼を寄せる人であった。念のためセバスチャンに聞いてみたところ、「確かにこれはAliceお嬢様の鍵でございます」という返事だったので、やはりAliceの鍵で間違いないようだ。

実際のSSL証明書では、認証局が自分の秘密鍵でサーバの公開鍵を署名している。これによりユーザ(Web閲覧者)は、そのサーバ証明書が正当であることが確認できる。

(1) 秘密鍵(Private Key)の作成

まずは秘密鍵を作成する。ここではオプションを一つ付けて、2048bitの鍵を作成している。デフォルトの512bitではちょっと弱いからだ。

$ openssl genrsa 2048 > server.key

実際にできた鍵の中身を見てみるには、opensslのrsaコマンドに -text オプションを付けてみればよい。

$ openssl rsa -text < server.key
Private-Key: (2048 bit)
modulus:
    00:a5:bf:ef:e2:7c:ee:99:f9:df:47:1e:45:2a:f5:
    df:bf:bc:9e:e1:00:90:06:84:e2:97:10:60:7d:61:
    73:96:33:a8:8d:c7:2e:02:f4:b1:69:2b:0f:f4:42:
    da:53:ca:34:d0:e0:f7:1a:e5:26:b7:94:ef:b9:8f:
    16:04:e6:05:35:c6:6d:d8:45:24:58:b5:53:8a:c3:
    dd:e9:7a:4e:78:7c:a5:9c:77:1f:17:eb:f0:fb:d8:
    f7:6c:4c:92:a2:e7:c4:14:30:2d:eb:48:b4:16:a9:
    db:9e:8a:fb:7f:1b:69:8b:1a:7f:64:6b:c1:1a:fc:
    32:e3:ec:15:f2:ea:21:16:e8:0a:67:0a:32:9b:5e:
    d8:19:ff:aa:08:93:a8:30:b5:4e:6f:01:ef:c0:af:
    30:ab:a1:ff:18:07:73:3c:a0:e1:96:93:fa:34:2e:
    ab:67:df:87:0e:90:f8:9d:d9:1d:72:e1:89:33:bc:
    bb:b7:68:f4:94:9c:5b:ab:a9:18:c6:19:91:3f:4f:
    e6:8e:a9:cb:c7:69:4f:e9:72:d6:f9:8d:9e:19:85:
    78:4c:00:e6:87:5b:b7:2a:46:1a:28:43:ab:73:6d:
    96:60:9b:87:ab:28:02:a0:dd:81:bc:d1:e6:c9:4e:
    d7:f8:e6:bf:ca:53:ae:01:d0:a4:fb:d9:e0:93:6c:
    25:21
publicExponent: 65537 (0x10001)
privateExponent:
    00:83:ff:b7:2a:c8:13:bc:cc:21:e4:a1:56:b4:3f:
  (省略)
prime1:
    00:d4:0d:76:ff:f8:f4:ed:c9:eb:42:f5:7b:bf:a5:
  (省略)
prime2:
    00:c8:19:db:3c:cf:05:0d:13:5c:76:0c:e8:bf:b2:
  (省略)
exponent1:
    36:ab:42:02:bc:2c:69:21:a5:85:9d:c4:49:a4:82:
  (省略)
exponent2:
    00:9d:44:55:1e:f1:c6:12:84:c9:02:af:1a:ae:af:
  (省略)
coefficient:
    00:9c:00:77:95:3d:2d:fc:52:64:87:01:11:80:02:
  (省略)
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEApb/v4nzumfnfRx5FKvXfv7ye4QCQBoTilxBgfWFzljOojccu
AvSxaSsP9ELaU8o00OD3GuUmt5TvuY8WBOYFNcZt2EUkWLVTisPd6XpOeHylnHcf
  (省略)
-----END RSA PRIVATE KEY-----

それぞれの値の説明などは、前回のopensslでRSA暗号と遊ぶで書いたのでそちらを参考。なお、ここでは例示のためにナチュラルに秘密鍵の中身を晒しているが、こんなことは現実のサーバでは絶対にしてはいけない。

(2) 証明書署名要求(CSR; Certificate Signing Request)の作成

証明書署名要求は、認証局に対して、サーバ管理者が自分の公開鍵に電子署名(すなわち、認証局秘密鍵で署名)してもらうよう依頼するメッセージである。原理的にはまず秘密鍵から公開鍵を作る必要があるが、opensslのreqコマンドで秘密鍵を指定してあげれば勝手に公開鍵を取りだしてくれるので、明示的にファイルとして作る必要は無い。(だから、この記事中でも公開鍵をファイルとして作ってはいない)。

$ openssl req -new -key server.key > server.csr

コマンドを実行すると対話形式で色々聞かれるが、ここではオレオレ証明書を作る予定なのでデフォルト値を用いてEnter連打で構わない。(ただしCommon Nameのところは、もしFQDNがあるならばそれを入れておいた方が妥当)。

$ openssl req -new -key server.key > server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

これで証明書署名要求ファイル(server.csr)ができあがる。さっそく中身を見てみよう。

ファイルの中を確認するには、opensslのreqコマンドの -text オプションを使えば良い。

$ openssl req -text < server.csr
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:a5:bf:ef:e2:7c:ee:99:f9:df:47:1e:45:2a:f5:
                    df:bf:bc:9e:e1:00:90:06:84:e2:97:10:60:7d:61:
                    73:96:33:a8:8d:c7:2e:02:f4:b1:69:2b:0f:f4:42:
                    da:53:ca:34:d0:e0:f7:1a:e5:26:b7:94:ef:b9:8f:
                    16:04:e6:05:35:c6:6d:d8:45:24:58:b5:53:8a:c3:
                    dd:e9:7a:4e:78:7c:a5:9c:77:1f:17:eb:f0:fb:d8:
                    f7:6c:4c:92:a2:e7:c4:14:30:2d:eb:48:b4:16:a9:
                    db:9e:8a:fb:7f:1b:69:8b:1a:7f:64:6b:c1:1a:fc:
                    32:e3:ec:15:f2:ea:21:16:e8:0a:67:0a:32:9b:5e:
                    d8:19:ff:aa:08:93:a8:30:b5:4e:6f:01:ef:c0:af:
                    30:ab:a1:ff:18:07:73:3c:a0:e1:96:93:fa:34:2e:
                    ab:67:df:87:0e:90:f8:9d:d9:1d:72:e1:89:33:bc:
                    bb:b7:68:f4:94:9c:5b:ab:a9:18:c6:19:91:3f:4f:
                    e6:8e:a9:cb:c7:69:4f:e9:72:d6:f9:8d:9e:19:85:
                    78:4c:00:e6:87:5b:b7:2a:46:1a:28:43:ab:73:6d:
                    96:60:9b:87:ab:28:02:a0:dd:81:bc:d1:e6:c9:4e:
                    d7:f8:e6:bf:ca:53:ae:01:d0:a4:fb:d9:e0:93:6c:
                    25:21
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha1WithRSAEncryption
         0d:f8:4e:10:f8:5d:cb:d4:1b:cd:01:b9:06:69:f2:80:ce:6a:
         bf:25:8a:83:e6:5c:39:29:8d:62:02:a5:6b:da:9e:1f:aa:3e:
      (省略)
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
(省略)
-----END CERTIFICATE REQUEST-----

[Subject]は証明書を受けたいこのサーバの情報で、オレオレなのでさっきデフォルトで入れた適当な値が書かれている。

続いて、[Public Key Info] のところにこのサーバの公開鍵(ModulusとPublicExponent)が書かれている。一方、秘密鍵であるprivateExponentは入っていない。そして、2つの素数であるprime1とprime2ももちろん入っていない。このことから、証明書署名要求はサーバの公開鍵に署名してもらう、ということが確認できた。

なお補足だが、先ほどの出力結果の最後を見るとCSRにはサーバの秘密鍵そのものは入っていないが、サーバの秘密鍵で署名した文字列が[Signature Algorithm]のところに入っているのが分かると思う。これは認証局(CA)に対し、要求者がそのサーバ管理者本人であることを示すためのものである。

(3)サーバ証明書の作成

通常ならば、server.csrVeriSignなどの認証局に送付して署名してもらう。しかしここでは、オレオレ証明書ということで自分で署名することにする。つまり、「server.csrはserver.keyから作ったけど、これをserver.keyで署名する」というわけである。

先ほどの図をもう一度出すと:

右側の認証局のPrivate Keyは、本来は左側のサーバのものとは違う、認証局が持つ秘密鍵である。しかしこれを、サーバ自身が認証局となって自分の秘密鍵CSRに署名してしまおう、というわけである。「オレオレ証明書」とは、なんともまぁよくできたネーミングである。

これが何故セキュリティ的に問題か。先ほどのAliceとBobの例で説明すると、Alice(のふりをしているかもしれない人)が、Bobにメールで鍵を送って、「これ、あたしの鍵だから!」と言っている。そしてその鍵の中身を確認すると、「この鍵の正当性は、あたしに聞きなさい!」と書いてある。なんというタカビーな娘だ。これではなんにも確認することができない。その鍵が本当にAliceのものかは判断できない……ので問題である。

しかしまぁ、今はオレオレ証明書を作ろう。CSRに署名を付けるには、opensslのx509コマンドを使って-reqオプションを付ける。また、署名する鍵(これは、オレオレなのでサーバ自身の秘密鍵server.key)を指定するオプション-signkeyを付ける。入力ファイルは、当然のことながらCSRファイルである。

$ openssl x509 -req -signkey server.key < server.csr > server.crt 
Signature ok
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
Getting Private key

これで出力されたserver.crtが、サーバ証明書(CRT)である。さっそく中身を見てみよう。opensslのx509コマンドで、-textオプションを使えば良い。

$ openssl x509 -text < server.crt
 Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 9522507648632171161 (0x8426be4107a92a99)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
        Validity
            Not Before: May 11 12:26:52 2013 GMT
            Not After : Jun 10 12:26:52 2013 GMT
        Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:a5:bf:ef:e2:7c:ee:99:f9:df:47:1e:45:2a:f5:
                    df:bf:bc:9e:e1:00:90:06:84:e2:97:10:60:7d:61:
                  (省略)
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha1WithRSAEncryption
         55:c9:7e:af:3f:48:a1:e0:0b:85:36:2e:c1:eb:42:68:a9:32:
       (省略)
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCEJr5BB6kqmTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
(省略)
-----END CERTIFICATE-----

Issuerが、送られてきたCSRに対して実際に署名した者である。通常は認証局の値が入るが、ここではオレオレ証明書なのでSubjectと同じ値が入っている。

Subjectが、先ほどのCSRと同様、証明されたいこのサーバ自身の情報である。Webサーバならば、通常はここにFQDNも入っている(今はオレオレなので省略した)。

Validityが、証明書の有効期間である。[Not Before]と[Not After]で分かるように、この証明書は2013年5月11日12:26:52から2013年6月10日12:26:52までしか有効ではない。オレオレ証明書では利便性のため、この有効期間を長く取るのが常套手段である。そのためにはCSRに署名をする際、-daysオプションに大きな値を入れてやれば良い。

$ openssl x509 -req -days 3650 -signkey server.key < server.csr > server.crt  

これで3650日、つまり10年有効なオレオレ証明書を作ることができる。

最後の[Signature Algorithm: sha1WithRSAEncryption]の後に、認証局の署名が付いている。これは認証局秘密鍵によって付けられた署名であるため、ユーザ(Webを見る人、もっと厳密に言えばユーザのPCのWebブラウザ)は認証曲の公開鍵を用いてこの証明書の正当性が確認できる。ただ、この署名はどこからどこまでをsha1取って署名しているのか、が分からなかった……具体的に手で叩いて確認するところまでできていないので今後の宿題。

また、見ての通り、Public Key(ModulusとExponent)も証明書には同梱されている。このためユーザは別途公開鍵を取りに行く必要は無い。証明書の正当性を確認したら、その中に同梱されている公開鍵を使って通信を開始すれば良いわけだ。

こうして見て分かった通り、サーバ証明書は、「サーバの公開鍵と、サーバのSubject(組織名・FQDNなど)と、有効期限」と、「それに付けられた認証曲(CA)の電子署名」のセットである。

SSL証明書ファイルをApacheへ設定

では、できあがったファイルをApacheに設定して、https通信できるWebサーバを構築しよう。必要なのは、先ほど作った以下2ファイルである。

途中で作った証明書署名要求(server.csr)は、もう使わないので削除して構わない。

証明書ファイルの設置

いろんな流儀があるけど、私はApacheのconf配下に[ssl.key]と[ssl.crt]ディレクトリを作ってそこに秘密鍵と証明書を置くことにしている。異論は認める。

# cd /etc/httpd/conf
# mkdir ssl.crt
# mkdir ssl.key -m 700
# mv /your/path/server.crt ssl.crt
# mv /your/path/server.key ssl.key
# chmod 400 ssl.key/server.key
# ls -lF
total 96
-rw-r--r--. 1 root root 34444 May 10 23:59 httpd.conf
-rw-r--r--. 1 root root 13139 Feb 22 20:20 magic
drwxr-xr-x. 2 root root  4096 May 11 23:13 ssl.crt/
drwx------. 2 root root  4096 May 11 23:13 ssl.key/
# ls -lF ssl.crt/
total 4
-rw-r--r--. 1 root root 1111 May 11 01:07 server.crt
# ls -lF ssl.key/
total 4
-r--------. 1 root root 1679 May 11 00:18 server.key

ssl.keyディレクトリはrootオーナの700にしておき、さらに秘密鍵パーミッションも400にして他人から読めないようにしておく。一方、公開鍵ファイルserver.crtは、もともとWebを通して広く配布するものなので、root以外から読めるようにしておいて構わない。

httpd-ssl.conf または ssl.conf に設定追加

Apacheの設定は、以下のように、証明書ファイルと秘密鍵ファイルを設定するディレクティブを追加してやれば良いだけである。

SSLCertificateFile /etc/httpd/conf/ssl.crt/server.crt
SSLCertificateKeyFile /etc/httpd/conf/ssl.key/server.key

インストールされているApacheの環境によって、この記述を追加するべき設定ファイル名は微妙に違う。

CentOSyumApacheを入れている場合は、(入っていなければ)追加でmod_sslをインストールする必要がある。

# yum install mod_ssl

すると /etc/httpd/conf.d/ssl.conf が出来るため、このファイルに設定を書く。

Apacheをソースからインストールしている場合は、mod_ssl付きでコンパイルしている必要がある(すなわち、./configure 時に --enable-ssl している必要がある)。httpdを-Mオプションで確認してみよう。

$ /usr/local/apache2/bin/httpd -M
Loaded Modules:
 core_module (static)
 mpm_prefork_module (static)
 http_module (static)
 so_module (static)
...
...
 ssl_module (shared)
...
Syntax OK

ここに[ssl_module]があれば、Apacheのconfディレクトリ配下の extra/httpd-ssl.conf に記述すれば良い。なおこの際、httpd.conf内でhttpd-ssl.confをIncludeする箇所がデフォルトではコメントアウトされているため、このコメントアウトを外す設定も必要である。

なおいずれの場合でも、SSLCertificateFileとSSLCertificateKeyFileのディレクティブは既に書かれているため、そのファイルパスを修正するだけで設定は完了するはずである。

Apacheをreload

confファイルを書き換えたら、Apacheをリロードすれば設定は完了する。reloadする前に、configtestをしておいた方が良い。以下はCentOSyumで入れている場合。

# service httpd configtest
Syntax OK
# service httpd reload
Reloading httpd:

リロードできたら、https://(サーバのIPアドレス)/ にアクセスする。

オレオレ証明書のためブラウザの警告が表示されるが、無視して続行してページ表示ができればOK。

Webブラウザで確認


最後に、Webブラウザから証明書を確認してみる。IEならばアドレスバーのところの「証明書のエラー」をクリックし、「証明書の表示」を押す。

「発行先」と「発行者」が同一であることが確認できる。確かにオレオレだ。

続き

次の記事(オレオレ証明書とルート証明書 - ろば電子が詰まっている)で、ブラウザでの証明書の確認方法などをまとめておきました。