0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PGP暗号化メールを一括で復号する

Last updated at Posted at 2020-09-19

背景

とある事情でEnigMail(ThunderBird)のユーザーだった。
が、先日、PGP暗号化メールのdecrypted copyを一括で作ることになった。
単一復号化は、メールを右クリックして「Decrypt to folder」で行えるが、一括復号化は、メールフィルタの「If OpenPGP encrypted, create decrypted copy」で行わねばならない。しかし、私の環境ではなぜかフィルタがうごかなかった。
ところで、右クリックでの復号に微細な不具合が見受けられたので、修正を試みた。それが https://gitlab.com/enigmail/enigmail/-/merge_requests/47 である。その過程で https://gitlab.com/enigmail/enigmail/blob/enigmail-2.1-branch/package/persistentCrypto.jsm を調査していたのだが、実は復号処理は簡単なのではないかと思ったので実装を試みた。

方法

言語

最近最も触っている言語であり、またメールパーサーが標準ライブラリに存在することもあり、Pythonを選択1

IMAP

EnigMailはThunderBirdの機能拡張だが、本ツールはスタンドアローンで動作する都合上、IMAPアクセスが必須となる。これは https://docs.python.org/ja/3/library/imaplib.html#imap4-example をほぼ丸写しで良い。

メール

復号エンジン

デファクトスタンダードとして、GnuPGとした。GnuPGは外部プログラムなので、subprocessモジュールを必要とする。ところが、subprocess.Popenにはコンテキストマネージャがない。そこで、(コード)ブロック中に例外発生時にはプロセスを停止させるようなPopenラッパーを作成した。

通常の暗号化

本文をgpg --decrypt --skip-verifyに流せば良い(検証結果に関わらず復号はされてほしいので--skip-verifyは必須である)。マルチパートの場合は1パートごとに。ただし、Content-Transfer-Encodingが指定されている場合は事前に処理する必要がある。base64.b64decodeやquopri.decodestringを用いる。
(中身が始めからエンコードされている場合あるいは)中身にマルチバイト文字が含まれる場合はエンコードする必要がある。このエンコードはEnigMailにならいBase64を72文字ごとに改行する実装としたが、末端のみ72文字未満でも改行する必要がある(A)。
添付ファイルのファイル名取り出しだが、PGPには、ファイル名を暗号化ファイルに埋め込む機能があるらしい(?)。GnuPGは--enable-special-filenamesなるオプションで、-&Nなるファイル名が埋め込まれることがあるので、この形式のファイル名が来た場合は無視する必要がある(B)。ただし今回作成したツールでは埋め込みファイル名ではなく常にContent-Disposition filenameを使うのでこの影響はない。
なお、この(A)および(B)がEnigMailの「微細な不具合」の内容である。

PGP/MIME

MIME partの1番目がバージョン情報(常に1)、2番目が実体メール(ただしヘッダは含まない)となっている。よって、「実体メール」にヘッダをコピーして返すようにすれば良い。

件名暗号化

PGP/MIMEは実体メールにヘッダは含まないと書いたが、EnigMailの件名暗号化は実体メールにsubjectヘッダを入力することで実装されている。よって、実体メールにヘッダをコピーする際、すでに存在しているヘッダは単にコピーしないようにすれば良い。

結果

https://github.com/cielavenir/imap_mass_decrypter/blob/master/imap_decrypter.py により、無事に一括復号化することができた(pypiは利用していない)。しかもメールフィルタはフィルタを掛けるフォルダを1つずつ指定する必要があるが、こちらであれば全フォルダを一度に対象にできる。

  1. Rubyだとmail gemとかを使うことになると思いますが、標準ライブラリではない

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?