PGP/MIME 入門
IIJ 技術研究所
山本和彦
作成:1998年9月27日
更新:2001年11月14日
もくじ

昔昔の PGP

テキスト・メールの機能をより豊かにする試みには、 大きな 2 つの流れがありました。 一方がプライバシの強化であり、 他方がマルチメディア化です。

マルチメディア化の成果は、 現在一般に普及した MIME(Multi-purpose Internet Mail Extensions) です。 MIME を使えば、 添付するデータ型を指定できますし、 またテキスト・データの文字コードを指示することも可能です。 さらに、マルチパートやメッセージというデータ型を使って、 電子メールの本文を再帰的に構造化できます。

プライバシの強化の例としては、 PEM(Privacy Enhanced Mail)が挙げられるでしょう。 PEM は、プライバシの保護とは何か、 また電子メールでのその実現に関する 知識を広げたという意味で、 暗号メールの記念碑だったといえるでしょう。 しかし、残念ながらベンダーの支持を受けられず、 普及しませんでした。

MIME や PEM は、IETF での標準化活動の成果であり、 RFC で規定されるプロトコル(通信規約)です。 これに対し、 PGP は Phil を始めとするプログラマが作成したプログラムでした。 (最近では PGP は OpenPGP と名を変えて IETF で標準化されています。) PGP は、ファイルの保護という機能ももっていますが、 ここでは電子メールに対するプライバシの保護機能に焦点を当てます。

PEM には使い始める際に認証局を探す必要があるという足枷があったのに対し、 PGP は知合い同士が公開鍵に署名し合うという方法をとっていました。 この使いやすさも手伝って、 PGP はインターネットでの実質的な標準となりました。

PGP では、暗号化した、あるいは、署名を施した データを電子メールを使って交換するために、 ASCII Armor という書式を提供しています。 以下がその例です。

	-----BEGIN PGP MESSAGE-----
	Version: 2.6.3i

	hIwDD2SKHJ5PdE0BBAC85K2TnTF8a1XoE0/628yCLCAzcM4uAhLuWj2Gwz3ODcxh
	Qlgsb7QazAeTlXv0kfk+e/SQ04r8AXtIEdg5MqSp3fC1EQzAbrZLLkDtYemR8ssu
	UMsK6vrAXiBj4kIHP6tz3uJ7twfLMPp3vwRURlzDwD6T5+U9BD2dTXJrH6F2qqYA
	AACmkSo1mD0rI9DCwpVp6hKHT767/tc0BnYHIL/d2Mun7u2z2pj5CDQyT7N10Iox
	GDdITQjN3YzcXbE82p1soI52aU0llaXFjg8IhMgJ30EzCfCC+FNxUD21pPJ+NB1z
	e0f5bbTggKIP4jEmKw1lu6vrrtSz8TwYv1wafPO69U2oNDcpp9BIP8e7OXcAreRw
	l3VWLI/c5iLyFycD/gqO18CjkKEX3EK25A==
	=BcFs
	-----END PGP MESSAGE-----
この書式は明らかに PEM の影響を受けていますし、Phil 自身もそう言っています。 それは、「はじめ」と「おわり」を示す文字列があること、 そしてバイナリ・データを ASCII 文字に変換する方式です。 後者は、PEM で開発された printable encoding と等価であり、 MIME では Base64 と呼ばれる符号化方式です。

暗号化したデータは PGP 無しでは読めないので、 このように符号化されても問題ありません。 しかし、テキストに署名したときは PGP を通さずとも読みたいものです。 そこで、PGP では「クリア署名」という書式を用意しています。 以下に例を示します。

	-----BEGIN PGP SIGNED MESSAGE-----

	このテキストに署名する。

	-----BEGIN PGP SIGNATURE-----
	Version: 2.6.3i
	Charset: noconv

	iQCVAwUBNg4y/Q9kihyeT3RNAQF2ygP+P0iEi+RXA3Aexk7OERT5q7S/G7czWb6l
	5pK+91zgeLeXz6NST6YQMAWdWS0+CUv4bstf+E8704KJju4cayMm4kMnD3HLLnRK
	t0OyJ/Ied+IT8SS6Lnh2xTZ8sgYm0Y41vbt3W3ILAePg9q4Gm11STF8s2//LTchD
	NlWvdihWh5k=
	=NjdE
	-----END PGP SIGNATURE-----
未だにこれらの書式を使っているメールリーダが数多くあります。 しかし、この書式には対象になったデータの型を指定できない、 テキスト・データの文字コードを指示できないというテキスト・メールと まったく同じ問題を抱えています。 PGP を MIME に統合した PGP/MIME が策定された現在、 このような昔ながらの書式は使用すべきではありません。

セキュリティ・マルチパートと PGP/MIME

