Internal e-signatures — the accountable, self-hosted tier
Obscura offers electronic signing at two tiers. This document explains the free
Internal tier: what it is, what legal weight it carries, and how to operate it so
that internally-signed documents verify as trusted inside your organization.
TL;DR — the Internal tier applies a real PAdES (PDF Advanced Electronic Signature)
using an in-house Certificate Authority that Obscura generates and manages for you.
It is cryptographically sound and accountable, but it is not certified by an
Indonesian PSrE. For the strongest legal standing use the paid Peruri tier.
The two tiers
| Peruri (certified) | Internal (accountable) | |
|---|---|---|
| Indonesian term | tanda tangan tersertifikasi | tanda tangan tidak tersertifikasi |
| Trust anchor | Peruri / a licensed PSrE (Kemenkominfo) | Obscura's own in-house CA |
| Cost | Paid (per-signature / quota, via Mekari) | Free (bundled with the esign module) |
| Identity assurance | eKYC-verified signer identity | The signer is your authenticated Obscura user |
| Legal weight | Strongest — presumed valid | Valid, but lower evidentiary weight |
| Verifies as trusted... | ...everywhere (public trust chain) | ...inside your org, once you distribute the CA |
Both tiers are legally recognised electronic signatures under Indonesian law —
UU ITE (UU No. 11/2008 as amended by UU No. 19/2016) and PP No. 71/2019. The law
distinguishes a certified signature (backed by a licensed PSrE) from an uncertified
one. An uncertified signature is still admissible; it simply carries less presumptive
weight, and the party relying on it may have to demonstrate the integrity of the
signing process. The Internal tier is engineered precisely to make that demonstration
easy: a tamper-evident PAdES signature, a trusted timestamp, an auditable in-house CA,
and an honest verifier.
Use Peruri when a document must stand up against a third party who does not trust
your organization (external contracts, filings, anything adversarial). Use Internal
for the large volume of internal approvals, memos, and disposisi where the parties
already operate within — and trust — your organization's systems.
What the Internal tier gives you
1. A real PAdES signature
Signing a PDF version embeds a CMS/PAdES signature over the document's byte range and
locks the signed fields. Any later edit to the bytes breaks the signature — the tamper is
cryptographically detectable, not merely visual.
What integrity covers. The signature protects the byte range as signed. As with any
PAdES approval signature, content added afterwards in an incremental update falls
outside that range and does not invalidate the original signature — the verifier honestly
reports integrity of exactly what each signer signed. Obscura records the signed version
for every signature, so always verify the specific signed version (multi-party signing
stacks these, each covering the state it signed).
2. A trusted timestamp (PAdES B-T)
By default a PAdES signature only records the signer's claimed time (the /M entry),
which the signer controls and which a verifier cannot trust. Obscura ships a self-hosted
RFC 3161 Time-Stamp Authority (TSA). When it is enabled, every internal signature
carries a signature timestamp issued by that TSA — an independent, cryptographically
signed assertion of when the document was signed. This upgrades the signature from
B-B (basic) to B-T (basic + trusted timestamp).
- The TSA is configured with the
ESIGN_TSA_URLenvironment variable. In the standard
compose stack it points at Obscura's own in-process responder
(http://127.0.0.1:8080/api/v1/tsa), so the signer timestamps itself with no external
dependency. - When
ESIGN_TSA_URLis empty, signing still works but produces a B-B signature and
the verifier reports the time source asclaimedrather thantsa. - The timestamp is fail-closed: if the TSA is configured but unreachable, the signing
operation fails rather than silently producing an un-timestamped signature.
3. Encrypted keys at rest
The in-house CA private key and every per-user signing key are stored encrypted at
rest when a master key is configured via BLOB_ENCRYPTION_KEY_FILE (the same
age-based at-rest key used for blob storage). The keys are stored as age ASCII armor in
the database.
- If
BLOB_ENCRYPTION_KEY_FILEis unset, signing keys are stored in plaintext and
the server logs a boot warning. This is acceptable for a local demo, not for
production. - On boot, any legacy plaintext key rows are transparently re-encrypted once a master
key is present. An undecryptable key fails closed on use (it will never be treated
as a usable key).
4. An honest verifier
Older builds collapsed verification into a single "valid" boolean that was effectively
always true (the library trusted certificates embedded in the PDF itself — meaningless
for a self-issued chain). The Internal tier ships a rewritten verifier that reports
three distinct, honest signals per signature:
| Signal | Meaning |
|---|---|
| Integrity | The CMS signature is cryptographically intact over its byte range (green) or the bytes were changed (red). |
| Issuer trusted | The signer's certificate chains to your in-house CA (green) — not to whatever cert happens to be embedded in the PDF — or it does not (gray). |
| Timestamped | An RFC 3161 signature timestamp is present, with its time and source (TSA vs claimed). |
You can run this any time from a document's Signatures panel via the Verify
signatures button. It re-reads the stored PDF bytes and re-checks them — it does not
trust a cached result. Endpoint: GET /api/v1/documents/{docID}/versions/{version}/verify.
Distributing the trust anchor
For internally-signed documents to verify as trusted (green "Issuer trusted") in
Obscura — and, importantly, in external tools like Adobe Acrobat or the OS trust
store — the relying machines must trust your in-house CA. You do this once, per
organization, using standard endpoint-management tooling.
- Download the CA. As an admin, go to Admin → Licensing → Organization CA. You'll
see the CA's subject, its validity window, and its SHA-256 fingerprint. Download it
as PEM (obscura-ca.pem) or DER (obscura-ca.cer).
- Under the hood these areGET /api/v1/esign/ca?format=pemand?format=der,
gated on theesignmodule anddocument.read. - Verify the fingerprint out-of-band. Before you trust the certificate anywhere,
confirm its SHA-256 fingerprint through a channel independent of the download
(read it out on a call, compare against a value the admin published separately). This
is what prevents a man-in-the-middle from substituting a rogue CA. You can recompute it
from the downloaded DER:
openssl x509 -inform der -in obscura-ca.cer -noout -fingerprint -sha256
It must match the fingerprint shown in the admin panel. - Push it to your trust stores via your existing endpoint management:
- Windows / GPO: deploy the.certo Trusted Root Certification Authorities via a
Group Policy certificate deployment.
- macOS / MDM (Jamf, Intune, etc.): push a certificate payload adding the CA to the
system keychain as trusted.
- Adobe Acrobat / Reader: add the CA to the Adobe Approved Trust List (AATL is for
public CAs; for an internal CA use a managed trust profile / registry policy so
Acrobat shows the signature as valid).
- Linux fleets: drop the PEM into the system trust anchors
(/usr/local/share/ca-certificates/…thenupdate-ca-certificates).
Once the CA is trusted on a machine, every document signed by Obscura's Internal tier
shows as trusted there — no per-document action needed.
Operating checklist (production)
- [ ] Set
BLOB_ENCRYPTION_KEY_FILEso CA + signing keys are encrypted at rest (no boot
warning). - [ ] Set
ESIGN_TSA_URL(the compose default self-calls the built-in TSA) so signatures
are B-T (trusted timestamp), not B-B. - [ ] Download the Organization CA, verify its fingerprint out-of-band, and distribute
it to your trust stores via MDM/GPO. - [ ] Spot-check with Verify signatures on a freshly-signed document: expect
integrity ✓, issuer trusted ✓, timestamped ✓ with source TSA.
Roadmap — external trust
Today the in-house CA is a root of its own: trusted wherever you distribute it, and
nowhere else by default. The planned next step (tracked in ROADMAP.md) is to let the org
CA be chained under a publicly-trusted or PSrE-anchored root, so that internally-signed
documents can verify as trusted outside your organization without each relying party
importing your CA. Until then, the Internal tier is deliberately scoped to accountable,
internally-trusted signing, and Peruri remains the tier for externally-certified
signatures.