Pular para o conteúdo principal

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:

  1. Navegador: o Web PKI lê o certificado do usuário; a página envia o conteúdo.
  2. Servidor: CadesSignatureStarter.start() devolve o hash a assinar, o algoritmo de hash e um arquivo de transferência.
  3. Navegador: o Web PKI assina o hash com signHash().
  4. 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 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.

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 .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.

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)ValorObservações
CADES_BEScades
CADES_BES_WITH_REVOCATION_VALUEScades-rv
CADES_Tcades-texige uma autoridade de carimbo de tempo
PKI_BRAZIL_CADES_ADR_BASICAadrbICP-Brasil
PKI_BRAZIL_CADES_ADR_BASICA_WITH_REVOCATION_VALUEadrb-rvICP-Brasil
PKI_BRAZIL_CADES_ADR_TEMPOadrtICP-Brasil; exige uma autoridade de carimbo de tempo
PKI_BRAZIL_CADES_ADR_COMPLETAadrcICP-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.

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ão True) define se o arquivo resultante contém o conteúdo.

Em cada linguagem​

BibliotecaDuas etapas (CadesSignatureStarter)Chave no servidor (CadesSigner)Junção (CadesSignatureEditor)
Node.jssim; setFileToSignFromPath, encapsulatedContentsimsim
Javasim; setFileToSign, setEncapsulateContentsimsim
PHPsim; setFileToSignFromPath, encapsulateContentsimnão
Pythonsim; set_file_to_sign_from_path, encapsulated_contentsimsim
Rubysimnãonã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​

Ver em Markdown