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. 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.

Browser signing means that the browser has somehow to interface with the security device attached to the computer where the browser runs, and there is no standard way to achieve that with current browsers, and not even the new WebCrypto API will address that. This may change in the future but for now the best alternative is to have a service run locally in the user machine that can access the security device and have Javascript in the browser call this local service. This approach is best exemplified with a Online Demo.

The demo provided consists of two parts: (a) a remote part consisting of some HTML and JavaScript and a couple of PHP scripts, and (b) a local hash signer service that runs a small HTTP(S) server that can interface with the security device.

The remote part of the demo can be found in the web directory of the installation (you need to install the PHP module first) in Linux. To run the demo in your development environment edit the signremotepdf-php.html and change the remote_server and local_server JavaScript variables at the top of the page and then point your PHP enabled web server document root to this web directory. If you then open in a browser the page you edited and click the Sign Remote PDF button you will receive the message, not surprisingly, that your local service is not runing.

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.daplie.com
localport=8443
ssljkstore=C\:\\Program Files\\MyPDFSigner\\tests\\mypdfsigner-ssl-hashsignerservice.jks
sslkeystorepasswd=5c3d995123cd147c
sslkeymanagerpasswd=7e9b9c6b42b85823

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.

A second alternative, instead of running a local service, would be to run an applet in the browser and have the applet interface with the security device, but given the fact that applets are not very liked by browsers this is not a good option.

You can quickly play with MyPDFSigner in a Ubuntu environment by launching the AWS AMI with id ami-73b53f13 available in the US Weest (Oregon) region. There is no charge to play with this AMI besides the default Ubuntu AMI charges (i.e., there are no extra KryptoKoder charges). Please Contact KryptoKoder if you would like a similar AMI in your preferred AWS region.

Below is a sample session showing how to login and run the basic commands:

$ ssh -i myawsusername-key-pair-oregon.pem ubuntu@ec2-54-202-49-21.us-west-2.compute.amazonaws.com
The authenticity of host 'ec2-54-202-49-21.us-west-2.compute.amazonaws.com (54.202.49.21)' can't be established.
ECDSA key fingerprint is SHA256:OHWSbXABv+zGu00nynxVVnK5YBc8+Ji/4IFnAelNXPo.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'ec2-54-202-49-21.us-west-2.compute.amazonaws.com,54.202.49.21' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-64-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

34 packages can be updated.
17 updates are security updates.


*** System restart required ***
Last login: Fri Mar 24 10:15:19 2017 from 178.254.116.78
ubuntu@ip-172-31-4-129:~$ mypdfsigner -h
MyPDFSigner v2.5.1, (c) 2009-2017 KryptoKoder LLC
Usage:
mypdfsigner -i input.pdf -o output.pdf -p "password" -l "location" -r "reason" -v <visible> -c <certify> -q <timestamp> -t "title" -a "author" -s "subject" -k "keywords" -z mypdfsigner.conf

Only -i is required. If -o is missing the output file is the same as the input with "-signed" appended.
If -i and -o are directories all the PDF files in the input directory are signed and placed in the output directory, if not there yet.
If -z is missing the configuration file "/usr/local/mypdfsigner/mypdfsigner.conf" is used instead.
The password is the open document password, needed if the signed document is to be encrypted.

The configuration file can store an encrypted password for the PKCS#12 key store. The password can be encrypted from the command line with -e "password".
ubuntu@ip-172-31-4-129:~$ php -f /usr/local/mypdfsigner/tests/test.php
0#Document signed
0#Document signature verified [Signer: MyPDFSigner Test]
ubuntu@ip-172-31-4-129:~$ python /usr/local/mypdfsigner/tests/test.py
0#Document signed
0#Document signature verified [Signer: MyPDFSigner Test]
ubuntu@ip-172-31-4-129:~$ ruby /usr/local/mypdfsigner/tests/test.rb
0#Document signed
0#Document signature verified [Signer: MyPDFSigner Test]
ubuntu@ip-172-31-4-129:~$ ls -l /tmp/*pdf
-rw-rw-r-- 1 ubuntu ubuntu 197352 Mar 27 11:51 /tmp/example-signed-php.pdf
-rw-rw-r-- 1 ubuntu ubuntu 197371 Mar 27 11:51 /tmp/example-signed-python.pdf
-rw-rw-r-- 1 ubuntu ubuntu 197358 Mar 27 11:52 /tmp/example-signed-ruby.pdf
ubuntu@ip-172-31-4-129:~$ exit
logout
Connection to ec2-54-202-49-21.us-west-2.compute.amazonaws.com closed.

You can also quickly play with MyPDFSigner in a Docker container by running the kryptokoder/mypdfsigner image as shown below. To test signing from the command line using a PKCS#12 store the example downloads and uncompresses an archive that includes the configuration, a sample PDF document and a signature image. The example also mounts a host directory as a data volume. If running Docker in Windows you need to adapt this to your environment.

Notice that after the signing takes place the signed document, example-signed-docker.pdf, is placed in the mounted directory.

[support@kryptokoder ~]$ cd /tmp/
[support@kryptokoder tmp]$ 
[support@kryptokoder tmp]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
debian              latest              8cedef9d7368        2 weeks ago         123 MB
[support@kryptokoder tmp]$ docker run kryptokoder/mypdfsigner -h
Unable to find image 'kryptokoder/mypdfsigner:latest' locally
latest: Pulling from kryptokoder/mypdfsigner
6d827a3ef358: Already exists 
2391cd0d77b4: Already exists 
869ede93680a: Already exists 
736308dab2b2: Already exists 
Digest: sha256:130749dc956b81d8389baec341896980590583a2d96940ff86910fe0635801ba
Status: Downloaded newer image for kryptokoder/mypdfsigner:latest
MyPDFSigner v2.6.0, (c) 2009-2017 KryptoKoder LLC
Usage:
mypdfsigner -i input.pdf -o output.pdf -p "password" -l "location" -r "reason" -v <visible> -c <certify> -q <timestamp> -t "title" -a "author" -s "subject" -k "keywords" -z mypdfsigner.conf

Only -i is required. If -o is missing the output file is the same as the input with "-signed" appended.
If -i and -o are directories all the PDF files in the input directory are signed and placed in the output directory, if not there yet.
If -z is missing the configuration file "/usr/local/mypdfsigner/mypdfsigner.conf" is used instead.
The password is the open document password, needed if the signed document is to be encrypted.

The configuration file can store an encrypted password for the PKCS#12 key store. The password can be encrypted from the command line with -e "password".
[support@kryptokoder tmp]$ wget https://www.kryptokoder.com/mypdfsigner-docker.tar.gz
--2017-04-06 14:30:41--  https://www.kryptokoder.com/mypdfsigner-docker.tar.gz
Resolving www.kryptokoder.com (www.kryptokoder.com)... 188.121.56.49
Connecting to www.kryptokoder.com (www.kryptokoder.com)|188.121.56.49|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 166657 (163K) [application/x-tar]
Saving to: ‘mypdfsigner-docker.tar.gz’

mypdfsigner-docker.tar.gz                            100%[=====================================================================================================================>] 162.75K  --.-KB/s    in 0.06s   

2017-04-06 14:30:41 (2.83 MB/s) - ‘mypdfsigner-docker.tar.gz’ saved [166657/166657]

[support@kryptokoder tmp]$ tar -xvf mypdfsigner-docker.tar.gz 
mypdfsigner-docker/
mypdfsigner-docker/signature.png
mypdfsigner-docker/mypdfsigner.conf
mypdfsigner-docker/mypdfsigner-test.p12
mypdfsigner-docker/example.pdf
[support@kryptokoder tmp]$ ls -l mypdfsigner-docker
total 184
-rw-r--r-- 1 support support 115360 Apr  6 13:59 example.pdf
-rw-rw-r-- 1 support support    314 Apr  6 14:02 mypdfsigner.conf
-rw-r--r-- 1 support support   1870 Apr  6 14:00 mypdfsigner-test.p12
-rw-r--r-- 1 support support  58748 Apr  6 14:01 signature.png
[support@kryptokoder tmp]$ docker run -d -P --name mypdfsigner-docker -v /tmp/mypdfsigner-docker:/tmp/mypdfsigner-docker kryptokoder/mypdfsigner -i /tmp/mypdfsigner-docker/example.pdf -o /tmp/mypdfsigner-docker/example-signed-docker.pdf -z /tmp/mypdfsigner-docker/mypdfsigner.conf -v -c -q
98742749f9bd395bc6807212710a6a445782b9553ae3226c84a28fe0533a7e43
[support@kryptokoder tmp]$ ls -l mypdfsigner-docker
total 380
-rw-r--r-- 1 support support 115360 Apr  6 13:59 example.pdf
-rw-r--r-- 1 support support 197312 Apr  6 14:34 example-signed-docker.pdf
-rw-rw-r-- 1 support support    314 Apr  6 14:02 mypdfsigner.conf
-rw-r--r-- 1 support support   1870 Apr  6 14:00 mypdfsigner-test.p12
-rw-r--r-- 1 support support  58748 Apr  6 14:01 signature.png
[support@kryptokoder tmp]$ docker images
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
kryptokoder/mypdfsigner   latest              c01b1c7347bb        22 hours ago        222 MB
debian                    latest              8cedef9d7368        2 weeks ago         123 MB
[support@kryptokoder tmp]$