pdfbox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From til...@apache.org
Subject svn commit: r1759639 - in /pdfbox/branches/2.0: examples/src/main/java/org/apache/pdfbox/examples/signature/ pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/ pdfbox/src/main/java/org/apache/pdfbox/pdmode...
Date Wed, 07 Sep 2016 16:53:46 GMT
Author: tilman
Date: Wed Sep  7 16:53:46 2016
New Revision: 1759639

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

Added:
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/ExternalSigningSupport.java
  (with props)
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SigningSupport.java
  (with props)
Modified:
    pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.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=1759639&r1=1759638&r2=1759639&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
Wed Sep  7 16:53:46 2016
@@ -33,6 +33,7 @@ import java.security.cert.CertificateExc
 import java.util.Calendar;
 
 import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
 
 /**
@@ -49,8 +50,12 @@ 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.
+     *
      * @param keystore the pkcs12 keystore containing the signing certificate
      * @param pin the password for recovering the key
      * @throws KeyStoreException if the keystore has not been initialized (loaded)
@@ -125,11 +130,24 @@ public class CreateSignature extends Cre
         // the signing date, needed for valid signature
         signature.setSignDate(Calendar.getInstance());
 
-        // register signature dictionary and sign interface
-        document.addSignature(signature, this);
+        if (externalSignature)
+        {
+            System.out.println("Sign externally...");
+            document.addSignature(signature);
+            externalSigning = document.saveIncrementalForExternalSigning(output);
+            // invoke external signature service
+            byte[] cmsSignature = sign(externalSigning.getContent());
+            // set signature bytes received from the service
+            externalSigning.setSignature(cmsSignature);
+        }
+        else
+        {
+            // register signature dictionary and sign interface
+            document.addSignature(signature, this);
 
-        // write incremental (only for signing purpose)
-        document.saveIncremental(output);
+            // write incremental (only for signing purpose)
+            document.saveIncremental(output);
+        }
     }
 
     public static void main(String[] args) throws IOException, GeneralSecurityException
@@ -141,7 +159,8 @@ public class CreateSignature extends Cre
         }
 
         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"))
             {
@@ -149,9 +168,14 @@ public class CreateSignature extends Cre
                 if (i >= args.length)
                 {
                     usage();
+                    System.exit(1);
                 }
                 tsaUrl = args[i];
             }
+            if (args[i].equals("-e"))
+            {
+                externalSig = true;
+            }
         }
 
         // load the keystore
@@ -170,6 +194,7 @@ public class CreateSignature extends Cre
 
         // sign PDF
         CreateSignature signing = new CreateSignature(keystore, password);
+        signing.setExternalSignature(externalSig);
 
         File inFile = new File(args[2]);
         String name = inFile.getName();
@@ -184,6 +209,12 @@ public class CreateSignature extends Cre
         System.err.println("usage: java " + CreateSignature.class.getName() + " " +
                            "<pkcs12_keystore> <password> <pdf_to_sign>\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");
+    }
+
+    public void setExternalSignature(boolean externalSignature)
+    {
+        this.externalSignature = externalSignature;
     }
 }

Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java?rev=1759639&r1=1759638&r2=1759639&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java Wed
Sep  7 16:53:46 2016
@@ -66,6 +66,7 @@ import org.apache.pdfbox.pdfparser.PDFXR
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.encryption.SecurityHandler;
 import org.apache.pdfbox.pdmodel.fdf.FDFDocument;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.COSFilterInputStream;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
 import org.apache.pdfbox.util.Charsets;
 import org.apache.pdfbox.util.Hex;
@@ -220,6 +221,7 @@ public class COSWriter implements ICOSVi
     private RandomAccessRead tempIncInput;
     private OutputStream incrementalOutput;
     private SignatureInterface signatureInterface;
+    private byte[] incrementPart;
 
     /**
      * COSWriter constructor comment.
@@ -744,7 +746,7 @@ public class COSWriter implements ICOSVi
         // copy the new incremental data into a buffer (e.g. signature dict, trailer)
         ByteArrayOutputStream byteOut = (ByteArrayOutputStream) output;
         byteOut.flush();
-        byte[] buffer = byteOut.toByteArray();
+        incrementPart = byteOut.toByteArray();
 
         // overwrite the ByteRange in the buffer
         byte[] byteRangeBytes = byteRange.getBytes(Charsets.ISO_8859_1);
@@ -752,26 +754,71 @@ public class COSWriter implements ICOSVi
         {
             if (i >= byteRangeBytes.length)
             {
-                buffer[(int)(byteRangeOffset + i - inLength)] = 0x20; // SPACE
+                incrementPart[(int) (byteRangeOffset + i - inLength)] = 0x20; // SPACE
             }
             else
             {
-                buffer[(int)(byteRangeOffset + i - inLength)] = byteRangeBytes[i];
+                incrementPart[(int) (byteRangeOffset + i - inLength)] = byteRangeBytes[i];
             }
         }
 
-        // get only the incremental bytes to be signed (includes /ByteRange but not /Contents)
-        byte[] signBuffer = new byte[buffer.length - (int)signatureLength];
-        int bufSignatureOffset = (int)(signatureOffset - inLength);
-        System.arraycopy(buffer, 0, signBuffer, 0, bufSignatureOffset);
-        System.arraycopy(buffer, bufSignatureOffset + (int)signatureLength,
-                         signBuffer, bufSignatureOffset, buffer.length - bufSignatureOffset
- (int)signatureLength);
+        if(signatureInterface != null)
+        {
+            // data to be signed
+            final InputStream dataToSign = getDataToSign();
 
-        SequenceInputStream signStream = new SequenceInputStream(new RandomAccessInputStream(incrementalInput),
-                new ByteArrayInputStream(signBuffer));
+            // sign the bytes
+            byte[] signatureBytes = signatureInterface.sign(dataToSign);
+            writeExternalSignature(signatureBytes);
+        }
+        // else signature should created externally and set via writeSignature()
+    }
+
+    /**
+     * Return the stream of PDF data to be signed. Clients should use this method only to
create
+     * signatures externally. {@link #write(PDDocument)} method should have been called prior.
+     * The created signature should be set using {@link #writeExternalSignature(byte[])}.
+     * <p>
+     * When {@link SignatureInterface} instance is used, COSWriter obtains and writes the
signature itsef.
+     * </p>
+     * Note that caller must close the obtained stream.
+     *
+     * @return data stream to be signed
+     * @throws IllegalStateException if PDF is not prepared for external signing
+     * @throws IOException if input data is closed
+     */
+    public InputStream getDataToSign() throws IOException
+    {
+        if (incrementPart == null || incrementalInput == null)
+        {
+            throw new IllegalStateException("PDF not prepared for signing");
+        }
+        // range of incremental bytes to be signed (includes /ByteRange but not /Contents)
+        int incPartSigOffset = (int) (signatureOffset - incrementalInput.length());
+        int afterSigOffset = incPartSigOffset + (int) signatureLength;
+        int[] range = {0, incPartSigOffset,
+                afterSigOffset, incrementPart.length - afterSigOffset};
+
+        return new SequenceInputStream(
+                new RandomAccessInputStream(incrementalInput),
+                new COSFilterInputStream(incrementPart, range));
+    }
+
+    /**
+     * Write externally created signature of PDF data obtained via {@link #getDataToSign()}
method.
+     *
+     * @param cmsSignature CMS signature byte array
+     * @throws IllegalStateException if PDF is not prepared for external signing
+     * @throws IOException if source data stream is closed
+     */
+    public void writeExternalSignature(byte[] cmsSignature) throws IOException {
+
+        if (incrementPart == null || incrementalInput == null)
+        {
+            throw new IllegalStateException("PDF not prepared for setting signature");
+        }
+        byte[] signatureBytes = Hex.getBytes(cmsSignature);
 
-        // sign the bytes
-        byte[] signatureBytes = Hex.getBytes(signatureInterface.sign(signStream));
         // substract 2 bytes because of the enclosing "<>"
         if (signatureBytes.length > signatureLength - 2)
         {
@@ -779,11 +826,15 @@ public class COSWriter implements ICOSVi
         }
 
         // overwrite the signature Contents in the buffer
-        System.arraycopy(signatureBytes, 0, buffer, bufSignatureOffset + 1, signatureBytes.length);
+        int incPartSigOffset = (int) (signatureOffset - incrementalInput.length());
+        System.arraycopy(signatureBytes, 0, incrementPart, incPartSigOffset + 1, signatureBytes.length);
 
         // write the data to the incremental output stream
         IOUtils.copy(new RandomAccessInputStream(incrementalInput), incrementalOutput);
-        incrementalOutput.write(buffer);
+        incrementalOutput.write(incrementPart);
+
+        // prevent further use
+        incrementPart = null;
     }
 
     private void writeXrefRange(long x, long y) throws IOException
