> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/ios/get-started/samples/digitalsignaturestest.md).

# DigitalSignatures

Sample Obj-C code to use Apryse SDK's high-level digital signature API for digitally signing and/or certifying PDF files.

Sample Obj-C code to use Apryse SDK's high-level digital signature API for digitally signing and/or certifying PDF files. Learn more about our [iOS SDK](/ios/guides.md) and [PDF Digital Signature Library](/core/digital-signature/signature.md).

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------------------
// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
//
// The following steps reflect typical intended usage of the digital signatures API:
//
// 0. Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
//
// 1. EITHER: 
//    (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
//    -OR-
//       (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
//    document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
//    
//    2.    Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
//       If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
//       NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
//        
//    [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
//       or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
//       certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
//       is signed.]
//        
// 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
//    a. Taking a PKCS #12 keyfile path and its password
//    b. Taking a buffer containing a PKCS #12 private keyfile and its password
//    c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
//       in the following fashion: 
//       i)    Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
//             validate/check a digital signature.
//       ii)   Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
//             pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
//       iii)  Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
//    NOTE: It is only possible to sign/certify one signature per call to the Save function.
//
// 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
//    IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
//    so as to not invalidate the other signature(s). 
//
// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
//
// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
//----------------------------------------------------------------------------------------------------------------------

// To build and run this sample, please specify OpenSSL include & lib paths to the Makefile
//
// If OpenSSL is installed elsewhere, it may be necessary to add the path to the headers in the $(INCLUDE) variable as
// well as the location of either libcrypto.a or libcrypto.so/libcrypto.dylib.

// Note for iOS development: This code can be used to digitally sign PDFs in iOS devices. When using the code signing
// part of this code, it will be necessary to compile OpenSSL for iOS.

#define USE_STD_SIGNATURE_HANDLER 1 // Comment out this line if you intend to use OpenSSLSignatureHandler rather than StdSignatureHandler.

#import <CoreFoundation/CoreFoundation.h>

#import <OBJC/PDFNetOBJC.h>
//////////////////// Here follows an example of how to implement a custom signature handler. //////////
#if (!USE_STD_SIGNATURE_HANDLER)

// OpenSSL includes
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pkcs12.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>

// Override PTSignatureHandler
@interface OpenSSLSignatureHandler : PTSignatureHandler
{
    SHA256_CTX m_sha_ctx;
    EVP_PKEY* mp_pkey;      // private key
    X509* mp_x509;          // signing certificate
    STACK_OF(X509)* mp_ca;  // certificate chain up to the CA
}
- (NSString*) GetName;
- (void) AppendData: (NSData*)data;
- (BOOL) Reset;
- (NSData*) CreateSignature;
- (PTSignatureHandler*) Clone;
- (id) init: (NSString*) pfxfile password: (NSString*) password;
- (void) dealloc;
@end // interface OpenSSLSignatureHandler

@implementation OpenSSLSignatureHandler
- (NSString*) GetName
{
    return (@"Adobe.PPKLite");
}
- (void) AppendData: (NSData*)data
{
    SHA256_Update(&m_sha_ctx, [data bytes], [data length]);
    return;
}
- (BOOL) Reset
{
    SHA256_Init(&m_sha_ctx);
    return (YES);
}
- (NSData*) CreateSignature
{
    unsigned char sha_buffer[SHA256_DIGEST_LENGTH];
    memset((void*) sha_buffer, 0, SHA256_DIGEST_LENGTH);
    SHA256_Final(sha_buffer, &m_sha_ctx);
    
    PKCS7* p7 = PKCS7_new();
    PKCS7_set_type(p7, NID_pkcs7_signed);
    
    PKCS7_SIGNER_INFO* p7Si = PKCS7_add_signature(p7, mp_x509, mp_pkey, EVP_sha256());
    PKCS7_add_attrib_content_type(p7Si, OBJ_nid2obj(NID_pkcs7_data));
    PKCS7_add0_attrib_signing_time(p7Si, NULL);
    PKCS7_add1_attrib_digest(p7Si, (const unsigned char*) sha_buffer, SHA256_DIGEST_LENGTH);
    PKCS7_add_certificate(p7, mp_x509);    

    int c = 0;
    for ( ; c < sk_X509_num(mp_ca); c++) {
        X509* cert = sk_X509_value(mp_ca, c);
        PKCS7_add_certificate(p7, cert);
    }
    PKCS7_set_detached(p7, 1);
    PKCS7_content_new(p7, NID_pkcs7_data);
    
    PKCS7_SIGNER_INFO_sign(p7Si);
    
    int p7Len = i2d_PKCS7(p7, NULL);
    NSMutableData* signature = [NSMutableData data];
    unsigned char* p7Buf = (unsigned char*) malloc(p7Len);
    if (p7Buf != NULL) {
        unsigned char* pP7Buf = p7Buf;
        i2d_PKCS7(p7, &pP7Buf);
        [signature appendBytes: (const void*) p7Buf length: p7Len];
        free(p7Buf);
    }
    PKCS7_free(p7);
    
    return (signature);
}
- (PTSignatureHandler*) Clone
{
    return (self);
}
- (id) init: (NSString*) pfxfile password: (NSString*) password;
{
    self = [super init];

    FILE* fp = fopen([pfxfile cStringUsingEncoding: NSASCIIStringEncoding], "rb");
    if (fp == NULL)
        @throw ([NSException exceptionWithName: @"PDFNet Exception" reason: @"Cannot open private key." userInfo: nil]);
    
    PKCS12* p12 = d2i_PKCS12_fp(fp, NULL);
    fclose(fp);
    
    if (p12 == NULL) 
        @throw ([NSException exceptionWithName: @"PDFNet Exception" reason: @"Cannot parse private key." userInfo: nil]);

    mp_pkey = NULL;
    mp_x509 = NULL;
    mp_ca = NULL;
    int parseResult = PKCS12_parse(p12, [password cStringUsingEncoding: NSASCIIStringEncoding], &mp_pkey, &mp_x509, &mp_ca);
    PKCS12_free(p12);
    
    if (parseResult == 0)
        @throw ([NSException exceptionWithName: @"PDFNet Exception" reason: @"Cannot parse private key." userInfo: nil]);

    [self Reset];
    
    return (self);
}
- (void) dealloc
{
    sk_X509_free(mp_ca);
    X509_free(mp_x509);
    EVP_PKEY_free(mp_pkey);
}
@end // implementation OpenSSLSignatureHandler

#endif // (!USE_STD_SIGNATURE_HANDLER)
////////// End of the OpenSSLSignatureHandler custom handler code. ////////////////////

