DEV Community

Cover image for Signing PDFs in Go? Meet pdfsign v1.0.0
Akkarapon Phikulsri
Akkarapon Phikulsri

Posted on

Signing PDFs in Go? Meet pdfsign v1.0.0

Hi :) Have you ever needed to sign a PDF document from a Go application ?

Well, I had to do that for one of my projects. I needed to digitally sign PDF documents using a certificate, so I started looking for a suitable Go package.

After exploring serveral options, I found

GitHub logo digitorus / pdfsign

Add/verify Advanced Electronic Signature (AES) and Qualified Electronic Signature (QES) in PDF (using pure Go)

Signing PDF files with Go

Build & Test golangci-lint Go Report Card Coverage Status Go Reference

A PDF signing and verification library written in Go. This library provides both command-line tools and Go APIs for digitally signing and verifying PDF documents.

See also our PDFSigner, a more advanced digital signature server that is using this project.

Quick Start

# Sign a PDF
./pdfsign sign -name "John Doe" input.pdf output.pdf certificate.crt private_key.key

# Verify a PDF signature
./pdfsign verify document.pdf
Enter fullscreen mode Exit fullscreen mode

Package Architecture

The library is organized into specialized subpackages, though most users will primarily interact with the root pdfsign package.

Package Purpose
pdfsign Primary entry point. Provides the fluent API for signing, verification, and document management.
extract Low-level signature inspection. Allows extracting raw PKCS#7 envelopes and signed data.
forms PDF form handling. Logic for field discovery and generating PDF object updates.
initials Configuration and placement logic for signing initials across multiple pages.
signers/... External Signers. Optional
…

and decided to give it a try. it seemed like a good fit for my case...

Well let take a look deep drive into that package

Maybe you're building a document management system, generating contracts or automating an approval process. At first, signing a PDF might seem simple

load document
add a signature
and save it !

However, things can get more complicated when you work with real-world documents. You might need timestamps, certificate validation, certification permissions, signature verification, encrypted PDFs, or metadata in languages other than English.

That's where digitorus/pdfsign comes in.

On October 26, 2026, pdfsign reached v1.0.0. This release introduces a redesigned Go API, PAdES Baseline support, stronger signature verification, post-quantum signing support, better handling of encrypted PDFs, and improved support for Unicode metadata.

In this blog we will explore the package through practical Go examples. We will also learn how to test certification signature.

What is pdfsign?

pdfsign is a PDF signing and verification library written in Go. It also provides command line tools, so you can integrate PDF signing into an application or work with PDF files directly from your terminal.

The root pdfsign package is now the main entry point for most signing and verification tasks. Other packages provide more focused functionality, such as signature extraction, forms, fonts, images and integrations with external signing systems.

Well I recently discovered that digitorus/pdfsign has released its stable v1.0.0 version. This release introduces several improvements

  • Redesigned Go API Makes signing and verification easier to configure.
  • PAdES Baseline B-B and B-T Supports basic signatures and timestamped signatures.
  • Stronger verification Improves checks for signatures, certificates, timestamps, and document permissions.
  • ML-DSA support Adds a post-quantum digital signature algorithm.
  • Better PDF handling Improves support for encrypted PDFs and Unicode metadata.

Let's start with the Go API and then see how these signing features behave in practice.

Example 1: Sign a PDF using Go

  • First, add the package to your Go module:
go get github.com/digitorus/pdfsign
Enter fullscreen mode Exit fullscreen mode

The following example demonstrates the signing flow using PAdES B-T

package main

import (
    "crypto"
    "crypto/x509"
    "os"

    "github.com/digitorus/pdfsign"
)

func signPDF(
    privateKey crypto.Signer,
    certificate *x509.Certificate,
    tsaURL string,
) error {
    doc, err := pdfsign.OpenFile("contract.pdf")
    if err != nil {
        return err
    }

    output, err := os.Create("contract-signed.pdf")
    if err != nil {
        return err
    }
    defer output.Close()

    doc.Sign(privateKey, certificate).
        Format(pdfsign.PAdES_B_T).
        Timestamp(tsaURL).
        Reason("Contract approval")

    _, err = doc.Write(output)
    return err
}
Enter fullscreen mode Exit fullscreen mode

This example assumes that you have already loaded the private key and certificate, and that the configured TSA endpoint is reachable.

Here's what the main steps do

  • pdfsign.OpenFile(...) opens the source PDF.

  • doc.Sign(...) configures the document for signing.

  • Format(pdfsign.PAdES_B_T) selects the PAdES B-T profile.

  • Timestamp(tsaURL) requests a timestamp from the configured TSA.

  • Reason(...) records the reason for signing.

  • doc.Write(output) writes the signed PDF.

The fluent API makes the signing configuration easy to scan, especially when you need to add more options later.

A production note: This is a focused example of the API, not a complete signing service. Your application still needs to handle credentials securely, validate inputs, define certificate and timestamp trust policies, and handle failures appropriately.

Example 2 Verify a signed PDF

Signing a PDF is only one part of the workflow. You may also need to verify a document before accepting it into your system.

Here's a basic example using the verification builder

package main

import (
    "fmt"

    "github.com/digitorus/pdfsign"
)

func verifyPDF() error {
    doc, err := pdfsign.OpenFile("contract-signed.pdf")
    if err != nil {
        return err
    }

    result := doc.Verify().Strict()

    if !result.Valid() {
        return fmt.Errorf(
            "PDF signature verification failed: %v",
            result.Err(),
        )
    }

    fmt.Println("PDF passed strict signature verification")
    return nil
}
Enter fullscreen mode Exit fullscreen mode

You can check it out in my playground!

PDF signing playground — pdfsign v1.0.0

Ten Go examples for learning and blog demonstrations, using github.com/digitorus/pdfsign v1.0.0. Requires Go 1.27 or newer. Run commands from the indicated example folder.

The root go.mod pins v1.0.0 for all ten examples. To add that exact release to another module, use go get github.com/digitorus/pdfsign@v1.0.0.

Each folder contains sample PDFs and explains expected behavior. A command that demonstrates rejection is not a broken example. Signature integrity, certificate trust, and timestamp trust are separate checks.

Examples 4–8 use a generated local test CA where appropriate…




However at the 0.9.0 previous version, I ran into a problem. My certificate contained Thai language information, and after signing the PDF, I could not open the document in Adobe Acrobat Reader.

Instead I got this error

There was an error opening this document. There was a problem reading this document (135).

But my deadline was getting closer. T_T

As a last resort, I decide to clone the repository and modify the package to support certificates containing Thai language information

I applied the fix locally and tested it. Luckily, it worked!

After confirming that the fix solved my problem, I decided to contribute it back to the original project digitorus/pdfsign so that other developers could benefit from it too.

I'm happy to see pdfsign reach its stable v1.0.0 release. Even better, my first contribution to the project was included in this release!

I also helped test and verify the fix to make sure it worked as expected.

contribution

Fun Fact

That’s the story of how I rushed to meet a deadline, trying to get PDF signing working before it was too late. But in the end, fixing just one small issue made a difference to the community, and that’s something I’m proud of.

Sometimes, even a one line change can make an impact :)

Top comments (0)