@@ -1219,7 +1270,9 @@ public class COSWriter implements ICOSVi
     }
 
     /**
-     * This will write the pdf document.
+     * This will write the pdf document. If signature should be created externally,
+     * {@link #writeExternalSignature(byte[])} should be invoked to set signature after calling
this
+     * method.
      *
      * @param doc The document to write.
      *
@@ -1231,10 +1284,13 @@ public class COSWriter implements ICOSVi
     }
 
     /**
-     * This will write the pdf document.
+     * This will write the pdf document. If signature should be created externally,
+     * {@link #writeExternalSignature(byte[])} should be invoked to set signature after calling
this
+     * method.
      *
      * @param doc The document to write.
-     * @param signInterface class to be used for signing 
+     * @param signInterface class to be used for signing; {@code null} if external signing
would be
+     * performed or there will be no signing at all
      *
      * @throws IOException If an error occurs while generating the data.
      * @throws IllegalStateException If the document has an encryption dictionary but no
protection

Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java?rev=1759639&r1=1759638&r2=1759639&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java Wed
Sep  7 16:53:46 2016
@@ -55,9 +55,11 @@ import org.apache.pdfbox.pdmodel.encrypt
 import org.apache.pdfbox.pdmodel.font.PDFont;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+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.SigningSupport;
 import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
 import org.apache.pdfbox.pdmodel.interactive.form.PDField;
 import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
@@ -97,13 +99,16 @@ public class PDDocument implements Close
     
     // fonts to subset before saving
     private final Set<PDFont> fontsToSubset = new HashSet<PDFont>();
-    
+
     // Signature interface
     private SignatureInterface signInterface;
-    
+
+    // helper class used to create external signature
+    private SigningSupport signingSupport;
+
     // document-wide cached resources
     private ResourceCache resourceCache = new DefaultResourceCache();
-    
+
     /**
      * Creates an empty PDF document.
      * You need to add at least one page for the document to be valid.
@@ -171,7 +176,34 @@ public class PDDocument implements Close
     }
 
     /**
-     * Add a signature.
+     * Add parameters of signature to be created externally using default signature options.
See
+     * {@link #saveIncrementalForExternalSigning(OutputStream)} method description on external
+     * signature creation scenario details.
+     *
+     * @param sigObject is the PDSignatureField model
+     * @throws IOException if there is an error creating required fields
+     */
+    public void addSignature(PDSignature sigObject) throws IOException
+    {
+        addSignature(sigObject, new SignatureOptions());
+    }
+
+    /**
+     * Add parameters of signature to be created externally. See
+     * {@link #saveIncrementalForExternalSigning(OutputStream)} method description on external
+     * signature creation scenario details.
+     *
+     * @param sigObject is the PDSignatureField model
+     * @param options signature options
+     * @throws IOException if there is an error creating required fields
+     */
+    public void addSignature(PDSignature sigObject, SignatureOptions options) throws IOException
+    {
+        addSignature(sigObject, null, options);
+    }
+
+    /**
+     * Add a signature to be created using the instance of given interface.
      * 
      * @param sigObject is the PDSignatureField model
      * @param signatureInterface is an interface which provides signing capabilities
@@ -1151,6 +1183,52 @@ public class PDDocument implements Close
     }
 
     /**
+     * Save PDF incrementally without closing for external signature creation scenario. The
general
+     * sequence is:
+     * <pre>
+     *    PDDocument pdDocument = ...;
+     *    OutputStream outputStream = ...;
+     *    SignatureOptions signatureOptions = ...; // options to specify fine tuned signature
options or null for defaults
+     *    PDSignature pdSignature = ...;
+     *
+     *    // add signature parameters to be used when creating signature dictionary
+     *    pdDocument.addSignature(pdSignature, signatureOptions);
+     *    // prepare PDF for signing and obtain helper class to be used
+     *    ExternalSigningSupport externalSigningSupport = pdDocument.saveIncrementalForExternalSigning(outputStream);
+     *    // get data to be signed
+     *    InputStream dataToBeSigned = externalSigningSupport.getContent();
+     *    // invoke signature service
+     *    byte[] signature = sign(dataToBeSigned);
+     *    // set resulted CMS signature
+     *    externalSigningSupport.setSignature(signature);
+     *
+     *    // last step is to close the document
+     *    pdDocument.close();
+     * </pre>
+     * <p>
+     * Note that after calling this method, only {@code close()} method may invoked for
+     * {@code PDDocument} instance and only AFTER {@link ExternalSigningSupport} instance
is used.
+     * </p>
+     *
+     * @param output stream to write final PDF
+     * @return instance to be used for external signing and setting CMS signature
+     * @throws IOException if the output could not be written
+     * @throws IllegalStateException if the document was not loaded from a file or a stream
or
+     * signature optionss were not set.
+     */
+    public ExternalSigningSupport saveIncrementalForExternalSigning(OutputStream output)
throws IOException
+    {
+        if (pdfSource == null)
+        {
+            throw new IllegalStateException("document was not loaded from a file or a stream");
+        }
+        COSWriter writer = new COSWriter(output, pdfSource);
+        writer.write(this);
+        signingSupport = new SigningSupport(writer);
+        return signingSupport;
+    }
+
+    /**
      * Returns the page at the given index.
      *
      * @param pageIndex the page index
@@ -1191,6 +1269,12 @@ public class PDDocument implements Close
     {
         if (!document.isClosed())
         {
+            // close resources and COSWriter
+            if (signingSupport != null)
+            {
+                signingSupport.close();
+            }
+
             // close all intermediate I/O streams
             document.close();
             

Added: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/ExternalSigningSupport.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/ExternalSigningSupport.java?rev=1759639&view=auto
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/ExternalSigningSupport.java
(added)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/ExternalSigningSupport.java
Wed Sep  7 16:53:46 2016
@@ -0,0 +1,30 @@
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Interface for external signature creation scenarios. It contains method for retrieving
PDF data
+ * to be sign and setting created CMS signature to the PDF.
+ *
+ */
+public interface ExternalSigningSupport
+{
+    /**
+     * Get PDF content to be signed. Obtained InputStream must be closed after use.
+     *
+     * @return content stream
+     *
+     * @throws java.io.IOException
+     */
+    InputStream getContent() throws IOException;
+
+    /**
+     * Set CMS signature bytes to PDF.
+     *
+     * @param signature CMS signature as byte array
+     *
+     * @throws IOException if exception occured during PDF writing
+     */
+    void setSignature(byte[] signature) throws IOException;
+}

Propchange: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/ExternalSigningSupport.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SigningSupport.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SigningSupport.java?rev=1759639&view=auto
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SigningSupport.java
(added)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SigningSupport.java
Wed Sep  7 16:53:46 2016
@@ -0,0 +1,50 @@
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature;
+
+import org.apache.pdfbox.pdfwriter.COSWriter;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Class to be used when creating PDF signatures externally. COSWriter is used to obtain
data to be
+ * signed and set the resulted CMS signature.
+ *
+ */
+public class SigningSupport implements ExternalSigningSupport, Closeable
+{
+    private COSWriter cosWriter;
+
+    public SigningSupport(COSWriter cosWriter)
+    {
+        this.cosWriter = cosWriter;
+    }
+
+    @Override
+    public InputStream getContent() throws IOException
+    {
+        return cosWriter.getDataToSign();
+    }
+
+    @Override
+    public void setSignature(byte[] signature) throws IOException
+    {
+        cosWriter.writeExternalSignature(signature);
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        if (cosWriter != null)
+        {
+            try
+            {
+                cosWriter.close();
+            }
+            finally
+            {
+                cosWriter = null;
+            }
+        }
+    }
+}

Propchange: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SigningSupport.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message