BOOL VerifySimple(NSString* in_docpath, NSString* in_public_key_file_path)
{
    PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath:in_docpath];
    NSLog(@"==========");
    PTVerificationOptions* opts = [[PTVerificationOptions alloc] initWithLevel:e_ptcompatibility_and_archiving];

    // Add trust root to store of trusted certificates contained in VerificationOptions.
    [opts AddTrustedCertificateWithFilePath: in_public_key_file_path in_trust_flags:(e_ptdefault_trust | e_ptcertification_trust)];

    PTSignaturesVerificationStatus result = [doc VerifySignedDigitalSignatures: opts];
    switch (result)
    {
    case e_ptdoc_unsigned:
        NSLog(@"Document has no signed signature fields.");
        return NO;
        /* e_failure == bad doc status, digest status, or permissions status
        (i.e. does not include trust issues, because those are flaky due to being network/config-related) */
    case e_ptdoc_failure:
        NSLog(@"Hard failure in verification on at least one signature.");
        return NO;
    case e_ptdoc_untrusted:
        NSLog(@"Could not verify trust for at least one signature.");
        return NO;
    case e_ptdoc_unsupported:
        /* If necessary, call GetUnsupportedFeatures on VerificationResult to check which
        unsupported features were encountered (requires verification using 'detailed' APIs) */
        NSLog(@"At least one signature contains unsupported features.");
        return NO;
        // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
    case e_ptdoc_verified:
        NSLog(@"All signed signatures in document verified.");
        return YES;
    default:
        [NSException raise:@"unrecognized document verification status" format:@"unrecognized document verification status"];
    }
}

BOOL VerifyAllAndPrint(NSString* in_docpath, NSString* in_public_key_file_path)
{
    PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath:in_docpath];
    NSLog(@"==========");
    PTVerificationOptions* opts = [[PTVerificationOptions alloc] initWithLevel:e_ptcompatibility_and_archiving];
    
    // Add trust root to store of trusted certificates contained in VerificationOptions.
    [opts AddTrustedCertificateWithFilePath: in_public_key_file_path in_trust_flags:(e_ptdefault_trust | e_ptcertification_trust)];

    // Iterate over the signatures and verify all of them.
    PTDigitalSignatureFieldIterator* digsig_fitr = [doc GetDigitalSignatureFieldIterator];
    BOOL verification_status = YES;
    for (; [digsig_fitr HasNext]; [digsig_fitr Next])
    {
        PTDigitalSignatureField* curr = [digsig_fitr Current];
        PTVerificationResult* result = [curr Verify: opts];
        if ([result GetVerificationStatus])
        {
            NSLog(@"Signature verified, objnum: %u", [[curr GetSDFObj] GetObjNum]);
        }
        else
        {
            NSLog(@"Signature verification failed, objnum: %u", [[curr GetSDFObj] GetObjNum]);
            verification_status = NO;
        }

        switch ([result GetDigestAlgorithm])
        {
        case e_ptsha1:
            NSLog(@"Digest algorithm: SHA-1");
            break;
        case e_ptsha256:
            NSLog(@"Digest algorithm: SHA-256");
            break;
        case e_ptsha384:
            NSLog(@"Digest algorithm: SHA-384");
            break;
        case e_ptsha512:
            NSLog(@"Digest algorithm: SHA-512");
            break;
        case e_ptripemd160:
            NSLog(@"Digest algorithm: RIPEMD-160");
            break;
        case e_ptunknown_digest_algorithm:
            NSLog(@"Digest algorithm: unknown");
            break;
        default:
            [NSException raise:@"unrecognized digest algorithm" format:@"unrecognized digest algorithm"];
        }

        NSLog(@"Detailed verification result: \n\t%@\n\t%@\n\t%@\n\t%@", 
            [result GetDocumentStatusAsString],
            [result GetDigestStatusAsString],
            [result GetTrustStatusAsString],
            [result GetPermissionsStatusAsString]);


        NSArray<PTDisallowedChange *>* changes = [result GetDisallowedChanges];
        for (id change in changes)
        {
            NSLog(@"\tDisallowed change: %@, objnum: %u", [change GetTypeAsString], [change GetObjNum]);
        }
        
        // Get and print all the detailed trust-related results, if they are available.
        if ([result HasTrustVerificationResult])
        {
            PTTrustVerificationResult* trust_verification_result = [result GetTrustVerificationResult];
            NSLog([trust_verification_result WasSuccessful]? @"Trust verified." : @"Trust not verifiable.");
            NSLog(@"%@", [trust_verification_result GetResultString]);

            unsigned long tmp_time_t = [trust_verification_result GetTimeOfTrustVerification];
            switch ([trust_verification_result GetTimeOfTrustVerificationEnum])
            {
            case e_ptcurrent:
                NSLog(@"Trust verification attempted with respect to current time (as epoch time): %lu", tmp_time_t);
                break;
            case e_ptsigning:
                NSLog(@"Trust verification attempted with respect to signing time (as epoch time): %lu", tmp_time_t);
                break;
            case e_pttimestamp:
                NSLog(@"Trust verification attempted with respect to secure embedded timestamp (as epoch time): %lu", tmp_time_t);
                break;
            default:
                [NSException raise:@"unrecognized time enum value" format:@"unrecognized time enum value"];
            }

            if ([[trust_verification_result GetCertPath ] count] == 0)
            {
                NSLog(@"Could not print certificate path.\n");
            }
            else
            {
                NSLog(@"Certificate path:\n");
                NSArray<PTX509Certificate*> *cert_path = [trust_verification_result GetCertPath];
                for (int j = 0; j < [cert_path count]; j++)

                {
                    NSLog(@"\tCertificate:\n");
                    PTX509Certificate* full_cert = cert_path[j];
                    NSLog(@"\t\tIssuer names:\n");
                    NSArray<PTX501AttributeTypeAndValue*>*issuer_dn = [[full_cert GetIssuerField] GetAllAttributesAndValues];
                    for (int i = 0; i < [issuer_dn count]; i++)
                    {
                        NSLog(@"\t\t\t%@",[[issuer_dn objectAtIndex:i] GetStringValue]);
                    }
                    NSLog(@"\t\tSubject names:\n");
                    NSArray<PTX501AttributeTypeAndValue*>* subject_dn = [[full_cert GetSubjectField] GetAllAttributesAndValues];
                    for (int i = 0; i < [subject_dn count]; i++)
                    {
                        NSLog(@"\t\t\t%@\n", [[subject_dn objectAtIndex:i] GetStringValue]);
                    }
                    NSLog(@"\t\tExtensions:\n");
                    NSArray<PTX509Extension*> *ex = [full_cert GetExtensions];
                    for (size_t i = 0; i < [ex count]; i++)
                    {
                        NSLog(@"\t\t\t%@\n",[[ex objectAtIndex:i] ToString]);
                    }
                }
            }
        }
        else
        {
            NSLog(@"No detailed trust verification result available.");
        }

        NSArray<NSString *> *unsupported_features = [ result GetUnsupportedFeatures ];
        for (int i = 0; i < [unsupported_features count]; i++)
        {
            NSLog(@"%@", [unsupported_features objectAtIndex:i]);
        }
        NSLog(@"==========");
    }

    return verification_status;
}

