Skip to content

janezkranjc/PDFSign

Repository files navigation

PDFSign

A tiny native macOS app that does exactly one thing: open a PDF and digitally sign it with a certificate — the same kind of signature Adobe Acrobat produces (adbe.pkcs7.detached, PKCS#7/CMS), without the bloat.

No dependencies. Pure Swift + Apple frameworks (SwiftUI, PDFKit, Security).

Build & run

make app     # builds PDFSign.app in the repo root
make run     # builds and opens it
make test    # runs the test suite

Usage

  1. Open a PDF (⌘O, the toolbar button, or drop a file on the window).
  2. Pick a certificate from the toolbar menu — it lists signing identities from your Keychain, or use Import Certificate (.p12)… to load a PKCS#12 file (kept for the session only).
  3. Click Sign, then drag a rectangle on the page where the visible signature stamp should go — or choose Invisible Signature.
  4. Optionally fill in a reason/location and pick a handwritten signature for the stamp: draw one with your trackpad/mouse (Draw New…), sign on your phone (Sign on Phone… — scan the QR code with the phone's camera, a full-screen signature pad opens in the browser; same Wi-Fi required, works with any phone), or import an image (Import Image… — a transparent PNG, or a photo/scan whose light background is removed automatically). Apple's Continuity Sketch is still available inside the phone sheet. Saved signatures are kept in ~/Library/Application Support/PDFSign/Signatures/ for reuse. (Preview's own saved signatures are stored encrypted in a private format, so they can't be imported directly — redraw once and it's saved here.)
  5. Sign & Save….

macOS will ask for permission the first time the app uses a private key from your Keychain — that's the Keychain access control doing its job.

Releases

Pushing a v* tag (or running the workflow manually) triggers .github/workflows/release.yml on a macOS runner: it runs the tests, builds the app signed with a Developer ID certificate (hardened runtime + secure timestamp), packages a signed drag-to-Applications DMG, optionally notarizes it, and attaches it to a GitHub Release.

Required repository secrets:

Secret Value
MACOS_CERTIFICATE base64 of a .p12 containing your Developer ID Application certificate + private key (base64 -i cert.p12 | pbcopy)
MACOS_CERTIFICATE_PASSWORD the .p12 password

Optional (recommended — without notarization Gatekeeper blocks downloaded apps): NOTARY_APPLE_ID (Apple ID email), NOTARY_PASSWORD (app-specific password from appleid.apple.com), NOTARY_TEAM_ID (10-char team ID).

Local equivalent: make dmg VERSION=v1.0 SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)" CODESIGN_FLAGS="--timestamp --options runtime".

What it writes

  • A standard incremental update: the original bytes are untouched, so any existing signatures remain valid (and you can sign an already-signed PDF).
  • A signature field + widget annotation with an appearance stream ("Digitally signed by …, Date, Reason, Location"), registered in AcroForm with SigFlags 3.
  • A detached CMS/PKCS#7 SignedData blob (SHA-256, signing time, full certificate chain), DER-encoded, over the /ByteRange of the file — verifiable by Adobe Acrobat, openssl smime -verify, and any PAdES-basic aware validator.
  • Matches the original file's cross-reference style (classic tables and cross-reference streams are both supported, including object streams).

Limitations

  • Encrypted (password-protected) PDFs are refused — remove the password first.
  • No RFC 3161 trusted timestamps and no LTV/PAdES-LT (could be added later).
  • Whether a validator shows the signer as trusted depends on the certificate's chain, exactly as in Acrobat: self-signed certificates validate as "signer unknown" unless the recipient trusts the certificate.

Layout

  • Sources/PDFSignCore — PDF parser (xref tables/streams, object streams), incremental-update signature writer, CMS signing, Keychain identity store.
  • Sources/PDFSign — the SwiftUI app.
  • Tests/PDFSignCoreTests — end-to-end tests: generate PDFs, sign with a throwaway certificate in a temporary keychain, verify independently with openssl.

About

A minimal macOS app for digitally signing PDFs

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages