Digital sign with Aws KMS

Hello

I want to sign documents using Aws KMS, but couldn’t find anything related to that on the documents.

Is it feasible?

Thank you

We are just releasing a new version of our SDK which will make this task easier.

Could you let us know what language+framework you are using so that we can provide you an answer.

1 Like

Thank you for the response.

Good to hear that you’re going to make it easier.

We’re using the Javascript SDK and wanted to implement the signing/verifying functionality in the backend with Python(Django) and Aws KSM.

Do you know any existing tools/ways/solutions for that? any help will be appreciated

Thanks again

Try the following Python code, but replace usage of SignDigest with your own code that contacts Amazon.

doc = PDFDoc(in_docpath)
doc.InitSecurityHandler()
page1 = doc.GetPage(1)

certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name)
widgetAnnot = SignatureWidget.Create(doc, Rect(143, 287, 219, 306), certification_sig_field)
page1.AnnotPushBack(widgetAnnot)

# (OPTIONAL) Add an appearance to the signature field.
img = Image.Create(doc.GetSDFDoc(), in_appearance_image_path)
widgetAnnot.CreateSignatureAppearance(img)

##### The following section of sample code inside this function is mostly new and pertains to the custom signing APIs.

# Create a digital signature dictionary inside the digital signature field, in preparation for signing.
certification_sig_field.CreateSigDictForCustomCertification("Adobe.PPKLite",
    DigitalSignatureField.e_ETSI_CAdES_detached if in_PAdES_signing_mode is True else DigitalSignatureField.e_adbe_pkcs7_detached,
    7500) # For security reasons, set the contents size to a value greater than but as close as possible to the size you expect your final signature to be.
# ... or, if you want to apply a normal non-certification signature, use CreateSigDictForSigning instead.

# (OPTIONAL) Add more information to the signature dictionary.
certification_sig_field.SetLocation("Vancouver, BC")
certification_sig_field.SetReason("Document certification.")
certification_sig_field.SetContactInfo("www.pdftron.com")

# Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
current_date = Date()
current_date.SetCurrentTime()
certification_sig_field.SetSigDictTimeOfSigning(current_date)

doc.Save(in_outpath, SDFDoc.e_incremental)

# Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
digest = certification_sig_field.CalculateDigest(in_digest_algorithm_type)

# At this point, you can do your custom signing.
#out_signature = DigitalSignatureField.SignDigest(digest, in_private_key_file_path, in_keyfile_password, True, in_digest_algorithm_type)

# With buffer overload
out_signature = []
private_key_buf = []
private_key_file = MappedFile(in_private_key_file_path)
private_key_file_sz = private_key_file.FileSize()
private_key_file_reader = FilterReader(private_key_file)
private_key_buf = private_key_file_reader.Read(private_key_file_sz)
out_signature = DigitalSignatureField.SignDigest(digest, private_key_buf, in_keyfile_password, True, in_digest_algorithm_type)

# Write the signature to the document.
doc.SaveCustomSignature(out_signature, certification_sig_field, in_outpath)
1 Like

Thank you so much

When I tried to run the code, I encountered this error

Traceback (most recent call last):
  File "main.py", line 23, in <module>
    certification_sig_field.CreateSigDictForCustomCertification("Adobe.PPKLite",
  File "/home/mir/pdftron/.venv/lib/python3.8/site-packages/PDFNetPython3/PDFNetPython.py", line 5643, in <lambda>
    __getattr__ = lambda self, name: _swig_getattr(self, DigitalSignatureField, name)
  File "/home/mir/pdftron/.venv/lib/python3.8/site-packages/PDFNetPython3/PDFNetPython.py", line 80, in _swig_getattr
    raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name))
AttributeError: 'DigitalSignatureField' object has no attribute 'CreateSigDictForCustomCertification'

also I couldn’t find “CreateSigDictForCustomCertification” in the pip package of yours

We just released a new SDK update, 9.1, which is now available on Pypl.

Please update to PDFNet 9.1 and try again.

1 Like

Thank you so much for the reply.

