PEMファイルが不正な形式のときに発生するerror:0906D06C:PEM routines:PEM_read_bio:no start lineの修正方法

intermediate🔒 SSL/TLS2026-06-20| Linux / macOS / Windows WSL — OpenSSL 1.0.x、1.1.x、3.x — PEMファイルを読み込むすべてのツール(nginx、Apache、curl、Python ssl、Node.js tls)

Error Message

error:0906D06C:PEM routines:PEM_read_bio:no start line
#openssl#pem#ssl-tls

シナリオ

深夜2時。サーバーに新しいTLS証明書をデプロイしています。リロードコマンドを実行すると、こんなエラーが表示されました:

error:0906D06C:PEM routines:PEM_read_bio:no start line

nginxが起動を拒否します。サイトがダウンしています。OpenSSLが証明書ファイルを解析できない——しかしファイルはディスク上に確かに存在しています。何が起きたのでしょうか?

OpenSSLはファイルをスキャンしましたが、-----BEGIN CERTIFICATE----- のようなヘッダーが見つかりませんでした。つまり、ファイルの形式が全く違うか、転送中に破損したか、エンコーディングのゴミが混入したか、あるいは設定ファイルが参照しているファイルとは別のファイルである可能性があります。

素早い診断

まずこの3つのチェックを実行してください——1分以内に原因を絞り込めます:

# 最初の行を確認 — -----BEGIN ... ----- ヘッダーであるべき
head -1 /etc/ssl/certs/server.crt

# ファイルのエンコーディングとBOMを確認
file /etc/ssl/certs/server.crt

# OpenSSLで解析を試みる
openssl x509 -in /etc/ssl/certs/server.crt -text -noout 2>&1 | head -5

head -1 がゴミのようなバイト列、空白行、または生のバイナリデータを出力した場合——それが答えです。

根本原因と対処法

原因1:ファイルがPEM形式ではなくDER形式になっている

DERは同じ証明書データをバイナリエンコードしたものです。認証局や Javaのkeytoolなどのツールは、デフォルトでDER形式で出力します。見分け方は簡単で、ファイルが-----BEGINではなくバイナリバイトで始まっています。.cer.derという拡張子も強いヒントになります。

DER形式かどうかを確認する:

file server.crt
# 出力例: server.crt: data  (バイナリ、ASCIIではない)

DERをPEMに変換する:

# 証明書の場合
openssl x509 -inform DER -in server.crt -out server.pem

# 秘密鍵の場合
openssl rsa -inform DER -in server.key -out server.key.pem

# PKCS#12バンドル(.p12 / .pfx)の場合
openssl pkcs12 -in bundle.p12 -out server.pem -nodes

原因2:ファイルにBOM(バイトオーダーマーク)が含まれている

メモ帳をはじめ多くのWindowsツールは、テキストファイル保存時にUTF-8 BOM(\xEF\xBB\xBF)を先頭に付加します。-----BEGINの前に3つの不可視バイトがあるだけで、OpenSSLのヘッダー検出が完全に機能しなくなります。

BOMを確認する:

xxd server.pem | head -1
# 次のような表示なら BOM あり: ef bb bf 2d 2d 2d 2d 2d  — ダッシュの前にBOMが存在

BOMを取り除く:

sed -i '1s/^\xEF\xBB\xBF//' server.pem

原因3:WindowsのCRLF改行コードが混入している

Windowsのツールで触れたファイルは、\nではなく\r\nの改行コードを持つことがよくあります。OpenSSLはUnix形式の改行コードを期待しており、余分なキャリッジリターンがあると正しく動作しません。

# キャリッジリターンを確認
cat -A server.pem | head -3
# 行末の ^M は \r が存在することを示す

# dos2unix で修正
dos2unix server.pem
# dos2unix がない場合:
sed -i 's/\r//' server.pem

原因4:ファイルが空、またはパスが間違っている

