pdfbox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From til...@apache.org
Subject svn commit: r1760247 - in /pdfbox/branches/2.0/examples/src: main/java/org/apache/pdfbox/examples/signature/ test/java/org/apache/pdfbox/examples/pdmodel/
Date Sun, 11 Sep 2016 12:23:57 GMT
Author: tilman
Date: Sun Sep 11 12:23:57 2016
New Revision: 1760247

URL: http://svn.apache.org/viewvc?rev=1760247&view=rev
Log:
PDFBOX-3065: add tests for external signing, as done by Petras Petkus

Modified:
    pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
    pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java
    pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
    pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java

Modified: pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java?rev=1760247&r1=1760246&r2=1760247&view=diff
==============================================================================
--- pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
(original)
+++ pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
Sun Sep 11 12:23:57 2016
@@ -50,9 +50,6 @@ import org.apache.pdfbox.pdmodel.interac
 public class CreateSignature extends CreateSignatureBase
 {
 
-    private boolean externalSignature;
-    private ExternalSigningSupport externalSigning;
-
     /**
      * Initialize the signature creator with a keystore and certficate password.
      *
@@ -130,11 +127,12 @@ public class CreateSignature extends Cre
         // the signing date, needed for valid signature
         signature.setSignDate(Calendar.getInstance());
 
-        if (externalSignature)
+        if (isExternalSigning())
         {
             System.out.println("Sign externally...");
             document.addSignature(signature);
-            externalSigning = document.saveIncrementalForExternalSigning(output);
+            ExternalSigningSupport externalSigning =
+                    document.saveIncrementalForExternalSigning(output);
             // invoke external signature service
             byte[] cmsSignature = sign(externalSigning.getContent());
             // set signature bytes received from the service
@@ -194,7 +192,7 @@ public class CreateSignature extends Cre
 
         // sign PDF
         CreateSignature signing = new CreateSignature(keystore, password);
-        signing.setExternalSignature(externalSig);
+        signing.setExternalSigning(externalSig);
 
         File inFile = new File(args[2]);
         String name = inFile.getName();
@@ -212,9 +210,4 @@ public class CreateSignature extends Cre
                            "  -tsa <url>    sign timestamp using the given TSA server\n"
+
                            "  -e            sign using external signature creation scenario");
     }
-
-    public void setExternalSignature(boolean externalSignature)
-    {
-        this.externalSignature = externalSignature;
-    }
 }

Modified: pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java?rev=1760247&r1=1760246&r2=1760247&view=diff
==============================================================================
--- pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java
(original)
+++ pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java
Sun Sep 11 12:23:57 2016
@@ -60,6 +60,7 @@ public abstract class CreateSignatureBas
     private PrivateKey privateKey;
     private Certificate certificate;
     private TSAClient tsaClient;
+    private boolean externalSigning;
 
     /**
      * Initialize the signature creator with a keystore (pkcs12) and pin that should be used
for the
@@ -236,4 +237,21 @@ public abstract class CreateSignatureBas
         }
     }
 
+    /**
+     * Set if external signing scenario should be used.
+     * If {@code false}, SignatureInterface would be used for signing.
+     * <p>
+     *     Default: {@code false}
+     * </p>
+     * @param externalSigning {@code true} if external signing should be performed
+     */
+    public void setExternalSigning(boolean externalSigning)
+    {
+        this.externalSigning = externalSigning;
+    }
+
+    public boolean isExternalSigning()
+    {
+        return externalSigning;
+    }
 }

Modified: pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java?rev=1760247&r1=1760246&r2=1760247&view=diff
==============================================================================
--- pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
(original)
+++ pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
Sun Sep 11 12:23:57 2016
@@ -31,7 +31,9 @@ import java.util.Calendar;
 import org.apache.pdfbox.io.IOUtils;
 
 import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSignDesigner;
@@ -48,7 +50,7 @@ public class CreateVisibleSignature exte
     private PDVisibleSignDesigner visibleSignDesigner;
     private final PDVisibleSigProperties visibleSignatureProperties = new PDVisibleSigProperties();
 
