Signing PDFs in the cloud or through a browser would be straightforward if it weren't for the fact that the only certificates automatically trusted by Adobe Reader need to have physical support, i.e., they need to be in a smart card, USB token, HSM or other security device. This means, particularly in the cases where security or compliance reasons prevent the PDF to sign to leave the server where it is stored, that a hash of the PDF prepared for signature needs to be sent to the security device (since a private key cannot be extracted from a security device), signed there, and the resulting signature blob needs to be returned and embedded in the previously prepared PDF.
Two common use cases are the following: (a) a company wants to send signed PDF statements to their customers Bob and Alice and wants to run the signing application in the cloud; and (b) user Alice needs to use a browser to sign a PDF located in a remote server using the private key in the smart card connected to her computer.
In both use cases the approach is the same:
You can use MyPDFSigner for the three steps above. For the signing step there two options : a Java based solution and a PHP, Ruby or Python solution. The Java based solution can interface with PKCS#11, PKCS#12, Windows Certificate Store and Apple Keychain stores. THe PHP, Ruby or Python solution can only interface with PKCS#11 and PKCS#12 stores.
Cloud signing means the security device is somehow in the cloud. One option is to use cloud based HSM that many Certificate Authorities or other organizations provide, in particular public cloud providers. The other option is to host your own cloud connected computer and plug your security device to it. In this second option you can have your main application in the cloud and only host the signing server in the your premises or hosting facility.
Google Cloud KMS
Below is an example of how to use Google Cloud KMS as the cloud signing provider. The integration is done through a PKCS#11 library that interfaces with KMS APIs. Integration with other cloud signing services should be equally simple as long as there is an associated PKCS#11 library.
To configure MyPDFSigner to work with Google Cloud KMS, one needs to start by creating a KeyRing and a Key and by providing the right permissions to the service account that we will use to interface with KMS.
Login to your GCP and set your project
gcloud auth login gcloud config set project <my-project-id>
Create a keyring in your location (example: us-central1)
gcloud kms keyrings create <my-keyring> --location=<my-location>
Confirm the keyring was created:
gcloud kms keyrings list --location=<my-location>
Create a key in your keyring in your location (note that
protection-level need to be as shown):
gcloud kms keys create <my-key> --purpose=asymmetric-signing --location=<my-location>> --keyring=<my-keyring> --default-algorithm=rsa-sign-pkcs1-2048-sha256 --protection-level=hsm
Confirm the key was created:
gcloud kms keys list --location=<my-location> --keyring=<my-keyring>
Using the console or the gcloud command line tool give the right permissions to the service account you will be using for signing
IAM & Admin > IAM > Permissions: Edit service account permissions and give the role Cloud KMS Crypto Operator
Once a cloud HSM signing key is created and the right permissions are in place one can generate a certificate signing request (CSR) or a self-signed certificate. The steps below show how to perform that and also how to install and configure MyPDFSigner in a Docker container.
Start with an appropriate Docker image (Debian 11 suggested here) and mount a volume if you need to exchange data with it, like the service account credentials
docker run -v C:\mydata:/tmp/mydata -it debian:11 /bin/bash
Update and install needed (an optional) packages
apt update -y apt install -y libengine-pkcs11-openssl wget gdebi vim
Get the Google KMS PKCS#11 library and unpack it
wget https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/v1.1/libkmsp11-1.1-linux-amd64.tar.gz tar -xvf libkmsp11-1.1-linux-amd64.tar.gz
PKCS11_MODULE_PATH environment variable to point to the shared library
export PKCS11_MODULE_PATH=/path/to/libkmsp11.so (example: /libkmsp11-1.1-linux-amd64/libkmsp11.so)
Create a PKCS#11 library KMS configuration. The configuration is
a YAML file,
pkcs11-config.yaml say, with the following content. The file needs to be writable by the owner only.
tokens: - key_ring: projects/<my-project-id>/locations/<my-location>/keyRings/<my-keyring>
KMS_PKCS11_CONFIG environment variable to point to the created configuration file
Copy the JSON credentials file to your container and set
GOOGLE_APPLICATION_CREDENTIALS environment variable to point to it (or use Workload Identity)
Create a self-signed certificate for testing purposes and save it to a file, say,
openssl req -new -x509 -days 10000 -subj '/CN=MyPDFSigner User/' -sha256 -engine pkcs11 -keyform engine -key pkcs11:object=<my-key> > /path/to/my-self-signed-kms-cert.pem
Get and install MyPDFSigner and check the installation was successful (the package used here is for Debian 11)
wget https://www.kryptokoder.com/mypdfsigner_3.4.0-2_amd64.deb gdebi mypdfsigner_3.4.0-2_amd64.deb mypdfsigner -h
Create a MyPDFSigner configuration file,
mypdfsigner-pkcs11-kms.conf say, with the following content. Make sure the paths match your setup and that the
<my-key> value matches the key you created above.
capem file entry is needed but can point to a non existent file if the
signerpem file contains a self-signed certificate.
#MyPDFSigner test configuration file certstore=PKCS11 SECURITY DEVICE sigrect=[-170 -80 -40 -40] sigimage=/usr/local/mypdfsigner/tests/signature.png tsaurl=http://adobe-timestamp.geotrust.com/tsa subfilter=ETSI.CAdES.detached signerpem=/path/to/my-self-signed-kms-cert.pem capem=/not/needed/ca.pem engine=/usr/lib/x86_64-linux-gnu/engines-1.1/libpkcs11.so pin= p11url=pkcs11:object=<my-key> library=/path/to/libkmsp11.so debug=on logfile=/tmp/mypdfsigner-kms.log
Finally test signing a document
mypdfsigner -i /usr/local/mypdfsigner/tests/example.pdf -o /tmp/example-signed.pdf -z /path/to/mypdfsigner-pkcs11-kms.conf
The expected output should be
0#Document signed: input[/usr/local/mypdfsigner/tests/example.pdf] output[/tmp/example-signed.pdf]
A Ready Made Docker Image
You can skip most of the above steps by running a "ready made" Docker image that has been configured to get you running in no time. The only missing steps are creating your KeyRing and HSM backed Key, and to update the credentials and certificate files and a few fields in the configuration files. The README file has all the steps. If you have all your needed files in a data folder and mount that folder as shown below, then you can quickly update the required files.
PS C:\> docker run -v C:\data:/data -it kryptokoder/mypdfsigner-gcp-kms /bin/bash root@409d0df38042:/# cat README This is a pre-configured Docker image to quickly test MyPDFSigner with Google KMS. To be able to run this demo you need a Google project with a KeyRing and a Key and a service account with the Cloud KMS Crypto Operator role. See https://www.kryptokoder.com/cloudandbrowser.html#GoogleCloudKMS for the steps necessary for that to happen. If you have your service account credentials and certificates in the /path/to/my/data folder then you can run Docker as shown below: docker run -v /path/to/my/data:/data -it kryptokoder/mypdfsigner-gcp-kms /bin/bash then, inside the container, copy the needed files to the already existing placeholder empty files:: cp /data/my-certificate.pem /signer.pem cp /data/my-ca-certificates-chain.pem /ca.pem (if not self signed) cp /data/my-service-account-credentails.json /service-account-credentials.json and using an editor (vi is available) update <my-project-id>, <my-location> and <my-keyring> in /pkcs11-config.yaml, and also update <my-key> in /mypdfsigner-pkcs11-kms.conf, using your own settings. You can check that the relevant environment variables are already set using: env | grep -e GOOGLE_APPLICATION_CREDENTIALS -e KMS_PKCS11_CONFIG -e PKCS11_MODULE_PATH Finally you can test signing a document using you GCP Cloud based HSM key: mypdfsigner -i /usr/local/mypdfsigner/tests/example.pdf -o /data/example-signed.pdf -z /mypdfsigner-pkcs11-kms.conf You should then have the signed document in your /path/to/my/data folder. Happy signing!... root@409d0df38042:/#
The local part of the demo, which for now is only available for Windows and Mac OS X, is included as part of the desktop application installer. To run it, edit the configuration file, as shown below, and then start the MyPDFSigner-Tray application (see the Online Demo for more details). Note that the SSL entries of the configuration file are only needed if running the demo under HTTPS.
The hash signer service provided adds some extra entries to the configuration file:
remoteurl=https://www.kryptokoder.com localprotocol=https localhost=localhost.kryptokoder.com localport=8443 ssljkstore=C\:\\Program Files\\MyPDFSigner\\tests\\mypdfsigner-ssl-hashsignerservice.jks sslkeystorepasswd=5c3d995123cd147c sslkeymanagerpasswd=5c3d995123cd147c
The localhost value should be (assuming the remote site runs under HTTPS) a 127.0.0.1 record on a public domain, and the ssljkstore is a Java KeyStore with the SSL certificates for that record. Note that although it is not possible to obtain an SSL certificate for localhost you can obtain one for, say, localhost.mydomain.com if you are the owner of mydomain.com. The sslkeystorepasswd and sslkeymanagerpasswd are the encrypted passwords of, respectively, the Java KeyStore and the private key in the Java KeyStore.