Skip to main content
Skip table of contents

Digital signatures - chapter 4

These examples were written in the context of Chapter 4 - "Creating signatures externally" of the Digital Signatures for PDF documents eBook.


c4_01_signwithpkcs11hsm

Signing a document with PKCS#11 using a Hardware Security Module (HSM) - currently only available for Java

JAVA

JAVA
package com.itextpdf.samples.signatures.chapter04;

import sun.security.pkcs11.SunPKCS11;

import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.signatures.BouncyCastleDigest;
import com.itextpdf.signatures.CertificateUtil;
import com.itextpdf.signatures.ICrlClient;
import com.itextpdf.signatures.CrlClientOnline;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.IExternalDigest;
import com.itextpdf.signatures.IExternalSignature;
import com.itextpdf.signatures.IOcspClient;
import com.itextpdf.signatures.OcspClientBouncyCastle;
import com.itextpdf.signatures.PdfSignatureAppearance;
import com.itextpdf.signatures.PdfSigner;
import com.itextpdf.signatures.PrivateKeySignature;
import com.itextpdf.signatures.ITSAClient;
import com.itextpdf.signatures.TSAClientBouncyCastle;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class C4_01_SignWithPKCS11HSM {
    public static final String DEST = "./target/signatures/chapter04/";

    public static final String SRC = "./src/test/resources/pdfs/hello.pdf";

    public static final String[] RESULT_FILES = new String[] {
            "hello_hsm.pdf"
    };

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        File file = new File(DEST);
        file.mkdirs();

        Properties properties = new Properties();

        // Specify the correct path to the certificate
        properties.load(new FileInputStream("C:/signkey.properties"));
        char[] pass = properties.getProperty("PASSWORD").toCharArray();
        String pkcs11cfg = properties.getProperty("PKCS11CFG");

        BouncyCastleProvider providerBC = new BouncyCastleProvider();
        Security.addProvider(providerBC);
        FileInputStream fis = new FileInputStream(pkcs11cfg);
        Provider providerPKCS11 = new SunPKCS11(fis);
        Security.addProvider(providerPKCS11);

        KeyStore ks = KeyStore.getInstance("PKCS11");
        ks.load(null, pass);
        String alias = ks.aliases().nextElement();
        PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
        Certificate[] chain = ks.getCertificateChain(alias);
        IOcspClient ocspClient = new OcspClientBouncyCastle(null);
        ITSAClient tsaClient = null;
        for (int i = 0; i < chain.length; i++) {
            X509Certificate cert = (X509Certificate) chain[i];
            String tsaUrl = CertificateUtil.getTSAURL(cert);
            if (tsaUrl != null) {
                tsaClient = new TSAClientBouncyCastle(tsaUrl);
                break;
            }
        }

        List<ICrlClient> crlList = new ArrayList<ICrlClient>();
        crlList.add(new CrlClientOnline(chain));

        new C4_01_SignWithPKCS11HSM().sign(SRC, DEST + RESULT_FILES[0], chain, pk,
                DigestAlgorithms.SHA256, providerPKCS11.getName(), PdfSigner.CryptoStandard.CMS,
                "HSM test", "Ghent", crlList, ocspClient, tsaClient, 0);
    }

    public void sign(String src, String dest, Certificate[] chain, PrivateKey pk,
            String digestAlgorithm, String provider, PdfSigner.CryptoStandard subfilter,
            String reason, String location, Collection<ICrlClient> crlList,
            IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize)
            throws GeneralSecurityException, IOException {
        PdfReader reader = new PdfReader(src);
        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), new StampingProperties());

        // Create the signature appearance
        Rectangle rect = new Rectangle(36, 648, 200, 100);
        PdfSignatureAppearance appearance = signer.getSignatureAppearance();
        appearance
                .setReason(reason)
                .setLocation(location)

                // Specify if the appearance before field is signed will be used
                // as a background for the signed field. The "false" value is the default value.
                .setReuseAppearance(false)
                .setPageRect(rect)
                .setPageNumber(1);
        signer.setFieldName("sig");

        IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
        IExternalDigest digest = new BouncyCastleDigest();

        // Sign the document using the detached mode, CMS or CAdES equivalent.
        signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
    }
}


c4_02_signwithpkcs11usb

Signing a document with PKCS#11 using a USB token - currently only available for Java

JAVA

JAVA
package com.itextpdf.samples.signatures.chapter04;

import java.io.FileOutputStream;
import java.util.Collection;
import sun.security.pkcs11.SunPKCS11;
import sun.security.pkcs11.wrapper.CK_C_INITIALIZE_ARGS;
import sun.security.pkcs11.wrapper.CK_TOKEN_INFO;
import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Exception;

