この記事の目的は、簡単に実装できる方法で、文書を透過的に署名・検証する方法を紹介することです。
このアプローチの明らかな利点は、文書を再び開いたときに、それまでの間に変更されていないことを確認し安心が得られることです。
これは、4Dコードを含むスマートテンプレートとして 4D Write Pro ドキュメントを外部ファイルとしてディスク上に保存する場合に特に重要です。文書に含まれているコードを実行する前に、ファイルが不正に変更されていないことを確認したいものです。
あるいは、外部から送られてきた文書の場合、それが正しい送信者から来たものであり、受信までの間に改変されていないことを確認します。
これから説明する原則は、4D Write Pro の文書に適用されますが、少し修正を加えるだけで、他のタイプの文書にも適用されます。
HDI: 4D Write Pro: 文書の署名と保護
暗号化、それとも署名?
ここではドキュメントの認証、つまり、オリジナルの所有者 (法的 / 公式 / その他) によって登録された瞬間から、受信者によって読まれる (または所有者によって再読される) 瞬間までの間に、ドキュメントが変更されていないことを確認する方法に集中します。ここでは暗号化については議論しません。暗号化は可能ですが、また別のトピックだからです (暗号に興味のある方は、このブログを参照ください。)
署名の作成
署名を作成するプロセスはいたって簡単です。元の文書から、”秘密” 鍵を使って文書全体 (BLOB として) に基づいた署名を作成します。この署名は (そしてこの署名だけが) 文書そのもの (通常は文書の最後) に保存されます。
署名を読みとる
文書を開く前に、署名が読みとられます。文書全体から新しい BLOB が作成されます (この際に最後の鍵が省かれるため、理論的にはオリジナルと同じものです)。この BLOB は署名の作成に使われた”秘密” 鍵と密接に結びついた “公開” 鍵を使って検証されます。この検証の結果によって、次に何が起こるかが決まります。
この段階では、3つの可能性があります
ここまで、文書が署名されていると仮定していましたが、そうとは限りません。文書に署名がないからといって、必ずしも破損しているわけではありませんが、注意が必要です。
文書を開くかどうかは、開発者、ユーザー、状況、環境……によって異なります。要するに、どうするかは自分で決断しなくてはなりません。
2つ目の可能性は、署名と内容が一致していることで、これは朗報です。その文書は、自ら作成した、あるいは託された / 送られたものであること、そして何よりも、その文書が作成以来変更されていないことを確信することができます。つまり、(送信者を信頼していれば) 安心して文書を開くことができるのです。その後、今後の用途に応じて、修正・保存・再署名を自由におこなうことができます。もし修正後に送信する必要がある場合は、こちら側でも署名しておくと、次の受信者が同じ操作をおこなえます。
最後に、3つ目の可能性は、送信中に文書が変更された (あるいは破損した) ケースです。わずかなカンマの追加や削除、わずかなフォーミュラの変更などで、署名が一致しなくなります。
すぐにわかるので、適切な処置をとることができます。
さっそく始めましょう!
この例題では、すでに 4D Write Pro フォーマット (つまり .4wp の拡張子) で保存されている 1つ以上の既存ファイルに署名します。ファイルを操作するので、4D.File と 4D.FileHandle クラスを使います。また、暗号化キー、署名キー、検証キーを管理するので、4D.CryptoKey も使います (これらのクラスについては、対応するBLOGを参照ください)。
安全なデフォルトキーの作成
まず最初に、秘密鍵と公開鍵のペアを作成し、エンティティとしてデータベースに保存します (このペアの鍵はのちほど “デフォルト” 鍵として使用します)。
$cryptoKey:=ds.CryptoKey.new()
$keyOptions:={type: "RSA"; size: 2048}
$key:=4D.CryptoKey.new($keyOptions)
$cryptoKey.privateKey:=$key.getPrivateKey()
$cryptoKey.publicKey:=$key.getPublicKey()
$cryptoKey.save()
安全に文書を署名
次に、秘密鍵を使ってファイル (4D.File) の末尾に署名を追加する必要があります。
まとめると、以下のようになります:
// 秘密鍵に基づいて新しい鍵を作成します
$keyOptions:={type: "PEM"; pem: $privateKey}
$key:=4D.CryptoKey.new($keyOptions)
//…
// .sign() 関数を使って署名を作成します
$signOptions:={hash: "SHA512"; encodingEncrypted: "Base64URL"}
$signature:=$key.sign($documentAsBlob; $signOptions)
// 文書の最後に署名を (BLOB として) 追加します
TEXT TO BLOB($signature; $blobSignature; UTF8 text without length)
$fileHandle.offset:=$documentSize
$fileHandle.writeBlob($blobSignature)
安全に署名を確認
最後に、文書を開く前に、その署名をチェックする必要があります。これは以下のメソッドでおこないます。ファイル (4D.File) と、公開鍵 (任意) を渡します。
// 公開鍵に基づいて新しい鍵を作成します
$signOptions:={hash: "SHA512"; encodingEncrypted: "Base64URL"}
$type:={Type; "PEM"; pem; $publicKey}
$key:=4D.CryptoKey.new($type)
// 署名を読み取ります
//…
$fileHandle.offset:=$documentSize-$length
$blobSignature:=$fileHandle.readBlob($length)
$textSignature:=BLOB to text($blobSignature; UTF8 text without length)
// .verify() 関数を使って署名を検証します
$check:=$key.verify($documentAsBlob; $signature; $signOptions)
If ($check.success)
$result:=1
Else
$result:=-1
End if
.4wp 以外の文書の場合は?
4D Write Pro が .4wpドキュメントを読み込むと、ドキュメントの終わりが自動的に検出されるので、どこで停止すればよいかがわかります。ファイルの最後に追加された署名は、それを混乱させません。言い換えれば、文書に署名があろうとなかろうと、署名が正しかろうとなかろうと、.4wpは問題なく読み込まれるので、”事前に” 署名をチェックする必要があります。
他のタイプの文書では、最後に署名を追加することで読み込みが中断される可能性が (場合によってはおおいに) ありますので、署名を削除する必要があるかもしれません (ただし、検証した後で!)。
安全に文書の署名を削除
このため、逆の操作をおこなうためのメソッドも作成しました。
文書から署名を削除して元の状態に戻し、文書に署名がない場合は何もしません。
$fileHandle:=$file.open("write")
//… 署名の長さを計算してから…
$fileHandle.setSize($documentSize-$length)
まとめ
この単純な例題では、文書に署名し、それを開く前に署名を検証する方法を示しました。2つのサイト間で文書を交換する場合、公開鍵と秘密鍵のペアが 2つ必要になりますが、原理は変わりません。文書作成者は秘密鍵で署名し、読者は作成者から提供された公開鍵でそれを検証します。
公開鍵と秘密鍵は、署名と認証の両方を可能にする貴重な資産であるため、データファイルに直接保存することが望ましいでしょう。