MIME の普及以来、テキスト・データしか取り扱えない PEM が 時代遅れであることは、誰の目にも明らかでした。 そこで、PEM を電子メールの一般的な枠組である MIME に統合する試みが始まりました。

この作業の結果、以下に挙げる「4 つの分離」が重要であることが 分かりました。

まず、「プライバシ保護の枠組と具体的なサービスの分離」を説明しましょう。 プライバシを保護するためのサービスは、 PEM ばかりではありません。 そこで、他のサービスが同じことを繰り返さなくてもよいように、 サービスに依存しない共通部分を取り出して枠組として与えることは重要です。 この枠組は、セキュリティ・マルチパートと呼ばれ、 RFC 1847 で規定されています。

セキュリティ・マルチパートには、 Multipart/Signed と Multipart/Encrypted があります。 前者を署名に後者を暗号化の際に使います。 これが「暗号化と電子署名の分離」です。 それまでの PEM や PGP では、 署名と暗号化を同時に処理するサービスがありました。 具体的には署名を施した後に暗号化している訳ですが、 ユーザには一括作業に見えます。

このように順番が決めうちされていると、 暗号化後に署名するというサービスが実現できないなどの問題があります。 そこで、暗号化と署名は別々のサービスだと考え、 必要なら組み合わせて使うことにしました。

たとえば、テキスト・データを署名後暗号化することを考えましょう。 今までの PGP なら、PGP に同時にやってもらっていました。 セキュリティ・マルチパートでは、 まず Multipart/Signed という署名されたデータを作成します。 このとき PGP/MIME なら PGP を 1 回使います。 つぎに、Multipart/Signed 全体をデータと考えて暗号化し、 Multipart/Encrypted を使います。 このとき PGP/MIME なら PGP をもう 1 回使うことになります。 やや面倒ではありますが、 「暗号化と電子署名の分離」のおかげで サービスを柔軟に組み合わせることが可能になった訳です。

マルチパート・セキュリティという枠組の中で実現された PEM が MOSS(MIME Objects Security Service) です。 MOSS は PEM の後継者ではありますが、互換性はありません。 MOSS は 4 つの分離の重要性を示した点で画期的でしたが、 PEM と同様ベンダーやユーザの支持を受けられず広まりませんでした。

次に、Multipart/Signed で実現されている 「署名における対象データと署名の分離」について説明しましょう。 これまでの署名は、PGP のクリア署名に代表されるように、 データと署名がくっついていました。 さらに、「はじめ」と「おわり」という文字列が データ中に現れては困るので、 データ中に行頭に "-" があると "- -" に置き換えるなどの作業が必要でした。

この区切りを置換する作業は、 昔ながらの電子メールの転送と同じでです。 MIME では、 任意の区切りを許すことでこの区切りの置換の除去に成功しています。 署名サービスでも、この MIME の機能を使わない手はありません。

そこで、Multipart/Signed のマルチパートというデータ型が重要になります。 Multipart/Signed の第 1 パートには、 保護したいデータをそのまま格納します。 区切りが重ならないので、置換する必要はありません。 第 2 パートには、署名のみを入れます。 PGP では -b オプションを指定することで、 「分離署名」を生成できるのでこれを利用します。 以下に例を示します。

	Content-Type: Multipart/Signed;
		protocol="application/pgp-signature";
		micalg="pgp-md5";
		boundary="--Security_Multipart(Tue_Sep_29_10:20:30_1998_809)--"

	----Security_Multipart(Tue_Sep_29_10:20:30_1998_809)--
	Content-Type: Text/Plain; charset=iso-2022-jp
	Content-Transfer-Encoding: 7bit

	これが署名されるデータ。
	- 
	行頭が "-" で始まっていても置換されない。

	----Security_Multipart(Tue_Sep_29_10:20:30_1998_809)--
	Content-Type: Application/Pgp-Signature
	Content-Transfer-Encoding: 7bit

	-----BEGIN PGP SIGNATURE-----
	Version: PGPfreeware 5.0i for non-commercial use
	MessageID: 5bQPHdQNRi61FLyumfe40TGVJ1WJkZea

	iQCVAwUANhA14g9kihyeT3RNAQGZOgP/Yf97cX61XR7jT8S0ZY+jNb8qJUQQN1IF
	AK2IXoHO0vFq/zs9aJlXUKjHF2+62lONALI3xRWLtbKQ0kAptu7BiM5RRBVHZk2N
	sE10mRkSxITrwvlJ5zzZmJbxJFf/I26jgMVN7aeFppGkD0eKI2OuNQly6tjeA4/J
	MiF4Bpg21e4=
	=MMuY
	-----END PGP SIGNATURE-----

	----Security_Multipart(Tue_Sep_29_10:20:30_1998_809)----

Multipart/Signed の素晴らしい点は、互換性です。つまり、

