M1 MacにUTMを使ってウェブ開発(LAMP)環境を構築する 【6】オレオレ認証局(CA)を作ってオレオレ証明書を発行2025年版

開発環境

ウェブ開発用のローカル環境構築の最後の仕上げは、これです。
何年かに一回、メインマシンを入れ替える度にこの作業をやる、ということなんだけど、以前やった時と全く同じようにやってもダメだったりすることもあるし、知識面でもほぼ忘れちゃってるから勉強し直さないとだし、とにかくまた頑張ろうと思う。

曖昧だった用語のお勉強(ChatGPT頼みww)

SSL/TLS 関連の拡張子がよう分からん

質問:以下の拡張子について教えれ。.key .pem .csr .crt

.key

  • 内容
    • 秘密鍵 (Private Key)
    • 暗号化と署名のために使用される非公開の鍵。
    • 非対称暗号方式の片方のキーで、非常に厳重に保管する必要がある。
  • 用途
    • サーバーがデータを復号化するため。
    • 証明書署名要求 (CSR) を作成する際に使用。
  • 特徴
    • 内容は機密性が高いテキスト形式で、通常は以下のように始まります:
      -----BEGIN PRIVATE KEY-----
      (データ)
      -----END PRIVATE KEY-----

.pem

  • 内容
    • PEM形式 (Privacy Enhanced Mail) のファイル。
    • PEM形式はBase64エンコードされたデータを含むテキストファイルで、鍵、証明書、CSRなど様々な内容を格納可能。
  • 用途
    • 秘密鍵や公開鍵、証明書など、TLS関連ファイルの保存形式として使われる。
    • 拡張子自体は用途を限定しないため、内容を確認する必要がある。
  • 特徴
    • ファイルの先頭と末尾に以下のようなヘッダーが付く:
      -----BEGIN CERTIFICATE-----
      (データ)
      -----END CERTIFICATE-----

.csr

  • 内容
    • 証明書署名要求 (Certificate Signing Request)。
    • 公開鍵と、それに関連する情報 (ドメイン名、組織名など) を含むファイル。
    • 認証局 (CA) に送信して、証明書を発行してもらうために使用。
  • 用途
    • SSL/TLS証明書を取得する際に必要。
  • 特徴
    • 内容はテキスト形式で、以下のように始まる:
      -----BEGIN CERTIFICATE REQUEST-----
      (データ)
      -----END CERTIFICATE REQUEST-----

.crt

  • 内容
    • 証明書 (Certificate)。
    • 認証局 (CA) によって署名された公開鍵と、それに関連する情報 (ドメイン名、組織情報、署名など) を含む。
  • 用途
    • サーバーやクライアントが、通信相手を検証するために使用。
    • Webサーバーにインストールして、HTTPS通信を有効にする。
  • 特徴
    • 内容は通常PEM形式で、以下のように始まる:
      コードをコピーする
      -----BEGIN CERTIFICATE-----
      (データ)
      -----END CERTIFICATE-----

主な違いと使い分け

拡張子 内容 用途
.key 秘密鍵 サーバー側で復号や署名に使用
.pem PEM形式の鍵や証明書 汎用的に鍵や証明書を保存
.csr 証明書署名要求 CAに証明書発行を依頼する際に使用
.crt 証明書 HTTPS通信で認証に使用

SSL/TLS 関連のディレクトリがよう分からん

質問:pkiとかtlsとかのディレクトリの目的や使い方を教えれ。

回答:pki や tls は、セキュリティ関連のディレクトリ構造で頻繁に使用される名前です。それぞれの意味と目的を以下に説明します。

PKI (Public Key Infrastructure)

  • 意味
    • 公開鍵基盤の略で、デジタル証明書を発行、管理、配布、失効する仕組み全体を指します。
  • 主な目的
    • 認証局 (CA) や証明書の管理に関連するファイルを格納します。
    • ディレクトリの役割としては、鍵や証明書、証明書失効リスト (CRL) などを組織的に管理する場所となります。
  • 典型的なディレクトリ構造
    • PKI を扱うディレクトリは通常、以下のような構造を持ちます:
