[Mew-dist 16207] S/MIME patch

Ryutaroh Matsumoto ryutaroh at example.com
2001年 2月 7日 (水) 03:55:29 JST


まつもとです。

S/MIMEの署名の付加機能と検証機能をMew 1.94.2に追加するパッチを作ったので
報告します。対象が1.94.2なのは1.95がβテストの最終段階に入っているように
見えるのと私が使っているXEmacs 20.4がサポート対象外になって1.95が動かな
かったからです(後者が主な理由)。1.95の次のリリースのβテストが始まったら
そのβテスト向けにパッチを更新しようと思います。

このパッチの目的はVeriSignなどから取得したディジタルIDでNetscapeやMSのメー
ラーで検証できる電子署名を生成することと、NetscapeやMSのメーラーで生成さ
れた電子署名をMewで検証することです。


目次
1. 使い方
2. このパッチのMew本体への変更点
3. やり残していること
4. VeriSignがただでくれる2ヵ月だけ有効なディジタルIDを使う


1. 使い方
OpenSSL (http://www.openssl.org)の最新版をインストールして「openssl」コ
マンドをパスが通っているところに置いて下さい。

このパッチはVeriSignなどから取得したディジタルIDとtrusted CAのPEMファイ
ルが必要です。それらのファイルの作り方は4節で述べます。使い始める前に変
数mew-smime-digital-idにディジタルIDが入っているファイル名(4節の(1)のファ
イル)を設定し、変数mew-smime-CA-fileにtrusted CAのファイル名(4節の(2)の
ファイル)を設定して下さい。CAのファイルには全てのCAのcertificateを詰め込
みます。

Mewで署名と検証を行う前に、

openssl smime -sign -in ファイル1 -out ファイル2 -signer mew-smime-digital-id

というコマンドを実行してファイル2に署名されたメールっぽいファイルが出来
る事を確認してください。次に

openssl smime -verify -in ファイル2 -CAfile mew-smime-CA-file
として前に署名したファイルの署名を検証してみてください。これらのコマンド
がちゃんと実行できないとOpenSSLまたはディジタルIDなどのインストールに問
題があります。

mew-sendでメールを書いた後電子署名を付けるにはM-x mew-smime-sign-letter
とします。ディジタルIDのパスフレーズを聞かれるので入力してください。
送信するメールすべてにいつもS/MIMEで電子署名を付けるにはPGPと同じように
(setq mew-protect-privacy-always t)
(setq mew-protect-privacy-always-type 'smime-signature)
とします。

S/MIMEで電子署名されたメールが送られるとPGPのときと同様にX-Mewのところに
「S/MIME signature verification succeeded」か「S/MIME signature
verification failed」と表示されます。これはただopensslのexit valueが0か
それ以外かで判断しているだけです。

2. このパッチのMew本体への変更点
Mew 1.94.2はPGPの他にも署名/暗号方式を扱えるように最初から考えて作って
あるので、変更点はほとんど変数にしかるべき変更を加えただけです。ただ
mew-encode.elの中の関数mew-encode-multipart-signedは以下の仮定を置いてい
ます。

o 署名部分のContent-Typeがprotocolと同じ。ここでprotocolとは
  "application/x-pkcs7-signature" などです。
o 署名部分にはContent-Dispositionが存在しない。

これらの仮定はPGPの署名では正しいのですが、S/MIMEの署名では成り立たない
のでPGPの署名に影響しないようにmew-encode-multipart-signedに変更を加えて
います。

3. やり残していること

o 暗号化
	暗号化はあまり自分が使いたいと思わないのであまりやる気がありませ
	ん。要望があればやるかも知れません。

o 署名の検証
	検証結果を成功と失敗しか表示しないのは若干不便です。署名に入って
	いる送信者のディジタルIDのメールアドレスと名前くらい表示したいと
	思います。またCRLに対応してないですがこれはopensslが対応するのを
	期待しています。

o エラー処理
	が不十分だと思います。


その他

動作確認はFreeBSD 2.2.8, XEmacs 20.4で行いました。それ以外の環境でも動く
ように作ったつもりですが、動かなかったらご連絡下さい。

4. VeriSignがただでくれる2ヵ月だけ有効なディジタルIDを使う

opensslでは自分とCAのcertificateをPEM形式に変換する必要があります。
以下その方法を説明します。

以下の記述はWindows のOutlook Expressに特化した記述ですが、Machintoshと
かNetscapeでも同じような手順でできると思います。UNIX上でMewしか使ってな
かったりするとそもそもVeriSignからディジタルIDを取得できないから以下の記
述はその場合無関係です。

(0) (もしまだ持っていなければ) VeriSignなどからdigital IDを取得してくだ
    さい。digital IDを発行する認証局のリストが
http://www.microsoft.com/windows/ie_intl/ja/oe/certpage.asp
    にあります。一番下にあるThawte Certification はただでdigital IDを発
    行してくれます。

(1) ディジタルIDのPEM形式への変換
(1-1) digital IDを取り出してMewを動かしているコンピュータに持って行きます。
    VeriSignからNetscapeまたはInternet Explorer上のIDを取得した場合には
http://digitalid.verisign.co.jp/client/help/brw_operation.html#name1
    にIDの抽出方法が書いてあります。Outlook Express 5.5の場合には
    IDがPKCS #12形式で書き出されました。

(1-2) (1-1)で取り出したIDが"id.pfx"というファイルにあるとします。自分のディ
      ジタルIDをPEM形式に変換します。
openssl pkcs12 -in id.pfx -out id.pem

(2) CAのcertificateのPEM形式への変換
(2-1) root認証局のIDをOutlookなどから抽出します。Outlook Expressの場合
     「ツール」->「オプション」->「セキュリティ」->「ディジタルID」に行
     き信頼れさたルート証明機関をすべて選択した状態で「エクスポート」を選び
     ます。そうするとそれらのディジタルIDを含むファイルができます。中間
     証明機関も同様にファイルに書き出します。

(2-2) そのファイルを以下のようにPEM形式に変換します。
openssl pkcs7 -inform DER -outform PEM -print_certs -in CA.p7b -out CA.pem

(2-3) (2-2) でルート証明機関と中間証明機関について2つのPEMファイルが出
      来たはずなのでそれをUNIXのcatで連結します。

--
松本  隆太郎
-------------- next part --------------
diff -aurN mew-1.94.2/mew-decode.el mew-1.94.2-smime0/mew-decode.el
--- mew-1.94.2/mew-decode.el	Wed Dec 22 12:08:53 1999
+++ mew-1.94.2-smime0/mew-decode.el	Wed Feb  7 02:06:07 2001
@@ -24,7 +24,8 @@
   '(("application/pgp-encrypted" mew-pgp-decrypt mew-pgp-ver mew-prog-pgp)))
 
 (defvar mew-decode-multipart-signed-switch
-  '(("application/pgp-signature" mew-pgp-verify mew-pgp-ver mew-prog-pgp)))
+  '(("application/pgp-signature" mew-pgp-verify mew-pgp-ver mew-prog-pgp)
+    ("application/x-pkcs7-signature" mew-smime-verify mew-smime-ver mew-prog-smime)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
diff -aurN mew-1.94.2/mew-encode.el mew-1.94.2-smime0/mew-encode.el
--- mew-1.94.2/mew-encode.el	Fri Feb 18 14:47:17 2000
+++ mew-1.94.2-smime0/mew-encode.el	Tue Feb  6 22:17:08 2001
@@ -24,7 +24,8 @@
   '(("application/pgp-encrypted" . mew-pgp-encrypt)))
 
 (defvar mew-encode-multipart-signed-switch
-  '(("application/pgp-signature" . mew-pgp-sign)))
+  '(("application/pgp-signature" . mew-pgp-sign)
+    ("application/x-pkcs7-signature" . mew-smime-sign)))
 
 ;;;
 ;;; Making a header
@@ -519,6 +520,7 @@
 	      mew-ct-mls mew-temp-dir)))
     (setq file2 (nth 0 fmc) cte2 (nth 1 fmc) micalg (nth 2 fmc))
     (setq error (nth 3 fmc))