import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.signatures.BouncyCastleDigest;
import com.itextpdf.signatures.CertificateUtil;
import com.itextpdf.signatures.ICrlClient;
import com.itextpdf.signatures.CrlClientOnline;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.IExternalDigest;
import com.itextpdf.signatures.IExternalSignature;
import com.itextpdf.signatures.IOcspClient;
import com.itextpdf.signatures.OcspClientBouncyCastle;
import com.itextpdf.signatures.PdfSignatureAppearance;
import com.itextpdf.signatures.PdfSigner;
import com.itextpdf.signatures.ITSAClient;
import com.itextpdf.signatures.PrivateKeySignature;
import com.itextpdf.signatures.TSAClientBouncyCastle;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class C4_02_SignWithPKCS11USB {
    public static final String DEST = "./target/signatures/chapter04/";

    public static final String SRC = "./src/test/resources/pdfs/hello.pdf";

    public static final String[] RESULT_FILES = new String[] {
            "hello_token.pdf"
    };

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        File file = new File(DEST);
        file.mkdirs();

        Properties properties = new Properties();

        // Specify the correct path to the certificate
        properties.load(new FileInputStream("C:/signkey.properties"));
        char[] pass = properties.getProperty("PASSWORD").toCharArray();

        // Specify the correct path to the CRYPTOKI (PKCS#11) DLL.
        String dllPath = "c:/windows/system32/dkck201.dll";
        long[] slots = getSlotsWithTokens(dllPath);
        if (slots != null) {
            String config = "name=ikey4000\n" +
                    "library=" + dllPath + "\n" +
                    "slotListIndex = " + slots[0];
            ByteArrayInputStream bais = new ByteArrayInputStream(config.getBytes());
            Provider providerPKCS11 = new SunPKCS11(bais);
            Security.addProvider(providerPKCS11);
            BouncyCastleProvider providerBC = new BouncyCastleProvider();
            Security.addProvider(providerBC);

            KeyStore ks = KeyStore.getInstance("PKCS11");
            ks.load(null, pass);
            String alias = ks.aliases().nextElement();
            PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
            Certificate[] chain = ks.getCertificateChain(alias);
            IOcspClient ocspClient = new OcspClientBouncyCastle(null);
            ITSAClient tsaClient = null;
            for (int i = 0; i < chain.length; i++) {
                X509Certificate cert = (X509Certificate) chain[i];
                String tsaUrl = CertificateUtil.getTSAURL(cert);
                if (tsaUrl != null) {
                    tsaClient = new TSAClientBouncyCastle(tsaUrl);
                    break;
                }
            }

            List<ICrlClient> crlList = new ArrayList<ICrlClient>();
            crlList.add(new CrlClientOnline(chain));

            new C4_02_SignWithPKCS11USB().sign(SRC, DEST + RESULT_FILES[0], chain, pk,
                    DigestAlgorithms.SHA256, providerPKCS11.getName(), PdfSigner.CryptoStandard.CMS,
                    "Test", "Ghent", crlList, ocspClient, tsaClient, 0);
        } else {
            System.out.println("An exception was encountered while getting token slot's indexes.");
        }
    }

    public void sign(String src, String dest, Certificate[] chain, PrivateKey pk,
            String digestAlgorithm, String provider, PdfSigner.CryptoStandard subfilter,
            String reason, String location, Collection<ICrlClient> crlList,
            IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize)
            throws GeneralSecurityException, IOException {
        PdfReader reader = new PdfReader(src);
        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), new StampingProperties());

        // Create the signature appearance
        Rectangle rect = new Rectangle(36, 648, 200, 100);
        PdfSignatureAppearance appearance = signer.getSignatureAppearance();
        appearance
                .setReason(reason)
                .setLocation(location)

                // Specify if the appearance before field is signed will be used
                // as a background for the signed field. The "false" value is the default value.
                .setReuseAppearance(false)
                .setPageRect(rect)
                .setPageNumber(1);
        signer.setFieldName("sig");

        IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
        IExternalDigest digest = new BouncyCastleDigest();

        // Sign the document using the detached mode, CMS or CAdES equivalent.
        signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
    }

    // Method returns a list of token slot's indexes
    public static long[] getSlotsWithTokens(String libraryPath) {
        CK_C_INITIALIZE_ARGS initArgs = new CK_C_INITIALIZE_ARGS();
        String functionList = "C_GetFunctionList";

        initArgs.flags = 0;
        PKCS11 tmpPKCS11 = null;
        long[] slotList = null;
        try {
            try {
                tmpPKCS11 = PKCS11.getInstance(libraryPath, functionList, initArgs, false);
            } catch (IOException ex) {
                ex.printStackTrace();
                return null;
            }
        } catch (PKCS11Exception e) {
            try {
                initArgs = null;
                tmpPKCS11 = PKCS11.getInstance(libraryPath, functionList, initArgs, true);
            } catch (IOException | PKCS11Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }

        try {
            slotList = tmpPKCS11.C_GetSlotList(true);

            for (long slot : slotList) {
                CK_TOKEN_INFO tokenInfo = tmpPKCS11.C_GetTokenInfo(slot);
                System.out.println("slot: " + slot + "\nmanufacturerID: "
                        + String.valueOf(tokenInfo.manufacturerID) + "\nmodel: "
                        + String.valueOf(tokenInfo.model));
            }
        } catch (Throwable ex) {
            ex.printStackTrace();
            return null;
        }

        return slotList;
    }
}

