自己署名CAをOpenSSLコマンドラインオプションで使う

# 概要

いろいろな場面で(例えば、DockerクライアントとかAWS Client VPNとか、その他いくらでも)、自己署名CAを用意して、クライアント証明書を用意する場面があります。そのとき、OpenSSLのopenssl.cnfを設定したり、EasyRSAを使ったりするのは面倒です。

そのような設定を使わずにOpenSSLコマンドのコマンドラインオプションのみで実行するためのメモです。コマンドラインのみで完結するのでバッチ処理も簡単です。

# 手順

ここでは、自己署名CA証明書の発行、クライアント証明書の発行、についてまとめます。ここで使用したのはRSA 2048bitです。バッチ処理などを行いやすくするために秘密鍵生成時点では秘密鍵を暗号化していません。

以下のOpenSSLを使用しました(Alpine 3.17.3)。1.0.9k(CentOS7)でもだいたい動くと思います(Ed25519/Ed448は使えない)。

```bash
$ openssl version
OpenSSL 3.0.9 30 May 2023 (Library: OpenSSL 3.0.9 30 May 2023)
```

## 自己署名CA証明書

まずは、自己署名CAの秘密鍵と証明書を用意します。ここでは、CAのCNを`My Test CA`とします。

### X.509v3 Extensionの設定ファイルの用意

X.509v3 Extensionの設定ファイルを用意しておきます。#の行はコメントなので不要です。
Extensionの内容はEasyRSA3で生成された証明書と同じになるようにしてあります。必要に応じて変更してください。

```text:x509v3ext-CA.txt
# CA用X509v3 extensionsの設定
# https://www.openssl.org/docs/manmaster/man5/x509v3_config.html
# easyrsa3で出力したものに合わせてある
# 自己署名の場合、authorityKeyIdentifier(AKID)はalwaysのもののみ付与される
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints=CA:TRUE
keyUsage=keyCertSign,cRLSign
```

### 自己署名CA証明書の発行

CA秘密鍵と証明書を作成します。

```bash
# CA秘密鍵の生成
$ openssl genpkey -out myca.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048
..+..............+..

# CAのCSRを作成
$ openssl req -new -key myca.key -out myca.csr -subj "/CN=My Test CA"

# 自己署名CA証明書を発行
$ openssl x509 -req -days 3650 -in myca.csr -signkey myca.key -extfile x509v3ext-CA.txt -out myca.crt
Certificate request self-signature ok
subject=CN = My Test CA
```

ここで出力された`myca.key`がCA秘密鍵、`myca.crt`がCA証明書です。

:::note info
秘密鍵にRSA 2048bit以外を使用することもできます(OpenSSLのバージョンによる)。

```bash
# RSAの鍵長を変更
$ openssl genpkey -out rsa.key -algorithm RSA -pkeyopt rsa_keygen_bits:4096

# ECDSA
$ openssl genpkey -out ec.key -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1

# ECDSAで使用できる曲線の種類を確認
$ openssl ecparam -list_curves
  secp112r1 : SECG/WTLS curve over a 112 bit prime field
  secp112r2 : SECG curve over a 112 bit prime field
  secp128r1 : SECG curve over a 128 bit prime field
...

# Ed25519/Ed448
$ openssl genpkey -out ed.key -algorithm ED25519
$ openssl genpkey -out ed.key -algorithm ED448
```
:::

## クライアント証明書

上で作成したCA秘密鍵とCA証明書を使って、クライアント証明書を発行します。

### X.509v3 Extensionの設定ファイルの用意

CA証明書と同様にExtensionの設定ファイルを用意します。

```text:x509v3ext-client.txt
# X509v3 extensionsの設定 2023-07-14
# https://www.openssl.org/docs/manmaster/man5/x509v3_config.html
# easyrsa3で出力したものに合わせてある
basicConstraints=CA:FALSE
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
extendedKeyUsage=clientAuth
keyUsage=digitalSignature
```

### クライアント証明書の発行

先ほどのCA秘密鍵`myca.key`とCA証明書`myca.crt`を用いてクライアント証明書を発行します。

```bash
# クライアント秘密鍵の生成
$ openssl genpkey -out client.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048
..+...............+.+.

# クライアントCSRの生成
$ openssl req -new -key client.key -out client.csr -subj "/CN=client.example.com"

# クライアント証明書の発行
$ openssl x509 -req -in client.csr -CA myca.crt -CAkey myca.key -CAcreateserial -days 365 -extfile x509v3ext-client.txt -out client.crt
Certificate request self-signature ok
subject=CN = client.example.com
```

ここで出力された`client.key`がクライアント秘密鍵、`client.crt`がクライアント証明書です。

# 秘密鍵の暗号化

必要に応じて秘密鍵を暗号化します。

- `-aes256` -- 暗号化方式(`-des3`、`-aes128`)
- `-passout` -- パスワードを渡す方法 https://www.openssl.org/docs/man3.0/man1/openssl-passphrase-options.html

```bash
$ openssl pkey -in private.key -passout pass:PASSWORD -aes256 -out private-enc.key
```

:::note info
OpenSSLをDockerで使用する
OpenSSLをコンテナで使用すると新しいバージョンを使えて便利です。簡単にメモしておきます。

```dockerfile:Dockerfile
FROM alpine:3.17.3
RUN apk update \
 && apk add openssl \
 && mkdir /work
ENTRYPOINT ["openssl"]
WORKDIR /work
```

```bash
# イメージを作成してコマンドを実行する
$ docker build -t myopenssl .
$ docker run --rm -u $UID:$(id -g) -v $(pwd):/work myopenssl version
OpenSSL 3.0.9 30 May 2023 (Library: OpenSSL 3.0.9 30 May 2023)
```
:::

# 参考

- OpenSSL 3.0 -- https://www.openssl.org/docs/man3.0/man1/
    - X509v3 Extension -- https://www.openssl.org/docs/manmaster/man5/x509v3_config.html
    - genpkey -- https://www.openssl.org/docs/man3.0/man1/openssl-genpkey.html
    - pkey -- https://www.openssl.org/docs/man3.0/man1/openssl-pkey.html