I’ve updated the library and sign a document however when I open it in Adobe Acrobat the signature is still invalid.

I created an RSA_2048 key in KMS for signing/verifying.

As I see in the KMS panel, these algorithms are valid for signing using this key

    RSASSA_PKCS1_V1_5_SHA_256
    RSASSA_PKCS1_V1_5_SHA_384
    RSASSA_PKCS1_V1_5_SHA_512
    RSASSA_PSS_SHA_256
    RSASSA_PSS_SHA_384
    RSASSA_PSS_SHA_512 

This is the code

import os
import hashlib
import boto3
from PDFNetPython3 import *


class AwsConfig:
    SIGN_AWS_ACCESS_KEY_ID = os.getenv('SIGN_AWS_ACCESS_KEY_ID') or 'AKIATMRHGXXX'
    SIGN_AWS_SECRET_ACCESS_KEY = os.getenv('SIGN_AWS_SECRET_ACCESS_KEY') or 'BXVTnahV9uUAbVloW4sAaZXXX'
    SIGN_AWS_REGION = os.getenv('SIGN_AWS_REGION') or 'us-east-2'
    SIGN_AWS_KEY_ID = os.getenv('SIGN_AWS_KEY_ID') or 'a3b2d70e-3a11-4ebe-81aXXX'


def sign(data):
    doc_hash = hashlib.sha256(data).digest()

    client = boto3.client(
        'kms',
        region_name=AwsConfig.SIGN_AWS_REGION,
        aws_access_key_id=AwsConfig.SIGN_AWS_ACCESS_KEY_ID,
        aws_secret_access_key=AwsConfig.SIGN_AWS_SECRET_ACCESS_KEY,
    )

    response = client.sign(
        KeyId=AwsConfig.SIGN_AWS_KEY_ID,
        Message=doc_hash,
        MessageType='DIGEST',
        SigningAlgorithm='RSASSA_PKCS1_V1_5_SHA_256'
    )
    client.verify
    return response['Signature']


PDFNet.Initialize("demo:1630101464969:78e7336503000000005495bc43021d4ea87b753b81f38676dXXX")


in_docpath = 'test.pdf'
in_cert_field_name = 'PDFTronCertificationSig'
in_PAdES_signing_mode = False
in_outpath = 'test_signed.pdf'
in_digest_algorithm_type = 1


doc = PDFDoc(in_docpath)
doc.InitSecurityHandler()
page1 = doc.GetPage(1)

certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name)
widgetAnnot = SignatureWidget.Create(doc, Rect(143, 287, 219, 306), certification_sig_field)
page1.AnnotPushBack(widgetAnnot)

certification_sig_field.CreateSigDictForCustomCertification("Adobe.PPKLite", DigitalSignatureField.e_ETSI_CAdES_detached if in_PAdES_signing_mode is True else DigitalSignatureField.e_adbe_pkcs7_detached, 7500) 

certification_sig_field.SetLocation("Vancouver, BC")
certification_sig_field.SetReason("Document certification.")
certification_sig_field.SetContactInfo("www.pdftron.com")

current_date = Date()
current_date.SetCurrentTime()
certification_sig_field.SetSigDictTimeOfSigning(current_date)

doc.Save(in_outpath, SDFDoc.e_incremental)

digest = certification_sig_field.CalculateDigest(in_digest_algorithm_type)
out_signature = sign(open(in_outpath, 'rb').read())

doc.SaveCustomSignature(out_signature, certification_sig_field, in_outpath)

Do you have any clue what might be the problem?

Thanks again

Thank you for the update.

First, it appears you are digesting twice by mistake. Instead of

out_signature = sign(open(in_outpath, 'rb').read())

call just

out_signature = sign(digest)

Next, you should probably include your certificates in the CMS signature. As an example, in OpenSSL this is done by calling PKCS7_add_certificate.

If you are trying to create a PAdES signature then you also need to add ESS_Signing_Cert[V2]. The easiest way to sign would probably be to find an Amazon API for creating detached CMS signatures, and furnish it with the digest, certificates, and anything else you want to include as an attribute.

