---
title: "Assinar um PDF (PAdES) - PKI Express"
description: "Como assinar um PDF (PAdES) com o PKI Express: a assinatura em duas etapas com o certificado do usuário e o Web PKI, a assinatura em uma etapa com uma chave no servidor (PKCS #12), políticas, representação visual e lotes."
canonical: "https://docs.lacunasoftware.com/articles/pki-express/pades-signature"
locale: "pt-BR"
product: "PKI Express"
last_updated: "2026-10-13"
---

# Assinar um PDF (PAdES) - PKI Express

O [PKI Express](https://docs.lacunasoftware.com/articles/pki-express) assina PDFs (PAdES) no seu servidor de duas formas: 

* **Com o certificado do usuário**, em duas etapas: o PKI Express calcula o hash a assinar, o [Web PKI](https://docs.lacunasoftware.com/articles/web-pki) o assina no
  navegador com a chave do usuário e o PKI Express grava o PDF assinado.
* **Com uma chave no servidor**, em uma etapa e sem navegador: o PKI Express assina com uma chave que o seu servidor guarda, como um arquivo
  PKCS #12 (.pfx).

As duas usam os comandos `start-pades`, `complete-sig` e `sign-pades`, presentes desde o PKI Express 1.0.0; assinar com um arquivo PKCS #12
exige a 1.3.0 ou posterior. 

O código é Node.js, dos [samples do PKI Suite](https://github.com/LacunaSoftware/PkiSuiteSamples), com o `pki-express` (1.16.2); os
samples em Java, PHP, Python e Ruby fazem o mesmo. 
Para um passo a passo do sample em Java, veja [Como funciona a assinatura com PKI Express em Java](https://docs.lacunasoftware.com/articles/pki-express/java/how-it-works).

## Fluxo com o certificado do usuário

1. **Navegador**: o Web PKI lista os certificados do usuário e lê o que ele escolher (`readCertificate()`); a página envia o conteúdo ao servidor.
1. **Servidor**: `PadesSignatureStarter.start()` devolve o hash a assinar, o algoritmo de hash e um arquivo de transferência.
1. **Navegador**: o Web PKI assina o hash com `signHash()` e envia a assinatura.
1. **Servidor**: `SignatureFinisher.complete()`, com o mesmo PDF, o arquivo de transferência e a assinatura, grava o PDF assinado.

## 1. Iniciar a assinatura

```js
const signatureStarter = new PadesSignatureStarter();
Util.setPkiDefaults(signatureStarter);
signatureStarter.signaturePolicy = StandardSignaturePolicies.PADES_BASIC_WITH_LTV;
signatureStarter.setPdfToSignFromPathSync(StorageMock.getDataPath(fileId));
signatureStarter.setCertificateFromBase64Sync(certContent);
signatureStarter.addFileReferenceSync('stamp', StorageMock.getPdfStampPath());
signatureStarter
	.setVisualRepresentationSync(PadesVisualElementsExpress.getVisualRepresentation());
signatureStarter.start()
	.then((response) => {
		const { toSignHash } = response;
		const { digestAlgorithm } = response;
		const { transferFile } = response;
		// render the page that signs the hash, with these values in hidden fields
	}).catch((err) => next(err));
```

* `certContent` é o certificado do usuário em Base64, como o `readCertificate()` do Web PKI o devolve. 
* `start()` resolve para `toSignHash`, `digestAlgorithm`, `digestAlgorithmOid` e `transferFile`, o nome de um arquivo que o PKI Express
  mantém na pasta de transfer data até a assinatura ser concluída. 
* `Util.setPkiDefaults()` aplica as configurações de confiança e a autoridade de carimbo de tempo dos samples; veja
  [Validar assinaturas](https://docs.lacunasoftware.com/articles/pki-express/validate-signatures#quais-certificados-são-confiáveis) e
  [Carimbo de tempo em PDF](https://docs.lacunasoftware.com/articles/pki-express/timestamp-pdf#a-autoridade-de-carimbo-de-tempo). 
* Se o PDF já estiver assinado, o mesmo fluxo acrescenta uma assinatura a ele. 

## 2. Assinar o hash no navegador

```js
pki.signHash({
	thumbprint: formElements.certThumbField.val(),
	hash: formElements.toSignHashField.val(),
	digestAlgorithm: formElements.digestAlgorithmField.val()
}).success(function (signature) {
	formElements.signatureField.val(signature);
	formElements.form.submit();
});
```

O thumbprint é o do certificado lido na primeira etapa; a página o guarda, com o arquivo de transferência, em campos ocultos. 
Sobre o Web PKI, veja [Web PKI](https://docs.lacunasoftware.com/articles/web-pki).

## 3. Concluir a assinatura

```js
const signatureFinisher = new SignatureFinisher();
Util.setPkiDefaults(signatureFinisher);
signatureFinisher.setFileToSignFromPathSync(StorageMock.getDataPath(fileId));
signatureFinisher.setTransferFileFromPathSync(transferFile);
signatureFinisher.signature = signature;
const outputFile = `${uuidv4()}.pdf`;
signatureFinisher.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
signatureFinisher
	.complete(true)
	.then((certificate) => { /* signed PDF at outputFile */ })
	.catch((err) => next(err));
```

* Informe ao `SignatureFinisher` o mesmo PDF da primeira etapa. 
* Com `complete(true)`, a Promise resolve para o certificado do signatário; isso exige o PKI Express 1.8 ou posterior. Sem o parâmetro, nada
  é devolvido. 
* O `SignatureFinisher` também conclui assinaturas CAdES e XML. 

## Assinar com uma chave no servidor

O `PadesSigner` assina em uma chamada, sem navegador. O sample assina com um arquivo PKCS #12 de teste, senha `1234`, ou com um gerado a
partir de um certificado emitido pelo sample do Amplia. 

```js
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';
await signer.addFileReference('stamp', StorageMock.getPdfStampPath());
await signer.setVisualRepresentation(PadesVisualElementsExpress.getVisualRepresentation());
const outputFile = `${uuidv4()}.pdf`;
signer.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
let signerCert = await signer.sign(true);
```

* O PKCS #12 também pode ser informado em bytes (`setPkcs12FromRaw`) ou em Base64 (`setPkcs12FromBase64`). 
* Em vez de um arquivo PKCS #12, a chave pode ser: 
  * um certificado instalado no servidor, pelo thumbprint (`certThumb`; PKI Express 1.3 ou posterior);
  * uma chave pelo nome (`keyName`) com o arquivo do certificado (`setCertFileFromPath`), no PKI Express 1.14 ou posterior, que acrescentou
    chaves guardadas no Azure Key Vault (veja [Integração com o Azure](https://docs.lacunasoftware.com/articles/pki-express/config/azure)); 
  * um certificado em nuvem, por uma sessão de serviço de confiança: veja [Serviços de certificado digital em nuvem](https://docs.lacunasoftware.com/articles/pki-express/config/trust-services).
* Defina `overwriteOriginalFile` em vez de `outputFile` para assinar o próprio PDF. 
* TODO(docs): em qual repositório de certificados o PKI Express procura o `certThumb` no Linux, e se a opção `setUseMachine` do Java vale lá.
* TODO(docs): como usar uma chave em token PKCS #11 ou HSM; o PKI Express 1.38.0 acrescentou o `pkcs11LibraryPath`, que nenhuma página descreve ainda. 

## Políticas de assinatura

A política é um valor de `StandardSignaturePolicies` e define o que entra na assinatura. Os samples usam `PADES_BASIC_WITH_LTV`. 

| Constante (Node.js, Python) | Valor | Observações |
|---|---|---|
| `PADES_BASIC` | `pades` | |
| `PADES_BASIC_WITH_LTV` | `pades-ltv` | |
| `PADES_T` | `pades-t` | exige uma autoridade de carimbo de tempo |
| `PKI_BRAZIL_PADES_ADR_BASICA` | `adrb` | ICP-Brasil, PKI Express 1.12.0 ou posterior |
| `PKI_BRAZIL_PADES_ADR_BASICA_WITH_LTV` | `adrb-ltv` | ICP-Brasil, PKI Express 1.12.0 ou posterior |
| `PKI_BRAZIL_PADES_ADR_TEMPO` | `adrt` | ICP-Brasil, PKI Express 1.12.0 ou posterior; exige uma autoridade de carimbo de tempo |

* Com uma política que exige carimbo de tempo, a biblioteca Node.js se recusa a executar se não houver autoridade de carimbo de tempo; o
  próprio PKI Express devolve o código 32 (`TimestampOptionsRequired`). Veja [Carimbo de tempo em PDF](https://docs.lacunasoftware.com/articles/pki-express/timestamp-pdf#a-autoridade-de-carimbo-de-tempo). 
* O Java dá às constantes nomes em camel case (`PadesBasicWithLTV`, `PkiBrazilPadesAdrBasica`, `PkiBrazilPadesAdrTempo`) e acrescenta
  `PkiBrazilPadesAdrCompleta` (`adrc`) e `PkiBrazilPadesAdrArquivamento` (`adra`); o PKI Express 1.34.0 acrescentou as políticas PAdES AD-RC e
  AD-RA da ICP-Brasil. 
* TODO(docs): a biblioteca PHP chama a política PAdES AD-RA de `pades-ad-ra` (`PADES_AD_RA`), e não `adra` como o Java; não está documentado
  qual valor o PKI Express 1.34.0 espera. 
* Os nomes das políticas diferem dos do Rest PKI: veja a [tabela de equivalência](https://docs.lacunasoftware.com/articles/pki-express/nodejs/convert-from-restpki).

## Representação visual e outras opções

* A representação visual (o carimbo desenhado na página) é um modelo JSON informado com `setVisualRepresentation()`, ou um arquivo com
  `setVisualRepresentationFile()`. As imagens que ele usa podem ser acrescentadas com `addFileReference(alias, path)` e citadas como
  `fref://alias`. Veja [Representação visual](https://docs.lacunasoftware.com/articles/pki-express/nodejs/visual-rep) e as [tags do texto](https://docs.lacunasoftware.com/articles/pki-express/pades-tags). 
* Outras opções de `PadesSignatureStarter` e `PadesSigner`, com a versão do PKI Express que cada uma exige: 

| Opção | Propriedade no Node.js | PKI Express |
|---|---|---|
| Motivo do signatário | `reason` | 1.13.0 |
| Ocultar a representação visual padrão | `suppressDefaultVisualRep` | 1.13.1 |
| Nome do campo de assinatura | `customSignatureFieldName` | 1.15.0 |
| Nível de certificação | `certificationLevel` | 1.16.0 |
| Nome do signatário e local | só Java: `setSignerName`, `setLocation` | 1.27.0 |
| Metadados do PDF | só PHP: `addMetadata(key, value)` | 1.38.0 |

## Em cada linguagem

As classes têm o mesmo nome em todas as bibliotecas cliente; os setters mudam: 

| Biblioteca | PDF e certificado (início) | Arquivo de transferência e assinatura (conclusão) | PKCS #12 (chave no servidor) |
|---|---|---|---|
| Node.js | `setPdfToSignFromPath`, `setCertificateFromBase64` | `setTransferFileFromPath`, `signature` | `setPkcs12FromPath`, `certPassword` |
| Java | `setPdfToSign`, `setCertificateBase64` | `setTransferFilePath`, `setSignature` | `setPkcs12`, `setCertPassword` |
| PHP | `setPdfToSign`, `setCertificateBase64` | `setTransferFile`, `setSignature` | `setPkcs12`, `setCertPassword` |
| Python | `set_pdf_to_sign_from_path`, `set_certificate_from_base64` | `set_transfer_file_from_path`, `signature` | `set_pkcs12_from_path`, `cert_password` |
| Ruby | `pdf_to_sign_path=`, `certificate_base64=` | `transfer_file_id=`, `signature=` | `pkcs12_path=`, `cert_password=` |

No Node.js, `start()`, `complete()` e `sign()` devolvem Promises; os setters `...Sync` são versões síncronas dos que devolvem Promises. 

## Lotes

Para assinar muitos PDFs com o mesmo certificado, o sample de lote executa as mesmas etapas de início e conclusão uma vez por documento,
chamadas por AJAX. No navegador, o `preauthorizeSignatures()` do Web PKI pede a autorização do usuário uma só vez, e depois o `signHash()`
assina cada hash. 

## Receitas médicas

O sample de receita gera um PDF com os metadados de prescrição da ICP-Brasil (OIDs como `2.16.76.1.12.1.1` e o CRM) e depois o assina com o
certificado do usuário. O nome do campo de assinatura precisa terminar com a palavra-chave `Emitente`. 

```js
signatureStarter.customSignatureFieldName = "Signature1 Emitente";
signatureStarter.signaturePolicy = StandardSignaturePolicies.PADES_BASIC;
```

TODO(docs): qual documento da ICP-Brasil define os metadados de prescrição e a palavra-chave `Emitente`.

## Erros

Um comando que falha termina com um código de retorno do PKI Express: por exemplo 14 (`ValidationFailed`), quando o certificado do
signatário não é confiável ou não é válido, ou 32 (`TimestampOptionsRequired`). No Node.js, a Promise é rejeitada com um `CommandError`. Veja
[Códigos de retorno](https://docs.lacunasoftware.com/articles/pki-express/return-codes). 

## Veja também

* [Assinar com CAdES](https://docs.lacunasoftware.com/articles/pki-express/cades-signature) e [Assinar um documento XML](https://docs.lacunasoftware.com/articles/pki-express/xml-signature)
* [Validar assinaturas](https://docs.lacunasoftware.com/articles/pki-express/validate-signatures)
* [Versão para impressão](https://docs.lacunasoftware.com/articles/pki-express/printer-friendly-version)
* [Referência rápida do PKI Express](https://docs.lacunasoftware.com/articles/pki-express/quick-reference)

**Demonstrações e exemplos**

- Demonstração: [Assinar um arquivo com seu certificado](https://try.lacunasoftware.com/pt/pki-suite/sign-a-file)
- Assinar um PDF com o PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/PadesSignatureExpressController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/pades-signature-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/pades-signature-express/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/pades_signature_express.py) · [Ruby](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/ruby/rails/app/controllers/pades_signature_express_controller.rb)
- Assinar um PDF com uma chave do servidor usando o PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/PadesServerKeyExpressController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/pades-server-key-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/pades-server-key-express/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/pades_server_key_express.py) · [Ruby](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/ruby/rails/app/controllers/pades_server_key_express_controller.rb)
- Assinar vários PDFs em lote com o PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/BatchPadesSignatureExpressController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/batch-pades-signature-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/batch-pades-signature-express/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/batch_pades_express.py) · [Ruby](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/ruby/rails/app/controllers/batch_pades_express_controller.rb)
- Assinar uma receita médica (Receita Simples) com o PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/ReceitaSimplesExpressController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/receita-simples-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/receita-simples-express/index.php) · [Ruby](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/ruby/rails/app/controllers/receita_simples_express_controller.rb)
