// Copyright (C) 2003-2026, Foxit Software Inc..
// All Rights Reserved.
//
// http://www.foxitsoftware.com
//
// The following code is copyrighted and contains proprietary information and trade secrets of Foxit Software Inc..
// You cannot distribute any part of Foxit PDF SDK to any third party or general public,
// unless there is a separate license agreement with Foxit Software Inc. which explicitly grants you such rights.
//
// This file contains an example to demonstrate how to use Foxit PDF SDK to add, sign and verify signature
// in PDF document.
//
// NOTE: Before using this demo, user should ensure that openssl environment has been prepared:
//       user should ensure all the openssl header files included by "#include" can be found and put the "libssl.a" and "libcrypto.a"
//       in the directory "../../../lib".

#include "FSPDFObjC.h"

// Include openssl header files
#include "openssl/rsa.h"
#include "openssl/evp.h"
#include "openssl/objects.h"
#include "openssl/x509.h"
#include "openssl/err.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/pkcs12.h"
#include "openssl/rand.h"
#include "openssl/pkcs7.h"
#include "openssl/stack.h"
#include "openssl/ts.h"


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>

#include <string>

NSString *input_path = @"../input_files/";
NSString *output_path = @"../output_files/";
NSString *output_directory = @"../output_files/signature/";

NSString *sn = @"bPxThFlBAYifO3yiW0C51bKbjPap1zHaf5aG5sX378TjFvB7Q0802A==";
NSString *key = @"8f3g1sGNtR8NAgfSNv+BvaJWy2fnrw36NZvzh8GUxYXvs+/YczAk4e34yqlOnmL4asgxGMO7+rlOcSHUSSKagx5g4G7VEXfkYLXnDDx77FOOedZPrFFcOCu1vT4+wOKzy1zkORq9cKtiFf44byH5sfRqf+OvELjep2teT9eF9yPCsSw2vRKljXlPy40DMP9XX1O5OTe+mSG3RvwKUMb0+RpG3YobM87j+/ynTDlJMMXw4vgwuTb1ZuAtlvkTdFz7HDLV81C6TYnn73ddHeGD3qYWBVsD4uJkDwyyH/SWr7QX0HSgVg8HSZqnab1VGdAUYxKY2vGWeOTJdu6qDGb5ZEb9O3kZZg+9LYAmUKzDfCYwfIsYZ8Y9LPC4wMx70kIq7JpNnzPvigrdshV8/ew1F0+qP/P5Z1EB53vDVrp2LVSuL873Wv4g3PzjZlNH+1qgT87hC+Buto4qsSUfP5Y2z0uApu+c/TfttCXbeMLSsw9TY1HQD3mkrZExMaQqRrAuXAcZWdvBnVuG3DCZmm8q0DLxJiCfYfViAU/AkbeKMIGaTJdrvIXBcssfde4nmU6qr0M4x4+Hs2ESddtY2QRtLAUBq/7h7GJlJHkEZr8ueaUy/6D/2vvAWz6wMLgmjlPbDTLqzfnCJylHCPtcJmu1SPPT9v1VreBX3+zNWcIT8HueyHEiUniqzwWkRKaotrnNb/zhnHLPpgaBSwKjyi8rOzzVwu/nY84G9ye4jsVjmkIRItKDrW7z22iS+QQuTwfqLVuMB0OD4jMkPCC2slzPfEeZnuh4+Rewln2qLRJVgI72zJ20OBWAelGSpO1Ii2Y3gOvojdmtUwx6R3Z1qFNWTTB25o9ytcux8BLOtTCkA8T8xaT4N339OTCsu/TmfahIDixKvr8cDa6F2WkBT8SOfv64W9PgiNv5B/BMOD2VWYaAl0N7yD0NkYYg/cDWRC4dfpdeip1jrr0k3mND0oSpIkxC+FicZbaYYwRdtJ9OSVWJ5OE0voLv8HgLze8mw6s4TrN3z+a2NQCveMa2VRN7YcEF7agPrvb/qU+otMwHae/wM7+f+HFAaOW+WTfzfxIVFB8/A1DN0NiEX+em4G7nO5F5ZcvfYPpaaioe8Nn33BbVLtMjXqsOBeM8AXiVfpD6YO75j7BqnBUPnivvD8BxJaaIQZvVSuQmvGm0ynt03IYwE8Rq+jYsunvsZ86jh5BiUR+Nm7sK0Qe9BdZn/7jdqohMv9czfMGUAscX+xBMunFbDpcRbWVpUADcm209L8Ndek2XY7YlCMxPgPVNvfWRF2jxehib9Tpd7zB5ZIDfAEH9zrpLfp29DLvCh2Yx8mH7xxdKG1O9Q0GdHphYHhej6smAcPm68R0GUQaTOLfXqNpueGJC1GAOI3Y4swjyt7jMTWcibnKLzwukWE5rnEDhn5n64JnyUM0ZFkRmtOiV";

// Some base type declarations
typedef std::string String;

// Used for implementing SignatureCallback.
class DigestContext {
  public:
    DigestContext(id<FSFileReaderCallback> file_read_callback, NSArray<NSNumber *> *byte_range_array)
        : file_read_callback_(file_read_callback), byte_range_array_(byte_range_array) {}
    ~DigestContext() {}

    id<FSFileReaderCallback> GetFileReadCallback() {
        return file_read_callback_;
    }
    NSUInteger GetByteRangeSize() {
        return byte_range_array_.count;
    }
    unsigned int GetByteRangeElement(NSUInteger index) {
        if (!byte_range_array_)
            return 0;
        return [byte_range_array_[index] unsignedIntValue];
    }

    SHA_CTX sha_ctx_;

  protected:
    id<FSFileReaderCallback> file_read_callback_;
    NSArray<NSNumber *> *byte_range_array_;
};

// Implementation of pdf::SignatureCallback
class SignatureCallbackImpl {
  public:
    SignatureCallbackImpl(std::string subfilter)
        : sub_filter_(subfilter), digest_context_(NULL) {}
    ~SignatureCallbackImpl();

    void Release() {
        delete this;
    }
    bool StartCalcDigest(id<FSFileReaderCallback> file, NSArray<NSNumber *> *byte_range_array, FSSignature *signature, const void *client_data);
    FSProgressiveState ContinueCalcDigest(const void *client_data, id<FSPauseCallback> pause);
    NSData *GetDigest(const void *client_data);
    NSData *Sign(const void *digest, uint32 digest_length, std::string cert_path,
                 std::string password, FSSignatureDigestAlgorithm digest_algorithm,
                 void *client_data);
    NSData *Sign(const void *digest, uint32 digest_length, id<FSFileStreamCallback>  cert_file_stream,
                 std::string password, FSSignatureDigestAlgorithm digest_algorithm,
                 void *client_data);
    FSSignatureStates VerifySigState(const void *digest, uint32 digest_length,
                                     const void *signed_data, uint32 signed_data_len,
                                     void *client_data);
    bool IsNeedPadData() { return false; }
    
    FSSignatureCallbackCertValidity CheckCertificateValidity(std::string cert_path, std::string cert_password, void* client_data) {
        // User can check the validity of input certificate here.
        // If no need to check, just return FSSignatureCallbackCertValid.
        return FSSignatureCallbackCertValid;
    }

  protected:
    bool GetTextFromFile(unsigned char *plainString);

    unsigned char *PKCS7Sign(std::string cert_file_path, String cert_file_password,
                             String plain_text, int &signed_data_size);
    bool PKCS7VerifySignature(String signed_data, String plain_text);
    bool ParseP12File(std::string cert_file_path, String cert_file_password,
                      EVP_PKEY **pkey, X509 **x509, STACK_OF(X509) * *ca);
    ASN1_INTEGER *CreateNonce(int bits);

  private:
    std::string sub_filter_;
    DigestContext *digest_context_;

    std::string cert_file_path_;
    std::string cert_file_password_;
};

#define FREE_CERT_KEY if(pkey)\
EVP_PKEY_free(pkey);\
if(x509)\
X509_free(x509);\
if(ca)\
sk_X509_free(ca);

void InitializeOpenssl() {
    //		SSLeay_add_all_algorithms();
}

SignatureCallbackImpl::~SignatureCallbackImpl() {
    if (digest_context_) {
        delete digest_context_;
        digest_context_ = NULL;
    }
}

bool SignatureCallbackImpl::GetTextFromFile(unsigned char *file_buffer) {
    if (!digest_context_ || !digest_context_->GetFileReadCallback())
        return false;
    id<FSFileReaderCallback> file_read = digest_context_->GetFileReadCallback();
    NSData *data = [file_read readBlock:digest_context_->GetByteRangeElement(0) size:digest_context_->GetByteRangeElement(1)];
    [data getBytes:file_buffer length:data.length];

    data = [file_read readBlock:digest_context_->GetByteRangeElement(2) size:digest_context_->GetByteRangeElement(3)];
    [data getBytes:file_buffer + (digest_context_->GetByteRangeElement(1) - digest_context_->GetByteRangeElement(0)) length:data.length];
    return true;
}

bool SignatureCallbackImpl::StartCalcDigest(id<FSFileReaderCallback> file, NSArray<NSNumber *> *byte_range_array, FSSignature *signature, const void *client_data) {
    if (digest_context_) {
        delete digest_context_;
        digest_context_ = NULL;
    }
    digest_context_ = new DigestContext(file, byte_range_array);
    if (!SHA1_Init(&digest_context_->sha_ctx_)) {
        delete digest_context_;
        digest_context_ = NULL;
        return false;
    }
    return true;
}

FSProgressiveState SignatureCallbackImpl::ContinueCalcDigest(const void *client_data, id<FSPauseCallback> pause) {
    if (!digest_context_)
        return FSProgressiveError;

    uint32 file_length = digest_context_->GetByteRangeElement(1) + digest_context_->GetByteRangeElement(3);
    unsigned char *file_buffer = (unsigned char *) malloc(file_length);
    if (!file_buffer || !GetTextFromFile(file_buffer))
        return FSProgressiveError;

    SHA1_Update(&digest_context_->sha_ctx_, file_buffer, file_length);
    free(file_buffer);
    return FSProgressiveFinished;
}

NSData *SignatureCallbackImpl::GetDigest(const void *client_data) {
    if (!digest_context_)
        return nil;
    unsigned char *md = reinterpret_cast<unsigned char *>(OPENSSL_malloc((SHA_DIGEST_LENGTH) * sizeof(unsigned char)));
    if (1 != SHA1_Final(md, &digest_context_->sha_ctx_))
        return nil;
    NSData *digest = [NSData dataWithBytes:reinterpret_cast<const void *>(md) length:SHA_DIGEST_LENGTH];
    OPENSSL_free(md);
    return digest;
}

NSData *SignatureCallbackImpl::Sign(const void *digest, uint32 digest_length, std::string cert_path,
                                    std::string password, FSSignatureDigestAlgorithm digest_algorithm,
                                    void *client_data) {
    if (!digest_context_)
        return nil;
    String plain_text;
    if ("adbe.pkcs7.sha1" == sub_filter_) {
        plain_text = String((const char *) digest, digest_length);
    }
    int signed_data_length = 0;
    unsigned char *signed_data_buffer = PKCS7Sign(cert_path, password,
                                                  plain_text, signed_data_length);
    if (!signed_data_buffer)
        return nil;

    NSData *signed_data = [NSData dataWithBytes:(const void *) signed_data_buffer length:signed_data_length];
    free(signed_data_buffer);
    return signed_data;
}