-    public void setvisibleSignDesigner(String filename, int x, int y, int zoomPercent, 
+    public void setVisibleSignDesigner(String filename, int x, int y, int zoomPercent, 
             FileInputStream imageStream, int page) 
             throws IOException
     {
@@ -124,21 +126,36 @@ public class CreateVisibleSignature exte
         // the signing date, needed for valid signature
         signature.setSignDate(Calendar.getInstance());
 
+        // do not set SignatureInterface instance, if external signing used
+        SignatureInterface signatureInterface = isExternalSigning() ? null : this;
+
         // register signature dictionary and sign interface
         if (visibleSignatureProperties != null && visibleSignatureProperties.isVisualSignEnabled())
         {
             signatureOptions = new SignatureOptions();
             signatureOptions.setVisualSignature(visibleSignatureProperties.getVisibleSignature());
             signatureOptions.setPage(visibleSignatureProperties.getPage() - 1);
-            doc.addSignature(signature, this, signatureOptions);
+            doc.addSignature(signature, signatureInterface, signatureOptions);
         }
         else
         {
-            doc.addSignature(signature, this);
+            doc.addSignature(signature, signatureInterface);
         }
 
-        // write incremental (only for signing purpose)
-        doc.saveIncremental(fos);
+        if (isExternalSigning())
+        {
+            System.out.println("Signing externally " + signedFile.getName());
+            ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
+            // invoke external signature service
+            byte[] cmsSignature = sign(externalSigning.getContent());
+            // set signature bytes received from the service
+            externalSigning.setSignature(cmsSignature);
+        }
+        else
+        {
+            // write incremental (only for signing purpose)
+            doc.saveIncremental(fos);
+        }
         doc.close();
         
         // do not close options before saving, because some COSStream objects within options

@@ -152,6 +169,7 @@ public class CreateVisibleSignature exte
      * [1] pin
      * [2] document that will be signed
      * [3] image of visible signature
+     *
      * @param args
      * @throws java.security.KeyStoreException
      * @throws java.security.cert.CertificateException
@@ -171,7 +189,8 @@ public class CreateVisibleSignature exte
         }
 
         String tsaUrl = null;
-        for(int i = 0; i < args.length; i++)
+        boolean externalSig = false;
+        for (int i = 0; i < args.length; i++)
         {
             if (args[i].equals("-tsa"))
             {
@@ -179,9 +198,14 @@ public class CreateVisibleSignature exte
                 if (i >= args.length)
                 {
                     usage();
+                    System.exit(1);
                 }
                 tsaUrl = args[i];
             }
+            if (args[i].equals("-e"))
+            {
+                externalSig = true;
+            }
         }
 
         File ksFile = new File(args[0]);
@@ -209,9 +233,10 @@ public class CreateVisibleSignature exte
 
         // page is 1-based here
         int page = 1;
-        signing.setvisibleSignDesigner (args[2], 0, 0, -50, imageStream, page);
+        signing.setVisibleSignDesigner(args[2], 0, 0, -50, imageStream, page);
         imageStream.close();
-        signing.setVisibleSignatureProperties ("name", "location", "Security", 0, page, true);
+        signing.setVisibleSignatureProperties("name", "location", "Security", 0, page, true);
+        signing.setExternalSigning(externalSig);
         signing.signPDF(documentFile, signedDocumentFile, tsaClient);
     }
 
@@ -223,6 +248,7 @@ public class CreateVisibleSignature exte
         System.err.println("Usage: java " + CreateVisibleSignature.class.getName()
                 + " <pkcs12-keystore-file> <pin> <input-pdf> <sign-image>\n"
+ "" +
                            "options:\n" +
-                           "  -tsa <url>    sign timestamp using the given TSA server");
+                           "  -tsa <url>    sign timestamp using the given TSA server\n"+
+                           "  -e            sign using external signature creation scenario");
     }
 }

