Sign an XML document - PKI Express
PKI Express signs XML documents on your server: the whole document or one element, in two steps with the user's certificate
and Web PKI, or in one step with a key held by the server.
The start-xml, complete-sig and sign-xml commands exist since PKI Express 1.0.0.
The code is Node.js, from the PKI Suite samples, with pki-express (1.16.2), and PHP
with lacuna/pki-express (1.17.0) for the element signatures. Only the Node.js, Java and PHP libraries sign XML; the Python and Ruby
libraries have no XML classes.
Flow with the user's certificate
The flow is the one of the PDF signature, with XmlSignatureStarter:
- Browser: Web PKI reads the user's certificate; the page posts its content.
- Server:
XmlSignatureStarter.start()returns the hash to sign, its digest algorithm and a transfer file. - Browser: Web PKI signs the hash with
signHash(). - Server:
SignatureFinisher.complete()with the same XML writes the signed XML.
Sign the whole document
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));
Then complete it as a PDF signature, with the XML as the file to sign and an .xml output:
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 */ });
Sign with a server key
XmlSigner signs in one call, with no browser; the sample uses a test PKCS #12 file with password 1234, or one generated from a
certificate issued by the Amplia sample.
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);
The other key sources (a certificate thumbprint, a key name, a cloud certificate) are the same as for PDFs: see Sign with a server key.
Sign one element
Set toSignElementId to the value of the Id attribute of the element to sign; PKI Express receives it as --element-id.
Brazilian electronic invoice (NF-e)
The NF-e sample signs the infNFe element with the NF-e national standard policy:
$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();
complete() then works as above, with SignatureFinisher and the same XML.
Certificate of origin (COD)
The COD sample makes two chained element signatures on a COD envelope: first the COD element, then CODEH on the file the first signature
produced, each with the COD policy and in two steps.
$signatureStarter->signaturePolicy = StandardSignaturePolicies::COD_WITH_SHA1;
$signatureStarter->toSignElementId = 'COD'; // then 'CODEH' on the signed file
Where the signature goes
Node.js (setSignatureElementInsertion) and Java (setSignatureElementInsertion with XmlElementInsertion) choose where the signature
element is inserted: append-child, prepend-child, append-sibling or prepend-sibling. This needs PKI Express 1.26.0 or later.
TODO(docs): where PKI Express inserts the signature by default.
Signature policies
| Constant (Node.js) | Value | Notes |
|---|---|---|
XADES_BES | xades | |
XML_DSIG_BASIC | basic | XMLDSig; PKI Express 1.32.0 added a SHA-1 variant for legacy signatures only |
NFE_PADRAO_NACIONAL | nfe | NF-e |
PKI_BRAZIL_XML_ADR_BASICA | adrb | ICP-Brasil |
PKI_BRAZIL_XML_ADR_TEMPO | adrt | ICP-Brasil; needs a timestamp authority |
PKI_BRAZIL_XML_ADR_COMPLETA | adrc | ICP-Brasil; needs a timestamp authority |
PKI_BRAZIL_XML_ADR_ARQUIVAMENTO | adra | ICP-Brasil, PKI Express 1.25.0 or later; needs a timestamp authority |
COD_WITH_SHA1, COD_WITH_SHA256 | cod-sha1, cod-sha256 | COD, PKI Express 1.6 or later |
COD_WITH_SHA256_EC14N | cod-sha256-ec14n | COD with the extra xml-exc-c14n# transform, PKI Express 1.36.0 |
- Java names the constants in camel case (
XadesBes,NFePadraoNacional,CodWithSHA1) and addsXadesBrazil(xades-brazil) andPkiBrazilXadesWithCertProtection. - TODO(docs): what the
xades-brazilandpki-brazil-with-cert-protectiongeneration policies produce. - TODO(docs): which client libraries set the
classic-enveloped-transformoption that PKI Express 1.27.0 added; only Java has it (setUseClassicEnvelopedTransform).
In each language
| Library | XML (start) | Element | Server key |
|---|---|---|---|
| Node.js | XmlSignatureStarter.setXmlToSignFromPath | toSignElementId | XmlSigner |
| Java | XmlSignatureStarter.setXmlToSign | setToSignElementId | XmlSigner |
| PHP | XmlSignatureStarter.setXmlToSign | toSignElementId or setToSignElementId | XmlSigner |
Errors
A failed command ends with a PKI Express return code, such as 14 (ValidationFailed) or 32 (TimestampOptionsRequired). See
Return codes.
See also
- Validate signatures, to open a signed XML
- Sign a PDF (PAdES) and Sign with CAdES
- Cloud certificate services, for the cloud-certificate XML sample
- PKI Express quick reference