NSData *SignatureCallbackImpl::Sign(const void *digest, uint32 digest_length, id<FSFileStreamCallback>  cert_file_stream,
                                    std::string password, FSSignatureDigestAlgorithm digest_algorithm,
                                    void *client_data) {
    return nil;
}

FSSignatureStates SignatureCallbackImpl::VerifySigState(const void *digest, uint32 digest_length,
                                                        const void *signed_data, uint32 signed_data_len, void *client_data) {
    // Usually, the content of a signature field is contain the certification of signer.
    // But we can't judge this certification is trusted.
    // For this example, the signer is ourself. So when using api PKCS7_verify to verify,
    // we pass NULL to it's parameter <i>certs</i>.
    // Meanwhile, if application should specify the certificates, we suggest pass flag PKCS7_NOINTERN to
    // api PKCS7_verify.
    if (!digest_context_)
        return FSSignatureStateVerifyErrorData;
    String plain_text;
    unsigned char *file_buffer = NULL;
    if ("adbe.pkcs7.sha1" == sub_filter_) {
        plain_text = String(reinterpret_cast<const char *>(digest), digest_length);
    } else {
        return FSSignatureStateUnknown;
    }

    String signed_data_str = String(reinterpret_cast<const char *>(signed_data), signed_data_len);
    bool ret = PKCS7VerifySignature(signed_data_str, plain_text);
    if (file_buffer)
        free(file_buffer);
    return ret ? FSSignatureStateVerifyNoChange : FSSignatureStateVerifyChange;
}

ASN1_INTEGER *SignatureCallbackImpl::CreateNonce(int bits) {
    unsigned char buf[20];
    int len = (bits - 1) / 8 + 1;
    // Generating random byte sequence.
    if (len > (int) sizeof(buf)) {
        return NULL;
    }
    if (RAND_bytes(buf, len) <= 0) {
        return NULL;
    }
    // Find the first non-zero byte and creating ASN1_INTEGER object.
    int i = 0;
    for (i = 0; i < len && !buf[i]; ++i)
        ;
    ASN1_INTEGER *nonce = NULL;
    if (!(nonce = ASN1_INTEGER_new())) {
        ASN1_INTEGER_free(nonce);
        return NULL;
    }
    OPENSSL_free(nonce->data);
    // Allocate at least one byte.
    nonce->length = len - i;
    if (!(nonce->data = reinterpret_cast<unsigned char *>(OPENSSL_malloc(nonce->length + 1)))) {
        ASN1_INTEGER_free(nonce);
        return NULL;
    }
    memcpy(nonce->data, buf + i, nonce->length);
    return nonce;
}

bool SignatureCallbackImpl::ParseP12File(std::string cert_file_path, String cert_file_password,
                                         EVP_PKEY **pkey, X509 **x509, STACK_OF(X509) * *ca) {
    FILE *file = NULL;
#if defined(_WIN32) || defined(_WIN64)
    _wfopen_s(&file, cert_file_path, @"rb");
#else
    file = fopen(cert_file_path.c_str(), "rb");
#endif  // defined(_WIN32) || defined(_WIN64)
    if (!file) {
        return false;
	}

        PKCS12 *pkcs12 = d2i_PKCS12_fp(file, NULL);
        fclose(file);
        if (!pkcs12) {
            return false;
        }

        if (!PKCS12_parse(pkcs12, cert_file_password.c_str(), pkey, x509, ca)) {
            return false;
        }

        PKCS12_free(pkcs12);
        if (!pkey)
            return false;
        return true;
}

#define HANDLE_CREATE_TS_ERROR     {\
                                    if (!ret)  {\
                                        TS_REQ_free(ts_req);\
                                        ts_req = NULL;\
                                        printf("could not create query\n");\
                                    }\
                                    TS_MSG_IMPRINT_free(msg_imprint);\
                                    X509_ALGOR_free(algo);\
                                    ASN1_OBJECT_free(policy_obj);\
                                    ASN1_INTEGER_free(nonce_asn1);\
                                    return ts_req;\
                                }

static ASN1_INTEGER *create_nonce(int bits)
{
    unsigned char buf[20];
    ASN1_INTEGER *nonce = NULL;
    int len = (bits - 1) / 8 + 1;
    int i;

    /* Generating random byte sequence. */
    if (len > (int)sizeof(buf))
    {
        printf("bit count error\n");
        ASN1_INTEGER_free(nonce);
        return NULL;
    }
    if (RAND_bytes(buf, len) <= 0)
    {
        printf("can not generate random number\n");
        ASN1_INTEGER_free(nonce);
        return NULL;
    }

    /* Find the first non-zero byte and creating ASN1_INTEGER object. */
    for (i = 0; i < len && !buf[i]; ++i);
    if (!(nonce = ASN1_INTEGER_new()))
    {
        printf("could not create nonce\n");
        ASN1_INTEGER_free(nonce);
        return NULL;
    }

    OPENSSL_free(nonce->data);
    /* Allocate at least one byte. */
    nonce->length = len - i;
    if (!(nonce->data = (unsigned char *)OPENSSL_malloc(nonce->length + 1)))
    {
        printf("out of memory\n");
        ASN1_INTEGER_free(nonce);
        return NULL;
    }

    memcpy(nonce->data, buf + i, nonce->length);

    return nonce;
}