余談ですが、PEM ではごちゃごちゃした署名がデータの前にあったので、 テキスト・メール用のメールリーダで読むととってもうっとうしかったのですが、 Multipart/Signed ではデータの方が先にあるので(あまり:-)うっとうしくありません。

このような Multipart/Signed の長所が認められ、 Multipart/Signed は PGP/MIME や S/MIME の署名サービスに利用されています。

最後に「暗号化における制御鍵と暗号文の分離」について考えましょう。 今までの暗号化されたデータには、 「対象となるデータが暗号化された暗号文」と 「それを復号化するための制御鍵」が 一緒にくっついていました。 よって、誰宛に暗号化されているのか分かっていました。 この 2 つを分離できれば、 誰宛の暗号化か分からなくする匿名暗号が実現できます。

Multipart/Encrypted では、 第1パートに制御鍵、第2パートに暗号文を入れます。 よって、第1パートと第2パートを別々に保存すれば、 匿名暗号になります。

しかし、PGP では暗号文と制御鍵を分離できません。 また今後もそのような機能が加わることはないそうです。 それにもかかわらず、PGP/MIME の暗号サービスは、 Multipart/Encrypted を使います。以下に例を示します。

	Content-Type: Multipart/Encrypted;
		protocol="application/pgp-encrypted";
		boundary="--Security_Multipart(Tue_Sep_29_10:49:32_1998_41)--"

	----Security_Multipart(Tue_Sep_29_10:49:32_1998_41)--
	Content-Type: Application/Pgp-Encrypted
	Content-Transfer-Encoding: 7bit

	Version: 1

	----Security_Multipart(Tue_Sep_29_10:49:32_1998_41)--
	Content-Type: Application/Octet-Stream
	Content-Transfer-Encoding: 7bit

	-----BEGIN PGP MESSAGE-----
	Version: PGPfreeware 5.0i for non-commercial use
	MessageID: roGirVqO8eQKO5c1F+18by9wfE8yM2fQ

	hQCMAw9kihyeT3RNAQP/cHxnmuP6kuNi00rBI51Kuc6Rs+XUxmyKjRkmwcggFsMB
	Zeawd2/M8A68VY4cDyql5tZw1UR61fPeKwY6qv0ciCbtGJRpjnqfotCax0HdZUl5
	nwOSwiyvcK5LfIBoL801C3q6q9Dh+t/LcgRXaYx4X5akLPR1ArMb/4VyOTrXraak
	kK1htSNxIsPnY2iM5LYtb510Tf9OoD9RE0MCsD/8YqsgPUEVRlR4tSyIeWNZE44t
	gM01N48ODy5I3EZ7APbka89JkGIwTf/0OYttX1sPc0KhphmOxVNqDinVBXyGacNu
	/s3TtnLcmo5aYuuD5hSy/J2Gqr6sjIUsE3x/zXgoaTXaOCGVf5zXZ6g5ku9VYKfd
	Qg==
	=8GHc
	-----END PGP MESSAGE-----

	----Security_Multipart(Tue_Sep_29_10:49:32_1998_41)----
このように第1パートはほとんど空っぽです。 この書式は冗長なので PGP/MIME で Multipart/Encrypted を使うのはやめようと 僕は提案しましたが、採用してもらえませんでした。 RFC 1847 の作者は 「冗長といっても 100 バイトぐらいだし、 また標準に合わせることは重要だ」と主張しました。

確かに、すべての暗号サービスが Multipart/Encrypted を使えば、 実装の際に「暗号化されていたので復号化しました」といったレポート機能の コードを共通化できます。 しかし、S/MIME は Multipart/Encrypted を採用しなかったので、 早くもこの主張にほころびが生じてしまいました。 (だからあれほど言ったのに...)

PGP/MIME を実装したメールリーダが増えた現在、 仕様を変更するのはよい考えとはいえません。 ですから、PGP/MIME の暗号サービスは冗長なままの書式で残るでしょう。 PGP/MIME は、PGP 2 (RFC 1991) に基づいています。 これの改訂版として、OpenPGP (RFC 2440) に基づいた RFC 3156 も公開されています。


日本語テキストの取り扱い

PGP/MIME を使って日本語テキストを暗号化したり、 署名を施したりする場合には、 まず行末が CRLF である ISO-2022-JP(JIS) に変換する必要があります。 この作業を正規化といいます。 以降ではなぜ正規化が必要なのか説明します。

最初に行末の正規化について考えてみましょう。 行末の表現方法は OS によってまちまちです。 以下に例を示します。