/pki
 ├── private/    # 秘密鍵を格納(非常に厳重に保護)
 ├── certs/      # 発行された証明書を格納
 ├── csr/        # 証明書署名要求 (CSR) ファイルを格納
 ├── crl/        # 証明書失効リスト (CRL) を格納
 ├── newcerts/   # 新たに発行された証明書の一時保管
 ├── index.txt   # 発行された証明書の一覧
 └── serial      # 証明書のシリアル番号の管理
  • 利用例
    • オレオレ認証局や商用CAによる証明書管理。
    • openssl のデフォルト設定で PKI に関連するファイルをこのディレクトリに格納することが推奨されています。

TLS (Transport Layer Security)

  • 意味
    • トランスポート層セキュリティの略で、通信を暗号化してデータのセキュリティを確保するプロトコルです。
    • HTTPS やメールの暗号化など、セキュアな通信を確立する際に使用されます。
  • 主な目的
    • TLS通信に使用される鍵や証明書の格納、または関連する設定ファイルを管理します。
  • 典型的なディレクトリ構造
    • TLS に関連するディレクトリ構造は明確に規定されていませんが、多くの場合以下のような構成です:
/tls
 ├── private/   # 秘密鍵を格納
 ├── certs/     # 証明書を格納
 ├── ca/        # 信頼できる認証局の証明書
 ├── dhparams/  # Diffie-Hellman パラメータ
 └── config/    # TLSに関連する設定ファイル
  • 利用例
    • Webサーバー (Apache, Nginx) やアプリケーションサーバーの TLS 設定。
    • HTTPS 通信を有効化する際に証明書と秘密鍵を格納。

使い分け

  • pki: 主に鍵と証明書の管理を行う場所として利用。認証局 (CA) の運用や証明書管理に適している。
  • tls: 通信プロトコル (TLS) に関連した鍵や証明書を格納し、実際のアプリケーションで利用。

実際の運用

  • pki は証明書の作成や管理のために、認証局で利用。
  • tls は、Webサーバーやアプリケーションサーバーなど、実際の通信で使用する鍵や証明書を管理。

たとえば、自前で認証局を構築する場合は pki ディレクトリに認証局 (CA) 関連のファイルを置き、TLS 通信に使用する証明書は tls ディレクトリにコピーして利用する形が一般的です。

OpenSSLの環境設定

opensslの確認

$ openssl version
OpenSSL 3.2.2 4 Jun 2024 (Library: OpenSSL 3.2.2 4 Jun 2024)

ディレクトリ構成

今回は

/pki
 ├─ CA/
 │  ├── private/
 │  ├── certs/
 │  ├── newcerts/
 │  ├── crl/
 │  ├── index.txt
 │  └── serial  
 └─ tls/
    ├── private/
    └── certs/  

こんなディレクトリ構成で行ってみようと思うので、足りないディレクトリやファイルは作っておく。
/etc/pki/tls 以下はprivate、certs共にあったのでそのまま使う。

$ mkdir /etc/pki/CA
$ cd /etc/pki/CA
$ mkdir private certs newcerts
$ touch index.txt
$ echo 00 > serial

openSSL設定

# カレントディレクトリを /etc/pki として
$ cp tls/openssl.cnf tls/openssl.cnf_bak
$ vim tls/openssl.cnf

[ CA_default ]

dir             = /etc/pki/CA           # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
crl_dir         = $dir/crl              # Where the issued crl are kept
database        = $dir/index.txt        # database index file.
#unique_subject = no                    # Set to 'no' to allow creation of
                                        # several certs with same subject.
new_certs_dir   = $dir/newcerts         # default place for new certs.

certificate     = $dir/cacert.pem       # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
crl             = $dir/crl.pem          # The current CRL
private_key     = $dir/private/cakey.pem # The private key

