Skip to main content
Skip table of contents

How to use a Digital Signing Service (DSS) such as GlobalSign, with iText

 Introduction

When we want to sign a PDF with a digital signature, we need to generate a hash from the document’s data and sign it with a private key. A Digital Signing Service (or DSS) is usually cloud-based software that takes the responsibility of signing the document hash. One such signing service is GlobalSign, a widely-used WebTrust-certified certificate authority and provider of Identity Services.

In this tutorial we will demonstrate how to use the GlobalSign API and iText Core to sign a PDF document’s hash and then add the digital signature to the PDF.

How the signing process works

The task of signing a PDF using a DSS can be split into 2 parts:


1)    A Web service client for communicating with the DSS.
This part depends on the API provided by the DSS.
In the example below, we use a custom helper class for GlobalSign that wraps the DSS functionality we need:


JAVA

JAVA
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

public class ApiConnect {

    public static final String baseURL = "https://emea.api.dss.globalsign.com:8443/v2";


    public static JSONObject login(Object aKey, Object aSecret)
            throws IOException, ParseException, ParseException {

        URL                loginURL = new URL(baseURL + "/login");
        HttpsURLConnection conn     = (HttpsURLConnection) loginURL.openConnection();

        JSONObject apiLogin = new JSONObject();
        apiLogin.put("api_key", aKey);
        apiLogin.put("api_secret", aSecret);

        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        conn.setRequestProperty("Content-Length", "" + apiLogin.toString().length());

        //Send Request
        conn.setDoOutput(true);
        DataOutputStream os = new DataOutputStream(conn.getOutputStream());
        os.writeBytes(apiLogin.toString());
        os.flush();
        os.close();

        //Get Response
        BufferedReader br      = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String         aux     = "";
        StringBuilder  builder = new StringBuilder();
        while ((aux = br.readLine()) != null) {
            builder.append(aux);
        }
        String output = builder.toString();

        JSONParser parser     = new JSONParser();
        JSONObject accessCode = (JSONObject) parser.parse(output);

        br.close();
        conn.disconnect();

        return accessCode;
    }

    public static JSONObject identity(JSONObject aObj) throws IOException, ParseException {

        URL                loginURL = new URL(baseURL + "/identity");
        HttpsURLConnection conn     = (HttpsURLConnection) loginURL.openConnection();

        //info for certificate with individual identities
        JSONObject apiID = new JSONObject();
        JSONObject subj  = new JSONObject();
        subj.put("common_name", "Jose Sue");
        /**         subj.put("country", "GB");
         JSONArray adm = new JSONArray();
         adm.add("Sales");
         subj.put("organizational_unit", adm);**/
        apiID.put("subject_dn", subj);

        //info for organization certificate has been prepopulated so we send an empty request
        //        JSONObject apiID = new JSONObject();

        String token = (String) aObj.get("access_token");

        conn.setRequestMethod("POST");
        conn.setRequestProperty("Authorization", "Bearer " + token);
        conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        conn.setRequestProperty("Content-Length", "" + apiID.toString().length());

        //Send Request
        conn.setDoOutput(true);
        DataOutputStream os = new DataOutputStream(conn.getOutputStream());
        os.writeBytes(apiID.toString());
        os.flush();
        os.close();

        //Get Response
        BufferedReader br      = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String         aux     = "";
        StringBuilder  builder = new StringBuilder();
        while ((aux = br.readLine()) != null) {
            builder.append(aux);
        }
        String output = builder.toString();

        JSONParser parser1  = new JSONParser();
        JSONObject identity = (JSONObject) parser1.parse(output);

        br.close();
        conn.disconnect();

        return identity;
    }

    public static JSONObject certificatePath(JSONObject aObj) throws IOException, ParseException {
        URL                loginURL = new URL(baseURL + "/certificate_path");
        HttpsURLConnection conn     = (HttpsURLConnection) loginURL.openConnection();

        String token = (String) aObj.get("access_token");

        conn.setRequestMethod("GET");
        conn.setRequestProperty("Authorization", "Bearer " + token);

        //Get Response
        BufferedReader br      = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String         aux     = "";
        StringBuilder  builder = new StringBuilder();
        while ((aux = br.readLine()) != null) {
            builder.append(aux);
        }
        String output = builder.toString();

        JSONParser parser = new JSONParser();
        JSONObject path   = (JSONObject) parser.parse(output);

        br.close();
        conn.disconnect();

        return path;
    }

