Documentation menu

Certification & field locking

Certification (DocMDP)

A certifying signature declares which later changes are allowed. Pass the certify option to the first signature:

Certify.java
import io.github.aniketc068.atick.Atick;
import java.nio.file.*;

byte[] pdf = Files.readAllBytes(Path.of("contract.pdf"));
byte[] pfx = Files.readAllBytes(Path.of("signer.pfx"));

// P=1 — no changes at all
byte[] out1 = Atick.signPfx(pdf, pfx, "{\"password\":\"••••\",\"certify\":1}");

// P=2 — form filling + signing
byte[] out2 = Atick.signPfx(pdf, pfx, "{\"password\":\"••••\",\"certify\":2}");

// P=3 — form filling + annotations
byte[] out3 = Atick.signPfx(pdf, pfx, "{\"password\":\"••••\",\"certify\":3}");

Omit certify (or set it to 0) to produce a normal, non-certifying approval signature.

LevelValueAllows
NONE0a normal approval signature (no certification)
NO_CHANGES1nothing — any later change (incl. another signature, LTV, timestamp) breaks it
FORM_FILLING2filling form fields + adding signatures
FORM_FILLING + ANNOTATIONS3the above + annotations
NO_CHANGES (P=1) forbids everything afterwards — so it cannot be combined with later LTV, document timestamps, or extra approval signatures. Use it as a single, final signature. For a document that will gather more signatures, certify with 2 (FORM_FILLING) or 3(FORM_FILLING + ANNOTATIONS).

Field locking (FieldMDP)

Lock specific form fields so they cannot be changed after signing — without certifying the whole document:

Lock.java
// lock these fields only
byte[] locked = Atick.signPfx(pdf, pfx,
    "{\"password\":\"••••\",\"lock_fields\":[\"ApproverName\"]}");

// lock ALL fields
byte[] lockedAll = Atick.signPfx(pdf, pfx,
    "{\"password\":\"••••\",\"lock_fields\":[\"*\"]}");

If a locked field is altered after signing, the signature is reported as invalid.

You can also certify and lock in one signature — combine certifywith lock_fields:

CertifyLock.java
byte[] out = Atick.signPfx(pdf, pfx,
    "{\"password\":\"••••\",\"certify\":1,\"lock_fields\":[\"*\"]}");

Pre-sign checks

Validate the signing certificate before signing. These checks run prior to producing any output, and signing is refused if a check fails — so an invalid certificate never produces a signature.

OptionEffect
verifyrun the full set of certificate checks below
verify_expirycertificate must not be expired (or not yet valid)
verify_crlcertificate must not be revoked per its CRL
verify_ocspcertificate must not be revoked per OCSP
trusted_rootsextra trusted roots (a base64-encoded DER certificate list) the chain may reach for the checks above

The granular keys mirror the umbrella verify flag, so you can run just one check: verify_expiry refuses to sign an expired (or not-yet-valid) certificate, verify_crl runs a pre-sign CRL revocation check, and verify_ocspruns a pre-sign OCSP revocation check. Supply trusted_roots as a list of base64-encoded DER certificates to add extra roots the chain is allowed to terminate at.

Verify.java
byte[] out = Atick.signPfx(pdf, pfx,
    "{\"password\":\"••••\"," +
    "\"verify\":true," +                                       // not expired + CRL + OCSP + not revoked
    "\"trusted_roots\":[\"<base64 DER>\",\"<base64 DER>\"]}"); // extra roots the chain may reach

You can also enable the individual checks instead of the umbrella verifyflag:

VerifyEach.java
byte[] out = Atick.signPfx(pdf, pfx,
    "{\"password\":\"••••\"," +
    "\"verify_expiry\":true," +
    "\"verify_crl\":true," +
    "\"verify_ocsp\":true}");

Because a failed pre-sign check refuses to sign, it surfaces as an Atick.AtickException. Wrap the call in a try/catch so a revoked or expired certificate is handled instead of crashing:

VerifyCatch.java
import io.github.aniketc068.atick.Atick;
import java.nio.file.*;

try {
    byte[] out = Atick.signPfx(pdf, pfx,
        "{\"password\":\"••••\"," +
        "\"verify\":true," +
        "\"trusted_roots\":[\"<base64 DER>\"]}");
    Files.write(Path.of("signed.pdf"), out);
} catch (Atick.AtickException e) {
    // certificate expired, revoked (CRL/OCSP), or chain did not reach a trusted root —
    // nothing was signed
    System.err.println("Signing refused: " + e.getMessage());
}
verify_crl and verify_ocsp reach out to the CA's revocation endpoints (discovered from the certificate). If those endpoints are unreachable the check cannot complete and signing is refused — keep the catch block above in place.

Next page →