[ usr_cert ]
# このセクションに以下の行を追記
subjectAltName = @alt_names

# [req_distinguished_name]項目で毎度入れる内容の初期値を入れておく
countryName_default = JP #日本
stateOrProvinceName_default = Tokyo #都道府県
localityName_default = Chiyoda-ku #市区町村
0.organizationName_default = OREORE.INC #組織名

今回のディレクトリ構成では[ CA_default ]セクションを変更する必要はなかった。
これで作業に入る。

オレオレ認証局(CA)の作成

CA は Certificate Authority(認証局)の略さ!

オレオレ認証局(CA)の秘密鍵の作成

$ cd /etc/pki/CA
$ openssl genrsa -aes256 -out ./private/cakey.pem 2048
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

# 以下で内容を確認することができる
$ openssl rsa -text -noout -in ./private/cakey.pem

オレオレ認証局(CA)の証明書署名要求(CSR)の作成

CSRは Certificate Signing Request の略さっ!!

# 対話形式で進めるやつ。後半は無視。
$ openssl req -new -key ./private/cakey.pem -out ./cacert.csr
Enter pass phrase for ./private/cakey.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) [JP]:
Locality Name (eg, city) [Chiyoda-ku]:
Organization Name (eg, company) [OREORE.INC]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your servers hostname) []:OREORE
Email Address []:hogehoge@gmail.com

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

# 同様に表示できる
$ openssl req -text -noout -in cacert.csr

ここで出てくるCommonNameは何でもいいが、後でサーバー証明書を発行する時はちゃんとドメイン名入れないとダメだから注意ね。

オレオレ認証局(CA)のサーバー証明書の作成

いわゆる自己証明書。
先ほど作成した証明書署名要求(CSR)に自分で署名してサーバー証明書を作成する。

$ openssl x509 -days 1825 -in ./cacert.csr -req -signkey ./private/cakey.pem -out ./cacert.pem
Enter pass phrase for ./private/cakey.pem:
Certificate request self-signature ok
subject=C=JP, L=Chiyoda-ku, O=OREORE.INC, CN=OREORE, emailAddress=hogehoge@gmail.com

ここで悩むのが -days に渡す日数なんだけど、昔は10年=3650日で設定するのが普通だったのが、セキュリティー強化の流れの中でどんどん期限が短縮され、今では398日(13ヶ月)ってことになってるはず。
最近の記事をいろいろ調べてるんだけど、ローカル環境だからかな、相変わらず5年とかでやってるケースが多いみたいなので、今回は5年=1850日でやってみることにする。13ヶ月後が楽しみである。

ともあれ、オレオレ認証局はこれで出来上がり。

クライアントからのサーバー証明書署名要求(CSR)への署名

オレオレ認証局(CA)のお仕事は、Webサイト運営者(クライアント)から送られてくるサーバー証明書への署名要求(CSR)に対して署名すること。

クライアント側が秘密鍵作る

この作業してたら、いま自分がCAとして作業してるのかクライアントとしてなのか分からなくなるので、しっかり意識することが大事。
クライアント側の作業は tlsディレクトリで行う。

$ cd ../tls
$ openssl genrsa -aes256 -out ./private/privkey.pem 2048

クライアントがCAに対して発行する証明書発行要求(CSR)ファイル作成

*.vagrant.testがクライアントなので、アウトプットするファイル名を”vagrant_test.csr”とする。

$ openssl req -new -key ./private/privkey.pem -out ./certs/vagrant_test.csr
# また対話式じゃ
Country Name (2 letter code) [JP]:
Prefecture Name [Tokyo]:
Locality Name (eg, city) [Chiyoda-ku]:
Organization Name (eg, company) [OREORE.INC]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your servers hostname) []:*.vagrant.test ##ここだけ注意して!!!
Email Address []:

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

オレオレ認証局(CA)がこのCSRに署名してサーバー証明書を発行するぞ!

Subject Alternative Name の設定

