2018年04月02日

住信SBIネット銀行からのメールの電子署名の検証に失敗する理由

(neo)muttでメールを読もうとするとエラーになる。

TL;DR
メールのフォーマットがRFC違反

エラー内容

Error reading S/MIME message
140714922093312:error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long:asn1_lib.c:157:
140714922093312:error:0D0D106E:asn1 encoding routines:B64_READ_ASN1:decode error:asn_mime.c:192:
140714922093312:error:0D0D40CC:asn1 encoding routines:SMIME_read_ASN1:asn1 sig parse error:asn_mime.c:490:
続けるには何かキーを...
証明書ファイルがない

過程

住信SBIネット銀行からのメールをmuttで読むと冒頭のエラーを吐く。 こういうのは問い合わせてもどうせ「対応していないソフトでは正しく表示されないことがあります」的な返事がくるだろうから自分で見てみる。

テスト用のコマンドを見つける

エラーメッセージで検索すると openssl smime もしくは後継となる openssl cms が出しているもののようだった。

$ openssl smime -verify -in sbi.eml
Error reading S/MIME message
140448899488512:error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long:asn1_lib.c:157:
140448899488512:error:0D0D106E:asn1 encoding routines:B64_READ_ASN1:decode error:asn_mime.c:192:
140448899488512:error:0D0D40CC:asn1 encoding routines:SMIME_read_ASN1:asn1 sig parse error:asn_mime.c:490:

確かに同じエラーが出た。

正しいメールと見比べる

同じ銀行系でも、例えば三井住友銀行からのメールは正しく検証できる。Eml形式なので目視で比較してみる。

  • 三井住友は本文1行目(boundaryより前)に “This is an S/MIME signed message” と入っている
  • 三井住友はヘッダが「MIME-Version」「Content-Type」の順で終わるのに対し、SBIは「MIME-Version」「Subject」「Content-Type」
  • 三井住友はマルチパートのうちBase64エンコードされた smime.p7s 部分が64文字で折り返されている。SBIはガタガタ(1行あたりの文字数が不規則で冒頭にスペースが入っていたりする)
  • ヘッダのクオートや空白など細かな差異
  • SBIのSubjectは複数行に渡っている

メールの内容をいじる

適当にエディタで開いて置換するなど。

  1. “This is an S/MIME signed message” を足してみる
  2. ヘッダの順番を三井住友と同じに並べかえる
  3. smime.p7s の整形 (空白を取り除いたり折り返し位置を調整したり)
  4. 改行・空白・クオートなどを合わせる
  5. MIMEに関係ないヘッダを(FromやSubjectも)ごっそり取り除く

すべて空振り。 ちなみに最後のヘッダの削除は三井住友のメールにも行い、そちらは問題なく処理できることを確認した。

署名部分が悪いのか?と思い、三井住友の smime.p7s を移植してみる。Base64エンコードされているのでエディタ間でコピーして貼り付けただけ。

$ openssl smime -in sbi.eml -verify
Content-Type: text/plain;charset=iso-2022-jp
Content-Transfer-Encoding: 7bit

(メールの本文)

Verification failure
139840071019264:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest failure:pk7_doit.c:1106:
139840071019264:error:21075069:PKCS7 routines:PKCS7_verify:signature failure:pk7_smime.c:400:

検証が行えるようになった。やっぱりSBIのsmime.p7sがおかしい?

署名部分を抽出してテスト

opensslのオプションを眺めると .p7s (PKCS#7ファイル)を扱うサブコマンドがあるので単独でテストしてみることにする。

Emlファイルの中で証明書のBase64部分だけテキストファイルに貼り付けてrubyでデコードしてみた。バイナリファイルができた。

opensslに通して確認。これがあっさりと読めてしまう。

ruby -r base64 -e 'File.write("sbi.p7s", Base64.decode64(STDIN.read))' < sbi-base64.txt
openssl pkcs7 -inform der -in sbi.p7s -print_certs -text

S/MIMEのままでは読めず、切り離すと読める。袋小路に入る。

  • openssl smime がデコードできない証明書なのか?
  • SBIの証明書の発行日が(三井住友と比べて)古いのでそれが原因か? ちなみに三井住友のは有効期限が1年。
  • Issuer の違い?

もういちどメールを見てみる

どうしようもなくなったのでまたEml形式に戻ってくる。

smime.p7s のデコードに失敗してるっぽいんだけどなあと思いながら一旦席を立って、戻ってきたときに不意に気づいた。Base64文字列に混じって ! がある。

確認するとBase64はやはり英数字と +-= だけ。(派生では ! を使うものもある) そして RFC2045 によると存在しない文字を使ってもデコード時にエラーにはならずただ無視される。Rubyの Base64.decode64 も RFC2045 対応。 つまり最初の手動での整形は不十分だったうえ、抽出してテストした方は黙ってエラー文字が取り除かれていたので気づかなかったと。

もう一度 smime.p7s を整形する。 ! 以外にもなにか含まれていると面倒なので万全を期すためデコード→エンコードを行う。

ruby -r base64 -e 'puts Base64.encode64(Base64.decode64(STDIN.read))' < sbi-base64.txt

ちなみに Base64.encode64 は60文字ごとに改行を追加するが RFC2045 ではあくまで最大76文字という規定なので特に問題はない。できあがったBase64文字列を sbi.eml の該当部分と置き換えて検証。

$ openssl smime -verify -in sbi.eml
Content-Type: text/plain;charset=iso-2022-jp
Content-Transfer-Encoding: 7bit

(メールの本文)

Verification successful

できた。

! が入る理由

だいたい予想はつくが…「mime exclamation」で検索すると「HTMLメールに!が入ってきて困る」という質問が。自己解決しているけど回答は、

When I try to send the email, I have a long string of html code. IN FACT, TOO LONG!

Exclamation Point in HTML Email

だよね。

長すぎる行は !\n で区切られているようなので復元して測ってみると2047・2047・366文字。上記の回答でもリンクされている RFC2822 では1行は998文字未満にしなければならない(MUST)とあるので、さて。

回避する方法

muttに署名検証に関する設定があれば、コマンド実行前にテキスト処理をして折り返しを修正すればなんとかなるのではないかと考えた。

マニュアルやデフォルトの設定ファイルを見ると smime_verify_command がそれっぽい。ところがこれが扱うのはmuttが分離したメッセージ本文と署名ファイルで、この段階では署名ファイルは正常なものができていた。(ここでmuttを通して添付ファイルを保存してもよかったと気づく。実際、保存した smime.p7s はBase64デコードしたものと同じだった) メーラーはある程度のRFC違反は許容するように作られているのだろう。

今回のエラーを出していたのはメールからX509形式の証明書を書き出す smime_get_signer_cert_command の方。 verify がメッセージが改ざんされていないことを確認するのに対し、こちらは証明書のSubjectとメールの発信元が一致しているか確認する(ために証明書を取得する)。 muttがPKCS7形式で証明書を分離できてるんだからそれをX509に変換したらいいのではないかと思うが、なにか理由があるんだろうか。

結局、電子署名で保護されてるメールをテキスト処理してもよくなかろうと思い(面倒になってきたし)、SBIからのメールについては署名の検証はあきらめることにした。

# muttrc
message-hook ~A 'set crypt_verify_sig=yes'
message-hook '~f post_master@netbk.co.jp' 'set crypt_verify_sig=no'


posted by かぷらす at 20:03| Comment(0) | 作業記録・備忘録 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください