Sign with an ECDSA Key
Elliptic curve keys are smaller and faster than RSA keys of the same strength.
NewAdobePKCS7Detached takes an
*rsa.PrivateKey only, so an EC key goes through its sibling,
sighandler.NewAdobePKCS7DetachedEcdsa. Everything after the handler is the ordinary
signing path, and the result is the same adbe.pkcs7.detached signature with an
ECDSA signer inside the PKCS#7 package.
Decoding the key
pfxData, err := os.ReadFile(pfxPath)
if err != nil {
return err
}
priv, cert, _, err := pkcs12.DecodeChain(pfxData, password)
if err != nil {
return err
}
ecdsaKey, ok := priv.(*ecdsa.PrivateKey)
if !ok {
return fmt.Errorf("private key is %T; only ECDSA keys are supported", priv)
}
handler, err := sighandler.NewAdobePKCS7DetachedEcdsa(ecdsaKey, cert)
if err != nil {
return err
}The example decodes the container with software.sslmate.com/src/go-pkcs12, which
reads the AES-encrypted .pfx files OpenSSL 3 writes by default and files that bundle
CA certificates with the signing certificate. The CA certificates are discarded, and
only the signing certificate goes into the PKCS#7 package. Given a container with an
RSA key, the example exits with private key is *rsa.PrivateKey; only ECDSA keys are supported and points to the PKCS12 example.
Signing
From here it is the same as signing with RSA:
signature := model.NewPdfSignature(handler)
signature.SetName("adbe.pkcs7.detached ECDSA PDF")
signature.SetReason("Test ECDSA")
signature.SetDate(time.Now(), "")
if err := signature.Initialize(); err != nil {
return err
}
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", "2025.07.28"),
annotator.NewSignatureLine("Reason", "Test"),
},
opts,
)
if err != nil {
return err
}
field.T = core.MakeString("Self signed PDF")
if err := appender.Sign(1, field); err != nil {
return err
}
return appender.WriteToFile(outputPath)Validation needs no special handler. A validation-only
sighandler.NewAdobePKCS7Detached(nil, nil) checks ECDSA signatures too, since the
algorithm comes from the signature itself.
PDF version
ECDSA in adbe.pkcs7.detached signatures is defined by PDF 2.0. UniPDF does not
enforce that and does not raise the version: the appender keeps the input’s header,
so signing a PDF 1.7 file produces a PDF 1.7 file with an ECDSA signature in it.
UniPDF validates that file without complaint. A validator that checks conformance
strictly may not, so when the signed output has to conform, start from a PDF 2.0
document.
Run the example
The example reads the key and certificate from a PKCS12 file and signs the first page with a visible signature box. To make a test key and file with OpenSSL:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
-keyout key.pem -out cert.pem -days 365 -subj "/CN=ECDSA Test Signer" \
-addext "keyUsage=digitalSignature"
openssl pkcs12 -export -inkey key.pem -in cert.pem -out signer.pfx -passout pass:testgit clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures/signing
go run pdf_sign_pkcs7_ecdsa.go <FILE.PFX> <PASSWORD> <INPUT_PDF_PATH> <OUTPUT_PDF_PATH>If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.
View the full source
A self-signed test certificate like this one verifies, but a trust check refuses it unless that certificate is itself one of your trust roots. See Validating Signer Trust and Revocation.