void CertifyPDF(NSString* in_docpath,
    NSString* in_cert_field_name,
    NSString* in_private_key_file_path,
    NSString* in_keyfile_password,
    NSString* in_appearance_image_path,
    NSString* in_outpath)
{
    NSLog(@"================================================================================");
    NSLog(@"Certifying PDF document");

    // Open an existing PDF
    PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: in_docpath];

    NSLog(@"PDFDoc has %@", ([doc HasSignatures] ? @"signatures" : @"no signatures"));

    PTPage* page1 = [doc GetPage: 1];

    // Create a text field that we can lock using the field permissions feature.
    PTTextWidget* annot1 = [PTTextWidget Create: doc pos: [[PTPDFRect alloc] initWithX1: 143 y1: 440 x2: 350 y2: 460] field_name: @"asdf_test_field"];
    [page1 AnnotPushBack: annot1];

    /* Create a new signature form field in the PDFDoc. The name argument is optional;
    leaving it empty causes it to be auto-generated. However, you may need the name for later.
    Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
    Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
    PTDigitalSignatureField* certification_sig_field = [doc CreateDigitalSignatureField: in_cert_field_name];
    PTSignatureWidget* widgetAnnot = [PTSignatureWidget CreateWithDigitalSignatureField: doc pos: [[PTPDFRect alloc] initWithX1: 143 y1: 287 x2: 219 y2: 306] field: certification_sig_field];
    [page1 AnnotPushBack: widgetAnnot];

    // (OPTIONAL) Add an appearance to the signature field.
    PTImage* img = [PTImage CreateWithFile: [doc GetSDFDoc] filename: in_appearance_image_path encoder_hints: [[PTObj alloc]init]];
    [widgetAnnot CreateSignatureAppearance: img];

    // Prepare the document locking permission level. It will be applied upon document certification.
    NSLog(@"Adding document permissions.");
    [certification_sig_field SetDocumentPermissions: e_ptannotating_formfilling_signing_allowed];
    
    // Prepare to lock the text field that we created earlier.
    NSLog(@"Adding field permissions.");
    NSMutableArray<NSString *> * fields_to_lock = [[NSMutableArray<NSString *> alloc] init];
    [fields_to_lock addObject:@"asdf_test_field"];
    [certification_sig_field SetFieldPermissions: e_ptdigsig_permission_include in_field_names: [fields_to_lock copy]];

#ifdef USE_STD_SIGNATURE_HANDLER
    [certification_sig_field CertifyOnNextSave: in_private_key_file_path in_password: in_keyfile_password];
#else
    OpenSSLSignatureHandler* sigHandler = [[OpenSSLSignatureHandler alloc] init:in_private_key_file_path password: in_keyfile_password];
    PTSignatureHandlerId sigHandlerId = [doc AddSignatureHandler: sigHandler];
    [certification_sig_field CertifyOnNextSaveWithCustomHandler: sigHandlerId];
    /* Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format 
    for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, 
    so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this 
    when using the StdSignatureHandler. */
    PTObj* f_obj = [certification_sig_field GetSDFObj];
    [[f_obj FindObj: @"V"] PutName: @"SubFilter" name: @"adbe.pkcs7.detached"];
#endif

    // (OPTIONAL) Add more information to the signature dictionary.
    [certification_sig_field SetLocation: @"Vancouver, BC"];
    [certification_sig_field SetReason: @"Document certification."];
    [certification_sig_field SetContactInfo: @"www.pdftron.com"];

    // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
    [doc SaveToFile: in_outpath flags: 0];

    NSLog(@"================================================================================");
}

void SignPDF(NSString* in_docpath,
    NSString* in_approval_field_name,
    NSString* in_private_key_file_path,
    NSString* in_keyfile_password,
    NSString* in_appearance_img_path,
    NSString* in_outpath)
{
    NSLog(@"================================================================================");
    NSLog(@"Signing PDF document");

    // Open an existing PDF
    PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: in_docpath];

    // Retrieve the unsigned approval signature field.
    PTField* found_approval_field = [doc GetField: in_approval_field_name];
    PTDigitalSignatureField* found_approval_signature_digsig_field = [[PTDigitalSignatureField alloc] initWithIn_field: found_approval_field];
    
    // (OPTIONAL) Add an appearance to the signature field.
    PTImage* img = [PTImage CreateWithFile: [doc GetSDFDoc] filename: in_appearance_img_path encoder_hints: [[PTObj alloc]init]];
    PTSignatureWidget* found_approval_signature_widget = [[PTSignatureWidget alloc] initWithD: [found_approval_field GetSDFObj]];
    [found_approval_signature_widget CreateSignatureAppearance: img];

    // Prepare the signature and signature handler for signing.
#ifdef USE_STD_SIGNATURE_HANDLER
    [found_approval_signature_digsig_field SignOnNextSave: in_private_key_file_path in_password: in_keyfile_password];
#else
    OpenSSLSignatureHandler* sigHandler = [[OpenSSLSignatureHandler alloc] init:in_private_key_file_path password: in_keyfile_password];
    PTSignatureHandlerId sigHandlerId = [doc AddSignatureHandler: sigHandler];
    [found_approval_signature_digsig_field SignOnNextSaveWithCustomHandler: sigHandlerId];
    /* Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format 
    for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, 
    so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this 
    when using the StdSignatureHandler. */
    PTObj* f_obj = [found_approval_signature_digsig_field GetSDFObj];
    [[f_obj FindObj: @"V"] PutName: @"SubFilter" name: @"adbe.pkcs7.detached"];
#endif

    // The actual approval signing will be done during the following incremental save operation.
    [doc SaveToFile: in_outpath flags: e_ptincremental];

    NSLog(@"================================================================================");
}

void ClearSignature(NSString* in_docpath,
    NSString* in_digsig_field_name,
    NSString* in_outpath)
{
    NSLog(@"================================================================================");
    NSLog(@"Clearing certification signature");

    PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: in_docpath];

    PTDigitalSignatureField* digsig = [[PTDigitalSignatureField alloc] initWithIn_field: [doc GetField: in_digsig_field_name]];
    
    NSLog(@"Clearing signature: %@", in_digsig_field_name);
    [digsig ClearSignature];

    if (![digsig HasCryptographicSignature])
    {
        NSLog(@"Cryptographic signature cleared properly.");
    }

    // Save incrementally so as to not invalidate other signatures from previous saves.
    [doc SaveToFile: in_outpath flags: e_ptincremental];

    NSLog(@"================================================================================");
}

