本文へスキップ
ecdsa.com

PEM と DER の比較|同じ証明書のテキスト形式とバイナリ形式

DER は、証明書や鍵の定義に使われる ASN.1 のバイナリエンコーディングです。PEM はその DER をそのまま base64 でエンコードし、BEGIN 行と END 行で挟んだものです。変換しても中身は何も変わりません。

PEM と DER の選択は、異なるデータのどちらかを選ぶという話ではありません。証明書、鍵、CSR、CRL は常に DER でエンコードされた ASN.1 として定義されており、PEM はまさにそのバイト列をテキストで包んだものです。問題は、ツール、プロトコル、プラットフォームがどちらの表現を想定しているか、そしてそれぞれをどう見分け、どう変換するかです。

比較表

DERPEM
定義ASN.1 の識別符号化規則(ITU-T X.690):バイナリのタグ・長さ・値RFC 7468 のテキスト:DER の base64 をラベルで挟んだもの
先頭証明書と鍵ではバイト 0x30(SEQUENCE)-----BEGIN CERTIFICATE-----、-----BEGIN PRIVATE KEY----- など
サイズ(P-256 証明書、実測)389 バイト583 バイト(約 4/3 倍に改行とラベルが加わる)
1 ファイルあたりのオブジェクト数1 つブロックをいくつでも(チェーン、鍵と証明書)
自己記述性ラベルなし。種類は文脈から判断ラベルが構造の名前を示す
一般的な拡張子.der、.cer、.crt(どちらの形式にも使われる).pem、.crt、.cer、.key、.csr(どちらの形式にも使われる)
フィンガープリントこのバイト列の SHA-256同じ値。フィンガープリントは中の DER から計算される
メール、YAML、環境変数での安全な受け渡し不可可

DER と PEM とは

DER は、ASN.1 データの正規のバイナリエンコーディングです。すべての値はタグ、長さ、内容で構成され、1 つの値に対して有効なエンコーディングはちょうど 1 つしかありません。この正規性があるからこそ署名が機能します。証明書の署名は tbsCertificate の DER バイト列を対象としており、検証者は何も再エンコードしません。

PEM は、このバイト列を標準の base64 でエンコードし(慣例では 1 行 64 文字)、構造の名前を示す BEGIN 行と END 行で挟みます。PEM という名称は 1993年の Privacy-Enhanced Mail(プライバシー強化メール)規格に由来し、PKIX オブジェクト向けの形式は RFC 7468 で規定されています。構造の名前には CERTIFICATE、X509 CRL、CERTIFICATE REQUEST、PUBLIC KEY、PRIVATE KEY、ENCRYPTED PRIVATE KEY のほか、OpenSSL の旧来の形式である RSA PRIVATE KEY と EC PRIVATE KEY があります。ブロックの前後や間にあるテキストは無視されます。コメント付きのバンドルや openssl x509 -text の出力がそのまま使えるのはこのためです。

変換と確認

次のスクリプトは OpenSSL 3.6.5 で実行したもので、使い捨ての証明書を作成し、2 つのエンコーディングが同じオブジェクトを保持していることを示します。

OpenSSL:1 つの証明書を PEM と DER で
#!/bin/sh
dir=$(mktemp -d)
cd "$dir" || exit 1
openssl req -new -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout key.pem \
  -subj "/CN=example.com" -days 30 -out cert.pem 2>/dev/null
openssl x509 -in cert.pem -outform DER -out cert.der
printf 'cert.pem %s bytes, cert.der %s bytes\n' "$(wc -c < cert.pem | tr -d ' ')" "$(wc -c < cert.der | tr -d ' ')"
head -1 cert.pem
openssl x509 -in cert.der -inform DER -noout -fingerprint -sha256 | cut -c1-40
openssl x509 -in cert.pem -noout -fingerprint -sha256 | cut -c1-40
cd / && rm -rf "$dir"
出力
cert.pem 583 bytes, cert.der 389 bytes
-----BEGIN CERTIFICATE-----
sha256 Fingerprint=FE:8B:DC:19:30:EE:A8:
sha256 Fingerprint=FE:8B:DC:19:30:EE:A8:

鍵(openssl pkey -in key.pem -outform DER)、CSR(openssl req -outform DER)、CRL(openssl crl -outform DER)にも同じ方法が使えます。形式の分からないファイルは、-----BEGIN で始まっていれば PEM、xxd file | head -1 の出力の先頭付近に 3082 があれば、ほぼ確実に DER です。

