Pular para o conteúdo principal

PKI Express quick reference

The essentials of PKI Express on one page, with links to the full pages.

What it is for​

PKI Express creates CMS/CAdES, PDF/PAdES and XmlDSig/XAdES digital signatures from virtually any programming language, on your own Windows or Linux servers. You install it on the server and drive it through a client library for Java, Node.js, PHP, Python or Ruby; to sign with the user's certificate on a web page, it works together with Web PKI.

Choose insteadWhen it fits better
PKI SDKYour application is .NET: PKI SDK is a library for .NET applications, and the PKI guide recommends it for server signatures in .NET
Rest PKI CoreYou would rather call a REST API than install software on your servers: use it as a service at core.pki.rest or as your own instance; it also hosts the page where the user signs
Web PKIYou need the browser side: listing the user's certificates and signing with them on a web page. It complements PKI Express rather than replacing it
SignerYou want to send documents to people and collect their signatures with a ready-made workflow, as SaaS or on-premises, instead of building the flow

Install​

Two parts: PKI Express itself, on each server that runs your application, and the client package for your language.

LanguagePackageCurrent majorRegistry
PKI Express, Windows Server 2008 R2+ / Windows 7+ (needs .NET Framework 4.6.1)pkie-1.38.0-x64.msi installer; x86 and zip packages on the setup page1 (1.38.0)cdn.lacunasoftware.com
PKI Express, Linux (Ubuntu/Mint, RHEL, CentOS/Oracle Linux/Fedora)pkie-1.38.0.tar.gz (setup)1 (1.38.0)cdn.lacunasoftware.com
Javacom.lacunasoftware.pkiexpress:pki-express1 (1.23.1)Maven Central
Node.jspki-express1 (1.16.2)npm
PHP 5.5+lacuna/pki-express1 (1.17.0)Packagist
Pythonpkiexpress1 (1.10.4)PyPI
Rubypki_express1 (1.3.2)RubyGems

On Linux, extract the package to /usr/share/pkie, link /usr/share/pkie/pkie into /usr/local/bin, set the log folder and run sudo pkie activate LacunaPkiLicense.config; the Ubuntu, RHEL and CentOS pages give the commands. To update, run the new installer (Windows) or replace the contents of /usr/share/pkie with the new package (Linux).

Configuration and authentication​

License, no API key. PKI Express needs a license file, LacunaPkiLicense.config; ask Lacuna for a free trial license. Activate it once per machine: pkie activate on Linux (manual activation if the network activation fails), the PKI Express Configuration Manager (pkiemgr.exe, in the installation folder) on Windows. Your application does not read the license, and there is no service URL: the client library runs the PKI Express installed on the same machine.

Server settings are set with pkie config --set name=value (administrator privileges); pkie config --effective shows the values in use.

SettingKeysPage
Log folder and minimum levellogDir, logLevelLogging
Temporary folder (cache of CRLs and CA certificates)tempDirTemporary directory
ProxyproxyHost, proxyPort, proxyUsername, proxyPasswordProxy
LAPP, to fetch CRLs and CA certificates through *.lacunasoftware.com (support must also authorize it)lappEnabledLAPP
Keys in Azure Key VaultazureAppId, azureAppSecret, azureKeyVaultEndpointAzure
Cloud certificates (BirdID, NeoID, RemoteID, SafeID, VIDaaS, others)trustServices:<service>:clientId, trustServices:<service>:clientSecret, trustServices:<service>:endpointCloud certificates
Keys on PKCS#11 tokens and HSMspkcs11LibraryPathChangelog 1.38.0

TODO(docs): pkcs11LibraryPath (PKI Express 1.38.0) has no configuration page; only the changelog names it, and neither the client libraries nor any published help text describe it.

In your code, every operation class inherits PkiExpressOperator, whose settings the samples apply in one helper, Util.setPkiDefaults(), called by every block below:

  • what to trust: only ICP-Brasil certificates by default; add roots with addTrustedRootSync(), or the Lacuna test root with trustLacunaTestRoot (trusting custom certificates);
  • timestampAuthority, a TimestampAuthority for policies that need a timestamp: the samples use REST PKI's, https://pki.rest/tsp/a402df41-8559-47b2-a05c-be555bf66310, with a REST PKI access token (setOAuthTokenAuthentication()); user and password, and mutual SSL, also work (timestamp authority);
  • offline, to run without network access.

Each class also takes an optional PkiExpressConfig (PKI Express folder, temporary folder, transfer data folder): without it, the libraries look for PKI Express in the default Windows installation folders, and run pkie from the PATH on Linux. On Linux, only the Java library can run PKI Express 1.31.0 or later from a custom folder; see client library settings.

Core operations​

The blocks are trimmed from the Node.js samples (nodejs/expressmvc/routes/ in PkiSuiteSamples), the language with the most PKI Express samples. Policy names come from StandardSignaturePolicies and differ from Rest PKI's: see the equivalence table.

TODO(docs): apart from the Java PAdES walkthrough (how it works) and the PHP project map (PHP samples), no page documents these operations; the code below comes from the samples.

Sign a PDF with the user's certificate​

PAdES with the key on the user's computer, in two server steps: PadesSignatureStarter.start() returns the hash that Web PKI signs in the browser (signHash()), and SignatureFinisher.complete() writes the signed PDF. Step by step in Java: how it works; signature stamp: visual representation.