    public static JSONObject timestamp(String digest, JSONObject aObj) throws IOException, ParseException {
        URL                loginURL = new URL(baseURL + "/timestamp/" + digest);
        HttpsURLConnection conn     = (HttpsURLConnection) loginURL.openConnection();

        String token = (String) aObj.get("access_token");

        conn.setRequestMethod("GET");
        conn.setRequestProperty("Authorization", "Bearer " + token);

        //Get Response
        BufferedReader br      = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String         aux     = "";
        StringBuilder  builder = new StringBuilder();
        while ((aux = br.readLine()) != null) {
            builder.append(aux);
        }
        String output = builder.toString();

        JSONParser parser = new JSONParser();
        JSONObject time   = (JSONObject) parser.parse(output);

        br.close();
        conn.disconnect();

        return time;
    }

    public static JSONObject sign(String id, String digest, JSONObject aObj)
            throws IOException, ParseException {
        URL                loginURL = new URL(baseURL + "/identity/" + id + "/sign/" + digest);
        HttpsURLConnection conn     = (HttpsURLConnection) loginURL.openConnection();

        String token = (String) aObj.get("access_token");

        conn.setRequestMethod("GET");
        conn.setRequestProperty("Authorization", "Bearer " + token);
        InputStream is = conn.getInputStream();
        //Get Response
        BufferedReader br      = new BufferedReader(new InputStreamReader(is));
        String         aux     = "";
        StringBuilder  builder = new StringBuilder();
        while ((aux = br.readLine()) != null) {
            builder.append(aux);
        }
        String output = builder.toString();

        JSONParser parser    = new JSONParser();
        JSONObject signature = (JSONObject) parser.parse(output);

        br.close();
        conn.disconnect();

        return signature;
    }


}


C#

C#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Newtonsoft.Json.Linq;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Signatures;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Ocsp;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.X509;
using Sign;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

    public class ApiConnect
    {
        private static string baseURL = "https://emea.api.dss.globalsign.com:8443/v2";

        public static JObject Login(X509Certificate2Collection clientCertificates, String aKey, String aSecret)
        {
            Uri loginURL = new Uri(baseURL + "/login");

            JObject apiLogin = new JObject();
            apiLogin.Add("api_key", aKey);
            apiLogin.Add("api_secret", aSecret);

            var httpWebRequest = (HttpWebRequest) WebRequest.Create(loginURL);
            httpWebRequest.Method = "POST";
            httpWebRequest.ContentType = "application/json; charset=UTF-8";
            httpWebRequest.ContentLength = apiLogin.ToString().Length;
            httpWebRequest.ClientCertificates = clientCertificates;

            //Send Request
            using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
            {
                streamWriter.Write(apiLogin.ToString());
            }

            //Get Response
            var httpResponse = (HttpWebResponse) httpWebRequest.GetResponse();
            string result;
            using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                result = streamReader.ReadToEnd();
            }

            JObject accessCode = JObject.Parse(result);

            return accessCode;
        }

        public static JObject Identity(X509Certificate2Collection clientCertificates, JObject aObj)
        {
            Uri loginURL = new Uri(baseURL + "/identity");

            //info for certificate with individual identities
            JObject apiID = new JObject();
            JObject subj  =  new JObject {{"common_name", "Jose Sue"}};
            apiID.Add("subject_dn", subj);

            String token = (String) aObj.GetValue("access_token");

            var httpWebRequest = (HttpWebRequest) WebRequest.Create(loginURL);
            httpWebRequest.Method = "POST";
            httpWebRequest.Headers.Add("Authorization", "Bearer " + token);
            httpWebRequest.ContentType = "application/json; charset=UTF-8";
            httpWebRequest.ContentLength = apiID.ToString().Length;
            httpWebRequest.ClientCertificates = clientCertificates;

            //Send Request
            using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
            {
                streamWriter.Write(apiID.ToString());
            }

            //Get Response
            var httpResponse = (HttpWebResponse) httpWebRequest.GetResponse();
            string result;
            using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                result = streamReader.ReadToEnd();
            }

            JObject identity = JObject.Parse(result);

            return identity;
        }

        public static JObject CertificatePath(X509Certificate2Collection clientCertificates, JObject aObj)
        {
            Uri loginURL = new Uri(baseURL + "/certificate_path");

            String token = (String) aObj.GetValue("access_token");

            var httpWebRequest = (HttpWebRequest) WebRequest.Create(loginURL);
            httpWebRequest.Method = "GET";
            httpWebRequest.Headers.Add("Authorization", "Bearer " + token);
            httpWebRequest.ClientCertificates = clientCertificates;

            //Get Response
            var httpResponse = (HttpWebResponse) httpWebRequest.GetResponse();
            string result;
            using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                result = streamReader.ReadToEnd();
            }

            JObject path = JObject.Parse(result);

            return path;
        }

        public static JObject Sign(X509Certificate2Collection clientCertificates, String id, String digest, JObject aObj)
        {
            Uri loginURL = new Uri(baseURL + "/identity/" + id + "/sign/" + digest);

            String token = (String) aObj.GetValue("access_token");

            var httpWebRequest = (HttpWebRequest) WebRequest.Create(loginURL);
            httpWebRequest.Method = "GET";
            httpWebRequest.Headers.Add("Authorization", "Bearer " + token);
            httpWebRequest.ClientCertificates = clientCertificates;

            //Get Response
            var httpResponse = (HttpWebResponse) httpWebRequest.GetResponse();
            string result;
            using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                result = streamReader.ReadToEnd();
            }

            JObject signature = JObject.Parse(result);

            return signature;
        }

        public static JObject Timestamp(X509Certificate2Collection clientCertificates, String digest, JObject aObj)
        {
            Uri loginURL = new Uri(baseURL + "/timestamp/" + digest);


            String token = (String) aObj.GetValue("access_token");

            var httpWebRequest = (HttpWebRequest) WebRequest.Create(loginURL);
            httpWebRequest.Method = "GET";
            httpWebRequest.Headers.Add("Authorization", "Bearer " + token);
            httpWebRequest.ClientCertificates = clientCertificates;

            //Get Response
            var httpResponse = (HttpWebResponse) httpWebRequest.GetResponse();
            string result;
            using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                result = streamReader.ReadToEnd();
            }

            JObject time = JObject.Parse(result);

            return time;
        }
    }