よくあるエラー

  • OpenSSL の no start line / Expecting: TRUSTED CERTIFICATE:ツールは PEM を想定していたのに、DER、またはラベルの異なる PEM ブロックを受け取りました。-inform DER を付けるか、ラベルを確認してください。
  • header too long / wrong tag:ツールは DER を想定していたのに、PEM のテキスト、または途中で切れたファイルを受け取りました。
  • Web フォームや YAML に貼り付けた PEM ブロック内の Windows の改行コードや余分なスペース:厳格なパーサーはこれを拒否します。手で編集せず、エンコードし直してください。
  • 証明書が 1 つだけ想定される場所での PEM バンドル:多くのツールは最初のブロックしか読まないため、順序が重要です(サーバーではリーフ証明書を先頭にします)。
  • Base64url と base64 の取り違え:PEM は +、/ とパディングを含む標準のアルファベットを使い、JOSE と WebAuthn は base64url を使います。JWK の x5c メンバーからコピーした証明書は標準の base64 でエンコードされた DER なので、PEM の行で挟めば有効な PEM ファイルになります。

ASN.1 エクスプローラーはどちらのエンコーディングも受け付けて構造を表示し、PEM バンドル分割ツールは複数の証明書を含むファイルを分割します。

バンドルとチェーン

DER には 1 つのオブジェクトしか入らないため、1 つのファイルに複数のオブジェクトを入れる必要がある場合は、PEM の連結かコンテナー形式を使います。サーバーの fullchain.pem はリーフ証明書の後に中間証明書を続けたもので、/etc/ssl/certs/ca-certificates.crt のような CA バンドルは、数百個のルート証明書を 1 つの PEM ファイルにまとめたものです。DER ベースの代替手段は、PKCS #7 の証明書バンドル(.p7b。コンテンツを持たず証明書だけを含む CMS の SignedData で、Windows でよく使われます)と、秘密鍵をチェーンと一緒に運ぶ場合の PKCS #12(.pfx)です。.p7b を PEM に変換するには openssl pkcs7 -print_certs -in bundle.p7b を使います。このコマンドは各証明書を個別の PEM ブロックとして書き出します。

エンコーディングよりも重要なのは、順序と、必要な証明書が揃っていることです。サーバーは、リーフ証明書を先頭に、クライアントが信頼するルートまでたどるのに必要なすべての中間証明書を送らなければなりません。中間証明書が欠けていると、ファイル形式にかかわらず、それを自分で取得しないクライアントは接続に失敗します。

どちらを選ぶべきか

  • Web サーバー、リバースプロキシ、ACME クライアント:PEM(fullchain.pem、privkey.pem)を使います。
  • Java(keytool -importcert、CertificateFactory):どちらも使えますが、正規の入力は DER です。鍵とチェーンをまとめる場合、キーストアには PKCS #12 を使います。
  • Windows での証明書のインポート:DER の .cer か、PEM でエンコードされた .cer を使います。鍵と証明書をまとめる場合は .pfx に入れます。
  • ソースコード、環境変数、Kubernetes のシークレットへの埋め込み:PEM のテキストを使います(フィールドが求める場合は DER の base64)。
  • 通信プロトコルとバイナリコンテナー:常に DER です。TLS、CMS、OCSP、X.509 拡張が PEM を運ぶことはありません。
  • アーカイブと監査:バイト単位で正確に保存するなら DER、人がファイルを確認する必要があるなら PEM を使います。

移行時の注意点

暗号の観点では、移行すべきものは何もありません。エンコーディングの切り替えは、デプロイ上の細部にすぎません。

  1. ファイル名を統一する:PEM には .pem、DER には .der を使い、自分たちのツールでは形式があいまいな .crt と .cer を避けてください。
  2. デプロイ時に変換する:リポジトリやシークレットストアには、正本を 1 つ(通常は PEM)だけ保管します。
  3. 変換後にフィンガープリントを確認する:openssl x509 -noout -fingerprint -sha256 で確認します。変換前後の値は一致しなければなりません。
  4. 秘密鍵は暗号化したままにする:openssl pkey -outform DER は、暗号方式を指定しない限り、暗号化されていない鍵を書き出します。また、DER の鍵はツールがあまり警告を出さずに扱うことが多い形式です。
  5. 想定する形式を設定に明記する:たとえば ssl_certificate は PEM を、Java のトラストストアは DER または PKCS #12 を想定します。形式に関するエラーの大半は、形式を推測することから生じます。

参考資料

すべての比較