static TS_REQ *create_ts_query(unsigned char *digest, int len)
{
    int ret = 0;
    TS_REQ *ts_req = NULL;
    const EVP_MD *md;
    TS_MSG_IMPRINT *msg_imprint = NULL;
    X509_ALGOR *algo = NULL;
    ASN1_OBJECT *policy_obj = NULL;
    ASN1_INTEGER *nonce_asn1 = NULL;

    switch (len) {
    case 20:
        md = EVP_get_digestbyname("sha1");
        break;
    case 32:
        md = EVP_get_digestbyname("sha256");
        break;
    default:
        HANDLE_CREATE_TS_ERROR;
    }

    /* Creating request object. */
    if (!(ts_req = TS_REQ_new()))
        HANDLE_CREATE_TS_ERROR;

    /* Setting version. */
    if (!TS_REQ_set_version(ts_req, 1))
        HANDLE_CREATE_TS_ERROR;

    /* Creating and adding MSG_IMPRINT object. */
    if (!(msg_imprint = TS_MSG_IMPRINT_new()))
        HANDLE_CREATE_TS_ERROR;

    /* Adding algorithm. */
    if (!(algo = X509_ALGOR_new()))
        HANDLE_CREATE_TS_ERROR;
    if (!(algo->algorithm = OBJ_nid2obj(EVP_MD_type(md))))
        HANDLE_CREATE_TS_ERROR;
    if (!(algo->parameter = ASN1_TYPE_new()))
        HANDLE_CREATE_TS_ERROR;
    algo->parameter->type = V_ASN1_NULL;
    if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo))
        HANDLE_CREATE_TS_ERROR;

    if (!TS_MSG_IMPRINT_set_msg(msg_imprint, digest, len))
        HANDLE_CREATE_TS_ERROR;

    if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint))
        HANDLE_CREATE_TS_ERROR;

    /* Setting nonce if requested. */
    if (!(nonce_asn1 = create_nonce(64)))
        HANDLE_CREATE_TS_ERROR;
    if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1))
        HANDLE_CREATE_TS_ERROR;

    /* Setting certificate request flag if requested. */
    if (!TS_REQ_set_cert_req(ts_req, 1))
        HANDLE_CREATE_TS_ERROR;

    ret = 1;
    HANDLE_CREATE_TS_ERROR;
}


static long Get_TS_REP(unsigned char *md, int md_len, unsigned char **pRet)
{
    TS_REQ *request = create_ts_query(md, md_len);
    int len = i2d_TS_REQ(request, NULL);
    unsigned char *tsReq = (unsigned char *)OPENSSL_malloc(len);

    unsigned char *p = tsReq;
    len = i2d_TS_REQ(request, (unsigned char **)&p);
    TS_REQ_free(request);

    char SendBuffer[1024] = "";
    char bsAuth[] = ":";
    NSData* bsAuthData = [NSData dataWithBytes:bsAuth length:1];
    NSData* bsDestAuth = [FSCodec base64Encode:bsAuthData];

    sprintf(SendBuffer, "GET %s HTTP/1.1\r\n"
        "Accept: */*\r\n"
        "Content-Type: application/timestamp-query\r\n"
        "Content-Length: %d\r\n"
        "Character-Encoding: binary\r\n"
        "User-Agent: PPKHandler\r\n"
        "Host: %s\r\n"
        "Connection: Keep-Alive\r\n"
        "Cache-Control: no-cache\r\n"
        "Authorization: Basic %s\r\n"
        "\r\n",
        "/TSAServer.aspx", len, "ca.signfiles.com", (char*)bsDestAuth.bytes);

    unsigned char *totalBuf = (unsigned char *)malloc(len + strlen(SendBuffer));

    memcpy(totalBuf, SendBuffer, strlen(SendBuffer));
    memcpy(totalBuf + strlen(SendBuffer), tsReq, len);

    OPENSSL_free(tsReq);

#ifdef _WIN32
    WSADATA  Ws;
    WSAStartup(MAKEWORD(2, 2), &Ws);
    SOCKET CientSocket;
#else
    int CientSocket;
#endif
    struct sockaddr_in ServerAddr;
    int Ret = 0;
    int AddrLen = 0;


    CientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

#ifdef _WIN32
    HOSTENT * iphost;
#else
    struct hostent *iphost;
#endif
    char serverIP[20] = "";

    if ((iphost = gethostbyname("ca.signfiles.com")) != NULL)
    {
        int i = 0;
        while (iphost->h_addr_list[i])
        {
            memcpy(&serverIP, inet_ntoa(*((struct in_addr *)iphost->h_addr_list[i])), 20);
            i++;
        }
    }

    struct servent *pST = getservbyname("http", NULL);

    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_addr.s_addr = inet_addr(serverIP);
    ServerAddr.sin_port = pST->s_port;
    memset(ServerAddr.sin_zero, 0x00, 8);
    connect(CientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));
    int sendLen = send(CientSocket, (const char *)totalBuf, len + (int)strlen(SendBuffer), 0);

    free(totalBuf);

    char recvBuf[8096] = "";
    int revtotalLen = 0, recTSL = 0;
    int revLen = recv(CientSocket, recvBuf, 8096, 0);
    int nTSRepPos = 0;
    int tsrepL = 0;
    if (revLen == 0)
    {
        printf("\r\ntsa server unreachable.\r\n");
#ifdef _WIN32
        closesocket(CientSocket);
        WSACleanup();
#else
        close(CientSocket);
#endif
        return 0;
    }
    revtotalLen += revLen;

    //get the ts reponse length
    String strRecv = recvBuf;
    String rspCode = strRecv.substr(9, 3);
    if (rspCode != "200")
    {
        printf("\r\n tsa server refuse request.\r\n%s\r\n", recvBuf);
#ifdef _WIN32
        closesocket(CientSocket);
        WSACleanup();
#else
        close(CientSocket);
#endif
        return 0;
    }
    nTSRepPos = (int)(strRecv.find("Content-Length") + strlen("Content-Length") + 2);

    while (strRecv.at(nTSRepPos) > 47 && strRecv.at(nTSRepPos) < 58)
    {
        tsrepL = tsrepL * 10 + strRecv.at(nTSRepPos) - 48;
        nTSRepPos += 1;
    }

    while (recTSL < tsrepL && revtotalLen < tsrepL)
    {
        printf("retry, len:%d\n", revLen);
        revLen = recv(CientSocket, recvBuf + revtotalLen, 8096 - revtotalLen, 0);
        recTSL += revLen;
        revtotalLen += revLen;
    }
    *pRet = (unsigned char *)OPENSSL_malloc(tsrepL);
    memcpy(*pRet, recvBuf + revtotalLen - tsrepL, tsrepL);

