Documentation for digitally signing PDF documents with Stirling PDF.
Stirling PDF supports digital signing of PDFs with:
- Self-signed certificates: For internal documents
- CA-signed certificates: For external documents with trust chain
Certificates are used in P12 format (PKCS#12).
There are three methods to integrate a signing certificate:
| Method | Description | Recommended for |
|---|---|---|
| A: Environment Variable | KEYSTORE_P12_BASE64 (Base64) |
Coolify, CI/CD, Cloud |
| B: Volume Mount | File to /configs/keystore.p12 |
Local development |
| C: User Certificates | /customFiles/signatures/ |
Individual selection |
Quick and easy for internal use:
# With tools container
./tools/run.sh -s './scripts/generate-cert.sh'
# Or directly
./scripts/generate-cert.shInteractive inputs:
- Organization: BAUER GROUP
- Country: DE
- Validity: 3 years (default)
- Key size: 4096 bit (recommended)
Result:
certs/
├── cert.p12 # Certificate for Stirling PDF (backup)
├── cert.crt # Public certificate
├── cert.key # Private key (KEEP SECURE!)
└── .passphrase # Certificate password
.env (automatically updated):
├── KEYSTORE_PASSWORD # Certificate passphrase
└── KEYSTORE_P12_BASE64 # Base64-encoded P12 certificate
For documents that need to be verified by third parties:
./scripts/ca-cert-workflow.shStep 1: Create CSR
# Choose option 1
./scripts/ca-cert-workflow.sh
# → Enter certificate information
# → Creates: certs/signing-request.csr and certs/signing-request.keyStep 2: Submit CSR to CA
Send certs/signing-request.csr to your Certificate Authority.
Step 3: Import Signed Certificate
# Choose option 2
./scripts/ca-cert-workflow.sh
# → Specify path to signed certificate
# → Optional: Import CA chain
# → Creates: certs/cert.p12The certificate is stored as Base64 in an environment variable. The container entrypoint automatically decodes it at startup to /configs/keystore.p12.
Benefits:
- No file mounts required
- Perfect for Coolify, CI/CD, Kubernetes
- Certificate can be stored as secret
In .env (automatically set by script):
# Enable server certificate
SYSTEM_SERVERCERTIFICATE_ENABLED=true
# Organization name (shown in UI)
SYSTEM_SERVERCERTIFICATE_ORGANIZATIONNAME=BAUER GROUP
# Password for the P12 certificate
KEYSTORE_PASSWORD=auto-generated-by-script
# Base64-encoded P12 certificate (automatically set by script)
KEYSTORE_P12_BASE64=MIIxxxxx...How it works:
./scripts/generate-cert.shcreates the certificate- Script writes
KEYSTORE_PASSWORDandKEYSTORE_P12_BASE64to.env - Container starts → Entrypoint decodes Base64 → writes
/configs/keystore.p12 - Stirling PDF uses the certificate for signing
Alternatively, the P12 file can be mounted directly.
In docker-compose.yml:
volumes:
- './certs/cert.p12:/configs/keystore.p12:ro'In .env:
SYSTEM_SERVERCERTIFICATE_ENABLED=true
SYSTEM_SERVERCERTIFICATE_ORGANIZATIONNAME=BAUER GROUP
KEYSTORE_PASSWORD=your-password-hereNote: The entrypoint first checks if
/configs/keystore.p12already exists (mounted). If so,KEYSTORE_P12_BASE64is ignored.
For individual certificates that users can select in the UI.
Global certificate (all users):
volumes:
- './certs/cert.p12:/customFiles/signatures/ALL_USERS/cert.p12:ro'User-specific certificates:
volumes:
- './certs/users/john.doe/cert.p12:/customFiles/signatures/john.doe/cert.p12:ro'Structure in container:
/customFiles/signatures/
├── ALL_USERS/ # Available for all users
│ └── company-cert.p12
└── john.doe/ # Only for user "john.doe"
└── personal-cert.p12
- Upload PDF in web interface
- Select Certificate Sign tool
- "Sign with BAUER GROUP" appears automatically
- Sign and download
- Upload PDF in web interface
- Select Certificate Sign tool
- Choose certificate from dropdown list
- Enter passphrase
- Sign and download
If you already have a PEM certificate:
./scripts/convert-to-p12.sh
# → Path to certificate (.crt/.pem)
# → Path to private key (.key)
# → Optional: CA chain# Show P12 contents
openssl pkcs12 -in certs/cert.p12 -info -noout
# Certificate details
openssl pkcs12 -in certs/cert.p12 -clcerts -nokeys | \
openssl x509 -noout -text
# Check expiration date
openssl pkcs12 -in certs/cert.p12 -clcerts -nokeys | \
openssl x509 -noout -enddate- Open PDF in Adobe Acrobat Reader
- Open signature panel (left sidebar)
- View signature details
# With pdfsig (Poppler-Utils)
pdfsig signed-document.pdf
# With qpdf
qpdf --show-xref signed-document.pdf- Protect private key:
chmod 600 cert.key - Store passphrase securely: Not in repositories!
- Regular renewal: Create new certificate before expiration
- KEYSTORE_PASSWORD: Store as secret in Coolify/CI system
| Usage | Recommended Validity |
|---|---|
| Test/Development | 1 year |
| Internal documents | 3 years |
| External documents | 1-2 years |
| Size | Security | Recommendation |
|---|---|---|
| 2048 bit | Good | Minimum |
| 4096 bit | Very good | Recommended |
# Check permissions
docker exec pdf_SERVER ls -la /configs/keystore.p12
docker exec pdf_SERVER ls -la /customFiles/signatures/
# Restart container
docker compose restart stirling-pdf# Test passphrase
openssl pkcs12 -in certs/cert.p12 -passin pass:"$(cat certs/.passphrase)" -info -noout- Check
SYSTEM_SERVERCERTIFICATE_ENABLED=true - Check
KEYSTORE_PASSWORDis set - Check volume mount to
/configs/keystore.p12
This is normal for self-signed certificates. For trusted signatures:
- Use CA-signed certificate
- Import root CA in PDF reader