Pular para o conteúdo principal

Sign with CAdES - PKI Express

PKI Express signs any file with CAdES, producing a CMS file (.p7s) on your server: in two steps with the user's certificate and Web PKI, or in one step with a key held by the server. It also co-signs a .p7s and merges CMS files that sign the same content. The start-cades, complete-sig and sign-cades commands exist since PKI Express 1.0.0; merge-cms since 1.9.0.

The code is Python, from the PKI Suite samples, the only language with samples for the three operations; it uses pkiexpress (1.10.4).

Flow with the user's certificate​

The flow is the one of the PDF signature, with CadesSignatureStarter in place of PadesSignatureStarter:

  1. Browser: Web PKI reads the user's certificate; the page posts its content.
  2. Server: CadesSignatureStarter.start() returns the hash to sign, its digest algorithm and a transfer file.
  3. Browser: Web PKI signs the hash with signHash().
  4. Server: SignatureFinisher.complete() writes the .p7s.

1. Start the signature​

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 is the user's certificate, Base64-encoded, as Web PKI's readCertificate() returns it.
  • encapsulated_content (default True) puts the signed file inside the .p7s. Set it to False for a detached signature, which holds only the signature.
  • set_commitment_type() adds a commitment type, with PKI Express 1.20.0 or later.

2. Sign the hash in the browser​

The page signs toSignHash with Web PKI's signHash() and posts the result, as in the PDF signature.

3. Complete the signature​

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)

Give SignatureFinisher the same file as in the first step; complete(get_cert=True) returns the signer's certificate.

Co-sign a CMS file​

To add a signature to an existing .p7s, sign the .p7s itself: the sample's "Co-sign with another certificate" link opens the same page with the signed file as the file to sign.

  • If that .p7s is detached, give the signed content as well, with set_data_file_from_path (--data-file). TODO(docs): no sample co-signs a detached signature.

Sign with a server key​

CadesSigner signs in one call, with no browser; the sample uses a test PKCS #12 file with password 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)

The other key sources (a certificate thumbprint, a key name, a cloud certificate) are the same as for PDFs: see Sign with a server key.

Signature policies​

Constant (Python, Node.js)ValueNotes
CADES_BEScades
CADES_BES_WITH_REVOCATION_VALUEScades-rv
CADES_Tcades-tneeds a timestamp authority
PKI_BRAZIL_CADES_ADR_BASICAadrbICP-Brasil
PKI_BRAZIL_CADES_ADR_BASICA_WITH_REVOCATION_VALUEadrb-rvICP-Brasil
PKI_BRAZIL_CADES_ADR_TEMPOadrtICP-Brasil; needs a timestamp authority
PKI_BRAZIL_CADES_ADR_COMPLETAadrcICP-Brasil; needs a timestamp authority
  • ICP-Brasil AD-RA (adra, PKI_BRAZIL_CADES_ADR_ARQUIVAMENTO in Node.js, PkiBrazilCadesAdrArquivamento in Java) needs PKI Express 1.26.0 or later and a timestamp authority.
  • For the timestamp authority, see Timestamp a PDF.

Merge CMS files​

CadesSignatureEditor joins CMS files that sign the same content into one. Give it the signed content too when the signatures are detached.

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()
  • With a single CMS file and the data file, merge() turns a detached signature into one with the content inside. PKI Express 1.17.0 added merging a single file; the Python library asks for 1.18.
  • encapsulate_content (default True) decides whether the merged file holds the content.

In each language​

LibraryTwo steps (CadesSignatureStarter)Server key (CadesSigner)Merge (CadesSignatureEditor)
Node.jsyes; setFileToSignFromPath, encapsulatedContentyesyes
Javayes; setFileToSign, setEncapsulateContentyesyes
PHPyes; setFileToSignFromPath, encapsulateContentyesno
Pythonyes; set_file_to_sign_from_path, encapsulated_contentyesyes
Rubyyesnono

The samples follow the same coverage: Ruby has only the two-step sample, PHP has no merge sample.

Batches​

The CAdES batch sample works as the PDF batch: the page calls the start and complete steps for each file, and Web PKI signs every hash with the same certificate.

Errors​

A failed command ends with a PKI Express return code, such as 14 (ValidationFailed) or 32 (TimestampOptionsRequired); in Python, the library raises an exception with the output in its message. See Return codes.

See also​

View as Markdown