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>