Sample code for 30+ languages & platforms
Lua Requires Chilkat v9.5.0.85+

Validate PDF Signatures

See more PDF Signatures Examples

This example demonstrates how to validate the signatures in a PDF and also shows how to get information from each signature.

Chilkat Lua Downloads

Lua
local chilkat = require("chilkat")

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

local pdf = chilkat.Pdf.new()

-- Load a PDF that has cryptographic signatures to be validated
local success = pdf:load_file("qa_data/pdf/sign_testing_1/helloSigned2.pdf")
if not success then
    print(pdf.last_error_text)
    return
end

-- Each time we verify a signature, information about the signature is written into
-- sigInfo (replacing whatever sigInfo previously contained).
local sigInfo = chilkat.JsonObject.new()
sigInfo.emit_compact = false

-- Iterate over each signature and validate each.
local numSignatures = pdf.num_signatures
local validated = false
local i = 0
while i < numSignatures do
    validated = pdf:verify_signature(i, sigInfo)
    print("Signature " .. i .. " validated: " .. tostring(validated))
    print(sigInfo:emit())
    i = i + 1
end

print("Finished.")

-- When VerifySignature validates a signature, a lot of information is deposited into the JSON sigInfo object.
-- The information can vary depending on what was included in the signature (for example, various authenticated attributes
-- and unauthenticated attributes may or may not be included).
-- Here is a sample of the information you'll see.
--
-- The following online tool can be used to generate code to parse any given JSON.
-- Generate Parsing Code from JSON