2)    Creation of the PDF signature and inserting it into the document.

From the perspective of iText, it does not matter where the hash signing occurs.
The signing logic is delegated to the implementation of either the IExternalSignature or IExternalSignatureContainer interfaces which actually compute the signature from the passed document's byte array.

Those interfaces look similar to each other, however, there is a significant difference in the return values.
IExternalSignature returns the signed hash only and does not give you any control over other signature components, while the output for the IExternalSignatureContainer is a complete PDF signature.

Normally an external signature is being used by the PdfSigner#signDetached method as follows:

JAVA
PdfSigner signer = new PdfSigner(pdfReader, outputStream, new StampingProperties());
signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);

As you can see, to make a PAdES compliant signature you need to extend IOscpClient as well.
Of course, you can always use the default implementation, e.g.:

IOcspClient ocspClient = new OcspClientBouncyCastle(null);

However, it seems reasonable to use the OSCP response that was provided by the GlobalSign API.

For this purpose, using IExternalSignatureContainer instead of IExternalSignature makes the code clearer.
The following example demonstrates using a container for an external signature, together with a custom TSAClient implementation.

JAVA

JAVA
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.signatures.*;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.tsp.TSPAlgorithms;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampToken;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;

class GSSignatureContainer implements IExternalSignatureContainer {

    /**Api credentials. Key*/
    private final String apiKey = "API key";

    /**Api credentials. secret*/
    private final String apiSecret = "API secret";

    /* The Signature dictionary. Should contain values for /Filter and /SubFilter at minimum. */
    private PdfDictionary sigDic;

    public GSSignatureContainer( PdfName filter, PdfName subFilter) {
        sigDic = new PdfDictionary();
        sigDic.put(PdfName.Filter, filter);
        sigDic.put(PdfName.SubFilter, subFilter);
    }

