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:
- Browser: Web PKI reads the user's certificate; the page posts its content.
- Server:
CadesSignatureStarter.start()returns the hash to sign, its digest algorithm and a transfer file. - Browser: Web PKI signs the hash with
signHash(). - 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_contentis the user's certificate, Base64-encoded, as Web PKI'sreadCertificate()returns it.encapsulated_content(defaultTrue) puts the signed file inside the.p7s. Set it toFalsefor 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
.p7sis detached, give the signed content as well, withset_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) | Value | Notes |
|---|---|---|
CADES_BES | cades | |
CADES_BES_WITH_REVOCATION_VALUES | cades-rv | |
CADES_T | cades-t | needs a timestamp authority |
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; needs a timestamp authority |
PKI_BRAZIL_CADES_ADR_COMPLETA | adrc | ICP-Brasil; needs a timestamp authority |
- ICP-Brasil AD-RA (
adra,PKI_BRAZIL_CADES_ADR_ARQUIVAMENTOin Node.js,PkiBrazilCadesAdrArquivamentoin 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(defaultTrue) decides whether the merged file holds the content.
In each language
| Library | Two steps (CadesSignatureStarter) | Server key (CadesSigner) | Merge (CadesSignatureEditor) |
|---|---|---|---|
| Node.js | yes; setFileToSignFromPath, encapsulatedContent | yes | yes |
| Java | yes; setFileToSign, setEncapsulateContent | yes | yes |
| PHP | yes; setFileToSignFromPath, encapsulateContent | yes | no |
| Python | yes; set_file_to_sign_from_path, encapsulated_content | yes | yes |
| Ruby | yes | no | no |
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
- Sign a PDF (PAdES) and Sign an XML document
- Validate signatures, to open a
.p7sand extract its content - Printer-friendly version, a PDF that lists the signers of a
.p7s - PKI Express quick reference