---
title: "Assinar com CAdES - PKI Express"
description: "Como assinar qualquer arquivo com CAdES (CMS, .p7s) no PKI Express: a assinatura em duas etapas com o Web PKI, a assinatura com chave no servidor, cossinatura, assinaturas destacadas, políticas e junção de arquivos CMS."
canonical: "https://docs.lacunasoftware.com/articles/pki-express/cades-signature"
locale: "pt-BR"
product: "PKI Express"
last_updated: "2026-10-13"
---

# Assinar com CAdES - PKI Express

O [PKI Express](https://docs.lacunasoftware.com/articles/pki-express) assina qualquer arquivo com CAdES e gera um arquivo CMS (`.p7s`) no seu servidor: em duas etapas, com o
certificado do usuário e o [Web PKI](https://docs.lacunasoftware.com/articles/web-pki), ou em uma etapa, com uma chave que o servidor guarda. Ele também cossina um
`.p7s` e junta arquivos CMS que assinam o mesmo conteúdo. 
Os comandos `start-cades`, `complete-sig` e `sign-cades` existem desde o PKI Express 1.0.0; o `merge-cms`, desde a 1.9.0. 

O código é Python, dos [samples do PKI Suite](https://github.com/LacunaSoftware/PkiSuiteSamples), a única linguagem com samples das três
operações; ele usa o `pkiexpress` (1.10.4). 

## Fluxo com o certificado do usuário

O fluxo é o da [assinatura de PDF](https://docs.lacunasoftware.com/articles/pki-express/pades-signature#fluxo-com-o-certificado-do-usuário), com `CadesSignatureStarter` no lugar de
`PadesSignatureStarter`:

1. **Navegador**: o Web PKI lê o certificado do usuário; a página envia o conteúdo.
1. **Servidor**: `CadesSignatureStarter.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()`.
1. **Servidor**: `SignatureFinisher.complete()` grava o `.p7s`.

## 1. Iniciar a assinatura

```python
signature_starter = CadesSignatureStarter()
set_pki_defaults(signature_starter)
signature_starter.signature_policy = \
    standard_signature_policies.PKI_BRAZIL_CADES_ADR_BASICA
signature_starter.set_file_to_sign_from_path(
    os.path.join(current_app.config['APPDATA_FOLDER'], file_id))
signature_starter.set_certificate_from_base64(cert_content)
signature_starter.encapsulated_content = True
response = signature_starter.start()
# response['toSignHash'], response['digestAlgorithm'], response['transferFile']
```

* `cert_content` é o certificado do usuário em Base64, como o `readCertificate()` do Web PKI o devolve. 
* `encapsulated_content` (padrão `True`) põe o arquivo assinado dentro do `.p7s`. Defina `False` para uma assinatura destacada (detached),
  que contém só a assinatura. 
* `set_commitment_type()` acrescenta um tipo de compromisso (commitment type), no PKI Express 1.20.0 ou posterior. 

## 2. Assinar o hash no navegador

A página assina o `toSignHash` com o `signHash()` do Web PKI e envia o resultado, como na [assinatura de PDF](https://docs.lacunasoftware.com/articles/pki-express/pades-signature#2-assinar-o-hash-no-navegador). 

## 3. Concluir a assinatura

```python
signature_finisher = SignatureFinisher()
set_pki_defaults(signature_finisher)
signature_finisher.set_file_to_sign_from_path(
    os.path.join(current_app.config['APPDATA_FOLDER'], file_id))
signature_finisher.set_transfer_file_from_path(transfer_file_id)
signature_finisher.signature = signature
create_app_data()  # Guarantees that "app data" folder exists.
output_file = '%s.p7s' % (str(uuid.uuid4()))
signature_finisher.output_file = \
    os.path.join(current_app.config['APPDATA_FOLDER'], output_file)
signer_cert = signature_finisher.complete(get_cert=True)
```

Informe ao `SignatureFinisher` o mesmo arquivo da primeira etapa; `complete(get_cert=True)` devolve o certificado do signatário. 

## Cossinar um arquivo CMS

Para acrescentar uma assinatura a um `.p7s` existente, assine o próprio `.p7s`: o link "Co-sign with another certificate" do sample abre a
mesma página com o arquivo assinado como arquivo a assinar. 

* Se esse `.p7s` for destacado, informe também o conteúdo assinado, com `set_data_file_from_path` (`--data-file`). 
  TODO(docs): nenhum sample cossina uma assinatura destacada.

## Assinar com uma chave no servidor

O `CadesSigner` assina em uma chamada, sem navegador; o sample usa um arquivo PKCS #12 de teste com a senha `1234`. 

```python
signer = CadesSigner()
set_pki_defaults(signer)
signer.signature_policy = \
    standard_signature_policies.PKI_BRAZIL_CADES_ADR_BASICA
signer.set_file_to_sign_from_path(file_path)
signer.set_pkcs12_from_path(join(current_app.static_folder,
                                 'Pierre de Fermat.pfx'))
signer.cert_password = '1234'
signer.encapsulated_content = True
create_app_data()  # Guarantees that "app_data" folder exists.
output_file = '%s.p7s' % (str(uuid.uuid4()))
signer.output_file = join(current_app.config['APPDATA_FOLDER'], output_file)
signer_cert = signer.sign(get_cert=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](https://docs.lacunasoftware.com/articles/pki-express/pades-signature#assinar-com-uma-chave-no-servidor). 

## Políticas de assinatura

| Constante (Python, Node.js) | Valor | Observações |
|---|---|---|
| `CADES_BES` | `cades` | |
| `CADES_BES_WITH_REVOCATION_VALUES` | `cades-rv` | |
| `CADES_T` | `cades-t` | exige uma autoridade de carimbo de tempo |
| `PKI_BRAZIL_CADES_ADR_BASICA` | `adrb` | ICP-Brasil |
| `PKI_BRAZIL_CADES_ADR_BASICA_WITH_REVOCATION_VALUE` | `adrb-rv` | ICP-Brasil |
| `PKI_BRAZIL_CADES_ADR_TEMPO` | `adrt` | ICP-Brasil; exige uma autoridade de carimbo de tempo |
| `PKI_BRAZIL_CADES_ADR_COMPLETA` | `adrc` | ICP-Brasil; exige uma autoridade de carimbo de tempo |

* A AD-RA da ICP-Brasil (`adra`, `PKI_BRAZIL_CADES_ADR_ARQUIVAMENTO` no Node.js, `PkiBrazilCadesAdrArquivamento` no Java) exige o PKI Express
  1.26.0 ou posterior e uma autoridade de carimbo de tempo. 
* Sobre a autoridade de carimbo de tempo, veja [Carimbo de tempo em PDF](https://docs.lacunasoftware.com/articles/pki-express/timestamp-pdf#a-autoridade-de-carimbo-de-tempo).

## Juntar arquivos CMS

O `CadesSignatureEditor` junta em um só arquivo CMS os arquivos que assinam o mesmo conteúdo. Informe também o conteúdo assinado quando as
assinaturas forem destacadas. 

```python
signature_editor = CadesSignatureEditor()
set_pki_defaults(signature_editor)
create_app_data()
output_file = '%s.p7s' % (str(uuid.uuid4()))
signature_editor.set_data_file_from_path(os.path.join(current_app.config['APPDATA_FOLDER'], data_file_id))
signature_editor.add_cms_file_from_path(os.path.join(current_app.config['APPDATA_FOLDER'], file_id1))
signature_editor.add_cms_file_from_path(os.path.join(current_app.config['APPDATA_FOLDER'], file_id2))
signature_editor.output_file = os.path.join(os.path.join(current_app.config['APPDATA_FOLDER'], output_file))
signature_editor.merge()
```

* Com um só arquivo CMS e o arquivo de dados, `merge()` transforma uma assinatura destacada em uma com o conteúdo dentro. O PKI Express 1.17.0
  passou a aceitar um só arquivo; a biblioteca Python pede a 1.18. 
* `encapsulate_content` (padrão `True`) define se o arquivo resultante contém o conteúdo. 

## Em cada linguagem

| Biblioteca | Duas etapas (`CadesSignatureStarter`) | Chave no servidor (`CadesSigner`) | Junção (`CadesSignatureEditor`) |
|---|---|---|---|
| Node.js | sim; `setFileToSignFromPath`, `encapsulatedContent` | sim | sim |
| Java | sim; `setFileToSign`, `setEncapsulateContent` | sim | sim |
| PHP | sim; `setFileToSignFromPath`, `encapsulateContent` | sim | não |
| Python | sim; `set_file_to_sign_from_path`, `encapsulated_content` | sim | sim |
| Ruby | sim | não | não |

Os samples têm a mesma cobertura: o Ruby só tem o sample em duas etapas, e o PHP não tem sample de junção. 

## Lotes

O sample de lote CAdES funciona como o [lote de PDFs](https://docs.lacunasoftware.com/articles/pki-express/pades-signature#lotes): a página chama as etapas de início e conclusão para cada
arquivo, e o Web PKI assina todos os hashes com o mesmo certificado. 

## Erros

Um comando que falha termina com um código de retorno do PKI Express, como 14 (`ValidationFailed`) ou 32 (`TimestampOptionsRequired`); no
Python, a biblioteca lança uma exceção com a saída na mensagem. Veja [Códigos de retorno](https://docs.lacunasoftware.com/articles/pki-express/return-codes). 

## Veja também

* [Assinar um PDF (PAdES)](https://docs.lacunasoftware.com/articles/pki-express/pades-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#assinaturas-cades), para abrir um `.p7s` e extrair o conteúdo
* [Versão para impressão](https://docs.lacunasoftware.com/articles/pki-express/printer-friendly-version#para-um-arquivo-cades), um PDF que lista os signatários de um `.p7s`
* [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 arquivo em CAdES com o PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/CadesSignatureExpressController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/cades-signature-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/cades-signature-express/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/cades_signature_express.py) · [Ruby](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/ruby/rails/app/controllers/cades_signature_express_controller.rb)
- Assinar vários arquivos em lote em CAdES com o PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/BatchCadesSignatureExpressControler.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/batch-cades-signature-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/batch-cades-signature-express/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/batch_cades_express.py) · [Ruby](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/ruby/rails/app/controllers/batch_cades_express_controller.rb)
- Assinar um arquivo em CAdES com uma chave do servidor usando o PKI Express: [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/cades-server-key-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/cades-server-key-express/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/cades_server_key_express.py)
- Mesclar assinaturas CMS/CAdES com o PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/MergeCadesExpressController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/merge-cades-express.js) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/merge_cades_express.py)