    @Override
    public byte[] sign(InputStream data) throws GeneralSecurityException {
        try {
            JSONObject access = ApiConnect.login(apiKey, apiSecret);

            JSONObject identity = ApiConnect.identity(access);
            System.out.println(identity.toJSONString());
            String cert = (String) identity.get("signing_cert");
            String id   = (String) identity.get("id");
            String oc1  = (String) identity.get("ocsp_response");

            JSONObject path = ApiConnect.certificatePath(access);
            String     ca   = (String) path.get("path");

            Certificate[] chain = createChain(cert, ca);

            String             hashAlgorithm = DigestAlgorithms.SHA256;//"SHA-256";
            BouncyCastleDigest digest        = new BouncyCastleDigest();
            MessageDigest      md            = digest.getMessageDigest(hashAlgorithm);

            byte[]   hash = DigestAlgorithms.digest(data, md);
            PdfPKCS7 sgn  = new PdfPKCS7(null, chain, hashAlgorithm, null, digest, false);

            Collection<byte[]> ocsp = getOCSP(oc1);

            byte[] attributeBytes = sgn.getAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CADES, ocsp, null);

             //create sha256 message digest
            byte[] attributeBytesDigest = MessageDigest.getInstance(hashAlgorithm).digest(attributeBytes);
            //decode hex signature
            String     hexDigest    = Hex.encodeHexString(attributeBytesDigest);
            JSONObject signResponse = ApiConnect.sign(id, hexDigest, access);

            String hexEncodedSignedHash = (String) signResponse.get("signature");
            //decode hex signature
            byte[] signedHash = Hex.decodeHex(hexEncodedSignedHash.toCharArray());
            sgn.setExternalDigest(signedHash, null, "RSA");

            ITSAClient tsaClient = new GSTSAClient(access);
            return sgn.getEncodedPKCS7(hash, PdfSigner.CryptoStandard.CADES, tsaClient, ocsp, null);
        } catch (IOException | GeneralSecurityException | ParseException | DecoderException de) {
            de.printStackTrace();
            throw new GeneralSecurityException(de);
        }


    }

    public Collection<byte[]> getOCSP(String ocspResponse) {
        try {
            byte[]        oc2       = Base64.getDecoder().decode(ocspResponse);
            OCSPResp      ocspResp  = new OCSPResp(oc2);
            BasicOCSPResp basicResp = (BasicOCSPResp) ocspResp.getResponseObject();
            return Collections.singletonList(basicResp.getEncoded());
        } catch (IOException | OCSPException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public void modifySigningDictionary(PdfDictionary signDic) {
        signDic.putAll(sigDic);
    }

    private class GSTSAClient implements ITSAClient {

        static final int DEFAULTTOKENSIZE = 4096;

        static final String DEFAULTHASHALGORITHM = "SHA-256";

        private final JSONObject accessToken;

        GSTSAClient(JSONObject accessToken) {
            this.accessToken = accessToken;
        }

        public MessageDigest getMessageDigest() throws GeneralSecurityException {
            return MessageDigest.getInstance(DEFAULTHASHALGORITHM);
        }

        public byte[] getTimeStampToken(byte[] imprint) throws Exception {
            TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator();
            tsqGenerator.setCertReq(true);

            BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());

            TimeStampRequest request = tsqGenerator.generate(TSPAlgorithms.SHA512, imprint,
                                                             nonce);
            JSONObject time  = ApiConnect.timestamp(Hex.encodeHexString(request.getMessageImprintDigest()),
                                                    accessToken);
            String     tst   = (String) time.get("token");
            byte[]     token = Base64.getDecoder().decode(tst);

            CMSSignedData  cms      = new CMSSignedData(token);
            TimeStampToken tstToken = new TimeStampToken(cms);
            return tstToken.getEncoded();
        }

        public int getTokenSizeEstimate() {
            return DEFAULTTOKENSIZE;
        }

    }

    private static Certificate[] createChain(String cert, String ca) throws IOException, CertificateException {
        Certificate[]      chainy = new Certificate[2];
        CertificateFactory fact   = CertificateFactory.getInstance("X.509");
        X509Certificate    cer    = null;
        InputStream        in     = new ByteArrayInputStream(cert.getBytes("UTF-8"));
        cer = (X509Certificate) fact.generateCertificate(in);
        chainy[0] = (Certificate) cer;
        X509Certificate caCert = null;
        in = new ByteArrayInputStream(ca.getBytes("UTF-8"));
        caCert = (X509Certificate) fact.generateCertificate(in);
        chainy[1] = (Certificate) caCert;
        return chainy;
    }
}

C#

C#
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using iText.Kernel.Pdf;
using iText.Signatures;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Ocsp;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.X509;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

public class GsSignatureContainer : IExternalSignatureContainer
{
    private PdfDictionary sigDic;

    /**Api credentials. Key*/
    private readonly String apiKey = "API key";

    /**Api credentials. secret*/
    private readonly String apiSecret = "API secret";

    private readonly X509Certificate2Collection _clientCertificates;

    public GsSignatureContainer(X509Certificate2Collection clientCertificates, PdfName filter,
        PdfName subFilter)
    {
        sigDic = new PdfDictionary();
        sigDic.Put(PdfName.Filter, filter);
        sigDic.Put(PdfName.SubFilter, subFilter);
        this._clientCertificates = clientCertificates;
    }

