自己CAを構築し、自己CAで署名付けした中間証明書とサーバ証明書を作成する

# 目的
* いろんな状況のサーバ証明書(revoke、期限切れ、署名連鎖不正 etc..)を検証するために証明書作成環境が必要
* ルート証明書は自己署名だが、中間証明書、サーバ証明書として「正常な連鎖状態にある」証明書を使いたい。
* 汎用的でどこでも使えるopensslのみで構築したい。
* 嵌る原因となるCA.sh、CA.plを一切使わない
* 嵌る原因となるopenssl.cnfも既定の暗黙参照のみ
* 久しぶりに構築しようとすると必ず嵌っているので整理

# 概要
* 構築しようとするCA環境は以下の通り
    * ルートCAは自己署名
    * 中間証明書はルートCAで署名
    * サーバ証明書は中間CAで署名　結果：自己署名ではないサーバ証明書の出来上がり
    
![cert_chain.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282300/da805c90-06bb-0c5c-cfbe-00cac0eb96ec.png)

* 構築・作成手順
    1. 自己署名によるルートCAを構築
    2. ↑のルートCAで署名付けした中間証明書と中間CAを構築
    3. ↑の中間CAで署名付けしたサーバ証明書を作成
* 証明書の検証
* 作成された証明書の利用、特別な証明書(有効期限切れ、SAN、AIA拡張、CRL/OCSP連携)については別途

# 環境
* OS :Linux推奨

```
$ uname -a
Linux vUbuntuLTS 5.3.0-28-generic #30~18.04.1-Ubuntu SMP Fri Jan 17 06:14:09 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
```

* openssl（2020/02/18時点最新)

    ```
    $ openssl
    OpenSSL> version
    OpenSSL 1.1.1  11 Sep 2018
    ```

# 構築手順
## 共通コマンド環境の作成
* 任意の作業ディレクトリ(self_ca)を作成し、RCA, ICA, serverの３ディレクトリを作成
    * RCA: ルートCA作業環境
    * ICA: 中間CA作業環境
    * server: サーバ証明書作業環境

    ```
    $ mkdir ~/self_ca
    $ mkdir ~/self_ca/RCA
    $ mkdir ~/self_ca/ICA
    $ mkdir ~/self_ca/server
    ```

## 自己ルートCAを構築
1. ルートCAディレクトリ ~/self_ca/RCAに移動
2. CAディレクトリとdatabaseを作成

    ```
    $ mkdir -p demoCA
    $ mkdir -p demoCA/certs
    $ mkdir -p demoCA/crl
    $ mkdir -p demoCA/newcerts
    $ mkdir -p demoCA/private
    $ touch demoCA/index.txt
    ```
3. 証明書要求(careq.pem)と秘密キー(demoCA/private/cakey.pem)を作成
    * x.509/Organization Nameには、作成する中間証明書、サーバ証明書と同じ組織名を入力する (ex. my)
    * x.509/Common Nameには、ルートCA（=本サーバ）にアクセス可能なFQDNを入力する。(ex. rca.vubuntults.mydomain)

    ```
    $ openssl req -new -keyout demoCA/private/cakey.pem -out demoCA/careq.pem
    Generating a RSA private key
    ...............................................+++++
    ..........................+++++
    writing new private key to 'demoCA/private/cakey.pem'
    Enter PEM pass phrase:
    Verifying - Enter PEM pass phrase:
    -----
    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]:JP
    State or Province Name (full name) [Some-State]:Tokyo
    Locality Name (eg, city) []:Shinjuku-ku
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:my
    Organizational Unit Name (eg, section) []:room
    Common Name (e.g. server FQDN or YOUR name) []:rca.vubuntults.mydomain                
    Email Address []:

    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:
    An optional company name []:
    ```
4. 証明書要求に自己署名し、ルート証明書(demoCA/cacert.pem)を作成する。
    * 有効期限：-days 1095
    * 「Can't open ./demoCA/index.txt.attr for reading, No such file or directory」は無視しても良い
    
    ```
    $ openssl ca -create_serial -out demoCA/cacert.pem -days 1095 -batch -keyfile demoCA/private/cakey.pem -selfsign -extensions v3_ca -infiles demoCA/careq.pem 
    Using configuration from /usr/lib/ssl/openssl.cnf
    Enter pass phrase for demoCA/private/cakey.pem:
    Can't open ./demoCA/index.txt.attr for reading, No such file or directory
    140363278688704:error:02001002:system library:fopen:No such file or directory:../crypto/bio/bss_file.c:72:fopen('./demoCA/index.txt.attr','r')
    140363278688704:error:2006D080:BIO routines:BIO_new_file:no such file:../crypto/bio/bss_file.c:79:
    Check that the request matches the signature
    Signature ok
    Certificate Details:
            Serial Number:
                19:e7:a9:be:65:1e:a5:93:6b:bf:40:81:e1:0b:dd:d2:0b:46:e0:46
            Validity
                Not Before: Feb 18 06:41:23 2020 GMT
                Not After : Feb 17 06:41:23 2023 GMT
            Subject:
                countryName               = JP
                stateOrProvinceName       = Tokyo
                organizationName          = my
                organizationalUnitName    = room
                commonName                = rca.vubuntults.mydomain
            X509v3 extensions:
                X509v3 Subject Key Identifier: 
                    EE:07:B4:FE:82:93:76:BB:F6:EE:AF:63:DC:20:D7:E9:EE:BC:79:0C
                X509v3 Authority Key Identifier: 
                    keyid:EE:07:B4:FE:82:93:76:BB:F6:EE:AF:63:DC:20:D7:E9:EE:BC:79:0C

                X509v3 Basic Constraints: critical
                    CA:TRUE
    Certificate is to be certified until Feb 17 06:41:23 2023 GMT (1095 days)

    Write out database with 1 new entries
    Data Base Updated
    ```

