Developer Tools
|
Office Productivity Applications
|
Platform-Agnostic APIs
|
Home | Online Demos | Downloads | Buy Now | Support | About Us | News | Working Together | Contact Us
In PDFOne (for Java) Version 5.5, we introduced support for digital signing using USB token. In this article, you will see how to sign PDF documents using the certificate installed in the USB token.
A USB token is a password-protected physical device used to store digital signature certificates. To sign PDF documents using an USB Token, you need a digital signature certificate that is installed on an USB token. USB token based certificates are an implementation of PKCS#11, one of the Public-Key Cryptography Standards. Digital signature certificates are issued by a Certificate Authority (CA). To access the connected USB token using Java you need the device driver library path of the token. For instance, when SafeNet USB token device is installed on Windows, the USB token’s library file "eTPKCS11.dll" is installed at the directory "C:\WINDOWS\system32\". In order to load the certificates installed on the USB token into the Java KeyStore object, you need a valid password/PIN to access the USB token.
You can load an existing document and sign straightaway using PdfDocument.addSignature()
method. PDFOne will take care of adding the signature field. The code example below shows you how to use PdfDocument.addSignature()
method.
import java.io.IOException; import javax.security.auth.callback.CallbackHandler; import com.gnostice.pdfone.PdfDocument; import com.gnostice.pdfone.PdfException; import com.gnostice.pdfone.PdfRect; public class AddSignatureUsingUSBToken { public static void main(String[] args) throws IOException, PdfException { String inputFileName = "InputDocument.pdf"; String outputFileName = "OutputDocument.pdf"; // Load an existing PDF document PdfDocument pdfDoc = new PdfDocument(); pdfDoc.load(inputFileName); /* * path of the library that has the PKCS11 implementation of * the USB token. */ String pathToPKCS11ImplLibOfUSBToken = "libFileName"; // PIN/password to access the certificates in USB token. String passwordToAccessKeyStore = "password"; /* * specify callback handler if you do not want to supply the * password directly to Gnostice API. */ CallbackHandler callbackHandlerToGetPassword = null; /* * when the token is not available in the default slot then * attempts are made from slot number '0' to this * maxSlotNumber to access the token. If the value specified * for maxSlotNumber is '-1', then only the default slot will * be attempted with, to access the token. */ int maxSlotNumber = -1; /* * Supply the parameters required to access the USB token, and * other details such as Reason, Location, and ContactInfo. */ pdfDoc.addSignature( pathToPKCS11ImplLibOfUSBToken, passwordToAccessKeyStore, callbackHandlerToGetPassword, maxSlotNumber, "John Doe", // signer's name "Approving the document", // reason "Bangalore, India", // location "johndoe@email.com", // contact info 1, // page number "sigField1", // field name new PdfRect(10, 20, 90, 40)); // location on page pdfDoc.save(outputFileName); pdfDoc.close(); } }
The following code snippets demonstrate various digital signing scenarios.
Scenario 1: I want to load an existing document and sign it. Then, when I view the output document in consumer applications such as Adobe Reader, I should see the validation result such as check mark over the signature field. NOTE: Usage of this feature is a violation of PAdES standard.
String inputFileName = "InputDocument.pdf"; String outputFileName = "OutputDocument.pdf"; // Load an existing PDF document PdfDocument pdfDoc = new PdfDocument(); pdfDoc.load(inputFileName); /* * Supply the parameters required to access the USB token, and * other details such as Reason, Location, and ContactInfo. */ PdfSignature pdfSignature = new PdfSignature( "libraryPath", // path to PKCS11 implementation library "password", // password to access KeyStore null, // issuer Common name null, // certificate Serial number null, // callbackHandler to get password -1, // maxSlotNumber "John Doe", // signer's name "Approving the document", // reason "Bangalore, India", // location "johndoe@email.com", // contact info 1); // page number PdfFormSignatureField signatureField = new PdfFormSignatureField( new PdfRect(10, 10, 90, 40)); signatureField.setName("sigfld"); signatureField.fill(pdfSignature); // specify whether validation result appearance should be included signatureField.setIncludeValidationResultAppearance(true); /* * specify if validation result appearance text should not be * shown when IncludeValidationResultAppearance is true. */ //signatureField.setIncludeValidationResultAppearanceText(false); pdfDoc.addFormField(signatureField, 1); pdfDoc.save(outputFileName); pdfDoc.close();
Scenario 2: I do not want to supply the PIN to access the USB token to the PdfDocument.addSignature()
method. Instead, I want to use javax.security.auth.callback.CallbackHandler
to supply the PIN.
String inputFileName = "InputDocument.pdf"; String outputFileName = "OutputDocument.pdf"; // Load an existing PDF document PdfDocument pdfDoc = new PdfDocument(); pdfDoc.load(inputFileName); /* * specify callback handler if you do not want to supply the * password directly to Gnostice API. */ CallbackHandler callbackHandlerToGetPassword = new CallbackHandler() { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { Callback callback = (Callback) callbacks[i]; if (callback instanceof PasswordCallback) { PasswordCallback passwordCallback = (PasswordCallback) callback; // security provider shows a password prompt to get a password // passwordCallback.getPrompt(); // directly supply the password passwordCallback.setPassword("password".toCharArray()); } } } }; /* * Supply the parameters required to access the USB token, and * other details such as Reason, Location, and ContactInfo. */ pdfDoc.addSignature("librayPath", // path to PKCS11 implementation library null, // password to access KeyStore callbackHandlerToGetPassword, -1, // maxSlotNumber "John Doe", // signer's name "Approving the document", // reason "Bangalore, India", // location "johndoe@email.com", // contact info 1, // page number "sigField1", // field name new PdfRect(10, 20, 100, 50)); // location on page pdfDoc.save(outputFileName); pdfDoc.close();
Scenario 3: I want to load a document and fill an existing blank signature form field. Also, I want to specify that the signature is detached (i.e., the original signed message digest over the document’s byte range shall be incorporated as the normal CMS SignedData field).
String inputFileName = "InputDocument.pdf"; String outputFileName = "OutputDocument.pdf"; // Load an existing PDF document PdfDocument pdfDoc = new PdfDocument(); pdfDoc.load(inputFileName); /* * Supply the parameters required to access the USB token, and * other details such as Reason, Location, and ContactInfo. */ PdfSignature pdfSignature = new PdfSignature( "libraryPath", // path to PKCS11 implementation library "password", // password to access KeyStore null, // issuerCommonName null, // certificateSerialNumber null, // callbackHandler to get password -1, // maxSlotNumber "John Doe", // signer's name "Approving the document", // reason "Bangalore, India", // location "johndoe@email.com", // contact info 1); // page number // specify whether the signature is detached pdfSignature.setDetached(true); // Retrieve a list of all signature fields List fields = pdfDoc.getAllFormFieldsOnPage(0, PdfFormField.TYPE_SIGNATURE); PdfFormSignatureField signatureField; // Iterate the list and fill the required signature field for (int i = 0; i < fields.size(); i++) { signatureField = (PdfFormSignatureField) fields.get(i); if (signatureField.isUnsigned()) { signatureField.fill(pdfSignature); break; } } pdfDoc.save(outputFileName); pdfDoc.close();
Scenario 4: According to our security policies we can not supply the USB token credentials to PDFOne API. Instead I want to connect to the device and get the required certificate at my end and supply required certificate object to PDFOne API to sign the document.
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStoreException; 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.Enumeration; import com.gnostice.pdfone.PdfDocument; import com.gnostice.pdfone.PdfException; import com.gnostice.pdfone.PdfRect; import com.gnostice.pdfone.PdfSignature; public class AddSignatureUsingUSBToken_RetrieveCertAtUserEnd { public static void main(String[] args) throws IOException, PdfException { // Start of block - User connects to the USB token at his end and reads the certificate Certificate[] certificateChain = null; PrivateKey privateKey = null; try { // Specify the configuration to access the USB token. String tokenName = "eToken"; String pathToPKCS11ImplLibOfUSBToken = "libraryPath"; String passwordToAccessKeyStore = "password"; boolean useFirstCertificateFromToken = true; String issuerCommonName = "MyName"; String certificateSerialNumber = "-398666017398154847914207"; String pkcs11Config = "name=" + tokenName + System.getProperty("line.separator") + "library=" + pathToPKCS11ImplLibOfUSBToken; byte[] pkcs11ConfigBytes = pkcs11Config.getBytes(); InputStream pkcs11ConfigStream = new ByteArrayInputStream(pkcs11ConfigBytes); // Create a Provider for accessing the USB token by supplying the configuration. Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(pkcs11ConfigStream); Security.addProvider(pkcs11Provider); // The USB device requires a PIN to access the certficates in the device. char[] pin = passwordToAccessKeyStore.toCharArray(); // Create the Keystore for accessing certificates in the USB device by supplying the PIN. KeyStore smartCardKeyStore = KeyStore.getInstance("PKCS11"); smartCardKeyStore.load(null, pin); // System.out.println("Keystore size: " + smartCardKeyStore.size()); // Enumerate the certificates in the keystore Enumeration aliasesEnum = smartCardKeyStore.aliases(); if (aliasesEnum.hasMoreElements()) { while(aliasesEnum.hasMoreElements()) { // choose the required certificate using the alias String alias = (String) aliasesEnum.nextElement(); X509Certificate cert = (X509Certificate) smartCardKeyStore .getCertificate(alias); // System.out.println(cert); // System.out.println("Serial Number: " + cert.getSerialNumber()); // System.out.println("IssuerDN: " + cert.getIssuerDN()); // Always read first certificate if (useFirstCertificateFromToken) { certificateChain = smartCardKeyStore.getCertificateChain(alias); privateKey = (PrivateKey) smartCardKeyStore.getKey(alias, null); break; } else { // Look for the matching certificate serial number and issuer common name if (cert.getIssuerDN().getName().indexOf("CN=" + issuerCommonName) != -1 && cert.getSerialNumber().toString().equals(certificateSerialNumber)) { // Get the certificate chain of the required certificate and get its private key. certificateChain = smartCardKeyStore.getCertificateChain(alias); privateKey = (PrivateKey) smartCardKeyStore.getKey(alias, null); break; } } } } else { throw new KeyStoreException("Keystore is empty"); } } catch(Exception e) { e.printStackTrace(); } if (certificateChain == null || privateKey == null) { return; } // End of block - User connects to the USB token at his end and reads the certificate // Load the document to be signed. PdfDocument pdfDoc = new PdfDocument(); pdfDoc.load("InputDocument.pdf"); /* * Supply Certificate[] array and PrivateKey objects retrieved * from the KeyStrore of USB token. */ PdfSignature pdfSignature = new PdfSignature( certificateChain, privateKey, "John Doe", // signer's name "Approving the document", // reason "Bangalore, India", // location "johndoe@email.com", // contact info 1); // page number pdfSignature.setFieldName("sigfld"); pdfSignature.setFieldRect(new PdfRect(10, 10, 100, 50)); // add the signature to the document pdfDoc.addSignature(pdfSignature); pdfDoc.save("OutputDocument.pdf"); pdfDoc.close(); } }
Scenario 5: I have connected more than one USB token and sometimes a token has more than one certificate installed in it. I want to sign the document using a specific certificate in the USB token. You can use the code snippet demonstrated in Scenario 4 to iterate the certificates of a token at your end and identify the issuer name and certificate serial number of the certificate you want to use.
String inputFileName = "InputDocument.pdf"; String outputFileName = "OutputDocument.pdf"; // Load an existing PDF document PdfDocument pdfDoc = new PdfDocument(); pdfDoc.load(inputFileName); /* * when the token is not available in the default slot then * attempts are made from slot number '0' to this * maxSlotNumber to access the token. If the value specified * for maxSlotNumber is '-1', then only the default slot will * be attempted with, to access the token. */ int maxSlotNumber = 10; /* * Supply the parameters required to access the USB token, and * other details such as Reason, Location, and ContactInfo. */ PdfSignature pdfSignature = new PdfSignature( "libraryPath", // path to PKCS11 implementation library "password", // password to access KeyStore "MyName", // issuer Common name "-3c6404190057ff9cfa39", // certificate Serial number null, // callbackHandler to get password maxSlotNumber, // maxSlotNumber "John Doe", // signer's name "Approving the document", // reason "Bangalore, India", // location "johndoe@email.com", // contact info 1); // page number pdfSignature.setFieldName("sigfld"); pdfSignature.setFieldRect(new PdfRect(10, 10, 100, 50)); pdfDoc.addSignature(pdfSignature); pdfDoc.save(outputFileName); pdfDoc.close();
---o0O0o---
Our .NET Developer Tools | |
---|---|
Gnostice Document Studio .NETMulti-format document-processing component suite for .NET developers. |
PDFOne .NETA .NET PDF component suite to create, edit, view, print, reorganize, encrypt, annotate, and bookmark PDF documents in .NET applications. |
Our Delphi/C++Builder developer tools | |
---|---|
Gnostice Document Studio DelphiMulti-format document-processing component suite for Delphi/C++Builder developers, covering both VCL and FireMonkey platforms. |
eDocEngine VCLA Delphi/C++Builder component suite for creating documents in over 20 formats and also export reports from popular Delphi reporting tools. |
PDFtoolkit VCLA Delphi/C++Builder component suite to edit, enhance, view, print, merge, split, encrypt, annotate, and bookmark PDF documents. |
Our Java developer tools | |
---|---|
Gnostice Document Studio JavaMulti-format document-processing component suite for Java developers. |
PDFOne (for Java)A Java PDF component suite to create, edit, view, print, reorganize, encrypt, annotate, bookmark PDF documents in Java applications. |
Our Platform-Agnostic Cloud and On-Premises APIs | |
---|---|
StarDocsCloud-hosted and On-Premises REST-based document-processing and document-viewing APIs |
Privacy | Legal | Feedback | Newsletter | Blog | Resellers | © 2002-2024 Gnostice Information Technologies Private Limited. All rights reserved. |