#ifdef _WIN32
    closesocket(CientSocket);
    WSACleanup();
#else
    close(CientSocket);
#endif
    return tsrepL;
}

static int append_tsp_token(PKCS7_SIGNER_INFO *sinfo, unsigned char *ts_rep, long tsrepL)
{
    unsigned char *p = ts_rep;
    TS_RESP *tsp = d2i_TS_RESP(NULL, (const unsigned char**)&p, tsrepL);
    if (tsp != NULL)
    {
        PKCS7* token = TS_RESP_get_token(tsp);
        if (!PKCS7_type_is_signed(token))
        {
            printf("Error in timestamp token: not signed!\n");
            return 1;
        }

        int p7_len = i2d_PKCS7(token, NULL);
        unsigned char *p7_der = (unsigned char *)OPENSSL_malloc(p7_len);
        unsigned char *p = p7_der;
        i2d_PKCS7(token, &p);

        if (sinfo)
        {
            //Add timestamp token to the PKCS7 signature object
            ASN1_STRING *value = ASN1_STRING_new();
            ASN1_STRING_set(value, p7_der, p7_len);
            PKCS7_add_attribute(sinfo, NID_id_smime_aa_timeStampToken, V_ASN1_SEQUENCE, value);
            OPENSSL_free(p7_der);
        }
    }
    else
    {
        printf("Error decoding timestamp token!\n");
        return 1;
    }

    return 0;
}

unsigned char *SignatureCallbackImpl::PKCS7Sign(std::string cert_file_path, String cert_file_password,
                                                String plain_text, int &signed_data_size) {
    PKCS7 *p7 = NULL;
    EVP_PKEY *pkey = NULL;
    X509 *x509 = NULL;
    STACK_OF(X509) *ca = NULL;
    if (!ParseP12File(cert_file_path, cert_file_password, &pkey, &x509, &ca))
        return NULL;

    p7 = PKCS7_new();
    PKCS7_set_type(p7, NID_pkcs7_signed);
    PKCS7_content_new(p7, NID_pkcs7_data);

    // Application should not judge the sign algorithm with the content's length.
    // Here, just for convenient;
    if (plain_text.size() > 32)
        PKCS7_ctrl(p7, PKCS7_OP_SET_DETACHED_SIGNATURE, 1, NULL);

    PKCS7_SIGNER_INFO *signer_info = PKCS7_add_signature(p7, x509, pkey, EVP_sha1());
    PKCS7_add_certificate(p7, x509);

# define CHECKED_STACK_OF(type, p) \
((_STACK*) (1 ? p : (STACK_OF(type)*)0))
    for (int i = 0; i < sk_num(CHECKED_STACK_OF(X509, ca)); i++)
        PKCS7_add_certificate(p7, (X509 *) sk_value(CHECKED_STACK_OF(X509, ca), i));

    // Set source data to BIO.
    BIO *p7bio = PKCS7_dataInit(p7, NULL);
    BIO_write(p7bio, plain_text.c_str(), (int) plain_text.size());
    PKCS7_dataFinal(p7, p7bio);
    
    //append the time stamp token to pkcs7 structure.
    unsigned char md256[64] = "";
    SHA256(signer_info->enc_digest->data, signer_info->enc_digest->length, md256);

    unsigned char *pTSRep = NULL;
    long lTSRepLength = Get_TS_REP(md256, 32, &pTSRep);
    if (lTSRepLength != 0)
        append_tsp_token(signer_info, pTSRep, lTSRepLength);
    OPENSSL_free(pTSRep);

    FREE_CERT_KEY;
    BIO_free_all(p7bio);
    // Get signed data.
    unsigned long der_length = i2d_PKCS7(p7, NULL);
    unsigned char *der = reinterpret_cast<unsigned char *>(malloc(der_length));
    memset(der, 0, der_length);
    unsigned char *der_temp = der;
    i2d_PKCS7(p7, &der_temp);
    PKCS7_free(p7);
    signed_data_size = (int) der_length;
    return (unsigned char *) der;
}

bool SignatureCallbackImpl::PKCS7VerifySignature(String signed_data, String plain_text) {
    // Retain PKCS7 object from signed data.
    BIO *vin = BIO_new_mem_buf((void *) signed_data.c_str(), (int) signed_data.size());
    PKCS7 *p7 = d2i_PKCS7_bio(vin, NULL);
    STACK_OF(PKCS7_SIGNER_INFO) *sk = PKCS7_get_signer_info(p7);
    int sign_count = sk_PKCS7_SIGNER_INFO_num(sk);

    //		int length = 0;
    bool bSigAppr = false;
    //		unsigned char *p = NULL;
    for (int i = 0; i < sign_count; i++) {
        PKCS7_SIGNER_INFO *sign_info = sk_PKCS7_SIGNER_INFO_value(sk, i);

        BIO *p7bio = BIO_new_mem_buf((void *) plain_text.c_str(), (int) plain_text.size());
        X509 *x509 = PKCS7_cert_from_signer_info(p7, sign_info);
        x509 = NULL;
        if (1 == PKCS7_verify(p7, NULL, NULL, p7bio, NULL, PKCS7_NOVERIFY))
            bSigAppr = true;
        BIO_free(p7bio);
    }
    PKCS7_free(p7);
    BIO_free(vin);
    return bSigAppr;
}