c4_03_signwithpkcs11sc

Signing a document with PKCS#11 using a BEID - currently only available for Java

JAVA

JAVA
package com.itextpdf.samples.signatures.chapter04;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Collection;
import sun.security.pkcs11.SunPKCS11;

import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.signatures.BouncyCastleDigest;
import com.itextpdf.signatures.ICrlClient;
import com.itextpdf.signatures.CrlClientOnline;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.IExternalDigest;
import com.itextpdf.signatures.IExternalSignature;
import com.itextpdf.signatures.IOcspClient;
import com.itextpdf.signatures.ITSAClient;
import com.itextpdf.signatures.OcspClientBouncyCastle;
import com.itextpdf.signatures.PdfSignatureAppearance;
import com.itextpdf.signatures.PdfSigner;
import com.itextpdf.signatures.PrivateKeySignature;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.security.pkcs11.wrapper.CK_C_INITIALIZE_ARGS;
import sun.security.pkcs11.wrapper.CK_TOKEN_INFO;
import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Exception;

public class C4_03_SignWithPKCS11SC {
    public static final String DEST = "./target/signatures/chapter04/";

    public static final String SRC = "./src/test/resources/pdfs/hello.pdf";

    public static final String[] RESULT_FILES = new String[] {
            "hello_smartcard_Authentication.pdf",
            "hello_smartcard_Signature.pdf"
    };

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        File file = new File(DEST);
        file.mkdirs();

        // Specify the correct path to the DLL.
        String dllPath = "c:/windows/system32/beidpkcs11.dll";
        long[] slots = getSlotsWithTokens(dllPath);
        if (slots != null) {
            String config = "name=beid\n" +
                    "library=" + dllPath + "\n" +
                    "slotListIndex = " + slots[0];
            ByteArrayInputStream bais = new ByteArrayInputStream(config.getBytes());
            Provider providerPKCS11 = new SunPKCS11(bais);
            Security.addProvider(providerPKCS11);
            BouncyCastleProvider providerBC = new BouncyCastleProvider();
            Security.addProvider(providerBC);
            KeyStore ks = KeyStore.getInstance("PKCS11");

            /* All the signing operations are passed to the BeId API.
             * That is why the stream and password are null.
             */
            ks.load(null, null);
            Enumeration<String> aliases = ks.aliases();
            while (aliases.hasMoreElements()) {
                System.out.println(aliases.nextElement());
            }

            smartCardSign(providerPKCS11.getName(), ks, "Authentication", DEST + RESULT_FILES[0]);
            smartCardSign(providerPKCS11.getName(), ks, "Signature", DEST + RESULT_FILES[1]);
        } else {
            System.out.println("An exception was encountered while getting token slot's indexes.");
        }
    }

    public static void smartCardSign(String provider, KeyStore ks, String alias, String dest)
            throws GeneralSecurityException, IOException {
        PrivateKey pk = (PrivateKey) ks.getKey(alias, null);
        Certificate[] chain = ks.getCertificateChain(alias);
        IOcspClient ocspClient = new OcspClientBouncyCastle(null);
        List<ICrlClient> crlList = new ArrayList<ICrlClient>();
        crlList.add(new CrlClientOnline(chain));

        new C4_03_SignWithPKCS11SC().sign(SRC, dest, chain, pk, DigestAlgorithms.SHA256, provider,
                PdfSigner.CryptoStandard.CMS, "Test", "Ghent",
                crlList, ocspClient, null, 0);
    }

    public void sign(String src, String dest, Certificate[] chain, PrivateKey pk,
            String digestAlgorithm, String provider, PdfSigner.CryptoStandard subfilter,
            String reason, String location, Collection<ICrlClient> crlList,
            IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize)
            throws GeneralSecurityException, IOException {
        PdfReader reader = new PdfReader(src);
        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), new StampingProperties());

        // Create the signature appearance
        Rectangle rect = new Rectangle(36, 648, 200, 100);
        PdfSignatureAppearance appearance = signer.getSignatureAppearance();
        appearance
                .setReason(reason)
                .setLocation(location)

                // Specify if the appearance before field is signed will be used
                // as a background for the signed field. The "false" value is the default value.
                .setReuseAppearance(false)
                .setPageRect(rect)
                .setPageNumber(1);
        signer.setFieldName("sig");

        IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
        IExternalDigest digest = new BouncyCastleDigest();

        // Sign the document using the detached mode, CMS or CAdES equivalent.
        signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
    }

    // Method returns a list of token slot's indexes
    public static long[] getSlotsWithTokens(String libraryPath) {
        CK_C_INITIALIZE_ARGS initArgs = new CK_C_INITIALIZE_ARGS();
        String functionList = "C_GetFunctionList";

        initArgs.flags = 0;
        PKCS11 tmpPKCS11 = null;
        long[] slotList = null;
        try {
            try {
                tmpPKCS11 = PKCS11.getInstance(libraryPath, functionList, initArgs, false);
            } catch (IOException ex) {
                ex.printStackTrace();
                return null;
            }
        } catch (PKCS11Exception e) {
            try {
                initArgs = null;
                tmpPKCS11 = PKCS11.getInstance(libraryPath, functionList, initArgs, true);
            } catch (IOException | PKCS11Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }

        try {
            slotList = tmpPKCS11.C_GetSlotList(true);

            for (long slot : slotList) {
                CK_TOKEN_INFO tokenInfo = tmpPKCS11.C_GetTokenInfo(slot);
                System.out.println("slot: " + slot + "\nmanufacturerID: "
                        + String.valueOf(tokenInfo.manufacturerID) + "\nmodel: "
                        + String.valueOf(tokenInfo.model));
            }
        } catch (Throwable ex) {
            ex.printStackTrace();
            return null;
        }

        return slotList;
    }
}