Modified: pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java?rev=1760247&r1=1760246&r2=1760247&view=diff
==============================================================================
--- pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
(original)
+++ pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
Sun Sep 11 12:23:57 2016
@@ -1,224 +1,257 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.pdfbox.examples.pdmodel;
-
-import junit.framework.TestCase;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.MessageDigest;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.Collection;
-import java.util.List;
-import org.apache.pdfbox.cos.COSName;
-import org.apache.pdfbox.cos.COSString;
-
-import org.apache.pdfbox.examples.signature.CreateSignature;
-import org.apache.pdfbox.examples.signature.CreateVisibleSignature;
-import org.apache.pdfbox.examples.signature.TSAClient;
-import org.apache.pdfbox.io.IOUtils;
-import org.apache.pdfbox.pdmodel.PDDocument;
-import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
-import org.apache.wink.client.MockHttpServer;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.tsp.TSPValidationException;
-import org.bouncycastle.util.Store;
-
-/**
- * Test for CreateSignature
- */
-public class TestCreateSignature extends TestCase
-{
-    private final String inDir = "src/test/resources/org/apache/pdfbox/examples/signature/";
-    private final String outDir = "target/test-output/";
-    private final String keystorePath = inDir + "keystore.p12";
-    private final String jpegPath = inDir + "stamp.jpg";
-    private final String password = "123456";
-    private Certificate certificate;
-
-    @Override
-    protected void setUp() throws Exception
-    {
-        super.setUp();
-        new File("target/test-output").mkdirs();
-        
-        KeyStore keystore = KeyStore.getInstance("PKCS12");
-        keystore.load(new FileInputStream(keystorePath), password.toCharArray());
-        certificate = keystore.getCertificateChain(keystore.aliases().nextElement())[0];
-    }
-
-    /**
-     * Signs a PDF using the "adbe.pkcs7.detached" SubFilter with the SHA-256 digest.
-     *
-     * @throws IOException
-     * @throws GeneralSecurityException
-     * @throws CMSException
-     * @throws OperatorCreationException
-     */
-    public void testDetachedSHA256()
-            throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
-    {
-        // load the keystore
-        KeyStore keystore = KeyStore.getInstance("PKCS12");
-        keystore.load(new FileInputStream(keystorePath), password.toCharArray());
-
-        // sign PDF
-        CreateSignature signing = new CreateSignature(keystore, password.toCharArray());
-        signing.signDetached(new File(inDir + "sign_me.pdf"), new File(outDir + "signed.pdf"));
-
-        checkSignature(new File(outDir + "signed.pdf"));
-    }
-
-    /**
-     * Signs a PDF using the "adbe.pkcs7.detached" SubFilter with the SHA-256 digest and
a signed
-     * timestamp from a Time Stamping Authority (TSA) server.
-     *
-     * This is not a complete test because we don't have the ability to return a valid response,
so
-     * we return a cached response which is well-formed, but does not match the timestamp
or nonce
-     * in the request. This allows us to test the basic TSA mechanism and test the nonce,
which is a
-     * good start.
-     *
-     * @throws IOException
-     * @throws GeneralSecurityException
-     * @throws CMSException
-     * @throws OperatorCreationException
-     */
-    public void testDetachedSHA256WithTSA()
-            throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
-    {
-        // mock TSA response content
-        InputStream input = new FileInputStream(inDir + "tsa_response.asn1");
-        byte[] content = IOUtils.toByteArray(input);
-        input.close();
-
-        // mock TSA server (RFC 3161)
-        MockHttpServer mockServer = new MockHttpServer(15371);
-        mockServer.startServer();
-        String tsaUrl = "http://localhost:" + mockServer.getServerPort() + "/";
-        MockHttpServer.MockHttpServerResponse response = new MockHttpServer.MockHttpServerResponse();
-        response.setMockResponseContent(content);
-        response.setMockResponseContentType("application/timestamp-reply");
-        response.setMockResponseCode(200);
-        mockServer.setMockHttpServerResponses(response);
-
-        // TSA client
-        MessageDigest digest = MessageDigest.getInstance("SHA-256");
-        TSAClient tsaClient = new TSAClient(new URL(tsaUrl), null, null, digest);
-
-        // load the keystore
-        KeyStore keystore = KeyStore.getInstance("PKCS12");
-        keystore.load(new FileInputStream(keystorePath), password.toCharArray());
-
-        // sign PDF (will fail due to nonce and timestamp differing)
-        try
-        {
-            String inPath = inDir + "sign_me_tsa.pdf";
-            String outPath = outDir + "signed_tsa.pdf";
-            CreateSignature signing = new CreateSignature(keystore, password.toCharArray());
-            signing.signDetached(new File(inPath), new File(outPath), tsaClient);
-        }
-        catch (IOException e)
-        {
-            assertTrue(e.getCause() instanceof TSPValidationException);
-        }
-
-        // TODO verify the signed PDF file
-        // TODO create a file signed with TSA
-    }
-    
-    /**
-     * Test creating visual signature.
-     *
-     * @throws IOException
-     * @throws CMSException
-     * @throws OperatorCreationException
-     * @throws GeneralSecurityException
-     */
-    public void testCreateVisibleSignature()
-            throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
-    {
-        // load the keystore
-        KeyStore keystore = KeyStore.getInstance("PKCS12");
-        keystore.load(new FileInputStream(keystorePath), password.toCharArray());
-
-        // sign PDF
-        String inPath = inDir + "sign_me.pdf";
-        FileInputStream fis = new FileInputStream(jpegPath);
-        CreateVisibleSignature signing = new CreateVisibleSignature(keystore, password.toCharArray());
-        signing.setvisibleSignDesigner(inPath, 0, 0, -50, fis, 1);
-        signing.setVisibleSignatureProperties("name", "location", "Security", 0, 1, true);
-        File destFile = new File(outDir + "signed_visible.pdf");
-        signing.signPDF(new File(inPath), destFile, null);
-        fis.close();
-
-        checkSignature(destFile);
-    }
-
-    // This check fails with a file created with the code before PDFBOX-3011 was solved.
-    private void checkSignature(File file)
-            throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
-    {
-        PDDocument document = PDDocument.load(file);
-        List<PDSignature> signatureDictionaries = document.getSignatureDictionaries();
-        if (signatureDictionaries.isEmpty())
-        {
-            fail("no signature found");
-        }
-        for (PDSignature sig : document.getSignatureDictionaries())
-        {
-            COSString contents = (COSString) sig.getCOSObject().getDictionaryObject(COSName.CONTENTS);
-            
-            FileInputStream fis = new FileInputStream(file);
-            byte[] buf = sig.getSignedContent(fis);
-            fis.close();
-
-            // inspiration:
-            // http://stackoverflow.com/a/26702631/535646
-            // http://stackoverflow.com/a/9261365/535646
-            CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(buf),
contents.getBytes());
-            Store certificatesStore = signedData.getCertificates();
-            Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
-            SignerInformation signerInformation = signers.iterator().next();
-            Collection matches = certificatesStore.getMatches(signerInformation.getSID());
-            X509CertificateHolder certificateHolder = (X509CertificateHolder) matches.iterator().next();
-            X509Certificate certFromSignedData = new JcaX509CertificateConverter().getCertificate(certificateHolder);
-
-            assertEquals(certificate, certFromSignedData);
-
-            // CMSVerifierCertificateNotValidException means that the keystore wasn't valid
at signing time
-            if (!signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certFromSignedData)))
-            {
-                fail("Signature verification failed");
-            }
-            break;
-        }
-        document.close();
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.pdmodel;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSString;
+
+import org.apache.pdfbox.examples.signature.CreateSignature;
+import org.apache.pdfbox.examples.signature.CreateVisibleSignature;
+import org.apache.pdfbox.examples.signature.TSAClient;
+import org.apache.pdfbox.io.IOUtils;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.wink.client.MockHttpServer;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.tsp.TSPValidationException;
+import org.bouncycastle.util.Store;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Test for CreateSignature. Each test case will run twice: once with SignatureInterface
+ * and once using external signature creation scenario.
+ */
+@RunWith(Parameterized.class)
+public class TestCreateSignature
+{
+    private static final String inDir = "src/test/resources/org/apache/pdfbox/examples/signature/";
+    private static final String outDir = "target/test-output/";
+    private static final String keystorePath = inDir + "keystore.p12";
+    private static final String jpegPath = inDir + "stamp.jpg";
+    private static final String password = "123456";
+    private static Certificate certificate;
+
+    @Parameterized.Parameter
+    public boolean externallySign;
+
+    /**
+     * Values for {@link #externallySign} test parameter to specify if signing should be
conducted
+     * using externally singing scenario ({@code true}) or SignatureInterface ({@code false}).
+     */
+    @Parameterized.Parameters
+    public static Collection signingTypes()
+    {
+        return Arrays.asList(false, true);
+    }
+
+    @BeforeClass
+    public static void init() throws Exception
+    {
+        new File("target/test-output").mkdirs();
+
+        KeyStore keystore = KeyStore.getInstance("PKCS12");
+        keystore.load(new FileInputStream(keystorePath), password.toCharArray());
+        certificate = keystore.getCertificateChain(keystore.aliases().nextElement())[0];
+    }
+
+    /**
+     * Signs a PDF using the "adbe.pkcs7.detached" SubFilter with the SHA-256 digest.
+     *
+     * @throws IOException
+     * @throws GeneralSecurityException
+     * @throws CMSException
+     * @throws OperatorCreationException
+     */
+    @Test
+    public void testDetachedSHA256()
+            throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
+    {
+        // load the keystore
+        KeyStore keystore = KeyStore.getInstance("PKCS12");
+        keystore.load(new FileInputStream(keystorePath), password.toCharArray());
+
+        // sign PDF
+        CreateSignature signing = new CreateSignature(keystore, password.toCharArray());
+        signing.setExternalSigning(externallySign);
+
+        final String fileName = getOutputFileName("signed{0}.pdf");
+        signing.signDetached(new File(inDir + "sign_me.pdf"), new File(outDir + fileName));
+
+        checkSignature(new File(outDir + fileName));
+    }
+
+    /**
+     * Signs a PDF using the "adbe.pkcs7.detached" SubFilter with the SHA-256 digest and
a signed
+     * timestamp from a Time Stamping Authority (TSA) server.
+     *
+     * This is not a complete test because we don't have the ability to return a valid response,
so
+     * we return a cached response which is well-formed, but does not match the timestamp
or nonce
+     * in the request. This allows us to test the basic TSA mechanism and test the nonce,
which is a
+     * good start.
+     *
+     * @throws IOException
+     * @throws GeneralSecurityException
+     * @throws CMSException
+     * @throws OperatorCreationException
+     */
+    @Test
+    public void testDetachedSHA256WithTSA()
+            throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
+    {
+        // mock TSA response content
+        InputStream input = new FileInputStream(inDir + "tsa_response.asn1");
+        byte[] content = IOUtils.toByteArray(input);
+        input.close();
+
+        // mock TSA server (RFC 3161)
+        MockHttpServer mockServer = new MockHttpServer(15371);
+        mockServer.startServer();
+        String tsaUrl = "http://localhost:" + mockServer.getServerPort() + "/";
+        MockHttpServer.MockHttpServerResponse response = new MockHttpServer.MockHttpServerResponse();
+        response.setMockResponseContent(content);
+        response.setMockResponseContentType("application/timestamp-reply");
+        response.setMockResponseCode(200);
+        mockServer.setMockHttpServerResponses(response);
+
+        // TSA client
+        MessageDigest digest = MessageDigest.getInstance("SHA-256");
+        TSAClient tsaClient = new TSAClient(new URL(tsaUrl), null, null, digest);
+
+        // load the keystore
+        KeyStore keystore = KeyStore.getInstance("PKCS12");
+        keystore.load(new FileInputStream(keystorePath), password.toCharArray());
+
+        // sign PDF (will fail due to nonce and timestamp differing)
+        try
+        {
+            String inPath = inDir + "sign_me_tsa.pdf";
+            String outPath = outDir + getOutputFileName("signed{0}_tsa.pdf");
+            CreateSignature signing = new CreateSignature(keystore, password.toCharArray());
+            signing.setExternalSigning(externallySign);
+            signing.signDetached(new File(inPath), new File(outPath), tsaClient);
+        }
+        catch (IOException e)
+        {
+            Assert.assertTrue(e.getCause() instanceof TSPValidationException);
+        }
+
+        // TODO verify the signed PDF file
+        // TODO create a file signed with TSA
+    }
+    
+    /**
+     * Test creating visual signature.
+     *
+     * @throws IOException
+     * @throws CMSException
+     * @throws OperatorCreationException
+     * @throws GeneralSecurityException
+     */
+    @Test
+    public void testCreateVisibleSignature()
+            throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
+    {
+        // load the keystore
+        KeyStore keystore = KeyStore.getInstance("PKCS12");
+        keystore.load(new FileInputStream(keystorePath), password.toCharArray());
+
+        // sign PDF
+        String inPath = inDir + "sign_me.pdf";
+        FileInputStream fis = new FileInputStream(jpegPath);
+        CreateVisibleSignature signing = new CreateVisibleSignature(keystore, password.toCharArray());
+        signing.setVisibleSignDesigner(inPath, 0, 0, -50, fis, 1);
+        signing.setVisibleSignatureProperties("name", "location", "Security", 0, 1, true);
+        signing.setExternalSigning(externallySign);
+
+        File destFile = new File(outDir + getOutputFileName("signed{0}_visible.pdf"));
+        signing.signPDF(new File(inPath), destFile, null);
+        fis.close();
+
+        checkSignature(destFile);
+    }
+
+    private String getOutputFileName(String filePattern)
+    {
+        return MessageFormat.format(filePattern,(externallySign ? "_ext" : ""));
+    }
+
+    // This check fails with a file created with the code before PDFBOX-3011 was solved.
+    private void checkSignature(File file)
+            throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
+    {
+        PDDocument document = PDDocument.load(file);
+        List<PDSignature> signatureDictionaries = document.getSignatureDictionaries();
+        if (signatureDictionaries.isEmpty())
+        {
+            Assert.fail("no signature found");
+        }
+        for (PDSignature sig : document.getSignatureDictionaries())
+        {
+            COSString contents = (COSString) sig.getCOSObject().getDictionaryObject(COSName.CONTENTS);
+            
+            FileInputStream fis = new FileInputStream(file);
+            byte[] buf = sig.getSignedContent(fis);
+            fis.close();
+
+            // inspiration:
+            // http://stackoverflow.com/a/26702631/535646
+            // http://stackoverflow.com/a/9261365/535646
+            CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(buf),
contents.getBytes());
+            Store certificatesStore = signedData.getCertificates();
+            Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
+            SignerInformation signerInformation = signers.iterator().next();
+            Collection matches = certificatesStore.getMatches(signerInformation.getSID());
+            X509CertificateHolder certificateHolder = (X509CertificateHolder) matches.iterator().next();
+            X509Certificate certFromSignedData = new JcaX509CertificateConverter().getCertificate(certificateHolder);
+
+            Assert.assertEquals(certificate, certFromSignedData);
+
+            // CMSVerifierCertificateNotValidException means that the keystore wasn't valid
at signing time
+            if (!signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certFromSignedData)))
+            {
+                Assert.fail("Signature verification failed");
+            }
+            break;
+        }
+        document.close();
+    }
+}



Mime
View raw message