署名に続いて暗号化と署名+暗号化も実装してみました。PGP/MIMEの署名+暗号化は、RFC3156によるとPGP/MIMEで署名したものをPGP/MIMEで暗号化する方法と、PGP的に署名+暗号化したものをMIME化する方法があるのですが、後者を採用している方が多そうなので後者にしてみました。
基本的にPGP(PGP/MIMEでないほう)で署名する時にはクリア署名するのが基本のようですが、送ろうと思えば普通に署名したものを送ることもできます。しかし受け取ったほうでは、それが署名されたものなのか暗号化されたものなのかぱっと見では分かりません。で、暗号化されている場合には自分の秘密鍵をロードするためにパスワードが必要なので先にユーザにパスワードをたずねるわけですが、実は暗号化されていなくて署名が付いているだけだとそのパスワードは実際には使われないという間抜けなことになってしまいます。
コールバック的にパスワードをユーザに要求できれば良いのですが、別プロセスで呼び出しているのでそれは難しいところ*1。そんなケースはほとんどないだろうしパスワードを入力するのが面倒ということくらいしか実害がなさそうなので、問答無用でパスワードを尋ねてしまうようにしてあります。
*1 GnuPGの場合には--status-fdを使って出力を解釈してやればできるのですがちと面倒
署名の検証を実装しようとして少しはまってしまいました。PGP/MIMEの場合分離された署名が署名用のパートに格納されているので、本文と分離された署名をGnuPGに渡してやれば良いのですが、別々に渡すためにはファイルを経由しなくてはいけません。できるならばファイルを経由したくないので、本文と分離されている署名を無理やりくっつけてクリア署名の形式にして渡してみたところ署名の検証で失敗します。
いろいろ調べて見たところ、クリア署名の場合には署名の種類がテキスト署名になっている必要があるようで、バイナリ署名された署名を無理やりくっつけてもダメなようです。さらに本文側はダッシュをエスケープしたり改行を正規化してやらなくてはいけません*1。あきらめて一時ファイルを使ってやるしかなさそうですね。
*1 そこまでやっても受け取ったメールがバイナリ署名されていたらどうしようもないです
上の署名の件ですが、正確にはバイナリで署名されているからだめということではなさそうです。
バイナリで署名したものを分離した場合には、元々のコンテンツはダッシュのエスケープや改行の正規化が行われない状態での署名です。これを無理やりクリア署名の形式にするときには、元のコンテンツを正規化する必要があります。ここで元のコンテンツと正規化されたコンテンツが異なることがあるため、検証に失敗します。
正規化しても変わらないコンテンツの場合、バイナリ署名したものを無理やりクリア署名の形式にしても正しく検証できました。
GnuPGでの分離署名でのテキストモードの話が、PGPとGnuPGの互換性で少し説明されています。このページを見ていたら、--openpgpオプションのナゾということでMDCの話がありました。GnuPGに渡すオプションをどうするかというのも問題ですね。
そこからたどって見つけた、Daa's BlogのPGP GnuPGカテゴリにもPGPネタが色々とありました。