You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2017/05/27 15:33:18 UTC

svn commit: r1796417 - in /pdfbox/trunk: examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java

Author: tilman
Date: Sat May 27 15:33:18 2017
New Revision: 1796417

URL: http://svn.apache.org/viewvc?rev=1796417&view=rev
Log:
PDFBOX-3811: add exception if saveIncrementalForExternalSigning is called twice without initializing reserve byte range, and a test inspired by Le Duc Duy

Modified:
    pdfbox/trunk/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java

Modified: pdfbox/trunk/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java?rev=1796417&r1=1796416&r2=1796417&view=diff
==============================================================================
--- pdfbox/trunk/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java (original)
+++ pdfbox/trunk/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java Sat May 27 15:33:18 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.pdfbox.examples.pdmodel;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -24,20 +25,24 @@ import java.net.URL;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 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 javax.xml.bind.DatatypeConverter;
+
 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.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
 import org.apache.wink.client.MockHttpServer;
 import org.bouncycastle.cert.X509CertificateHolder;
@@ -256,4 +261,58 @@ public class TestCreateSignature
             }
         }
     }
+
+    private String calculateDigestString(InputStream inputStream) throws NoSuchAlgorithmException, IOException
+    {
+        MessageDigest md = MessageDigest.getInstance("SHA-256");
+        return DatatypeConverter.printHexBinary(md.digest(IOUtils.toByteArray(inputStream)));
+    }
+
+    /**
+     * PDFBOX-3811: make sure that calling saveIncrementalForExternalSigning() more than once
+     * brings the same result.
+     * 
+     * @throws IOException
+     * @throws NoSuchAlgorithmException 
+     */
+    @Test
+    public void testPDFBox3811() throws IOException, NoSuchAlgorithmException
+    {
+        if (!externallySign)
+        {
+            return;
+        }
+        
+        // create simple PDF
+        PDDocument document = new PDDocument();
+        PDPage page = new PDPage();
+        document.addPage(page);
+        new PDPageContentStream(document, page).close();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        document.save(baos);
+        document.close();
+        
+        document = PDDocument.load(baos.toByteArray());
+        // for stable digest
+        document.setDocumentId(12345L);
+        
+        PDSignature signature = new PDSignature();
+        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
+        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
+        document.addSignature(signature);
+
+        String digestString = calculateDigestString(document.saveIncrementalForExternalSigning(new ByteArrayOutputStream()).getContent());
+        boolean caught = false;
+        try
+        {
+            document.saveIncrementalForExternalSigning(new ByteArrayOutputStream());
+        }
+        catch (IllegalStateException ex)
+        {
+            caught = true;
+        }
+        Assert.assertTrue("IllegalStateException should have been thrown", caught);
+        document.getLastSignatureDictionary().setByteRange(PDDocument.RESERVE_BYTE_RANGE);
+        Assert.assertEquals(digestString, calculateDigestString(document.saveIncrementalForExternalSigning(new ByteArrayOutputStream()).getContent()));
+    }
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java?rev=1796417&r1=1796416&r2=1796417&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java Sat May 27 15:33:18 2017
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -77,6 +78,13 @@ import org.apache.pdfbox.pdmodel.interac
  */
 public class PDDocument implements Closeable
 {
+    /**
+     * For signing: large reserve byte range used as placeholder in the saved PDF until actual the
+     * length of the PDF is known. You'll need to assign this yourself (with {@link PDSignature#setByteRange(int[])} ) only if you call 
+     * {@link #saveIncrementalForExternalSigning(java.io.OutputStream) saveIncrementalForExternalSigning()} twice.
+     */
+    public static final int[] RESERVE_BYTE_RANGE = new int[] { 0, 1000000000, 1000000000, 1000000000 };
+        
     private static final Log LOG = LogFactory.getLog(PDDocument.class);
 
     /**
@@ -263,8 +271,8 @@ public class PDDocument implements Close
             sigObject.setContents(new byte[SignatureOptions.DEFAULT_SIGNATURE_SIZE]);
         }
 
-        // Reserve ByteRange
-        sigObject.setByteRange(new int[] { 0, 1000000000, 1000000000, 1000000000 });
+        // Reserve ByteRange, will be overwritten in COSWriter
+        sigObject.setByteRange(RESERVE_BYTE_RANGE);
 
         signInterface = signatureInterface;
 
@@ -1336,6 +1344,12 @@ public class PDDocument implements Close
         {
             throw new IllegalStateException("document was not loaded from a file or a stream");
         }
+        int[] byteRange = getLastSignatureDictionary().getByteRange();
+        if (!Arrays.equals(byteRange, RESERVE_BYTE_RANGE))
+        {
+            throw new IllegalStateException("signature reserve byte range has been changed "
+                    + "after addSignature(), please call setByteRange(RESERVE_BYTE_RANGE)");
+        }
         COSWriter writer = new COSWriter(output, pdfSource);
         writer.write(this);
         signingSupport = new SigningSupport(writer);