UNIX
LF
Macintosh
CR
Windows/MS-DOS
CRLF
このような状況でなんの合意もなしに PGP を使うと困ったことが起きます。 たとえば、UNIX で行末が LF であるテキスト・データに対し、 PGP を使ってクリア署名を生成したとしましょう。 これを Macintosh で受け取ると、 クリア署名の行末は CR に変換されています。 ですから、Macintosh でこのクリア署名を検証すると失敗します。 (PGP に -t オプションを指定してテキストであることを明示しておけば、 PGP 自身が行末を CRLF に変更するので署名の検証は成功します。 ただし、今議論しているのはそんな実装の話ではなく、 プロトコルの話です。)

そこで、テキスト・データを暗号化したり、署名を施す前に、 行末をある決められた文字列に変換する必要があります。 復号化や署名の検証の際には逆の変換が必要です。 この行末を表す文字列には、RFC 822 で定められた行末である CRLF を使うことになっています。

同様な問題を抱え、しかも厄介なのが日本語の文字コードです。 日本では、 ISO-2022-JP(JUNET コード、いわゆる JIS)、 Shift_JIS、EUC-Japan という 3 つの文字コードが主に利用されています。 ISO-2022-JP と EUC-Japan は主に UNIX で、 Shift_JIS は Windows/MS-DOS および Macintosh で使われています。

文字コードに関して、 何らかの取り決めがないと署名の検証に失敗する可能性があるのは明白です。 たとえば、UNIX で EUC-Japan に対して署名し、 Windows で Shift_JIS に変換されたクリア署名を検証すれば、 必ず検証は失敗します。

文字コードも行末と同様正規化する必要があります。 RFC 1847 では配送の際の文字コードに正規化することが決まっていますので、 ISO-2022-JP が正規型になります。 つまり、電子メールで PGP の日本語テキスト・データをやりとりするには、 どんな OS でもあらかじめ

必要があります。

ここまで注意してもまだ問題が起こる可能性があります。 最初の問題は、 ISO-2022-JP がいわゆる新 JIS、旧 JIS、ASCII、ローマンを 格納できることに起因しています。 配送システムによってはおせっかいにも、

それぞれ交換してしまいます。 新 JIS が旧 JIS に変更されても、 人間にとっては見ためはあんまり変わりませんが、 署名にとっては大問題です。

RFC 1847 では、 Multpart/Signed の内容は絶対に変更してはならないと規定されています。 RFC 1847 が規定される前に実装された配送システムがこの規約を 守らないのはいたしかたないのですが、 これからはこのようなお行儀の悪い配送システムをお行儀のよいものに 置き換えていく必要があります。

新 JIS と旧 JIS を変換してしまう配送システムはそれほど多くないので、 最初の問題はそんなに深刻ではありません。一方、次の問題は 頻繁に起こるのでとても深刻です。 それは、ISO-2022-JP が Shift-JIS や EUC-Japan より表現能力が 大きいことに起因しています。

たとえば、ISO-2022-JP で記述された日本語テキストには、 新 JIS と旧 JIS が混ざっていたとしましょう。 これに署名を施して電子メールで送ります。 Windows 系のメール配送システムがこの電子メールを受け取ると、 しばしば Shift_JIS に変換します。 Shift_JIS では新 JIS と旧 JIS を区別できないので、 新旧の情報が失われてしまいます。 署名の検証の際に、ISO-2022-JP に戻そうとしても、 すべて新 JIS かすべて旧 JIS のいずれかのテキストしか生成できません。 よって、署名の検証は失敗します。

そこで、最初の問題と同様、やっぱり配送システムが文字コードを 変換するのは罪悪だということが分かります。 そもそも、MIME では charset で文字コードを指定しているので、 勝手に文字コードを変えられるとメールリーダは困ります。

また、最初の問題とこの問題をあわせて考えると、安全のために、

	ISO-2022-JP では新 JIS と ASCII のみを使うようにしましょう
という結論が導かれます。

最後に結論をまとめます。


鍵の配送

PGP 5.x では、 鍵の生成時に公開鍵を 鍵配布サーバに登録しようとします。 HTTP を使ってこの鍵配布サーバにアクセスすれば、 必要な公開鍵が見付かるかもしれません。

一方、公開鍵をメールで送りたいことも多いでしょう。 この目的のために RFC 2015 では、 公開鍵の Content-Type: として application/pgp-keys を予約しています。

送信する場合は、PGP の鍵リングから公開鍵を取り出し、 application/pgp-keys というラベルを付けて送ります。 受信したメール内に、 application/pgp-keys というラベルがあれば、 ユーザに登録してもいいかを尋ねて登録の作業に移ればよいわけです。

application/pgp-keys というラベルが付けられてない、 つまり本文に直接公開鍵の ASCII Armor を張り付けられていた場合は、 手動で登録するしかありません。 また、application/pgp-keys を知らない MIME メールリーダは、 これを application/octet-stream と同様に取り扱う必要があります。 つまり、少なくともバイナリとしてファイルに保存できなければいけません。