## 中間CAを構築
1. 証明書要求(newreq.pem)と秘密キー(newkey.pem)を作成する。
    1. 中間CAディレクトリ ~/self_ca/ICAに移動
    2. 証明書要求コマンド(openssl req)を実行
        * x.509/Organization Nameには、ルート証明書、サーバ証明書と同じ組織名を入力する (ex. my)
        * x.509/Common Nameには、中間CA（=本サーバ）にアクセス可能なFQDNを入力する。(ex. ica.vubuntults.mydomain)

        ```
        $ openssl req -new -keyout newkey.pem -out newreq.pem -days 1095
        Ignoring -days; not generating a certificate
        Generating a RSA private key
        ..........+++++
        ...........................................................+++++
        writing new private key to 'newkey.pem'
        Enter PEM pass phrase:
        Verifying - Enter PEM pass phrase:
        -----
        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]:JP
        State or Province Name (full name) [Some-State]:Tokyo 
        Locality Name (eg, city) []:Shinjuku-ku
        Organization Name (eg, company) [Internet Widgits Pty Ltd]:my
        Organizational Unit Name (eg, section) []:room
        Common Name (e.g. server FQDN or YOUR name) []:ica.vubuntults.mydomain
        Email Address []:

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

2. 証明書要求に対してルートCAで署名をし、中間証明書(../ICA/newcert.pem)を作成する
    1. ルートCAディレクトリ ~/self_ca/RCAに移動
    2. 署名コマンド(openssl ca)を実行

        ```
        $ openssl ca -policy policy_anything -out ../ICA/newcert.pem -extensions v3_ca -infiles ../ICA/newreq.pem 
        Using configuration from /usr/lib/ssl/openssl.cnf
        Enter pass phrase for ./demoCA/private/cakey.pem:
        Check that the request matches the signature
        Signature ok
        Certificate Details:
                Serial Number:
                    19:e7:a9:be:65:1e:a5:93:6b:bf:40:81:e1:0b:dd:d2:0b:46:e0:48
                Validity
                    Not Before: Feb 18 09:09:40 2020 GMT
                    Not After : Feb 17 09:09:40 2021 GMT
                Subject:
                    countryName               = JP
                    stateOrProvinceName       = Tokyo
                    localityName              = Shinjuku-ku
                    organizationName          = my
                    organizationalUnitName    = room
                    commonName                = ica.vubuntults.mydomain
                X509v3 extensions:
                    X509v3 Subject Key Identifier: 
                        D1:8B:4F:7B:FE:14:FA:AB:92:80:3C:D4:63:E5:F2:A9:67:CA:EB:31
                    X509v3 Authority Key Identifier: 
                        keyid:EE:07:B4:FE:82:93:76:BB:F6:EE:AF:63:DC:20:D7:E9:EE:BC:79:0C

                    X509v3 Basic Constraints: critical
                        CA:TRUE
        Certificate is to be certified until Feb 17 09:09:40 2021 GMT (365 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
        ```

3. 中間CAを構築する
    1. 中間CAディレクトリ ~/self_ca/ICAに移動
    2. CAディレクトリとdatabaseを作成

        ```
        $ mkdir -p demoCA
        $ mkdir -p demoCA/certs
        $ mkdir -p demoCA/crl
        $ mkdir -p demoCA/newcerts
        $ mkdir -p demoCA/private
        $ touch demoCA/index.txt
        ```

    3. 公開キー、秘密キーを所定のディレクトリに移動

        ```
        $ mv newkey.pem demoCA/private/cakey.pem
        $ mv cacert.pem demoCA/cacert.pem
        ```

    4. 公開キーのシリアルを作成し、所定のファイル(demoCA/serial)に保存する

        ```
        $ openssl x509 -in demoCA/cacert.pem -noout -next_serial -out demoCA/serial
        ```

## 証明書チェインを作成
* ここまででルート証明書と中間証明書が出来ており、その証明書チェインを作成しとく
* 証明書チェインはTLSサーバ等で必要になるケースがあり、またサーバ証明書を作成した後の検証に使われる
* 文字通りルート証明書、中間証明書を連結(cat)したもの。

1. 自己CAディレクトリ(~/self_ca)に移動
2. ルート証明書、中間証明書から不要な情報（CERTIFICATEブロック以外)を削除する。

    ```
    $ openssl x509 -outform pem -in RCA/demoCA/cacert.pem -out rca.pem
    $ openssl x509 -outform pem -in ICA/demoCA/cacert.pem -out ica.pem
    ```

3. [option] ルート証明書と中間証明書のチェインを確認

    ```
    $ openssl verify -CAfile rca.pem ica.pem 
    ica.pem: OK
    ```

4. 連結して証明書チェイン(cert_chain.pem)を作成

    ```
    $ cat rca.pem ica.pem >> cert_chain.pem
    ```

## サーバ証明書を作成
1. 秘密キーを作成
    1. 2048bitのRSAキーを作成し、キーをAES256で暗号化する。

        ```
        $ openssl genrsa -aes256 2048 > server_key.pem
        Generating RSA private key, 2048 bit long modulus (2 primes)
        ..+++++
        .................................................................................................+++++
        e is 65537 (0x010001)
        Enter pass phrase:
        Verifying - Enter pass phrase:
        ```

    2. パスフレーズをクリアするためopenssl rsaで上書きする

        ```
        $ openssl rsa -in server_key.pem -out server_key.pem 
        Enter pass phrase for server_key.pem:
        writing RSA key
        ```

2. 証明書要求(server_req.pem)を作成
    * x.509/Organization Nameには、ルート証明書、中間証明書と同じ組織名を入力する (ex. my)

    ```
    $ openssl req -new -key server_key.pem -out server_req.pem
    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]:JP
    State or Province Name (full name) [Some-State]:Tokyo
    Locality Name (eg, city) []:Shinjuku-ku
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:my
    Organizational Unit Name (eg, section) []:room
    Common Name (e.g. server FQDN or YOUR name) []:www.vubuntults.mydomain
    Email Address []:

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