c4_07_clientserversigning

Signing a document on the client using a signature created on the server:

JAVA

JAVA
package com.itextpdf.samples.signatures.chapter04;

import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.signatures.*;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;

public class C4_07_ClientServerSigning {
    public static final String DEST = "./target/signatures/chapter04/";

    public static final String SRC = "./src/test/resources/pdfs/hello.pdf";
    public static final String CERT = "https://demo.itextsupport.com/SigningApp/itextpdf.cer";

    public static final String[] RESULT_FILES = new String[] {
            "hello_server.pdf"
    };

    public static void main(String[] args) throws GeneralSecurityException, IOException {
        File file = new File(DEST);
        file.mkdirs();

        CertificateFactory factory = CertificateFactory.getInstance("X.509");
        URL certUrl = new URL(CERT);
        Certificate[] chain = new Certificate[1];
        try (InputStream stream = certUrl.openStream()) {
            chain[0] = factory.generateCertificate(stream);
        }

        new C4_07_ClientServerSigning().sign(SRC, DEST + RESULT_FILES[0], chain, PdfSigner.CryptoStandard.CMS,
                "Test", "Ghent");
    }

    public void sign(String src, String dest, Certificate[] chain, PdfSigner.CryptoStandard subfilter,
            String reason, String location) throws GeneralSecurityException, IOException {
        PdfReader reader = new PdfReader(src);
        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), new StampingProperties());

        // Create the signature appearance
        Rectangle rect = new Rectangle(36, 648, 200, 100);
        PdfSignatureAppearance appearance = signer.getSignatureAppearance();
        appearance
                .setReason(reason)
                .setLocation(location)
                .setPageRect(rect)
                .setPageNumber(1);
        signer.setFieldName("sig");

        IExternalDigest digest = new BouncyCastleDigest();
        IExternalSignature signature = new ServerSignature();

        // Sign the document using the detached mode, CMS or CAdES equivalent.
        signer.signDetached(digest, signature, chain, null, null, null,
                0, subfilter);
    }

    public class ServerSignature implements IExternalSignature {
        public static final String SIGN = "http://demo.itextsupport.com/SigningApp/signbytes";

        public String getDigestAlgorithmName() {
            return DigestAlgorithms.SHA256;
        }

        public String getSignatureAlgorithmName() {
            return "RSA";
        }

        @Override
        public ISignatureMechanismParams getSignatureMechanismParameters() {
            return null;
        }

        public byte[] sign(byte[] message) throws GeneralSecurityException {
            try {
                URL url = new URL(SIGN);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setDoOutput(true);
                conn.setRequestMethod("POST");
                conn.connect();

                OutputStream os = conn.getOutputStream();
                os.write(message);
                os.flush();
                os.close();

                InputStream is = conn.getInputStream();
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] b = new byte[32];
                int read;
                while ((read = is.read(b)) != -1) {
                    baos.write(b, 0, read);
                }

                is.close();

                return baos.toByteArray();
            } catch (IOException e) {
                throw new PdfException(e);
            }
        }
    }
}

C#

C#
using System;
using System.IO;
using System.Net;
using iText.Bouncycastle.Cert;
using iText.Bouncycastle.X509;
using iText.Commons.Bouncycastle.Cert;
using iText.Kernel;
using iText.Kernel.Exceptions;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Signatures;
using Org.BouncyCastle.X509;

