Cloudflare の PKI ツール cfssl で証明書を作成する

# `cfssl` とは

 `openssl` より簡単に証明書や認証局を作成できるコマンドを Cloudflare が公開しており、おすすめなので使い方をメモします。

> [Introducing CFSSL - CloudFlare's PKI toolkit](https://blog.cloudflare.com/introducing-cfssl/)
>
> [GitHub - cloudflare/cfssl: CFSSL: Cloudflare's PKI and TLS toolkit](https://github.com/cloudflare/cfssl)

# インストール

macOS の場合は、Homebrew でインストールできます。

```shell
brew install cfssl
```

# 自己署名サーバ証明書の作成（認証局 CA 署名なしで発行されるもの）

CSR に必要な情報を準備します。

```shell
cat << EOS > selfsign_server.json
{
    "CN": "khayama",
    "hosts": ["example.com","*.example.com"],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "O": "khayama",
            "OU": "Sales",
            "L": "Shibuya",
            "ST": "Tokyo",
            "C": "JP"
        }
    ]
}
EOS
```

上記に基づき、自己署名サーバ証明書（認証局 CA 署名なしで発行されるもの）を発行します。

```shell
% cfssl selfsign "" selfsign_server.json | cfssljson -bare selfsign_server
2021/10/06 01:19:35 [INFO] generate received request
2021/10/06 01:19:35 [INFO] received CSR
2021/10/06 01:19:35 [INFO] generating key: ecdsa-256
2021/10/06 01:19:35 [INFO] encoded CSR
*** WARNING ***

Self-signed certificates are dangerous. Use this self-signed
certificate at your own risk.

It is strongly recommended that these certificates NOT be used
in production.

*** WARNING ***
```

以下のコマンドで中身を確認できます。

```shell
cfssl certinfo -csr selfsign_server.csr
cfssl certinfo -cert selfsign_server.pem   
```

また、サーバーに登録・公開後は、以下のコマンドでサーバー証明書を確認できます。

```shell
% cfssl certinfo -domain www.cloudflare.com
{
  "subject": {
    "common_name": "www.cloudflare.com",
    "country": "US",
    "organization": "Cloudflare, Inc.",
    "locality": "San Francisco",
    "province": "California",
    "names": [
      "US",
      "California",
      "San Francisco",
      "Cloudflare, Inc.",
      "www.cloudflare.com"
    ]
  },
  "issuer": {
    "common_name": "Cloudflare Inc ECC CA-3",
    "country": "US",
    "organization": "Cloudflare, Inc.",
    "names": [
      "US",
      "Cloudflare, Inc.",
      "Cloudflare Inc ECC CA-3"
    ]
  },
  "serial_number": "2420254955216867616085561251230852636",
  "sans": [
    "*.www.cloudflare.com",
    "www.cloudflare.com"
  ],
  "not_before": "2021-09-18T00:00:00Z",
  "not_after": "2022-09-17T23:59:59Z",
  "sigalg": "ECDSAWithSHA256",
  "authority_key_id": "A5:CE:37:EA:EB:B0:75:0E:94:67:88:B4:45:FA:D9:24:10:87:96:1F",
  "subject_key_id": "80:4D:4A:42:32:AE:09:8F:51:07:4B:A8:D4:D4:76:A8:BB:41:B0:31",
  "pem": "-----BEGIN CERTIFICATE-----\nMIIFKDCCBM+gAwIBAgIQAdIfyDzGygOhDxOVwqcmHDAKBggqhkjOPQQDAjBKMQsw\nCQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX\nQ2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjEwOTE4MDAwMDAwWhcNMjIwOTE3\nMjM1OTU5WjByMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG\nA1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEb\nMBkGA1UEAxMSd3d3LmNsb3VkZmxhcmUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D\nAQcDQgAE4oAICmiZSBhltLFYAuI14RPj9CXN1D0bCFvGl8fJ74BDOoKKE7MPn6uK\nY88Jwmc4a5nGWnykGd6eL0E1NSOFHqOCA20wggNpMB8GA1UdIwQYMBaAFKXON+rr\nsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBSATUpCMq4Jj1EHS6jU1Haou0GwMTAz\nBgNVHREELDAqghQqLnd3dy5jbG91ZGZsYXJlLmNvbYISd3d3LmNsb3VkZmxhcmUu\nY29tMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH\nAwIwewYDVR0fBHQwcjA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Ns\nb3VkZmxhcmVJbmNFQ0NDQS0zLmNybDA3oDWgM4YxaHR0cDovL2NybDQuZGlnaWNl\ncnQuY29tL0Nsb3VkZmxhcmVJbmNFQ0NDQS0zLmNybDA+BgNVHSAENzA1MDMGBmeB\nDAECAjApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMw\ndgYIKwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy\ndC5jb20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9D\nbG91ZGZsYXJlSW5jRUNDQ0EtMy5jcnQwDAYDVR0TAQH/BAIwADCCAX4GCisGAQQB\n1nkCBAIEggFuBIIBagFoAHYAKXm+8J45OSHwVnOfY6V35b5XfZxgCvj5TV0mXCVd\nx4QAAAF79j4m0AAABAMARzBFAiEAvKDJ5n/u7CgYVtco9JCPRbZJq/z82l9HFlZA\nu+xCmkcCIDI25qHABDHpxIf6Y6Vyzfbx6YqtXbH9qaDcGWrU7Gq+AHYAUaOw9f0B\neZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeUAAAF79j4nPgAABAMARzBFAiBTDKQr\nx0RKDWbCh9TzLUERGevPkPYMna4J6Tx9ar1dIQIhALQ0UM+WOHzDM59V454jiTKE\nyEiML9y/X9BMkI/+YdlrAHYAQcjKsd8iRkoQxqE6CUKHXk4xixsD6+tLx2jwkGKW\nBvYAAAF79j4nEgAABAMARzBFAiEA7SA5gPLY4H484JBw4CKagi6S/c3aZJV/tBWj\nyuNBS3cCIG8tZ6o7tbPO6xnlH4uqOFv8SmpLLe1UQNDKPxzsw6BSMAoGCCqGSM49\nBAMCA0cAMEQCIGhqV3zVr73qOPWtWrAM9Rws7JtjI63UlTJHofnjxiabAiAiMGF7\nIF0SAGLGP8LXoFc913Pe30/OqeK0MI+LgAhcPw==\n-----END CERTIFICATE-----\n"
}
```

# 自己署名クライアント証明書の作成（認証局 CA 署名なしで発行されるもの）

CSR に必要な情報を準備します。

```shell
cat << EOS > selfsign_client.json
{
    "CN": "khayama",
    "hosts": [""],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "O": "khayama",
            "OU": "Sales",
            "L": "Shibuya",
            "ST": "Tokyo",
            "C": "JP"
        }
    ]
}
EOS
```

上記に基づき、自己署名クライアント証明書（認証局 CA 署名なしで発行されるもの）を発行します。

```shell
% cfssl selfsign "" selfsign_client.json | cfssljson -bare selfsign_client
2021/10/06 01:34:10 [INFO] generate received request
2021/10/06 01:34:10 [INFO] received CSR
2021/10/06 01:34:10 [INFO] generating key: ecdsa-256
2021/10/06 01:34:10 [INFO] encoded CSR
*** WARNING ***

Self-signed certificates are dangerous. Use this self-signed
certificate at your own risk.

It is strongly recommended that these certificates NOT be used
in production.

*** WARNING ***
```

以下のコマンドで中身を確認できます。

```shell
cfssl certinfo -csr selfsign_client.csr
cfssl certinfo -cert selfsign_client.pem   
```

# 認証局 (CA) 署名を行う場合

## 認証局 (CA) の作成

> [Generating self-signed root CA certificate and private key](https://github.com/cloudflare/cfssl#generating-self-signed-root-ca-certificate-and-private-key)

デフォルトの雛形を活用する、

```shell
cfssl print-defaults csr > ca-csr.json
vi ca-csr.json
cfssl print-defaults config > ca-config.json
vi ca-config.json
```

もしくはこのような形で、必要な情報を作成します。

```shell
cat << EOS > ca-csr.json
{
    "CN": "khayama",
    "hosts": [""],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "O": "khayama",
            "OU": "Sales",
            "L": "Shibuya",
            "ST": "Tokyo",
            "C": "JP"
        }
    ],
    "ca": {
        "expiry": "876000h"
    }
}
EOS
```

```shell
cat << EOS > ca-config.json
{
    "signing": {
        "default": {
            "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                    ]
        }
    }
}
EOS
```

以下のコマンドで認証局（CA）に必要なファイルが作成できます。

```shell
% cfssl genkey -initca ca-csr.json | cfssljson -bare ca
2021/10/05 16:10:21 [INFO] generate received request
2021/10/05 16:10:21 [INFO] received CSR
2021/10/05 16:10:21 [INFO] generating key: ecdsa-256
2021/10/05 16:10:21 [INFO] encoded CSR
2021/10/05 16:10:21 [INFO] signed certificate with serial number 94923687166579298930001532856714475579276107133
% tree             
.
├── ca-config.json
├── ca-csr.json
├── ca-key.pem (認証局の秘密鍵)
├── ca.csr 
└── ca.pem (自己署名証明書：認証局の秘密鍵で対応する公開鍵に署名した証明書)

0 directories, 5 files
```

以下のコマンドで中身を確認できます。

```shell
cfssl certinfo -csr ca.csr
cfssl certinfo -cert ca.pem   
```

## サーバ証明書の作成

CSR に必要な情報を準備します。

```shell
cat << EOS > server.json
{
    "CN": "khayama",
    "hosts": ["example.com","*.example.com"],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "O": "khayama",
            "OU": "Sales",
            "L": "Shibuya",
            "ST": "Tokyo",
            "C": "JP"
        }
    ]
}
EOS
```

CSR を作成します。

```shell
% cfssl genkey server.json | cfssljson -bare server
2021/10/05 17:22:08 [INFO] generate received request
2021/10/05 17:22:08 [INFO] received CSR
2021/10/05 17:22:08 [INFO] generating key: ecdsa-256
2021/10/05 17:22:08 [INFO] encoded CSR
```

CSR に基づき、先ほど作成した認証局（CA）署名付きの証明書を発行します。

```shell
% cfssl sign -ca ca.pem -ca-key ca-key.pem -config ca-config.json server.csr | cfssljson -bare server
2021/10/05 17:29:28 [INFO] signed certificate with serial number 616173582156044846833395198357644919983259435829
```

以下のコマンドで中身を確認できます。

```shell
cfssl certinfo -csr server.csr
cfssl certinfo -cert server.pem   
```

## クライアント証明書の作成

CSR に必要な情報を準備します。

```shell
cat << EOS > client.json
{
    "CN": "khayama",
    "hosts": [""],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "O": "khayama",
            "OU": "Sales",
            "L": "Shibuya",
            "ST": "Tokyo",
            "C": "JP"
        }
    ]
}
EOS
```

CSR を作成します。

```shell
% cfssl genkey client.json | cfssljson -bare client
2021/10/05 17:31:03 [INFO] generate received request
2021/10/05 17:31:03 [INFO] received CSR
2021/10/05 17:31:03 [INFO] generating key: ecdsa-256
2021/10/05 17:31:03 [INFO] encoded CSR
```

CSR に基づき、先ほど作成した認証局（CA）署名付きの証明書を発行します。

```shell
% cfssl sign -ca ca.pem -ca-key ca-key.pem -config ca-config.json client.csr | cfssljson -bare client
2021/10/05 17:31:27 [INFO] signed certificate with serial number 603013714077290885383974725881063800048964067072
```

以下のコマンドで中身を確認できます。

```shell
cfssl certinfo -csr client.csr
cfssl certinfo -cert client.pem   
```

# 参考

- [cfsslを用いたオレオレ証明書作成 - Qiita](https://qiita.com/iaoiui/items/fc2ea829498402d4a8e3)
- [SSL証明書発行ではcfsslを使おう（opensslめんどくさい） - Qiita](https://qiita.com/AkiQ/items/5489033346a12c55ff43)
- [cfssl-ca-tips.md](https://gist.github.com/tsaarni/557511180c49e9a3c281f5b67f25b093)
- [docs/generate-self-signed-certificates.md at master · coreos/docs](https://github.com/coreos/docs/blob/master/os/generate-self-signed-certificates.md)
- [Mutual TLS - Testing mTLS · Cloudflare for Teams documentation](https://developers.cloudflare.com/cloudflare-one/identity/devices/mutual-tls-authentication#testing-mtls)