Skip to content

Commit

Permalink
[UP-487] Example for PAdES B-T signing. (unidoc#231)
Browse files Browse the repository at this point in the history
* [UP-487] Example for PAdES B-T signing.

* Added new examples to README.md
  • Loading branch information
anovik committed Aug 10, 2023
1 parent 6ab1ad7 commit c8c568f
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 2 deletions.
4 changes: 3 additions & 1 deletion signatures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ Examples for digital signing of PDF files with UniDoc:
- [pdf_sign_appearance.go](pdf_sign_appearance.go) Example of creating signature appearance fields.
- [pdf_sign_validate.go](pdf_sign_validate.go) Example of signature validation.
- [pdf_sign_pem_multicert.go](pdf_sign_pem_multicert.go) Example of signing using a certificate chain and a private key, extracted from PEM files.

- [pdf_sign_pades_b_b.go](pdf_sign_pades_b_b.go) Example of signing with a PAdES B-B compatible digital signature.
- [pdf_sign_pades_b_t.go](pdf_sign_pades_b_t.go) Example of signing with a PAdES B-T compatible digital signature.
- [pdf_sign_validate_pades_b_b.go](pdf_sign_validate_pades_b_b.go) Example of PAdES signature validation.
For LTV enabling digital signatures, see the [LTV](ltv) guide and samples.

## pdf_sign_hsm_pkcs11_cgo.go
Expand Down
140 changes: 140 additions & 0 deletions signatures/pdf_sign_pades_b_t.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* This example showcases how to create a PAdES B-T compatible digital signature for a PDF file.
*
* $ ./pdf_sign_pades_b_t <FILE.PFX> <PASSWORD> <FILE.PEM> <INPUT_PDF_PATH> <OUTPUT_PDF_PATH>
*/
package main

import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"os"
"time"

"golang.org/x/crypto/pkcs12"

"github.com/unidoc/unipdf/v3/annotator"
"github.com/unidoc/unipdf/v3/common/license"
"github.com/unidoc/unipdf/v3/core"
"github.com/unidoc/unipdf/v3/model"
"github.com/unidoc/unipdf/v3/model/sighandler"
)

func init() {
// Make sure to load your metered License API key prior to using the library.
// If you need a key, you can sign up and create a free one at https://cloud.unidoc.io
err := license.SetMeteredKey(os.Getenv(`UNIDOC_LICENSE_API_KEY`))
if err != nil {
panic(err)
}
}

const usagef = "Usage: %s PFX_FILE PASSWORD PEM_FILE INPUT_PDF_PATH OUTPUT_PDF_PATH\n"

func main() {
args := os.Args
if len(args) < 6 {
fmt.Printf(usagef, os.Args[0])
return
}
pfxPath := args[1]
password := args[2]
pemPath := args[3]
inputPath := args[4]
outputPath := args[5]

// Get private key and X509 certificate from the PFX file.
pfxData, err := ioutil.ReadFile(pfxPath)
if err != nil {
log.Fatal("Fail: %v\n", err)
}

priv, cert, err := pkcs12.Decode(pfxData, password)
if err != nil {
log.Fatal("Fail: %v\n", err)
}

// Get cacert certificate from the PEM file.
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
}

// Create reader.
file, err := os.Open(inputPath)
if err != nil {
log.Fatal("Fail: %v\n", err)
}
defer file.Close()

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

// Create appender.
appender, err := model.NewPdfAppender(reader)
if err != nil {
log.Fatal("Fail: %v\n", err)
}

// Set timestamp server.
timestampServerURL := "https://freetsa.org/tsr"

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

// Create signature.
signature := model.NewPdfSignature(handler)
signature.SetName("PAdES B-T Signature PDF")
signature.SetReason("TestPAdESPDF")
signature.SetDate(time.Now(), "")

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

// Create signature field and appearance.
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)
}

// Write output PDF file.
err = appender.WriteToFile(outputPath)
if err != nil {
log.Fatal("Fail: %v\n", err)
}

log.Printf("PDF file successfully signed. Output path: %s\n", outputPath)
}
2 changes: 1 addition & 1 deletion signatures/pdf_sign_validate_pades_b_b.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func main() {
log.Fatal("Fail: %v\n", err)
}

// Create signature handler.
// Create signature handler. It is possible to validate B-T level the same way if you create NewEtsiPAdESLevelT here
padesHandler, err := sighandler.NewEtsiPAdESLevelB(nil, nil, nil)
if err != nil {
log.Fatal("Fail: %v\n", err)
Expand Down

0 comments on commit c8c568f

Please sign in to comment.