void PrintSignaturesInfo(NSString* in_docpath)
{
    NSLog(@"================================================================================");
    NSLog(@"Reading and printing digital signature information");

    PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: in_docpath];
    if (![doc HasSignatures])
    {
        NSLog(@"Doc has no signatures.");
        NSLog(@"================================================================================");
        return;
    }
    else
    {
        NSLog(@"Doc has signatures.");
    }

    
    PTFieldIterator* fitr = [doc GetFieldIterator];
    for(; [fitr HasNext]; [fitr Next]) 
    {
        NSLog(@"==========");
        if ([[fitr Current] IsLockedByDigitalSignature])
        {
            NSLog(@"Field locked by a digital signature");
        }
        else
        {
            NSLog(@"Field not locked by a digital signature");
        }

        NSLog(@"Field name: %@", [[fitr Current] GetName]);
        NSLog(@"==========");
    }

    NSLog(@"====================");
    NSLog(@"Now iterating over digital signatures only.");
    NSLog(@"====================");

    PTDigitalSignatureFieldIterator* digsig_fitr = [doc GetDigitalSignatureFieldIterator];
    for (; [digsig_fitr HasNext]; [digsig_fitr Next])
    {
        NSLog(@"==========");
        NSLog(@"Field name of digital signature: %@", [[[PTField alloc] initWithField_dict: [[digsig_fitr Current] GetSDFObj]] GetName]);

        PTDigitalSignatureField* digsigfield = [digsig_fitr Current];
        if (![digsigfield HasCryptographicSignature])
        {
            NSLog(@"Either digital signature field lacks a digital signature dictionary, "
                @"or digital signature dictionary lacks a cryptographic Contents entry. "
                @"Digital signature field is not presently considered signed.");
            NSLog(@"==========");
            continue;
        }

        int cert_count = [digsigfield GetCertCount];
        NSLog(@"Cert count: %d", cert_count);
        for (int i = 0; i < cert_count; ++i)
        {
            NSData* cert = [digsigfield GetCert:i];
            NSLog(@"Cert #%d size: %lu", i, cert.length);
        }

        PTDigitalSignatureFieldSubFilterType subfilter = [digsigfield GetSubFilter];

        NSLog(@"Subfilter type: %d", (int)subfilter);

        if (subfilter != e_ptETSI_RFC3161)
        {
            NSLog(@"Signature's signer: %@", [digsigfield GetSignatureName] == NULL? @"" : [digsigfield GetSignatureName]);

            PTDate* signing_time = [digsigfield GetSigningTime];
            if ([signing_time IsValid])
            {
                NSLog(@"Signing time is valid.");
            }

            NSLog(@"Location: %@", [digsigfield GetLocation] == NULL? @"" : [digsigfield GetLocation]);
            NSLog(@"Reason: %@", [digsigfield GetReason] == NULL? @"" : [digsigfield GetReason]);
            NSLog(@"Contact info: %@", [digsigfield GetContactInfo] == NULL? @"" : [digsigfield GetContactInfo]);
        }
        else
        {
            NSLog(@"SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)");
        }

        NSLog(([digsigfield HasVisibleAppearance]) ? @"Visible" : @"Not visible");

        PTDigitalSignatureFieldDocumentPermissions digsig_doc_perms = [digsigfield GetDocumentPermissions];
        NSArray<NSString *>* locked_fields = [digsigfield GetLockedFields];
        for (id field_name in locked_fields)
        {
            NSLog(@"This digital signature locks a field named: %@", field_name);
        }

        switch (digsig_doc_perms)
        {
        case e_ptno_changes_allowed:
            NSLog(@"No changes to the document can be made without invalidating this digital signature.");
            break;
        case e_ptformfilling_signing_allowed:
            NSLog(@"Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
            break;
        case e_ptannotating_formfilling_signing_allowed:
            NSLog(@"Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
            break;
        case e_ptunrestricted:
            NSLog(@"Document not restricted by this digital signature.");
            break;
        default:
            [NSException raise:@"Unrecognized digital signature document permission level." format:@"Unrecognized digital signature document permission level."];
        }
        NSLog(@"==========");
    }

    NSLog(@"================================================================================");
}

void CustomSigningAPI(NSString* doc_path,
    NSString* cert_field_name,
    NSString* private_key_file_path,
    NSString* keyfile_password,
    NSString* public_key_file_path,
    NSString* appearance_image_path,
    PTDigestAlgorithmType digest_algorithm_type,
    BOOL PAdES_signing_mode,
    NSString* output_path)
{
    NSLog(@"================================================================================");
    NSLog(@"Custom signing PDF document");

    PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: doc_path];
    PTPage* page1 = [doc GetPage: 1];

    PTDigitalSignatureField* digsig_field = [doc CreateDigitalSignatureField: cert_field_name];
    PTSignatureWidget* widgetAnnot = [PTSignatureWidget CreateWithDigitalSignatureField: doc pos: [[PTPDFRect alloc] initWithX1: 143 y1: 287 x2: 219 y2: 306] field: digsig_field];
    [page1 AnnotPushBack: widgetAnnot];

    // (OPTIONAL) Add an appearance to the signature field.
    PTImage* img = [PTImage Create: [doc GetSDFDoc] filename: appearance_image_path];
    [widgetAnnot CreateSignatureAppearance: img];
    
    // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
    [digsig_field CreateSigDictForCustomSigning: @"Adobe.PPKLite"
    in_subfilter_type: PAdES_signing_mode ? e_ptETSI_CAdES_detached : e_ptadbe_pkcs7_detached 
    in_contents_size_to_reserve: 7500]; // For security reasons, set the contents size to a value greater than but as close as possible to the size you expect your final signature to be, in bytes.
                                        // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
    
    // (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
    PTDate* current_date = [[PTDate alloc] init];
    [current_date SetCurrentTime];
    [digsig_field SetSigDictTimeOfSigning: current_date];

    [doc SaveToFile: output_path flags: e_ptincremental];

    // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
    NSData* pdf_digest = [digsig_field CalculateDigest: digest_algorithm_type];

    PTX509Certificate* signer_cert = [[PTX509Certificate alloc] initWithIn_certificate_path: public_key_file_path];

    // Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
    // The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
    NSData* pades_versioned_ess_signing_cert_attribute = [PTDigitalSignatureField GenerateESSSigningCertPAdESAttribute: signer_cert in_digest_algorithm_type: digest_algorithm_type];

    // Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
    // The signedAttrs are certain attributes that become protected by their inclusion in the signature.
    NSData* signedAttrs = [PTDigitalSignatureField GenerateCMSSignedAttributes: pdf_digest in_custom_signedattributes_buf: pades_versioned_ess_signing_cert_attribute];

    // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
    NSData* signedAttrs_digest = [PTDigestAlgorithm CalculateDigest: digest_algorithm_type in_message_buf: signedAttrs];

    //////////////////////////// custom digest signing starts ////////////////////////////
    // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
    // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
    NSData* signature_value = [PTDigestAlgorithm SignDigest: signedAttrs_digest
        digest_algorithm_type: digest_algorithm_type
        pkcs12_keyfile_path: private_key_file_path
        pkcs12_password: keyfile_password];
    //////////////////////////// custom digest signing ends //////////////////////////////

    // Then, load all your chain certificates into a container of X509Certificate.
    NSArray<PTX509Certificate *> *chain_certs;

    // Then, create ObjectIdentifiers for the algorithms you have used.
    // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
    PTObjectIdentifier* digest_algorithm_oid = [[PTObjectIdentifier alloc] initWithIn_digest_algorithm_type: digest_algorithm_type];
    PTObjectIdentifier* signature_algorithm_oid = [[PTObjectIdentifier alloc] initWithIn_oid_enum: e_ptRSA_encryption_PKCS1];

    // Then, put the CMS signature components together.
    NSData* cms_signature = [PTDigitalSignatureField GenerateCMSSignature: signer_cert in_chain_certs_list: chain_certs
        in_digest_algorithm_oid: digest_algorithm_oid in_signature_algorithm_oid: signature_algorithm_oid
        in_signature_value_buf: signature_value in_signedattributes_buf: signedAttrs];

    // Write the signature to the document.
    [doc SaveCustomSignatureToFile: cms_signature in_field: digsig_field in_path: output_path];
    
    NSLog(@"================================================================================");
}

