Indian eSign (CCA)
ATick supports the CCA eSign Online Electronic Signature Service for every API version (v1.x … v3.x). The flow is the same across versions — only the request XML attributes differ.
flow.txt
PDF -> SHA-256 of the ByteRange (the InputHash, hex)
-> build the <Esign ...> request XML for your version, put the InputHash in <InputHash>
-> sign the request XML (your managex-xml-sdk) [enveloped W3C XML-DSig]
-> POST it (multipart/form-data) to the ESP
-> EsignResp -> <DocSignature> (rawrsa / pkcs7 / pkcs7Pdf / pkcs7complete)
-> embed it into the PDFStep 1 — prepare + hash
esign_prepare.py
prepared, ctx = atick.prepare_deferred_multi(
pdf, atick.Style(cn="...", always_check=True),
[(1, (40, 640, 300, 750)), (2, (320, 380, 560, 490))], # signature box on each page
mode="single", sub_filter="adbe.pkcs7.detached",
certify=atick.Certify.FORM_FILLING_ANNOTATIONS, # CFA -> later timestamp won't invalidate
contents_size=60000, # room for chain + revocation + timestamp
)
input_hash_hex = bytes(ctx["digest"]).hex() # goes into <InputHash>
data = bytes(ctx["data"]) # the exact bytes the ESP signsStep 2 — sign the request XML with managex-xml-sdk
managex-xml-sdkneeds a trusted-roots folder. Before it will sign, it validates your ASP certificate against the CA root certificate(s) in the folder you give asValidationConfig(trusted_roots_folder=…). That foldermust exist and contain the CA root certificate(s) as PEM files — if the folder is missing or empty, signing the request fails and eSign will not work. Put your CA's root (and intermediate) certificates there as.pemfiles.
esign_sign_request.py
import managex_xml_sdk as mx
from managex_xml_sdk.models.certificate_models import PFXConfig, ValidationConfig
request = (f'<Esign ver="2.1" sc="Y" ts="..." txn="TXN1" ekycIdType="A" aspId="..." AuthMode="1" '
f'responseSigType="pkcs7Pdf" responseUrl="https://.../"><Docs>'
f'<InputHash id="1" hashAlgorithm="SHA256" docInfo="Agreement">{input_hash_hex}</InputHash>'
f'</Docs></Esign>')
signer = mx.PFXSigner(PFXConfig(method="pfx", pfx_file="asp.pfx", password="...",
validation_config=ValidationConfig(trusted_roots_folder="roots")))
params = mx.SignatureEnvelopeParameters(
signature_info=mx.SignatureInfo(signature_algorithm=mx.SignatureAlgorithm.RSA_SHA256),
reference_info=mx.ReferenceInfo(uri="", transforms=[mx.TransformAlgorithm.ENVELOPED_SIGNATURE]),
key_info=mx.KeyInfo(include_subject_name=True, include_certificate=True))
signed_request = signer.sign_xml_content(request.encode(), params) # POST this to the ESPStep 3 — embed the ESP response
esign_embed.py
import base64, xml.etree.ElementTree as ET
root = ET.fromstring(esign_response_xml)
doc_sig = base64.b64decode(next(e.text for e in root.iter() if e.tag.endswith("DocSignature")))
signed = atick.embed(prepared, doc_sig) # pkcs7 / pkcs7Pdf / pkcs7complete (a full CMS)
# rawrsa: atick.embed_rawrsa(prepared, doc_sig, user_cert)pkcs7Pdf and pkcs7complete responses already carry the full chain, the revocation (under pdfRevocationInfoArchival) and a CA timestamp — so the embedded signature is LTV-complete and timestamped out of the box.
responseSigType
| Value | Returns | Embed with |
|---|---|---|
rawrsa | a raw RSA signature + the signer cert | atick.embed_rawrsa |
pkcs7 | a CMS, signer cert only (no revocation) | atick.embed |
pkcs7Pdf | a CMS, full chain + CRL/OCSP (signed attr) + timestamp | atick.embed |
pkcs7complete | a CMS, full chain + revocation (unsigned attr) | atick.embed |
Simulating the ESP for testing
atick.cms_pfx(data, pfx, pw, revocation=True, timestamp=True) produces apkcs7Pdf-style CMS, and atick.sign_hash_pfx(data, pfx, pw) arawrsa response — so you can run the whole flow end-to-end without a live ESP. Seeexamples/esign/.
Command line
esign_cli.sh
atick esign-prepare in.pdf prepared.pdf --certify form-annots # prints the InputHash
# sign the request XML (managex-xml-sdk) + POST to the ESP, save the response
atick esign-embed prepared.pdf response.xml signed.pdf