Sign with Google Cloud KMS Service
This guide will explain the process of signing a PDF file using an external signing service Google Cloud KMS. A Google Cloud KMS KEY with Asymmetric sign
Purpose is necessary to achieve this.
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
In your terminal, clone examples repository using the following command: It contains the Go code we will be using for this guide.
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
The import
section of the example code imports the necessary UniPDF packages and other Go libraries.
The init
function defined in lines 43-50
, initializes the program by setting your metered license key to authenticate your library request.
The main
function is defined in lines 61-112
. The inputPath
, outputPath
, gcCredentialsPath
and keyName
are parsed from the command line arguments in lines 62-70
. The code in lines 73-81
, generates a PDF file signed with empty signature. Then the byte range of the signature is parsed using parseByteRange(signature.ByteRange)
in line 84
. The getExternalSignatureAndSign
function simulates the process of sending the PDF buffer to a signing device or signing web service and getting back the signature. The PDF signed bytes and signature are obtained from this function. The external signature is applied to the PDF data buffer using the following piece of code:
sigBytes := make([]byte, sigLen)
copy(sigBytes, signatureData)
sig := core.MakeHexString(string(sigBytes)).WriteString()
copy(pdfData[byteRange[1]:byteRange[2]], []byte(sig))
Finally, the signed document is written to the output path.
The function generateSignedFile
returns a signed version of the input PDF file using the specified signature handler. In line 130
, a new PDF Appender is created using model.NewPdfAppender(reader)
from the reader. Then a new signature is created in lines 136-139
and gets initialized by calling signature.Initialize()
.
A signature field and appearance is created using:
opts := annotator.NewSignatureFieldOpts()
opts.FontSize = 10
opts.Rect = []float64{10, 25, 75, 60}
field, err := annotator.NewSignatureField(
signature,
[]*annotator.SignatureLine{
annotator.NewSignatureLine("Name", "John Doe"),
annotator.NewSignatureLine("Date", "2023.01.01"),
annotator.NewSignatureLine("Reason", "GC KMS Sing Test"),
},
opts,
)
if err != nil {
return nil, nil, err
}
field.T = core.MakeString("External signature")
Then the document is signed using appender.Sign(1, field)
. Finally, the buffer that contains the signed document and the signature object are returned from the function in line 175
.
The getExternalSignatureAndSign
function starting in line 180
simulates an external service which signs the specified PDF file and returns its signed bytes and signature. In this function in line 181
an externalSigner
is obtained by calling GcKmsExternalSigner(credPath, keyName)
. A certificate template is created in line 190-201
which is used to create a certificate data. This certificate data is parsed into a x509.Certificate
object in line 209
. Then using generateSignedFile(inputPath, handler)
the signed PDF bytes and the signature is obtained 221
. Finally, the signature content bytes and PDF bytes are returned from the function.
The parseByteRange
function parses the ByteRange
value of the signature field.
A CryptoSigner
type is defined in lines 263-268
. The Public
and Sign
methods of this type are part of the crypto.Signer
interface implementation. The NewCryptoSigner
function returns a new CryptoSigner
.
The externalSigner
type is defined in lines 382-395
. The GcKmsExternalSigner
function defined in liens 399-417
, is used to wrap externalSigner
to model.SignatureHandler
. The InitSignature
method which initializes the model.PdfSignature
parameters is part of the model.SignatureHandler
interface implementation.
The Sign
method tries to sign the digest based on the signature dictionary sig
and applies the signature data to the signature dictionary’s Contents
field. It returns an error in case of a failure. IsApplicable
method checks if the signature handler is applicable for the PdfSignature
. The NewDigest
method returns a new model.Hasher
digest data. The Validate
method, as can be guessed from its name, validates the PdfSignature
. These four methods are part of the model.SignatureHandler
interface.
The getPublicKey()
method defined in lines 477-479
, returns the public key of the externalSigner
. toSigHandler()
casts the externalSigner
to model.SignatureHandler
type.
Run the code
Run the code using the following command:
go run pdf_sign_external_aws_kms.go <INPUT_PDF_PATH> <OUTPUT_PDF_PATH> <KEY_ID>