// POST /start: certContent is the user's certificate, read by Web PKI
const signatureStarter = new PadesSignatureStarter();
Util.setPkiDefaults(signatureStarter);
signatureStarter.signaturePolicy = StandardSignaturePolicies.PADES_BASIC_WITH_LTV;
signatureStarter.setPdfToSignFromPathSync(StorageMock.getDataPath(fileId));
signatureStarter.setCertificateFromBase64Sync(certContent);
signatureStarter.start().then((response) => { /* toSignHash, digestAlgorithm, transferFile */ });

// POST /complete: signature is the result of Web PKI's signHash()
const signatureFinisher = new SignatureFinisher();
Util.setPkiDefaults(signatureFinisher);
signatureFinisher.setFileToSignFromPathSync(StorageMock.getDataPath(fileId));
signatureFinisher.setTransferFileFromPathSync(transferFile);
signatureFinisher.signature = signature;
signatureFinisher.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
signatureFinisher.complete(true).then((result) => { /* signed PDF at outputFile */ });

Sign a PDF with a server key​

One step, no browser: PadesSigner.sign() signs with a PKCS #12 file on the server.

const signer = new PadesSigner();
Util.setPkiDefaults(signer);
signer.signaturePolicy = StandardSignaturePolicies.PADES_BASIC_WITH_LTV;
await signer.setPdfToSignFromPath(StorageMock.getDataPath(fileId));
await signer.setPkcs12FromPath(StorageMock.getSamplePkcs12Path());
signer.certPassword = '1234';
signer.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
let signerCert = await signer.sign(true);

Sign a file with CAdES​

CadesSignatureStarter follows the same start/complete flow, finished by SignatureFinisher (output .p7s); if the file is already a CMS, PKI Express co-signs it.

const signatureStarter = new CadesSignatureStarter();
Util.setPkiDefaults(signatureStarter);
signatureStarter.signaturePolicy = StandardSignaturePolicies.PKI_BRAZIL_CADES_ADR_BASICA;
signatureStarter.setFileToSignFromPathSync(StorageMock.getDataPath(fileId));
signatureStarter.setCertificateFromBase64Sync(certContent);
signatureStarter.start().then((response) => { /* same as the PDF flow */ });

Sign an XML document​

XmlSigner.sign() signs with a server key; with the user's certificate, use XmlSignatureStarter and SignatureFinisher as in the PDF flow.

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';
signer.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
let signerCert = await signer.sign(true);

Validate a signed PDF​

PadesSignatureExplorer.open() returns the file's signatures and, with validate = true, validates them; CadesSignatureExplorer does the same for CMS/CAdES.

const sigExplorer = new PadesSignatureExplorer();
Util.setPkiDefaults(sigExplorer);
sigExplorer.validate = true;
sigExplorer.setSignatureFileFromPathSync(filePath);
sigExplorer.signaturePolicy = StandardSignaturePolicies.PADES_BASIC;
sigExplorer.open().then((r) => res.render('open-pades-express', { signature: r }));

Timestamp a PDF​

PadesTimestamper.stamp() adds a timestamp to a PDF, using the operator's timestamp authority.

const stamper = new PadesTimestamper();
Util.setPkiDefaults(stamper);
stamper.setPdfFromPath(pdfPath).then(() => {
stamper.setOutputFilePath(outputFilePath);
stamper.stamp().then(() => { /* timestamped PDF at outputFilePath */ });
});

Make a printer-friendly version​

After opening the signatures with PadesSignatureExplorer, PdfMarker.apply() stamps texts and images built with PdfHelper on the PDF, such as the signers' names and a verification code.

const pdfMarker = new PdfMarker();
Util.setPkiDefaults(pdfMarker);
pdfMarker.setFileFromPathSync(pdfPath);
const pdf = new PdfHelper();
pdfMarker.marks.push(pdf.mark().onAllPages()
.onContainer(pdf.container().width(1).anchorRight(1).height(1).anchorBottom(1))
.addElement(pdf.imageElement().withOpacity(75).withImageContent(Util.getIcpBrasilLogoContent(), 'image/png')));
pdfMarker.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
pdfMarker.apply();

Authenticate with a certificate​

Authentication.start() returns a nonce and digest algorithm for Web PKI to sign; complete() checks the signature and validates the certificate.

const auth = new Authentication();
Util.setPkiDefaults(auth);
auth.setNonce(nonce); // from auth.start(), signed in the browser by Web PKI
await auth.setCertificateFromBase64(certContent);
auth.setSignature(signature);
const result = await auth.complete();
if (result.validationResults.isValid()) { const userCert = result.certificate; }

Errors​

Each PKI Express operation ends with a return code, 00 - Ok or an error from 01 to 43; signatures, authentications and inspections with validation also report validation errors such as CertificateChainRootNotTrusted. Full list: return codes. In Node.js, a failed operation rejects its promise with a CommandError whose code is the return code (ValidationError for code 14, InstallationNotFoundError when PKI Express is not found); the samples pass it to next(err). PHP throws CommandException ($e->returnCode), Ruby PkiExpress::CommandError (e.code.exitstatus); Java and Python throw a plain exception with the output in the message: see return codes in the client libraries.

ErrorMeaningWhat to do
17 - NotActivatedPKI Express is not activated on this machineActivate it with the license (setup)
14 - ValidationFailed with CertificateChainRootNotTrustedThe certificate's root is not trusted; by default, only ICP-Brasil isTrust the root: trustLacunaTestRoot or addTrustedRootSync() (trusting custom certificates)
CertificateRevocationStatusUnknownPKI Express could not access the certificate's CRLsConfigure the proxy or LAPP
32 - TimestampOptionsRequiredNo timestamp authority given, and the policy requires a timestampSet the operator's timestampAuthority (timestamp authority)

Test certificates and credentials​

Try it​

Run the demos, or start from a sample in your language:

View as Markdown