Sign with AWS KMS Service

This guide explains the process of digitally signing a PDF file using an external signing service AWS KMS (AWS Key Management Service).

Before you begin

You should get your API key from your UniCloud account.

If this is your first time using UniPDF SDK, follow this guide to set up a local development environment.

Project setup

Clone the project repository

git clone https://github.com/unidoc/unipdf-examples.git

Then navigate to the signatures folder in the unipdf-examples directory.

cd unipdf-examples/signatures

Configure environment variables

Configure your license key using the following command: Replace the UNIDOC_LICENSE_API_KEY with your API credentials from your UniCloud account.

Linux/Mac

export UNIDOC_LICENSE_API_KEY=PUT_YOUR_API_KEY_HERE

Windows

set UNIDOC_LICENSE_API_KEY=PUT_YOUR_API_KEY_HERE

The next section will explain the example code.

How it works

In the provided example code, the import section imports the necessary UniPDF packages and other Go libraries. The init function gets the metered license key and authenticates your library request.

The main function of the program starts in line 56. The input path, output path and key ID are parsed from the command line arguments in lines 57-64. A PDF file signed with empty signature is generated using the following steps in lines 67-75:

handler, err := sighandler.NewEmptyAdobePKCS7Detached(sigLen)
if err != nil {
  log.Fatalf("Fail: %v\n", err)
}

_, signature, err := generateSignedFile(inputPath, handler)
if err != nil {
  log.Fatalf("Fail: %v\n", err)
}

Then the byte ranges are parsed using parseByteRange(signature.ByteRange). The part where the external signing is simulated is in line 88. This part returns a signature data which is then used to apply the signature to the PDF data buffer. This is done using:

sigBytes := make([]byte, sigLen)
copy(sigBytes, signatureData)

sig := core.MakeHexString(string(sigBytes)).WriteString()
copy(pdfData[byteRange[1]:byteRange[2]], []byte(sig))

The document is written to file in line 103.

The generateSignedFile function generates a signed version of the input PDF file using the specified signature handler. This function generates private key, creates certificate, and then signs the document using appender.Sign method. After signing the document, it returns document buffer and signature.

The function that is used to simulate an external service by signing the specified PDF file and return its signed bytes and signature is defined in lines 176-222. This function first gets an externalSigner type AwsKmsExternalSigner(keyId) in lines 177. Then a new certificate template is initialized by providing basic certificate information such as CommonName, Organization, NotBefore, NotAfter etc. Using this template and awsKmsSign external signer, a certificate data is prepared and then parsed by x509.ParseCertificate(certData) to a certificate object. From this certificate a certificate chain is initialized and assigned to awsKmsSign.certChain in line 211.

Finally, the file is signed by calling generateSignedFile and the signature.Contents.Bytes() and pdfBytes are returned from the function.

The parseByteRange function parses the ByteRange value of the signature field.

In line 255, a call back function is defined to be used in the CryptoSigner type which is defined in lines 258-263. An EncryptionAlgorithmOID() method of the CryptoSigner is defined. This returns a asn1.ObjectIdentifier type of encryption OID algorithm supported by the external services. The CryptoSigner type implements the crypto.Signer interface by defining the Public() and Sign() methods in lines 271-289 and 292-314 respectively. The Public() method returns the public key corresponding to the private key and the Sign() method signs digest with the private key.

The NewCryptoSigner function in lines 316-322 returns a new CryptoSigner object.

The externalSigner type which is a wrapper for a third-party signer is defined in lines 327-340. The AwsKmsExternalSigner function wraps externalSigner to model.SignatureHandler.

The InitSignature method of externalSigner, initializes a model.PdfSignature by setting the necessary fields of the object. This is defined in lines 369-391. The Sign method signs the given digest and returns an error in case of a fail. IsApplicable method checks if the handler is applicable for the given model.PdfSignature. NewDigest creates a new model.Hasher digest.

The Validate method in lines 469-474, validates the given model.PdfSignature. The getPublicKey() function returns crypto.PublicKey of the externalSigner object. And finally the toSigHandler method converts the externalSigner to model.SignatureHandler type.

Run the code

Run the code using the following terminal command.

go run pdf_sign_external_aws_kms.go <INPUT_PDF_PATH> <OUTPUT_PDF_PATH> <KEY_ID>

Got any Questions?

We're here to help you.