OpenSSLでX.509証明書を作成してGoで署名検証するまでのメモ

# ステップ

- 1.　ルート証明書/サーバ証明書の作成
    - ① ルート証明書の作成
    - ② 証明書署名要求の作成
    - ③ サーバ証明書の発行
    - ④ OpenSSLで署名検証
- 2.　Goで署名検証


# 1. ルート証明書/サーバ証明書の作成

**① ルート証明書を作成します**

とりあえず有効期限は`-days 365000`として長くしています。
また、`-newkey rsa:4096 -keyout root.key`オプションを付けることで秘密鍵も同時に生成しています。
さらに`-subj "/CN=my root cert"`とすることで、インタラクティブに項目を入力しないようにしています。

```
openssl req -x509 -nodes -days 365000 -newkey rsa:4096 -keyout root.key -out root.pem -subj "/CN=my root cert"
```

<details><summary>実行例</summary>

```bash
ubuntu@wsl:cert$openssl req -x509 -nodes -days 365000 -newkey rsa:4096 -keyout root.key -out root.pem -subj "/CN=my root cert"
Generating a RSA private key
.........................................................................................................................................................................................................................................................................................................................................++++
.............................................................++++
writing new private key to 'root.key'
-----
```
</details>


**② サーバ証明書の証明書署名要求を作成します**

組織名などその他の項目を設定したい場合は次のように書くことができます。
`/C=JP/ST=Kanagawa/L=Yokohama/O=myorg/OU=myorgunit/CN=my server cert`

```
openssl req -nodes -newkey rsa:4096 -keyout cert.key -out cert-req.pem -subj "/CN=my server cert"
```

<details><summary>実行例</summary>

```bash
ubuntu@wsl:cert$openssl req -nodes -newkey rsa:4096 -keyout cert.key -out cert-req.pem -subj "/CN=my server cert"
Generating a RSA private key
......................................................................++++
................................................................................++++
writing new private key to 'cert.key'
-----
```
</details>

**③ ルート証明書で署名したサーバ証明書を発行します**

CommonNameを設定しただけではサーバ接続時にエラーとなることがあります。この場合はSAN(Subject Alternative Name)を使用する必要があります。SANを使用する際は下記コマンドに`-extfile ./subj.txt`のようにオプションを追加します。subj.txtには`subjectAltName = DNS:localhost,DNS:127.0.0.1`のようにDNS名を記載します。

```
openssl x509 -req -in cert-req.pem -days 365000 -CA root.pem -CAkey root.key -set_serial 01 -out cert.pem
```


<details><summary>実行例</summary>

```bash
ubuntu@wsl:cert$openssl x509 -req -in cert-req.pem -days 365000 -CA root.pem -CAkey root.key -set_serial 01 -out cert.pem
Signature ok
subject=CN = my server cert
Getting CA Private Key
```
</details>

**④ サーバ証明書を検証します**

発行したサーバ証明書が正しくルート証明書で署名されていることを確認するため、ここではOpenSSLコマンドで検証します

```
openssl verify -CAfile root.pem cert.pem
```

<details><summary>実行例</summary>

```bash
ubuntu@wsl:cert$openssl verify -CAfile root.pem cert.pem
cert.pem: OK
```
</details>

# 2. Goで署名検証

上記で発行したルート証明書とサーバ証明書を用いて、Goで署名検証を行ってみます。
やっていることは至極単純で、以下の通りです。

* ルート証明書の読み込み
* サーバ証明書の読み込み
* ルート証明書を証明書プールへ登録
* 検証オプションを指定してサーバ証明書を検証

```go:main.go
package main

import (
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
)

func main() {

	// ルート証明書の読み込み
	rootFile := "./root.pem"
	rootBytes, _ := ioutil.ReadFile(rootFile)
	rootBlock, _ := pem.Decode(rootBytes)
	root, _ := x509.ParseCertificate(rootBlock.Bytes)
	println("Read certification : ", root.Subject.String())

	// サーバ証明書の読み込み
	certFile := "./cert.pem"
	certBytes, _ := ioutil.ReadFile(certFile)
	certBlock, _ := pem.Decode(certBytes)
	cert, _ := x509.ParseCertificate(certBlock.Bytes)
	println("Read certification : ", cert.Subject.String())

	// 証明書プールの作成
	certPool := x509.NewCertPool()
	certPool.AddCert(root)

	// 署名検証オプションの作成
	opts := x509.VerifyOptions{
		Roots: certPool,
	}

	// 署名検証を実行
	if _, err := cert.Verify(opts); err != nil {
		panic("Cert verification failed: " + err.Error())
	}
	println("Cert verification succeeded")

}
```

上記のコードの実行結果は以下のようになり、署名検証に成功していることが確認できます

```bash
ubuntu@wsl:cert$go run main.go
Read certification :  CN=my root cert
Read certification :  CN=my server cert
Cert verification succeeded
```

# 参考資料

* [6.3.3.2 Creating SSL Certificates and Keys Using openssl](https://dev.mysql.com/doc/refman/5.7/en/creating-ssl-files-using-openssl.html)
* [Creating a Self-Signed Certificate With OpenSSL](https://www.baeldung.com/openssl-self-signed-cert)
* [OpenSSLコマンドの備忘録](https://qiita.com/takech9203/items/5206f8e2572e95209bbc)
* [Digital Certificate](https://www.infraexpert.com/study/security6.html)
* https://pkg.go.dev/crypto/x509@go1.18.3#VerifyOptions