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
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
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
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
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#
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
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#
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
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#
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)
{
}
}
}
}