Sample code for 30+ languages & platforms
Zig Requires Chilkat v11.7.0+

Encrypt and Sign a PDF, and Sign an Encrypted PDF

See more PDF Protection Examples

Demonstrates signing together with encryption. A pending encrypt is applied by SignPdf before it signs, so one call produces a PDF that is both encrypted and signed. A PDF that is already encrypted can be signed too, and stays encrypted. When it was opened with only the user password its permissions decide: adding a signature field needs both the modify and the annotate permission. Loading the PDF with its owner password lifts that.

Chilkat Zig Downloads

Zig
const std = @import("std");
const chilkat = @import("chilkat");

pub fn main(init: std.process.Init) !void {
    const alloc = init.arena.allocator();

    // This example requires the Chilkat API to have been previously unlocked.
    // See Global Unlock Sample for sample code.

    // The PDF used in this example is available at two_page_memo.pdf

    // The signing certificate, with its private key.
    const cert = try chilkat.Cert.init();
    defer cert.deinit();
    cert.loadPfxFile("qa_data/pfx/myPdfSigningCert.pfx", "secret") catch {
        std.debug.print("{s}\n", .{try cert.getLastErrorText(alloc)});
        return;
    };

    const pdf = try chilkat.Pdf.init();
    defer pdf.deinit();
    pdf.loadFile("qa_data/pdf/two_page_memo.pdf") catch {
        std.debug.print("{s}\n", .{try pdf.getLastErrorText(alloc)});
        return;
    };

    // Encrypt with AES-256 and do not permit modifying.
    pdf.setUserPassword("user-secret");
    pdf.setOwnerPassword("owner-secret");
    const json = try chilkat.JsonObject.init();
    defer json.deinit();
    json.updateBool("permissions.modify", false) catch {};
    const json_out = try chilkat.JsonObject.init();
    defer json_out.deinit();
    pdf.protection("encrypt", json, json_out) catch {
        std.debug.print("{s}\n", .{try pdf.getLastErrorText(alloc)});
        return;
    };

    // The encryption is still pending.  SignPdf applies it first and signs the encrypted result.
    pdf.setSigningCert(cert) catch {
        std.debug.print("{s}\n", .{try pdf.getLastErrorText(alloc)});
        return;
    };

    const sig_opts = try chilkat.JsonObject.init();
    defer sig_opts.deinit();
    sig_opts.updateBool("invisibleSignature", true) catch {};
    sig_opts.updateInt("signingCertificateV2", 1) catch {};
    sig_opts.updateInt("signingTime", 1) catch {};
    pdf.signPdf(sig_opts, "qa_output/memo_encrypted_signed.pdf") catch {
        std.debug.print("{s}\n", .{try pdf.getLastErrorText(alloc)});
        return;
    };

    // The result is encrypted and signed.
    const check = try chilkat.Pdf.init();
    defer check.deinit();
    check.setUserPassword("user-secret");
    check.loadFile("qa_output/memo_encrypted_signed.pdf") catch {
        std.debug.print("{s}\n", .{try check.getLastErrorText(alloc)});
        return;
    };

    const empty = try chilkat.JsonObject.init();
    defer empty.deinit();
    const info = try chilkat.JsonObject.init();
    defer info.deinit();
    check.protection("getInfo", empty, info) catch {
        std.debug.print("{s}\n", .{try check.getLastErrorText(alloc)});
        return;
    };

    std.debug.print("algorithm: {s}\n", .{try info.stringOf(alloc, "loaded.algorithm")});
    std.debug.print("signatures: {d}\n", .{check.getNumSignatures()});
    const sig_info = try chilkat.JsonObject.init();
    defer sig_info.deinit();
    var verified: bool = if (check.verifySignature(0, sig_info)) true else |_| false;
    std.debug.print("signature verifies: {}\n", .{verified});

    // Signing an encrypted PDF.  This one was opened with only the user password, and its
    // permissions do not allow modifying, so another signature is refused.
    check.setSigningCert(cert) catch {
        std.debug.print("{s}\n", .{try check.getLastErrorText(alloc)});
        return;
    };

    check.signPdf(sig_opts, "qa_output/memo_signed_twice.pdf") catch {
        std.debug.print("A second signature was refused: the permissions do not allow it.\n", .{});
    };

    // Loaded with the owner password, the same PDF can be signed.  It stays encrypted, and the
    // first signature stays valid because the new one is added as an incremental update.
    const owner = try chilkat.Pdf.init();
    defer owner.deinit();
    owner.setUserPassword("user-secret");
    owner.setOwnerPassword("owner-secret");
    owner.loadFile("qa_output/memo_encrypted_signed.pdf") catch {
        std.debug.print("{s}\n", .{try owner.getLastErrorText(alloc)});
        return;
    };

    owner.setSigningCert(cert) catch {
        std.debug.print("{s}\n", .{try owner.getLastErrorText(alloc)});
        return;
    };

    owner.signPdf(sig_opts, "qa_output/memo_signed_twice.pdf") catch {
        std.debug.print("{s}\n", .{try owner.getLastErrorText(alloc)});
        return;
    };

    const twice = try chilkat.Pdf.init();
    defer twice.deinit();
    twice.setUserPassword("user-secret");
    twice.loadFile("qa_output/memo_signed_twice.pdf") catch {
        std.debug.print("{s}\n", .{try twice.getLastErrorText(alloc)});
        return;
    };

    std.debug.print("signatures after signing with owner rights: {d}\n", .{twice.getNumSignatures()});
    verified = if (twice.verifySignature(0, sig_info)) true else |_| false;
    std.debug.print("first signature still verifies: {}\n", .{verified});
    verified = if (twice.verifySignature(1, sig_info)) true else |_| false;
    std.debug.print("second signature verifies: {}\n", .{verified});

    // Sample output:

    // algorithm: aes-256
    // signatures: 1
    // signature verifies: True
    // A second signature was refused: the permissions do not allow it.
    // signatures after signing with owner rights: 2
    // first signature still verifies: True
    // second signature verifies: True
}