3. 中間CAで証明書要求に署名し、サーバ証明書(www_vubuntults_mydomain.crt)を発行する
    * 中間CA作業ディレクトリ(~/self_ca/ICA)で実行しないとエラーとなる。
    * サーバ証明書は~/self_ca/server/www_vubuntults_mydomain.crtに生成

    ```
    $ openssl ca -batch -in ../server/server_req.pem -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem -out ../server/www_vubuntults_mydomain.crt -days 1095
    Using configuration from /usr/lib/ssl/openssl.cnf
    Enter pass phrase for demoCA/private/cakey.pem:
    Can't open ./demoCA/index.txt.attr for reading, No such file or directory
    140058091311552:error:02001002:system library:fopen:No such file or directory:../crypto/bio/bss_file.c:72:fopen('./demoCA/index.txt.attr','r')
    140058091311552:error:2006D080:BIO routines:BIO_new_file:no such file:../crypto/bio/bss_file.c:79:
    Check that the request matches the signature
    Signature ok
    Certificate Details:
            Serial Number:
                19:e7:a9:be:65:1e:a5:93:6b:bf:40:81:e1:0b:dd:d2:0b:46:e0:49
            Validity
                Not Before: Feb 18 09:28:33 2020 GMT
                Not After : Feb 17 09:28:33 2023 GMT
            Subject:
                countryName               = JP
                stateOrProvinceName       = Tokyo
                organizationName          = my
                organizationalUnitName    = room
                commonName                = www.vubuntults.mydomain
            X509v3 extensions:
                X509v3 Basic Constraints: 
                    CA:FALSE
                Netscape Comment: 
                    OpenSSL Generated Certificate
                X509v3 Subject Key Identifier: 
                    27:D5:91:6B:5E:50:43:B5:1E:3A:74:F3:D0:F0:84:52:9F:E2:73:AD
                X509v3 Authority Key Identifier: 
                    keyid:D1:8B:4F:7B:FE:14:FA:AB:92:80:3C:D4:63:E5:F2:A9:67:CA:EB:31

    Certificate is to be certified until Feb 17 09:28:33 2023 GMT (1095 days)

    Write out database with 1 new entries
    Data Base Updated
    ```

# サーバ証明書を検証
* [証明書チェインを作成](#証明書チェインを作成)で作成した証明書チェインを使って、サーバ証明書を検証する。
* コマンド出力でOKとなればOK

    ```
    $ cd ~/self_ca
    $ openssl verify -CAfile cert_chain.pem server/www_vubuntults_mydomain.crt 
    server/www_vubuntults_mydomain.crt: OK
    ```