Help us understand the problem. What is going on with this article?

最近の電子署名事情ってどうよ

はじめに

テレワークのご時世、問題の一つは「印鑑」です。
押印のためだけに物理出勤なんかしてらんねーよ!
ということで…

2020年、たぶん使える電子署名3つ。

プロトコル・フォーマット 対象 トラストチェーン アイデンティティ 公開鍵配布方法
メール S/MIME 電子メール SSLと同じ CAに出した内容 最初にメール, DNS SMIMEA
gpg PGP any Web of Trust, DNS 自分で入れた内容 鍵サーバ, DNS CERT
マイナンバーカード署名PDF adbe.pkcs7.detached, SHA256withRSA PDF 日本国政府JPKI X509v3 Subject Alternative Nameに住所・氏名・性別・生年月日 (マンナンバーは見えません) ルート署名書はgo.jpから持ってくる

メール (S/MIME)

かつてはCoMoDoで無料で個人S/MIME証明書というのがありましたが、今は売り物となっています。
一応無料で証明書を出してくれるサービスもまだあることはありますが鍵ペアの生成が相手側サーバ上で行われるタイプなので、どこまで安全かというと疑問は残ります。

Certificates for secure electronic mail | Actalis S.p.A.

上記を順番にやっていくと、pfxという名前のpkcs12が届きます。
あとはmacOSのキーチェーンやiOSのプロファイルに突っ込めば標準のメーラで使用できるようになりますし、firefoxなどでも簡単に入ります。

後述のgpgと同じようにDNSの上に公開鍵をいれる方法もあるのですが、UDPのDNSSECにいれるにはデカイ等の課題があります(このRFCにはTCPを使え(SHOULD)と書いてあります。53/tcpを塞いでいるところって結構あるんですよね)。

最大の課題は、対応しているメールクライアントが無いんですね...

RFC 8162 - Using Secure DNS to Associate Certificates with Domain Names for S/MIME
global end-to-end dnssec secured email

試してみる場合、SMIMEA に記載の方法でやることができます。
例えばこのサイトのcheckme at ends2ends.comさんの場合はこんな感じです。

$ dig +dnssec -t type53  784fec98da493ee792a4e9ccfa72fe9e61f7eb0261bb279ecb29a401._smimecert.ends2ends.com @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10150
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 512
;; QUESTION SECTION:
;784fec98da493ee792a4e9ccfa72fe9e61f7eb0261bb279ecb29a401._smimecert.ends2ends.com. IN SMIMEA

;; ANSWER SECTION:
784fec98da493ee792a4e9ccfa72fe9e61f7eb0261bb279ecb29a401._smimecert.ends2ends.com. 299 IN SMIMEA 3 0 0 308203DF30820384A00302010202011C300A06082A8648CE3D040302
305D3117301506035504030C0E5874634E20534D494D454120434131
(中略)
46022100D612B0100A676DF52899007C7928626CA2916C65C9F38851 
4DB51D8708367B15022100E631E51A941EEDB80C6F24C13875DBBF89 
F231D80AFF1EAACD73A2C2438FFF74

784fec98da493ee792a4e9ccfa72fe9e61f7eb0261bb279ecb29a401._smimecert.ends2ends.com. 299 IN RRSIG SMIMEA 13 4 300 20200705223108 
20200703213108 13918 ends2ends.com. rRqPYPpe6bdAEuLMbsgNxiWRQEciuKpmS/29Jl5hCKT9NZ9wAUdPxis6 
LwbCuSDxlePf+udB+eoC6lNgH+kBbw==

;; Query time: 13 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)

自分のドメインに貼り付けてdig type53 なんとか._smimecert.example.com @8.8.8.8や上記検証サイトを通ることを確認した後、rdns querylogしつつ、このアドレスにメールを送る操作をいくつかのMUAで行ったのですか、type53のqueryは来ませんでした😂

gpg

みんな大好きgpg!

wotやら使い方の話は今更なのでここでは割愛して、自分のDNSサーバに鍵を入れる話です。
今日、公開鍵サーバに公開鍵を送るというのは個人情報やSPAMの問題が大きいですのでDNSの上でやります。
言うまでもありませんが、DNSSECは有効にしましょう。

参考: PGP and DNS

DNSレコード長の問題が発生しにくい方法であるhttps併用でいきます。

wkd

gpg --with-wkd-hash -k ${自分のメールアドレス}

とやると、ハッシュ値@ドメイン名が表示されますので、公開鍵(asc)を

https://ドメイン名/.well-known/openpgpgkey/hu/ハッシュ

におきます。
公開鍵をとってきたい人は

$ gpg --auto-key-locate wkd --locate-keys ${メールアドレス}

とすればgpgが勝手に公開鍵をhttpsでとってきてくれるという塩梅です。

pka

こちらはDNSのレコード上に、公開鍵のhttpsの場所を載せる方法です。
DNSには鍵そのものは載りませんので、大きな鍵でも大丈夫ですし、よそのhttpsの上に載せる事もできます。

