Referência rápida do PKI Express
O essencial do PKI Express em uma página, com links para as páginas completas.
Para que serve
O PKI Express cria assinaturas digitais CMS/CAdES, PDF/PAdES e XmlDSig/XAdES a partir de praticamente qualquer linguagem de programação, nos seus próprios servidores Windows ou Linux. Você o instala no servidor e o aciona por uma biblioteca cliente para Java, Node.js, PHP, Python ou Ruby; para assinar com o certificado do usuário numa página web, ele trabalha junto com o Web PKI.
| Alternativa | Quando é a melhor escolha |
|---|---|
| PKI SDK | Sua aplicação é .NET: o PKI SDK é uma biblioteca para aplicações .NET, e o guia de certificação digital o recomenda para assinaturas no servidor em .NET |
| Rest PKI Core | Você prefere chamar uma API REST a instalar software nos seus servidores: use-o como serviço em core.pki.rest ou numa instância própria; ele também hospeda a página em que o usuário assina |
| Web PKI | Você precisa da parte do navegador: listar os certificados do usuário e assinar com eles numa página web. Ele complementa o PKI Express, em vez de substituí-lo |
| Signer | Você quer enviar documentos para pessoas e colher as assinaturas delas com um fluxo pronto, como SaaS ou on-premises, em vez de construir o fluxo |
Instalação
Duas partes: o próprio PKI Express, em cada servidor que executa a sua aplicação, e o pacote cliente da sua linguagem.
| Linguagem | Pacote | Major atual | Registro | Código-fonte |
|---|---|---|---|---|
| PKI Express, Windows Server 2008 R2+ / Windows 7+ (requer o .NET Framework 4.6.1) | Instalador pkie-1.38.0-x64.msi; pacotes x86 e zip na página de instalação | 1 (1.38.0) | cdn.lacunasoftware.com | — |
| PKI Express, Linux (Ubuntu/Mint, RHEL, CentOS/Oracle Linux/Fedora) | pkie-1.38.0.tar.gz (instalação) | 1 (1.38.0) | cdn.lacunasoftware.com | — |
| Java | com.lacunasoftware.pkiexpress:pki-express | 1 (1.23.1) | Maven Central | PkiExpressJava |
| Node.js | pki-express | 1 (1.16.2) | npm | PkiExpressNode |
| PHP 5.5+ | lacuna/pki-express | 1 (1.17.0) | Packagist | PkiExpressPhp |
| Python | pkiexpress | 1 (1.10.4) | PyPI | PkiExpressPython |
| Ruby | pki_express | 1 (1.3.2) | RubyGems | PkiExpressRuby |
As bibliotecas cliente têm código aberto, na organização LacunaSoftware do GitHub, com os nomes acima, sem hífen. Cada release tem
uma tag com a versão do pacote; assim, um arquivo da versão que você usa fica, por exemplo, em
https://raw.githubusercontent.com/LacunaSoftware/PkiExpressPhp/1.17.0/src/CommandException.php. O branch padrão é master no Java
e develop nas demais.
No Linux, extraia o pacote em /usr/share/pkie, crie um link para /usr/share/pkie/pkie em /usr/local/bin, configure a pasta de logs e execute sudo pkie activate LacunaPkiLicense.config; as páginas de Ubuntu, RHEL e CentOS trazem os comandos. Para atualizar, execute o novo instalador (Windows) ou substitua o conteúdo de /usr/share/pkie pelo novo pacote (Linux).
Configuração e autenticação
Licença, sem chave de API. O PKI Express precisa de um arquivo de licença, LacunaPkiLicense.config; peça à Lacuna uma licença de testes gratuita. Ative-a uma vez em cada máquina: com pkie activate no Linux (ativação manual se a ativação pela rede falhar) e com o PKI Express Configuration Manager (pkiemgr.exe, na pasta de instalação) no Windows. A sua aplicação não lê a licença, e não há URL de serviço: a biblioteca cliente executa o PKI Express instalado na mesma máquina.
As configurações do servidor são definidas com pkie config --set name=value (com privilégios de administrador); pkie config --effective mostra os valores em uso.
| Configuração | Chaves | Página |
|---|---|---|
| Pasta e nível mínimo de log | logDir, logLevel | Logs |
| Pasta temporária (cache de LCRs e de certificados de AC) | tempDir | Pasta temporária |
| Proxy | proxyHost, proxyPort, proxyUsername, proxyPassword | Proxy |
LAPP, para obter LCRs e certificados de AC por meio de *.lacunasoftware.com (o suporte também precisa autorizar o acesso) | lappEnabled | LAPP |
| Chaves no Azure Key Vault | azureAppId, azureAppSecret, azureKeyVaultEndpoint | Azure |
| Certificados em nuvem (BirdID, NeoID, RemoteID, SafeID, VIDaaS e outros) | trustServices:<service>:clientId, trustServices:<service>:clientSecret, trustServices:<service>:endpoint | Certificados em nuvem |
| Chaves em tokens e HSMs PKCS#11 | pkcs11LibraryPath | Histórico de versões, 1.38.0 |
TODO(docs): o pkcs11LibraryPath (PKI Express 1.38.0) não tem página de configuração; só o histórico de versões o cita, e nem as bibliotecas cliente nem um texto de ajuda publicado o descrevem.
No seu código, toda classe de operação herda de PkiExpressOperator, cujas configurações os exemplos aplicam num único helper, Util.setPkiDefaults(), chamado por todos os blocos abaixo:
- em que confiar: por padrão, só em certificados ICP-Brasil; adicione raízes com
addTrustedRootSync(), ou a raiz de teste da Lacuna comtrustLacunaTestRoot(confiando em certificados que não sejam da ICP-Brasil); timestampAuthority, umTimestampAuthoritypara as políticas que exigem carimbo de tempo: os exemplos usam o do REST PKI,https://pki.rest/tsp/a402df41-8559-47b2-a05c-be555bf66310, com um token de acesso do REST PKI (setOAuthTokenAuthentication()); usuário e senha, e SSL mútuo, também funcionam (autoridade de carimbo de tempo);offline, para executar sem acesso à rede.
Cada classe também aceita um PkiExpressConfig opcional (pasta do PKI Express, pasta temporária, pasta dos arquivos de transfer data): sem ele, as bibliotecas procuram o PKI Express nas pastas de instalação padrão do Windows e executam o pkie do PATH no Linux. No Linux, só a biblioteca de Java consegue executar o PKI Express 1.31.0 ou posterior a partir de uma pasta personalizada; veja configurações da biblioteca cliente.
Operações principais
Os blocos são trechos dos exemplos em Node.js (nodejs/expressmvc/routes/ no PkiSuiteSamples), a linguagem com mais exemplos do PKI Express. Os nomes das políticas vêm de StandardSignaturePolicies e diferem dos do Rest PKI: veja a tabela de equivalência.
Cada operação tem a sua página: Assinar um PDF (PAdES), com a variante de chave no servidor; Assinar com CAdES; Assinar um documento XML; Validar assinaturas; Carimbo de tempo em PDF; Versão para impressão; Autenticação com certificado; Gerar uma chave e um arquivo PKCS #12.
Assinar um PDF com o certificado do usuário
PAdES com a chave no computador do usuário, em duas etapas no servidor: PadesSignatureStarter.start() retorna o hash que o Web PKI assina no navegador (signHash()), e SignatureFinisher.complete() grava o PDF assinado. Passo a passo em Java: como funciona; aparência da assinatura: representação visual. Detalhes: Assinar um PDF (PAdES).
// 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 */ });
Assinar um PDF com uma chave no servidor
Uma etapa, sem navegador: PadesSigner.sign() assina com um arquivo PKCS #12 no servidor. Detalhes: Assinar com uma chave no servidor.
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);
Assinar um arquivo com CAdES
O CadesSignatureStarter segue o mesmo fluxo de início e conclusão, finalizado pelo SignatureFinisher (saída .p7s); se o arquivo já for um CMS, o PKI Express o coassina. Detalhes: Assinar com CAdES.
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 */ });
Assinar um documento XML
XmlSigner.sign() assina com uma chave no servidor; com o certificado do usuário, use XmlSignatureStarter e SignatureFinisher, como no fluxo do PDF. Detalhes: Assinar um documento XML.
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);
Validar um PDF assinado
PadesSignatureExplorer.open() retorna as assinaturas do arquivo e, com validate = true, as valida; o CadesSignatureExplorer faz o mesmo para CMS/CAdES. Detalhes: Validar assinaturas.
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 }));
Aplicar carimbo de tempo a um PDF
PadesTimestamper.stamp() aplica um carimbo de tempo a um PDF, usando a autoridade de carimbo de tempo do operador. Detalhes: Carimbo de tempo em PDF.
const stamper = new PadesTimestamper();
Util.setPkiDefaults(stamper);
stamper.setPdfFromPath(pdfPath).then(() => {
stamper.setOutputFilePath(outputFilePath);
stamper.stamp().then(() => { /* timestamped PDF at outputFilePath */ });
});
Gerar uma versão para impressão
Depois de abrir as assinaturas com o PadesSignatureExplorer, PdfMarker.apply() aplica ao PDF textos e imagens montados com o PdfHelper, como os nomes dos signatários e um código de verificação. Detalhes: Versão para impressão.
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();
Gerar uma chave e um arquivo PKCS #12
KeyGenerator.generate() gera uma chave RSA (1024, 2048 ou 4096 bits; formatos json, blob, xml) e, com genCsr, um CSR em PEM
para a sua AC; depois, Pkcs12Generator.generate() junta a chave ao certificado emitido num arquivo PKCS #12 para assinar com ele. O Ruby
não tem nenhuma das duas classes. Detalhes, código PHP e os nomes em cada linguagem: Gerar uma chave e um arquivo PKCS #12.
const keyGenerator = new KeyGenerator({ keySize: 2048, genCsr: true });
const genKey = await keyGenerator.generate(); // genKey.key, genKey.csr
// ...the CA issues the certificate from genKey.csr...
const pkcs12Generator = new Pkcs12Generator({ key: genKey.key });
pkcs12Generator.setCertFileFromPathSync(certPath);
const result = await pkcs12Generator.generate(password); // result.pfx.contentRaw
Autenticar com um certificado
Authentication.start() retorna um nonce e um algoritmo de digest para o Web PKI assinar; complete() verifica a assinatura e valida o certificado. Detalhes: Autenticação com certificado.
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; }
Erros
Toda operação do PKI Express termina com um código de retorno, 00 - Ok ou um erro de 01 a 43; assinaturas, autenticações e inspeções com validação também informam erros de validação, como CertificateChainRootNotTrusted. Lista completa: códigos de retorno.
Em Node.js, uma operação que falha rejeita a sua promise com um CommandError cujo code é o código de retorno (ValidationError para o código 14, InstallationNotFoundError quando o PKI Express não é encontrado); os exemplos o repassam para next(err). O PHP lança CommandException ($e->returnCode), o Ruby PkiExpress::CommandError (e.code.exitstatus); Java e Python lançam uma exceção simples, com a saída na mensagem: veja códigos de retorno nas bibliotecas cliente.
| Erro | Significado | O que fazer |
|---|---|---|
17 - NotActivated | O PKI Express não está ativado nesta máquina | Ative-o com a licença (instalação) |
14 - ValidationFailed com CertificateChainRootNotTrusted | A raiz do certificado não é confiável; por padrão, só a ICP-Brasil é | Confie na raiz: trustLacunaTestRoot ou addTrustedRootSync() (confiando em certificados que não sejam da ICP-Brasil) |
CertificateRevocationStatusUnknown | O PKI Express não conseguiu acessar as LCRs do certificado | Configure o proxy ou o LAPP |
32 - TimestampOptionsRequired | Nenhuma autoridade de carimbo de tempo foi informada, e a política exige carimbo de tempo | Defina o timestampAuthority do operador (autoridade de carimbo de tempo) |
Certificados e credenciais de teste
- Certificados de teste: arquivos PKCS #12 da ICP de testes da Lacuna, senha
1234; nunca confie nessa ICP em produção. - O PKI Express só os aceita depois que você confia na raiz de teste da Lacuna:
trustLacunaTestRoot(Node.js, Java, PHP) outrust_lacuna_test_root(Python, Ruby). - Licença: solicite uma licença de testes gratuita e ative-a como em Configuração e autenticação.
Experimente
Rode as demos ou comece por um exemplo na sua linguagem: