遅かれ早かれ、VPN の利用者が秘密鍵をメールで送ると申し出て、管理者に証明書を作ってもらおうとします。断るべきはまさにその申し出です。機能する流れは、最初のコマンドから鍵をクライアントに置いたままにします。クライアントが鍵を生成し、証明書署名要求を送り、発行者がそれを検証して承認し、署名済みの証明書が戻ってきて、それを作った鍵の隣に置かれます。以下のチェックリストは各段階でフェイルクローズし、それが要点です。五つの段階のうち二つは、急いでいるときに人が飛ばしがちな段階だからです。

手順 1: 鍵が住む場所で生成する

鍵を保持するマシン上で生成し、そこに置いたままにします。ソフトウェア鍵は厳しい権限のファイルになります。ハードウェアに支えられた鍵や管理ストアの鍵はファイルにすらならず、その登録手順は通常 OpenSSL コマンドではなく別のツールです。いずれにせよ、秘密の半分を誰かが見る必要はありません。

ソフトウェアの流れはこうです。鍵と要求を同じセッションで作ります。

umask 077
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out client.key
openssl req -new -key client.key -out client.csr -subj "/CN=alice-vpn/O=Example"

umask の行は見た目以上に重要です。これがないと、既定で寛容なシステムでは鍵が誰でも読める形で書かれ、あとでファイルをコピーするとそれが広がります。既存の鍵の再利用は、すでに保護され方針に合っていれば正当です。デバイスごとに新しく作るほうが説明しやすいでしょう。

手順 2: 身元は要求の中に置き、メールには書かない

要求を作る前に、VPN が実際に何を認証するのかを決めます。その答えは CSR の中に入るものだからです。OpenVPN は伝統的にクライアントの身元を証明書のコモンネームに結び付け、strongSwan のようなツールは身元文字列をサブジェクトやサブジェクト代替名と照合します。「私の名前は alice です」というメールは証拠にならず、サブジェクトが同じことを言う要求はほんの少しだけましです。

検証側がサブジェクト代替名を確認する場合は追加してください。現代の照合は多くの場合、旧来の CN を無視します。

openssl req -new -key client.key -out client.csr \
  -subj "/CN=alice-vpn/O=Example" \
  -addext "subjectAltName=DNS:alice-vpn"

CN は短く、安定させ、ノート PC の型番ではなくアカウントに結び付けます。利用者デバイスのシリアル番号を含む名前は、最初のハードウェア交換を待っているサポート問題です。

手順 3: 署名する前に要求を検証する

発行者は二つの異なることを検証し、それらを混同することが典型的な誤りです。第一に、要求が内部的に整合していること。署名はそれが運ぶ公開鍵に対して検証されます。第二に、要求者が要求する内容への権利を持つこと。これは暗号的な検査では答えられません。

機械的な部分はコマンド一つで、サブジェクトとともに検証結果を出力するはずです。

openssl req -in client.csr -noout -verify -subject
openssl req -in client.csr -noout -pubkey | openssl pkey -pubin -noout -text

これが何を証明するかに注意してください。検証に通る要求は、送信者が提出時点で対応する秘密鍵を持っていたことだけを示します。送信者がサブジェクトに記された本人であることは証明せず、要求されたグループや経路へのアクセスを承認するものでもありません。登録の審査は別の関門であり、誰も承認していないグループを求める要求は、黙って編集するのではなく拒否すべきです。

手順 4: 承認し、明示的な拡張で署名する

署名は、要求をそのまま受け入れるのではなく CA が方針を決める場面です。有効期間、鍵用途、ロール拡張は拡張ファイルに入れるもので、半年後に思い出せない既定値に入れるものではありません。VPN 認証用のクライアント証明書には、用途としてクライアント認証が必要です。

cat > client.ext <<'EOF'
basicConstraints = CA:FALSE
keyUsage = digitalSignature
extendedKeyUsage = clientAuth
EOF
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -days 365 -extfile client.ext -out client.crt

既存のシリアルファイルがすでに使われている場合は -CAserial をそれとともに使い、CA 鍵はオフラインか、方針が名指す保護された保管場所に置きます。証明書のシリアル番号は発行者ごとに一意でなければなりません。シリアルファイルをゼロから作り直すと衝突の危険があり、失効が曖昧になります。

何を発行したかを記録します。シリアル、サブジェクト、要求された身元、承認者、有効期限です。その記録こそが後に失効判断の拠り所になり、登録の足跡がメールボックスにしかないなら、証明書だけから再構成することはできません。

手順 5: 対応する鍵の隣に導入し、ペアを証明する

クライアントが受け取るのは証明書と発行者のチェーンであり、鍵ではありません。接続する前に、証明書がディスク上にすでにある鍵と実際にペアになることを確認してください。一致しないペアは、ずっと後に紛らわしいハンドシェイク失敗を生みます:

openssl x509 -in client.crt -noout -pubkey | openssl pkey -pubin -outform DER | openssl sha256
openssl pkey -in client.key -pubout -outform DER | openssl sha256

二つのダイジェストは一致しなければなりません。そのうえで接続し、サーバーが記録する身元が承認したものかどうかを確認します。トンネルが上がったことだけでは足りません。鍵を失った場合やデバイスを交換した場合の答えは、失効と新しいデバイスからの新しい要求です。管理者にどこか別の場所から鍵を送り返してもらうよう頼んでも元の鍵は取り戻せず、要求を安全にしていたモデルが静かに壊れます。

関連記事