namespace iText.Samples.Signatures.Chapter04
{
    public class C4_07_ClientServerSigning
    {
        public static readonly string DEST = "results/signatures/chapter04/";

        public static readonly string SRC = "../../../resources/pdfs/hello.pdf";
        public static readonly string CERT = "https://demo.itextsupport.com/SigningApp/itextpdf.cer";

        public static readonly String[] RESULT_FILES =
        {
            "hello_server.pdf"
        };

        public static void Main(String[] args)
        {
            DirectoryInfo directory = new DirectoryInfo(DEST);
            directory.Create();

            Uri certUrl = new Uri(CERT);
            HttpWebRequest request = (HttpWebRequest) WebRequest.Create(certUrl);
            request.Method = WebRequestMethods.Http.Get;
            HttpWebResponse response = (HttpWebResponse) request.GetResponse();
            X509CertificateParser parser = new X509CertificateParser();
            X509Certificate[] chain = new X509Certificate[1];
            using (Stream stream = response.GetResponseStream())
            {
                chain[0] = parser.ReadCertificate(stream);
            }

            new C4_07_ClientServerSigning().Sign(SRC, DEST + RESULT_FILES[0], chain, PdfSigner.CryptoStandard.CMS,
                "Test", "Ghent");
        }

        public void Sign(String src, String dest, X509Certificate[] chain, PdfSigner.CryptoStandard subfilter,
            String reason, String location)
        {
            PdfReader reader = new PdfReader(src);
            PdfSigner signer = new PdfSigner(reader, new FileStream(dest, FileMode.Create), new StampingProperties());

            // Create the signature appearance
            Rectangle rect = new Rectangle(36, 648, 200, 100);
            PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
            appearance
                .SetReason(reason)
                .SetLocation(location)
                .SetPageRect(rect)
                .SetPageNumber(1);
            signer.SetFieldName("sig");

            IExternalSignature pks = new ServerSignature();

            IX509Certificate[] certificateWrappers = new IX509Certificate[chain.Length];
            for (int i = 0; i < certificateWrappers.Length; ++i) {
                certificateWrappers[i] = new X509CertificateBC(chain[i]);
            }
            // Sign the document using the detached mode, CMS or CAdES equivalent.
            signer.SignDetached(pks, certificateWrappers, null, null, null, 0, subfilter);
        }

        public class ServerSignature : IExternalSignature
        {
            public static readonly String SIGN = "http://demo.itextsupport.com/SigningApp/signbytes";

            public String GetDigestAlgorithmName()
            {
                return DigestAlgorithms.SHA256;
            }

            public String GetSignatureAlgorithmName()
            {
                return "RSA";
            }

            public ISignatureMechanismParams GetSignatureMechanismParameters()
            {
                return null;
            }

            public byte[] Sign(byte[] message)
            {
                try
                {
                    Uri url = new Uri(SIGN);
                    var httpWebRequest = (HttpWebRequest) WebRequest.Create(url);
                    httpWebRequest.Method = "POST";

                    using (Stream stream = httpWebRequest.GetRequestStream())
                    {
                        stream.Write(message, 0, message.Length);
                    }

                    var httpResponse = (HttpWebResponse) httpWebRequest.GetResponse();
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        Stream stream = httpResponse.GetResponseStream();
                        stream.CopyTo(memoryStream);
                        stream.Close();
                        
                        return memoryStream.ToArray();
                    }
                }
                catch (IOException e)
                {
                    throw new PdfException(e);
                }
            }
        }
    }
}

c4_08_serverclientsigning

Signing a document on the server using a signature created on the client:

JAVA

JAVA
package com.itextpdf.samples.signatures.chapter04;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;

import java.util.List;

public class C4_08_ServerClientSigning {
    public static final String DEST = "./target/signatures/chapter04/";

    public static final String KEYSTORE = "./src/test/resources/encryption/ks";
    public static final String CERT = "./src/test/resources/encryption/bruno.crt";
    public static final String PRE = "http://demo.itextsupport.com/SigningApp/presign";
    public static final String POST = "http://demo.itextsupport.com/SigningApp/postsign";

    public static final String[] RESULT_FILES = new String[] {
            "hello_server2.pdf"
    };

    public static final char[] PASSWORD = "password".toCharArray();

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        File file = new File(DEST);
        file.mkdirs();

        // Make a connection to a PreSign servlet to ask to create a document,
        // then calculate its hash and send it to us
        URL url = new URL(PRE);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setRequestMethod("POST");
        conn.connect();

        // Upload your self-signed certificate
        OutputStream os = conn.getOutputStream();
        FileInputStream fis = new FileInputStream(CERT);
        int read;
        byte[] data = new byte[256];
        while ((read = fis.read(data, 0, data.length)) != -1) {
            os.write(data, 0, read);
        }

