Assinar documentos em lote
A sua aplicação pode fazer o usuário assinar vários documentos do Signer com o certificado dele, confirmando uma única vez. O Signer não tem um endpoint de assinatura em lote: a sua aplicação faz as chamadas de assinatura pública do Signer, início e conclusão, uma vez por documento, enquanto o Web PKI assina cada hash no navegador depois de uma única confirmação.
Os endpoints de assinatura pública desta página não estão na referência da API do Signer (Swagger). Os samples da Lacuna os descrevem como
suportados para integrações de clientes; a única fonte sobre assinatura em lote é o sample em Java
batch-pades-signature-signer.
Antes de começar
- O seu servidor chama o Signer com uma chave de API, como no restante do Guia de Integração.
- A sua página carrega o Web PKI para listar os certificados do usuário e assinar os hashes.
- O certificado precisa pertencer ao participante: o Signer verifica se ele tem o CPF do participante e, se não tiver, falha com
CpfMismatch. O sample cria cada documento com o CPF lido do certificado que o usuário escolheu.
Fluxo
- Servidor: para cada documento, obtenha a URL de ação do participante e extraia dela a chave (key) e o ticket do documento.
- Navegador: o Web PKI lê o certificado do usuário e pede que ele autorize todas as assinaturas de uma vez.
- Servidor: para cada documento, inicie a assinatura com a chave, o ticket e o certificado; o Signer devolve o hash a assinar e um token.
- Navegador: o Web PKI assina o hash.
- Servidor: conclua a assinatura com a chave, a assinatura e o token.
1. Obter a chave e o ticket de cada documento
Os documentos precisam já existir no Signer, com o usuário como signatário no fluxo deles (veja Criar documentos); o
sample cria dez, só para a demonstração.
Para cada documento, o sample chama POST /api/documents/{id}/action-url com o emailAddress e o identifier do participante e extrai a
chave e o ticket do embedUrl devolvido, cujo formato é …/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");
A chave vai na URL das chamadas de início e de conclusão; o ticket autoriza a assinatura e vai na requisição de início. Guarde o ticket no servidor, associado à chave: o sample o guarda na sessão HTTP.
- O formato da chave e do ticket só aparece nos comentários dos samples, não na referência da API.
- Para criar muitos documentos, a referência da API recomenda várias requisições (para 100 documentos, 10 requisições de 10), com
disablePendingActionNotificationsdefinido, e depoisPOST /api/users/notify-pendingquando todos estiverem criados.
2. Autorizar todas as assinaturas de uma vez
No navegador, o sample lê o certificado escolhido e chama o preauthorizeSignatures do Web PKI com o número de documentos, para que o
usuário veja um único diálogo de confirmação:
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. Iniciar, assinar e concluir cada assinatura
Início. O seu servidor envia o certificado e o ticket para POST /api/documents/keys/{key}/signature/certificate. A resposta traz
toSignHash, digestAlgorithm e token, além de success e 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);
Assinatura. O navegador assina o hash com o signHash do Web PKI:
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();
});
Conclusão. O seu servidor envia a assinatura e o token para POST /api/documents/keys/{key}/signature; a resposta traz success e
validationResults:
CompleteSignatureRequest completeRequest = new CompleteSignatureRequest();
completeRequest.setSignature(Base64.getDecoder().decode(request.getSignature()));
completeRequest.setToken(request.getToken());
signerClient.completePublicSignature(request.getId(), completeRequest);
O certificado, o hash e a assinatura trafegam em Base64. O sample executa as três etapas como um pipeline de filas, dois documentos por vez em cada etapa. Um documento que falha sai do pipeline e os demais seguem, sem nova tentativa:
startQueue.process(startSignature, {threads: 2, output: performQueue});
performQueue.process(performSignature, {threads: 2, output: completeQueue});
completeQueue.process(completeSignature, {threads: 2, completed: onBatchCompleted});
Depois da conclusão, baixe cada documento assinado pelo seu servidor, por exemplo com GET /api/documents/{id}/content?type=Signatures,
para que a chave de API nunca chegue ao navegador. Veja Baixar e listar documentos.
O que as fontes ainda não dizem
- TODO(docs): o formato do algoritmo de hash: o sample em Java diz que o Signer devolve um OID (como
2.16.840.1.101.3.4.2.1) e o converte para o nome que o Web PKI espera (comoSHA-256), enquanto o sample de assinatura pública em .NET espera um nome comoSHA256. - TODO(docs): limites de documentos por lote e de requisições (rate limiting) nas chamadas de assinatura; o sample só diz para ajustar a concorrência ao seu plano do Signer.
- TODO(docs): por quanto tempo o ticket e o token valem, e se uma chamada de conclusão pode ser repetida.
- TODO(docs): os códigos de erro das chamadas de início e de conclusão, e o que significam
successevalidationResults. - TODO(docs): se as chamadas
keys/{key}exigem o headerX-Api-Key; o cliente Java sempre o envia.
Veja também
- Criar documentos
- Assinatura embutida, para assinar um documento por vez dentro da sua aplicação
- Referência rápida do Signer