////////////////////////

NSString *DateTimeToString(const FSDateTime *dateTime) {
    return [NSString stringWithFormat:@"%d/%d/%d-%d:%d:%d %@%d:%d", dateTime.year,
                                      dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second,
                                      dateTime.utc_hour_offset > 0 ? @"+" : @"-",
                                      dateTime.utc_hour_offset, dateTime.utc_minute_offset];
}

FSDateTime *GetLocalDateTime() {
    time_t t = time(NULL);
    struct tm *rime = localtime(&t);
    tzset();
    int timezone_hour = (int) (timezone / 3600 * -1);
    int timezone_minute = (int) (timezone % 3600) / 60;

    FSDateTime *datetime = [[FSDateTime alloc] init];
    datetime.year = static_cast<uint16>(rime->tm_year + 1900);
    datetime.month = static_cast<uint16>(rime->tm_mon + 1);
    datetime.day = static_cast<uint16>(rime->tm_mday);
    datetime.hour = static_cast<uint16>(rime->tm_hour);
    datetime.minute = static_cast<uint16>(rime->tm_min);
    datetime.second = static_cast<uint16>(rime->tm_sec);
    datetime.utc_hour_offset = timezone_hour;
    datetime.utc_minute_offset = timezone_minute;

    return datetime;
}

std::string TransformSignatureStateToString(uint32 sig_state) {
    std::string state_str;
    if ((sig_state & FSSignatureStateUnknown) == FSSignatureStateUnknown)
        state_str += "Unknown";
    if (sig_state & FSSignatureStateNoSignData){
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "NoSignData";
    }
    if (sig_state & FSSignatureStateUnsigned) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "Unsigned";
    }
    if (sig_state & FSSignatureStateSigned) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "Signed";
    }
    if (sig_state & FSSignatureStateVerifyValid) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerfiyValid";
    }
    if (sig_state & FSSignatureStateVerifyInvalid) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyInvalid";
    }
    if (sig_state & FSSignatureStateVerifyErrorData) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyErrorData";
    }
    if (sig_state & FSSignatureStateVerifyNoSupportWay) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyNoSupportWay";
    }
    if (sig_state & FSSignatureStateVerifyErrorByteRange) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyErrorByteRange";
    }
    if (sig_state & FSSignatureStateVerifyChange) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyChange";
    }
    if (sig_state & FSSignatureStateVerifyNoChange) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyNoChange";
    }
    if (sig_state & FSSignatureStateVerifyIncredible) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyIncredible";
    }
    if (sig_state & FSSignatureStateVerifyIssueValid) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyIssueValid";
    }
    if (sig_state & FSSignatureStateVerifyIssueUnknown) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyIssueUnknown";
    }
    if (sig_state & FSSignatureStateVerifyIssueRevoke) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyIssueRevoke";
    }
    if (sig_state & FSSignatureStateVerifyIssueExpire) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyIssueExpire";
    }
    if (sig_state & FSSignatureStateVerifyIssueUncheck) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyIssueUncheck";
    }
    if (sig_state & FSSignatureStateVerifyIssueCurrent) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyIssueCurrent";
    }
    if (sig_state & FSSignatureStateVerifyTimestampNone) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyTimestampNone";
    }
    if (sig_state & FSSignatureStateVerifyTimestampDoc) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyTimestampDoc";
    }
    if (sig_state & FSSignatureStateVerifyTimestampValid) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyTimestampValid";
    }
    if (sig_state & FSSignatureStateVerifyTimestampInvalid) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyTimestampInvalid";
    }
    if (sig_state & FSSignatureStateVerifyTimestampExpire) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyTimestampExpire";
    }
    if (sig_state & FSSignatureStateVerifyTimestampIssueUnknown) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyTimestampIssueUnknown";
    }
    if (sig_state & FSSignatureStateVerifyTimestampIssueValid) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyTimestampIssueValid";
    }
    if (sig_state & FSSignatureStateVerifyTimestampTimeBefore) {
        if (state_str.length() > 0)
            state_str += "|";
        state_str += "VerifyTimestampTimeBefore";
    }
    if (sig_state & FSSignatureStateCertCannotGetVRI) {
        if (state_str.length()>0) state_str += "|";
        state_str += "CertCannotGetVRI";
    }
    return state_str;
}

