opensslでSANを含んだオレオレ証明書を作ってみる

# はじめに
chrome58 から、SSL証明書の CN (Common Name) から SAN (509v3 extensions: X509v3 Subject Alternative Name) を評価するようになったため、従来のオレオレ証明書の作り方が通用しなくなった。

openssl 1.1.1以上であれば、`-addext`オプションでSANをコマンドラインで指定できるようになったらしいので、なるべく現環境に影響を及ぼさず簡単にオレオレ証明書を作成できないか検証してみた。

# 前提条件
- openssl 1.1.1 以上(-addextオプションを使う)
- ubuntu 18.04.2 (筆者の環境)

# openssl 1.1.1 をビルドする

ubuntu 18.04では、openssl 1.1.0g で`-addext`オプションが使えないので、致し方なくビルドする。
なぜかRUNPATHの指定がないので、ライブラリパスの問題を回避するために(面倒だから)、`-static`を指定する。

```terminal:opensslのビルド
$ wget https://www.openssl.org/source/openssl-1.1.1b.tar.gz
$ tar zxvf openssl-1.1.1b.tar.gz
$ cd ./openssl-1.1.1b/
$ ./config --prefix=/opt/openssl-1.1.1b -static
$ make
$ make test
$ sudo make install
```

# CA環境をつくる

適当なところにディレクトリを作成し、`CA.pl`と`openssl.cnf`をコピーする。 

```terminal
$ mkdir rootca
$ cd rootca
$ cp -rp /opt/openssl-1.1.1b/ssl/misc/CA.pl .
$ cp -rp /opt/openssl-1.1.1b/ssl/openssl.cnf .
```

`CA.pl` を書き換える。

```diff:diff&nbsp;-up&nbsp;/opt/openssl-1.1.1b/ssl/misc/CA.pl&nbsp;./CA.pl
--- /opt/openssl-1.1.1b/ssl/misc/CA.pl  2019-03-23 04:47:58.527507782 +0900
+++ CA.pl       2019-03-23 07:50:32.303802280 +0900
@@ -15,6 +15,9 @@
 use strict;
 use warnings;

+$ENV{'OPENSSL'} = "/opt/openssl-1.1.1b/bin/openssl";
+$ENV{"OPENSSL_CONFIG"} = "-config ./openssl.cnf";
+
 my $openssl = "openssl";
 if(defined $ENV{'OPENSSL'}) {
     $openssl = $ENV{'OPENSSL'};
@@ -25,8 +28,8 @@ if(defined $ENV{'OPENSSL'}) {
 my $verbose = 1;

 my $OPENSSL_CONFIG = $ENV{"OPENSSL_CONFIG"} || "";
-my $DAYS = "-days 365";
-my $CADAYS = "-days 1095";     # 3 years
+my $DAYS = "-days 3650";
+my $CADAYS = "-days 3650";     # 3 years
 my $REQ = "$openssl req $OPENSSL_CONFIG";
 my $CA = "$openssl ca $OPENSSL_CONFIG";
 my $VERIFY = "$openssl verify";
```

`openssl.cnf`を書き換える。

* `copy_extensions`は、CA署名時にSANを渡すために必要。(必須)
* `unique_subject`は、revokeしなくても同じ証明書が何度も発行できるようにするためのもの。
あとはお好みで。

```diff:diff&nbsp;-up&nbsp;/opt/openssl-1.1.1b/ssl/openssl.cnf&nbsp;./openssl.cnf
--- /opt/openssl-1.1.1b/ssl/openssl.cnf 2019-03-23 04:47:58.551507775 +0900
+++ openssl.cnf 2019-03-23 07:50:20.979734732 +0900
@@ -46,7 +46,7 @@ dir           = ./demoCA              # Where everything is k
 certs          = $dir/certs            # Where the issued certs are kept
 crl_dir                = $dir/crl              # Where the issued crl are kept
 database       = $dir/index.txt        # database index file.
-#unique_subject        = no                    # Set to 'no' to allow creation of
+unique_subject = no                    # Set to 'no' to allow creation of
                                        # several certs with same subject.
 new_certs_dir  = $dir/newcerts         # default place for new certs.

@@ -65,16 +65,16 @@ name_opt    = ca_default            # Subject Name o
 cert_opt       = ca_default            # Certificate field options

 # Extension copying option: use with caution.
-# copy_extensions = copy
+copy_extensions = copy

 # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
 # so this is commented out by default to leave a V1 CRL.
 # crlnumber must also be commented out to leave a V1 CRL.
 # crl_extensions       = crl_ext

-default_days   = 365                   # how long to certify for
+default_days   = 3650                  # how long to certify for
 default_crl_days= 30                   # how long before next CRL
-default_md     = default               # use public key default MD
+default_md     = sha256                # use public key default MD
 preserve       = no                    # keep passed DN ordering

 # A few difference way of specifying how similar the request should look
@@ -105,7 +105,7 @@ emailAddress                = optional

 ####################################################################
 [ req ]
-default_bits           = 2048
+default_bits           = 4096
 default_keyfile        = privkey.pem
 distinguished_name     = req_distinguished_name
 attributes             = req_attributes
```

# CA証明書を作る

パスフレーズをなくすために`-extra-req "-nodes"`を追加。

```terminal:CA証明書をつくる
$ ./CA.pl -newca -extra-req "-nodes"
```

# サーバ証明書のCSRを作る

パスフレーズをなくすため、`-newreq`ではなく`-newreq-nodes`で作成する。
SANを`-addext`の後に記載していく。

`DNS:`で続く箇所は、DNSホスト名が指定でき、サブドメインに当たる部分を`*`で置き換えることができる。
`IP:`で続く箇所は、IPアドレスを個別に指定できる。残念ながらネットワークアドレスは指定できないようだ。

```terminal:サーバ証明書のCSRを作る
$ ./CA.pl -newreq-nodes -extra-req "-addext 'subjectAltName = DNS:sample.com,DNS:*.sample.com,IP:192.168.1.1'"
```

# サーバ証明書を署名する

通常の手順と変わらないが、証明書にSANがふくまていることが確認できる。

```terminal:サーバ証明書を署名する
$ ./CA.pl -sign
====
/opt/openssl-1.1.1b/bin/openssl ca -config ./openssl.cnf -policy policy_anything -out newcert.pem  -infiles newreq.pem
Using configuration from ./openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number:
...(snap)...
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
...(snap)...

            X509v3 Subject Alternative Name:
                DNS:sample.com, DNS:*.sample.com, IP Address:192.168.1.1
Certificate is to be certified until Mar 19 23:10:18 2029 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
==> 0
====
Signed certificate is in newcert.pem
```

# 各ファイル

| ファイル名| 用途 |
|:-:|:-:|
|`./demoCA/cacert.pem` | CA証明書 |
|`./demoCA/private/cakey.pem`   | CA証明書の秘密鍵 |
|`newcert.pem` | サーバ証明書 |
|`newkey.pem` | サーバ証明書の秘密鍵 |

# その他便利なコマンド

生成したサーバ証明書を使ってブラウザで動作確認する。
以下は、`https://<CA作成したサーバ>:8443/`で確認することができる。

```terminal:ブラウザによる動作確認
$ openssl s_server -accept 8443 -cert newcert.pem -key newkey.pem -WWW
```

生成したサーバ証明書をPKCS#12形式に変換する。

```terminal:PKCS#12形式への変換
$ openssl pkcs12 -export -in newcert.pem -inkey newkey.pem -out server.p12
```

よきオレオレライフを。