Skip to content

Document signing

The module that applies PAdES signatures to PDFs. Signing happens through the eID Mongolia /v3 interface using the citizen's PIN2 certificate — either in their own name or on behalf of an organisation they can represent.

UI: /me/eid/sign.

The flow

sequenceDiagram
  participant U as Citizen
  participant A as Gerege App
  participant E as eID Mongolia /v3
  U->>A: upload the PDF (POST /v1/sign/init)
  A->>A: compute the PDF digest
  A->>E: signature/notification/etsi — send the digest
  E-->>U: PIN2 push on the phone
  U->>E: approve with PIN2  ← the legal consent
  loop poll
    A->>E: session state?
  end
  E-->>A: signature confirmed
  U->>A: GET /v1/sign/{id}/download
  A-->>U: PDF with the PAdES signature
Endpoint What
POST /v1/sign/init Upload a PDF and start a signing session
POST /v1/sign/initiate Digest-based session (no file)
GET /v1/sign/status/{sid} Session state
GET /v1/sign/{id} Signature details
GET /v1/sign/{id}/download Download the signed PDF

Two kinds of stamp

At download time one of two paths applies:

  1. eID Mongolia's official stampPAdES-T plus a verification page.
  2. The server's Document-Signer — a PAdES signature embedded into the PDF with digitorus/pdfsign, carrying the citizen's name and national ID in the signature field.

The Document-Signer is mandatory in production

Without SIGN_SIGNER_CERT_FILE and SIGN_SIGNER_KEY_FILE the module will not load in production. In development a temporary certificate is generated.

Signing on behalf of an organisation

When signing for an organisation:

  • The signature itself is still made with the citizen's PIN2 certificate (not a stamp).
  • The eID session is bound to that organisation (NTRMN-<regNo>) and the representation right is verified — without it, 403.
  • On completion the confirmed organisation name is taken from the poll and the signature reason records "on behalf of …".

Representation rights come from the "authorised signers" registry in the eID PKI profile.

TSA (timestamping)

A standalone TSA connection is a later stage. Today the eID Mongolia stamp provides the PAdES-T level.

The sign relay for third-party RPs

Third-party RPs (for example template.gerege.mn) have no eID Mongolia signature RP credentials, so direct calls return 401. Gerege App fronts them as a reverse proxy:

RP → https://<host>/rp/sign/v3/*  →  eidmongolia.mn /v3/*
      Authorization: Bearer <SIGN_RELAY_TOKEN>
                            ↓ swapped for ↓
      Authorization: Bearer <the real EID_RP_SECRET>
Property Value
Path /rp/sign/* (the prefix is stripped)
RP authentication SIGN_RELAY_TOKEN, compared in constant time
The eID secret Never exposed to the RP
Disabling An empty SIGN_RELAY_TOKEN turns the relay off

Relay calls appear in the gateway log.

Signature images and stamps

What appears on the document:

Endpoint What
GET/PUT/DELETE /v1/me/signature Personal signature image
GET/PUT/DELETE /v1/me/orgstamp/{regNo} Organisation stamp
PUT /v1/me/latin-name · /org-name-latin/{regNo} Latin names

SSRF protection when fetching remote images

The asset-fetch client refuses to reach internal addresses (isDisallowedFetchIP) — loopback, private and link-local ranges are all blocked.

Security notes

  • Legal consent is the citizen's PIN2 on their phone — the server cannot substitute for it.
  • Session state is held briefly in Redis.
  • EID_DISPLAY_TEXT controls the text shown on the phone; overly long values are clamped.
  • Every signing operation is written to the audit log.