Assinar com CAdES - PKI Express
O 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, 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, 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, com CadesSignatureStarter no lugar de
PadesSignatureStarter:
- Navegador: o Web PKI lê o certificado do usuário; a página envia o conteúdo.
- Servidor:
CadesSignatureStarter.start()devolve o hash a assinar, o algoritmo de hash e um arquivo de transferência. - Navegador: o Web PKI assina o hash com
signHash(). - Servidor:
SignatureFinisher.complete()grava o.p7s.
1. Iniciar a assinatura
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 oreadCertificate()do Web PKI o devolve.encapsulated_content(padrãoTrue) põe o arquivo assinado dentro do.p7s. DefinaFalsepara 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.
3. Concluir a assinatura
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
.p7sfor destacado, informe também o conteúdo assinado, comset_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.
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.
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_ARQUIVAMENTOno Node.js,PkiBrazilCadesAdrArquivamentono 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.
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.
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ãoTrue) 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: 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.
Veja também
- Assinar um PDF (PAdES) e Assinar um documento XML
- Validar assinaturas, para abrir um
.p7se extrair o conteúdo - Versão para impressão, um PDF que lista os signatários de um
.p7s - Referência rápida do PKI Express