Electronic signature
pdf signpdf verify_signaturespdf verify_signaturesxml signxml verify_signaturexml verify_signaturesign create_test_keystoresign certificate_info
pdf sign <in_path> <out_path> <keystore_path> <json_params>
Description
Sign a PDF: PAdES style detached CMS (ETSI.CAdES.detached, SHA-256), certificate chain embedded, incremental save so that the original bytes and the earlier signatures stay intact. The file is written beside the target, verified, then moved in one step: a failure leaves the target untouched. An expired or not yet valid certificate is refused unless allow_expired. Refused: an encrypted PDF, a PDF certified with no change allowed, a visible signature on a certified PDF. Returns file, bytes, signer, issuer, key, subfilter, chain_length, timestamped, visible
Sign a PDF: PAdES style detached CMS (ETSI.CAdES.detached, SHA-256), certificate chain embedded, incremental save so that the original bytes and the earlier signatures stay intact. The file is written beside the target, verified, then moved in one step: a failure leaves the target untouched. An expired or not yet valid certificate is refused unless allow_expired. Refused: an encrypted PDF, a PDF certified with no change allowed, a visible signature on a certified PDF. Returns file, bytes, signer, issuer, key, subfilter, chain_length, timestamped, visible
Parameters
in_path: The PDF to sign - String - required
out_path: The signed PDF to write (it can be in_path): replaced in one move, only after its signature was checked - String - required
keystore_path: The keystore: .p12, .pfx or .jks (or the parameter keystore_type) - String - required
json_params: password or password_env (required); key_password or key_password_env; keystore_type (pkcs12, jks); alias (required if there are several keys); reason, location, contact; tsa_url (RFC 3161 timestamp authority: https, or http for localhost); visible {page (1), x, y, width, height in points from the bottom left corner of the unrotated page, text (default: signer and date; characters outside Latin-1 become ?; cut to the box)}; allow_expired (false) - String - required
in_path: The PDF to sign - String - required
out_path: The signed PDF to write (it can be in_path): replaced in one move, only after its signature was checked - String - required
keystore_path: The keystore: .p12, .pfx or .jks (or the parameter keystore_type) - String - required
json_params: password or password_env (required); key_password or key_password_env; keystore_type (pkcs12, jks); alias (required if there are several keys); reason, location, contact; tsa_url (RFC 3161 timestamp authority: https, or http for localhost); visible {page (1), x, y, width, height in points from the bottom left corner of the unrotated page, text (default: signer and date; characters outside Latin-1 become ?; cut to the box)}; allow_expired (false) - String - required
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; pdf create "tuto_s"; pdf page add "tuto_s" "A4" "portrait"; pdf text add "tuto_s" 1 40 40 "Invoice F2026-0042, total 1250.00 EUR" "font=HELVETICA_BOLD;size=14"; pdf save "tuto_s" "tmp/tuto_sign/in.pdf"; pdf close "tuto_s"; json load "tuto_k" (pdf sign "tmp/tuto_sign/in.pdf" "tmp/tuto_sign/signed.pdf" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reason\": \"Approval\", \"location\": \"Saint-Denis\"}"); -> "[res]" (concat "signed=" (json select "tuto_k" "/signed") ", " "signer=" (json select "tuto_k" "/signer") ", " "subfilter=" (json select "tuto_k" "/subfilter") ", " "chain_length=" (json select "tuto_k" "/chain_length") ", " "timestamped=" (json select "tuto_k" "/timestamped") ", " "visible=" (json select "tuto_k" "/visible")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
signed=true, signer=CN=Test Signer,O=Demo, subfilter=ETSI.CAdES.detached, chain_length=1, timestamped=false, visible=false
Complete: a visible signature on page 1
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; pdf create "tuto_s"; pdf page add "tuto_s" "A4" "portrait"; pdf text add "tuto_s" 1 40 40 "Invoice F2026-0042, total 1250.00 EUR" "font=HELVETICA_BOLD;size=14"; pdf save "tuto_s" "tmp/tuto_sign/in.pdf"; pdf close "tuto_s"; json load "tuto_k" (pdf sign "tmp/tuto_sign/in.pdf" "tmp/tuto_sign/signed.pdf" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reason\": \"Approval\", \"visible\": {\"page\": 1, \"x\": 40, \"y\": 60, \"width\": 220, \"height\": 50, \"text\": \"Signed for Demo, invoice F2026-0042\"}}"); -> "[res]" (concat "signed=" (json select "tuto_k" "/signed") ", " "visible=" (json select "tuto_k" "/visible") ", " "page=" (json select "tuto_k" "/page")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
signed=true, visible=true, page=1
A timestamp: the result comes from a real call to a local test authority (a small server made with BouncyCastle on localhost), not a public one. Its proof of time is accepted only if the authority is in the trust store of the verifier
admin
pdf sign "tmp/tuto_sign/in.pdf" "tmp/tuto_sign/signed.pdf" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"tsa_url\": \"http://localhost:20873/ok\"}"
mentdb
{"file": "tmp/tuto_sign/signed.pdf", "bytes": 133383, "signed": true, "signer": "CN=Test Signer,O=Demo", "issuer": "CN=Test Signer,O=Demo", "key": "RSA 2048", "subfilter": "ETSI.CAdES.detached", "chain_length": 1, "timestamped": true, "visible": false}
Used in use cases (1 recipe)
Security engineer › Sign a PDF and check it: not modified is not the same as trusted
Security engineer › Sign a PDF and check it: not modified is not the same as trusted
Often used with
equal · file delete · file exist · file mkdir · pdf close · pdf create · pdf page add · pdf save
equal · file delete · file exist · file mkdir · pdf close · pdf create · pdf page add · pdf save
pdf verify_signatures <in_path>
Description
Check every signature of a PDF. Without a trust store, valid is always false: see the description of this page. The signature value is read in the gap of the ByteRange, which must hold it and nothing else. Returns file_length, signature_count, status, valid, integrity_ok, all_revisions_intact, document_covered, unsigned_bytes_at_end, trust_store_used, all_trusted, revocation_checked (false), unsigned_update, changed_after_first_signature, content_changed_after_first_signature, certification_level, docmdp_compliance, revisions, warnings, and signatures: type (signature, document_timestamp), subfilter, signer, issuer, serial, key, signing_time, signing_time_source, reason, location, contact, status, valid, integrity_ok, revision_intact, digest_valid, signature_valid, covers_whole_document, bytes_after_signature, byte_range, digest_algorithm, signature_algorithm, signing_certificate_match, certificate_valid_at_signing, trusted, timestamp_present, timestamp_valid, timestamp_time, timestamp_authority, timestamp_trusted, problems
Check every signature of a PDF. Without a trust store, valid is always false: see the description of this page. The signature value is read in the gap of the ByteRange, which must hold it and nothing else. Returns file_length, signature_count, status, valid, integrity_ok, all_revisions_intact, document_covered, unsigned_bytes_at_end, trust_store_used, all_trusted, revocation_checked (false), unsigned_update, changed_after_first_signature, content_changed_after_first_signature, certification_level, docmdp_compliance, revisions, warnings, and signatures: type (signature, document_timestamp), subfilter, signer, issuer, serial, key, signing_time, signing_time_source, reason, location, contact, status, valid, integrity_ok, revision_intact, digest_valid, signature_valid, covers_whole_document, bytes_after_signature, byte_range, digest_algorithm, signature_algorithm, signing_certificate_match, certificate_valid_at_signing, trusted, timestamp_present, timestamp_valid, timestamp_time, timestamp_authority, timestamp_trusted, problems
Parameters
in_path: The PDF - String - required
in_path: The PDF - String - required
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; pdf create "tuto_s"; pdf page add "tuto_s" "A4" "portrait"; pdf text add "tuto_s" 1 40 40 "Invoice F2026-0042, total 1250.00 EUR" "font=HELVETICA_BOLD;size=14"; pdf save "tuto_s" "tmp/tuto_sign/in.pdf"; pdf close "tuto_s"; pdf sign "tmp/tuto_sign/in.pdf" "tmp/tuto_sign/signed.pdf" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reason\": \"Approval\", \"location\": \"Saint-Denis\"}"; json load "tuto_k" (pdf verify_signatures "tmp/tuto_sign/signed.pdf"); -> "[res]" (concat "status=" (json select "tuto_k" "/status") ", " "valid=" (json select "tuto_k" "/valid") ", " "integrity_ok=" (json select "tuto_k" "/integrity_ok") ", " "signature_count=" (json select "tuto_k" "/signature_count") ", " "document_covered=" (json select "tuto_k" "/document_covered") ", " "digest_valid=" (json select "tuto_k" "/signatures/[0]/digest_valid") ", " "signature_valid=" (json select "tuto_k" "/signatures/[0]/signature_valid") ", " "covers_whole_document=" (json select "tuto_k" "/signatures/[0]/covers_whole_document") ", " "signer=" (json select "tuto_k" "/signatures/[0]/signer") ", " "reason=" (json select "tuto_k" "/signatures/[0]/reason") ", " "subfilter=" (json select "tuto_k" "/signatures/[0]/subfilter")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
status=intact_untrusted, valid=false, integrity_ok=true, signature_count=1, document_covered=true, digest_valid=true, signature_valid=true, covers_whole_document=true, signer=CN=Test Signer,O=Demo, reason=Approval, subfilter=ETSI.CAdES.detached
Complete: one byte added after the signature: the digest is still right, the document is no longer covered, so it is tampered
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; pdf create "tuto_s"; pdf page add "tuto_s" "A4" "portrait"; pdf text add "tuto_s" 1 40 40 "Invoice F2026-0042, total 1250.00 EUR" "font=HELVETICA_BOLD;size=14"; pdf save "tuto_s" "tmp/tuto_sign/in.pdf"; pdf close "tuto_s"; pdf sign "tmp/tuto_sign/in.pdf" "tmp/tuto_sign/signed.pdf" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reason\": \"Approval\", \"location\": \"Saint-Denis\"}"; file append "tmp/tuto_sign/signed.pdf" " "; json load "tuto_k" (pdf verify_signatures "tmp/tuto_sign/signed.pdf"); -> "[res]" (concat "status=" (json select "tuto_k" "/status") ", " "integrity_ok=" (json select "tuto_k" "/integrity_ok") ", " "document_covered=" (json select "tuto_k" "/document_covered") ", " "unsigned_bytes_at_end=" (json select "tuto_k" "/unsigned_bytes_at_end") ", " "digest_valid=" (json select "tuto_k" "/signatures/[0]/digest_valid") ", " "covers_whole_document=" (json select "tuto_k" "/signatures/[0]/covers_whole_document") ", " "bytes_after_signature=" (json select "tuto_k" "/signatures/[0]/bytes_after_signature")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
status=tampered, integrity_ok=false, document_covered=false, unsigned_bytes_at_end=1, digest_valid=true, covers_whole_document=false, bytes_after_signature=1
Complete: a PDF without signature
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; pdf create "tuto_s"; pdf page add "tuto_s" "A4" "portrait"; pdf text add "tuto_s" 1 40 40 "Invoice F2026-0042, total 1250.00 EUR" "font=HELVETICA_BOLD;size=14"; pdf save "tuto_s" "tmp/tuto_sign/in.pdf"; pdf close "tuto_s"; json load "tuto_k" (pdf verify_signatures "tmp/tuto_sign/in.pdf"); -> "[res]" (concat "status=" (json select "tuto_k" "/status") ", " "valid=" (json select "tuto_k" "/valid") ", " "signature_count=" (json select "tuto_k" "/signature_count")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
status=unsigned, valid=false, signature_count=0
pdf verify_signatures <in_path> <json_params>
Description
Check with parameters: trust_store (the .p12, .pfx or .jks that holds the certificates you accept), trust_store_type, password or password_env (of the trust store), now (ISO date of the check, default the current time). status is trusted and valid is true when every signature is intact and its certificate path reaches the trust store, at the time of a timestamp from an accepted authority, else at now. No revocation check
Check with parameters: trust_store (the .p12, .pfx or .jks that holds the certificates you accept), trust_store_type, password or password_env (of the trust store), now (ISO date of the check, default the current time). status is trusted and valid is true when every signature is intact and its certificate path reaches the trust store, at the time of a timestamp from an accepted authority, else at now. No revocation check
Parameters
in_path: The PDF - String - required
json_params: The parameters - String - required
in_path: The PDF - String - required
json_params: The parameters - String - required
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; pdf create "tuto_s"; pdf page add "tuto_s" "A4" "portrait"; pdf text add "tuto_s" 1 40 40 "Invoice F2026-0042, total 1250.00 EUR" "font=HELVETICA_BOLD;size=14"; pdf save "tuto_s" "tmp/tuto_sign/in.pdf"; pdf close "tuto_s"; pdf sign "tmp/tuto_sign/in.pdf" "tmp/tuto_sign/signed.pdf" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reason\": \"Approval\", \"location\": \"Saint-Denis\"}"; json load "tuto_a" (pdf verify_signatures "tmp/tuto_sign/signed.pdf" "{\"trust_store\": \"tmp/tuto_sign/test.p12\", \"password\": \"test-only-password\"}"); json load "tuto_b" (pdf verify_signatures "tmp/tuto_sign/signed.pdf" "{\"trust_store\": \"tmp/tuto_sign/test.p12\", \"password\": \"test-only-password\", \"now\": \"2099-01-01\"}"); -> "[res]" (concat "today: " (json select "tuto_a" "/status") ", valid=" (json select "tuto_a" "/valid") " | in 2099: " (json select "tuto_b" "/status") ", valid=" (json select "tuto_b" "/valid")); json unload "tuto_a"; json unload "tuto_b"; file delete "tmp/tuto_sign"; [res]
mentdb
today: trusted, valid=true | in 2099: intact_untrusted, valid=false
Complete: a second signer that is not in the trust store: the crypto is fine, the document is not trusted, each revision names its signer
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; pdf create "tuto_s"; pdf page add "tuto_s" "A4" "portrait"; pdf text add "tuto_s" 1 40 40 "Invoice F2026-0042, total 1250.00 EUR" "font=HELVETICA_BOLD;size=14"; pdf save "tuto_s" "tmp/tuto_sign/in.pdf"; pdf close "tuto_s"; pdf sign "tmp/tuto_sign/in.pdf" "tmp/tuto_sign/signed.pdf" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reason\": \"Approval\", \"location\": \"Saint-Denis\"}"; sign create_test_keystore "tmp/tuto_sign/other.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Mallory\", \"days\": 30, \"algorithm\": \"EC\"}"; pdf sign "tmp/tuto_sign/signed.pdf" "tmp/tuto_sign/signed2.pdf" "tmp/tuto_sign/other.p12" "{\"password\": \"test-only-password\"}"; json load "tuto_k" (pdf verify_signatures "tmp/tuto_sign/signed2.pdf" "{\"trust_store\": \"tmp/tuto_sign/test.p12\", \"password\": \"test-only-password\"}"); -> "[res]" (concat "status=" (json select "tuto_k" "/status") ", " "valid=" (json select "tuto_k" "/valid") ", " "integrity_ok=" (json select "tuto_k" "/integrity_ok") ", " "changed_after_first_signature=" (json select "tuto_k" "/changed_after_first_signature") ", " "content_changed_after_first_signature=" (json select "tuto_k" "/content_changed_after_first_signature") ", " "docmdp_compliance=" (json select "tuto_k" "/docmdp_compliance") ", " "signer=" (json select "tuto_k" "/revisions/[0]/signer") ", " "signer=" (json select "tuto_k" "/revisions/[1]/signer") ", " "trusted=" (json select "tuto_k" "/revisions/[1]/trusted") ", " "covers_previous=" (json select "tuto_k" "/revisions/[1]/covers_previous") ", " "changes_allowed=" (json select "tuto_k" "/revisions/[1]/changes_allowed")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
status=intact_untrusted, valid=false, integrity_ok=true, changed_after_first_signature=true, content_changed_after_first_signature=false, docmdp_compliance=not_certified, signer=CN=Test Signer,O=Demo, signer=CN=Mallory, trusted=false, covers_previous=true, changes_allowed=no_docmdp_set
Used in use cases (1 recipe)
Security engineer › Sign a PDF and check it: not modified is not the same as trusted
Security engineer › Sign a PDF and check it: not modified is not the same as trusted
Often used with
equal · file delete · file exist · file mkdir · pdf close · pdf create · pdf page add · pdf save
equal · file delete · file exist · file mkdir · pdf close · pdf create · pdf page add · pdf save
xml sign <in_path_or_text> <out_path_or_empty> <keystore_path> <json_params>
Description
Sign an XML document: XML-DSig, SHA-256, exclusive canonicalization, RSA or ECDSA, the certificate chain in KeyInfo. The input has no DOCTYPE. The result is checked before being returned or written, always in UTF-8, without any indentation added. Returns signed, mode, reference, xades, signer, issuer, key, chain_length, and xml (no output file) or file and bytes
Sign an XML document: XML-DSig, SHA-256, exclusive canonicalization, RSA or ECDSA, the certificate chain in KeyInfo. The input has no DOCTYPE. The result is checked before being returned or written, always in UTF-8, without any indentation added. Returns signed, mode, reference, xades, signer, issuer, key, chain_length, and xml (no output file) or file and bytes
Parameters
in_path_or_text: The XML text or the path of an XML file (a value whose first character is an opening angle bracket is read as text) - String - required
out_path_or_empty: The file to write, or empty to get the signed text in the field xml - String - required
keystore_path: The keystore: .p12, .pfx or .jks (or the parameter keystore_type) - String - required
json_params: password or password_env (required); key_password or key_password_env; keystore_type; alias; mode (enveloped: the signature is added to the document, enveloping: the document goes inside the signature); reference_id (enveloped: the id of the element to sign, the signature goes inside it, default the whole document; enveloping: the id of the Object); xades (false: true adds XAdES-BES QualifyingProperties as a signed reference); allow_expired (false) - String - required
in_path_or_text: The XML text or the path of an XML file (a value whose first character is an opening angle bracket is read as text) - String - required
out_path_or_empty: The file to write, or empty to get the signed text in the field xml - String - required
keystore_path: The keystore: .p12, .pfx or .jks (or the parameter keystore_type) - String - required
json_params: password or password_env (required); key_password or key_password_env; keystore_type; alias; mode (enveloped: the signature is added to the document, enveloping: the document goes inside the signature); reference_id (enveloped: the id of the element to sign, the signature goes inside it, default the whole document; enveloping: the id of the Object); xades (false: true adds XAdES-BES QualifyingProperties as a signed reference); allow_expired (false) - String - required
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; json load "tuto_k" (xml sign "<invoice id=\"F1\"><line id=\"L1\">Coffee</line><total>12.50</total></invoice>" "" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\"}"); -> "[res]" (concat "signed=" (json select "tuto_k" "/signed") ", " "mode=" (json select "tuto_k" "/mode") ", " "reference=" (json select "tuto_k" "/reference") ", " "xades=" (json select "tuto_k" "/xades") ", " "signer=" (json select "tuto_k" "/signer")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
signed=true, mode=enveloped, reference=, xades=false, signer=CN=Test Signer,O=Demo
Complete: one element with XAdES-BES, written to a file
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; json load "tuto_k" (xml sign "<invoice id=\"F1\"><line id=\"L1\">Coffee</line><total>12.50</total></invoice>" "tmp/tuto_sign/signed.xml" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reference_id\": \"L1\", \"xades\": true}"); -> "[res]" (concat "signed=" (json select "tuto_k" "/signed") ", " "mode=" (json select "tuto_k" "/mode") ", " "reference=" (json select "tuto_k" "/reference") ", " "xades=" (json select "tuto_k" "/xades")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
signed=true, mode=enveloped, reference=#L1, xades=true
Complete: enveloping
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; json load "tuto_k" (xml sign "<invoice id=\"F1\"><line id=\"L1\">Coffee</line><total>12.50</total></invoice>" "" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"mode\": \"enveloping\", \"reference_id\": \"doc\"}"); -> "[res]" (concat "signed=" (json select "tuto_k" "/signed") ", " "mode=" (json select "tuto_k" "/mode") ", " "reference=" (json select "tuto_k" "/reference")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
signed=true, mode=enveloping, reference=#doc
xml verify_signature <in_path_or_text>
Description
Check the signatures of an XML document with the secure validation of the JDK. Without a trust store, valid is always false. Returns status, valid, integrity_ok, core_valid (the cryptography only), signature_count, signer, issuer, references [{uri, type, valid}], signed_elements [{uri, element, namespace, path, whole_document, enveloped_signature_excluded, content}], whole_document_signed, xades, xades_details {signing_time, signing_certificate_match}, signing_time, signing_time_source, certificate_valid_at_signing, trusted, trust_store_used, revocation_checked (false), problems, and signatures when there are several. The first signature fills the main fields; status, valid and trusted concern them all. trusted needs a certificate valid at the XAdES signing time (claimed), else at the time of the check
Check the signatures of an XML document with the secure validation of the JDK. Without a trust store, valid is always false. Returns status, valid, integrity_ok, core_valid (the cryptography only), signature_count, signer, issuer, references [{uri, type, valid}], signed_elements [{uri, element, namespace, path, whole_document, enveloped_signature_excluded, content}], whole_document_signed, xades, xades_details {signing_time, signing_certificate_match}, signing_time, signing_time_source, certificate_valid_at_signing, trusted, trust_store_used, revocation_checked (false), problems, and signatures when there are several. The first signature fills the main fields; status, valid and trusted concern them all. trusted needs a certificate valid at the XAdES signing time (claimed), else at the time of the check
Parameters
in_path_or_text: The XML text or the path of an XML file (a value whose first character is an opening angle bracket is read as text) - String - required
in_path_or_text: The XML text or the path of an XML file (a value whose first character is an opening angle bracket is read as text) - String - required
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; xml sign "<invoice id=\"F1\"><line id=\"L1\">Coffee</line><total>12.50</total></invoice>" "tmp/tuto_sign/signed.xml" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reference_id\": \"L1\"}"; json load "tuto_k" (xml verify_signature "tmp/tuto_sign/signed.xml"); -> "[res]" (concat "status=" (json select "tuto_k" "/status") ", " "integrity_ok=" (json select "tuto_k" "/integrity_ok") ", " "signer=" (json select "tuto_k" "/signer") ", " "whole_document_signed=" (json select "tuto_k" "/whole_document_signed") ", " "path=" (json select "tuto_k" "/signed_elements/[0]/path") ", " "uri=" (json select "tuto_k" "/references/[0]/uri") ", " "valid=" (json select "tuto_k" "/references/[0]/valid")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
status=intact_untrusted, integrity_ok=true, signer=CN=Test Signer,O=Demo, whole_document_signed=false, path=/invoice/line, uri=#L1, valid=true
Complete: a change of the total in a whole document signature
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; json load "tuto_a" (xml sign "<invoice id=\"F1\"><line id=\"L1\">Coffee</line><total>12.50</total></invoice>" "" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\"}"); -> "[signed]" (json select "tuto_a" "/xml"); json load "tuto_b" (xml verify_signature (string replace [signed] "12.50" "99.00")); -> "[res]" (concat "status=" (json select "tuto_b" "/status") ", reference=" (json select "tuto_b" "/references/[0]/valid")); json unload "tuto_a"; json unload "tuto_b"; file delete "tmp/tuto_sign"; [res]
mentdb
status=tampered, reference=false
Complete: an id used twice is refused
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; json load "tuto_a" (xml sign "<invoice id=\"F1\"><line id=\"L1\">Coffee</line><total>12.50</total></invoice>" "" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"reference_id\": \"L1\"}"); -> "[signed]" (json select "tuto_a" "/xml"); json load "tuto_b" (xml verify_signature (string replace [signed] "<total>" "<total id=\"L1\">")); -> "[res]" (concat "status=" (json select "tuto_b" "/status") ", " (json select "tuto_b" "/problems/[0]")); json unload "tuto_a"; json unload "tuto_b"; file delete "tmp/tuto_sign"; [res]
mentdb
status=tampered, duplicate id refused: #L1
Complete: unsigned data added inside the signature is refused: the enveloped transform would have hidden it
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; json load "tuto_a" (xml sign "<invoice id=\"F1\"><line id=\"L1\">Coffee</line><total>12.50</total></invoice>" "" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\"}"); -> "[signed]" (json select "tuto_a" "/xml"); json load "tuto_b" (xml verify_signature (string replace [signed] "</ds:Signature>" "<ds:Object><amount>999999</amount></ds:Object></ds:Signature>")); -> "[res]" (concat "status=" (json select "tuto_b" "/status") ", " (json select "tuto_b" "/problems/[0]")); json unload "tuto_a"; json unload "tuto_b"; file delete "tmp/tuto_sign"; [res]
mentdb
status=tampered, a ds:Object is not covered by any reference: its content is not signed
xml verify_signature <in_path_or_text> <json_params>
Description
Check with parameters: trust_store, trust_store_type, password or password_env (of the trust store), now (ISO date of the check, default the current time). status is trusted and valid is true when the signature is good and a certificate path reaches the trust store at now. No revocation check
Check with parameters: trust_store, trust_store_type, password or password_env (of the trust store), now (ISO date of the check, default the current time). status is trusted and valid is true when the signature is good and a certificate path reaches the trust store at now. No revocation check
Parameters
in_path_or_text: The XML text or file - String - required
json_params: The parameters - String - required
in_path_or_text: The XML text or file - String - required
json_params: The parameters - String - required
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"overwrite\": true}"; xml sign "<invoice id=\"F1\"><line id=\"L1\">Coffee</line><total>12.50</total></invoice>" "tmp/tuto_sign/signed.xml" "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"xades\": true}"; json load "tuto_k" (xml verify_signature "tmp/tuto_sign/signed.xml" "{\"trust_store\": \"tmp/tuto_sign/test.p12\", \"password\": \"test-only-password\"}"); -> "[res]" (concat "status=" (json select "tuto_k" "/status") ", " "valid=" (json select "tuto_k" "/valid") ", " "trusted=" (json select "tuto_k" "/trusted") ", " "xades=" (json select "tuto_k" "/xades") ", " "whole_document_signed=" (json select "tuto_k" "/whole_document_signed") ", " "path=" (json select "tuto_k" "/signed_elements/[0]/path") ", " "signing_time_source=" (json select "tuto_k" "/signing_time_source") ", " "signing_certificate_match=" (json select "tuto_k" "/xades_details/signing_certificate_match")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
status=trusted, valid=true, trusted=true, xades=true, whole_document_signed=true, path=/invoice, signing_time_source=claimed, signing_certificate_match=true
sign create_test_keystore <out_path> <json_params>
Description
Create a keystore with a new key and a SELF-SIGNED certificate, FOR TESTS AND DEMOS ONLY: nobody trusts it, a PDF reader will show the signature as unknown, and it has no legal value; in production use a certificate from a certification authority. The extension gives the type (.p12, .pfx, .jks). The file is readable by its owner only; an existing file is kept unless overwrite
Create a keystore with a new key and a SELF-SIGNED certificate, FOR TESTS AND DEMOS ONLY: nobody trusts it, a PDF reader will show the signature as unknown, and it has no legal value; in production use a certificate from a certification authority. The extension gives the type (.p12, .pfx, .jks). The file is readable by its owner only; an existing file is kept unless overwrite
Parameters
out_path: The keystore to create - String - required
json_params: password or password_env (required); subject (CN=MentDB Test Signer,O=MentDB Test); days (365, up to 3650); algorithm (RSA or RSA-2048, RSA-3072, RSA-4096, EC or EC-P256, EC-P384); alias (test); not_before (ISO date, default now); overwrite (false) - String - required
out_path: The keystore to create - String - required
json_params: password or password_env (required); subject (CN=MentDB Test Signer,O=MentDB Test); days (365, up to 3650); algorithm (RSA or RSA-2048, RSA-3072, RSA-4096, EC or EC-P256, EC-P384); alias (test); not_before (ISO date, default now); overwrite (false) - String - required
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; json load "tuto_k" (sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Test Signer,O=Demo\", \"days\": 30, \"algorithm\": \"EC\"}"); -> "[res]" (concat "type=" (json select "tuto_k" "/type") ", " "alias=" (json select "tuto_k" "/alias") ", " "subject=" (json select "tuto_k" "/subject") ", " "algorithm=" (json select "tuto_k" "/algorithm") ", " "days=" (json select "tuto_k" "/days") ", " "self_signed=" (json select "tuto_k" "/self_signed") ", " "warning=" (json select "tuto_k" "/warning")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
type=PKCS12, alias=test, subject=CN=Test Signer,O=Demo, algorithm=EC-P256, days=30, self_signed=true, warning=self-signed test certificate: for tests and demos only, not for production, trusted by nobody
Complete: a fixed period, in JKS format
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; json load "tuto_k" (sign create_test_keystore "tmp/tuto_sign/test.jks" "{\"password\": \"test-only-password\", \"subject\": \"CN=Old Signer\", \"not_before\": \"2020-01-01\", \"days\": 10}"); -> "[res]" (concat "type=" (json select "tuto_k" "/type") ", " "subject=" (json select "tuto_k" "/subject") ", " "not_before=" (json select "tuto_k" "/not_before") ", " "not_after=" (json select "tuto_k" "/not_after")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
type=JKS, subject=CN=Old Signer, not_before=2020-01-01T00:00:00Z, not_after=2020-01-11T00:00:00Z
Used in use cases (1 recipe)
Security engineer › Sign a PDF and check it: not modified is not the same as trusted
Security engineer › Sign a PDF and check it: not modified is not the same as trusted
Often used with
equal · file delete · file exist · file mkdir · pdf close · pdf create · pdf page add · pdf save
equal · file delete · file exist · file mkdir · pdf close · pdf create · pdf page add · pdf save
sign certificate_info <keystore_path> <json_params>
Description
List the entries of a keystore, without ever exposing a key: count, and for each entry alias, kind (private_key, trusted_certificate), subject, issuer, serial, not_before, not_after, expired, valid_now, self_signed, key, signature_algorithm, sha256, chain_length
List the entries of a keystore, without ever exposing a key: count, and for each entry alias, kind (private_key, trusted_certificate), subject, issuer, serial, not_before, not_after, expired, valid_now, self_signed, key, signature_algorithm, sha256, chain_length
Parameters
keystore_path: The keystore: .p12, .pfx or .jks - String - required
json_params: password or password_env (required); keystore_type; now (ISO date for expired and valid_now, default the current time) - String - required
keystore_path: The keystore: .p12, .pfx or .jks - String - required
json_params: password or password_env (required); keystore_type; now (ISO date for expired and valid_now, default the current time) - String - required
admin
if (equal (file exist "tmp/tuto_sign") "0") {file mkdir "tmp/tuto_sign";}; sign create_test_keystore "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"subject\": \"CN=Old Signer,O=Demo\", \"not_before\": \"2020-01-01\", \"days\": 10}"; json load "tuto_k" (sign certificate_info "tmp/tuto_sign/test.p12" "{\"password\": \"test-only-password\", \"now\": \"2020-01-05\"}"); -> "[res]" (concat "count=" (json select "tuto_k" "/count") ", " "alias=" (json select "tuto_k" "/entries/[0]/alias") ", " "kind=" (json select "tuto_k" "/entries/[0]/kind") ", " "subject=" (json select "tuto_k" "/entries/[0]/subject") ", " "not_after=" (json select "tuto_k" "/entries/[0]/not_after") ", " "expired=" (json select "tuto_k" "/entries/[0]/expired") ", " "key=" (json select "tuto_k" "/entries/[0]/key")); json unload "tuto_k"; file delete "tmp/tuto_sign"; [res]
mentdb
count=1, alias=test, kind=private_key, subject=CN=Old Signer,O=Demo, not_after=2020-01-11T00:00:00Z, expired=false, key=RSA 2048