        os.flush();
        os.close();

        // Use cookies to maintain a session
        List<String> cookies = conn.getHeaderFields().get("Set-Cookie");

        // Receive a hash that needs to be signed
        InputStream is = conn.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        data = new byte[256];
        while ((read = is.read(data)) != -1) {
            baos.write(data, 0, read);
        }

        is.close();
        byte[] hash = baos.toByteArray();

        // Load your private key from the key store
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(new FileInputStream(KEYSTORE), PASSWORD);
        String alias = ks.aliases().nextElement();
        PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);

        // Sign the hash received from the server
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initSign(pk);
        sig.update(hash);
        data = sig.sign();

        // Make a connection to the PostSign Servlet to send the signed bytes to the server.
        url = new URL(POST);
        conn = (HttpURLConnection) url.openConnection();
        for (String cookie : cookies) {
            conn.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
        }

        conn.setDoOutput(true);
        conn.setRequestMethod("POST");
        conn.connect();

        //Upload the signed bytes
        os = conn.getOutputStream();
        os.write(data);
        os.flush();
        os.close();

        // Receive the signed document
        is = conn.getInputStream();
        FileOutputStream fos = new FileOutputStream(DEST + RESULT_FILES[0]);
        data = new byte[256];
        while ((read = is.read(data)) != -1) {
            fos.write(data, 0, read);
        }

        is.close();
        fos.flush();
        fos.close();
    }
}

C#

C#
using System;
using System.IO;
using System.Net;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;

namespace iText.Samples.Signatures.Chapter04
{
    public class C4_08_ServerClientSigning
    {
        public static readonly String DEST = "results/signatures/chapter04/";

        public static readonly String KEYSTORE = "../../../resources/encryption/ks";
        public static readonly String CERT = "../../../resources/encryption/bruno.crt";
        public static readonly String PRE = "http://demo.itextsupport.com/SigningApp/presign";
        public static readonly String POST = "http://demo.itextsupport.com/SigningApp/postsign";

        public static readonly String[] RESULT_FILES = new String[]
        {
            "hello_server2.pdf"
        };

        public static readonly char[] PASSWORD = "password".ToCharArray();

        public static void Main(String[] args)
        {
            DirectoryInfo directory = new DirectoryInfo(DEST);
            directory.Create();

            // Make a connection to a PreSign servlet to ask to create a document,
            // then calculate its hash and send it to us
            HttpWebRequest request = (HttpWebRequest) WebRequest.Create(PRE);
            request.Method = "POST";

            // Upload our self-signed certificate
            Stream os = request.GetRequestStream();
            byte[] data = new byte[256];
            int read;
            using (FileStream fis = new FileStream(CERT, FileMode.Open))
            {
                while ((read = fis.Read(data, 0, data.Length)) != 0)
                {
                    os.Write(data, 0, read);
                }
            }

            os.Flush();
            os.Close();

            HttpWebResponse response = (HttpWebResponse) request.GetResponse();

            // Use cookies to maintain a session
            String cookies = response.Headers["Set-Cookie"];

            // Receive a hash that needs to be signed
            Stream istream = response.GetResponseStream();
            MemoryStream memoryStream = new MemoryStream();
            istream.CopyTo(memoryStream);
            istream.Close();
            byte[] hash = memoryStream.ToArray();

            // Load our private key from the key store
            Pkcs12Store store = new Pkcs12StoreBuilder().Build();
            store.Load(new FileStream(KEYSTORE, FileMode.Open, FileAccess.Read), PASSWORD);

            // Searching for private key
            String alias = null;
            foreach (string al in store.Aliases)
            {
                if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
                {
                    alias = al;
                    break;
                }
            }

            AsymmetricKeyEntry pk = store.GetKey(alias);

            // Sign the hash received from the server
            ISigner sig = SignerUtilities.GetSigner("SHA256withRSA");
            sig.Init(true, pk.Key);
            sig.BlockUpdate(hash, 0, hash.Length);
            data = sig.GenerateSignature();

            // Make a connection to the PostSign Servlet
            request = (HttpWebRequest) WebRequest.Create(POST);
            request.Headers.Add(HttpRequestHeader.Cookie, cookies.Split(";".ToCharArray(), 2)[0]);
            request.Method = "POST";

            // Upload the signed bytes
            os = request.GetRequestStream();
            os.Write(data, 0, data.Length);
            os.Flush();
            os.Close();

            // Receive the signed document
            response = (HttpWebResponse) request.GetResponse();
            istream = response.GetResponseStream();
            using (FileStream fos = new FileStream(DEST + RESULT_FILES[0], FileMode.Create))
            {
                data = new byte[256];
                while ((read = istream.Read(data, 0, data.Length)) != 0)
                {
                    fos.Write(data, 0, read);
                }

                istream.Close();
            }
        }
    }
}

