無料のタイムスタンプ機関
電子帳簿保存法で一部のデータの保存要件となっているタイムスタンプ。
もともとタイムスタンプとはデータの日時を示す数値ですが、電子帳簿保存法の文脈では、特定の時点で「電子データ」が存在することを証明しかつその後の改ざんを検知するための電子署名を用いた技術を指します。
電子帳簿保存法に適合させるには国に認定されたタイムスタンプ機関のタイムスタンプが必要です。
筆者はこの要件で電子保存をあきらめました。
ただ、この件について調べている際に無料のタイムスタンプ機関があることを知ったので、電子帳簿保存には使えませんが、今回はそれについて書きます。
また自身でタイムスタンプサーバーを運用する場合についても書いています。
freeTSA.org
freeTSA.orgは、電子帳簿保存法の要件を満たすものではありませんが、タイムスタンプを無料で付与してくれる組織です。
ここで得られるタイムスタンプは、その日時にデータが存在したことを証明してくれるものです。
ファイルの中身を証明するものになりますので、タイムスタンプ取得後にファイルの内容に変更があった場合に変更後のファイルは証明の対象外となります。そのような場合には再度タイムスタンプを取得する必要があります。
原理は非ディジタル世界と似たような理屈で、第三者に事前にデータ(正確に言えばハッシュなのでデータそのものではありません)を開示して証人になってもらうというものです。
freeTSA.orgは電子署名という技術を通してデータが特定の時点で存在していたことを証明してくれる第三者となりますが、この組織が本当に万人に対して中立かつ信頼できるかどうかということは証明されていません。
国もこの組織を信頼することができないので、この組織のタイムスタンプは電子帳簿保存法には利用できないのです。
またあってはならないことですが、仮にfreeTSA.orgの秘密鍵(署名用の鍵)が漏洩した場合は、その鍵によって証明されていた内容はすべて無になります。
要するに、法的な効力はあまり望めないということです。
それでも、なにかしらのデータ保護には役に立つかもしれません。
タイムスタンプ要求の生成
freeTSA.orgにタイムスタンプを付けてもらうには、opensslで TSQ(Time Stamp Request)を発行し、curlでデータを送信します。
opensslやcurlはマルチプラットフォームのアプリでWindowsでも使えます。ただ、curl.exe はWindows10に同梱されていますが、opensslはインストールが必要なので筆者はDebian環境で試しました。
公式サイトを参考に、Debian11から次のようにタイムスタンプ要求を生成します。
bash
$ openssl ts -query -data target-file.txt -no_nonce -sha512 -cert -out target-file.tsq
これらのコマンドについて解説していきます。
- openssl ts
まず、TSQを生成します。これには openssl-ts を利用します。
- -query
-query オプションでクエリ(タイムスタンプのリクエスト)を生成します。
- -data
-data オプションでタイムスタンプが証明する対象のファイルを指定します。
- -no_nonce
-no_nonceはナンスを含めない設定です。これはリクエスト時にランダムな値を送信してリプレイアタック攻撃を防ぐものです。
ここではナンスの検証の方法を紹介する為にここでは-no_nonceをつけません(ナンスを発行します)が、公式ページではこのオプションを付与していたので紹介しています。
- -sha512
-sha512はハッシュアルゴリズムの選択で、sha512を利用するものす。このほかfreeTSA.orgではsha1,sha224,sha256,sha384が利用できるようです。
もし、対象外のアルゴリズムを使った場合は次で紹介するコマンドを実行時に、「 Message digest algorithm is not supported 」というエラーになります。
ちなみにopensslで利用できるアルゴリズムを取得するには「 openssl dgst -list 」とすればでてきます。
- -cert
-cert は応答に証明書を含めることを要求するものです。あとでも触れますが、署名したサーバーの証明書があった方が検証がしやすくなります。
- -out
-out でリクエストの保存先をファイルに指定しています。
コマンド実行時に指定されたアルゴリズムでファイルはハッシュ化され、署名はハッシュに対して行われます。生成されるtarget-file.tsqファイルはバイナリデータとなりますが、「 openssl ts -query -in target-file.tsq -text 」とすることでその中身を確認できます。
タイムスタンプ要求を送信
タイムスタンプ要求は、curlを使って送信します。
bash
$ curl -H "Content-Type: application/timestamp-query" --data-binary '@target-file.tsq' https://freetsa.org/tsr > target-file.tsr
こちらもコマンドを説明していきます。
- -H
-Hオプションで任意のヘッダを設定します。ここでは「 Content-Type: application/timestamp-query 」ヘッダを指定しています。
- --data-binary
データをバイナリで送信する設定になります。この後の@は続く文字列がファイル名であることを示しています。(@を付けないとその文字列は送信対象の文字列として扱われてしまいます)
@とファイル名をつけることで、先ほど生成した target-file.tsq ファイルの中身を curl で送信しています。
- https...
アドレスの部分はエンドポイントのURLです。https://freetsa.org/tsr となっています。
curlのレスポンスを、リダイレクトでファイルに保存しています。
タイムスタンプの確認
レスポンスで取得したタイムスタンプの中身を確認するには openssl-ts の -replyオプションを使います。
bash
$ openssl ts -reply -in target-file.tsr -text Using configuration from /usr/lib/ssl/openssl.cnf Status info: Status: Granted. Status description: unspecified Failure info: unspecified TST info: Version: 1 Policy OID: tsa_policy1 Hash Algorithm: sha256 Message data: 0000 - 27 c8 8d af 3d b5 ec 23-b1 6c 7c cf b5 bf 93 b9 '...=..#.l|..... 0010 - 2a 53 56 7c 85 3c ba 40-b7 91 40 72 68 7d 55 2f *SV|.<.@..@rh}U/ Serial number: 0x046954A4 Time stamp: May 14 11:21:03 2023 GMT Accuracy: unspecified Ordering: yes Nonce: 0x18839887331C4173 TSA: DirName:/O=Free TSA/OU=TSA/description=This certificate digitally signs documents and time stamp requests made using the freetsa.org online services/CN=www.freetsa.org/emailAddress=busilezas@gmail.com/L=Wuerzburg/C=DE/ST=Bayern Extensions:
Time stamp部分に署名日時がありこの時点で、target-file.txtが存在したことを証明しています。
Nonceにはナンスが出力されます。これを照合する運用ならば、署名要求を確認するコマンド「 openssl ts -query -in target-file.tsq -text 」で出力される値と一致するかを確認します。
検証
最後に、タイムスタンプとファイルを検証することで、ファイルが改ざんされていないかを確認できます。
これには openssl-ts を利用します。
bash
$ openssl ts -verify -data target-file.txt -in file.tsr -CAfile cacert.pem -untrusted tsa.crt
検証で「 Verification: OK 」となれば、先に確認した署名時刻にtarget-file.txtファイルが存在し、かつその時点からファイルが改ざんされていないことが、freetsa.orgにより証明されたことになります。
また次のようにすると、リクエストとレスポンス間での検証をします。
bash
$ openssl ts -verify -in target-file.tsr -queryfile target-file.tsq -CAfile cacert.pem -untrusted tsa.crt
この時は元のファイルとの検証は行われなませんが、リクエストを読み解いた際にハッシュ化アルゴリズムとハッシュ値があるので、それを使って元のファイルで再現できるか確認ができればレスポンスとの検証に変えることもできます。
-CAfileオプションについてですが、これにはpem形式のはルートCAの証明書ファイルのパスを渡します。ここではfreetsa.orgで配布している自己署名のRootCA証明書を利用します。
-untrusted オプションにはpem形式の中間証明書を渡します。この証明書を信頼するかどうかは上位の証明書に依存します。ここではfreetsa.orgのタイムスタンプ用中間CAの証明書を渡します。これも先のサイトで配布されています。
それぞれの証明書をWindowsにインストールするとつぎのように表示されます(クリックで拡大します)。
タイムスタンプのリクエスト時に-certオプションで証明書を含めると、tsa.crtに相当する証明書がレスポンスに含まれるので-untrastedオプションを付与しなくても検証は成功しますが、要求時に-certオプションを付けない場合は、検証時に-untrustedオプションでタイムスタンプ署名をした中間CAの証明書をチェーンの中に含めないと検証は成功しません。
ちなみに証明書の中身を確認するには、openssl-x509 を利用します。
bash
$ openssl x509 -in cacert.pem -textタイムスタンプの自己付与
今度はタイムスタンプ要求を自サーバーで処理してみたいと思います。
基本的には先ほど生成した target-file.tsqファイルをopenssl-ts の -replyオプションで署名するだけですが、その前にopensslの準備が必要です。証明書はバージョン3にしておく必要があるようです。
まず、自身の環境にROOT CAが無い場合は、それををつくります。
- 鍵の生成
ROOT CA用の鍵を生成します。本番運用時は特に漏洩に注意してください。
$ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out root-ca.keyopenssl-genpkeyは -algrithmでアルゴリズムを指定してキーを生成することができます。この時、-pkeyoptで生成時のオプションを付与することができます。ここではキー長さを2048ビットにしています。
また次のようにして鍵を生成することもできます。
$ openssl genrsa -out root-ca.key 2048 - 署名要求
通常は、鍵を生成したら署名要求を作成してCAに署名してもらいますが、ここでは自己署名なので次の方法で生成署名します。(一度署名要求を作ってからの自己署名する方法でもいいです)
$ openssl req -new -x509 -key root-ca.key -out root-ca.crt -days 365先のコマンドを作成すると、次のような項目の入力を求められます。これらは署名要求に自身の情報を設定するための項目で、-newオプションによるものです。
- Country Name
国を2桁で、日本なら JP です。
- State or Province Name (full name)
日本でいうなら都道府県です。
- Locality Name (eg, city)
市町村です。
- Organization Name
組織名(会社名)です。
- Organizational Unit Name (eg, section)
組織内の部署名です。
- Common Name (e.g. server FQDN or YOUR name) []:
ここが一番重要な値で、証明書をもつ端末を識別するための名前で、他者と重ならないように設定します。
ここではROOT CAとしました。
- Email Address
メールアドレスです。
- challenge password
署名要求と共におくる際に用いるパスワードです。今回は設定しませんので空白です。
- An optional company name
オプションの会社名です。サーバー管理会社当を書くようになっています。今回は空白にしますが、用途によってはこの値が必要なこともあります。
通常 /etc/ssl/openssl.cnf にopensslの設定がありますが、この[ req ] セクションに x509_extensions = v3_ca というエントリーがあり、さらに[ v3_ca ]セクションがデフォルトで記されています。
この効力で先のコマンドでROOT CAは証明書のバージョン3の形式で署名が行われます。
- Country Name
ROOT CAができたら、今度はタイムスタンプサーバーの証明書を作ります。
- 鍵の生成
今度はタイムススタンプサーバー用の鍵を作成します。
$ openssl genrsa -out ts.key 2048 - 署名要求の作成
タイムスタンプサーバーの署名要求を作成します。
$ openssl req -new -key ts.key -out ts.csrコマンド入力後に先ほどのRoot CAと同じ質問項目が表示されます。Common Nameだけ TIMESTAMP とし他は先と同じ設定にします。
この時、-reqextsオプションに/etc/ssl/openssl.confにあるセクション名を指定することで、それに従ったバージョン3用の署名要求を作成することができます。
デフォルト設定は次のようになっていました。
openssl.conf
... [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment ...
このままではタイムスタンプには使えないので、次のように別のセクションを作成します。
openssl.conf
... [ v3_ts ] basicConstraints = CA:FALSE keyUsage = critical, digitalSignature extendedKeyUsage=critical, timeStamping ...
basicConstraintsで基本的な設定をするのですが、ここではタイムスタンプサーバーはCAとして利用できないようにFALSEを指定しています。
keyUsageには鍵の使い道を記します。nonRepudiation は署名者が後から署名を否認できないようにする設定、digitalSignatureは署名利用、、keyEnciphermentは暗号化用に証明書を利用するためのフラグです。
criticalは列挙しているKeyUsageの項目が未知のものだった場合証明書を発行しないようにする設定です。
拡張の設定で、timeStampingを指定してタイムスタンプサーバとして利用できるようにします。
ちなみに署名要求での段階ではバージョン3の項目は必須ではありません。ここで指定しなくても(-reqextsを付与しなくても)署名時バージョン3で署名する事が可能です。
- 署名
署名します。署名はROOT CAによって行います。
バージョン3の拡張設定は、先ほど作成した v3_tsセクションの値を用いるので、-extfileに openssl.cnfを指定したあとでその中の v3_ts セクションを指定しています。 -extfileで指定するファイルは openssl.cnfである必要はりません。またセクションのないファイルを指定する場合は、-extensionsを指定する必要もありません。
$ openssl x509 -req -days 365 -in ts.csr -CA root-ca.crt -CAkey root-ca.key -out ts.crt -extfile /etc/ssl/openssl.cnf -extensions v3_ts -CAcreateserial
これでタイムスタンプを発行する用意ができました。
タイムスタンプの付与は openssl-tsの -replyオプションを使います。
タイムスタンプサーバーとしての opensslの設定は openssl.cnfの[ tsa ] で指定されている[ tsa_config1 ]セクションに記されています。
まずはこれを修正します。存在しない場合は、tsa_config1セクションを作成します。先ほど同様にコマンドラインオプションで参照先ファイルを変更できますので、openssl.cnfファイルに記述しなければいけないというものではありません。
/etc/ssl/openssl.cnf
... [ tsa_config1 ] dir = ./demoCA # TS管理ディレクトリ serial = $dir/tsaserial # 連番管理ファイルへのパス crypto_device = builtin # buildin指定で署名にopensslのライブラリを使います signer_cert = $dir/tsacert.pem # タイムスタンプサーバーの証明書 certs = $dir/cacert.pem # 返信に含める証明書(オプション) signer_key = $dir/private/tsakey.pem # タイムスタンプサーバーの鍵 signer_digest = sha256 # 署名アルゴリズム default_policy = tsa_policy1 # ポリシーは設定ファイルの別で指定されていて、OIDの形式で証明書とアルゴリズム等の設定をします。 other_policies = tsa_policy2, tsa_policy3 digests = sha1, sha256, sha384, sha512 # 受け入れ可能なアルゴリズム accuracy = secs:1, millisecs:500, microsecs:100 #タイムスタンプの精度 clock_precision_digits = 0 # 含める必要がある秒数の小数点部 最大6 ordering = yes tsa_name = yes # TSAの名前を含めるか ess_cert_id_chain = no ess_cert_id_alg = sha1 ...
これで、先ほど curlを使って外部に処理を依頼していたタイムスタンプの付与を自身で行うことができます。
設定ファイル中に存在する各種パラメータは、 -signer ts.crt、 -inkey ts.key、 -chain ts.crt、 といったオプションで上書きすることもできます。