PGP and DNS にあるipgp.shを実行します。

$ ./ipgp.sh ${鍵ID} ${https上においた鍵のURL}

とすると、DNSにいれるCERTレコードが出てくるのでDNSにはりつけます。

ハッシュ値._pka.ドメイン名. 3600 IN CERT 6 0 0 うんぬんかんぬん

もちろん、上記のwkdの場所をそのまま入れても動きます。

公開鍵をとってくる方法は

$ gpg --auto-key-locate pka --locate-keys ${メールアドレス}

です。

マイナンバーカードで署名したPDF

建前としては最強!となるはずです。
このスマートカードには二種類の鍵ペアがありまして、以下は本名が漏れるタイプの鍵ペア(署名用証明書)を使います。
利用者認証用のほうではありません。

以下macOSを想定しますが、linuxでも同じです。

PINについて

以下で利用するのは「6桁〜16桁」の電子署名を行うためのPINです。
5回間違えると出頭です。

パスワードの失念 | 公的個人認証サービス ポータルサイト

ちなみに

アルファベットは大文字のみ使うことができます。 小文字で入力された場合は、大文字に変換されます。

という謎仕様です。
mynaはこの仕様にしたがってtoupper(3)してくれますが、当然ながらOpenSCは勝手にtoupper(3)したりはしませんので大文字で入れましょう。
小文字で入れると「mynaでは動くのにOpenSCのpkcs11経由で動かない」という現象が発生します。

カードリーダとマイナンバーカード

PCSCを話してくれるカードリーダを持ってきてマイナンバーカードをセットします。
Amazonで1000円くらいです。
本記事で唯一の金銭出費...

mynaで証明書を確認

まず自分の証明書を確認します。

jpki/myna: マイナンバーカード・ユーティリティ・JPKI署名ツール

mynaをいれて

$ myna jpki cert sign -p ${署名PIN} -f pem > my.crt

として証明書を作り、

$ openssl x509 -in my.crt -text -noout

で確認します。

しかし、肝心の日本語(utf8)表記の本名や住所は、

 X509v3 Subject Alternative Name:
                othername:<unsupported>, othername:<unsupported>, othername:<unsupported>, othername:<unsupported>, othername:<unsupported>, othername:<unsupported>

となってしまって見えません。
普通のCNはシリアル番号らしき数値で、Lには市町村名がローマ字表記で入っています。
SANの中身を見るには、

$ openssl asn1parse -in my.crt

とするとhexで出ます。
たとえば、

  613:d=4  hl=3 l= 229 cons: SEQUENCE
  616:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Subject Alternative Name
  621:d=5  hl=3 l= 221 prim: OCTET STRING      [HEX] DEADBEAF01234…

となればoffsetが621とのことなので

$ openssl asn1parse -in my.crt -strparse 621

とすると、

   0:d=0  hl=3 l= 218 cons: SEQUENCE
    3:d=1  hl=2 l=  31 cons: cont [ 0 ]
    5:d=2  hl=2 l=  10 prim: OBJECT            :1.2.392.200149.8.5.5.1
   17:d=2  hl=2 l=  17 cons: cont [ 0 ]
   19:d=3  hl=2 l=  15 prim: UTF8STRING        :${お名前}
   36:d=1  hl=2 l=  25 cons: cont [ 0 ]
   38:d=2  hl=2 l=  10 prim: OBJECT            :1.2.392.200149.8.5.5.4
   50:d=2  hl=2 l=  11 cons: cont [ 0 ]
   52:d=3  hl=2 l=   9 prim: UTF8STRING        :${生年月日}
   63:d=1  hl=2 l=  17 cons: cont [ 0 ]
   65:d=2  hl=2 l=  10 prim: OBJECT            :1.2.392.200149.8.5.5.3
   77:d=2  hl=2 l=   3 cons: cont [ 0 ]
   79:d=3  hl=2 l=   1 prim: UTF8STRING        :${性別}
   82:d=1  hl=2 l=  76 cons: cont [ 0 ]
   84:d=2  hl=2 l=  10 prim: OBJECT            :1.2.392.200149.8.5.5.5
   96:d=2  hl=2 l=  62 cons: cont [ 0 ]
   98:d=3  hl=2 l=  60 prim: UTF8STRING        :${住所}

と出てきます。

jsignpdfによる署名

OpenSCを入れた後、

JSignPdf download | SourceForge.net

を使います。
macの場合

$ cd /Applications/JSignPdf.app/Contents/Resources
$ echo "pkcs11config.path=conf/pkcs11.cfg” >> conf/conf.properties
$ echo -e "name=JPKI-SIGN\nlibrary=/Library/OpenSC/lib/opensc-pkcs11.so\nslot=1” >> conf/pkcs11.cfg
$ java -Djava.security.debug=sunpkcs11 -jar Java/JSignPdf.jar -lkt
(PKCS11が出てることを確認する)
$ java -Djava.security.debug=sunpkcs11 -jar Java/JSignPdf.jar -kst PKCS11 -ksp ${署名PIN} -lk

