【OpenSSL】localhost を https 化する 自己証明書の作成方法

自己証明書（通称オレオレ証明書）の作成方法です :smiley:

## 1. CA用の秘密鍵を生成
このコマンドは、2048ビットのRSA秘密鍵を生成し、`localCA.key` というファイルに保存します。

```bash
$ openssl genrsa -out localCA.key 2048
```
- openssl genrsa: RSA 秘密鍵を生成するコマンド
- -out: 生成した秘密鍵の出力ファイル名 (例: localCA.key)
- 2048: 秘密鍵の長さ (ビット単位) (例: 2048ビット)


## 2. 秘密鍵と識別情報から証明書署名要求 (CSR) の作成
このコマンドは、以下の情報を設定した CSR を生成し、`localCA.csr` というファイルに保存します。

```bash
$ openssl req -batch -new -key localCA.key  -out localCA.csr \
  -subj "/C=jp/ST=Osaka/L=Osaka-shi/O=\"Example Inc\"/OU=Foo/CN=tek"
```
- openssl req: 証明書署名要求 (CSR) を作成するコマンド
- -batch: 対話形式ではなく、バッチ処理で実行
- -new: 新しい CSR を作成
- -key: 秘密鍵ファイル (例: localCA.key)
- -out: 生成した CSR の出力ファイル名 (例: localCA.csr)
- -subj: 証明書の情報 (Subject) を指定

### -subj オプションについて
- 国名 (C): JP
- 都道府県 (ST): Osaka
- 市区町村 (L): Osaka-shi
- 組織名 (O): Example Inc
- 組織単位 (OU): Foo
- コモンネーム (CN): tek

項目は / で区切ります。
組織名 (O) や組織単位 (OU) など、値内にスペースが含まれる場合は、二重引用符 (") で囲みます。

## 3. 公開鍵が入ったCSRを秘密鍵で自己署名してCA証明書を発行
このコマンドは、`localCA.csr` に含まれる情報に基づいて、`localCA.key` を用いて自己署名証明書を生成し、`localCA.crt` というファイルに保存します。

```bash
$ openssl x509 -req -days 3650 -signkey localCA.key -in localCA.csr -out localCA.crt
```

- openssl x509: 証明書を発行するコマンド
- -in: CSR ファイル (例: localCA.csr)
- -out: 生成した証明書の出力ファイル名 (例: localCA.crt)
- -req: CSR を基に証明書を発行
- -signkey: 秘密鍵ファイル (例: localCA.key)
- -days: 証明書の有効期限 (日数) (例: 3650日) ※運用環境に合わせて設定
- -sha256: ハッシュアルゴリズム (例: SHA-256)

### 作成されたCA証明書の中身を確認
既存のSSL証明書の内容を表示するためのコマンドです。
```bash
$ openssl x509 -text -noout -in localCA.crt
```

```bash
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            70:3c:59:37:c6:cb:dc:f0:5e:16:13:54:98:1c:a5:30:1f:a5:a1:08
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = jp, ST = Osaka, L = Osaka-shi, O = \"Example Inc\", OU = Foo, CN = localhost
        Validity
            Not Before: Mar 19 21:08:48 2024 GMT
            Not After : Mar 17 21:08:48 2034 GMT
        Subject: C = jp, ST = Osaka, L = Osaka-shi, O = \"Example Inc\", OU = Foo, CN = localhost
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
```

## 4. サーバー用の秘密鍵を生成
このコマンドは、2048ビットのRSA秘密鍵を生成し、`localhost.key` というファイルに保存します。
```bash
$ openssl genrsa -out localhost.key 2048
```

## 5. 秘密鍵と識別情報から証明書署名要求 (CSR)を作成
このコマンドは、以下の情報を設定した CSR を生成し、`localhost.csr` というファイルに保存します。

```bash
$ openssl req -batch -new -key localhost.key  -out localhost.csr \
  -subj "/C=jp/ST=Osaka/L=Osaka-shi/O=\"Example Inc\"/OU=Foo/CN=localhost"
```
## 6. 証明書に付加する SAN (Subject Alternative Name サブジェクト代替名) を入れたテキストファイルを作成
このコマンドは、`localhost.csx`というファイルを作成し、そのファイルに**Subject Alternative Name (SAN)**情報を書き込みます。SAN情報は、HTTPS接続時にサーバの証明書とホスト名の照合に使用されます。

```bash
$ echo 'subjectAltName = DNS:localhost, DNS:localhost.localdomain, IP:127.0.0.1, DNS:app, DNS:app.localdomain' > localhost.csx
```

## 7. CSRにSAN情報も付加し、CA秘密鍵とCA証明書とで署名してサーバー証明書を作成
このコマンドは、`localhost.csr`という証明書署名要求(CSR)ファイルと`localhost.csx`というSAN情報ファイルを使用して、`localhost.crt`というHTTPSサーバ証明書を作成します。

```bash
$ openssl x509 -req -days 1825 -CA localCA.crt -CAkey localCA.key -CAcreateserial -in localhost.csr -extfile localhost.csx -out localhost.crt
```

### 補足：作成されたファイル
```bash
$ ll
total 30
-rw-r--r-- 1 user 197609 1232 Mar 20 06:20 localCA.crt
-rw-r--r-- 1 user 197609 1018 Mar 20 06:20 localCA.csr
-rw-r--r-- 1 user 197609 1732 Mar 20 06:20 localCA.key
-rw-r--r-- 1 user 197609   42 Mar 20 06:20 localCA.srl
-rw-r--r-- 1 user 197609 1596 Mar 20 06:20 localhost.crt
-rw-r--r-- 1 user 197609 1026 Mar 20 06:20 localhost.csr
-rw-r--r-- 1 user 197609  102 Mar 20 06:20 localhost.csx
-rw-r--r-- 1 user 197609 1732 Mar 20 06:20 localhost.key
```

## 8. CA証明書をクライアントにインストール
Windwosの場合
- Windowsキーを押して、`certlm.msc`を実行
- 信頼されたルート証明機関に`localCA.crt`をインポート

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482880/7c09c7b1-37e4-cb32-2f6c-fac3fcba02c2.png">

信頼されたルート証明機関を右クリック
<img width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482880/0c6d76d8-28de-37a9-975b-31f1a0b5d9a1.png">

ウィザードにしたがって信頼されたルート証明機関にインポートする
<img width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482880/27e29703-ebf3-bd57-e678-1dde9e3e143f.png">

## 9. サーバー側で秘密鍵とサーバー証明書を指定して起動
サーバー側の設定例

### Node.js での実装例
```server.js
const https = require('https');
const fs = require('fs');

const port = 8443;
const options = {
    key: fs.readFileSync('ssl/localhost.crt'),
    cert: fs.readFileSync('ssl/localhost.key')    
};

https.createServer(options, (req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
}).listen(port);
```

### Go での実装例
```go
package main

import "github.com/gin-gonic/gin"

func main() {
    router := gin.Default()
    router.GET("/", func(c *gin.Context) {
        c.HTML(200, "index.html", nil)
    })
    router.RunTLS(":8443", "ssl/localhost.crt", "ssl/localhost.key")
}
```

TLSハンドシェイクが成功すれば、下記の画像のように`https`が表示されます :v_tone2:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482880/991a18c8-62a9-0cb4-a401-337dbb6d034b.png)


## Reference
- [OpenSSL](https://www.openssl.org/docs/manmaster/man1/req.html)
- [OpenSSL commands](https://www.openssl.org/docs/manmaster/man1/index.html)
- [localhost を https 化する ~独自CA証明書を作って~](https://zenn.dev/search?q=localhost%2520https)
- [Chromeがコモンネームの設定を非推奨化、そのエラー対策としての自己署名証明書のCSRの作り方](https://rms.ne.jp/digital-certificate-news/chrome-deprecates_subject-cn-matching)