開発環境やテスト用に、認証局を通さず自分で署名したサーバー証明書を作りたいときは、openssl req -x509を使う。秘密鍵と証明書を1コマンドで作れる。

$ openssl req -x509 -newkey rsa:2048 -noenc -keyout server.key -out server.crt -days 365 \
    -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

server.keyが秘密鍵、server.crtが証明書である。-daysは証明書の有効日数である。

実行例はUbuntu 24.04のOpenSSL 3.0.13のもの。

有効期間を指定する

-daysを省略すると、有効期間は30日になる。開発用に長く使う場合は、-daysを明示する。

$ openssl req -x509 -key server.key -out server.crt -subj "/CN=a"
$ openssl x509 -in server.crt -noout -dates
notBefore=Sep 30 14:53:44 2026 GMT
notAfter=Oct 30 14:53:44 2026 GMT
$ openssl req -x509 -key server.key -out server.crt -subj "/CN=a" -days 3650
$ openssl x509 -in server.crt -noout -enddate
notAfter=Sep 27 14:53:44 2036 GMT

開発用であれば1年程度にして、期限切れ時に作り直すのが扱いやすい。長い有効期間は、秘密鍵が漏れた場合の影響も長引く。

有効期限の確認方法は別の記事で説明している。

参考: 【openssl】証明書ファイルの有効期限を確認する

SANを指定する

現在のブラウザーやHTTPクライアントは、コモンネーム(CN)ではなく、SAN(Subject Alternative Name)でホスト名を検証する。SANがない証明書は、CNが一致していてもホスト名の検証に失敗する場合がある。-addextでSANを指定する。

$ openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc \
    -keyout ec.key -out ec.crt -days 365 -subj "/CN=localhost" \
    -addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1,IP:::1"
$ openssl x509 -in ec.crt -noout -ext subjectAltName
X509v3 Subject Alternative Name:
    DNS:localhost, DNS:*.localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1

DNS名とIPアドレスは、DNS:とIP:で区別して、カンマで区切って並べる。

既存の秘密鍵から作る

すでに秘密鍵がある場合は、-newkeyと-keyoutの代わりに-keyを指定する。

$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out k.key
$ openssl req -x509 -key k.key -out k.crt -days 30 -subj "/CN=a"

秘密鍵の作り方は別の記事で説明している。

参考: 【openssl】秘密鍵を作成する (genpkey)

CSRから作る

CSRが手元にある場合は、openssl x509 -req -signkeyで、CSRと同じ秘密鍵を使って署名できる。

$ openssl req -new -key k.key -out k.csr -subj "/CN=b"
$ openssl x509 -req -in k.csr -signkey k.key -out k2.crt -days 30
Certificate request self-signature ok
$ openssl x509 -in k2.crt -noout -subject -issuer -ext basicConstraints
subject=CN = b
issuer=CN = b
No extensions in certificate

この方法では、CSRに含まれる拡張(SANなど)は証明書に引き継がれない。SANが必要な場合は、req -x509 -addextを使う。

参考: 【openssl】CSRを作成する (req -new)

鍵のパスフレーズを省略する

-newkeyで鍵を作るとき、-noencを付けないと、秘密鍵をパスフレーズで暗号化する。Webサーバーを自動で起動する場合など、パスフレーズを入力できない用途では-noencを付ける。

-noencは、OpenSSL 3.0で導入されたオプションである。古い記事で見かける-nodesは、同じ意味の非推奨の名前である。

証明書に設定される拡張

req -x509で作った証明書は、デフォルトでbasicConstraintsがCA:TRUEになる。つまり、認証局の証明書として扱われる。

$ openssl x509 -in server.crt -noout -ext basicConstraints
X509v3 Basic Constraints: critical
    CA:TRUE

サーバー証明書として使うだけなら、CA:FALSEを明示したほうが意図に合う。キー用途も合わせて指定する。

$ openssl req -x509 -key k.key -out ext.crt -subj "/CN=a" -days 30 \
    -addext "subjectAltName=DNS:a" \
    -addext "keyUsage=digitalSignature,keyEncipherment" \
    -addext "extendedKeyUsage=serverAuth" \
    -addext "basicConstraints=critical,CA:FALSE"
$ openssl x509 -in ext.crt -noout -ext keyUsage,extendedKeyUsage,basicConstraints
X509v3 Key Usage:
    Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
    TLS Web Server Authentication
X509v3 Basic Constraints: critical
    CA:FALSE

作成した証明書を確認する

openssl x509で、サブジェクト、発行者、有効期間を確認する。自己署名の証明書は、サブジェクトと発行者が同じになる。

$ openssl x509 -in server.crt -noout -subject -issuer -dates
subject=CN = localhost
issuer=CN = localhost
notBefore=Sep 30 14:53:44 2026 GMT
notAfter=Sep 30 14:53:44 2027 GMT

証明書自身を信頼する認証局として渡すと、openssl verifyで検証できる。何も指定しない場合は、信頼済みの認証局のリストに含まれないため失敗する。

$ openssl verify -CAfile server.crt server.crt
server.crt: OK
$ openssl verify server.crt
CN = localhost
error 18 at 0 depth lookup: self-signed certificate
error server.crt: verification failed

クライアントから接続して確認する

openssl s_serverでHTTPSサーバーを起動し、openssl s_clientで接続すると、証明書を使った接続を試せる。

$ openssl s_server -accept 4433 -cert server.crt -key server.key -www

別のターミナルから接続する。証明書を信頼していない場合は、検証に失敗する。

$ echo | openssl s_client -connect localhost:4433 2>&1 | grep -E 'Verif'
Verification error: self-signed certificate
Verify return code: 18 (self-signed certificate)

-CAfileで証明書自身を信頼する認証局として渡すと、検証に成功する。

$ echo | openssl s_client -connect localhost:4433 -CAfile server.crt -verify_hostname localhost 2>&1 | grep -E 'Verif'
Verification: OK
Verify return code: 0 (ok)

参考: 【openssl】指定したURLの証明書をコマンドで確認する

秘密鍵と証明書を1つのファイルにまとめる

サーバーによっては、証明書と秘密鍵を1つのPEMファイルにまとめる必要がある。catで連結する。

$ cat server.crt server.key > combined.pem

バージョンによる違い

OpenSSL 3.5や3.6でも、有効期間のデフォルトとCA:TRUEの挙動は同じである。

macOS標準の/usr/bin/openssl(LibreSSL 3.3.6)では、以下が異なる。

  • -noencに対応していない。代わりに-nodesを使う
  • デフォルトでbasicConstraintsが付かない
  • openssl x509の-extに対応していない。拡張の確認は-textを使う

-addextでのSAN指定と、-daysのデフォルト30日は、LibreSSL 3.3.6でも同じである。

参考

openssl-req - OpenSSL Documentation