BOOL TimestampAndEnableLTV(NSString* doc_path, 
    NSString* tsa_url, 
    NSString* trusted_cert_path, 
    NSString* appearance_img_path,
    NSString* output_path)
{
    PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: doc_path];
    PTDigitalSignatureField* doctimestamp_signature_field = [doc CreateDigitalSignatureField: @""];
    PTTimestampingConfiguration* tst_config = [[PTTimestampingConfiguration alloc] initWithIn_url: tsa_url];
    PTVerificationOptions* opts = [[PTVerificationOptions alloc] initWithLevel:e_ptcompatibility_and_archiving];

    /* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
    the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
    response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
    function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
    in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
    [ opts AddTrustedCertificateWithFilePath: trusted_cert_path in_trust_flags:e_ptdefault_trust];
    /* By default, we only check online for revocation of certificates using the newer and lighter 
    OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
    it may be necessary to enable online CRL revocation checking in order to verify some timestamps
    (i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
    [ opts EnableOnlineCRLRevocationChecking: YES];

    PTSignatureWidget* widgetAnnot = [PTSignatureWidget CreateWithDigitalSignatureField: doc pos: [[PTPDFRect alloc] initWithX1: 0 y1: 100 x2: 200 y2: 150] field: doctimestamp_signature_field];
    [[doc GetPage: 1] AnnotPushBack: widgetAnnot];

    // (OPTIONAL) Add an appearance to the signature field.
    PTImage* img = [PTImage CreateWithFile: [doc GetSDFDoc] filename: appearance_img_path encoder_hints: [[PTObj alloc]init]];

    [widgetAnnot CreateSignatureAppearance: img];

    NSLog(@"Testing timestamping configuration.");
    PTTimestampingResult* config_result = [tst_config TestConfiguration:opts];
    if([ config_result GetStatus])
    {
        NSLog(@"Success: timestamping configuration usable. Attempting to timestamp.");
    }
    else
    {
        // Print details of timestamping failure.
        NSLog(@"%@", [config_result GetString]);
        if ([config_result HasResponseVerificationResult])
        {
            PTEmbeddedTimestampVerificationResult* tst_result = [config_result GetResponseVerificationResult];
            NSLog(@"CMS digest status: %@\n", [tst_result GetCMSDigestStatusAsString]);
            NSLog(@"Message digest status: %@\n", [tst_result GetMessageImprintDigestStatusAsString]);
            NSLog(@"Trust status: %@\n", [tst_result GetTrustStatusAsString]);
        }
        return NO;
    }

    [doctimestamp_signature_field TimestampOnNextSave: tst_config in_timestamp_response_verification_options: opts];

    // Save/signing throws if timestamping fails.
    [doc SaveToFile: output_path flags: e_ptincremental];
    NSLog(@"Timestamping successful. Adding LTV information for DocTimeStamp signature.");

    // Add LTV information for timestamp signature to document.
    PTVerificationResult* timestamp_verification_result = [doctimestamp_signature_field Verify: opts];
    if (! [doctimestamp_signature_field EnableLTVOfflineVerification: timestamp_verification_result])
    {
        NSLog(@"Could not enable LTV for DocTimeStamp.");
        return NO;
    }
    [doc SaveToFile: output_path flags: e_ptincremental];
    NSLog(@"Added LTV information for DocTimeStamp signature successfully.");

    return YES;
}

int main(int argc, char *argv[])
{
    @autoreleasepool {

        // Initialize PDFNetC
        [PTPDFNet Initialize: 0];
    

    #if (!USE_STD_SIGNATURE_HANDLER)
        // Initialize OpenSSL library
        CRYPTO_malloc_init();
        ERR_load_crypto_strings();
        OpenSSL_add_all_algorithms();
    #endif // (!USE_STD_SIGNATURE_HANDLER)

        int ret = 0;
        
        NSString* input_path = @"../../TestFiles/";
        NSString* output_path = @"../../TestFiles/Output/";

        //////////////////// TEST 0: 
        /* Create an approval signature field that we can sign after certifying.
        (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
        @try
        {
            PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath:[NSString stringWithFormat:@"%@/%@", input_path, @"waiver.pdf"]];
            PTDigitalSignatureField* approval_signature_field = [doc CreateDigitalSignatureField: @"PDFTronApprovalSig"];
            PTSignatureWidget* widgetAnnotApproval = [PTSignatureWidget CreateWithDigitalSignatureField: doc pos: [[PTPDFRect alloc] initWithX1: 300 y1: 287 x2: 376 y2: 306] field: approval_signature_field];
            PTPage* page1 = [doc GetPage: 1];
            [page1 AnnotPushBack: widgetAnnotApproval];
            [doc SaveToFile: [NSString stringWithFormat:@"%@/%@", output_path, @"waiver_withApprovalField_output.pdf"] flags: e_ptremove_unused];
        }
        @catch (NSException* e)
        {
            NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
            ret = 1;
        }

        //////////////////// TEST 1: certify a PDF.
        @try
        {
            CertifyPDF([NSString stringWithFormat:@"%@/%@", input_path, @"waiver_withApprovalField.pdf"],
                @"PDFTronCertificationSig",
                [NSString stringWithFormat:@"%@/%@", input_path, @"pdftron.pfx"],
                @"password",
                [NSString stringWithFormat:@"%@/%@", input_path, @"pdftron.bmp"],
                [NSString stringWithFormat:@"%@/%@", output_path, @"waiver_withApprovalField_certified_output.pdf"]);
            PrintSignaturesInfo([NSString stringWithFormat:@"%@/%@", output_path, @"waiver_withApprovalField_certified_output.pdf"]);
        }
        @catch (NSException* e)
        {
            NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
            ret = 1;
        }

        //////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
        @try
        {
            SignPDF([NSString stringWithFormat:@"%@/%@", input_path, @"waiver_withApprovalField_certified.pdf"],
                @"PDFTronApprovalSig",
                [NSString stringWithFormat:@"%@/%@", input_path, @"pdftron.pfx"],
                @"password",
                [NSString stringWithFormat:@"%@/%@", input_path, @"signature.jpg"],
                [NSString stringWithFormat:@"%@/%@", output_path, @"waiver_withApprovalField_certified_approved_output.pdf"]);
            PrintSignaturesInfo([NSString stringWithFormat:@"%@/%@", output_path, @"waiver_withApprovalField_certified_approved_output.pdf"]);
        }
        @catch (NSException* e)
        {
            NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
            ret = 1;
        }

        //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
        @try
        {
            ClearSignature([NSString stringWithFormat:@"%@/%@", input_path, @"waiver_withApprovalField_certified_approved.pdf"],
                @"PDFTronCertificationSig",
                [NSString stringWithFormat:@"%@/%@", output_path, @"waiver_withApprovalField_certified_approved_certcleared_output.pdf"]);
            PrintSignaturesInfo([NSString stringWithFormat:@"%@/%@", output_path, @"waiver_withApprovalField_certified_approved_certcleared_output.pdf"]);
        }
        @catch (NSException* e)
        {
            NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
            ret = 1;
        }
        
        //////////////////// TEST 4: Verify a document's digital signatures.
        @try
        {
            if (!VerifyAllAndPrint([NSString stringWithFormat:@"%@/%@", input_path, @"waiver_withApprovalField_certified_approved.pdf"],
                [NSString stringWithFormat:@"%@/%@", input_path, @"pdftron.cer"]))
            {
                ret = 1;
            }
        }
        @catch (NSException* e)
        {
            NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
            ret = 1;
        }
        
        //////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
        @try
        {
            if (!VerifySimple([NSString stringWithFormat:@"%@/%@", input_path, @"waiver_withApprovalField_certified_approved.pdf"],
                [NSString stringWithFormat:@"%@/%@", input_path, @"pdftron.cer"]))
            {
                ret = 1;
            }
        }
        @catch (NSException* e)
        {
            NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
            ret = 1;
        }

        //////////////////// TEST 6: Custom signing API.
        // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
        // which allows users to customize the process of signing documents. Among other things, this
        // includes the capability to allow for easy integration of PDF-specific signing-related operations
        // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
        // to system keystores, etc.
        @try
        {
            CustomSigningAPI([NSString stringWithFormat:@"%@/%@", input_path, @"waiver.pdf"],
                @"PDFTronApprovalSig",
                [NSString stringWithFormat:@"%@/%@", input_path, @"pdftron.pfx"],
                @"password",
                [NSString stringWithFormat:@"%@/%@", input_path, @"pdftron.cer"],
                [NSString stringWithFormat:@"%@/%@", input_path, @"signature.jpg"],
                e_ptsha256,
                YES,
                [NSString stringWithFormat:@"%@/%@", output_path, @"waiver_custom_signed.pdf"]);
        }
        @catch (NSException* e)
        {
            NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
            ret = 1;
        }

        //////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
        // @try
        // {
        //     // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
        //     // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
        //     // Note that this url may not work in the future. A reliable solution requires using your own TSA.
        //     NSString* tsa_url = @"YOUR_URL_OF_TSA";
        //     if ([tsa_url isEqualToString:@"YOUR_URL_OF_TSA"])
        //     {
        //         [NSException raise:@"Error: The URL of your timestamp authority was not specified." format:@"Error: The URL of your timestamp authority was not specified."];
        //     }
        //
        //     // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
        //     // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
        //     // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
        //     NSString* trusted_cert_path = @"YOUR_CERTIFICATE";
        //     if ([trusted_cert_path isEqualToString:@"YOUR_CERTIFICATE"])
        //     {
        //         [NSException raise:@"Error: The path to your timestamp authority trusted root certificate was not specified." format:@"Error: The path to your timestamp authority trusted root certificate was not specified."];
        //     }
        //
        //     if (!TimestampAndEnableLTV([NSString stringWithFormat:@"%@/%@", input_path, @"waiver.pdf"],
        //        tsa_url,
        //        trusted_cert_path,
        //        [NSString stringWithFormat:@"%@/%@", input_path, @"signature.jpg"],
        //        [NSString stringWithFormat:@"%@/%@", output_path, @"waiver_DocTimeStamp_LTV.pdf"]))
        //     {
        //         ret = 1;
        //     }
        // }
        // @catch (NSException* e)
        // {
        //     NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
        //     ret = 1;
        // }

        //////////////////// End of tests. ////////////////////

        if (!ret)
        {
            NSLog(@"Tests successful.");
            NSLog(@"==========");
        }
        else
        {
            NSLog(@"Tests FAILED!!!");
            NSLog(@"==========");
        }

    #if (!USE_STD_SIGNATURE_HANDLER)
        ERR_free_strings();
        EVP_cleanup();
    #endif // (!USE_STD_SIGNATURE_HANDLER)
        [PTPDFNet Terminate: 0];
        return ret;
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2020 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------------------
// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
//
// The following steps reflect typical intended usage of the digital signatures API:
//
//    0.    Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
//
//    1.    EITHER:
//        (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
//        -OR-
//        (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
//        document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
//
//    2.    Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
//        If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
//        NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
//
//    [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
//        or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
//        certain types of changes to be made to the document without invalidating the cryptographic digital signature's hash once it
//        is signed.]
//
//    4.     Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
//        a.    Taking a PKCS #12 keyfile path and its password
//        b.    Taking a buffer containing a PKCS #12 private keyfile and its password
//        c.    Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
//            in the following fashion:
//            i)        Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
//                    validate/check a digital signature.
//            ii)        Create an instance of the implemented SignatureHandler and register it with PDFDoc with
//                    pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
//            iii)    Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
//        NOTE: It is only possible to sign/certify one signature per call to the Save function.
//
//    5.    Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic hash to it.
//        IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
//        so as to not invalidate the other signature's('s) cryptographic hashes.
//
// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
//
// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
//----------------------------------------------------------------------------------------------------------------------

// To build and run this sample, please specify OpenSSL include & lib paths to the Makefile
//
// If OpenSSL is installed elsewhere, it may be necessary to add the path to the headers in the $(INCLUDE) variable as
// well as the location of either libcrypto.a or libcrypto.so/libcrypto.dylib.

// Note for iOS development: This code can be used to digitally sign PDFs in iOS devices. When using the code signing
// part of this code, it will be necessary to compile OpenSSL for iOS.

import PDFNet
import Foundation

// EXPERIMENTAL. Digital signature verification is undergoing active development, but currently does not support a number of features. If we are missing a feature that is important to you, or if you have files that do not act as expected, please contact us using one of the following forms: https://apryse.com/form/trial-support/
func VerifyAllAndPrint(_ in_docpath: String?, _ in_public_key_file_path: String?) -> Bool {
    let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)
    print("==========")
    let opts: PTVerificationOptions = PTVerificationOptions(level: e_ptcompatibility_and_archiving)

    // Trust the public certificate we use for signing.
    let trusted_cert_file: PTMappedFile = PTMappedFile(filename: in_public_key_file_path)
    let file_sz = trusted_cert_file.fileSize()
    let file_reader: PTFilterReader = PTFilterReader(filter: trusted_cert_file)
    let trusted_cert_buf = file_reader.read(file_sz)
    opts.addTrustedCertificate(trusted_cert_buf)

    // Iterate over the signatures and verify all of them.
    let digsig_fitr: PTDigitalSignatureFieldIterator = doc.getDigitalSignatureFieldIterator()
    var verification_status = true
    while digsig_fitr.hasNext() {
        defer {
            digsig_fitr.next()
        }
        
        let curr: PTDigitalSignatureField = digsig_fitr.current()
        let result: PTVerificationResult = curr.verify(opts)
        if result.getVerificationStatus() {
            print("Signature verified, objnum: \(curr.getSDFObj().getNum())")
        } else {
            print("Signature verification failed, objnum: \(curr.getSDFObj().getNum())")
            verification_status = false
        }
        
        switch result.getSignersDigestAlgorithm() {
        case e_ptsha1:
            print("Digest algorithm: SHA-1")
        case e_ptsha256:
            print("Digest algorithm: SHA-256")
        case e_ptsha384:
            print("Digest algorithm: SHA-384")
        case e_ptsha512:
            print("Digest algorithm: SHA-512")
        case e_ptripemd160:
            print("Digest algorithm: RIPEMD-160")
        case e_ptunknown_digest_algorithm:
            print("Digest algorithm: unknown")
        default:
            let e = NSException(name: NSExceptionName("unrecognized digest algorithm"), reason: "unrecognized digest algorithm", userInfo: nil)
            e.raise()
        }
        
        print("Detailed verification result: ")
        switch result.getDocumentStatus() {
        case e_ptno_error:
            print("\tNo general error to report.")
        case e_ptcorrupt_file:
            print("\tSignatureHandler reported file corruption.")
        case e_ptunsigned:
            print("\tThe signature has not yet been cryptographically signed.")
        case e_ptbad_byteranges:
            print("\tSignatureHandler reports corruption in the ByteRanges in the digital signature.")
        case e_ptcorrupt_cryptographic_contents:
            print("\tSignatureHandler reports corruption in the Contents of the digital signature.")
        default:
            let e = NSException(name: NSExceptionName("unrecognized document status"), reason: "unrecognized document status", userInfo: nil)
            e.raise()
        }
        switch result.getDigestStatus() {
        case e_ptdigest_invalid:
            print("\tThe digest is incorrect.")
        case e_ptdigest_verified:
            print("\tThe digest is correct.")
        case e_ptdigest_verification_disabled:
            print("\tDigest verification has been disabled.")
        case e_ptweak_digest_algorithm_but_digest_verifiable:
            print("\tThe digest is correct, but the digest algorithm is weak and not secure.")
        case e_ptno_digest_status:
            print("\tNo digest status to report.")
        case e_ptunsupported_encoding:
            print("\tNo installed SignatureHandler was able to recognize the signature's encoding.")
        default:
            let e = NSException(name: NSExceptionName("unrecognized digest status"), reason: "unrecognized digest status", userInfo: nil)
            e.raise()
        }
        switch result.getTrustStatus() {
        case e_pttrust_verified:
            print("\tEstablished trust in signer successfully.")
        case e_ptuntrusted:
            print("\tTrust could not be established.")
        case e_pttrust_verification_disabled:
            print("\tTrust verification has been disabled.")
        case e_ptno_trust_status:
            print("\tNo trust status to report.")
        default:
            let e = NSException(name: NSExceptionName("unrecognized trust status"), reason: "unrecognized trust status", userInfo: nil)
            e.raise()
        }
        
        switch result.getPermissionsStatus() {
        case e_ptinvalidated_by_disallowed_changes:
            print("\tThe document has changes that are disallowed by the signature's permissions settings.")
        case e_pthas_allowed_changes:
            print("\tThe document has changes that are allowed by the signature's permissions settings.")
        case e_ptunmodified:
            print("\tThe document has not been modified since it was signed.")
        case e_ptpermissions_verification_disabled:
            print("\tPermissions verification has been disabled.")
        case e_ptno_permissions_status:
            print("\tNo permissions status to report.")
        default:
            let e = NSException(name: NSExceptionName("unrecognized modification permissions status"), reason: "unrecognized modification permissions status", userInfo: nil)
            e.raise()
        }
        
        let changes: [PTDisallowedChange] = result.getDisallowedChanges()
        for change in changes {
            print("\tDisallowed change: \(change.getTypeAsString() ?? ""), objnum: \(change.getObjNum())")
        }
        
        if result.hasTrust() {
            let trust_verification_result: PTTrustVerificationResult = result.getTrust()
            print(trust_verification_result.wasSuccessful() ? "Trust verified." : "Trust not verifiable.")
            print("Trust verification result string: \(trust_verification_result.getString() ?? "")")
            
            let tmp_time_t = trust_verification_result.getTimeOfTrustVerification()
            switch trust_verification_result.getTimeOfTrustVerificationEnum() {
            case e_ptcurrent:
                print(String(format: "Trust verification attempted with respect to current time (as epoch time): %lu", tmp_time_t))
            case e_ptsigning:
                print(String(format: "Trust verification attempted with respect to signing time (as epoch time): %lu", tmp_time_t))
            case e_pttimestamp:
                print(String(format: "Trust verification attempted with respect to secure embedded timestamp (as epoch time): %lu", tmp_time_t))
            default:
                let e = NSException(name: NSExceptionName("unrecognized time enum value"), reason: "unrecognized time enum value", userInfo: nil)
                e.raise()
            }
        } else {
            print("No detailed trust verification result available.")
        }
        
        print("==========")
    }
    
    return verification_status
}

func CertifyPDF(_ in_docpath: String?, _ in_cert_field_name: String?, _ in_private_key_file_path: String?, _ in_keyfile_password: String?, _ in_appearance_image_path: String?, _ in_outpath: String?) {
    
    print("================================================================================")
    print("Certifying PDF document")

    // Open an existing PDF
    let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)

    print("PDFDoc has \(doc.hasSignatures() ? "signatures" : "no signatures")")

    let page1: PTPage = doc.getPage(1)
    
    // Create a text field that we can lock using the field permissions feature.
    let annot1 = PTTextWidget.create(doc, pos: PTPDFRect(x1: 50, y1: 550, x2: 350, y2: 600), field_name: "asdf_test_field")
    page1.annotPushBack(annot1)

    /* Create a new signature form field in the PDFDoc. The name argument is optional;
        leaving it empty causes it to be auto-generated. However, you may need the name for later.
        Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
        Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
    let certification_sig_field: PTDigitalSignatureField = doc.createDigitalSignatureField(in_cert_field_name)
    let widgetAnnot: PTSignatureWidget = PTSignatureWidget.create(withDigitalSignatureField: doc, pos: PTPDFRect(x1: 0, y1: 100, x2: 200, y2: 150), field: certification_sig_field)
    page1.annotPushBack(widgetAnnot)

    // (OPTIONAL) Add an appearance to the signature field.
    let img = PTImage.create(withFile: doc.getSDFDoc(), filename: in_appearance_image_path, encoder_hints: PTObj())
    widgetAnnot.createSignatureAppearance(img)

    // Prepare the document locking permission level. It will be applied upon document certification.
    print("Adding document permissions.")
    certification_sig_field.setDocumentPermissions(e_ptannotating_formfilling_signing_allowed)
    
    // Prepare to lock the text field that we created earlier.
    print("Adding field permissions.")
    var fields_to_lock: [String] = []
    fields_to_lock.append("asdf_test_field")
    certification_sig_field.setFieldPermissions(e_ptdigsig_permission_include, in_field_names: fields_to_lock)
    
    certification_sig_field.certify(onNextSave: in_private_key_file_path, in_password: in_keyfile_password)

    // (OPTIONAL) Add more information to the signature dictionary.
    certification_sig_field.setLocation("Vancouver, BC")
    certification_sig_field.setReason("Document certification.")
    certification_sig_field.setContactInfo("www.pdftron.com")

    // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
    doc.save(toFile: in_outpath, flags: 0)

    print("================================================================================")
}

func SignPDF(_ in_docpath: String?, _ in_approval_field_name: String?, _ in_private_key_file_path: String?, _ in_keyfile_password: String?, _ in_appearance_img_path: String?, _ in_outpath: String?) {
    print("================================================================================")
    print("Signing PDF document")

    // Open an existing PDF
    let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)

    // Retrieve the unsigned approval signature field.
    let found_approval_field: PTField = doc.getField(in_approval_field_name)
    let found_approval_signature_digsig_field: PTDigitalSignatureField = PTDigitalSignatureField(in_field: found_approval_field)

    // (OPTIONAL) Add an appearance to the signature field.
    let img: PTImage = PTImage.create(withFile: doc.getSDFDoc(), filename: in_appearance_img_path, encoder_hints: PTObj())
    let found_approval_signature_widget: PTSignatureWidget = PTSignatureWidget(d: found_approval_field.getSDFObj())
    found_approval_signature_widget.createSignatureAppearance(img)

    // Prepare the signature and signature handler for signing.
    found_approval_signature_digsig_field.sign(onNextSave: in_private_key_file_path, in_password: in_keyfile_password)

    // The actual approval signing will be done during the following incremental save operation.
    doc.save(toFile: in_outpath, flags: e_ptincremental.rawValue)

    print("================================================================================")
}

func ClearSignature(_ in_docpath: String?, _ in_digsig_field_name: String?, _ in_outpath: String?) {
    print("================================================================================")
    print("Clearing certification signature")
    
    let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)
    
    let digsig: PTDigitalSignatureField = PTDigitalSignatureField(in_field: doc.getField(in_digsig_field_name))
    
    print("Clearing signature: \(in_digsig_field_name ?? "")")
    digsig.clearSignature()
    
    if !digsig.hasCryptographicSignature() {
        print("Cryptographic signature cleared properly.")
    }
    
    // Save incrementally so as to not invalidate other signatures' hashes from previous saves.
    doc.save(toFile: in_outpath, flags: e_ptincremental.rawValue)

    print("================================================================================")
}

func PrintSignaturesInfo(_ in_docpath: String?) {
    print("================================================================================")
    print("Reading and printing digital signature information")

    let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)
    if !doc.hasSignatures() {
        print("Doc has no signatures.")
        print("================================================================================")
        return
    } else {
        print("Doc has signatures.")
    }

    let fitr: PTFieldIterator = doc.getFieldIterator()

    while fitr.hasNext() {
        defer {
            fitr.next()
        }
        
        print("==========")
        if fitr.current().isLockedByDigitalSignature() {
            print("Field locked by a digital signature")
        } else {
            print("Field not locked by a digital signature")
        }

        print("Field name: \(fitr.current().getName() ?? "")")
        print("==========")
    }
    
    print("====================")
    print("Now iterating over digital signatures only.")
    print("====================")

    let digsig_fitr: PTDigitalSignatureFieldIterator = doc.getDigitalSignatureFieldIterator()

    while digsig_fitr.hasNext() {
        defer {
            digsig_fitr.next()
        }
        
        print("==========")
        print("Field name of digital signature: \(PTField(field_dict: digsig_fitr.current().getSDFObj()).getName() ?? "")")

        let digsigfield: PTDigitalSignatureField = digsig_fitr.current()
        if !digsigfield.hasCryptographicSignature() {
            print("""
            Either digital signature field lacks a digital signature dictionary, \
            or digital signature dictionary lacks a cryptographic hash entry. \
            Digital signature field is not presently considered signed.
            """)
            print("==========")
            continue
        }

        let cert_count = digsigfield.getCertCount()
        print("Cert count: \(cert_count)")
        for i in 0..<cert_count {
            let cert: Data = digsigfield.getCert(i)
            print(String(format: "Cert #%d size: %lu", i, cert.count))
        }

        let subfilter = digsigfield.getSubFilter()

        print("Subfilter type: \(subfilter)")

        if subfilter != e_ptETSI_RFC3161 {
            print("Signature's signer: \(digsigfield.getSignatureName() ?? "")")

            let signing_time: PTDate = digsigfield.getSigningTime()
            if signing_time.isValid() {
                print("Signing day: \((signing_time as TRN_date).getDay())")
            }

            print("Location: \(digsigfield.getLocation() ?? "")")
            print("Reason: \(digsigfield.getReason() ?? "")")
            print("Contact info: \(digsigfield.getContactInfo() ?? "")")
        } else {
            print("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)")
        }

        print((digsigfield.hasVisibleAppearance()) ? "Visible" : "Not visible")
        
        let digsig_doc_perms = digsigfield.getDocumentPermissions()
        let locked_fields: [String]  = digsigfield.getLockedFields()
        for field_name in locked_fields {
            print("This digital signature locks a field named: \(field_name)")
        }
        
        switch digsig_doc_perms {
        case e_ptno_changes_allowed:
            print("No changes to the document can be made without invalidating this digital signature.")
        case e_ptformfilling_signing_allowed:
            print("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
        case e_ptannotating_formfilling_signing_allowed:
            print("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
        case e_ptunrestricted:
            print("Document not restricted by this digital signature.")
        default:
            //NSException.raise("Unrecognized digital signature document permission level.", format: "Unrecognized digital signature document permission level.")
            print()
        }
        print("==========")
    }

    print("================================================================================")
}

func runDigitalSignaturesTest() -> Int {
    return autoreleasepool {
        
        var result = true
        
        let input_path: String = (Bundle.main.resourcePath)! + ("/")
        let output_path: String = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("").path
        
        //////////////////// TEST 0:
        /* Create an approval signature field that we can sign after certifying.
         (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
        do {
            try PTPDFNet.catchException {
                let doc: PTPDFDoc = PTPDFDoc(filepath: "\(input_path)\("tiger.pdf")")
                let approval_signature_field: PTDigitalSignatureField = doc.createDigitalSignatureField("PDFTronApprovalSig")
                let widgetAnnotApproval: PTSignatureWidget = PTSignatureWidget.create(withDigitalSignatureField: doc, pos: PTPDFRect(x1: 300, y1: 300, x2: 500, y2: 200), field: approval_signature_field)
                let page1: PTPage = doc.getPage(1)
                page1.annotPushBack(widgetAnnotApproval)
                doc.save(toFile: "\(output_path)\("tiger_withApprovalField_output.pdf")", flags: e_ptremove_unused.rawValue)
            }
        } catch {
            print("Exception: \(error)\n")
            result = false
        }
        
        do {
            try PTPDFNet.catchException {
                CertifyPDF("\(input_path)\("tiger_withApprovalField.pdf")",
                    "PDFTronCertificationSig",
                    "\(input_path)\("pdftron.pfx")",
                    "password",
                    "\(input_path)\("pdftron.bmp")",
                    "\(output_path)\("tiger_withApprovalField_certified_output.pdf")")
                PrintSignaturesInfo("\(output_path)\("tiger_withApprovalField_certified_output.pdf")")
            }
        } catch {
            print("Exception: \(error)\n")
            result = false
        }
        
        //////////////////// TEST 2: sign a PDF with a certification and an unsigned signature field in it.
        do {
            try PTPDFNet.catchException {
                SignPDF("\(input_path)\("tiger_withApprovalField_certified.pdf")",
                    "PDFTronApprovalSig",
                    "\(input_path)\("pdftron.pfx")",
                    "password",
                    "\(input_path)\("signature.jpg")",
                    "\(output_path)\("tiger_withApprovalField_certified_approved_output.pdf")")
                PrintSignaturesInfo("\(output_path)\("tiger_withApprovalField_certified_approved_output.pdf")")
            }
        } catch {
            print("Exception: \(error)\n")
            result = false
        }
        
        //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
        do {
            try PTPDFNet.catchException {
                ClearSignature("\(input_path)\("tiger_withApprovalField_certified_approved.pdf")",
                    "PDFTronCertificationSig",
                    "\(output_path)\("tiger_withApprovalField_certified_approved_certcleared_output.pdf")")
                PrintSignaturesInfo("\(output_path)\("tiger_withApprovalField_certified_approved_certcleared_output.pdf")")
            }
        } catch {
            print("Exception: \(error)\n")
            result = false
        }
        
        //////////////////// TEST 4: Verify a document's digital signatures.
        do {
            try PTPDFNet.catchException {
                // EXPERIMENTAL. Digital signature verification is undergoing active development, but currently does not support a number of features. If we are missing a feature that is important to you, or if you have files that do not act as expected, please contact us using one of the following forms: https://apryse.com/form/trial-support/
                
                if !VerifyAllAndPrint("\(input_path)\("tiger_withApprovalField_certified_approved.pdf")", "\(input_path)\("pdftron.cer")") {
                    result = false
                }
            }
        } catch {
            print("Exception: \(error)\n")
            result = false
        }
        
        //////////////////// End of tests. ////////////////////

        if result {
            print("Tests successful.")
            print("==========")
        } else {
            print("Tests FAILED!!!")
            print("==========")
        }
        
        return result ? 1 : 0
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/ios/get-started/samples/digitalsignaturestest.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
