SSL自署認証局を構築して、セキュアっぽいSSL証明書を発行する

# はじめに

WEBサイトをSSL（HTTPS）化して運用したいときにはSSL証明書が必要です。

通常、HTTPSのサイトは商用利用が前提となっているため、SSL証明書は一概に非常に高価です（数万円〜など）。
ただ実際には、ステージング環境との通信を秘匿したいとか、テストサイトをセキュアに運用したいとか、
自宅サーバをカジュアルにSSL化したいとか…etc あると思うんですよね。

そんな時、昨今は格安SSL証明書とか無料SSL証明書なんてのもあるわけです。

ただし、会社情報や個人情報を徒らに晒さなければならんとか、
実は取得までに時間かかるものもあるとか…デメリットもありかと思われ。

そんな時、昔から行われている方法で自己署名認証局（通称：オレオレ認証局）を利用した方法があります。
つまり、自分で「安全だ！」と主張する自署認証局を構築し、SSL証明書に署名を行ってインストールする方法を
今回は紹介したいと思います。

（認証局はCA：Certification Authorityと言われますので、以後は自己署名認証局は『自署CA』と記載し、
　有料のSSL証明書を発行する通常の認証局は『認定CA』と記載するルールにしたいと思います。）

これは、署名に関しては全くのところ「確実に信頼ならない」わけですが、構築方法を工夫することで
実際の認証や暗号化の処理を実質的には「確実にセキュアに」できるわけです。

自署CAについては多くのサイトに情報が書かれていると思いますが、技術的にセキュアにできるメソッドに絞って
今回やってみようと思います。

# サーバ環境のセキュリティのこと

ご存じない方もいるかもしれませんが、ここ2〜3年でSSL関連の脆弱性が頻発しています。

これから行う操作は、OpenSSLのライブラリに依るopensslコマンドで行うことになりますが、
OpenSSLモジュール自体のバージョンが古いと、いくら強固な暗号化や認証方式を指定しても
意味をなさない可能性があります。

作業の前にバージョンは確認しておきましょう。

```
$ openssl version
```

