はじめに
「公開鍵で暗号化、秘密鍵で復号」——この説明を聞いて、すぐにピンとくる人はどのくらいいるだろうか。
筆者自身、公開鍵・秘密鍵という言葉は知っていたが、「具体的に何が起きているのか」と聞かれると曖昧だった。抽象的な説明だけでは、どうしても腹落ちしない。
そこで本記事では、メールの偽物検知という身近な仕組みを題材にして、公開鍵・秘密鍵が実際にどう使われているかを追ってみる。
メールは「誰でも送信元を偽れる」
まず前提として、メールは手紙と同じで誰でも任意の送信元を名乗れる仕組みになっている。
From: ceo@your-company.com ← 嘘の送信元を書ける
To: employee@your-company.com
Subject: 至急振り込みお願いします
口座番号: XXXX-XXXX
これは仕様上の欠陥ではなく、メールが生まれた時代(1970年代)にはそもそも悪用が想定されていなかったため。
だから、受信サーバー(Gmail、Outlookなど)は届いたメールに対して 「本当にそのドメインの持ち主が送ったのか?」 を検証する。この検証の仕組みの1つが DKIM(DomainKeys Identified Mail) だ。
メール送信の全体像
まず、メールが届くまでの流れを押さえておく。
送信者 → 送信メールサーバー → インターネット → 受信メールサーバー → 受信者
(SMTPサーバー) (Gmail等)
受信メールサーバーは、メールを受信トレイに入れる前に「本物か?」を検証する。検証に失敗すると、迷惑メールフォルダに振り分けたり、拒否したりする。
DKIMはこの検証ステップで使われる。仕組みの核にあるのが、公開鍵・秘密鍵だ。
公開鍵・秘密鍵とは
ただの数字の列
「鍵」と言っても物理的な鍵ではない。非常に長い数字の列のことを指す。
秘密鍵の例(実際はもっと長い): 3082013b020100...a7f2
公開鍵の例: 3082012a030100...b4e1
この2つはペアで生成され、数学的に関連している。重要な性質は以下の通り:
- 秘密鍵で計算した結果は、対応する公開鍵でしか元に戻せない
- 公開鍵から秘密鍵を逆算することは(現実的な時間では)不可能
- 秘密鍵は持ち主だけが保管し、公開鍵は誰でも見られるように公開する
「署名」と「暗号化」— 2つの使い方
公開鍵・秘密鍵のペアには、2つの使い方がある。
| 用途 | やること | 目的 |
|---|---|---|
| 署名 | 秘密鍵で署名 → 公開鍵で検証 | 「本人が書いた」「改ざんされていない」を証明 |
| 暗号化 | 公開鍵で暗号化 → 秘密鍵で復号 | 「本人だけが読める」を実現 |
DKIMでは**「署名」の方だけ**を使う。暗号化は使わない。
ここが重要なポイントで、DKIMはメールを暗号化する仕組みではない。 メール本文は平文のまま送られる。DKIMが保証するのは「本人が送った」「途中で改ざんされていない」の2点だけだ。
DKIMの署名と検証を追う
ここが本記事の核心。実際にメール送信から検証までの流れを、具体的に追ってみる。
送信時: 署名を作る
Step 1: メール本文をハッシュ化する(要約を作る)
「申請が受理されました」 → ハッシュ関数(SHA-256) → "a3b8f1..."
※ ハッシュ関数 = 任意の長さのデータを固定長の数字に変換する関数
※ 同じ入力なら必ず同じ出力。1文字でも違えば全く異なる値になる
Step 2: ハッシュを秘密鍵で変換する(これが「署名」)
"a3b8f1..." + 秘密鍵 → "x9kf2m..."
送信サーバーは、この署名をメールのヘッダーに添付して送る。
実際に送られるメール:
┌──────────────────────────────────────┐
│ From: info@example.com │
│ To: user@gmail.com │
│ DKIM-Signature: x9kf2m... │ ← 署名(おまけとして添付)
│ │
│ 申請が受理されました │ ← 本文(平文のまま、誰でも読める)
└──────────────────────────────────────┘
繰り返しになるが、メール本文は暗号化されない。 署名はあくまで「おまけ」として添付されているだけだ。
受信時: 署名を検証する
受信サーバー(Gmailなど)は、3つの情報を使って検証する。
受信サーバーが持っている情報:
① 本文: 「申請が受理されました」(メールから取得)
② 署名: "x9kf2m..."(メールヘッダーから取得)
③ 公開鍵: DNSから取得(後述)
検証の手順:
A. 署名を公開鍵で逆変換 → "a3b8f1..." が出てくる
B. 本文を同じハッシュ関数にかける → "a3b8f1..." が出てくる
C. AとBを比較 → 一致すればOK
なぜこれで「本物」だと証明できるのか?
- 正しい署名を作れるのは、秘密鍵を持っている送信サーバーだけ
- 公開鍵で検証が通る = 対応する秘密鍵で署名された = 正規の送信サーバーから送られた
改ざんされたらどうなるか
攻撃者がメール本文を途中で書き換えた場合:
本文: 「申請が却下されました」 ← 改ざん
署名: "x9kf2m..." ← 元のまま(秘密鍵がないので作り直せない)
受信サーバーが検証:
A. 署名を公開鍵で逆変換 → "a3b8f1..."(元の本文のハッシュ)
B. 改ざん後の本文をハッシュ → "7c2d9e..."(異なる値)
C. 一致しない → NG、改ざんされている
偽メールを送ろうとしたら
攻撃者が偽メールを作る場合:
本文: 「口座番号を教えてください」
署名: ??? ← 秘密鍵がないので正しい署名を計算できない
適当な署名を付けても → 公開鍵で逆変換した結果が本文と一致しない → NG
公開鍵はどこにあるのか — DNSの役割
受信サーバーはどうやって公開鍵を手に入れるのか。答えは DNS(Domain Name System) だ。
メールヘッダーが教えてくれる
メールに添付されるDKIM署名のヘッダーには、以下の情報が含まれている。
DKIM-Signature:
a=rsa-sha256; ← 使ったハッシュ関数
d=hyogo-medical-shinsei.com; ← ドメイン
s=xxx; ← セレクタ(鍵の識別子)
...
受信サーバーはこれを見て、xxx._domainkey.hyogo-medical-shinsei.com というアドレスでDNSに問い合わせる。DNSがそのドメインの公開鍵を返す。
受信サーバー → DNS: 「xxx._domainkey.hyogo-medical-shinsei.com の情報をくれ」
DNS → 受信サーバー: 公開鍵の値を返す
DNSとは
DNSはドメインの公開設定台帳のようなもの。「このドメインのWebサーバーはどこ?」「メールの公開鍵は?」といった情報を、誰でも問い合わせできる。
DKIMでは、ドメインの管理者が事前にDNSへ公開鍵を登録しておく。受信サーバーはメールが届くたびにDNSを引いて公開鍵を取得し、署名を検証する。
AWSを使っている場合、DNSの管理は Route53、メール送信と署名は SES(Simple Email Service) が担当する。SESでドメインを登録するとDKIM用の署名鍵ペアが自動生成され、公開鍵をRoute53に登録する流れになる。
同じ仕組みが使われている場所
DKIMで見た「秘密鍵で署名 → 公開鍵で検証」の仕組みは、他の多くの場面でも使われている。
| 場面 | 何をしているか | 使い方 |
|---|---|---|
| SSH | サーバーへのログイン時に、秘密鍵で本人であることを証明 | 署名 |
| HTTPS | サーバー証明書が本物かを検証 + 通信内容の暗号化 | 署名 + 暗号化 |
| JWT | APIの認証トークンが改ざんされていないかを検証 | 署名 |
| Gitコミット署名 | コミットが本人によるものかを証明 | 署名 |
いずれも原理は同じだ。違いは「何に対して署名・暗号化するか」だけ。DKIMではメール本文のハッシュに署名していたが、SSHでは認証チャレンジに署名し、JWTではトークンのペイロードに署名する。
「署名」は本人確認と改ざん検知、「暗号化」は秘匿。目的が違うだけで、使っている道具は同じ公開鍵・秘密鍵のペアだ。
まとめ
- メールは誰でも送信元を偽れるので、受信サーバーが「本物か?」を検証する仕組みが必要
- DKIMは秘密鍵で署名 → 公開鍵で検証することで、送信元の正当性と改ざんの有無を確認する
- メールは暗号化されない。署名は「おまけ」として添付されるだけ
- 公開鍵はDNSに公開されており、受信サーバーがメールヘッダーの情報を元に取得する
- この仕組みはSSH、HTTPS、JWT、Git署名でも同じ原理で使われている
公開鍵・秘密鍵は抽象的に学ぶより、DKIMのような具体例で「誰が何を持っていて、いつ何を計算するのか」を追った方が理解しやすい。本記事がその一助になれば幸いだ。