Enabling LTV Using Second Revision And TimeStamp

This guide will demonstrate the process of digitally signing a PDF file using a PKCS12 (.p12/.pfx) file and LTV enabled the signature.

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 import section in lines 12-32, imports all the necessary dependencies. The init function in lines 34-41, authenticates UniPDF library request by setting the metered license key using license.SetMeteredKey(os.Getenv(`UNIDOC_LICENSE_API_KEY`)).

The main function is defined in lines 45-115. Inside this function the code in lines 51-54 gets the p12Path, password, inputPath and outputPath from the command line arguments. The private key and the certificate are loaded and decoded from the p12Path in lines 57-65. The next section loads the certificate chain from the command line arguments and decodes it as follows:

certChain := []*x509.Certificate{cert}
if len(args) == 6 {
  issuerCertData, err := ioutil.ReadFile(args[5])
  if err != nil {
    log.Fatal("Fail: %v\n", err)
  }

  for len(issuerCertData) != 0 {
    var block *pem.Block
    block, issuerCertData = pem.Decode(issuerCertData)
    if block == nil {
      break
    }

    issuer, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
      log.Fatal("Fail: %v\n", err)
    }
    certChain = append(certChain, issuer)
  }
}

The document is signed and written to buffer in line 91. Then the buffer that contains the signed content is passed through the ltvEnableAndTimestamp function to enable LTV, timestamp and then write to buffer. Then at line 103, the timestamp signature is LTV enabled using ltvEnableTimestampSig(bytes.NewReader(signedBytes)).

Finally, the document is written to file using ioutil.WriteFile(outputPath, signedBytes, 0644) in line 109.

The signFile function in lines 117-179, is used to sign a file and return signed content as byte buffer. In this function in line 137, a new handler is created using sighandler.NewAdobePKCS7Detached(priv, cert). Then in line 142-146, a new PdfSignature object is created from the signature handler. Signature field and appearance is created in lines 153-166. Finally, the document is signed in line 168 and returned as a buffer at line 178.

The ltvEnableAndTimestamp function is used to create a timeStamp signature with LTV enabled. In line 189 of this function a new PdfAppender is created using model.NewPdfAppender(reader). Then a new model.LTV object is created in line 200. After this the LTV is enabled using ltv.EnableAll(certChain).

In line 205, The timestamp handler is created using handler, err := sighandler.NewDocTimeStamp("https://freetsa.org/tsr", crypto.SHA512). Then in lines 211-226, the PdfSignature and PdfSignatureField are created as follows:

signature := model.NewPdfSignature(handler)
signature.SetName("Test Sign Timestamp")
signature.SetReason("TestSignTimestamp")

if err := signature.Initialize(); err != nil {
  return nil, err
}

sigField := model.NewPdfFieldSignature(signature)
sigField.T = core.MakeString("Test Sign Timestamp")
sigField.Rect = core.MakeArray(
  core.MakeInteger(0),
  core.MakeInteger(0),
  core.MakeInteger(0),
  core.MakeInteger(0),
)

Using the Sign method of the Appender object, the document is signed in line 228. Finally, the signed document is returned as bytes.buffer in line 238.

The definition of the ltvEnableTimestampSig function starts in line 241. In line255, a new model.LTV object is created using ltv, err := model.NewLTV(appender). Then it is enabled in line 260 using ltv.EnableAll(nil). The document is returned as a buffer in line 270.

Run the code

To run the code use the following command:

go run pdf_sign_ltv_timestamp_revision.go <FILE.p12> <P12_PASS> <INPUT_PDF_PATH> <OUTPUT_PDF_PATH> [<EXTRA_CERTS.pem>]

Got any Questions?

We're here to help you.