Skip to content
Signing and Encrypting PDF File

Signing and Encrypting PDF File

Encryption and signing both happen at write time, and they have to happen in that order: encrypt the document, then sign it as an incremental update. Signing first and encrypting afterwards would rewrite the bytes the signature covers and break it.

The catch is that model.NewPdfAppender cannot open an encrypted document. It builds an internal read-only snapshot of the source without attempting decryption, so on a password-protected file the snapshot has no page list and Sign fails with page 1 not found. NewPdfAppenderWithOpts passes the reader options through to that snapshot, which is what makes the password available.

Encrypting, then signing

readerOpts := model.NewReaderOpts()
readerOpts.Password = password

pdfReader, err := model.NewPdfReaderWithOpts(bytes.NewReader(buf), readerOpts)
if err != nil {
    return err
}

pdfAppender, err := model.NewPdfAppenderWithOpts(pdfReader, readerOpts, encryptOptions)
if err != nil {
    return err
}

Use model.NewPdfReaderWithOpts rather than model.NewPdfReader for the same reason: the plain constructor does not attempt decryption either, and leaves the document structure unloaded.

The encryption itself goes through a writer:

pdfWriter, err := pdfReader.ToWriter(nil)
if err != nil {
    return err
}

if err := pdfWriter.Encrypt([]byte(password), []byte(password), encryptOptions); err != nil {
    return err
}

Encrypt takes the user password first and the owner password second. The user password is what opens the document; the owner password lifts the permission restrictions.

Encryption options

encryptOptions := &model.EncryptOptions{
    Permissions: security.PermPrinting | security.PermFullPrintQuality |
        security.PermModify | security.PermAnnotate | security.PermFillForms |
        security.PermRotateInsert | security.PermExtractGraphics |
        security.PermDisabilityExtract,
    Algorithm: model.AES_128bit,
}
AlgorithmNotes
model.RC4_128bitThe zero value, so an EncryptOptions that omits Algorithm gets RC4. Obsolete; avoid.
model.AES_128bitSets the output version to PDF 1.5.
model.AES_256bitSets the output version to PDF 2.0.

Encrypt sets the version outright rather than raising it, so encrypting a PDF 1.7 document with AES-128 writes it out as 1.5. Call pdfWriter.SetVersion after Encrypt if the document uses features that need a later version.

Passing nil options to Encrypt grants security.PermOwner, that is, everything. Restricting permissions means listing the ones you want, as above. PermFillForms belongs on that list for a document that is going to be signed, since a signature is a form field value.

Limitations

The encryptOptions argument to NewPdfAppenderWithOpts only takes effect if the source document is not already encrypted. When it is, the appender reuses the crypt filter it found in the file and encrypts the appended objects with that, so the algorithm and permissions of the original are preserved and the options are ignored. That is what you want here, but it means the appender cannot change a document’s encryption.

When the appender does encrypt, it uses the ReaderOpts password as both the user and the owner password. There is no way to give them different values through the appender; use a writer, as the example does.

The signature covers the encrypted bytes as they appear in the file. A reader that cannot supply the password cannot validate the signature either, since validation re-reads the byte ranges from the file.

Run the example

encryptDocument writes an AES-128 encrypted copy to a buffer, and addSignature appends a visible signature to page 1 of that copy. Both the input and the output use the password password, hardcoded in main.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures
go run pdf_sign_and_encrypt_pdf.go <IN.pdf> <OUT.pdf>

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

Sample output

Signed and encrypted document

Last updated on