早めに除外しておく価値があります。scpの失敗、$CERT_PATH環境変数の設定ミス、設定ファイルのタイポによって、nginxがサイズゼロのファイルや存在しないファイルを参照してしまうことがあります。エラーメッセージは壊れた証明書の場合と全く同じです。

# ファイルサイズを確認
ls -lh server.pem
# 0バイト = 空のファイル

# アプリが実際に読み込んでいるファイルを確認
strace -e openat nginx -t 2>&1 | grep pem
# または設定ファイルを直接確認
nginx -T | grep ssl_certificate

原因5:PEMチェーンの一部しか含まれていない

多くの環境では、リーフ証明書と中間CA証明書を含むフルチェーンが必要です。コピーの途中で切れてしまったり、中間証明書が抜けていたりすると、破損したファイルと同じようにこのエラーが発生します。

# ファイルに含まれる証明書の数を確認
grep -c 'BEGIN CERTIFICATE' server.pem

# バンドル内の各証明書を確認
openssl crl2pkcs7 -nocrl -certfile server.pem | openssl pkcs7 -print_certs -text -noout | grep Subject

必要に応じてチェーンを再構築する:

cat server.crt intermediate.crt root.crt > fullchain.pem

原因6:秘密鍵と証明書のパスが入れ替わっている

nginxとApacheでは証明書と秘密鍵に別々のディレクティブを使います。パスを取り違えるのはよくあるミスですが、OpenSSLのエラーメッセージはその旨を教えてくれません。

# どちらのファイルかを確認
openssl x509 -in server.pem -noout -subject 2>/dev/null && echo "これは証明書です"
openssl rsa -in server.pem -noout -check 2>/dev/null && echo "これは秘密鍵です"

検証

修正を適用したら、サーバーを再起動する前にOpenSSLがファイルを正常に解析できるか確認してください:

# 証明書
openssl x509 -in server.pem -text -noout
# Subject:、Issuer:、Validity: などが表示されれば正常

# 秘密鍵
openssl rsa -in server.key -check
# RSA key ok と表示されれば正常

# 証明書と秘密鍵が対応しているか確認(モジュラスが一致している必要あり)
openssl x509 -noout -modulus -in server.pem | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5
# 両方の行が同じハッシュ値を出力する必要あり

# TLS設定全体をテスト(nginxの例)
nginx -t
# nginx: configuration file /etc/nginx/nginx.conf test is successful

恒久的な対策:証明書管理チェックリスト

  • 証明書ファイルの転送にはscpやシークレットマネージャーを使用する——ターミナルやテキストエディタを介したコピー&ペーストは避ける。
  • Windowsマシンで証明書を触った場合は、サーバーに移してすぐにdos2unixを実行する。
  • CAからエクスポートするたびにopenssl x509 -in file -noout -subjectで検証する——2秒で完了し、深夜のインシデントを防げる。
  • 証明書、秘密鍵、チェーンは別々のファイルに保管する。アプリが明示的に要求する場合のみフルチェーンにまとめる。
  • CI/CDでは、nginxやApacheをリロードする前にopenssl verifyを実行するステップを追加する。本番環境ではなく、パイプラインの段階で壊れた証明書を検出する。

Python / Node.js での状況

コードで証明書を直接読み込む際にも、アプリケーションログに同じエラーが現れます:

# Python: 同じOpenSSLエラーをラップした ssl.SSLError
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain('/path/to/server.pem', '/path/to/server.key')
# ファイルが不正な場合: ssl.SSLError: [SSL] PEM lib (_ssl.c:4045) がスロー

# Node.js: 同じOpenSSLが根底にある
https.createServer({
  cert: fs.readFileSync('/path/to/server.pem'),  // 不正な場合はスロー
  key: fs.readFileSync('/path/to/server.key'),
})

根本原因も対処法も同じです。まずopenssl x509 -in server.pem -noout -subjectをコマンドラインで実行してください——OpenSSLがコマンドラインで拒否するなら、アプリケーションでも同様に拒否されます。

Related Error Notes