CSR(Certificate Signing Request)
概要
CSR(Certificate Signing Request) は、SSL/TLS 証明書の発行を認証局(CA)に申請するために送付する署名付きデータです。RFC 2986(PKCS#10)で形式が定義されており、公開鍵、ドメイン名(Common Name)、組織情報などを ASN.1 構造で格納し、対応する秘密鍵で署名します。
認証局は受け取った CSR の内容を検証し、問題がなければ CSR に含まれる公開鍵をもとに SSL/TLS 証明書を発行します。秘密鍵は CSR に含まれず、サーバー側から外部に送信されることはありません。この仕組みにより、証明書の発行プロセスを通じて秘密鍵の安全性が維持されます。
Let’s Encrypt のような DV 証明書を ACME プロトコル(RFC 8555)で取得する場合、certbot や acme.sh が CSR の生成と送信を自動的に処理します。一方、OV 証明書や EV 証明書を商用の認証局から取得する場合は、管理者が手動で CSR を生成し、認証局のウェブポータルにアップロードする手順が一般的です。
仕組み
CSR は公開鍵・ドメイン名・組織情報を ASN.1 構造に格納し、秘密鍵で署名したデータです。
CSR の構造
CSR は RFC 2986 に従い、次の 3 つの要素で構成されます。
- 証明書要求情報(CertificationRequestInfo) — Subject(ドメイン名、組織名など)、公開鍵、属性(拡張フィールドを含む)
- 署名アルゴリズム識別子 — SHA-256 with RSA、ECDSA with SHA-256 など
- デジタル署名 — CertificationRequestInfo を秘密鍵で署名した値
PEM 形式でエンコードされた CSR は -----BEGIN CERTIFICATE REQUEST----- で始まり、-----END CERTIFICATE REQUEST----- で終わります。内部は DER(Distinguished Encoding Rules)でエンコードされた ASN.1 データの Base64 表現です。
Subject フィールド
CSR の Subject フィールドには以下の情報を指定します。
| フィールド | 略称 | 説明 | 例 |
|---|---|---|---|
| Common Name | CN | 証明書で保護するドメイン名 | example.com |
| Organization | O | 法人名(OV/EV 証明書で必須) | Example Inc. |
| Organizational Unit | OU | 部署名(省略可) | IT Department |
| Country | C | 国コード(ISO 3166-1 alpha-2) | JP |
| State | ST | 都道府県 | Tokyo |
| Locality | L | 市区町村 | Shibuya |
DV 証明書では CN のみが必須で、O 以下の組織情報は認証局に無視されます。OV 証明書と EV 証明書では O と C が必須であり、認証局が登記情報と照合して組織の実在を確認します。
RFC 6125(2011 年)以降、ドメインの検証には Subject の CN ではなく SAN(Subject Alternative Name)拡張が推奨されています。Chrome は 2017 年(バージョン 58)以降、CN のみの証明書を拒否し SAN のみを参照します。CSR を生成する際は SAN を必ず含めてください。
鍵ペアの生成と CSR への署名
CSR を生成するには、まず秘密鍵と公開鍵のペアを作成します。RSA の場合、鍵長は 2,048 ビット以上 が CA/Browser Forum の Baseline Requirements で求められています。ECDSA の場合は P-256 または P-384 曲線を使います。
CSR は秘密鍵で署名されます。認証局は CSR に含まれる公開鍵を使って署名を検証し、CSR の送信者が秘密鍵を保有していることを確認します。この仕組みを Proof of Possession(所有証明)と呼びます。
証明書発行フロー
CSR を使った証明書発行の全体フローは次の通りです。
- サーバー管理者が秘密鍵と CSR を生成する
- CSR を認証局に送信する(ウェブポータルへのアップロード、または ACME プロトコル経由)
- 認証局が CSR の署名を検証する
- 認証局がドメイン管理権限を検証する(DV)、またはドメインと組織の実在を確認する(OV/EV)
- 認証局が CSR に含まれる公開鍵をもとに X.509 証明書を発行する
- サーバー管理者が発行された証明書と秘密鍵をウェブサーバーに設定する
ACME プロトコルを使う場合、ステップ 1〜6 のすべてが自動化されます。certbot は一時的な鍵ペアと CSR を生成し、Let’s Encrypt の ACMEv2 API にリクエストを送り、チャレンジ検証を経て証明書を取得し、ファイルとして保存します。
設定例
RSA・ECDSA の鍵生成から SAN 付き CSR の作成、certbot での自動処理までを示します。
RSA 鍵での CSR 生成
openssl を使って RSA 2,048 ビット の秘密鍵と CSR を一度に生成する例です。
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr \
-subj "/C=JP/ST=Tokyo/L=Shibuya/O=Example Inc./CN=example.com"
-nodes オプションは秘密鍵をパスフレーズなしで生成します。本番環境ではパスフレーズの設定を検討してください。ただし、ウェブサーバーの起動時に毎回パスフレーズの入力が必要になるため、自動起動と相性が悪い場合があります。
ECDSA 鍵での CSR 生成
ECDSA(P-256 曲線)を使う場合は、先に秘密鍵を生成してから CSR を作成します。
# ECDSA P-256 秘密鍵を生成
openssl ecparam -genkey -name prime256v1 -out server-ec.key
# CSR を生成
openssl req -new -key server-ec.key -out server-ec.csr \
-subj "/C=JP/ST=Tokyo/L=Shibuya/O=Example Inc./CN=example.com"
ECDSA は RSA と比べて鍵長が短く、TLS ハンドシェイクのパフォーマンスに優れます。P-256 は RSA 3,072 ビットと同等の暗号強度を持ちます。
SAN を含む CSR の生成
複数ドメインやワイルドカードを SAN に含める場合、設定ファイルを使います。
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr \
-config <(cat <<-EOF
[req]
default_bits = 2048
distinguished_name = dn
req_extensions = v3_req
prompt = no
[dn]
C = JP
ST = Tokyo
L = Shibuya
O = Example Inc.
CN = example.com
[v3_req]
subjectAltName = DNS:example.com,DNS:www.example.com,DNS:api.example.com
EOF
)
Let’s Encrypt(certbot)の場合
certbot は CSR の生成を内部で自動処理しますが、独自の CSR を渡すこともできます。
# certbot に CSR を渡して証明書を取得
certbot certonly --webroot -w /var/www/html --csr server.csr
通常の使い方では certbot が鍵ペアと CSR を自動生成するため、手動で CSR を作成する必要はありません。
# certbot が鍵ペアと CSR を自動生成する通常のフロー
certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com
確認方法
生成した CSR の内容を確認するには openssl を使います。
# CSR の内容をテキスト表示
openssl req -in server.csr -noout -text
# Subject フィールドのみを表示
openssl req -in server.csr -noout -subject
# CSR の署名を検証
openssl req -in server.csr -verify -noout
Subject の出力例です。
subject=C = JP, ST = Tokyo, L = Shibuya, O = Example Inc., CN = example.com
署名検証の出力例です。
verify OK
openssl req -verify で verify OK と出力されれば、CSR の署名が正しいことを確認できます。認証局に送付する前に必ず実行してください。
サーバーに設定された証明書の公開鍵が CSR の公開鍵と一致しているかを確認するには、両方の公開鍵のハッシュを比較します。
# CSR の公開鍵ハッシュ
openssl req -in server.csr -noout -pubkey | openssl md5
# 秘密鍵の公開鍵ハッシュ
openssl rsa -in server.key -pubout | openssl md5
# 証明書の公開鍵ハッシュ
openssl x509 -in server.crt -noout -pubkey | openssl md5
3 つのハッシュがすべて一致すれば、秘密鍵・CSR・証明書が同じ鍵ペアに基づいていることが確認できます。
外部の視点からも確認したい場合は、Labee Dev Toolbox の SSL Cert API を使うと、外部の視点から見た結果を取得できます。
curl "https://labee.dev/api/ssl-cert?hostname=example.com"
{
"success": true,
"data": {
"hostname": "example.com",
"port": 443,
"reachable": true,
"status": 200
},
"error": null,
"meta": { "responseTime": 123 }
}
data.reachable が true であれば、外部の環境から HTTPS 接続が成立しています。CSR から発行された証明書が正しくサーバーに設定されていることの外部確認として使えます。
よくある問題
CSR の生成・送付で発生しやすいミスと、証明書発行後の設定トラブルを整理します。
秘密鍵と証明書の不一致
CSR を生成した秘密鍵と、ウェブサーバーに設定する秘密鍵は同一でなければなりません。証明書の更新時に新しい CSR を生成したのに、古い秘密鍵のままサーバーに設定すると、nginx では SSL_CTX_use_PrivateKey_file ... error が、Apache では AH02565: Certificate and private key ... do not match が発生します。openssl md5 で公開鍵のハッシュを比較して不一致を特定してください。
SAN の未指定
CSR に SAN を含めずに CN だけでドメインを指定すると、Chrome(バージョン 58 以降)で NET::ERR_CERT_COMMON_NAME_INVALID エラーになります。2017 年以降、Chrome は CN を無視し SAN のみでドメインを検証します。CSR 生成時に subjectAltName 拡張を必ず含めてください。
弱い鍵長の使用
RSA 1,024 ビット の鍵は 2013 年以降、主要なブラウザーと認証局がすべて拒否しています。CA/Browser Forum の Baseline Requirements では RSA 2,048 ビット以上 が必須です。openssl req -text で CSR の Public-Key フィールドを確認し、鍵長が十分かどうかを検証してください。
CSR のエンコードエラー
CSR ファイルをテキストエディタで開いて保存した際に、改行コードが CRLF から LF に変わったり、余分な空白が挿入されたりすると、認証局の検証で Base64 デコードに失敗します。CSR ファイルは生成後に内容を編集しないでください。openssl req -verify でファイルの整合性を確認できます。
ACME 環境で手動 CSR を生成してしまう
Let’s Encrypt を certbot で使う場合、手動で CSR を生成する必要はありません。certbot が鍵ペアの生成から CSR の作成、証明書の取得まですべて自動処理します。手動で CSR を生成して --csr オプションで渡す方法も使えますが、証明書の自動更新(certbot renew)が機能しなくなります。自動更新を前提とする環境では certbot のデフォルトフローを使ってください。