ちなみに、2016年12月現在での直近のOpenSSLの脆弱性は以下のようです。
[OpenSSL に複数の脆弱性](https://jvn.jp/vu/JVNVU92930223/index.html)

お使いの環境のバージョンは大丈夫そうでしょうか。
もしバージョンが古い場合は、アップデートしてから構築作業をはじめます。

# SSL通信の超簡易スキーム図

SSLの仕組みについては、詳細を説明した図が様々の書籍やWEBサイトに出ていると思いますが、
今回意識すべきことに絞って極限まで簡略したものが以下です。

![ssl_easy.png](https://qiita-image-store.s3.amazonaws.com/0/147015/18ab4273-4bce-e188-ee49-ce11aa06b7f2.png)

赤字で記載した部分については「認証」、
青字で記載した部分については「暗号化」、
をそれぞれ行います。

認証が脅かされると「なりすまし」、暗号化が脅かされると「盗聴」というリスクにそれぞれ繋がります。
つまり、認証と暗号化をしっかりやってやれば安全なSSL通信になるわけです。

# 前提条件

- 上記のスキーム図では分かりやすくするため、CAを別環境として分離していますが、<br>
今回は対象のWEBサーバ内に自署CAを構築することにします。サーバ上のCA配置パスは以下の通りです。<br>
（本ページを参考にご自身で構築を試される方は、その他適当なパスにして頂いて大丈夫です。）

```
/etc/pki/CA/AllaboutCA
```

- 上記を実現するために、opensslのコンフィグファイルには手を入れておく必要があります。<br>
（yumでインストールしていれば、/etc/pki/tls/openssl.cnfのはずです。）

```/etc/pki/tls/openssl.cnfの書き換え
[ CA_default ]
dir		= /etc/pki/CA		# Where everything is kept

↓　　　　　↓　　　　　↓

[ CA_default ]
dir		= /etc/pki/AllaboutCA		# Where everything is kept
```

- 対象のWEBサーバ（Redhat系を想定）にはApacheが構築されているものとして、<br>
証明書は下記のディレクトリへ配置します。

```
/etc/httpd/conf/cert
```
- Apacheは親プロセスをrootユーザー権限で起動し、子プロセスはapacheユーザー権限で動作するものとします。<br>
（＝デフォルトの動作）<br>
　→つまり自署CAやサーバ秘密鍵の類いはroot権限の方が良いですので、そのように調整します。

# 構築

前提条件の通りですので、rootへスイッチして構築していきます。

【1】WEBサーバ（Apache）の秘密鍵を作成する

SSL通信の安全性は結局のところ、暗号スイート（Cipher Suites）の選択で決定されるのでApacheのSSL周りの
設定も重要ですが、趣旨から外れますので今回はこの辺りは割愛です。

サーバ秘密鍵はRSA形式のものが作成されますが、暗号スイートによらず鍵長が強度に直結しますので、
2048bitで作成するように指定しています。
ちなみに秘密鍵自体を暗号化することもできるのですが、今回は通信の安全性だけ担保されれば良いので
そういうオプションは付けないようにしました。

```
# cd /etc/httpd/conf/cert
# openssl genrsa 2048 > server.key
```

【2】WEBサーバ（Apache）のCSRを作成する

以前は署名アルゴリズムはSHA-1というのが一般的でしたが、認定CAをはじめ現在はより信頼度の高い
SHA-2が広く使用されています。ここでは「-sha256」として指定します。

```
# openssl req -new -sha256 -key server.key > server.csr
```

ここで、CSR用情報の入力は以下のようにします。

運営するサイトのFQDNは「hoge.allabout.co.jp」であるものとします。
FQDNさえ間違わなければ、自署CAですのでその他の情報は適当でも大丈夫です。
（実際に構築を試される方は、ご自身の環境情報やFQDNに読み換えてください。）

後ほど同様の情報をCA構築時に入力しますが、そちらも合わせるようにしてください。

```
-----
Country Name (2 letter code) [XX]:JP　　　　　　　　　←国コード(2桁)
State or Province Name (full name) []:Tokyo　　　　　　　←県名
Locality Name (eg, city) [Default City]:Shibuya　　　　　　　←都市名
Organization Name (eg, company) [Default Company Ltd]:All About　　←会社名
Organizational Unit Name (eg, section) []:System　　　　　　　　　　←部署名
Common Name (eg, your name or your server's hostname) []:hoge.allabout.co.jp　←FQDN
Email Address []:hoge@mail.allabout.co.jp　　　　　　　　　　　　　　　　　　←メールアドレス

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:　　　　←何も入力しないで[Enter]
An optional company name []:　　　←何も入力しないで[Enter]
```

【3】CAを構築する

```
# mkdir -p /etc/pki/AllaboutCA
# cd /etc/pki/AllaboutCA
# mkdir newcerts private
# touch index.txt serial
# echo 00 > serial
```

この辺は機械的にやりましょう。CAに必要なファイルやディレクトリを揃えていきます。

【4】CAの秘密鍵と証明書を作成する

WEBサーバの秘密鍵と同様に鍵長が重要です。「rsa:2048」としてこれを指定しています。
「-days 7300」はCAの存続有効期限です、20年としています。
環境によるかと思いますが、期限をあまり長くしすぎるとエラーが発生して操作が行えないようです。

```
# openssl req -new -days 7300 -x509 -newkey rsa:2048 -out cacert.pem -keyout private/cakey.pem
```

以下のように出力される事を確認し、CAのパスフレーズを入力してください。
***パスフレーズは絶対に忘れないようにしないといけません。***

当然ですがもしパスフレーズを忘れてしまうと、証明書に署名したり証明書を破棄したりすることは出来なくなり
CAは有名無実化することとなります。

```
 Generating a 2048 bit RSA private key
 ............................................................................+++
 .....+++
 writing new private key to 'private/cakey.pem'
 Enter PEM pass phrase:【CAのパスフレーズ】
 Verifying - Enter PEM pass phrase:【CAのパスフレーズ】
 -----
```

つづけて、証明書用情報の入力を行います。

CSRに入力した情報と内容を合わせるようにしてください。CSRと同様ですので入力例は割愛します。

```
【※※※割愛※※※】
```

以下のファイルが生成された事を確認してください。

```
cacert.pem　：　CAの証明書
private/cakey.pem　：　CAの秘密鍵
```

以下のコマンドを実行すると、作成した証明書の内容を確認することができます。
指定したCAの期限などの内容を確認しておきましょう。

```
$ openssl x509 -in cacert.pem -text
```

【5】自署CAによるサーバ証明書の承認（署名）

OpenSSLのデフォルト設定だとこの署名はSHA-1になってしまいますので、ここでは
「-md sha256」として明示的に指定します。
「-days 3650」は10年の指定ですが、サーバ証明書に対してCAの署名が有効である期限と
いうことになります。当然ですが、CAの存続有効期限以上の指定は行えません。

```
# cd /etc/httpd/conf/cert
# openssl ca -md sha256 -days 3650 -out server.crt -infiles server.csr
```

上記の実行にはCAのパスフレーズが必要となります。
以下のように入力した情報が表示され、内容に誤りがないことを確認してください。

```
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/AllaboutCA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 0 (0x0)
        Validity
            Not Before: Dec  9 07:37:01 2016 GMT
            Not After : Dec  7 07:37:01 2026 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = All About
            organizationalUnitName    = System
            commonName                = hoge.allabout.co.jp
            emailAddress              = hoge@mail.allabout.co.jp
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                AF:FF:AC:5A:28:72:EF:24:5C:24:2E:1F:98:3A:87:8F:F8:C9:46:E4
            X509v3 Authority Key Identifier: 
                keyid:A6:B7:6C:38:39:46:32:D5:52:D8:64:1B:69:46:42:9D:7A:29:7B:E5

Certificate is to be certified until Dec  7 07:37:01 2026 GMT (3650 days)
Sign the certificate? [y/n]:y　　　　　　←yを入力して[Enter]


1 out of 1 certificate requests certified, commit? [y/n]y　　　　　　←yを入力して[Enter]
Write out database with 1 new entries
Data Base Updated
```

これで作業はほぼ完了です！

【6】秘密鍵のパーミッション調整

ここまでの手順では、各秘密鍵のパーミッションが644などどなっており問題があります。
運用を開始する前に、修正しておきましょう。

```
# chmod 400 /etc/httpd/conf/cert/server.key
# chmod 400 -R /etc/pki/AllaboutCA/private/
```

# 動作確認

- 以下のように、VirtualHostなどで結構ですので作成した証明書をApacheに組み込んでreloadします。

```/etc/httpd/conf/httpd.confなどへの記述
<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile /etc/httpd/conf/cert/server.crt
    SSLCertificateKeyFile /etc/httpd/conf/cert/server.key

    【※※※以下割愛※※※】
</VirtualHost>
```

どうでしょう。
ページは見えましたか？

自己署名ですので、IEですと「この Web サイトのセキュリティ証明書には問題があります」などの
警告メッセージが表示されるかと思います。
（証明書が信頼された機関から発行されていないという警告です。……当然ですね；）

この場合、自署CAでクライアント証明書を生成できますのでそれをブラウザに食わせることで、
環境ごとに信頼できるサーバとして警告を抑止することができます。

```
# cd /etc/pki/AllaboutCA
# openssl x509 -in cacert.pem -outform DER -out cacert.cer
```

Windowsの場合、生成されたcacert.cerをPCへダウンロードしてダブルクリックすると、
証明書のプロパティが表示され、[証明書のインストール]が行えるはずです。
ウィザードに沿って【信頼されたルート証明機関】へ追加してやることで、警告は消えます。

# おわりに

いかがでしたでしょうか。

泥臭いやり方ですが、この方法だとどの機関にも頼らず高い認証と暗号化の要件は満たすことができます。
手軽に証明書が必要な必要な際などの参考になればと思います。