Signature Creation Application (SCA).
The CodeB Remote Signature Service — the browser signer at sign.html together with the CSC v2 API server — plays the Signature Creation Application (SCA) role defined by ETSI TS 119 432. Its job is to compose the Data-To-Be-Signed (DTBS), run the Signature Activation Protocol (SAP) so the user authorises signature creation, hand the SAD off to the Remote Wallet Secure Cryptographic Device (RWSCD, currently our software-backed ICryptoModule), and finalise the signature into a PAdES envelope. This page maps each CodeB endpoint and browser step onto the ETSI vocabulary so an auditor can trace conformance end-to-end.
ICryptoModule boundary is HSM-ready — when shipped to a certified QSCD (Azure Key Vault Managed HSM at FIPS 140-3 Level 3, or PKCS#11 against Utimaco / Thales), the same SCA continues to work unmodified against a qualified RWSCD.The four ETSI actors
ETSI TS 119 432 identifies four cooperating actors in a cloud signature. Every remote signature flow in CodeB is a specific realization of these:
- Signer — the natural person whose intent binds the signature. Signs in as themselves via OIDC.
- SCA (Signature Creation Application) — composes the DTBS, gathers signer intent, orchestrates SAP, drives the RWSCD, finalises the envelope. = sign.html + csc.ashx.
- SIC (Signature Initiator Client) — where the signer starts (a document viewer, a PMS, a legal-review app). In the reference flow the SIC and SCA are collocated inside sign.html; in the API flow the SIC is your business app calling the CSC v2 API.
- RWSCD (Remote Wallet Secure Cryptographic Device) — holds the signing key, releases operations only on presentation of a valid SAD. =
ICryptoModuleboundary. Today:SoftwareCryptoModule.cs. Roadmap:HsmAzureKeyVaultCryptoModule.cs,HsmPkcs11CryptoModule.cs.
ETSI TS 119 432 operation → CodeB endpoint mapping
Every protocol operation in the ETSI spec has a concrete corresponding call in the CodeB stack:
| ETSI TS 119 432 operation | CodeB endpoint / step | What happens |
|---|---|---|
identify-signer |
OIDC authorization code + PKCE → access token | Standard OIDC round-trip. See the OIDC sign-in cookbook. |
discover-credentials |
POST /csc/v2/credentials/list |
Returns credential IDs owned by the identified signer. |
get-credential-info |
POST /csc/v2/credentials/info |
Returns subject DN, issuer DN, serial, validity window, algorithm. |
prepare-signature |
SCA composes the CMS signedAttributes DER, hashes with SHA-256 |
Client-side inside sign.html (browser flow) or server-side inside signDoc (server flow). Produces the SAD-bound hash. |
authorize-signature (SAP init) |
POST /csc/v2/credentials/authorize |
SCAL1 → returns SAD immediately. SCAL2 → returns pending=true + WebAuthn challenge. |
authorize-signature-confirm (SAP finalize) |
POST /csc/v2/credentials/authorize/confirm |
SCAL2 only. Confirms user intent via WebAuthn assertion + PIN. Releases the SAD JWT. |
authorize-signature (SAP via wallet) |
OpenID4VP vp-start with transaction_data |
ARF §5.7.5 alternative: the EU Wallet becomes the SAP authoriser. See ARF §5.7.5 binding below. |
sign-hash |
POST /csc/v2/signatures/signHash |
RWSCD signs the SAD-bound hash. Returns raw ECDSA r||s. |
sign-doc |
POST /csc/v2/signatures/signDoc |
Server-side one-shot: SCA composes + RWSCD signs + PAdES assembly. Returns signed PDF. |
finalize-signature |
SCA assembles the PAdES envelope (client-side sign.html or server-side signDoc) | Levels B-B / B-T / B-LT / B-LTA per digital signatures cookbook. |
timestamp-signature |
POST /csc/v2/signatures/timestamp or POST /csc/v2/signatures/tsa |
RFC 3161 timestamp. Proxy to external TSA (Sectigo default) OR in-house issuer (tenant-generated TSA cert). |
revocation-info |
POST /csc/v2/ocsp |
RFC 6960 OCSP for the signer + TSA certs. Used to embed id-aa-ets-revocationValues for PAdES-B-LT. |
Signature Activation Level (SCAL) trust anchors
Per ETSI EN 419 241-1:
SCAL1
Sole control level 1 — the signer authorises through user identification only (session-bound). Suitable for low-risk, one-off signatures.
- Trigger:
?scal=1or tenant defaultCsc:DefaultScal=1 - Flow:
credentials/authorizereturns the SAD immediately - SAD lifetime: 300 s
SCAL2
Sole control level 2 — the signer authorises through user identification plus a per-signature explicit consent (PIN, WebAuthn, or wallet VP). Suitable for QES trajectories and any SCA/RWSCD deployment requiring per-signature explicit consent.
- Trigger:
?scal=2or tenant defaultCsc:DefaultScal=2 - Flow:
authorizereturnspending+ challenge →authorize/confirmafter user gesture - Second factor options: PIN, WebAuthn (FIDO2 assertion), or wallet VP with matching
transaction_data_hashes(see below)
ARF §5.7.5 — wallet as SAP authoriser
The July 2026 EU Digital Identity Wallet Architecture Reference Framework 3.0 defines a transaction_data parameter on OpenID4VP requests (Sec. 5.7.5, referencing OpenID4VP 1.0 Final §5.7.5). This is the mechanism that lets the EU Wallet substitute for the WebAuthn / PIN second factor in SCAL2 — making the wallet the authorised source of signer intent.
Wire format
The SCA appends ?transaction_data=<b64u> to /oidc.ashx?action=vp-start. The b64u value decodes to a JSON array of base64url-encoded transaction_data entries. Each entry decodes to a JSON object with:
{
"type": "qes_authorization", // ETSI TS 119 432 SAP marker
"credential_ids": [ "aloaha-eudi-pid" ], // DCQL credential id(s) the entry binds to
"documentDigests": [
{ "hash": "<b64 SHA-256 of DTBS>",
"hashAlgorithmOID": "2.16.840.1.101.3.4.2.1",
"label": "Contract v3.pdf" }
],
"processID": "sca-txn-a1b2c3d4"
}
The server (see oidc.ashx vp-start) validates that:
- the outer array has 1–16 entries;
- each entry is a base64url string that decodes to a JSON object;
- each entry has
typeand a non-emptycredential_idsarray; - the total value fits inside the 32 KiB body cap.
Any failure returns HTTP 400 invalid_request with an error_description that names the failing check (fail-closed per RASP policy).
What the wallet does
Per OpenID4VP 1.0 Final §5.7.5, when the wallet processes each entry it MUST:
- display the transaction context to the holder for explicit consent;
- compute
hash(transaction_data_entry_string)using an agreed hash algorithm (SHA-256 baseline); - include
transaction_data_hashesandtransaction_data_hashes_alginside the KB-JWT of every SD-JWT VC in the returned VP token.
What the SCA verifies
The SCA re-computes the same hash locally, compares byte-for-byte with the wallet's echoed hash, and only then mints the SAD JWT with authorization_source=wallet. The SAD payload includes transaction_data_hashes as a signed claim so downstream RWSCD calls can verify the same binding.
What ships today (2026-07-27)
- Shipped: vp-start accepts
transaction_data, validates strictly, stores onVpSession, and emits the array inside the signed JAR request object so wallets that implement §5.7.5 hash + echo correctly. - Shipped: LOUD diagnostics (
[ARF-TXDATA-DIAG]) +[METRIC] arf.txdata.accepted|rejected. - Shipped: per-tenant persistence through
SaveVpSessionToDiskso the binding survives app-pool recycles for the full 300 s TTL. - Next iteration: KB-JWT
transaction_data_hashesextraction insidevp-response+ surface into the GET-poll body + SAD-issuance hook incredentials/authorize.
DTBS / DTBSR construction
Per ETSI TS 119 432 §5.3, the SCA constructs the Data To Be Signed (DTBS) and its representation (DTBSR = SHA-256 of the CMS signedAttributes). CodeB layout:
- DTBS = the bytes of the PDF covered by the
ByteRangearray (the whole file except the/Contents<...>hex window). - signedAttributes = DER-sorted SET of five mandatory CMS attributes:
contentType,messageDigest(SHA-256 of DTBS),signingTime,signingCertificateV2(RFC 5035 ESSCertIDv2 withissuerSerial),id-aa-CMSAlgorithmProtection(RFC 6211). - DTBSR = SHA-256 of the DER-encoded
signedAttributesSET. - SAD binding = the DTBSR base64 is the
hash[0]value incredentials/authorize. The SAD JWT payload includes this same hash, andsignHashrefuses to sign any value that does not match.
This layout produces envelopes that Adobe Reader (built-in Adobe.PPKLite validator) verifies as PAdES basic profile, and that pyHanko / iText validate as PAdES-B-B / B-T / B-LT / B-LTA depending on the added unsigned attributes.
Observability + operation logging
Every protocol operation emits a diagnostic line and a metric so an auditor can reconstruct the full SCA flow from logs alone:
[CSC-SCAL2-DIAG] authorize enter tenant=<t> sub=<sub> scal=2 hashes=1 [CSC-SCAL2-DIAG] webauthn-options-minted tenant=<t> authId=<16hex> ... [CSC-SCAL2-DIAG] confirm-ok tenant=<t> authId=<16hex> factor=webauthn+pin [METRIC] csc.sad.issued tenant=<t> scal=2 factor=webauthn [ARF-TXDATA-DIAG] vp-start tenant=<t> sid=<sid> tx_entries=1 raw_bytes=488 [METRIC] arf.txdata.accepted tenant=<t> entries=1 [ARF-TXDATA-DIAG] JAR-embed sid=<sid> entries=1
The metric tags are stable across releases so downstream SIEM pipelines can build dashboards.
Standards + citations
- ETSI TS 119 432 — Protocols for Remote Digital Signature Creation. SCA / SIC / RWSCD / Signer roles + protocol operation catalogue.
- ETSI EN 419 241-1 — Trustworthy Systems Supporting Server Signing. SCAL1 vs SCAL2 definitions.
- Cloud Signature Consortium API v2 — concrete protocol realization for most SCA verbs.
- OpenID4VP 1.0 Final §5.7.5 —
transaction_dataparameter +transaction_data_hashesreturn. - EU Digital Identity Wallet Architecture Reference Framework 3.0 (21 July 2026) §5.7.5 — wallet-as-SAP profile.
- Regulation (EU) 910/2014 Articles 3(11), 26, 32 — AdES definition, validation requirements.
Questions? Ask us.