Documentation · Capabilities

Diia.Signature and Smart ID

The user's key lives not in a file but in an app: Diia or Privat24. The person scans a QR code, confirms the action on their phone — and you get the same CAdES signature as from a file key. No password, no file.

Both providers are connected with a single parameter to the same sign and auth widgets:

const signer = await embed('sign', { providers: ['file', 'diia', 'smartid'] });

You choose the set and the order. Want Diia only — pass ['diia'], and there will be no switcher at all.

The key point: the document goes nowhere

Only the hash — 32 bytes — is sent to the provider. The file itself never leaves the user's device even in cloud mode: the widget computes the digest inside its own iframe and sends only that.

document ──▶ [your browser: WASM core] ──▶ hash (32 bytes) ──▶ provider
   │                                                              │
   └──── stays with you ◀──── signature ◀──────────────────────────┘

So cloud signing does not change your privacy model: the service you connect sees neither the document's content nor its name — only the digest.

How the two providers differ

Diia.Signature Smart ID
App Diia Privat24
Who can use it anyone with Diia.Signature activated PrivatBank customers
Hash GOST 34.311 Kupyna-256 (DSTU 7564)
Files per session up to 10 up to 40
QR lifetime 3 minutes 3 minutes
Exchange path through our server directly browser ↔ bank

The last difference matters for your threat model. Smart ID works directly: the session is encrypted in the user's browser, and our backend takes no part in the exchange at all — the only thing that passes through it is rendering the QR image from a non-secret link. Diia is different: the app sends the signature to the endpoint registered with the Diia state enterprise — that is, to our server, from which the widget picks it up.

No need to choose a hash

Each provider accepts its own hash algorithm, and the widget substitutes the right one itself — based on the chosen method. If you pass digest explicitly, it is ignored for the cloud method: otherwise the provider would simply reject the request.

A batch of files — one user action

Several documents are signed in a single session: one QR code, one confirmation in the app — and a batch of signatures back.

const { batch } = await signer.signBatch([file1, file2, file3]);

Each file in the batch can fail individually without bringing down the rest: the result contains { fileName, signature } for the successful ones and { fileName, error } for the others.

Detached or with data

The provider signs a hash, so physically it returns a detached signature. By default the widget embeds your document into it and hands back a full .p7s — the signature value does not change in the process. If you need detached specifically — pass { detached: true }.

What the user sees in the app

The organization name, address and purpose of the operation are taken by the provider from its own side — from the data you supplied during onboarding. The signing request itself carries none of this, so the text cannot be changed from code.

For Diia these are three different fields: the name and the full legal-entity name are set during onboarding, while the operation line is the name of the offer. For login, Diia asks for a name following the template “Authorization in ‘Your platform name’”. Keep in mind that an offer cannot be edited: to change this line, a new offer is created and the settings are switched over to it.

Limitations worth knowing in advance

  • A QR code lives 3 minutes and is single-use. Didn't make it in time — you start over; this is the providers' limitation, not ours.
  • Cloud signing spends our partner account quota with the provider, so unlike the file key it is available to paid domains only. Domains on the automatic trial are not admitted by default: the Origin header is easy to spoof, and anyone could endlessly draw fresh quotas at our expense.
  • Diia's test environment requires the test app (Android — App Tester, iOS — TestFlight). The production app does not work with the sandbox.

Login with QES via the same methods

Both providers can not only sign but also confirm identity — see Login with QES. Same widget, same parameter:

const auth = await embed('auth', { providers: ['file', 'diia', 'smartid'] });