証明書発行の前に追加設定ファイルを作成

  1. san.ext(ファイル名はなんでも可)
  2. このファイルに subjectAltName=DNS:hogehoge.com を追記(マルチドメインの場合はカンマ区切り)
  3. opensslコマンドに -extfile san.ext を追加して証明書発行を実行
$ echo "subjectAltName=DNS:vagrant.test,DNS:*.vagrant.test" > /etc/pki/CA/san.ext

ほんじゃ署名するぞ

# カレントディレクトリは tls/
openssl ca -in ./certs/vagrant_test.csr -out ./certs/vagrant_test.crt.pem -days 825 -extfile /etc/pki/CA/san.ext
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 0 (0x0)
        Validity
            Not Before: Jan  1 19:04:41 2025 GMT
            Not After : Feb  2 19:04:41 2026 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = OREORE.INC
            commonName                = *.vagrant.test
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:vagrant.test, DNS:*.vagrant.test
Certificate is to be certified until Feb  2 19:04:41 2026 GMT (397 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
Database updated

SignatureがOKでDatabaseがアップデートされた!ということで、覗いてみよう。

$ cat /etc/pki/CA/index.txt
V   260202190441Z       00  unknown /C=JP/ST=Tokyo/O=OREORE.INC/CN=*.vagrant.test

$ ls /etc/pki/tls/certs/
vagrant_test.csr  vagrant_test.pem

# 間違えてvagrant_test.pem という名前で作っちゃったけど、crtだということを明示したいのでリネームする。

$ mv /etc/pki/tls/certs/vagrant_test.pem /etc/pki/tls/certs/vagrant_test.crt.pem

クライアント側へ証明書を渡す!

クライアントのウェブサーバーに読ませるファイルは、vagrant_test.crt.pem
で、ウェブサーバーが起動する際に毎回パスフレーズを求められるようになってしまう。
これを避けるために、パスワード無しで済むようにする。

$ openssl rsa -in /etc/pki/tls/private/privkey.pem -out /etc/pki/tls/private/privkey-nopass.pem
Enter pass phrase for ./tls/private/privkey.pem:
writing RSA key

これでApacheのconf側に

<VirtualHost *:443>
    ServerName vagrant.test
    SSLEngine On
    SSLCertificateFile /etc/pki/tls/certs/vagrant_test.crt.pem
    SSLCertificateKeyFile /etc/pki/tls/private/privkey-nopass.pem
</VirtualHost>

こんな感じで記述してやるとイイカンジ。
これは次回詳しくやろう。

認証局(CA)についてクライアントOS(Mac)に知らせる

そもそも証明書を発行できるようになったといっても、認証局の信頼性がなければ意味がない。
というわけで、
/etc/pki/CA/cacert.pem
このファイルをOSに登録するでござる。
ということで、まずはこのファイルを共有ディレクトリ経由でmacに渡してやる。

ルート証明書をキーチェーンへ追加する

  1. キーチェーンアクセスアプリを開く。

    • FinderでCommand + Spaceを押して検索バーを開き、キーチェーンアクセスと入力。
    • または、アプリケーション > ユーティリティ > キーチェーンアクセス
  2. キーチェーンアクセスの左メニューから、追加先を選択。

    • 「システム」または「ログイン」キーチェーンを選択。
      • システムキーチェーンに追加すると、すべてのユーザーが証明書を利用できる。
      • ログインキーチェーンに追加すると、現在のユーザーのみが利用できる。
  3. 証明書をインポートする。

    • メニューバーの[ファイル] > [読み込む…]をクリック。
    • または、ドラッグ&ドロップで証明書をウィンドウに直接追加。
  4. ルート証明書を信頼する
    インポートしただけでは、まだ信頼はされてない。

    • インポートした証明書をダブルクリックし、「信頼」を展開する。
    • 「この証明書を使用するとき」のドロップダウンから「常に信頼」を選択

これでOK!!

ちなみに今日は元日。いやもう2日の早朝5時。
眠い。寝る。