FSSignature *AddSiganture(FSPDFPage *pdf_page, NSString *sub_filter) {
    float page_height = [pdf_page getHeight];
    float page_width = [pdf_page getWidth];
    FSRectF *new_sig_rect = [[FSRectF alloc] initWithLeft1:0 bottom1:(float) (page_height * 0.9) right1:(float) (page_width * 0.4) top1:page_height];
    // Add a new signature to page.
    FSSignature *new_sig = [pdf_page addSignature:new_sig_rect];
    if ([new_sig isEmpty])
        return nil;
    // Set values for the new signature.
    [new_sig setKeyValue:FSSignatureKeyNameSigner value:@"Foxit PDF SDK"];
    NSString *new_value;
    new_value = [NSString stringWithFormat:@"As a sample for subfilter \"%@\"", sub_filter];
    [new_sig setKeyValue:FSSignatureKeyNameReason value:new_value];
    [new_sig setKeyValue:FSSignatureKeyNameContactInfo value:@"support@foxitsoftware.com"];
    [new_sig setKeyValue:FSSignatureKeyNameDN value:@"CN=CN,MAIL=MAIL@MAIL.COM"];
    [new_sig setKeyValue:FSSignatureKeyNameLocation value:@"Fuzhou, China"];
    new_value = [NSString stringWithFormat:@"As a sample for subfilter \"%@\"", sub_filter];
    [new_sig setKeyValue:FSSignatureKeyNameText value:new_value];
    FSDateTime *sign_time = GetLocalDateTime();
    [new_sig setSignTime:sign_time];

    NSString *image_file_path = [input_path stringByAppendingPathComponent:@"FoxitLogo.jpg"];
    FSImage *image = [[FSImage alloc] initWithPath:image_file_path];
    [new_sig setImage:image frame_index:0];

    // Set appearance flags to decide which content would be used in appearance.
    uint32 ap_flags = FSSignatureAPFlagLabel | FSSignatureAPFlagSigner |
                      FSSignatureAPFlagReason | FSSignatureAPFlagDN |
                      FSSignatureAPFlagLocation | FSSignatureAPFlagText |
                      FSSignatureAPFlagSigningTime | FSSignatureAPFlagBitmap;
    [new_sig setAppearanceFlags:ap_flags];

    return new_sig;
}

@interface SignatureCallback : NSObject<FSSignatureCallback>

@end

@implementation SignatureCallback {
	SignatureCallbackImpl *imp;
}

- (id)initWithSubFilter:(NSString *)subFilter {
	if (self = [super init]) {
		imp = new SignatureCallbackImpl(std::string([subFilter UTF8String]));
	}
	return self;
}

- (FSProgressiveState)continueCalcDigest:(id<FSPauseCallback>)pause client_data:(void *)client_data {
	return imp->ContinueCalcDigest(client_data, pause);
}

- (NSData *)getDigest:(void *)client_data {
	return imp->GetDigest(client_data);
}

- (BOOL)isNeedPadData {
	return imp->IsNeedPadData();
}
- (FSSignatureCallbackCertValidity)checkCertificateValidity:(NSString*)cert_path cert_password:(NSString*)cert_password client_data:(void*)client_data {
  return imp->CheckCertificateValidity(std::string([cert_path UTF8String]), std::string([cert_password UTF8String]), client_data);
}

- (NSData *)sign:(NSData *)digest cert_path:(NSString *)cert_path cert_password:(NSString *)cert_password digest_algorithm:(FSSignatureDigestAlgorithm)digest_algorithm client_data:(void *)client_data {
	return imp->Sign(digest.bytes, (uint32)digest.length, std::string([cert_path UTF8String]), std::string([cert_password UTF8String]), digest_algorithm, client_data);
}

- (NSData *)sign:(NSData*)digest cert_file_stream:(id<FSFileStreamCallback>)cert_file_stream cert_password: (NSString* )cert_password digest_algorithm: (FSSignatureDigestAlgorithm)digest_algorithm client_data: (void* )client_data {
	return imp->Sign(digest.bytes, (uint32)digest.length, cert_file_stream, std::string([cert_password UTF8String]), digest_algorithm, client_data);
}

- (BOOL)startCalcDigest:(id<FSFileReaderCallback>)file byte_range_array:(NSArray<NSNumber *> *)byte_range_array signature:(FSSignature *)signature client_data:(void *)client_data {
	return imp->StartCalcDigest(file, byte_range_array, signature, client_data);
}

- (FSSignatureStates)verifySigState:(NSData *)digest signed_data:(NSData *)signed_data client_data:(void *)client_data {
	return imp->VerifySigState(digest.bytes, (uint32)digest.length, signed_data.bytes, (uint32)signed_data.length, client_data);
}

@end

void AdobePPKLiteSignature(FSPDFDoc *pdf_doc, bool use_default) {
    NSString *filter = @"Adobe.PPKLite";
    NSString *sub_filter = @"adbe.pkcs7.detached";

    if (!use_default) {
        InitializeOpenssl();
        sub_filter = @"adbe.pkcs7.sha1";
        SignatureCallback *sig_callback = [[SignatureCallback alloc] initWithSubFilter:sub_filter];
        [FSLibrary registerSignatureCallback:filter sub_filter:sub_filter signature_callback:sig_callback];
    }

    printf("Use signature callback object for filter \"%s\" and sub-filter \"%s\"\n",
           [filter UTF8String], [sub_filter UTF8String]);
    FSPDFPage *pdf_page = [pdf_doc getPage:0];
    // Add a new signature to first page.
    FSSignature *new_signature = AddSiganture(pdf_page, sub_filter);
    // Set filter and subfilter for the new signature.
    [new_signature setFilter:filter];
    [new_signature setSubFilter:sub_filter];
    bool is_signed = [new_signature isSigned];
    unsigned int sig_state = [new_signature getState];
    printf("[Before signing] Signed?:%s\t State:%s\n",
           is_signed ? "true" : "false",
           TransformSignatureStateToString(sig_state).c_str());

    // Sign the new signature.
    NSString *signed_pdf_path = [output_directory stringByAppendingPathComponent:@"signed_newsignature.pdf"];
    if (use_default)
        signed_pdf_path = [output_directory stringByAppendingPathComponent:@"signed_newsignature_default_handler.pdf"];

    NSString *cert_file_path = [input_path stringByAppendingPathComponent:@"foxit_all.pfx"];
    NSString *cert_file_password = @"123456";
    // Cert file path will be passed back to application through callback function FSSignatureCallback::Sign().
    // In this demo, the cert file path will be used for signing in callback function FSSignatureCallback::Sign().
    [new_signature startSign:cert_file_path cert_password:cert_file_password digest_algorithm:FSSignatureDigestSHA1 save_path:signed_pdf_path client_data:nil pause:nil];
    printf("[Sign] Finished!\n");
    is_signed = [new_signature isSigned];
    sig_state = [new_signature getState];
    printf("[After signing] Signed?:%s\tState:%s\n",
           is_signed ? "true" : "false",
           TransformSignatureStateToString(sig_state).c_str());

    // Open the signed document and verify the newly added signature (which is the last one).
    printf("Signed PDF file: %s\n", [signed_pdf_path UTF8String]);
    FSPDFDoc *signed_pdf_doc = [[FSPDFDoc alloc] initWithPath:signed_pdf_path];
    FSErrorCode error_code = [signed_pdf_doc load:nil];
    if (FSErrSuccess != error_code) {
        printf("Fail to open the signed PDF file.\n");
        return;
    }
    // Get the last signature which is just added and signed.
    int sig_count = [signed_pdf_doc getSignatureCount];
    FSSignature *signed_signature = [signed_pdf_doc getSignature:sig_count - 1];
    // Verify the intergrity of signature.
    [signed_signature startVerify:nil pause:nil];
    printf("[Verify] Finished!\n");
    is_signed = [signed_signature isSigned];
    sig_state = [signed_signature getState];
    printf("[After verifying] Signed?:%s\tState:%s\n",
           is_signed ? "true" : "false",
           TransformSignatureStateToString(sig_state).c_str());
}

