Pular para o conteúdo principal

Sign documents in a batch

Your application can have a user sign several Signer documents with their certificate, confirming only once. Signer has no batch signature endpoint: your application runs Signer's public signature calls, start and complete, once per document, while Web PKI signs every hash in the browser after a single confirmation.

Not in the API reference

The public signature endpoints on this page are not in Signer's API reference (Swagger). Lacuna's samples describe them as supported for client integrations; the only source for batch signing is the Java sample batch-pades-signature-signer.

Before you start​

  • Your server calls Signer with an API key, as in the rest of the Integration Guide.
  • Your page loads Web PKI to list the user's certificates and sign the hashes.
  • The certificate must belong to the participant: Signer checks that it has the participant's CPF, and fails with CpfMismatch otherwise. The sample creates each document with the CPF read from the certificate the user selected.

Flow​

  1. Server: for each document, get the participant's action URL and take from it the document's key and ticket.
  2. Browser: Web PKI reads the user's certificate and asks them to authorize all the signatures at once.
  3. Server: for each document, start the signature with the key, the ticket and the certificate; Signer returns the hash to sign and a token.
  4. Browser: Web PKI signs the hash.
  5. Server: complete the signature with the key, the signature and the token.

1. Get each document's key and ticket​

The documents must already exist on Signer, with the user as a signer in their flow (see Create documents); the sample creates ten, only for the demonstration. For each document, the sample calls POST /api/documents/{id}/action-url with the participant's emailAddress and identifier, then extracts the key and the ticket from the embedUrl it returns, whose shape is …/document/key/{key}/sign-embedded?ticket={ticket}:

private static final Pattern KEY_PATTERN = Pattern.compile("/key/([^/?]+)");
private static final Pattern TICKET_PATTERN = Pattern.compile("[?&]ticket=([^&]+)");
List<CreateDocumentResult> results = signerClient.createDocument(documentRequest);
UUID documentId = results.get(0).getDocumentId();

ActionUrlRequest actionUrlRequest = new ActionUrlRequest();
actionUrlRequest.setEmailAddress(SIGNER_EMAIL);
actionUrlRequest.setIdentifier(cpf);
ActionUrlResponse actionUrlResponse = signerClient.getActionUrl(documentId, actionUrlRequest);

String embedUrl = actionUrlResponse.getEmbedUrl();
String key = extractGroup(KEY_PATTERN, embedUrl, "key");
String ticket = extractGroup(TICKET_PATTERN, embedUrl, "ticket");

The key goes in the URL of the start and complete calls; the ticket authorizes the signature and goes in the start request. Keep the ticket on the server, associated with the key: the sample stores it in the HTTP session.

  • The key and ticket format is stated only in the samples' comments, not in the API reference.
  • To create many documents, the API reference recommends several requests (for 100 documents, 10 requests of 10), with disablePendingActionNotifications set, then POST /api/users/notify-pending once they are all created.

2. Authorize all the signatures at once​

In the browser, the sample reads the selected certificate and calls Web PKI's preauthorizeSignatures with the number of documents, so that the user sees a single confirmation dialog:

pki.readCertificate(selectedCertThumbprint).success(function (certEncoded) {
selectedCertContent = certEncoded;
pki.preauthorizeSignatures({
certificateThumbprint: selectedCertThumbprint,
signatureCount: formElements.documentsIds.length
}).success(startBatch); // Callback to be called if the user authorizes the signatures.
});

3. Start, sign and complete each signature​

Start. Your server sends the certificate and the ticket to POST /api/documents/keys/{key}/signature/certificate. The response has toSignHash, digestAlgorithm and token, along with success and validationResults:

String ticket = StorageMock.getSignerTicket(session, request.getId());
PublicStartSignatureRequest startRequest = new PublicStartSignatureRequest();
startRequest.setCertificate(Base64.getDecoder().decode(request.getCertContent()));
startRequest.setTicket(ticket);
StartSignatureResponse result = signerClient.startPublicSignature(request.getId(), startRequest);

Sign. The browser signs the hash with Web PKI's signHash:

pki.signHash({
thumbprint: formElements.certificateSelect.val(),
hash: step.toSignHash,
digestAlgorithm: step.digestAlgorithm
}).success(function (signature) {
step.signature = signature;
done(step);
}).error(function (error) {
renderFail(step, error);
done();
});

Complete. Your server sends the signature and the token to POST /api/documents/keys/{key}/signature; the response has success and validationResults:

CompleteSignatureRequest completeRequest = new CompleteSignatureRequest();
completeRequest.setSignature(Base64.getDecoder().decode(request.getSignature()));
completeRequest.setToken(request.getToken());
signerClient.completePublicSignature(request.getId(), completeRequest);

The certificate, the hash and the signature travel as Base64. The sample runs the three steps as a pipeline of queues, two documents at a time in each step. A document that fails leaves the pipeline and the others go on, with no retry:

startQueue.process(startSignature, {threads: 2, output: performQueue});
performQueue.process(performSignature, {threads: 2, output: completeQueue});
completeQueue.process(completeSignature, {threads: 2, completed: onBatchCompleted});

Once completed, download each signed document from your server, for example with GET /api/documents/{id}/content?type=Signatures, so that the API key never reaches the browser. See Download and list documents.

What the sources don't say yet​

  • TODO(docs): the digest algorithm format: the Java sample says Signer returns an OID (such as 2.16.840.1.101.3.4.2.1) and converts it to the name Web PKI expects (such as SHA-256), while the .NET public signature sample expects a name such as SHA256.
  • TODO(docs): limits on the number of documents per batch and rate limits for the signature calls; the sample only says to tune its concurrency to your Signer plan.
  • TODO(docs): how long the ticket and the token stay valid, and whether a complete call can be retried.
  • TODO(docs): the error codes of the start and complete calls, and what success and validationResults mean.
  • TODO(docs): whether the keys/{key} calls need the X-Api-Key header; the Java client always sends it.

See also​

View as Markdown