Creating PAdES B-LTA Signature

In this guide, the process of applying PAdES B-LTA level signature to a PDF document using UniPDF will be discussed.

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

How it works

The following section will explain the example code.

In the example code, the import section in lines 8-27, imports the necessary UniPDF packages and other Go libraries. Then the init function in lines 29-36, loads the API key from the system environment and sets the license using license.SetMeteredKey(os.Getenv(`UNIDOC_LICENSE_API_KEY`)).

In line 41-50, the command line arguments are parsed, and the necessary inputs are obtained. The X509 certificate and the private key are decoded from the PFX file, whose path is provided in the command line arguments. In lines 64-76, the CA Certificate is obtained from the PEM file using:

caCertF, err := ioutil.ReadFile(pemPath)
if err != nil {
  log.Fatal("Fail: %v\n", err)
}

certDERBlock, _ := pem.Decode(caCertF)

cacert, err := x509.ParseCertificate(certDERBlock.Bytes)

if err != nil {
  log.Fatal("Fail: %v\n", err)
  return
}

Then following that, a new model.PDFReader is created in line 85 using model.NewPdfReader(file). Using this model.PdfReader a new model.PdfAppender is created in line 91. In line 91 a time stamp server URL is defined. Then a new etsiPAdES signature handler is instantiated in line 100 using:

handler, err := sighandler.NewEtsiPAdESLevelLT(priv.(*rsa.PrivateKey), cert, cacert, timestampServerURL, appender)
if err != nil {
  log.Fatal("Fail: %v\n", err)
}

Using the signature handler, a new model.PdfSignature is created using model.NewPdfSignature(handler). This signature is initialized in line 111 using signature.Initialize(). This prepares the signature dictionary for signing, which includes setting all necessary fields, and also allocating sufficient space to the Contents so that the finalized signature can be inserted once the hash is calculated.

In lines 116-133, the signature filed appearance is created, and the document is signed 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.05.08"),
    annotator.NewSignatureLine("Reason", "PAdES signature test"),
  },
  opts,
)
field.T = core.MakeString("Self signed PDF")

if err = appender.Sign(1, field); err != nil {
  log.Fatal("Fail: %v\n", err)
}

In line 136, the document is written to a buffer using appender.Write(buffer). Then in line 143, a second pass is performed to save DSS/VRI information. To make the second pass a new model.PdfReader is created from the buffer containing the signed document. Then an model.PdfAppender is created from this model.PdfReader and the DSS is set using appender2.SetDSS(appender.GetDSS()). Then in lines 155-159 the document is written to a buffer using:

buf2 := bytes.NewBuffer(nil)
err = appender2.Write(buf2)
if err != nil {
  log.Fatal("Fail: %v\n", err)
}

Starting in line 162, The document stamp which is specific to B-LTA is added to the signed document. To add the time stamp first a new model.PdfReader is created from the buffer. Then a new signature handler is created using the timestamp server URL and the hashing algorithm as follows.


handler, err = sighandler.NewDocTimeStamp(timestampServerURL, crypto.SHA512)
if err != nil {
  log.Fatal("Fail: %v\n", err)
}

In lines 177-197, a new model.PdfSignature and a model.PdfFieldSignature are created. The time stamp is signed using:

if err = appender3.Sign(1, sigField); err != nil {
		log.Fatal("Fail: %v\n", err)
	}

Finally, the document is written to file using appender3.WriteToFile(outputPath) in line 203.

Run the code

Run the code using the following command:

go run pdf_sign_pades_b_lta.go <FILE.PFX> <PASSWORD> <FILE.PEM> <INPUT_PDF_PATH> <OUTPUT_PDF_PATH>

Got any Questions?

We're here to help you.