c4_09_deferredsigning

Signing a document by creating a blank signature container, creating a signature appearance on the server and getting a hash to send to the client (deferred signing):

JAVA

JAVA
package com.itextpdf.samples.signatures.chapter04;

import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;

import com.itextpdf.signatures.BouncyCastleDigest;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.ExternalBlankSignatureContainer;
import com.itextpdf.signatures.IExternalSignatureContainer;
import com.itextpdf.signatures.PdfPKCS7;
import com.itextpdf.signatures.PdfSignatureAppearance;
import com.itextpdf.signatures.PdfSigner;
import com.itextpdf.signatures.PrivateKeySignature;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class C4_09_DeferredSigning {
    public static final String DEST = "./target/signatures/chapter04/";

    public static final String SRC = "./src/test/resources/pdfs/hello.pdf";
    public static final String TEMP = "./target/signatures/chapter04/hello_empty_sig.pdf";
    public static final String KEYSTORE = "./src/test/resources/encryption/ks";

    public static final char[] PASSWORD = "password".toCharArray();

    public static final String[] RESULT_FILES = new String[] {
            "hello_sig_ok.pdf"
    };

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        File file = new File(DEST);
        file.mkdirs();

        BouncyCastleProvider providerBC = new BouncyCastleProvider();
        Security.addProvider(providerBC);

        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(new FileInputStream(KEYSTORE), PASSWORD);
        String alias = ks.aliases().nextElement();
        Certificate[] chain = ks.getCertificateChain(alias);
        PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);

        C4_09_DeferredSigning app = new C4_09_DeferredSigning();
        app.emptySignature(SRC, TEMP, "sig", chain);
        app.createSignature(TEMP, DEST + RESULT_FILES[0], "sig", pk, chain);
    }

    public void emptySignature(String src, String dest, String fieldname, Certificate[] chain)
            throws IOException, GeneralSecurityException {
        PdfReader reader = new PdfReader(src);
        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), new StampingProperties());
        PdfSignatureAppearance appearance = signer.getSignatureAppearance();
        appearance
                .setPageRect(new Rectangle(36, 748, 200, 100))
                .setPageNumber(1)
                .setCertificate(chain[0]);
        signer.setFieldName(fieldname);

        /* ExternalBlankSignatureContainer constructor will create the PdfDictionary for the signature
         * information and will insert the /Filter and /SubFilter values into this dictionary.
         * It will leave just a blank placeholder for the signature that is to be inserted later.
         */
        IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.Adobe_PPKLite,
                PdfName.Adbe_pkcs7_detached);

        // Sign the document using an external container.
        // 8192 is the size of the empty signature placeholder.
        signer.signExternalContainer(external, 8192);
    }

    public void createSignature(String src, String dest, String fieldName, PrivateKey pk, Certificate[] chain)
            throws IOException, GeneralSecurityException {
        PdfReader reader = new PdfReader(src);
        try(FileOutputStream os = new FileOutputStream(dest)) {
            PdfSigner signer = new PdfSigner(reader, os, new StampingProperties());

            IExternalSignatureContainer external = new MyExternalSignatureContainer(pk, chain);

            // Signs a PDF where space was already reserved. The field must cover the whole document.
            signer.signDeferred(signer.getDocument(), fieldName, os, external);
        }
    }

    class MyExternalSignatureContainer implements IExternalSignatureContainer {

        protected PrivateKey pk;
        protected Certificate[] chain;

        public MyExternalSignatureContainer(PrivateKey pk, Certificate[] chain) {
            this.pk = pk;
            this.chain = chain;
        }

        public byte[] sign(InputStream is) throws GeneralSecurityException {
            try {
                PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256", "BC");
                String digestAlgorithm = signature.getDigestAlgorithmName();
                BouncyCastleDigest digest = new BouncyCastleDigest();

                PdfPKCS7 sgn = new PdfPKCS7(null, chain, digestAlgorithm, null, digest, false);
                byte hash[] = DigestAlgorithms.digest(is, digest.getMessageDigest(digestAlgorithm));
                byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
                byte[] extSignature = signature.sign(sh);
                sgn.setExternalSignatureValue(extSignature, null, signature.getSignatureAlgorithmName());

                return sgn.getEncodedPKCS7(hash, PdfSigner.CryptoStandard.CMS, null, null, null);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
        }

        public void modifySigningDictionary(PdfDictionary signDic) {
        }
    }
}

C#

C#
using System;
using System.IO;
using iText.Bouncycastle.Cert;
using iText.Bouncycastle.X509;
using iText.Bouncycastle.Crypto;
using iText.Commons.Bouncycastle.Cert;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Signatures;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;

