Last active
August 29, 2015 14:23
-
-
Save JoelGeraci-Datalogics/db57e56cba8a233edd24 to your computer and use it in GitHub Desktop.
Add Seed Values to PDF Digital Signatures
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright Datalogics, Inc. 2015 | |
*/ | |
package pdfjt.cookbook.signatures; | |
import com.adobe.internal.io.ByteReader; | |
import com.adobe.internal.io.ByteWriter; | |
import com.adobe.internal.io.InputStreamByteReader; | |
import com.adobe.pdfjt.core.types.ASName; | |
import com.adobe.pdfjt.pdf.digsig.PDFDocMDPPermissions; | |
import com.adobe.pdfjt.pdf.digsig.PDFFieldAction; | |
import com.adobe.pdfjt.pdf.document.PDFDocument; | |
import com.adobe.pdfjt.pdf.document.PDFOpenOptions; | |
import com.adobe.pdfjt.pdf.document.PDFSaveFullOptions; | |
import com.adobe.pdfjt.pdf.document.PDFVersion; | |
import com.adobe.pdfjt.pdf.graphics.PDFRectangle; | |
import com.adobe.pdfjt.pdf.page.PDFPage; | |
import com.adobe.pdfjt.services.digsig.SigCertificateSeedValue; | |
import com.adobe.pdfjt.services.digsig.SigFieldLock; | |
import com.adobe.pdfjt.services.digsig.SigSeedValue; | |
import com.adobe.pdfjt.services.digsig.SignatureFieldFactory; | |
import com.adobe.pdfjt.services.digsig.SignatureFieldInterface; | |
import java.io.InputStream; | |
import java.net.URL; | |
import java.util.ArrayList; | |
import java.util.List; | |
import pdfjt.util.SampleFileServices; | |
import sun.security.x509.X509CertImpl; | |
/** | |
* This sample adds two unsigned digital signature fields to an existing PDF | |
* file. The first signature field is created using just the default values of | |
* the SignatureFieldFactory class and can be signed using any certificate | |
* including self-generated certificates. The second signature is also created | |
* by the SignatureFieldFactory class but is further modified to have | |
* "Seed Values". When a user attempts to sign a “seeded” field, the | |
* author-specified behaviors are automatically invoked and enforced by Adobe | |
* Acrobat and Reader. In this case the seed values limit the user’s choices | |
* when signing the field only allowing them to sign using a certificate issued | |
* by a particular CA (GlobalSign in this case) and restricting the "Reasons" | |
* for signing to a list of three. | |
* | |
* To modify this sample to support the CA of your choice, replace the | |
* GlobalSign root certificate (Root-R3.cer) with one of your choosing. | |
*/ | |
public class SignatureWithSeedValues | |
{ | |
static final String inputURL = "http://dev.datalogics.com/cookbook/signatures/NoSignature.pdf"; | |
static final String inputCertURL = "http://dev.datalogics.com/cookbook/signatures/Root-R3.cer"; | |
static final String urlForEnrollment = "https://www.globalsign.com/en/pdf-signing/"; | |
static final String outputFile = "cookbook/Signatures/output/SignatureWithSeedValue.pdf"; | |
static final int inches = 72; | |
public static void main(String[] args) throws Exception { | |
try { | |
/* | |
* The document we are going to place the signature fields on | |
* already has graphics for signature lines on it so we read that | |
* document in here so we can add the actual signature fields in | |
* their correct positions. | |
*/ | |
InputStream fis = new URL(inputURL).openStream(); | |
ByteReader byteReader = new InputStreamByteReader(fis); | |
PDFDocument pdfDocument = PDFDocument.newInstance(byteReader, PDFOpenOptions.newInstance()); | |
/* | |
* The SignatureFieldFactory class requires a reference to the page | |
* we want to add the signature to, a rectangle defining the | |
* position of the field and a field name ("anySignature"). | |
*/ | |
PDFPage firstPage = pdfDocument.requireCatalog().getPages().getPage(0); | |
PDFRectangle pdfRectangle = PDFRectangle.newInstance(pdfDocument, 1*inches, 7.5*inches, 4*inches, 8*inches); | |
/* | |
* Now we have enough information to add the signature field with only the default properties. | |
*/ | |
SignatureFieldFactory.createSignatureField(firstPage, pdfRectangle, "anySignature"); | |
/* | |
* Now we add a second signature field named "globalSignOnlySignature" to the page 1.5 inches below the first . | |
*/ | |
pdfRectangle = PDFRectangle.newInstance(pdfDocument, 1*inches, 6*inches, 4*inches, 6.5*inches); | |
SignatureFieldInterface signatureField2 = SignatureFieldFactory.createSignatureField(firstPage, pdfRectangle,"globalSignOnlySignature"); | |
/* | |
* To create the seed value that limits the number of certificate | |
* issuers the signer is permitted to use, we first need to read in | |
* the public root certificate of the CAs in question. Though this | |
* sample uses only one, multiple CAs can be supported so they get | |
* stored in an array of arrays. | |
*/ | |
InputStream certStream = new URL(inputCertURL).openStream(); | |
X509CertImpl certificate = new X509CertImpl(certStream); | |
byte[] encodedCert = certificate.getEncoded(); | |
byte[][] issuers = {encodedCert}; | |
/* | |
* Once we have the array of certificate issuers, we can create the | |
* "certificate" seed value which is just one several seed values. | |
* By setting the flag to 2 we are making the use of a certificate | |
* issued by GlobalSign a requirement rather than a suggestion which | |
* appears as the default in the Acrobat or Reader UI. Setting the | |
* URL will allow Acrobat prompt the user to open a URL that can be | |
* used to enroll for a new credential if a matching credential is | |
* not found. | |
*/ | |
SigCertificateSeedValue sigCertificateSeedValue = new SigCertificateSeedValue(); | |
sigCertificateSeedValue.setIssuers(issuers); | |
sigCertificateSeedValue.setFlags(2); | |
sigCertificateSeedValue.setURL(urlForEnrollment); | |
/* | |
* Now we add the complete certificate seed value to a new SigSeedValue object. | |
*/ | |
SigSeedValue sigSeedValue = new SigSeedValue(); | |
sigSeedValue.setCertificateSeedValue(sigCertificateSeedValue); | |
/* | |
* To limit the "Reasons" for signing to only the list we supply, we | |
* create a list and add those to the SigSeedValue object. These | |
* will automatically appear in the signing dialog in Acrobat and | |
* Reader replacing the defaults. | |
*/ | |
List<String> reasons = new ArrayList<String>(); | |
reasons.add("I have approved this document."); | |
reasons.add("I have reviewed this document approve pending recommended changes."); | |
reasons.add("I have rejected this document."); | |
sigSeedValue.setReasons(reasons); | |
/* | |
* Because we know that Acrobat’s default signature handler supports | |
* all the seed values defined by the PDF standard. We set the | |
* default handler to be a requirement with the following two settings. | |
*/ | |
sigSeedValue.setFilter(ASName.create("Adobe.PPKLite")); | |
sigSeedValue.setFlags(1); | |
/* | |
* Finally we add the seed values to the signature field. | |
*/ | |
signatureField2.setSeedValue(sigSeedValue); | |
ByteWriter outputWriter = SampleFileServices.getRAFByteWriter(outputFile); | |
pdfDocument.save(outputWriter, PDFSaveFullOptions.newInstance(PDFVersion.v1_7)); | |
System.out.println("Done!"); | |
} finally { | |
// | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment