DKIM 公開鍵(DKIM Public Key)
概要
DKIM 公開鍵(DKIM Public Key) は、DKIM 署名を検証するために DNS の TXT レコードとして公開される暗号鍵です。RFC 6376 の Section 3.6.1 で定義されており、送信サーバーが秘密鍵で生成した署名を、受信サーバーがこの公開鍵で検証します。
公開鍵は セレクタ._domainkey.ドメイン の TXT レコードに格納されます。たとえば Google Workspace でセレクタが google、ドメインが example.com の場合、google._domainkey.example.com の TXT レコードに公開鍵が設定されます。受信サーバーはメールの DKIM-Signature ヘッダーから s=(セレクタ)と d=(ドメイン)を読み取り、この FQDN で DNS を引いて公開鍵を取得します。
DKIM の信頼性は公開鍵の鍵長とアルゴリズムに直接依存します。RFC 8301(2018年)は署名者に対して 2048 ビット以上の RSA 鍵の使用を要求(MUST)し、1024 ビット鍵での署名生成を明確に禁止しました。受信側は 1024 ビット鍵の検証をまだ受け入れなければなりませんが、新規発行は 2048 ビット以上が必須です。
仕組み
DKIM 公開鍵の DNS レコード構造、対応する暗号アルゴリズムと鍵長、受信サーバーが署名を検証するフロー、および t= フラグの動作を説明します。
公開鍵レコードの構造
DKIM 公開鍵の TXT レコードは、セミコロン区切りのタグ=値ペアで構成されます。
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
各タグの意味は以下の通りです。
| タグ | 必須 | 説明 |
|---|---|---|
v= | 推奨 | バージョン識別子。値は DKIM1 のみ。省略可能だが、他のTXTレコードと区別するために記述が推奨される |
k= | 任意 | 鍵の種別。省略時は rsa。RFC 8463 で ed25519 が追加された |
p= | 必須 | Base64 エンコードされた公開鍵データ。空文字(p=)の場合はその鍵の失効を示す |
h= | 任意 | 受け入れ可能なハッシュアルゴリズム。省略時はすべてのアルゴリズムを許可 |
t= | 任意 | フラグ。t=y はテストモード、t=s はドメイン完全一致を要求 |
s= | 任意 | サービスタイプ。省略時はすべてのサービス(*)。s=email でメール専用に限定 |
n= | 任意 | 管理者向けの注釈。検証には使用されない |
p= タグが公開鍵レコードの中核です。この値が正しくなければ、レコードが存在しても署名検証は必ず失敗します。
鍵のアルゴリズムと鍵長
DKIM で使用できるアルゴリズムは 2 種類あります。
RSA(k=rsa) は最も広くサポートされている方式です。RFC 8301 が定める要件は次の通りです。
- 署名者は 2048 ビット以上の鍵を使用しなければならない(MUST)
- 検証者は 1024 ビット〜4096 ビットの鍵を検証できなければならない(MUST)
- 512 ビット以下の鍵は検証を拒否しなければならない(MUST)
RSA 2048 ビット鍵の Base64 エンコード後のサイズは約 400 文字です。DNS の TXT レコードは 1 つの文字列が 255 バイトに制限されているため、2048 ビット鍵は複数の文字列に分割して格納されます。DNS プロトコルはこれらの文字列を結合して 1 つの値として扱います。
Ed25519(k=ed25519) は RFC 8463(2018年)で追加されました。RSA と比較して鍵が短く(公開鍵は 32 バイト、Base64 で 44 文字)、署名生成・検証ともに高速です。ただし受信側のサポートが不完全なため、現時点では RSA とのデュアル署名(同一メールに RSA と Ed25519 の 2 つの DKIM-Signature を付与)が推奨されます。
検証フロー
受信サーバーが公開鍵を使って DKIM 署名を検証するフローは次の通りです。
- メールの
DKIM-Signatureヘッダーからs=(セレクタ)、d=(ドメイン)、a=(アルゴリズム)、b=(署名値)を読み取る セレクタ._domainkey.ドメインの TXT レコードを DNS に問い合わせる- TXT レコードの
p=タグから公開鍵を取得する k=タグのアルゴリズム(省略時はrsa)がa=タグと一致するか確認するDKIM-Signatureのh=タグに指定されたヘッダーと本文を正規化し、ハッシュを算出する- 公開鍵で
b=の署名値を復号し、算出したハッシュと一致すればdkim=pass
p= タグが空文字の場合、ステップ 3 で「鍵が失効している」と判断され、dkim=fail になります。これは鍵ローテーション後に旧セレクタを無効化するための仕組みです。
t= フラグの動作
t=y(テストモード)が設定されている場合、受信サーバーはその DKIM 署名の検証結果をメール配送の判断に使わないことが推奨されます。新しい鍵を本番で有効化する前に、署名が正しく生成されているかを確認するために使います。テスト完了後は t=y を削除します。
t=s が設定されている場合、DKIM-Signature の i=(署名者識別子)のドメイン部分が d= のドメインと完全に一致しなければなりません。サブドメインからの署名は拒否されます。
設定例
OpenSSL を使った RSA・Ed25519 鍵ペアの生成手順と、主要サービスごとの公開鍵登録方式を示します。
RSA 2048 ビット鍵の生成
OpenSSL で DKIM 用の鍵ペアを生成する手順です。
openssl genrsa -out dkim-private.pem 2048
openssl rsa -in dkim-private.pem -pubout -outform DER | openssl base64 -A
2 番目のコマンドで出力される Base64 文字列が p= タグの値になります。
生成した公開鍵を DNS に登録する場合、TXT レコードは次のようになります。
selector._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
2048 ビット鍵は 1 つの TXT 文字列の 255 バイト制限を超えるため、DNS プロバイダーによっては自動的に分割されます。手動で設定する場合は、次のように複数の引用符付き文字列として記述します。
selector._domainkey.example.com. IN TXT ("v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
"残りの公開鍵データ...")
Ed25519 鍵の生成
Ed25519 の鍵ペアは OpenSSL 1.1.1 以降で生成できます。
openssl genpkey -algorithm ed25519 -out dkim-ed25519-private.pem
openssl pkey -in dkim-ed25519-private.pem -pubout -outform DER | tail -c 32 | openssl base64 -A
Ed25519 の公開鍵は 44 文字程度と短いため、255 バイト制限に収まります。
ed25519._domainkey.example.com. IN TXT "v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="
主要サービスの公開鍵形式
サービスごとに公開鍵の設定方法が異なります。
| サービス | 鍵長 | 設定方式 | セレクタ例 |
|---|---|---|---|
| Google Workspace | 2048 ビット | TXT レコード直接登録 | google |
| SendGrid | 2048 ビット | CNAME(SendGrid 側が鍵を管理) | s1, s2 |
| Amazon SES | 2048 ビット | CNAME(AWS 側が鍵を管理) | selector1〜selector3 |
| Microsoft 365 | 2048 ビット | CNAME | selector1, selector2 |
CNAME 方式のサービスでは、公開鍵の実体はサービス提供者の DNS に置かれます。ドメイン所有者が直接鍵を管理する必要がなく、サービス側が鍵ローテーションを自動で行えるという利点があります。
# SendGrid の CNAME 方式
s1._domainkey.example.com. IN CNAME s1.domainkey.uXXXX.wl.sendgrid.net.
確認方法
DKIM 公開鍵が正しく DNS に公開されているかは dig で確認できます。
dig TXT google._domainkey.example.com +short
"v=DKIM1; k=rsa; p=MIIBIjANBgkqh..."
レスポンスに v=DKIM1 で始まるレコードが含まれていれば、公開鍵が設定されています。p= タグの値が空でないこと、送信サービスの管理画面に表示される公開鍵と一致することを確認します。
鍵長を確認するには、p= タグの Base64 文字列をデコードしてバイト数を調べます。
echo "MIIBIjANBgkqh..." | openssl base64 -d | wc -c
出力が 270 バイト前後であれば 2048 ビット鍵、162 バイト前後であれば 1024 ビット鍵です。
CNAME で設定されているサービスの場合は、CNAME の参照先を確認してから TXT を引きます。
dig CNAME s1._domainkey.example.com +short
dig TXT s1._domainkey.example.com +short
外部の視点からも確認したい場合は、Labee Dev Toolbox の Mail Auth API を使うと、外部の視点から見た結果を取得できます。
curl "https://labee.dev/api/mail-auth?domain=example.com&selector=google"
{
"success": true,
"data": {
"spf": { "record": "v=spf1 include:_spf.google.com ~all", "exists": true },
"dkim": {
"record": "v=DKIM1; k=rsa; p=MIIBIjANBgkqh...",
"exists": true,
"selector": "google"
},
"dmarc": { "record": "v=DMARC1; p=reject; rua=mailto:dmarc@example.com", "exists": true },
"bimi": { "record": null, "exists": false }
},
"error": null,
"meta": { "responseTime": 123 }
}
レスポンスの data.dkim.exists が true であれば、指定したセレクタの DKIM 公開鍵が外部から参照可能な状態です。data.dkim.record に TXT レコードの内容がそのまま返ります。data.dkim.selector にはリクエストで指定したセレクタ名が格納されます。selector パラメーターを省略した場合、デフォルト値として default が使われます。
よくある問題
DKIM 公開鍵の設定ではコピーミスや鍵長の陳腐化など、レコードの内容に起因するトラブルが発生しやすいです。
公開鍵のコピーミス
RSA 2048 ビットの公開鍵は Base64 で約 400 文字あります。管理画面からコピーする際に先頭や末尾が欠けると、Base64 としてデコードできず dkim=fail になります。DNS に登録した後、dig で取得した p= タグの値と管理画面の値を文字数レベルで比較します。
CNAME 方式のサービスでは公開鍵のコピーミスは起きませんが、CNAME 先のホスト名を間違えると同様にレコードが見つかりません。
1024 ビット鍵のまま運用している
RFC 8301 は 2018 年に 2048 ビット以上の鍵を必須としました。1024 ビット鍵は受信側で検証自体は行われるものの、Gmail は 2048 ビット未満の鍵で署名されたメールに対して警告を出します。古いサービスや設定をそのまま使い続けている場合、管理画面で 2048 ビット鍵を再生成し、DNS レコードを更新します。
TXT レコードの文字列分割の問題
2048 ビット RSA 鍵の公開鍵は DNS TXT レコードの 255 バイト制限を超えます。DNS プロバイダーの管理画面によっては、自動分割に対応していないものがあります。その場合、手動で 255 バイト以内の複数の引用符付き文字列に分割して登録する必要があります。分割位置が Base64 エンコードの途中であっても問題ありません。DNS はこれらの文字列を結合して解釈します。
p= タグが空で鍵が失効している
p= タグの値が空(v=DKIM1; k=rsa; p=)のレコードは、鍵の失効を意味します。鍵ローテーション後に旧セレクタを無効化する正規の方法ですが、現在使用中のセレクタでこの状態になっていると、すべてのメールが dkim=fail になります。dig でレコードを確認し、p= タグに公開鍵データが含まれていることを確認します。
テストモード(t=y)を外し忘れている
DKIM 公開鍵レコードに t=y が残っていると、受信サーバーによっては検証結果を信頼性の判定に使わない場合があります。DMARC との組み合わせでは、t=y の署名は DMARC の判定に使われないこともあります。テストが完了したら t=y を削除し、本番モードに切り替えます。