namespace iText.Samples.Signatures.Chapter04
{
    public class C4_09_DeferredSigning
    {
        public static readonly String DEST = "results/signatures/chapter04/";

        public static readonly String SRC = "../../../resources/pdfs/hello.pdf";
        public static readonly String TEMP = "results/signatures/chapter04/hello_empty_sig.pdf";
        public static readonly String KEYSTORE = "../../../resources/encryption/ks";

        public static readonly char[] PASSWORD = "password".ToCharArray();

        public static readonly String[] RESULT_FILES = new String[]
        {
            "hello_sig_ok.pdf"
        };

        public static void Main(String[] args)
        {
            DirectoryInfo directory = new DirectoryInfo(DEST);
            directory.Create();
            Pkcs12Store pk12 = new Pkcs12StoreBuilder().Build();
            pk12.Load(new FileStream(KEYSTORE, FileMode.Open, FileAccess.Read), PASSWORD);
            string alias = null;
            foreach (var a in pk12.Aliases)
            {
                alias = ((string) a);
                if (pk12.IsKeyEntry(alias))
                    break;
            }

            ICipherParameters pk = pk12.GetKey(alias).Key;
            X509CertificateEntry[] ce = pk12.GetCertificateChain(alias);
            X509Certificate[] chain = new X509Certificate[ce.Length];
            for (int k = 0; k < ce.Length; ++k)
            {
                chain[k] = ce[k].Certificate;
            }

            C4_09_DeferredSigning app = new C4_09_DeferredSigning();
            app.EmptySignature(SRC, TEMP, "sig", chain);
            app.CreateSignature(TEMP, DEST + RESULT_FILES[0], "sig", pk, chain);
        }

        public void EmptySignature(String src, String dest, String fieldname, X509Certificate[] chain)
        {
            PdfReader reader = new PdfReader(src);
            PdfSigner signer = new PdfSigner(reader, new FileStream(dest, FileMode.Create), new StampingProperties());

            PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
            appearance
                .SetPageRect(new Rectangle(36, 748, 200, 100))
                .SetPageNumber(1)
                .SetCertificate(new X509CertificateBC(chain[0]));
            signer.SetFieldName(fieldname);

            /* ExternalBlankSignatureContainer constructor will create the PdfDictionary for the signature
             * information and will insert the /Filter and /SubFilter values into this dictionary.
             * It will leave just a blank placeholder for the signature that is to be inserted later.
             */
            IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.Adobe_PPKLite,
                PdfName.Adbe_pkcs7_detached);

            // Sign the document using an external container
            // 8192 is the size of the empty signature placeholder.
            signer.SignExternalContainer(external, 8192);
        }

        public void CreateSignature(String src, String dest, String fieldName,
            ICipherParameters pk, X509Certificate[] chain)
        {
            PdfReader reader = new PdfReader(src);
            using (FileStream os = new FileStream(dest, FileMode.Create))
            {
                PdfSigner signer = new PdfSigner(reader, os, new StampingProperties());

                IExternalSignatureContainer external = new MyExternalSignatureContainer(pk, chain);

                // Signs a PDF where space was already reserved. The field must cover the whole document.
                PdfSigner.SignDeferred(signer.GetDocument(), fieldName, os, external);
            }
        }

        class MyExternalSignatureContainer : IExternalSignatureContainer
        {
            protected ICipherParameters pk;
            protected X509Certificate[] chain;

            public MyExternalSignatureContainer(ICipherParameters pk, X509Certificate[] chain)
            {
                this.pk = pk;
                this.chain = chain;
            }

            public byte[] Sign(Stream inputStream)
            {
                try
                {
                    PrivateKeySignature signature = new PrivateKeySignature(new PrivateKeyBC(pk), "SHA256");
                    String digestAlgorithmName = signature.GetDigestAlgorithmName();

                    IX509Certificate[] certificateWrappers = new IX509Certificate[chain.Length];
                    for (int i = 0; i < certificateWrappers.Length; ++i) {
                        certificateWrappers[i] = new X509CertificateBC(chain[i]);
                    }
                    PdfPKCS7 sgn = new PdfPKCS7(null, certificateWrappers, digestAlgorithmName, false);
                    byte[] hash = DigestAlgorithms.Digest(inputStream, digestAlgorithmName);
                    byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS,
                        null, null);
                    byte[] extSignature = signature.Sign(sh);
                    sgn.SetExternalSignatureValue(extSignature, null, signature.GetSignatureAlgorithmName());

                    return sgn.GetEncodedPKCS7(hash, PdfSigner.CryptoStandard.CMS, null,
                        null, null);
                }
                catch (IOException ioe)
                {
                    throw new Exception(ioe.Message);
                }
            }

            public void ModifySigningDictionary(PdfDictionary signDic)
            {
            }
        }
    }
}

JavaScript errors detected

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

If this problem persists, please contact our support.