PKCS#8秘密鍵ファイルのMIMEタイプ検出を追加
Marcelに application/pkcs8 のMIMEタイプ検出が追加されました。マジックバイトによるコンテンツ判定と .p8 拡張子のマッピングが定義され、PKCS#8形式の秘密鍵ファイルを正確に識別できるようになります。
背景
Issue #90 で報告されていたように、Marcelは .p8 ファイルに対して正しいMIMEタイプを返せていませんでした。Marcel::MimeType.for を呼び出すと "application/x-x509-key;format=pem" が返され、application/pkcs8 としては認識されていませんでした。
この問題は active_storage_validations のようなバリデーションgemでの利用に影響していました。content_type: "application/pkcs8" による検証が通らず、.p8 ファイルを適切にバリデーションする手段がない状態でした。PKCS#8はRSAやECDSAなどの秘密鍵を格納するための標準フォーマットであり、特にApple Push NotificationサービスやJWT署名など、Railsアプリケーションで扱う機会の多い鍵形式です。
技術的な変更
lib/marcel/mime_type/definitions.rb に1行の定義が追加され、PKCS#8のMIMEタイプが登録されました。
変更後:
Marcel::MimeType.extend "application/pkcs8", magic: [[0, '-----BEGIN PRIVATE KEY-----']], extensions: %w( p8 )
検出の仕組みとして、ファイル先頭(オフセット 0)にある -----BEGIN PRIVATE KEY----- という文字列をマジックバイトとして使用しています。これはPEM形式でエンコードされたPKCS#8秘密鍵のヘッダーであり、この文字列が存在する場合に application/pkcs8 と判定されます。
あわせて test/fixtures/magic/application/pkcs8/pkcs8_key.p8 というフィクスチャファイルが追加されており、RSAキーのPEM形式サンプルを用いた検出テストが構成されています。
設計判断
マジックバイトによるコンテンツ検出 と 拡張子マッピング を組み合わせる既存パターンが踏襲されています。同ファイル内では -----BEGIN CERTIFICATE----- を用いた application/x-x509-ca-cert の定義が存在しており、今回のPKCS#8定義はこれと同じアプローチをとっています。
PEM形式のヘッダー文字列はBase64エンコードされた本体と明確に区別できるため、誤検出のリスクが低く、シンプルな文字列マッチで信頼性の高い判定が実現できます。一方で、DER形式(バイナリ)のPKCS#8ファイルはこのマジックバイトを持たないため、今回の変更では検出対象外となります。
なお、PR本文ではPKCS#12(.p12)への対応も検討されましたが、PKCS#12はバイナリ形式であり同様の手法が適用できないため、今回のスコープからは除外されています。
まとめ
1行の定義追加により、PEM形式のPKCS#8秘密鍵ファイルがMarcelで正確に識別されるようになりました。既存のMIMEタイプ定義パターンを踏襲したシンプルな変更でありながら、Active Storageを利用するアプリケーションで .p8 ファイルのコンテンツタイプバリデーションが正常に機能するようになります。