Assinar um documento XML - PKI Express
O PKI Express assina documentos XML no seu servidor: o documento inteiro ou um elemento, em duas etapas, com o certificado do
usuário e o Web PKI, ou em uma etapa, com uma chave que o servidor guarda.
Os comandos start-xml, complete-sig e sign-xml existem desde o PKI Express 1.0.0.
O código é Node.js, dos samples do PKI Suite, com o pki-express (1.16.2), e PHP,
com o lacuna/pki-express (1.17.0), para as assinaturas de elementos. Só as bibliotecas Node.js, Java e PHP assinam XML; as bibliotecas
Python e Ruby não têm classes de XML.
Fluxo com o certificado do usuário
O fluxo é o da assinatura de PDF, com XmlSignatureStarter:
- Navegador: o Web PKI lê o certificado do usuário; a página envia o conteúdo.
- Servidor:
XmlSignatureStarter.start()devolve o hash a assinar, o algoritmo de hash e um arquivo de transferência. - Navegador: o Web PKI assina o hash com
signHash(). - Servidor:
SignatureFinisher.complete(), com o mesmo XML, grava o XML assinado.
Assinar o documento inteiro
const signatureStarter = new XmlSignatureStarter();
Util.setPkiDefaults(signatureStarter);
signatureStarter.signaturePolicy = StandardSignaturePolicies.XADES_BES;
signatureStarter.setXmlToSignFromPathSync(StorageMock.getSampleDocPath(SampleDocs.SAMPLE_XML));
signatureStarter.setCertificateFromBase64Sync(certContent);
signatureStarter.start()
.then((response) => {
const { toSignHash } = response;
const { digestAlgorithm } = response;
const { transferFile } = response;
// render the page that signs the hash with Web PKI's signHash()
}).catch((err) => next(err));
Depois, conclua como na assinatura de PDF, com o XML como arquivo a assinar e uma saída .xml:
const signatureFinisher = new SignatureFinisher();
Util.setPkiDefaults(signatureFinisher);
signatureFinisher.setFileToSignFromPathSync(StorageMock.getSampleDocPath(SampleDocs.SAMPLE_XML));
signatureFinisher.setTransferFileFromPathSync(transferFile);
signatureFinisher.signature = signature;
const outputFile = `${uuidv4()}.xml`;
signatureFinisher.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
signatureFinisher.complete(true).then((certificate) => { /* signed XML at outputFile */ });
Assinar com uma chave no servidor
O XmlSigner assina em uma chamada, sem navegador; o sample usa um arquivo PKCS #12 de teste com a senha 1234, ou um gerado a partir de
um certificado emitido pelo sample do Amplia.
const signer = new XmlSigner();
Util.setPkiDefaults(signer);
signer.signaturePolicy = StandardSignaturePolicies.XADES_BES;
await signer.setXmlToSignFromPath(StorageMock.getSampleDocPath(SampleDocs.SAMPLE_XML));
await signer.setPkcs12FromPath(StorageMock.getSamplePkcs12Path());
signer.certPassword = '1234';
const outputFile = `${uuidv4()}.xml`;
signer.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
let signerCert = await signer.sign(true);
As outras origens de chave (thumbprint de um certificado, nome de uma chave, certificado em nuvem) são as mesmas dos PDFs: veja Assinar com uma chave no servidor.
Assinar um elemento
Defina toSignElementId com o valor do atributo Id do elemento a assinar; o PKI Express o recebe como --element-id.
Nota fiscal eletrônica (NF-e)
O sample de NF-e assina o elemento infNFe com a política do padrão nacional da NF-e:
$signatureStarter = new XmlSignatureStarter();
Util::setPkiDefaults($signatureStarter);
$signatureStarter->signaturePolicy = StandardSignaturePolicies::NFE_PADRAO_NACIONAL;
$signatureStarter->toSignElementId = 'NFe35141214314050000662550010001084271182362300';
$signatureStarter->setXmlToSign(StorageMock::getSampleDocPath(SampleDocs::SAMPLE_NFE));
$signatureStarter->setCertificateBase64($certContent);
$response = $signatureStarter->start();
Depois, o complete() funciona como acima, com o SignatureFinisher e o mesmo XML.
Certificado de origem (COD)
O sample de COD faz duas assinaturas de elemento encadeadas em um envelope COD: primeiro o elemento COD, depois o CODEH no arquivo que a
primeira assinatura gerou, cada uma com a política COD e em duas etapas.
$signatureStarter->signaturePolicy = StandardSignaturePolicies::COD_WITH_SHA1;
$signatureStarter->toSignElementId = 'COD'; // then 'CODEH' on the signed file
Onde fica a assinatura
O Node.js (setSignatureElementInsertion) e o Java (setSignatureElementInsertion com XmlElementInsertion) escolhem onde o elemento da
assinatura é inserido: append-child, prepend-child, append-sibling ou prepend-sibling. Isso exige o PKI Express 1.26.0 ou posterior.
TODO(docs): onde o PKI Express insere a assinatura por padrão.
Políticas de assinatura
| Constante (Node.js) | Valor | Observações |
|---|---|---|
XADES_BES | xades | |
XML_DSIG_BASIC | basic | XMLDSig; o PKI Express 1.32.0 acrescentou uma variante com SHA-1 só para assinaturas legadas |
NFE_PADRAO_NACIONAL | nfe | NF-e |
PKI_BRAZIL_XML_ADR_BASICA | adrb | ICP-Brasil |
PKI_BRAZIL_XML_ADR_TEMPO | adrt | ICP-Brasil; exige uma autoridade de carimbo de tempo |
PKI_BRAZIL_XML_ADR_COMPLETA | adrc | ICP-Brasil; exige uma autoridade de carimbo de tempo |
PKI_BRAZIL_XML_ADR_ARQUIVAMENTO | adra | ICP-Brasil, PKI Express 1.25.0 ou posterior; exige uma autoridade de carimbo de tempo |
COD_WITH_SHA1, COD_WITH_SHA256 | cod-sha1, cod-sha256 | COD, PKI Express 1.6 ou posterior |
COD_WITH_SHA256_EC14N | cod-sha256-ec14n | COD com a transformação extra xml-exc-c14n#, PKI Express 1.36.0 |
- O Java dá às constantes nomes em camel case (
XadesBes,NFePadraoNacional,CodWithSHA1) e acrescentaXadesBrazil(xades-brazil) ePkiBrazilXadesWithCertProtection. - TODO(docs): o que as políticas de geração
xades-brazilepki-brazil-with-cert-protectionproduzem. - TODO(docs): quais bibliotecas cliente definem a opção
classic-enveloped-transformque o PKI Express 1.27.0 acrescentou; só o Java a tem (setUseClassicEnvelopedTransform).
Em cada linguagem
| Biblioteca | XML (início) | Elemento | Chave no servidor |
|---|---|---|---|
| Node.js | XmlSignatureStarter.setXmlToSignFromPath | toSignElementId | XmlSigner |
| Java | XmlSignatureStarter.setXmlToSign | setToSignElementId | XmlSigner |
| PHP | XmlSignatureStarter.setXmlToSign | toSignElementId ou setToSignElementId | XmlSigner |
Erros
Um comando que falha termina com um código de retorno do PKI Express, como 14 (ValidationFailed) ou 32 (TimestampOptionsRequired). Veja
Códigos de retorno.
Veja também
- Validar assinaturas, para abrir um XML assinado
- Assinar um PDF (PAdES) e Assinar com CAdES
- Serviços de certificado digital em nuvem, para o sample de XML com certificado em nuvem
- Referência rápida do PKI Express