-- {
--   "validated": true,
--   "signatureDictionary": {         <--- This is the contents of the PDF Signature Dictionary for this signature.
--     "/ByteRange": [
--       0,
--       154682,
--       170512,
--       3233
--     ],
--     "/Contents": "<hex_data>",
--     "/Filter": "/Adobe.PPKLite",        <--- The meaning of the Signature Dictionary entries are defined in the PDF format specification document.
--     "/M": "D:20201006110216-05'00'",
--     "/Name": "yubikey rsa 1024 authentication",
--     "/Prop_Build": {
--       "/App": {
--         "/Name": "/Adobe#20Acrobat#20Pro#20DC",
--         "/OS": [
--           "/Win"
--         ],
--         "/R": 1313792,
--         "/REx": "2020.012.20048",
--         "/TrustedMode": true
--       },
--       "/Filter": {
--         "/Date": "Sep 11 2020 16:30:54",
--         "/Name": "/Adobe.PPKLite",
--         "/R": 131104,
--         "/V": 2
--       },
--       "/PubSec": {
--         "/Date": "Sep 11 2020 16:30:54",
--         "/NonEFontNoWarn": true,
--         "/R": 131105
--       }
--     },
--     "/SubFilter": "/adbe.pkcs7.detached",
--     "/Type": "/Sig"
--   },
--   "pkcs7": {          <--- This is the content of the CMS signature.
--     "verify": {
--       "certs": [      <--- Each signing certificate is listed here (by issuer common name and signing cert's serail number (in hex))
--         {
--           "issuerCN": "yubikey rsa 1024 authentication",
--           "serial": "66BE58138D761E92BC594A722932657BE26D421F"
--         }
--       ],
--       "digestAlgorithms": [
--         "sha256"
--       ],
--       "signerInfo": [     <--- contains data from each SignerInfo
--         {
--           "cert": {
--             "serialNumber": "66BE58138D761E92BC594A722932657BE26D421F",
--             "issuerCN": "yubikey rsa 1024 authentication",
--             "digestAlgOid": "2.16.840.1.101.3.4.2.1",
--             "digestAlgName": "SHA256"
--           },
--           "contentType": "1.2.840.113549.1.7.1",
--           "messageDigest": "btQOuSEvC31mdRFHtyEUPw8R9NuKfk0XPcQ6Lcmn6pk=",
--           "signingAlgOid": "1.2.840.113549.1.1.11",
--           "signingAlgName": "RSA-SHA256-PKCSV-1_5",
--           "authAttr": {     <--- CMS authenticated attributes are contained here.
--             "1.2.840.113583.1.1.8": {
--               "der": "MAA="
--             },
--             "1.2.840.113549.1.9.3": {
--               "name": "contentType",
--               "oid": "1.2.840.113549.1.7.1"
--             },
--             "1.2.840.113549.1.9.4": {
--               "name": "messageDigest",
--               "digest": "btQOuSEvC31mdRFHtyEUPw8R9NuKfk0XPcQ6Lcmn6pk="
--             }
--           },
--           "unauthAttr": {    <--- CMS unauthenticated attributes are contained here.
--             "1.2.840.113549.1.9.16.2.14": {
--               "name": "timestampToken",
--               "der": "MIIOvAYJKo ... Es/70g=",
--               "verify": {
--                 "digestAlgorithms": [
--                   "sha256"
--                 ],
--                 "signerInfo": [
--                   {
--                     "cert": {
--                       "serialNumber": "04CD3F8568AE76C61BB0FE7160CCA76D",
--                       "issuerCN": "DigiCert SHA2 Assured ID Timestamping CA",
--                       "digestAlgOid": "2.16.840.1.101.3.4.2.1",
--                       "digestAlgName": "SHA256"
--                     },
--                     "contentType": "1.2.840.113549.1.9.16.1.4",
--                     "signingTime": "201006160423Z",
--                     "messageDigest": "Atv5Rj3kidB8IR6CplYiX3o6De/k8SC6JJ6uUPAGO0g=",
--                     "signingAlgOid": "1.2.840.113549.1.1.1",
--                     "signingAlgName": "RSA-PKCSV-1_5",
--                     "authAttr": {
--                       "1.2.840.113549.1.9.3": {
--                         "name": "contentType",
--                         "oid": "1.2.840.113549.1.9.16.1.4"
--                       },
--                       "1.2.840.113549.1.9.5": {
--                         "name": "signingTime",
--                         "utctime": "201006160423Z"
--                       },
--                       "1.2.840.113549.1.9.16.2.12": {
--                         "name": "signingCertificate",
--                         "der": "MBowGDAWBBQDJb1QXtqWMC3CL0+gHkwovig0xQ=="
--                       },
--                       "1.2.840.113549.1.9.4": {
--                         "name": "messageDigest",
--                         "digest": "Atv5Rj3kidB8IR6CplYiX3o6De/k8SC6JJ6uUPAGO0g="
--                       }
--                     }
--                   }
--                 ]
--               },
--               "timestampSignatureVerified": true,
--               "tstInfo": {
--                 "tsaPolicyId": "2.16.840.1.114412.7.1",
--                 "messageImprint": {
--                   "hashAlg": "sha256",
--                   "digest": "gLJtrRWUSDfjzDkF1MfWG1wyHA6FrUJLkWMGRG+eMlA=",
--                   "digestMatches": true
--                 },
--                 "serialNumber": "00CE57E1113970607EF63B1D1160545321",
--                 "genTime": "20201006160423Z"
--               }
--             }
--           }
--         }
--       ],
--       "pkcs7": {
--         "verify": {
--           "certs": [
--             {
--               "issuerCN": "DigiCert SHA2 Assured ID Timestamping CA",
--               "serial": "04CD3F8568AE76C61BB0FE7160CCA76D"
--             },
--             {
--               "issuerCN": "DigiCert Assured ID Root CA",
--               "serial": "0AA125D6D6321B7E41E405DA3697C215"
--             }
--           ]
--         }
--       }
--     }
--   }
-- }

-- **** The point of this code is to show how to get at each desired piece of information contained in the signature.
-- **** If your signature contains additional information not shown here, then you can use the online tool to generate the parse code.
-- **** It is likely you're only interested in a few items of information, and therefore you wouldn't copy all of this code, but might
-- **** choose to use bits and pieces to get the information you find important.

local json = chilkat.JsonObject.new()

-- Imagine that the "json" object contains the information obtained by validating a signature...
-- The code below was generated using the online tool:  Generate Parsing Code from JSON

local unauthAttrTimestampTokenTstInfoGenTime = chilkat.DtObj.new()
local signingTime = chilkat.DtObj.new()
local authAttrSigningTimeUtctime = chilkat.DtObj.new()
local intVal = 0
local strVal = ""
local issuerCN = ""
local serial = ""
local certSerialNumber = ""
local certIssuerCN = ""
local certDigestAlgOid = ""
local certDigestAlgName = ""
local contentType = ""
local messageDigest = ""
local signingAlgOid = ""
local signingAlgName = ""
local authAttr1_2_840_113583_1_1_8Der = ""
local authAttrContentTypeName = ""
local authAttrContentTypeOid = ""
local authAttrMessageDigestName = ""
local authAttrMessageDigestDigest = ""
local unauthAttrTimestampTokenName = ""
local unauthAttrTimestampTokenDer = ""
local unauthAttrTimestampTokenTimestampSignatureVerified = false
local unauthAttrTimestampTokenTstInfoTsaPolicyId = ""
local unauthAttrTimestampTokenTstInfoMessageImprintHashAlg = ""
local unauthAttrTimestampTokenTstInfoMessageImprintDigest = ""
local unauthAttrTimestampTokenTstInfoMessageImprintDigestMatches = false
local unauthAttrTimestampTokenTstInfoSerialNumber = ""
local j = 0
local count_j = 0
local authAttrSigningTimeName = ""
local authAttrSigningCertificateName = ""
local authAttrSigningCertificateDer = ""

validated = json:bool_of("validated")
local signatureDictionary_Contents = json:string_of("signatureDictionary./Contents")
local signatureDictionary_Filter = json:string_of("signatureDictionary./Filter")
local signatureDictionary_M = json:string_of("signatureDictionary./M")
local signatureDictionary_Name = json:string_of("signatureDictionary./Name")
local signatureDictionary_Prop_Build_App_Name = json:string_of("signatureDictionary./Prop_Build./App./Name")
local signatureDictionary_Prop_Build_App_R = json:int_of("signatureDictionary./Prop_Build./App./R")
local signatureDictionary_Prop_Build_App_REx = json:string_of("signatureDictionary./Prop_Build./App./REx")
local signatureDictionary_Prop_Build_App_TrustedMode = json:bool_of("signatureDictionary./Prop_Build./App./TrustedMode")
local signatureDictionary_Prop_Build_Filter_Date = json:string_of("signatureDictionary./Prop_Build./Filter./Date")
local signatureDictionary_Prop_Build_Filter_Name = json:string_of("signatureDictionary./Prop_Build./Filter./Name")
local signatureDictionary_Prop_Build_Filter_R = json:int_of("signatureDictionary./Prop_Build./Filter./R")
local signatureDictionary_Prop_Build_Filter_V = json:int_of("signatureDictionary./Prop_Build./Filter./V")
local signatureDictionary_Prop_Build_PubSec_Date = json:string_of("signatureDictionary./Prop_Build./PubSec./Date")
local signatureDictionary_Prop_Build_PubSec_NonEFontNoWarn = json:bool_of("signatureDictionary./Prop_Build./PubSec./NonEFontNoWarn")
local signatureDictionary_Prop_Build_PubSec_R = json:int_of("signatureDictionary./Prop_Build./PubSec./R")
local signatureDictionary_SubFilter = json:string_of("signatureDictionary./SubFilter")
local signatureDictionary_Type = json:string_of("signatureDictionary./Type")
i = 0
local count_i = json:size_of_array("signatureDictionary./ByteRange")
while i < count_i do
    json.i = i
    intVal = json:int_of("signatureDictionary./ByteRange[i]")
    i = i + 1
end

i = 0
count_i = json:size_of_array("signatureDictionary./Prop_Build./App./OS")
while i < count_i do
    json.i = i
    strVal = json:string_of("signatureDictionary./Prop_Build./App./OS[i]")
    i = i + 1
end

i = 0
count_i = json:size_of_array("pkcs7.verify.certs")
while i < count_i do
    json.i = i
    issuerCN = json:string_of("pkcs7.verify.certs[i].issuerCN")
    serial = json:string_of("pkcs7.verify.certs[i].serial")
    i = i + 1
end

i = 0
count_i = json:size_of_array("pkcs7.verify.digestAlgorithms")
while i < count_i do
    json.i = i
    strVal = json:string_of("pkcs7.verify.digestAlgorithms[i]")
    i = i + 1
end

i = 0
count_i = json:size_of_array("pkcs7.verify.signerInfo")
while i < count_i do
    json.i = i
    certSerialNumber = json:string_of("pkcs7.verify.signerInfo[i].cert.serialNumber")
    certIssuerCN = json:string_of("pkcs7.verify.signerInfo[i].cert.issuerCN")
    certDigestAlgOid = json:string_of("pkcs7.verify.signerInfo[i].cert.digestAlgOid")
    certDigestAlgName = json:string_of("pkcs7.verify.signerInfo[i].cert.digestAlgName")
    contentType = json:string_of("pkcs7.verify.signerInfo[i].contentType")
    messageDigest = json:string_of("pkcs7.verify.signerInfo[i].messageDigest")
    signingAlgOid = json:string_of("pkcs7.verify.signerInfo[i].signingAlgOid")
    signingAlgName = json:string_of("pkcs7.verify.signerInfo[i].signingAlgName")
    authAttr1_2_840_113583_1_1_8Der = json:string_of("pkcs7.verify.signerInfo[i].authAttr.\"1.2.840.113583.1.1.8\".der")
    authAttrContentTypeName = json:string_of("pkcs7.verify.signerInfo[i].authAttr.\"1.2.840.113549.1.9.3\".name")
    authAttrContentTypeOid = json:string_of("pkcs7.verify.signerInfo[i].authAttr.\"1.2.840.113549.1.9.3\".oid")
    authAttrMessageDigestName = json:string_of("pkcs7.verify.signerInfo[i].authAttr.\"1.2.840.113549.1.9.4\".name")
    authAttrMessageDigestDigest = json:string_of("pkcs7.verify.signerInfo[i].authAttr.\"1.2.840.113549.1.9.4\".digest")
    unauthAttrTimestampTokenName = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".name")
    unauthAttrTimestampTokenDer = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".der")
    unauthAttrTimestampTokenTimestampSignatureVerified = json:bool_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".timestampSignatureVerified")
    unauthAttrTimestampTokenTstInfoTsaPolicyId = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".tstInfo.tsaPolicyId")
    unauthAttrTimestampTokenTstInfoMessageImprintHashAlg = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".tstInfo.messageImprint.hashAlg")
    unauthAttrTimestampTokenTstInfoMessageImprintDigest = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".tstInfo.messageImprint.digest")
    unauthAttrTimestampTokenTstInfoMessageImprintDigestMatches = json:bool_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".tstInfo.messageImprint.digestMatches")
    unauthAttrTimestampTokenTstInfoSerialNumber = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".tstInfo.serialNumber")
    json:dt_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".tstInfo.genTime", false, unauthAttrTimestampTokenTstInfoGenTime)
    j = 0
    count_j = json:size_of_array("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.digestAlgorithms")
    while j < count_j do
        json.j = j
        strVal = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.digestAlgorithms[j]")
        j = j + 1
    end

    j = 0
    count_j = json:size_of_array("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo")
    while j < count_j do
        json.j = j
        certSerialNumber = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].cert.serialNumber")
        certIssuerCN = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].cert.issuerCN")
        certDigestAlgOid = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].cert.digestAlgOid")
        certDigestAlgName = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].cert.digestAlgName")
        contentType = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].contentType")
        json:dt_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].signingTime", false, signingTime)
        messageDigest = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].messageDigest")
        signingAlgOid = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].signingAlgOid")
        signingAlgName = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].signingAlgName")
        authAttrContentTypeName = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].authAttr.\"1.2.840.113549.1.9.3\".name")
        authAttrContentTypeOid = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].authAttr.\"1.2.840.113549.1.9.3\".oid")
        authAttrSigningTimeName = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].authAttr.\"1.2.840.113549.1.9.5\".name")
        json:dt_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].authAttr.\"1.2.840.113549.1.9.5\".utctime", false, authAttrSigningTimeUtctime)
        authAttrSigningCertificateName = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].authAttr.\"1.2.840.113549.1.9.16.2.12\".name")
        authAttrSigningCertificateDer = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].authAttr.\"1.2.840.113549.1.9.16.2.12\".der")
        authAttrMessageDigestName = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].authAttr.\"1.2.840.113549.1.9.4\".name")
        authAttrMessageDigestDigest = json:string_of("pkcs7.verify.signerInfo[i].unauthAttr.\"1.2.840.113549.1.9.16.2.14\".verify.signerInfo[j].authAttr.\"1.2.840.113549.1.9.4\".digest")
        j = j + 1
    end

    i = i + 1
end

i = 0
count_i = json:size_of_array("pkcs7.verify.pkcs7.verify.certs")
while i < count_i do
    json.i = i
    issuerCN = json:string_of("pkcs7.verify.pkcs7.verify.certs[i].issuerCN")
    serial = json:string_of("pkcs7.verify.pkcs7.verify.certs[i].serial")
    i = i + 1
end