The purpose of this article is to show how, in an easy-to-implement way, you can sign and verify documents transparently.
The obvious benefit of this approach is peace of mind when the documents are opened again, safe in the knowledge that they have not been modified in the meantime.
This is especially important if you store 4D Write Pro documents as smart templates containing 4D code as external files on disk. Before executing this code you want to ensure the file was not externally modified.
Or, in the case of external documents, to be certain that they have come from the right sender and have not been altered during their digital journey.
The principle we’re about to describe applies to 4D Write Pro documents but applies to any other type of document with a few minor modifications.
Sample database
Encryption or signature?
Here, we’re going to concentrate on the authentication of a document, i.e. how to be sure that it hasn’t been modified between the moment it was registered by its original owner (legal/official/etc.) and the moment it is read by its recipient (or reread by its owner!). We’re not talking about encryption here, although that can also be done, but that’s another subject! (If you are interested in cryptography, please refer to this BLOG)
Creating a signature.
The process of creating a signature is fairly straightforward: from the original document we create a signature based on the on whole document (as a BLOB) using a “private” key. This signature (and only this signature) will be stored in the document itself, usually at the end of the document.
Rereading the signature
Before opening the document, the official signature is reread. A new BLOB will be created from the whole document (without the ending key, so theoretically identical to the original) and will be verified, this time using a public key, intimately linked to the private key that has been used to create the signature. The result of this verification will determine what happens next!
At this stage, there are three possibilities…
We have assumed that the document is signed, but this is not necessarily the case. If the document does not contain a signature, caution is called for, although it is not necessarily corrupt.
Whether or not to open the document depends on the developer, the user, the circumstances, the environment… In short, you have to decide what to do!
The second possibility is that the signature corresponds to the content. Good news then. You can be sure that the document is the one you created or that was entrusted to you/sent to you/etc., and above all that it has not been altered since its creation. This means that you can open it with complete peace of mind, provided, of course, that you trust the sender. You are then free to modify it, save it, sign it again or not, depending on its future use. If you need to modify it and then send it back to the sender, it’s a good idea to sign it yourself, so that the same operation can be carried out by the future recipient.
Finally, the third possibility is that the document may have been altered (or even corrupted) during transfer. The slightest comma added or removed, the slightest formula modified, etc. will mean that the signature no longer matches!
You’ll know immediately, so you can take the appropriate action.
Let’s get started!
In this example, we’re going to sign one or more existing files already saved in 4D Write Pro format, i.e. with a . “4wp” extension. As we’ll be managing files, we’ll be using the 4D.File and 4D.FileHandle classes. And as we’ll be managing encryption, signature and verification keys, we’ll also be using 4D.CryptoKey (see the corresponding BLOG if you’re not yet familiar with these classes…).
SAFE Create Default Keys
First of all, we’ll create a pair of private/public keys and store them once and for all as an entity in the database (This pair of keys will be used as “default” keys later on, as an example).
$cryptoKey:=ds.CryptoKey.new()
$keyOptions:={type: "RSA"; size: 2048}
$key:=4D.CryptoKey.new($keyOptions)
$cryptoKey.privateKey:=$key.getPrivateKey()
$cryptoKey.publicKey:=$key.getPublicKey()
$cryptoKey.save()
SAFE Sign Document
Next, you need to add a signature to the end of a file (4D.File) using a private key.
To sum up, here’s what you need to do:
// create a new key based on private key
$keyOptions:={type: "PEM"; pem: $privateKey}
$key:=4D.CryptoKey.new($keyOptions)
//…
// create the signature with the .sign() function
$signOptions:={hash: "SHA512"; encodingEncrypted: "Base64URL"}
$signature:=$key.sign($documentAsBlob; $signOptions)
// append signature (as a BLOB) at the end of the document
TEXT TO BLOB($signature; $blobSignature; UTF8 text without length)
$fileHandle.offset:=$documentSize
$fileHandle.writeBlob($blobSignature)
SAFE Check signature
Finally, BEFORE opening the document, you need to check its signature. This is done by the following method, which is passed a file (4D.File) and, optionally, a public key
// create a new key based on public key
$signOptions:={hash: "SHA512"; encodingEncrypted: "Base64URL"}
$type:={Type; "PEM"; pem; $publicKey}
$key:=4D.CryptoKey.new($type)
// read the real signature
//…
$fileHandle.offset:=$documentSize-$length
$blobSignature:=$fileHandle.readBlob($length)
$textSignature:=BLOB to text($blobSignature; UTF8 text without length)
// check the signature using the .verify() function
$check:=$key.verify($documentAsBlob; $signature; $signOptions)
If ($check.success)
$result:=1
Else
$result:=-1
End if
This works with .4wp, but what about other documents?
When 4D Write Pro loads a .4wp document, the end of the document is automatically detected so that you know where to stop. The signature added at the end of the file does not disrupt it. In other words, whether the document is signed or not, whether the signature is correct or not, the .4wp will be loaded without any problem, hence the need to check its signature beforehand.
For other types of documents, it is possible (even probable) that adding a signature at the end will disrupt loading, so it may be necessary – after validating it, of course – to remove the signature.
SAFE Unsign document
For this reason, this other method has been created to perform the opposite operation.
It removes the signature from a document to restore it to its original state, and if the document is not signed, it simply does nothing.
$fileHandle:=$file.open("write")
//… calculate signature length then…
$fileHandle.setSize($documentSize-$length)
Conclusion
This simple example shows how to sign a document and verify the signature before opening it. When exchanging documents between two sites, two pairs of public/private keys are required, but the principle remains the same. The document creator signs with a private key, and the reader verifies it with the public key supplied by the creator.
The public and private keys are precious assets, enabling both signature and authentication, which is why it’s advisable to save them in the Data file itself!