Chromeで使えるオレオレ証明書を作成する方法

opensslを使用してChromeで使える自己署名証明書（オレオレ証明書）を作成する方法を解説します。
また、簡単に生成できるツールも用意しました。よければご活用下さい（dockerが必要です）


# 発端

ローカル環境でhttpsを実現するために、オレオレ(自己署名)SSL証明書を作成したのですがchromeでは`NET::ERR_CERT_COMMON_NAME_INVALID`となってしまいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71575/82d20da3-c1be-ea3f-96e1-7781b4006098.png)


原因は、Chromeの仕様変更により、ドメイン名のチェックを**Common Name**(通称CN)ではなく**Subject Alternative Names**(通称SAN)を参考にする様になったためだそうです。

ここではSubject Alternative Namesを含んだ自己署名サーバ証明書の作り方を解説します。

# (追記) :tada: 自己署名証明書を作成するツールを作りました。

[self-sign-cert](https://github.com/t-kuni/self-sign-cert)
dockerがインストールされている必要がありますが、1コマンドで自己署名証明書が作成できます。
詳しい使い方は該当リポジトリのReadmeを参照してください。

# 手順

## SANを含んだサーバ証明書を生成する

適当なディレクトリに移動する

```
mkdir /work
cd /work
```

SANの設定が書かれたテキストファイルを用意します。
ドメイン名とIPアドレスは自身の環境に合わせて変更してください。
ファイル名は何でもよいですが、ここでは`subjectnames.txt`としました。

```txt:subjectnames.txt
subjectAltName = DNS:your-domain.com, IP:127.0.0.1
```

秘密鍵(server.key)、署名要求(server.csr)、サーバ証明書(server.crt)を生成します。
サーバ証明書を作成する際に`-extfile`オプションでSANを埋め込むのがキモです。

```sh
# 秘密鍵(server.key)を生成する
openssl genrsa 2048 > server.key
# 秘密鍵から署名要求(server.csr)を生成する
# your-domain.comは自身の環境に合わせて変更してください。
openssl req -new -key server.key -subj "/C=JP/ST=Some-State/O=Some-Org/CN=your-domain.com" > server.csr
# 署名要求を秘密鍵で署名してサーバ証明書を作成する
openssl x509 -days 3650 -req -extfile subjectnames.txt -signkey server.key < server.csr > server.crt
```


生成したサーバ証明書の内容を確認する

```sh
openssl x509 -in server.crt -text -noout
```

以下の様にCommon NameとSubject Alternative Namesが出力されていればOK

![2019-08-16_15h36_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71575/94e9dc60-6fad-d5ce-a491-1d20e2c797c0.png)

## Nginxの設定

NginxでSSLを使うための設定を追加します。
ここでは`/etc/nginx/conf.d/default.conf`を修正しています。

```nginx
server {
    listen 80;
    listen 443 ssl;
    ssl_certificate     /work/server.crt; # サーバ証明書のパスを指定
    ssl_certificate_key /work/server.key; # 秘密鍵のパスを指定

    server_name your-domain.com; # CN, SANと合わせる
    
    ...
```

nginxの設定のテスト＆再読み込み
※環境によりコマンドが異なる場合があります。

```
nginx -t
nginx reload
```

## Chromeに信頼できるサーバ証明書として登録する

サーバ証明書(server.crt)をChromeに登録します。
手順は以下のページを参考にしてください。

[Google Chromeへ証明書ファイルをインポートするには \| サポート・お申し込みガイド | GMOグローバルサイン【公式】](https://jp.globalsign.com/support/faq/558.html)

## 動作確認

chromeから`https://your-domain.com`にアクセスしてNET::ERR_CERT_COMMON_NAME_INVALIDが発生しなければOKです。

# あとがき

自己署名証明書を作るのにわりと手間取りまして、よくよく考えるとopenssl全然分からんし署名要求って何？サーバ証明書って結局なんなの？？登場人物多すぎ！！（泣）というレベルで全然わかってませんでした。
そんな訳で、整理も兼ねて図を書きましたところ、それなりに分かりやすい図が出来た（気がする）のでシェアさせてください。
これを見れば「自己署名証明書の作成って結局何やってんの？」というのが分かる様になってopensslの一連のコマンドが何をやっているのかもイメージできる様になるはずです。

では早速ですが、まず、一般的な「**認証局に署名して貰ってサーバ証明書を作成する**」ケースです。

①自身のサーバ上で**秘密鍵**を生成する
②自身のサーバ上で**秘密鍵**から**署名要求**を生成する
③**署名要求**を認証局に渡して署名してもらう。これが**サーバ証明書**となる。
④クライアントにはあらかじめ認証局のサーバ証明書を「信頼できる証明書」として登録しておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71575/d49a4b9f-ede1-83c6-5b77-155acfa4dd27.png)



続いて、今回の「**自己署名でサーバ証明書を作成**」するケースです。

①、②は同じです。
こちらのケースでは自己署名なので認証局は登場しません。
ではどうするかというと③で自身の秘密鍵を使って署名します。
そうして出来たサーバ証明書を「信頼するサーバ証明書」としてクライアントに登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71575/4e6cafb0-0347-db63-f0b2-be103db8331a.png)

今回使用したopensslコマンドとの対応ですが

`openssl genrsa`が**秘密鍵**を生成するコマンドで**図中①**に対応
`openssl req`が秘密鍵から**署名要求**を生成するコマンドで**図中②**に対応
`openssl x509`が署名要求を署名して**サーバ証明書**を作成するコマンドで**図中③**に対応

となっております。

以上、
どうでしょうか、なんとなくサーバ証明書を生成する一連の流れがイメージできたでしょうか？
理解の一助となっていれば幸いです。