    public byte[] Sign(Stream data)
    {
        //get JSON access token
        JObject access = ApiConnect.Login(_clientCertificates, apiKey, apiSecret);

        //get JSON with id/certificate/ocsp response
        JObject identity = ApiConnect.Identity(_clientCertificates, access);
        String cert = (String) identity.GetValue("signing_cert");
        String id = (String) identity.GetValue("id");
        String oc1 = (String) identity.GetValue("ocsp_response");
        JObject path = ApiConnect.CertificatePath(_clientCertificates, access);
        String ca = (String) path.GetValue("path");


        X509Certificate[] chain = CreateChain(cert, ca);


        //OCSP
        byte[] oc2 = Convert.FromBase64String(oc1);
        OcspResp ocspResp = new OcspResp(oc2);

        BasicOcspResp basicResp = (BasicOcspResp) ocspResp.GetResponseObject();
        byte[] oc = basicResp.GetEncoded();
        Collection<byte[]> ocspCollection = new Collection<byte[]>();
        ocspCollection.Add(oc);
        String hashAlgorithm = DigestAlgorithms.SHA256;
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);

        byte[] hash = DigestAlgorithms.Digest(data, DigestAlgorithms.GetMessageDigest(hashAlgorithm));

        byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CADES, ocspCollection,
            null);

        //create sha256 message digest
        using (SHA256 sha256 = SHA256.Create())
        {
            sh = sha256.ComputeHash(sh);
        }

        //create hex encoded sha256 message digest
        String hexencodedDigest = Hex.ToHexString(sh);

        JObject signed = ApiConnect.Sign(_clientCertificates, id, hexencodedDigest, access);
        String sig = (String) signed.GetValue("signature");

        //decode hex signature
        byte[] dsg = Hex.Decode(sig);

        //include signature on PDF
        sgn.SetExternalDigest(dsg, null, "RSA");

        //create TimeStamp Client
        ITSAClient tsc = new GSTSAClient(_clientCertificates, access);

        return sgn.GetEncodedPKCS7(hash, PdfSigner.CryptoStandard.CADES, tsc, ocspCollection, null);
    }

    public Collection<byte[]> getOCSP(String ocspResponse)
    {
        byte[] oc2 = Convert.FromBase64String(ocspResponse);
        OcspResp ocspResp = new OcspResp(oc2);
        BasicOcspResp basicResp = (BasicOcspResp) ocspResp.GetResponseObject();
        Collection<byte[]> ocspCollection = new Collection<byte[]>();
        ocspCollection.Add(basicResp.GetEncoded());
        return ocspCollection;
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.PutAll(sigDic);
    }

    private static X509Certificate[] CreateChain(String cert, String ca)
    {
        X509Certificate[] chainy = new X509Certificate[2];

        X509CertificateParser parser = new X509CertificateParser();

        chainy[0] = new X509Certificate(parser.ReadCertificate(Encoding.UTF8.GetBytes(cert))
            .CertificateStructure);
        chainy[1] = new X509Certificate(parser.ReadCertificate(Encoding.UTF8.GetBytes(ca))
            .CertificateStructure);

        return chainy;
    }

    /** Timestamp client for GlobalSign API*/
    class GSTSAClient : ITSAClient
    {
        public static int DEFAULTTOKENSIZE = 4096;
        public static String DEFAULTHASHALGORITHM = "SHA-256";
        private JObject accessToken;

        private readonly X509Certificate2Collection _clientCertificates;

        public GSTSAClient(X509Certificate2Collection clientCertificates, JObject accessToken)
        {
            this.accessToken = accessToken;
            this._clientCertificates = clientCertificates;
        }

        public IDigest GetMessageDigest()
        {
            return new Sha256Digest();
        }

        public byte[] GetTimeStampToken(byte[] imprint)
        {
            TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator();
            tsqGenerator.SetCertReq(true);

            BigInteger nonce = BigInteger.ValueOf((long) (new TimeSpan(DateTime.Now.Ticks)).TotalMilliseconds);

            TimeStampRequest request = tsqGenerator.Generate(new DerObjectIdentifier(
                    DigestAlgorithms.GetAllowedDigest(DEFAULTHASHALGORITHM)),
                imprint, nonce);

            JObject time = ApiConnect.Timestamp(_clientCertificates,
                Hex.ToHexString(request.GetMessageImprintDigest()), accessToken);
            String tst = (String) time.GetValue("token");
            byte[] token = Base64.Decode(tst);

            CmsSignedData cms = new CmsSignedData(token);

            TimeStampToken tstToken = new TimeStampToken(cms);
            return tstToken.GetEncoded();
        }

        public int GetTokenSizeEstimate()
        {
            return DEFAULTTOKENSIZE;
        }
    }
}

