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
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#
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:
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
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#
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
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#
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?