Skip to content

Instantly share code, notes, and snippets.

@JoelGeraci-Datalogics
Last active August 29, 2015 14:23
Show Gist options
  • Save JoelGeraci-Datalogics/db57e56cba8a233edd24 to your computer and use it in GitHub Desktop.
Save JoelGeraci-Datalogics/db57e56cba8a233edd24 to your computer and use it in GitHub Desktop.
Add Seed Values to PDF Digital Signatures
/*
* 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