Good catch, thanks

For the second part, how do I add the certificate to the signature field? is there any API for that?

Thanks again

There is no API with us to store certificates manually. It is possible that Amazon will do so by default, however it is also possible that they will only include the signer certificate by default but not the root or intermediate(s). In case Amazon does not add them, you should first try to figure out if Amazon has an API/setting to enable certificate embedding into CMS signatures.

Otherwise, you can use OpenSSL to add certificates to the signature (which should be possible, since the certificates field is its own member of CMS according to RFC 5652, and not part of the signed ‘contents’ since it’s not the same as ‘signed attributes’).

It is possible that we may decide to add an API in future release to allow certificate addition to custom signatures. Also, we may add support in future for LTV-style certificate addition. We consider all customer feedback when deciding on new features.

Can you try the aforementioned ideas for the time being?

It is possible now to customize which certificates are added as part of signing, by usage of the custom signing API.

See the usage of the chain_certs argument to GenerateCMSSignature, within our new custom signing guide below.

Hello, I am following this guide in order to sign documents with google KMS, GlobalSign and PHP.
I am getting an error on the line that generates de CMS signature.
The line (248):

$cms_signature = \DigitalSignatureField::GenerateCMSSignature($companyCert, $chain_certs, $digest_algorithm_oid, $signature_algorithm_oid, $pdf_enctipted_digest, $signedAttrs);

the error:
Fatal error: Uncaught TypeError: Expected SWIGTYPE_p_std__vectorT_pdftron__Crypto__X509Certificate_t for argument 2 of DigitalSignatureField_GenerateCMSSignature in /…/pdftron.php:248

The fault is with in the $chain_certs, which (the guide sadly omits it) I am setting like this:

$rootCert = new \X509Certificate("/******/root.crt");
$intermediateCert = new \X509Certificate("/*****/intermediate.crt");
$chain_certs = array($rootCert, $intermediateCert);

What am I doing wrong? How do I form the $chain_certs
Please help out.

1 Like

I have also tried to initialize the $chain_certs variable like this:

        $chain_certs = new \VectorX509Certificate();
        $chain_certs->push($rootCert);
        $chain_certs->push($intermediateCert);

This way instead of getting a PHP error code from SWIG, the whole php-fpm hangs and the process gets reset with
2022/10/25 18:20:31 [error] 32258#32258: 233 recv() failed (104: Connection reset by peer) while reading response header from upstream, client
and
WARNING: [pool www] child 6306 exited on signal 11 (SIGSEGV - core dumped) after 125.622895 seconds from start

Does anybody has any more ideas?

1 Like

Ok So I keep digging into this.
To do so, I changed the DigitalSignaturesTest.php that comes with samples into a DigitalSignaturesTestPlus.php that generates a CMS signature as told by PDFTron documentation.
The full code looks like this:

function CertifyPDF($in_docpath,
	$in_cert_field_name,
	$in_private_key_file_path,
	$in_keyfile_password,
	$in_appearance_image_path,
	$in_outpath)
{

	echo(nl2br('================================================================================'.PHP_EOL));
	echo(nl2br('Certifying PDF document'.PHP_EOL));

	// Open an existing PDF
	$doc = new PDFDoc($in_docpath);

	if ($doc->HasSignatures())
	{
		echo(nl2br('PDFDoc has signatures'.PHP_EOL));
	}
	else
	{
		echo(nl2br('PDFDoc has no signatures'.PHP_EOL));
	}

	$page1 = $doc->GetPage(1);

	// Create a new signature form field in the PDFDoc. The name argument is optional;
	// leaving it empty causes it to be auto-generated. However, you may need the name for later.
	// Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
	// Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible.
	$certification_sig_field = $doc->CreateDigitalSignatureField($in_cert_field_name);
	$widgetAnnot = SignatureWidget::Create($doc, new Rect(143.0, 287.0, 219.0, 306.0), $certification_sig_field);
	$page1->AnnotPushBack($widgetAnnot);

	// (OPTIONAL) Add an appearance to the signature field.
	$img = Image::Create($doc->GetSDFDoc(), $in_appearance_image_path);
	$widgetAnnot->CreateSignatureAppearance($img);

	// Prepare the document locking permission level. It will be applied upon document certification.
	echo(nl2br('Adding document permissions.'.PHP_EOL));
        $certification_sig_field->SetDocumentPermissions(DigitalSignatureField::e_no_changes_allowed);
        //this is where the fun begins

	//$certification_sig_field->CertifyOnNextSave($in_private_key_file_path, $in_keyfile_password);

        // Prepare the document locking permission level. It will be applied upon document certification.
        // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
        $certification_sig_field->CreateSigDictForCustomCertification("Adobe.PPKLite", DigitalSignatureField::e_adbe_pkcs7_detached, 19500);
        echo(nl2br('Adding Adobe.PPKLite'.PHP_EOL));
        // For security reasons, set the contents size to a value greater than but as close as possible to the size you expect your final signature to be, in bytes.
        // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
        $current_date = new Date();
        $current_date->SetCurrentTime();
        $certification_sig_field->SetSigDictTimeOfSigning($current_date);
        echo(nl2br('Adding SetSigDictTimeOfSigning'.PHP_EOL));

	// (OPTIONAL) Add more information to the signature dictionary.
        $certification_sig_field->SetLocation('Place, XX');
        $certification_sig_field->SetReason('Document Certification');
        $certification_sig_field->SetContactInfo('www.xyz.com');
        echo(nl2br('Adding Location-Reason-Contact info'.PHP_EOL));

	// Save the PDFDoc so far
	$doc->Save($in_outpath, SDFDoc::e_incremental);

        $pdf_digest = $certification_sig_field->CalculateDigest(DigestAlgorithm::e_SHA256);
        // with the digest of the pdf calculated we must encryp it with the key on google KMS
        //$pdf_digest = $this->encryptDigestWithGoogleKMS($pdf_digest);
        $companyCert = new X509Certificate("/place/certificates/cert.pem");
        $rootCert = new X509Certificate("/place/certificates/root.pem");
        $intermediateCert = new X509Certificate("/place/certificates/inter.pem");

        $chain_certs = new VectorX509Certificate();
        $chain_certs->push($rootCert);
        $chain_certs->push($intermediateCert);
        echo(nl2br('Setting $chain_certs variable as VectorX509Certificate Object'.PHP_EOL));
        $signedAttrs = array();

        // Then, create ObjectIdentifiers for the algorithms you have used.
        $digest_algorithm_oid = new ObjectIdentifier(ObjectIdentifier::e_SHA256);
        $signature_algorithm_oid = new ObjectIdentifier(ObjectIdentifier::e_RSA_encryption_PKCS1);

        // Then, put the CMS signature components together.
        echo(nl2br('Before DigitalSignatureField::GenerateCMSSignature'.PHP_EOL));
        $cms_signature = DigitalSignatureField::GenerateCMSSignature($companyCert, $chain_certs, $digest_algorithm_oid, $signature_algorithm_oid, $pdf_digest, $signedAttrs);
        $doc->SaveCustomSignature($cms_signature, $certification_sig_field, $in_outpath);

	echo(nl2br('================================================================================'.PHP_EOL));
}

The result:

$ php -e DigitalSignaturesTestPlus.php
================================================================================<br />
Certifying PDF document<br />
PDFDoc has signatures<br />
Adding document permissions.<br />
Adding Adobe.PPKLite<br />
Adding SetSigDictTimeOfSigning<br />
Adding Location-Reason-Contact info<br />
Setting $chain_certs variable as VectorX509Certificate Object<br />
Before DigitalSignatureField::GenerateCMSSignature<br />
Segmentation fault (core dumped)

Either there is something really wrong with GenerateCMSSignature or with the way of setting the $chain_certs var (which I am completely guessing).

PDFTron guys, is there any a way I can make this work?

Santiago

1 Like