think
16px
820px

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_URL environment 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_URL is empty, signing still works but produces a B-B signature and
    the verifier reports the time source as claimed rather than tsa.
  • 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_FILE is 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.

  1. 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 are GET /api/v1/esign/ca?format=pem and ?format=der,
    gated on the esign module and document.read.
  2. 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.
  3. Push it to your trust stores via your existing endpoint management:
    - Windows / GPO: deploy the .cer to 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/… then update-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_FILE so 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.