Short answer. Multi-signature means several signatures by different people on one document, collected in a single file. A CMS container (CAdES, .p7s) is designed to hold multiple signer entries, so "adding a second signature" means appending one more entry to the existing container without touching the first. In DSTUcrypt this is a single call, coSign(existingSignature, data) — available with the paid "Multiple signers" option (+700 UAH/month or +6,048 UAH/year per domain) — while verify returns a signers array with the details of everyone who signed.
When you need more than one signature: typical scenarios
A single signature covers far from every document workflow. In real applications, three scenarios come up most often:
- A two-party contract. The customer and the contractor sign the same contract text — each with their own key. The document counts as concluded once the container holds both signatures.
- Chain approval. An order or a payment document is signed first by the director, then by the accountant (or the other way around — per your internal rules). Each next participant adds their signature to the already-signed file.
- Meeting minutes. Resolutions of a general meeting or a committee session are signed by everyone present — there may be five signers, or ten.
In all three cases the technical task is the same: a document that already carries a valid signature needs one more added — in a way that does not break the previous ones and lets the verifier see the full list of signers.
How multi-signature works inside a CMS container
A CAdES container (a .p7s file) is a CMS SignedData structure. Simplified, it holds two things: the document data itself (in the attached variant) and a list of signer entries. Each entry is self-contained: it carries that person's certificate, the document digest computed by a hash function (DSTU GOST 34.311-95 or the modern Kupyna, DSTU 7564), the actual DSTU 4145 signature, and — depending on the level — a timestamp.
The key consequence of this design: signer entries do not depend on each other. The second signer does not "sign the first signer's signature" — they add their own entry over the same data to the list. Adding a new signer therefore does not invalidate previous signatures, and verification runs on each entry independently. That is why a single .p7s file can carry two — or ten — equal signatures.
coSign: add a second signer with one call
Manually appending a signer entry to an ASN.1 structure is a job for a crypto library, not for product code. In DSTUcrypt, the signing widget has a dedicated method for this — coSign(existingSignature, data): you pass the existing CMS container and the original document data, the user signs with their key in our iframe — and you get back an updated container that now holds two signers.
second signer — one coSign call
import { embed } from 'https://dstucrypt.io/embed/dstucrypt-embed.mjs';
const s = await embed('sign', { mount: 'modal' });
// Multi-signature: add a signer to an existing CMS
try {
const { signature, accredited } = await s.coSign(existingSignature, data);
signature.download('contract.p7s'); // updated container: now holds two signatures
} catch (e) {
if (e.code === 'MULTISIGN_NOT_ENABLED') {
// the "Multiple signers" option is not active for this domain
}
}
An important licensing detail: coSign is a paid option called "Multiple signers", priced at +700 UAH/month or +6,048 UAH/year per domain on top of the base subscription. If the option is not paid for the domain, the call is rejected with error.code === 'MULTISIGN_NOT_ENABLED' — handle this code and show the user a clear message.
Why this is convenient specifically in DSTUcrypt. All cryptography runs in the signer's browser (WebAssembly) inside our iframe: the key and password never reach your server or ours. You don't have to parse ASN.1, stitch signer entries together, or host a crypto library — coSign hides all of that behind one promise, and verify immediately returns a ready-made list of signers with a QES flag for each.
The full flow in your app: from the first signature to the last
Multi-signature is always a process spread over time: signers act in turn, often from different devices and on different days. Your application acts as the container's courier between them:
- The first signer signs the document with a regular
sign(data, { format: 'CAdES-T' })— you receive a CMS container. - Store the container (
signature.bytesorsignature.base64) in your database or file storage, along with a status like "awaiting second signer". - Invite the second signer — by email, message, or a task in your app's dashboard.
- The second signer opens the document in your UI; you call
coSign(existingSignature, data)with the stored container and the original data. - Store the updated container in place of the previous one. If there are more than two signers, repeat steps 3–5 for each subsequent one.
Note: coSign needs both the existing signature and the document data itself — so keep the original file next to the container until the whole chain is complete.
How to verify a document with multiple signatures
Verifying a multi-signature file is no different from a regular one: the verify widget detects the format from the content itself and returns a result for each signer separately — as a signers array.
verify: the signers array — one entry per signer
const v = await embed('verify', { mount: 'modal' });
// attached signature — pass null as the second argument
const { valid, signers } = await v.verify(signatureBytes, null);
for (const signer of signers) {
console.log(signer.valid, signer.accredited,
signer.signerName, signer.signerCode, signer.signingTime);
}
The most useful fields of each signers element:
| Field | What it means |
|---|---|
| valid / status | whether this signer's signature is valid (a status like TOTAL-VALID ) |
| accredited | true — the certificate is from an accredited provider, i.e. a QES; false — the signature is valid but not a QES; null — trust data unavailable |
| signerName / signerOrg | the signer's full name and organization |
| signerCode | RNOKPP (tax number) or EDRPOU — to match the signer against the one expected in your system |
| signingTime / timestampTime | the signing time and the TSP timestamp — they reveal the signing order |
| ocspStatus / crlStatus | certificate status according to revocation data |
If it is your backend (not the frontend) that decides whether to accept the document, duplicate the check with the authoritative server-side POST /api/verify — it returns the same signers array with accredited and qualified for each, but computed by the native core on the server, out of the frontend's reach.
Limitations and tips
- The format is the CAdES family.
coSignadds a signer specifically to an existing CMS container, so for multi-signature scenarios plan your workflow around CAdES formats (.p7s). - Pin down the time. Sign at a level with a timestamp (e.g.
CAdES-T): each signer then gets an independent proof of the moment of signing, and the chain order is easy to reconstruct fromsigningTime/timestampTime. - The application controls the order. Cryptographically, signer entries are equal — if your rules require "director first, then accountant", that sequence is enforced by your business logic and verified via the timestamps.
- Match signers by code. After each
coSign, it pays to runverifyand comparesignerCodeagainst the expected RNOKPP/EDRPOU — that is how you catch the case where the wrong person signed the document. - Keep the original data until the chain is complete. It is needed for every subsequent
coSigncall.
Frequently asked questions
How much does the "Multiple signers" option cost?
The "Multiple signers" option costs +700 UAH/month or +6,048 UAH/year per domain — on top of the base subscription. Without the option active, the coSign call is rejected with error.code === 'MULTISIGN_NOT_ENABLED'.
Does the second signature "break" the first one?
No. coSign adds a new signer entry to the existing CMS container without changing the previous ones. The first signature remains valid, and verify checks each signer independently, showing valid, accredited, the full name and the code for each.
How do I hand the document over to the second signer?
After the first signature, store the CMS container (e.g. the .p7s file) in your application — in a database or storage. When the second signer opens the document in your UI, call coSign(existingSignature, data): the DSTUcrypt widget opens in their browser and returns an updated container with both signatures.
How do I find out who has already signed the document?
Call verify: the result contains a signers array with valid, accredited (whether it is a QES), signerName (full name), signerOrg, signerCode (RNOKPP/EDRPOU) and signingTime for each signer. From this array it is easy to build a checklist of "who has signed, who we are still waiting for".
Read next
- How to add QES to your site in 10 minutes
- Verifying an electronic signature on your site
- Electronic signature formats: CAdES, PAdES, XAdES, ASiC
Multi-signature on your site — in 10 minutes
Ready-made widgets for signing, verification and encryption under DSTU standards. The key and password never leave the user's browser. Every new domain gets 7 days free.
Live demoHow to integrate