void CheckSignatureInfo(FSPDFDoc *pdf_doc) {
	int sig_count = [pdf_doc getSignatureCount];
	if (sig_count<1) {
		printf("No signature in current PDF file.\n");
		return;
	}
	for (int i = 0; i < sig_count; i++) {
		printf("Signature index: %d\n", i);
		FSSignature *signature = [pdf_doc getSignature:i];
		if ([signature isEmpty]) continue;
		bool is_signed = [signature isSigned];
		printf("Signed?:%s\n", is_signed? "true":"false");
		unsigned int sig_state = [signature getState];
		printf("State:%s\n", TransformSignatureStateToString(sig_state).c_str());
		
		FSWidget *widget_annot = [[signature getControl:0] getWidget];
		printf("Object number %u of related widget annotation's dictionary\n", [[widget_annot getDict] getObjNum]);
		
		NSString *filter = [signature getFilter];
		printf("Filter:%s\n", [filter UTF8String]);
		NSString *sub_filter = [signature getSubFilter];
		printf("Sub-filter:%s\n", [sub_filter UTF8String]);
		if (is_signed){
			FSDateTime *sign_time = [signature getSignTime];
			NSString *time_str = DateTimeToString(sign_time);
			printf("Sign Time:%s\n", [time_str UTF8String]);
		}
		NSString *key_value;
		key_value = [signature getKeyValue:FSSignatureKeyNameSigner];
		printf("Signer:%s\n", [key_value UTF8String]);
		key_value = [signature getKeyValue:FSSignatureKeyNameLocation];
		printf("Location:%s\n", [key_value UTF8String]);
		key_value = [signature getKeyValue:FSSignatureKeyNameReason];
		printf("Reason:%s\n", [key_value UTF8String]);
		key_value = [signature getKeyValue:FSSignatureKeyNameContactInfo];
		printf("Contact Info:%s\n", [key_value UTF8String]);
		key_value = [signature getKeyValue:FSSignatureKeyNameDN];
		printf("DN:%s\n", [key_value UTF8String]);
		key_value = [signature getKeyValue:FSSignatureKeyNameText];
		printf("Text:%s\n", [key_value UTF8String]);
	}
}

int main(int argc, const char *argv[]) {
    @autoreleasepool {
        NSString *output_directory = [output_path stringByAppendingPathComponent:@"signature/"];

        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL isDir = NO;
        if ([fileManager fileExistsAtPath:output_directory isDirectory:&isDir]) {
            if (!isDir) {
                NSError *error = nil;
                if (![fileManager removeItemAtPath:output_directory error:&error] || error) {
                    return -1;
                }
                if (![fileManager createDirectoryAtPath:output_directory withIntermediateDirectories:YES attributes:nil error:&error] || error) {
                    return -1;
                }
            }
        } else {
            NSError *error = nil;
            if (![fileManager createDirectoryAtPath:output_directory withIntermediateDirectories:YES attributes:nil error:&error] || error) {
                return -1;
            }
        }

        // Initialize library
        FSErrorCode errorCode = [FSLibrary initialize:sn key:key];
        if (errorCode != FSErrSuccess) {
            NSLog(@"Library Initialize Error: %ld", errorCode);
            return -1;
        }

        NSString *input_file = [input_path stringByAppendingPathComponent:@"AboutFoxit.pdf"];
        printf("Input file path: %s\n", [input_file UTF8String]);
        @try {
            for (int i = 0; i < 2; i++) {
                FSPDFDoc *pdf_doc = [[FSPDFDoc alloc] initWithPath:input_file];
                [pdf_doc startLoad:nil is_cache_stream:NO pause:nil];

                // Check information of existed signature(s) in PDF file if there's any signature in the PDF file.
                CheckSignatureInfo(pdf_doc);
                // Add new signature, sign it and verify it with filter "Adobe.PPKLite" and different subfilter.

                AdobePPKLiteSignature(pdf_doc, i > 0 ? true : false);
                CheckSignatureInfo(pdf_doc);
            }
        }
        @catch (NSException *e) {
            NSLog(@"Unknown Exception, %@", e);
            return -1;
        }
    }
    [FSLibrary destroy];
    return 0;
}