+    (setq ct2 (nth 4 fmc) cd2 (nth 5 fmc))
     (if error
 	(progn
 	  (if (file-exists-p file1) (delete-file file1))
@@ -538,7 +540,8 @@
       ;; After the sigend part
       (insert (format "\n--%s\n" boundary))
       (mew-encode-singlepart 
-       (mew-encode-syntax-single file2 (list protocol) cte2))
+       (mew-encode-syntax-single file2 (if ct2 ct2 (list protocol))
+        cte2 cd2))
       (insert (format "\n--%s--\n" boundary))
       ;; Throw away the garbage 
       (if (file-exists-p file1) (delete-file file1))
diff -aurN mew-1.94.2/mew-smime.el mew-1.94.2-smime0/mew-smime.el
--- mew-1.94.2/mew-smime.el	Thu Jan  1 09:00:00 1970
+++ mew-1.94.2-smime0/mew-smime.el	Wed Feb  7 02:43:07 2001
@@ -0,0 +1,91 @@
+; This file is based on mew-pgp.el in Mew 1.94.2, whose copyright also 
+; applies to this file. This file is originally written by Ryutaroh
+; Matsumoto <ryutaroh at example.com>, February 7, 2000.
+
+(provide 'mew-smime)
+
+; configuration variables
+(defvar mew-smime-digital-id "/home/lemon/ryutaroh/id.pem"
+"*Filename containing your digital ID in the PEM format.")
+(defvar mew-smime-CA-file "/home/lemon/ryutaroh/CA2.pem"
+"*Filename containing certificates of the trusted CAs, such as VeriSign.")
+
+
+; internal variables
+(defvar mew-smime-running nil)
+(defvar mew-smime-prompt-enter-pass   "Enter pass phrase: ")
+(defvar mew-smime-prompt-reenter-pass "Re-enter pass phrase: ")
+(defconst mew-smime-msg-enter-pass "Enter PEM pass phrase:")
+(defvar mew-smime-string nil)
+
+; The following variables are used only in the variable
+; mew-decode-multipart-signed-switch in mew-decode.el.
+(defvar mew-smime-ver 0)
+(defvar mew-prog-smime "openssl")
+
+
+(defun mew-smime-passphrase (&optional again)
+  (let ((prompt (if again
+		    mew-smime-prompt-reenter-pass
+		  mew-smime-prompt-enter-pass)))
+      (mew-input-passwd prompt)))
+
+
+(defun mew-smime-process-filter1 (process string)
+  ;; sign or decrypt, not verify
+  (setq mew-smime-string (concat mew-smime-string string))
+  (cond
+   ;; pass phrase for sign or decrypt
+   ((string-match mew-smime-msg-enter-pass string)
+    (process-send-string process (format "%s\n" (mew-smime-passphrase)))
+    (set-process-filter process nil))))
+
+
+(defun mew-smime-process-sentinel (process event)
+(setq mew-smime-running nil))
+
+(defun mew-smime-sign (file1)
+  (message "S/MIME signing ... ")
+  (setq mew-smime-running 'signing)
+  (let ((process-connection-type mew-connection-type2)
+	file2 process)
+    (setq file2 (mew-make-temp-name))
+    ;; not perfectly unique but OK
+    (setq process
+	  (start-process
+	   "S/MIME sign"
+	   nil
+	   "openssl"
+	   "smime" "-sign" "-in" file1 "-out" file2 "-outform" "DER" "-signer" mew-smime-digital-id))
+    (mew-set-process-cs process mew-cs-autoconv mew-cs-dummy)
+    (set-process-filter process 'mew-smime-process-filter1)
+    (set-process-sentinel process 'mew-smime-process-sentinel)
+    ;; Wait for the termination of OpenSSL.
+    ;; Emacs doesn't provide synchronize mechanism with
+    ;; an asynchronous process. So, take this way. 
+    (while mew-smime-running
+	(if mew-xemacs-p
+	    (accept-process-output)
+	  (sit-for 1)
+	  ;; accept-process-output or sleep-for is not enough
+	  (discard-input)))
+    (message "S/MIME signing ... done")
+    (list file2 mew-b64 "sha1" nil
+     (list "application/x-pkcs7-signature" (list "name" "smime.p7s"))
+     (list "attachment" (list "filename" "smime.p7s"))))) ;; return
+
+
+
+(defun mew-smime-sign-letter ()
+  "Sign the entire draft with S/MIME. Input your passphrase."
+  (interactive)
+  (mew-draft-make-message 'smime-signature))
+
+
+(defun mew-smime-verify (file1 file2)
+  (message "PGP verifying ... ")
+  (if (= 0 (call-process "openssl" nil nil nil
+    "smime" "-verify" "-inform" "DER" "-in" file2 "-content" file1
+    "-CAfile" mew-smime-CA-file))
+  "S/MIME signature verification succeeded"
+  "S/MIME signature verification failed"))
diff -aurN mew-1.94.2/mew-vars.el mew-1.94.2-smime0/mew-vars.el
--- mew-1.94.2/mew-vars.el	Thu Jan 13 15:23:23 2000
+++ mew-1.94.2-smime0/mew-vars.el	Tue Feb  6 22:17:12 2001
@@ -77,6 +77,7 @@
 (defconst mew-ct-apo "Application/Octet-Stream")
 (defconst mew-ct-pgs "application/pgp-signature") ;; due to the RFC 1847 bug
 (defconst mew-ct-pge "application/pgp-encrypted") ;; due to the RFC 1847 bug
+(defconst mew-ct-sms "application/x-pkcs7-signature")
 (defconst mew-ct-apk "Application/Pgp-Keys")
 
 (defconst mew-us-ascii "us-ascii")
@@ -1414,6 +1415,7 @@
 
 (defvar mew-privacy-database
   (list
+   (list 'smime-signature (list (list mew-ct-mls mew-ct-sms)) "SS")
    (list 'pgp-signature  (list (list mew-ct-mls mew-ct-pgs)) "PS")
    (list 'pgp-encryption (list (list mew-ct-mle mew-ct-pge)) "PE")
    (list 'pgp-signature-encryption
diff -aurN mew-1.94.2/mew.el mew-1.94.2-smime0/mew.el
--- mew-1.94.2/mew.el	Mon Feb 28 12:52:41 2000
+++ mew-1.94.2-smime0/mew.el	Tue Feb  6 22:15:22 2001
@@ -684,6 +684,7 @@
 (require 'mew-mark)
 (require 'mew-header)
 (require 'mew-pgp)
+(require 'mew-smime)
 (require 'mew-bq)
 (require 'mew-syntax)
 (require 'mew-scan)
-------------- next part --------------
テキスト形式以外の添付ファイルを保管しました...
ファイル名: 無し
型:         application/x-pkcs7-signature
サイズ:     1800 バイト
説明:       attachment; filename="smime.p7s"
URL:        <http://www.mew.org/pipermail/mew-dist/attachments/20010207/e033122d/attachment.bin>


Mew-dist メーリングリストの案内