とします。
出力が

DEBUG Relaxing SSL security.
DEBUG Registering SunPKCS11 provider from configuration in conf/pkcs11.cfg
SunPKCS11 loading /Applications/JSignPdf.app/Contents/Resources/conf/pkcs11.cfg
sunpkcs11: Initializing PKCS#11 library /Library/OpenSC/lib/opensc-pkcs11.so
Information for provider SunPKCS11-JPKI-SIGN
Library info:
中略
DEBUG SunPKCS11 provider registered with name SunPKCS11-JPKI-SIGN
INFO  Getting keystore type instance: PKCS11
sunpkcs11: login succeeded
INFO  Getting key alias
INFO  Key aliases in the keystore:
Digital Signature Certificate
DEBUG Removing security provider with name SunPKCS11-JPKI-SIGN

となれば成功です。
最後から2行目が使える鍵の名前ですね。

エラーが出ているときはmynaで確認をしておきましょう。
PINリトライ回数が減っているのに気づかずにやっていると、役所出頭になります。

$ myna pin status
券面事項PIN(A):    のこり10回
券面事項PIN(B):    のこり10回
入力補助PIN:    のこり 3回
入力補助PIN(A):    のこり10回
入力補助PIN(B):    のこり10回
JPKI認証用PIN:    のこり 3回
JPKI署名用PIN:    のこり 5回

0になるとロックです。

うまく行かない時は、上記のpkcs11.confpkcs11-spy.soにして、

$ env PKCS11SPY=/Library/OpenSC/lib/opensc-pkcs11.so PKCS11SPY_OUTPUT=logfile java -Djava.security.debug=sunpkcs11 -jar Java/JSignPdf.jar ...

とやるとデバック情報を得ることが出来ます。

PDFへの署名は、

$ java -jar Java/JSignPdf.jar -kst PKCS11 -ksp ${署名PIN} ${署名したいpdf} -d ${出力ディレクトリ}

で、${出力ディレクトリ}/${もとのPDFの名前}_sigined.pdfの名前で署名されたpdfが出来ます。

FreeTSAによる時刻の証明をつける場合は、

-ts https://freetsa.org/tsr -tsh SHA-512

も追加です。

参考: FreeTSA - How to sign documents with time stamp > JSignPdf

検証

そもそもJPKIのCA証明書は普通のOSの/etc/ssl/certs/にはありませんので、自分で追加します。

acrobat

以下を参照してください(丸投げ!)。

マイナンバーカードを使ってPDFに電子署名をしよう!

公証人に認証された電子署名済みの電磁的記録を検証する - Qiita

PDF の電子署名(certificate, digitally sign)の仕組みと電子締結運用のメモ - Qiita

poppler

macの場合はhomebrewで入ります。

$ pdfsig ${署名済みpdf}
Digital Signature Info of: ${署名済みpdf}
Signature #1:
  - Signer Certificate Common Name: シリアル番号
  - Signer full Distinguished Name: CN=シリアル番号,L=市町村名,L=都道府県名,C=JP
  - Signing Time: Jul 01 2020 00:00:00
  - Signing Hash Algorithm: SHA1
  - Signature Type: adbe.pkcs7.detached
  - Signed Ranges: [0 - 160], [30162 - 762262]
  - Total document signed
  - Signature Validation: Signature is Valid.
  - Certificate Validation: Certificate issuer is unknown.

popplerはnssを使っているので、

certutil -A -t c,c,c -i my.crt -n JPKI -d ${適当なディレクトリ}
certutil -A -t c,c,c -i ${JPKIのCA証明書} -n JPKIROOT -d ${適当なディレクトリ}

${適当なディレクトリ}/cert9.dbに証明書を登録できますが、登録したところでSANのutf8までは表示してくれません。
最後の部分が

  - Certificate Validation: Certificate is Trusted.

になるだけです。

そのた

pdfの電子署名の中身はpkcs7なので、頑張ってpdfから取り出せばあとは煮るなり焼くなりできるはずです...が、もう疲れたので今後の課題とします(笑)。

参考:
php - How to extract and verify PDF signature (PKCS7) with openssl? - Stack Overflow
m32/endesive: en-crypt, de-crypt, si-gn, ve-rify - smime, pdf, xades and plain files in pure python

結論

メールはS/MIMEで署名しましょう。PPAPしてくるヤツを見かけたら殺しましょう。

普通のファイルはgpgで署名しましょう。ただし公開鍵を鍵サーバにあげてはいけません。自分のhttpsとdnssecの上においておいて、相手にはgpg --auto-key-locateでとってもらうようにお願いしましょう。

ガチで本名で署名したいときは、PDFにマイナンバーカードで署名しましょう。
PDFを受け取った相手が「なんか不明な署名がついている」と言い出したら、本記事のURLを告げて、それで面倒だと言い出したら「名前をSANにしか入れない上にCA証明書を広めていない日本国政府が悪い」といっておきましょう(笑)。

では皆様、よいテレワーク電子署名ライフを!

mtakatou
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away