Example code

The following code snippet shows how you can call it:

JAVA

JAVA
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.signatures.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.net.ssl.*;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;

public class GSTest {

    private static final String input = "hello.pdf";

    private static final String output =  "signed_gs_hello.pdf";

    /** path to ssl certificate*/
    private static final String sslCertificate = "path to ssl certificate";

    /**password for ssl certificate*/
    private static final String sslPassword =  "password for ssl certificate";

    /**
     * We need to initialize SSL connection
     */
    public static void init() {
        try {
            Security.addProvider(new BouncyCastleProvider());
            InputStream trustStream   = new FileInputStream(sslCertificate);
            char[]      trustPassword = sslPassword.toCharArray();
            KeyStore    trustStore    = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(trustStream, trustPassword);

            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(trustStore, trustPassword);
            KeyManager[] kms = kmf.getKeyManagers();

            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {

                        public void checkClientTrusted(X509Certificate[] arg0, String arg1)
                                throws CertificateException {
                        }

                        public void checkServerTrusted(X509Certificate[] arg0, String arg1)
                                throws CertificateException {
                        }

                        public X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                    }
            };

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(kms, trustAllCerts, null);
            SSLContext.setDefault(sslContext);
        } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException |
                UnrecoverableKeyException | KeyManagementException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        Security.addProvider(new BouncyCastleProvider());
        init();
        PdfReader          reader             = new PdfReader(input);
        OutputStream       fos                = new FileOutputStream(output);
        StampingProperties stampingProperties = new StampingProperties();
        //For any signature in the Pdf  but the first one, you need to use appendMode
        //        stampingProperties.useAppendMode();
        PdfSigner pdfSigner = new PdfSigner(reader, fos, stampingProperties);

         /*you can modify the signature appearance */
        PdfSignatureAppearance appearance = pdfSigner.GetSignatureAppearance();
        appearance.setPageRect(new Rectangle(36, 508, 254, 200));
        appearance.setPageNumber(1);
        appearance.setLayer2FontSize(14f);
        appearance.setReason("Test signing");

        IExternalSignatureContainer gsContainer = new GSSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
        pdfSigner.signExternalContainer(gsContainer, 8049);
    }
}

C#

C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Signatures;

public class GSTest
    {
        /** path to ssl certificate*/
        private static String sslCertificate = "path to ssl certificate";

        /**password to ssl certificate*/
        private static String sslPassword = "password for ssl certificate";


        private static readonly string SRC =  "hello.pdf";

        private static readonly string DEST = "signed_gs_hello.pdf";

        /** client certificates for SSL */
        static X509Certificate2Collection GetClientCertificates()
        {
            X509Certificate2Collection clientCertificates = new X509Certificate2Collection();
            clientCertificates.Import(sslCertificate, sslPassword,
                X509KeyStorageFlags.DefaultKeySet);
            ServicePointManager.Expect100Continue = true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            return clientCertificates;
        }

        public static void Main(String[] args)
        {
            PdfReader reader = new PdfReader(SRC);
            using (FileStream os = new FileStream(DEST, FileMode.OpenOrCreate))
            {
                StampingProperties stampingProperties = new StampingProperties();
                //For any signature in the Pdf  but the first one, you need to use appendMode
                //        stampingProperties.useAppendMode();
                PdfSigner pdfSigner = new PdfSigner(reader, os, stampingProperties);
                //you can modify the signature appearance 
                PdfSignatureAppearance appearance = pdfSigner.GetSignatureAppearance();
                appearance.SetPageRect(new Rectangle(36, 508, 254, 200));
                appearance.SetPageNumber(1);
                appearance.SetLayer2FontSize(14f);
                appearance.SetReason("Test signing");
                var clientCertificates = GetClientCertificates();
                IExternalSignatureContainer external = new GsSignatureContainer(clientCertificates,
                    PdfName.Adobe_PPKLite,
                    PdfName.Adbe_pkcs7_detached);
                pdfSigner.SignExternalContainer(external, 8049);
            }

        }
    }


Optionally, you can add the LTV data to the result file to get a PAdES compliant signature, as described in: How to enable LTV for a timestamp signature?

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.