xiongzhu 4 年之前
父節點
當前提交
7fbfc6b9f3
共有 26 個文件被更改,包括 1386 次插入1142 次删除
  1. 0 15
      o2server/pom.xml
  2. 3 4
      o2server/x_processplatform_assemble_surface/pom.xml
  3. 4 1
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDocToPDFWorkOrWorkCompleted.java
  4. 3 3
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CMSProcessableInputStream.java
  5. 34 23
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateEmbeddedTimeStamp.java
  6. 38 39
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateEmptySignatureForm.java
  7. 26 9
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateSignature.java
  8. 22 10
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateSignatureBase.java
  9. 17 11
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateSignedTimeStamp.java
  10. 58 0
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateSignedTimestampBase.java
  11. 145 172
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateVisibleSignature.java
  12. 334 282
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateVisibleSignature2.java
  13. 238 273
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/ShowSignature.java
  14. 50 85
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/SigUtils.java
  15. 63 28
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/TSAClient.java
  16. 18 15
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/ValidationTimeStamp.java
  17. 55 38
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/cert/CRLVerifier.java
  18. 1 1
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/cert/CertificateVerificationResult.java
  19. 80 32
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/cert/CertificateVerifier.java
  20. 45 22
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/cert/OcspHelper.java
  21. 107 45
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/validation/AddValidationInformation.java
  22. 38 28
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/validation/CertInformationCollector.java
  23. 7 6
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/validation/CertInformationHelper.java
  24. 二進制
      o2server/x_processplatform_assemble_surface/src/main/resources/myKeystore.p12
  25. 二進制
      o2server/x_processplatform_assemble_surface/src/main/resources/sig.jpg
  26. 二進制
      o2server/x_processplatform_assemble_surface/src/main/resources/sig.png

+ 0 - 15
o2server/pom.xml

@@ -1011,21 +1011,6 @@
 				<artifactId>zip4j</artifactId>
 				<version>2.3.2</version>
 			</dependency>
-			<dependency>
-				<groupId>org.apache.lucene</groupId>
-				<artifactId>lucene-core</artifactId>
-				<version>7.7.2</version>
-			</dependency>
-			<dependency>
-				<groupId>org.apache.lucene</groupId>
-				<artifactId>lucene-analyzers-common</artifactId>
-				<version>7.7.2</version>
-			</dependency>
-			<dependency>
-				<groupId>com.github.kevinsawicki</groupId>
-				<artifactId>http-request</artifactId>
-				<version>6.0</version>
-			</dependency>
 		</dependencies>
 	</dependencyManagement>
 	<repositories>

+ 3 - 4
o2server/x_processplatform_assemble_surface/pom.xml

@@ -51,35 +51,34 @@
 		<dependency>
 			<groupId>com.github.kevinsawicki</groupId>
 			<artifactId>http-request</artifactId>
+			<version>6.0</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.pdfbox</groupId>
 			<artifactId>pdfbox</artifactId>
-			<version>3.0.0-RC1</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.pdfbox</groupId>
 			<artifactId>pdfbox-tools</artifactId>
-			<version>3.0.0-RC1</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.pdfbox</groupId>
 			<artifactId>xmpbox</artifactId>
-			<version>3.0.0-RC1</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.pdfbox</groupId>
 			<artifactId>preflight</artifactId>
-			<version>3.0.0-RC1</version>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.lucene</groupId>
 			<artifactId>lucene-core</artifactId>
+			<version>7.7.2</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.lucene</groupId>
 			<artifactId>lucene-analyzers-common</artifactId>
+			<version>7.7.2</version>
 		</dependency>
 	</dependencies>
 	<build>

+ 4 - 1
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDocToPDFWorkOrWorkCompleted.java

@@ -278,7 +278,10 @@ class ActionDocToPDFWorkOrWorkCompleted extends BaseAction {
 
             CreateVisibleSignature2 signing = new CreateVisibleSignature2(keystore, password.toCharArray());
 
-            signing.setImage(ImageIO.read(getClass().getResourceAsStream("sig.jpg")));
+            File imgFile = TempFile.createTempFile("sig", ".png");
+            FileUtils.copyInputStreamToFile(getClass().getResourceAsStream("sig.png"), imgFile);
+
+            signing.setImageFile(imgFile);
             signing.setExternalSigning(false);
 
             // Set the signature rectangle

+ 3 - 3
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CMSProcessableInputStream.java

@@ -16,7 +16,6 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
-import org.apache.pdfbox.io.IOUtils;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.cms.CMSException;
@@ -25,6 +24,7 @@ import org.bouncycastle.cms.CMSTypedData;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import org.apache.pdfbox.io.IOUtils;
 
 /**
  * Wraps a InputStream into a CMSProcessable object for bouncy castle. It's a memory saving
@@ -35,7 +35,7 @@ import java.io.OutputStream;
  */
 class CMSProcessableInputStream implements CMSTypedData
 {
-    private final InputStream in;
+    private InputStream in;
     private final ASN1ObjectIdentifier contentType;
 
     CMSProcessableInputStream(InputStream is)
@@ -68,4 +68,4 @@ class CMSProcessableInputStream implements CMSTypedData
     {
         return contentType;
     }
-}
+}

+ 34 - 23
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateEmbeddedTimeStamp.java

@@ -17,18 +17,21 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
-import org.apache.pdfbox.Loader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
 import org.apache.pdfbox.util.Hex;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSSignedData;
 
-import java.io.*;
-import java.nio.file.Files;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-
 /**
  * An example for timestamp-signing a PDF for PADeS-Specification. The document will only be changed
  * in its existing signature by a signed timestamp (A timestamp and the Hash-Value of the document
@@ -80,37 +83,45 @@ public class CreateEmbeddedTimeStamp
         }
 
         // sign
-        try (PDDocument doc = Loader.loadPDF(inFile))
-        {
-            document = doc;
-            processTimeStamping(inFile, outFile);
-        }
+        PDDocument doc = PDDocument.load(inFile);
+        document = doc;
+        processTimeStamping(outFile, inFile.getAbsolutePath());
+        doc.close();
     }
 
     /**
-     * Processes the time-stamping of the signature.
-     * 
-     * @param inFile The existing PDF file
+     * Processes the time-stamping of the Signature.
+     *
      * @param outFile Where the new file will be written to
+     * @param fileName of the existing file containing the pdf
      * @throws IOException
      */
-    private void processTimeStamping(File inFile, File outFile) throws IOException
+    private void processTimeStamping(File outFile, String fileName) throws IOException
     {
         try
         {
-            byte[] documentBytes = Files.readAllBytes(inFile.toPath());
+            byte[] documentBytes;
+            FileInputStream fis = new FileInputStream(fileName);
+            documentBytes = IOUtils.toByteArray(fis);
+            fis.close();
             processRelevantSignatures(documentBytes);
 
-            if (changedEncodedSignature == null)
-            {
-                throw new IllegalStateException("No signature");
-            }
-            try (FileOutputStream output = new FileOutputStream(outFile))
+            if (changedEncodedSignature != null)
             {
+                FileOutputStream output = new FileOutputStream(outFile);
                 embedNewSignatureIntoDocument(documentBytes, output);
+                output.close();
             }
         }
-        catch (IOException | NoSuchAlgorithmException | CMSException e)
+        catch (IOException e)
+        {
+            throw new IOException(e);
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new IOException(e);
+        }
+        catch (CMSException e)
         {
             throw new IOException(e);
         }
@@ -191,7 +202,7 @@ public class CreateEmbeddedTimeStamp
         String tsaUrl = null;
         for (int i = 0; i < args.length; i++)
         {
-            if ("-tsa".equals(args[i]))
+            if (args[i].equals("-tsa"))
             {
                 i++;
                 if (i >= args.length)

+ 38 - 39
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateEmptySignatureForm.java

@@ -17,6 +17,7 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
+import java.io.IOException;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
@@ -28,8 +29,6 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
 import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
 import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
 
-import java.io.IOException;
-
 /**
  * An example of creating an AcroForm and an empty signature field from scratch.
  * 
@@ -45,46 +44,46 @@ public final class CreateEmptySignatureForm
     public static void main(String[] args) throws IOException
     {
         // Create a new document with an empty page.
-        try (PDDocument document = new PDDocument())
-        {
-            PDPage page = new PDPage(PDRectangle.A4);
-            document.addPage(page);
-
-            // Adobe Acrobat uses Helvetica as a default font and
-            // stores that under the name '/Helv' in the resources dictionary
-            PDFont font = PDType1Font.HELVETICA;
-            PDResources resources = new PDResources();
-            resources.put(COSName.getPDFName("Helv"), font);
-
-            // Add a new AcroForm and add that to the document
-            PDAcroForm acroForm = new PDAcroForm(document);
-            document.getDocumentCatalog().setAcroForm(acroForm);
-
-            // Add and set the resources and default appearance at the form level
-            acroForm.setDefaultResources(resources);
-
-            // Acrobat sets the font size on the form level to be
-            // auto sized as default. This is done by setting the font size to '0'
-            String defaultAppearanceString = "/Helv 0 Tf 0 g";
-            acroForm.setDefaultAppearance(defaultAppearanceString);
-            // --- end of general AcroForm stuff ---
-
-            // Create empty signature field, it will get the name "Signature1"
-            PDSignatureField signatureField = new PDSignatureField(acroForm);
-            PDAnnotationWidget widget = signatureField.getWidgets().get(0);
-            PDRectangle rect = new PDRectangle(50, 650, 200, 50);
-            widget.setRectangle(rect);
-            widget.setPage(page);
+        PDDocument document = new PDDocument();
+        PDPage page = new PDPage(PDRectangle.A4);
+        document.addPage(page);
+        
+        // Adobe Acrobat uses Helvetica as a default font and 
+        // stores that under the name '/Helv' in the resources dictionary
+        PDFont font = PDType1Font.HELVETICA;
+        PDResources resources = new PDResources();
+        resources.put(COSName.getPDFName("Helv"), font);
+        
+        // Add a new AcroForm and add that to the document
+        PDAcroForm acroForm = new PDAcroForm(document);
+        document.getDocumentCatalog().setAcroForm(acroForm);
+        
+        // Add and set the resources and default appearance at the form level
+        acroForm.setDefaultResources(resources);
+        
+        // Acrobat sets the font size on the form level to be
+        // auto sized as default. This is done by setting the font size to '0'
+        String defaultAppearanceString = "/Helv 0 Tf 0 g";
+        acroForm.setDefaultAppearance(defaultAppearanceString);
+        
+        // --- end of general AcroForm stuff ---
+        
+        // Create empty signature field, it will get the name "Signature1"
+        PDSignatureField signatureField = new PDSignatureField(acroForm);
+        PDAnnotationWidget widget = signatureField.getWidgets().get(0);
+        PDRectangle rect = new PDRectangle(50, 650, 200, 50);
+        widget.setRectangle(rect);
+        widget.setPage(page);
 
-            // see thread from PDFBox users mailing list 17.2.2021 - 19.2.2021
-            // https://mail-archives.apache.org/mod_mbox/pdfbox-users/202102.mbox/thread
-            widget.setPrinted(true);
+        // see thread from PDFBox users mailing list 17.2.2021 - 19.2.2021
+        // https://mail-archives.apache.org/mod_mbox/pdfbox-users/202102.mbox/thread
+        widget.setPrinted(true);
 
-            page.getAnnotations().add(widget);
+        page.getAnnotations().add(widget);
 
-            acroForm.getFields().add(signatureField);
+        acroForm.getFields().add(signatureField);        
 
-            document.save(args[0]);
-        }
+        document.save(args[0]);
+        document.close();
     }
 }

+ 26 - 9
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateSignature.java

@@ -16,17 +16,26 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
-import org.apache.pdfbox.Loader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+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.SignatureOptions;
 
-import java.io.*;
-import java.security.*;
-import java.security.cert.CertificateException;
-import java.util.Calendar;
-
 /**
  * An example for signing a PDF with bouncy castle.
  * A keystore can be created with the java keytool, for example:
@@ -92,15 +101,23 @@ public class CreateSignature extends CreateSignatureBase
         {
             throw new FileNotFoundException("Document for signing does not exist");
         }
-        
+
         setTsaUrl(tsaUrl);
 
+        FileOutputStream fos = new FileOutputStream(outFile);
+
         // sign
-        try (FileOutputStream fos = new FileOutputStream(outFile);
-                PDDocument doc = Loader.loadPDF(inFile))
+        PDDocument doc = null;
+        try
         {
+            doc = PDDocument.load(inFile);
             signDetached(doc, fos);
         }
+        finally
+        {
+            IOUtils.closeQuietly(doc);
+            IOUtils.closeQuietly(fos);
+        }
     }
 
     public void signDetached(PDDocument document, OutputStream output)

+ 22 - 10
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateSignatureBase.java

@@ -16,6 +16,19 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Enumeration;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
 import org.bouncycastle.cert.jcajce.JcaCertStore;
 import org.bouncycastle.cms.CMSException;
@@ -27,15 +40,6 @@ import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.*;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Enumeration;
-
 public abstract class CreateSignatureBase implements SignatureInterface
 {
     private PrivateKey privateKey;
@@ -144,7 +148,15 @@ public abstract class CreateSignatureBase implements SignatureInterface
             }
             return signedData.getEncoded();
         }
-        catch (GeneralSecurityException | CMSException | OperatorCreationException e)
+        catch (GeneralSecurityException e)
+        {
+            throw new IOException(e);
+        }
+        catch (CMSException e)
+        {
+            throw new IOException(e);
+        }
+        catch (OperatorCreationException e)
         {
             throw new IOException(e);
         }

+ 17 - 11
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateSignedTimeStamp.java

@@ -16,17 +16,22 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.pdfbox.Loader;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
 
-import java.io.*;
-import java.security.NoSuchAlgorithmException;
-
 /**
  * An example for timestamp-signing a PDF for PADeS-Specification. The document will be extended by
  * a signed TimeStamp (another kind of signature) (Signed TimeStamp and Hash-Value of the document
@@ -78,12 +83,13 @@ public class CreateSignedTimeStamp implements SignatureInterface
             throw new FileNotFoundException("Document for signing does not exist");
         }
 
+        FileOutputStream fos = new FileOutputStream(outFile);
+
         // sign
-        try (PDDocument doc = Loader.loadPDF(inFile);
-             FileOutputStream fos = new FileOutputStream(outFile))
-        {
-            signDetached(doc, fos);
-        }
+        PDDocument doc = PDDocument.load(inFile);
+        signDetached(doc, fos);
+        doc.close();
+        fos.close();
     }
 
     /**
@@ -136,7 +142,7 @@ public class CreateSignedTimeStamp implements SignatureInterface
         return new byte[] {};
     }
 
-    public static void main(String[] args) throws IOException
+    public static void main(String[] args) throws IOException, GeneralSecurityException
     {
         if (args.length != 3)
         {
@@ -145,7 +151,7 @@ public class CreateSignedTimeStamp implements SignatureInterface
         }
 
         String tsaUrl = null;
-        if ("-tsa".equals(args[1]))
+        if (args[1].equals("-tsa"))
         {
             tsaUrl = args[2];
         }

+ 58 - 0
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateSignedTimestampBase.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed 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 com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.pdfbox.io.IOUtils;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
+import org.bouncycastle.tsp.TimeStampToken;
+
+public abstract class CreateSignedTimestampBase implements SignatureInterface
+{
+    private TSAClient tsaClient;
+
+    public void setTsaClient(TSAClient tsaClient)
+    {
+        this.tsaClient = tsaClient;
+    }
+
+    public TSAClient getTsaClient()
+    {
+        return tsaClient;
+    }
+
+    /**
+     * SignatureInterface implementation.
+     *
+     * This method will be called from inside of the pdfbox and create the PKCS #7 signature. The given InputStream
+     * contains the bytes that are given by the byte range.
+     *
+     * This method is for internal use only.
+     *
+     * Use your favorite cryptographic library to implement PKCS #7 signature creation.
+     *
+     * @throws IOException
+     */
+    @Override
+    public byte[] sign(InputStream content) throws IOException
+    {
+        TimeStampToken timeStampToken = getTsaClient().getTimeStampToken(IOUtils.toByteArray(content));
+        return timeStampToken.getEncoded();
+    }
+}

+ 145 - 172
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateVisibleSignature.java

@@ -16,10 +16,21 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
-import org.apache.pdfbox.Loader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Calendar;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.io.IOUtils;
-import org.apache.pdfbox.io.MemoryUsageSetting;
+
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
@@ -31,14 +42,6 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
 import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
 import org.apache.pdfbox.util.Hex;
 
-import java.io.*;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
-import java.util.Calendar;
-
 /**
  * This is an example for visual signing a pdf.
 
@@ -51,8 +54,6 @@ public class CreateVisibleSignature extends CreateSignatureBase
     private PDVisibleSignDesigner visibleSignDesigner;
     private final PDVisibleSigProperties visibleSignatureProperties = new PDVisibleSigProperties();
     private boolean lateExternalSigning = false;
-    private MemoryUsageSetting memoryUsageSetting = MemoryUsageSetting.setupMainMemoryOnly();
-    private PDDocument doc = null;
 
     public boolean isLateExternalSigning()
     {
@@ -72,29 +73,9 @@ public class CreateVisibleSignature extends CreateSignatureBase
     }
 
     /**
-     * Get the memory usage setting.
-     *
-     * @return the memory usage setting.
-     */
-    public MemoryUsageSetting getMemoryUsageSetting()
-    {
-        return memoryUsageSetting;
-    }
-
-    /**
-     * Set the memory usage setting.
-     *
-     * @param memoryUsageSetting the memory usage setting.
-     */
-    public void setMemoryUsageSetting(MemoryUsageSetting memoryUsageSetting)
-    {
-        this.memoryUsageSetting = memoryUsageSetting;
-    }
-
-    /**
-     * Open the PDF, create and set the visible signature designer for a new signature field.
+     * Set visible signature designer for a new signature field.
      * 
-     * @param filename path of the PDF file
+     * @param filename
      * @param x position of the signature field
      * @param y position of the signature field
      * @param zoomPercent increase (positive value) or decrease (negative value) image with x percent.
@@ -106,11 +87,10 @@ public class CreateVisibleSignature extends CreateSignatureBase
             InputStream imageStream, int page) 
             throws IOException
     {
-        doc = Loader.loadPDF(new File(filename), memoryUsageSetting);
-        visibleSignDesigner = new PDVisibleSignDesigner(doc, imageStream, page);
+        visibleSignDesigner = new PDVisibleSignDesigner(filename, imageStream, page);
         visibleSignDesigner.xAxis(x).yAxis(y).zoom(zoomPercent).adjustForRotation();
     }
-
+    
     /**
      * Set visible signature designer for an existing signature field.
      * 
@@ -124,7 +104,7 @@ public class CreateVisibleSignature extends CreateSignatureBase
         visibleSignDesigner = new PDVisibleSignDesigner(imageStream);
         visibleSignDesigner.zoom(zoomPercent);
     }
-
+    
     /**
      * Set visible signature properties for new signature fields.
      * 
@@ -142,7 +122,7 @@ public class CreateVisibleSignature extends CreateSignatureBase
                 preferredSize(preferredSize).page(page).visualSignEnabled(visualSignEnabled).
                 setPdVisibleSignature(visibleSignDesigner);
     }
-
+    
     /**
      * Set visible signature properties for existing signature fields.
      * 
@@ -192,8 +172,7 @@ public class CreateVisibleSignature extends CreateSignatureBase
     /**
      * Sign pdf file and create new file that ends with "_signed.pdf".
      *
-     * @param inputFile The source pdf document file. It will be opened if it hasn't been opened
-     * before in {@link #setVisibleSignDesigner(String, int, int, int, InputStream, int)}.
+     * @param inputFile The source pdf document file.
      * @param signedFile The file to be signed.
      * @param tsaUrl optional TSA url
      * @param signatureFieldName optional name of an existing (unsigned) signature field
@@ -209,147 +188,143 @@ public class CreateVisibleSignature extends CreateSignatureBase
         setTsaUrl(tsaUrl);
 
         // creating output document and prepare the IO streams.
-        if (doc == null)
+        FileOutputStream fos = new FileOutputStream(signedFile);
+
+        // load document
+        PDDocument doc = PDDocument.load(inputFile);
+
+        int accessPermissions = SigUtils.getMDPPermission(doc);
+        if (accessPermissions == 1)
         {
-            doc = Loader.loadPDF(inputFile, memoryUsageSetting);
+            throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
         }
+        // Note that PDFBox has a bug that visual signing on certified files with permission 2
+        // doesn't work properly, see PDFBOX-3699. As long as this issue is open, you may want to 
+        // be careful with such files.        
 
-        try (FileOutputStream fos = new FileOutputStream(signedFile))
-        {
-            int accessPermissions = SigUtils.getMDPPermission(doc);
-            if (accessPermissions == 1)
-            {
-                throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
-            }
-            // Note that PDFBox has a bug that visual signing on certified files with permission 2
-            // doesn't work properly, see PDFBOX-3699. As long as this issue is open, you may want to
-            // be careful with such files.
+        PDSignature signature;
 
-            PDSignature signature;
+        // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example. 
+        signature = findExistingSignature(doc, signatureFieldName);
 
-            // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example.
-            signature = findExistingSignature(doc, signatureFieldName);
+        if (signature == null)
+        {
+            // create signature dictionary
+            signature = new PDSignature();
+        }
 
-            if (signature == null)
-            {
-                // create signature dictionary
-                signature = new PDSignature();
-            }
+        // Optional: certify
+        // can be done only if version is at least 1.5 and if not already set
+        // doing this on a PDF/A-1b file fails validation by Adobe preflight (PDFBOX-3821)
+        // PDF/A-1b requires PDF version 1.4 max, so don't increase the version on such files.
+        if (doc.getVersion() >= 1.5f && accessPermissions == 0)
+        {
+            SigUtils.setMDPPermission(doc, signature, 2);
+        }
 
-            // Optional: certify
-            // can be done only if version is at least 1.5 and if not already set
-            // doing this on a PDF/A-1b file fails validation by Adobe preflight (PDFBOX-3821)
-            // PDF/A-1b requires PDF version 1.4 max, so don't increase the version on such files.
-            if (doc.getVersion() >= 1.5f && accessPermissions == 0)
+        PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(null);
+        if (acroForm != null && acroForm.getNeedAppearances())
+        {
+            // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible 
+            // with Adobe Reader
+            if (acroForm.getFields().isEmpty())
             {
-                SigUtils.setMDPPermission(doc, signature, 2);
+                // we can safely delete it if there are no fields
+                acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
+                // note that if you've set MDP permissions, the removal of this item
+                // may result in Adobe Reader claiming that the document has been changed.
+                // and/or that field content won't be displayed properly.
+                // ==> decide what you prefer and adjust your code accordingly.
             }
-
-            PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(null);
-            if (acroForm != null && acroForm.getNeedAppearances())
+            else
             {
-                // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible
-                // with Adobe Reader
-                if (acroForm.getFields().isEmpty())
-                {
-                    // we can safely delete it if there are no fields
-                    acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
-                    // note that if you've set MDP permissions, the removal of this item
-                    // may result in Adobe Reader claiming that the document has been changed.
-                    // and/or that field content won't be displayed properly.
-                    // ==> decide what you prefer and adjust your code accordingly.
-                }
-                else
-                {
-                    System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
-                }
+                System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
             }
+        }
 
-            // default filter
-            signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
-
-            // subfilter for basic and PAdES Part 2 signatures
-            signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
-
-            if (visibleSignatureProperties != null)
-            {
-                // this builds the signature structures in a separate document
-                visibleSignatureProperties.buildSignature();
+        // default filter
+        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
+        
+        // subfilter for basic and PAdES Part 2 signatures
+        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
+        
+        if (visibleSignatureProperties != null)
+        {
+            // this builds the signature structures in a separate document
+            visibleSignatureProperties.buildSignature();
 
-                signature.setName(visibleSignatureProperties.getSignerName());
-                signature.setLocation(visibleSignatureProperties.getSignerLocation());
-                signature.setReason(visibleSignatureProperties.getSignatureReason());
-            }
+            signature.setName(visibleSignatureProperties.getSignerName());
+            signature.setLocation(visibleSignatureProperties.getSignerLocation());
+            signature.setReason(visibleSignatureProperties.getSignatureReason());
+        }
 
-            // the signing date, needed for valid signature
-            signature.setSignDate(Calendar.getInstance());
+        // 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;
+        // 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, signatureInterface, signatureOptions);
-            }
-            else
-            {
-                doc.addSignature(signature, signatureInterface);
-            }
+        // 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, signatureInterface, signatureOptions);
+        }
+        else
+        {
+            doc.addSignature(signature, signatureInterface);
+        }
 
-            if (isExternalSigning())
+        if (isExternalSigning())
+        {
+            ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
+            // invoke external signature service
+            byte[] cmsSignature = sign(externalSigning.getContent());
+
+            // Explanation of late external signing (off by default):
+            // If you want to add the signature in a separate step, then set an empty byte array
+            // and call signature.getByteRange() and remember the offset signature.getByteRange()[1]+1.
+            // you can write the ascii hex signature at a later time even if you don't have this
+            // PDDocument object anymore, with classic java file random access methods.
+            // If you can't remember the offset value from ByteRange because your context has changed, 
+            // then open the file with PDFBox, find the field with findExistingSignature() or
+            // PDDocument.getLastSignatureDictionary() and get the ByteRange from there.
+            // Close the file and then write the signature as explained earlier in this comment.
+            if (isLateExternalSigning())
             {
-                ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
-                // invoke external signature service
-                byte[] cmsSignature = sign(externalSigning.getContent());
-
-                // Explanation of late external signing (off by default):
-                // If you want to add the signature in a separate step, then set an empty byte array
-                // and call signature.getByteRange() and remember the offset signature.getByteRange()[1]+1.
-                // you can write the ascii hex signature at a later time even if you don't have this
-                // PDDocument object anymore, with classic java file random access methods.
-                // If you can't remember the offset value from ByteRange because your context has changed,
-                // then open the file with PDFBox, find the field with findExistingSignature() or
-                // PDDocument.getLastSignatureDictionary() and get the ByteRange from there.
-                // Close the file and then write the signature as explained earlier in this comment.
-                if (isLateExternalSigning())
-                {
-                    // this saves the file with a 0 signature
-                    externalSigning.setSignature(new byte[0]);
-
-                    // remember the offset (add 1 because of "<")
-                    int offset = signature.getByteRange()[1] + 1;
-
-                    // now write the signature at the correct offset without any PDFBox methods
-                    try (RandomAccessFile raf = new RandomAccessFile(signedFile, "rw"))
-                    {
-                        raf.seek(offset);
-                        raf.write(Hex.getBytes(cmsSignature));
-                    }
-                }
-                else
-                {
-                    // set signature bytes received from the service and save the file
-                    externalSigning.setSignature(cmsSignature);
-                }
+                // this saves the file with a 0 signature
+                externalSigning.setSignature(new byte[0]);
+                
+                // remember the offset (add 1 because of "<")
+                int offset = signature.getByteRange()[1] + 1;
+
+                // now write the signature at the correct offset without any PDFBox methods
+                RandomAccessFile raf = new RandomAccessFile(signedFile, "rw");
+                raf.seek(offset);
+                raf.write(Hex.getBytes(cmsSignature));
+                raf.close();
             }
             else
             {
-                // write incremental (only for signing purpose)
-                doc.saveIncremental(fos);
+                // set signature bytes received from the service and save the file
+                externalSigning.setSignature(cmsSignature);
             }
         }
-
+        else
+        {
+            // write incremental (only for signing purpose)
+            doc.saveIncremental(fos);
+        }
+        doc.close();
+        
         // Do not close signatureOptions before saving, because some COSStream objects within
         // are transferred to the signed document.
         // Do not allow signatureOptions get out of scope before saving, because then the COSDocument
         // in signature options might by closed by gc, which would close COSStream objects prematurely.
         // See https://issues.apache.org/jira/browse/PDFBOX-3743
         IOUtils.closeQuietly(signatureOptions);
-        IOUtils.closeQuietly(doc);
     }
 
     // Find an existing signature (assumed to be empty). You will usually not need this.
@@ -390,11 +365,11 @@ public class CreateVisibleSignature extends CreateSignatureBase
      * [3] image of visible signature
      *
      * @param args
-     * @throws KeyStoreException
-     * @throws CertificateException
-     * @throws IOException
-     * @throws NoSuchAlgorithmException
-     * @throws UnrecoverableKeyException
+     * @throws java.security.KeyStoreException
+     * @throws java.security.cert.CertificateException
+     * @throws java.io.IOException
+     * @throws java.security.NoSuchAlgorithmException
+     * @throws java.security.UnrecoverableKeyException
      */
     public static void main(String[] args) throws KeyStoreException, CertificateException,
             IOException, NoSuchAlgorithmException, UnrecoverableKeyException
@@ -413,7 +388,7 @@ public class CreateVisibleSignature extends CreateSignatureBase
         boolean externalSig = false;
         for (int i = 0; i < args.length; i++)
         {
-            if ("-tsa".equals(args[i]))
+            if (args[i].equals("-tsa"))
             {
                 i++;
                 if (i >= args.length)
@@ -423,7 +398,7 @@ public class CreateVisibleSignature extends CreateSignatureBase
                 }
                 tsaUrl = args[i];
             }
-            if ("-e".equals(args[i]))
+            if (args[i].equals("-e"))
             {
                 externalSig = true;
             }
@@ -438,17 +413,16 @@ public class CreateVisibleSignature extends CreateSignatureBase
 
         CreateVisibleSignature signing = new CreateVisibleSignature(keystore, pin.clone());
 
-        File signedDocumentFile;
-        int page;
-        try (InputStream imageStream = new FileInputStream(args[3]))
-        {
-            String name = documentFile.getName();
-            String substring = name.substring(0, name.lastIndexOf('.'));
-            signedDocumentFile = new File(documentFile.getParent(), substring + "_signed.pdf");
-            // page is 1-based here
-            page = 1;
-            signing.setVisibleSignDesigner(args[2], 0, 0, -50, imageStream, page);
-        }
+        InputStream imageStream = new FileInputStream(args[3]);
+
+        String name = documentFile.getName();
+        String substring = name.substring(0, name.lastIndexOf('.'));
+        File signedDocumentFile = new File(documentFile.getParent(), substring + "_signed.pdf");
+
+        // page is 1-based here
+        int page = 1;
+        signing.setVisibleSignDesigner(args[2], 0, 0, -50, imageStream, page);
+        imageStream.close();
         signing.setVisibleSignatureProperties("name", "location", "Security", 0, page, true);
         signing.setExternalSigning(externalSig);
         signing.signPDF(documentFile, signedDocumentFile, tsaUrl);
@@ -465,5 +439,4 @@ public class CreateVisibleSignature extends CreateSignatureBase
                            "  -tsa <url>    sign timestamp using the given TSA server\n"+
                            "  -e            sign using external signature creation scenario");
     }
-
 }

+ 334 - 282
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/CreateVisibleSignature2.java

@@ -16,7 +16,26 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
-import org.apache.pdfbox.Loader;
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.List;
+
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.PDDocument;
@@ -41,19 +60,10 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDField;
 import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
 import org.apache.pdfbox.util.Hex;
 import org.apache.pdfbox.util.Matrix;
-
-import javax.imageio.ImageIO;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.io.*;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
-import java.util.Calendar;
-import java.util.List;
+import org.bouncycastle.asn1.x500.RDN;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x500.style.IETFUtils;
 
 /**
  * This is a second example for visual signing a pdf. It doesn't use the "design pattern" influenced
@@ -66,37 +76,42 @@ import java.util.List;
  * @author Vakhtang Koroghlishvili
  * @author Tilman Hausherr
  */
-public class CreateVisibleSignature2 extends CreateSignatureBase {
+public class CreateVisibleSignature2 extends CreateSignatureBase
+{
     private SignatureOptions signatureOptions;
-    private boolean          lateExternalSigning = false;
-    private BufferedImage    image               = null;
+    private boolean lateExternalSigning = false;
+    private File imageFile = null;
 
     /**
      * Initialize the signature creator with a keystore (pkcs12) and pin that
      * should be used for the signature.
      *
      * @param keystore is a pkcs12 keystore.
-     * @param pin      is the pin for the keystore / private key
-     * @throws KeyStoreException         if the keystore has not been initialized (loaded)
-     * @throws NoSuchAlgorithmException  if the algorithm for recovering the key cannot be found
+     * @param pin is the pin for the keystore / private key
+     * @throws KeyStoreException if the keystore has not been initialized (loaded)
+     * @throws NoSuchAlgorithmException if the algorithm for recovering the key cannot be found
      * @throws UnrecoverableKeyException if the given password is wrong
-     * @throws CertificateException      if the certificate is not valid as signing time
-     * @throws IOException               if no certificate could be found
+     * @throws CertificateException if the certificate is not valid as signing time
+     * @throws IOException if no certificate could be found
      */
     public CreateVisibleSignature2(KeyStore keystore, char[] pin)
-            throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException {
+            throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException
+    {
         super(keystore, pin);
     }
 
-    public BufferedImage getImage() {
-        return image;
+    public File getImageFile()
+    {
+        return imageFile;
     }
 
-    public void setImage(BufferedImage image) {
-        this.image = image;
+    public void setImageFile(File imageFile)
+    {
+        this.imageFile = imageFile;
     }
 
-    public boolean isLateExternalSigning() {
+    public boolean isLateExternalSigning()
+    {
         return lateExternalSigning;
     }
 
@@ -107,153 +122,174 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
      *
      * @param lateExternalSigning
      */
-    public void setLateExternalSigning(boolean lateExternalSigning) {
+    public void setLateExternalSigning(boolean lateExternalSigning)
+    {
         this.lateExternalSigning = lateExternalSigning;
     }
 
     /**
      * Sign pdf file and create new file that ends with "_signed.pdf".
      *
-     * @param inputFile  The source pdf document file.
+     * @param inputFile The source pdf document file.
      * @param signedFile The file to be signed.
-     * @param humanRect  rectangle from a human viewpoint (coordinates start at top left)
-     * @param tsaUrl     optional TSA url
+     * @param humanRect rectangle from a human viewpoint (coordinates start at top left)
+     * @param tsaUrl optional TSA url
      * @throws IOException
      */
-    public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl) throws IOException {
+    public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl) throws IOException
+    {
         this.signPDF(inputFile, signedFile, humanRect, tsaUrl, null);
     }
 
     /**
      * Sign pdf file and create new file that ends with "_signed.pdf".
      *
-     * @param inputFile          The source pdf document file.
-     * @param signedFile         The file to be signed.
-     * @param humanRect          rectangle from a human viewpoint (coordinates start at top left)
-     * @param tsaUrl             optional TSA url
+     * @param inputFile The source pdf document file.
+     * @param signedFile The file to be signed.
+     * @param humanRect rectangle from a human viewpoint (coordinates start at top left)
+     * @param tsaUrl optional TSA url
      * @param signatureFieldName optional name of an existing (unsigned) signature field
      * @throws IOException
      */
-    public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl, String signatureFieldName) throws IOException {
-        if (inputFile == null || !inputFile.exists()) {
+    public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl, String signatureFieldName) throws IOException
+    {
+        if (inputFile == null || !inputFile.exists())
+        {
             throw new IOException("Document for signing does not exist");
         }
 
         setTsaUrl(tsaUrl);
 
         // creating output document and prepare the IO streams.
-        try (FileOutputStream fos = new FileOutputStream(signedFile);
-             PDDocument doc = Loader.loadPDF(inputFile)) {
-            int accessPermissions = SigUtils.getMDPPermission(doc);
-            if (accessPermissions == 1) {
-                throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
-            }
-            // Note that PDFBox has a bug that visual signing on certified files with permission 2
-            // doesn't work properly, see PDFBOX-3699. As long as this issue is open, you may want to
-            // be careful with such files.
-
-            PDSignature signature = null;
-            PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(null);
-            PDRectangle rect = null;
-
-            // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example.
-            if (acroForm != null) {
-                signature = findExistingSignature(acroForm, signatureFieldName);
-                if (signature != null) {
-                    rect = acroForm.getField(signatureFieldName).getWidgets().get(0).getRectangle();
-                }
-            }
+        FileOutputStream fos = new FileOutputStream(signedFile);
 
-            if (signature == null) {
-                // create signature dictionary
-                signature = new PDSignature();
-            }
+        PDDocument doc = PDDocument.load(inputFile);
+        int accessPermissions = SigUtils.getMDPPermission(doc);
+        if (accessPermissions == 1)
+        {
+            throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
+        }
+        // Note that PDFBox has a bug that visual signing on certified files with permission 2
+        // doesn't work properly, see PDFBOX-3699. As long as this issue is open, you may want to
+        // be careful with such files.
 
-            if (rect == null) {
-                rect = createSignatureRectangle(doc, humanRect);
+        PDSignature signature = null;
+        PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(null);
+        PDRectangle rect = null;
+
+        // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example.
+        if (acroForm != null)
+        {
+            signature = findExistingSignature(acroForm, signatureFieldName);
+            if (signature != null)
+            {
+                rect = acroForm.getField(signatureFieldName).getWidgets().get(0).getRectangle();
             }
+        }
 
-            // Optional: certify
-            // can be done only if version is at least 1.5 and if not already set
-            // doing this on a PDF/A-1b file fails validation by Adobe preflight (PDFBOX-3821)
-            // PDF/A-1b requires PDF version 1.4 max, so don't increase the version on such files.
-            if (doc.getVersion() >= 1.5f && accessPermissions == 0) {
-                SigUtils.setMDPPermission(doc, signature, 2);
-            }
+        if (signature == null)
+        {
+            // create signature dictionary
+            signature = new PDSignature();
+        }
 
-            if (acroForm != null && acroForm.getNeedAppearances()) {
-                // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible 
-                // with Adobe Reader
-                if (acroForm.getFields().isEmpty()) {
-                    // we can safely delete it if there are no fields
-                    acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
-                    // note that if you've set MDP permissions, the removal of this item
-                    // may result in Adobe Reader claiming that the document has been changed.
-                    // and/or that field content won't be displayed properly.
-                    // ==> decide what you prefer and adjust your code accordingly.
-                } else {
-                    System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
-                }
-            }
+        if (rect == null)
+        {
+            rect = createSignatureRectangle(doc, humanRect);
+        }
 
-            // default filter
-            signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
-
-            // subfilter for basic and PAdES Part 2 signatures
-            signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
-
-            signature.setName("Name");
-            signature.setLocation("Location");
-            signature.setReason("Reason");
-
-            // 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
-            signatureOptions = new SignatureOptions();
-            signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect, signature));
-            signatureOptions.setPage(doc.getNumberOfPages());
-            doc.addSignature(signature, signatureInterface, signatureOptions);
-
-            if (isExternalSigning()) {
-                ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
-                // invoke external signature service
-                byte[] cmsSignature = sign(externalSigning.getContent());
-
-                // Explanation of late external signing (off by default):
-                // If you want to add the signature in a separate step, then set an empty byte array
-                // and call signature.getByteRange() and remember the offset signature.getByteRange()[1]+1.
-                // you can write the ascii hex signature at a later time even if you don't have this
-                // PDDocument object anymore, with classic java file random access methods.
-                // If you can't remember the offset value from ByteRange because your context has changed,
-                // then open the file with PDFBox, find the field with findExistingSignature() or
-                // PDDocument.getLastSignatureDictionary() and get the ByteRange from there.
-                // Close the file and then write the signature as explained earlier in this comment.
-                if (isLateExternalSigning()) {
-                    // this saves the file with a 0 signature
-                    externalSigning.setSignature(new byte[0]);
-
-                    // remember the offset (add 1 because of "<")
-                    int offset = signature.getByteRange()[1] + 1;
-
-                    // now write the signature at the correct offset without any PDFBox methods
-                    try (RandomAccessFile raf = new RandomAccessFile(signedFile, "rw")) {
-                        raf.seek(offset);
-                        raf.write(Hex.getBytes(cmsSignature));
-                    }
-                } else {
-                    // set signature bytes received from the service and save the file
-                    externalSigning.setSignature(cmsSignature);
-                }
-            } else {
-                // write incremental (only for signing purpose)
-                doc.saveIncremental(fos);
+        // Optional: certify
+        // can be done only if version is at least 1.5 and if not already set
+        // doing this on a PDF/A-1b file fails validation by Adobe preflight (PDFBOX-3821)
+        // PDF/A-1b requires PDF version 1.4 max, so don't increase the version on such files.
+        if (doc.getVersion() >= 1.5f && accessPermissions == 0)
+        {
+            SigUtils.setMDPPermission(doc, signature, 2);
+        }
+
+        if (acroForm != null && acroForm.getNeedAppearances())
+        {
+            // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible 
+            // with Adobe Reader
+            if (acroForm.getFields().isEmpty())
+            {
+                // we can safely delete it if there are no fields
+                acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
+                // note that if you've set MDP permissions, the removal of this item
+                // may result in Adobe Reader claiming that the document has been changed.
+                // and/or that field content won't be displayed properly.
+                // ==> decide what you prefer and adjust your code accordingly.
+            }
+            else
+            {
+                System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
             }
         }
 
+        // default filter
+        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
+
+        // subfilter for basic and PAdES Part 2 signatures
+        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
+
+        signature.setName("Name");
+        signature.setLocation("Location");
+        signature.setReason("Reason");
+
+        // 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
+        signatureOptions = new SignatureOptions();
+        signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect, signature));
+        signatureOptions.setPage(0);
+        doc.addSignature(signature, signatureInterface, signatureOptions);
+
+        if (isExternalSigning())
+        {
+            ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
+            // invoke external signature service
+            byte[] cmsSignature = sign(externalSigning.getContent());
+
+            // Explanation of late external signing (off by default):
+            // If you want to add the signature in a separate step, then set an empty byte array
+            // and call signature.getByteRange() and remember the offset signature.getByteRange()[1]+1.
+            // you can write the ascii hex signature at a later time even if you don't have this
+            // PDDocument object anymore, with classic java file random access methods.
+            // If you can't remember the offset value from ByteRange because your context has changed,
+            // then open the file with PDFBox, find the field with findExistingSignature() or
+            // PDDocument.getLastSignatureDictionary() and get the ByteRange from there.
+            // Close the file and then write the signature as explained earlier in this comment.
+            if (isLateExternalSigning())
+            {
+                // this saves the file with a 0 signature
+                externalSigning.setSignature(new byte[0]);
+
+                // remember the offset (add 1 because of "<")
+                int offset = signature.getByteRange()[1] + 1;
+
+                // now write the signature at the correct offset without any PDFBox methods
+                RandomAccessFile raf = new RandomAccessFile(signedFile, "rw");
+                raf.seek(offset);
+                raf.write(Hex.getBytes(cmsSignature));
+                raf.close();
+            }
+            else
+            {
+                // set signature bytes received from the service and save the file
+                externalSigning.setSignature(cmsSignature);
+            }
+        }
+        else
+        {
+            // write incremental (only for signing purpose)
+            doc.saveIncremental(fos);
+        }
+        doc.close();
+        
         // Do not close signatureOptions before saving, because some COSStream objects within
         // are transferred to the signed document.
         // Do not allow signatureOptions get out of scope before saving, because then the COSDocument
@@ -262,7 +298,8 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
         IOUtils.closeQuietly(signatureOptions);
     }
 
-    private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect) {
+    private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect)
+    {
         float x = (float) humanRect.getX();
         float y = (float) humanRect.getY();
         float width = (float) humanRect.getWidth();
@@ -271,7 +308,8 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
         PDRectangle pageRect = page.getCropBox();
         PDRectangle rect = new PDRectangle();
         // signing should be at the same position regardless of page rotation.
-        switch (page.getRotation()) {
+        switch (page.getRotation())
+        {
             case 90:
                 rect.setLowerLeftY(x);
                 rect.setUpperRightY(x + width);
@@ -303,142 +341,148 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
 
     // create a template PDF document with empty signature and return it as a stream.
     private InputStream createVisualSignatureTemplate(PDDocument srcDoc, int pageNum,
-                                                      PDRectangle rect, PDSignature signature) throws IOException {
-        try (PDDocument doc = new PDDocument()) {
-            PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
-            doc.addPage(page);
-            PDAcroForm acroForm = new PDAcroForm(doc);
-            doc.getDocumentCatalog().setAcroForm(acroForm);
-            PDSignatureField signatureField = new PDSignatureField(acroForm);
-            PDAnnotationWidget widget = signatureField.getWidgets().get(0);
-            List<PDField> acroFormFields = acroForm.getFields();
-            acroForm.setSignaturesExist(true);
-            acroForm.setAppendOnly(true);
-            acroForm.getCOSObject().setDirect(true);
-            acroFormFields.add(signatureField);
-
-            widget.setRectangle(rect);
-
-            // from PDVisualSigBuilder.createHolderForm()
-            PDStream stream = new PDStream(doc);
-            PDFormXObject form = new PDFormXObject(stream);
-            PDResources res = new PDResources();
-            form.setResources(res);
-            form.setFormType(1);
-            PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
-            float height = bbox.getHeight();
-            Matrix initialScale = null;
-            switch (srcDoc.getPage(pageNum).getRotation()) {
-                case 90:
-                    form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
-                    initialScale = Matrix
-                            .getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
-                    height = bbox.getWidth();
-                    break;
-                case 180:
-                    form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
-                    break;
-                case 270:
-                    form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
-                    initialScale = Matrix
-                            .getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
-                    height = bbox.getWidth();
-                    break;
-                case 0:
-                default:
-                    break;
-            }
-            form.setBBox(bbox);
-            PDFont font = PDType1Font.HELVETICA_BOLD;
-
-            // from PDVisualSigBuilder.createAppearanceDictionary()
-            PDAppearanceDictionary appearance = new PDAppearanceDictionary();
-            appearance.getCOSObject().setDirect(true);
-            PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
-            appearance.setNormalAppearance(appearanceStream);
-            widget.setAppearance(appearance);
-
-            try (PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream)) {
-                // for 90° and 270° scale ratio of width / height
-                // not really sure about this
-                // why does scale have no effect when done in the form matrix???
-                if (initialScale != null) {
-                    cs.transform(initialScale);
-                }
-
-                // show background (just for debugging, to see the rect size + position)
-//                cs.setNonStrokingColor(Color.yellow);
-//                cs.addRect(-5000, -5000, 10000, 10000);
-//                cs.fill();
-
-                if (image != null) {
-                    // show background image
-                    // save and restore graphics if the image is too large and needs to be scaled
-                    cs.saveGraphicsState();
-                    cs.transform(Matrix
-                            .getScaleInstance(rect.getHeight() / (float) image.getHeight(), rect
-                                    .getHeight() / (float) image.getHeight()));
-                    ByteArrayOutputStream out = new ByteArrayOutputStream();
-                    ImageIO.write(image, "png", out);
-                    PDImageXObject img = PDImageXObject.createFromByteArray(doc, out.toByteArray(), "sign.png");
-                    cs.drawImage(img, 0, 0);
-                    cs.restoreGraphicsState();
-                }
-
-                // show text
-                float fontSize = 10;
-                float leading = fontSize * 1.5f;
-//                cs.beginText();
-//                cs.setFont(font, fontSize);
-//                cs.setNonStrokingColor(Color.black);
-//                cs.newLineAtOffset(fontSize, height - leading);
-//                cs.setLeading(leading);
-//
-//                X509Certificate cert = (X509Certificate) getCertificateChain()[0];
-//
-//                // https://stackoverflow.com/questions/2914521/
-//                X500Name x500Name = new X500Name(cert.getSubjectX500Principal().getName());
-//                RDN cn = x500Name.getRDNs(BCStyle.CN)[0];
-//                String name = IETFUtils.valueToString(cn.getFirst().getValue());
-//
-//                // See https://stackoverflow.com/questions/12575990
-//                // for better date formatting
-//                String date = signature.getSignDate().getTime().toString();
-//                String reason = signature.getReason();
-//
-//                cs.showText("Signer: " + name);
-//                cs.newLine();
-//                cs.showText(date);
-//                cs.newLine();
-//                cs.showText("Reason: " + reason);
-//
-//                cs.endText();
-            }
-
-            // no need to set annotations and /P entry
+            PDRectangle rect, PDSignature signature) throws IOException
+    {
+        PDDocument doc = new PDDocument();
+
+        PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
+        doc.addPage(page);
+        PDAcroForm acroForm = new PDAcroForm(doc);
+        doc.getDocumentCatalog().setAcroForm(acroForm);
+        PDSignatureField signatureField = new PDSignatureField(acroForm);
+        PDAnnotationWidget widget = signatureField.getWidgets().get(0);
+        List<PDField> acroFormFields = acroForm.getFields();
+        acroForm.setSignaturesExist(true);
+        acroForm.setAppendOnly(true);
+        acroForm.getCOSObject().setDirect(true);
+        acroFormFields.add(signatureField);
+
+        widget.setRectangle(rect);
+
+        // from PDVisualSigBuilder.createHolderForm()
+        PDStream stream = new PDStream(doc);
+        PDFormXObject form = new PDFormXObject(stream);
+        PDResources res = new PDResources();
+        form.setResources(res);
+        form.setFormType(1);
+        PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
+        float height = bbox.getHeight();
+        Matrix initialScale = null;
+        switch (srcDoc.getPage(pageNum).getRotation())
+        {
+            case 90:
+                form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
+                initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
+                height = bbox.getWidth();
+                break;
+            case 180:
+                form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
+                break;
+            case 270:
+                form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
+                initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
+                height = bbox.getWidth();
+                break;
+            case 0:
+            default:
+                break;
+        }
+        form.setBBox(bbox);
+        PDFont font = PDType1Font.HELVETICA_BOLD;
+
+        // from PDVisualSigBuilder.createAppearanceDictionary()
+        PDAppearanceDictionary appearance = new PDAppearanceDictionary();
+        appearance.getCOSObject().setDirect(true);
+        PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
+        appearance.setNormalAppearance(appearanceStream);
+        widget.setAppearance(appearance);
+
+        PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream);
+
+        // for 90° and 270° scale ratio of width / height
+        // not really sure about this
+        // why does scale have no effect when done in the form matrix???
+        if (initialScale != null)
+        {
+            cs.transform(initialScale);
+        }
 
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            doc.save(baos);
-            return new ByteArrayInputStream(baos.toByteArray());
+        // show background (just for debugging, to see the rect size + position)
+        cs.setNonStrokingColor(Color.yellow);
+        cs.addRect(-5000, -5000, 10000, 10000);
+        cs.fill();
+
+        if (imageFile != null)
+        {
+            // show background image
+            // save and restore graphics if the image is too large and needs to be scaled
+            cs.saveGraphicsState();
+            cs.transform(Matrix.getScaleInstance(0.25f, 0.25f));
+            PDImageXObject img = PDImageXObject.createFromFileByExtension(imageFile, doc);
+            cs.drawImage(img, 0, 0);
+            cs.restoreGraphicsState();
         }
+
+        // show text
+        float fontSize = 10;
+        float leading = fontSize * 1.5f;
+        cs.beginText();
+        cs.setFont(font, fontSize);
+        cs.setNonStrokingColor(Color.black);
+        cs.newLineAtOffset(fontSize, height - leading);
+        cs.setLeading(leading);
+
+        X509Certificate cert = (X509Certificate) getCertificateChain()[0];
+
+        // https://stackoverflow.com/questions/2914521/
+        X500Name x500Name = new X500Name(cert.getSubjectX500Principal().getName());
+        RDN cn = x500Name.getRDNs(BCStyle.CN)[0];
+        String name = IETFUtils.valueToString(cn.getFirst().getValue());
+
+        // See https://stackoverflow.com/questions/12575990
+        // for better date formatting
+        String date = signature.getSignDate().getTime().toString();
+        String reason = signature.getReason();
+
+        cs.showText("Signer: " + name);
+        cs.newLine();
+        cs.showText(date);
+        cs.newLine();
+        cs.showText("Reason: " + reason);
+
+        cs.endText();
+
+        cs.close();
+
+        // no need to set annotations and /P entry
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        doc.save(baos);
+        doc.close();
+        return new ByteArrayInputStream(baos.toByteArray());
     }
 
     // Find an existing signature (assumed to be empty). You will usually not need this.
-    private PDSignature findExistingSignature(PDAcroForm acroForm, String sigFieldName) {
+    private PDSignature findExistingSignature(PDAcroForm acroForm, String sigFieldName)
+    {
         PDSignature signature = null;
         PDSignatureField signatureField;
-        if (acroForm != null) {
+        if (acroForm != null)
+        {
             signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
-            if (signatureField != null) {
+            if (signatureField != null)
+            {
                 // retrieve signature dictionary
                 signature = signatureField.getSignature();
-                if (signature == null) {
+                if (signature == null)
+                {
                     signature = new PDSignature();
                     // after solving PDFBOX-3524
                     // signatureField.setValue(signature)
                     // until then:
                     signatureField.getCOSObject().setItem(COSName.V, signature);
-                } else {
+                }
+                else
+                {
                     throw new IllegalStateException("The signature field " + sigFieldName + " is already signed.");
                 }
             }
@@ -454,15 +498,17 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
      * [3] image of visible signature
      *
      * @param args
-     * @throws KeyStoreException
-     * @throws CertificateException
-     * @throws IOException
-     * @throws NoSuchAlgorithmException
-     * @throws UnrecoverableKeyException
+     * @throws java.security.KeyStoreException
+     * @throws java.security.cert.CertificateException
+     * @throws java.io.IOException
+     * @throws java.security.NoSuchAlgorithmException
+     * @throws java.security.UnrecoverableKeyException
      */
     public static void main(String[] args) throws KeyStoreException, CertificateException,
-            IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
-        if (args.length < 3) {
+            IOException, NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        if (args.length < 3)
+        {
             usage();
             System.exit(1);
         }
@@ -471,16 +517,20 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
         // External signing is needed if you are using an external signing service, e.g. to sign
         // several files at once.
         boolean externalSig = false;
-        for (int i = 0; i < args.length; i++) {
-            if ("-tsa".equals(args[i])) {
+        for (int i = 0; i < args.length; i++)
+        {
+            if (args[i].equals("-tsa"))
+            {
                 i++;
-                if (i >= args.length) {
+                if (i >= args.length)
+                {
                     usage();
                     System.exit(1);
                 }
                 tsaUrl = args[i];
             }
-            if ("-e".equals(args[i])) {
+            if (args[i].equals("-e"))
+            {
                 externalSig = true;
             }
         }
@@ -494,8 +544,9 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
 
         CreateVisibleSignature2 signing = new CreateVisibleSignature2(keystore, pin.clone());
 
-        if (args.length >= 4 && !"-tsa".equals(args[3])) {
-            signing.setImage(ImageIO.read(new File(args[3])));
+        if (args.length >= 4 && !"-tsa".equals(args[3]))
+        {
+            signing.setImageFile(new File(args[3]));
         }
 
         File signedDocumentFile;
@@ -518,12 +569,13 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
     /**
      * This will print the usage for this program.
      */
-    private static void usage() {
+    private static void usage()
+    {
         System.err.println("Usage: java " + CreateVisibleSignature2.class.getName()
                 + " <pkcs12-keystore-file> <pin> <input-pdf> <sign-image>\n" + "" +
-                "options:\n" +
-                "  -tsa <url>    sign timestamp using the given TSA server\n" +
-                "  -e            sign using external signature creation scenario");
+                           "options:\n" +
+                           "  -tsa <url>    sign timestamp using the given TSA server\n"+
+                           "  -e            sign using external signature creation scenario");
 
         // generate pkcs12-keystore-file with
         // keytool -storepass 123456 -storetype PKCS12 -keystore file.p12 -genkey -alias client -keyalg RSA

+ 238 - 273
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/ShowSignature.java

@@ -16,19 +16,42 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.cos.COSString;
 import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerificationException;
 import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerifier;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.pdfbox.cos.*;
 import org.apache.pdfbox.io.IOUtils;
-import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
+import org.apache.pdfbox.io.RandomAccessBufferedFileInputStream;
 import org.apache.pdfbox.pdfparser.PDFParser;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
 import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
-import org.apache.pdfbox.pdmodel.interactive.digitalsignature.COSFilterInputStream;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.util.Charsets;
 import org.apache.pdfbox.util.Hex;
 import org.bouncycastle.asn1.cms.Attribute;
 import org.bouncycastle.asn1.cms.CMSAttributes;
@@ -38,6 +61,7 @@ import org.bouncycastle.cert.jcajce.JcaCertStore;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSProcessable;
+import org.bouncycastle.cms.CMSProcessableByteArray;
 import org.bouncycastle.cms.CMSSignedData;
 import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
@@ -49,17 +73,6 @@ import org.bouncycastle.util.CollectionStore;
 import org.bouncycastle.util.Selector;
 import org.bouncycastle.util.Store;
 
-import java.io.*;
-import java.nio.charset.StandardCharsets;
-import java.security.*;
-import java.security.cert.Certificate;
-import java.security.cert.*;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
 /**
  * This will get the signature(s) from the document, do some verifications and
  * show the signature(s) and the certificates. This is a complex topic - the
@@ -69,8 +82,6 @@ import java.util.Set;
  */
 public final class ShowSignature
 {
-    private static final Log LOG = LogFactory.getLog(ShowSignature.class);
-
     private final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
 
     private ShowSignature()
@@ -83,14 +94,12 @@ public final class ShowSignature
      * @param args The command-line arguments.
      *
      * @throws IOException If there is an error reading the file.
-     * @throws TSPException
+     * @throws org.bouncycastle.tsp.TSPException
+     * @throws java.security.GeneralSecurityException
      * @throws org.apache.pdfbox.examples.signature.cert.CertificateVerificationException
-     * @throws GeneralSecurityException
      */
-    public static void main(String[] args) throws IOException,
-                                                  TSPException,
-            CertificateVerificationException,
-                                                  GeneralSecurityException
+    public static void main(String[] args) throws IOException, TSPException, GeneralSecurityException,
+            CertificateVerificationException
     {
         // register BouncyCastle provider, needed for "exotic" algorithms
         Security.addProvider(SecurityProvider.getProvider());
@@ -99,10 +108,8 @@ public final class ShowSignature
         show.showSignature( args );
     }
 
-    private void showSignature(String[] args) throws IOException,
-                                                     GeneralSecurityException,
-                                                     TSPException,
-                                                     CertificateVerificationException
+    private void showSignature(String[] args) throws IOException, TSPException, GeneralSecurityException,
+            CertificateVerificationException
     {
         if( args.length != 2 )
         {
@@ -112,170 +119,179 @@ public final class ShowSignature
         {
             String password = args[0];
             File infile = new File(args[1]);
-            // use old-style document loading to disable leniency
-            // see also https://www.pdf-insecurity.org/
-            RandomAccessReadBufferedFile raFile = new RandomAccessReadBufferedFile(infile);
-            // If your files are not too large, you can also download the PDF into a byte array
-            // with IOUtils.toByteArray() and pass a RandomAccessBuffer() object to the
-            // PDFParser constructor.
-            PDFParser parser = new PDFParser(raFile, password);
-            try (PDDocument document = parser.parse(false))
+            PDDocument document = null;
+            try
             {
+                // use old-style document loading to disable leniency
+                // see also https://www.pdf-insecurity.org/
+                RandomAccessBufferedFileInputStream raFile = new RandomAccessBufferedFileInputStream(infile);
+                // If your files are not too large, you can also download the PDF into a byte array
+                // with IOUtils.toByteArray() and pass a RandomAccessBuffer() object to the
+                // PDFParser constructor.
+                PDFParser parser = new PDFParser(raFile, password);
+                parser.setLenient(false);
+                parser.parse();
+                document = parser.getPDDocument();
                 for (PDSignature sig : document.getSignatureDictionaries())
                 {
                     COSDictionary sigDict = sig.getCOSObject();
                     byte[] contents = sig.getContents();
 
                     // download the signed content
-                    // we're doing this as a stream, to be able to handle huge files                    
-                    try (FileInputStream fis = new FileInputStream(infile);
-                         InputStream signedContentAsStream = new COSFilterInputStream(fis, sig.getByteRange()))
+                    FileInputStream fis = new FileInputStream(infile);
+                    byte[] buf = null;
+                    try
                     {
-                        System.out.println("Signature found");
-                        
-                        if (sig.getName() != null)
+                        buf = sig.getSignedContent(fis); // alternatively, pass a byte array here
+                    }
+                    finally
+                    {
+                        fis.close();
+                    }
+
+                    System.out.println("Signature found");
+
+                    if (sig.getName() != null)
+                    {
+                        System.out.println("Name:     " + sig.getName());
+                    }
+                    if (sig.getSignDate() != null)
+                    {
+                        System.out.println("Modified: " + sdf.format(sig.getSignDate().getTime()));
+                    }
+                    String subFilter = sig.getSubFilter();
+                    if (subFilter != null)
+                    {
+                        if (subFilter.equals("adbe.pkcs7.detached") || 
+                            subFilter.equals("ETSI.CAdES.detached"))
                         {
-                            System.out.println("Name:     " + sig.getName());
+                            verifyPKCS7(buf, contents, sig);
                         }
-                        if (sig.getSignDate() != null)
+                        else if (subFilter.equals("adbe.pkcs7.sha1"))
                         {
-                            System.out.println("Modified: " + sdf.format(sig.getSignDate().getTime()));
+                            // example: PDFBOX-1452.pdf
+                            CertificateFactory factory = CertificateFactory.getInstance("X.509");
+                            ByteArrayInputStream certStream = new ByteArrayInputStream(contents);
+                            Collection<? extends Certificate> certs = factory.generateCertificates(certStream);
+                            System.out.println("certs=" + certs);
+
+                            byte[] hash = MessageDigest.getInstance("SHA1").digest(buf);
+                            verifyPKCS7(hash, contents, sig);
                         }
-                        String subFilter = sig.getSubFilter();
-                        if (subFilter != null)
+                        else if (subFilter.equals("adbe.x509.rsa_sha1"))
                         {
-                            switch (subFilter)
+                            // example: PDFBOX-2693.pdf
+                            COSString certString = (COSString) sigDict.getDictionaryObject(COSName.CERT);
+                            //TODO this could also be an array.
+                            if (certString == null)
                             {
-                                case "adbe.pkcs7.detached":
-                                case "ETSI.CAdES.detached":
-                                    verifyPKCS7(signedContentAsStream, contents, sig);
-                                    break;
-                                case "adbe.pkcs7.sha1":
+                                System.err.println("The /Cert certificate string is missing in the signature dictionary");
+                                return;
+                            }
+                            byte[] certData = certString.getBytes();
+                            CertificateFactory factory = CertificateFactory.getInstance("X.509");
+                            ByteArrayInputStream certStream = new ByteArrayInputStream(certData);
+                            Collection<? extends Certificate> certs = factory.generateCertificates(certStream);
+                            System.out.println("certs=" + certs);
+
+                            X509Certificate cert = (X509Certificate) certs.iterator().next();
+
+                            // to verify signature, see code at
+                            // https://stackoverflow.com/questions/43383859/
+
+                            try
+                            {
+                                if (sig.getSignDate() != null)
+                                {
+                                    cert.checkValidity(sig.getSignDate().getTime());
+                                    System.out.println("Certificate valid at signing time");
+                                }
+                                else
                                 {
-                                    // example: PDFBOX-1452.pdf
-                                    CertificateFactory factory = CertificateFactory.getInstance("X.509");
-                                    ByteArrayInputStream certStream = new ByteArrayInputStream(contents);
-                                    Collection<? extends Certificate> certs = factory.generateCertificates(certStream);
-                                    System.out.println("certs=" + certs);
-                                    MessageDigest md = MessageDigest.getInstance("SHA1");
-                                    try (DigestInputStream dis = new DigestInputStream(signedContentAsStream, md))
-                                    {
-                                        while (dis.read() != -1)                                        
-                                        {
-                                            // do nothing
-                                        }
-                                    }
-                                    byte [] hash = md.digest();
-                                    verifyPKCS7(new ByteArrayInputStream(hash), contents, sig);
-                                    break;
+                                    System.err.println("Certificate cannot be verified without signing time");
                                 }
-                                case "adbe.x509.rsa_sha1":
+                            }
+                            catch (CertificateExpiredException ex)
+                            {
+                                System.err.println("Certificate expired at signing time");
+                            }
+                            catch (CertificateNotYetValidException ex)
+                            {
+                                System.err.println("Certificate not yet valid at signing time");
+                            }
+                            if (CertificateVerifier.isSelfSigned(cert))
+                            {
+                                System.err.println("Certificate is self-signed, LOL!");
+                            }
+                            else
+                            {
+                                System.out.println("Certificate is not self-signed");
+
+                                if (sig.getSignDate() != null)
                                 {
-                                    // example: PDFBOX-2693.pdf
-                                    COSString certString = (COSString) sigDict.getDictionaryObject(COSName.CERT);
-                                    //TODO this could also be an array.
-                                    if (certString == null)
-                                    {
-                                        System.err.println("The /Cert certificate string is missing in the signature dictionary");
-                                        return;
-                                    }
-                                    byte[] certData = certString.getBytes();
-                                    CertificateFactory factory = CertificateFactory.getInstance("X.509");
-                                    ByteArrayInputStream certStream = new ByteArrayInputStream(certData);
-                                    Collection<? extends Certificate> certs = factory.generateCertificates(certStream);
-                                    System.out.println("certs=" + certs);
-                                    
-                                    X509Certificate cert = (X509Certificate) certs.iterator().next();
-                                    
-                                    // to verify signature, see code at
-                                    // https://stackoverflow.com/questions/43383859/
-                                    
-                                    try
-                                    {
-                                        if (sig.getSignDate() != null)
-                                        {
-                                            cert.checkValidity(sig.getSignDate().getTime());
-                                            System.out.println("Certificate valid at signing time");
-                                        }
-                                        else
-                                        {
-                                            System.err.println("Certificate cannot be verified without signing time");
-                                        }
-                                    }
-                                    catch (CertificateExpiredException ex)
-                                    {
-                                        System.err.println("Certificate expired at signing time");
-                                    }
-                                    catch (CertificateNotYetValidException ex)
-                                    {
-                                        System.err.println("Certificate not yet valid at signing time");
-                                    }
-                                    if (CertificateVerifier.isSelfSigned(cert))
-                                    {
-                                        System.err.println("Certificate is self-signed, LOL!");
-                                    }
-                                    else
-                                    {
-                                        System.out.println("Certificate is not self-signed");
-                                        
-                                        if (sig.getSignDate() != null)
-                                        {
-                                            @SuppressWarnings("unchecked")
-                                                    Store<X509CertificateHolder> store = new JcaCertStore(certs);
-                                            SigUtils.verifyCertificateChain(store, cert, sig.getSignDate().getTime());
-                                        }
-                                    }
-                                    break;
+                                    @SuppressWarnings("unchecked")
+                                    Store<X509CertificateHolder> store = new JcaCertStore(certs);
+                                    SigUtils.verifyCertificateChain(store, cert, sig.getSignDate().getTime());
                                 }
-                                case "ETSI.RFC3161":
-                                    // e.g. PDFBOX-1848, file_timestamped.pdf
-                                    verifyETSIdotRFC3161(signedContentAsStream, contents);
-                                    
-                                    // verifyPKCS7(hash, contents, sig) does not work
-                                    break;
-                                    
-                                default:
-                                    System.err.println("Unknown certificate type: " + subFilter);
-                                    break;
                             }
                         }
+                        else if (subFilter.equals("ETSI.RFC3161"))
+                        {
+                            // e.g. PDFBOX-1848, file_timestamped.pdf
+                            verifyETSIdotRFC3161(buf, contents);
+                        }
                         else
                         {
-                            throw new IOException("Missing subfilter for cert dictionary");
+                            System.err.println("Unknown certificate type: " + subFilter);
                         }
-                        
-                        int[] byteRange = sig.getByteRange();
-                        if (byteRange.length != 4)
+                    }
+                    else
+                    {
+                        throw new IOException("Missing subfilter for cert dictionary");
+                    }
+
+                    int[] byteRange = sig.getByteRange();
+                    if (byteRange.length != 4)
+                    {
+                        System.err.println("Signature byteRange must have 4 items");
+                    }
+                    else
+                    {
+                        long fileLen = infile.length();
+                        long rangeMax = byteRange[2] + (long) byteRange[3];
+                        // multiply content length with 2 (because it is in hex in the PDF) and add 2 for < and >
+                        int contentLen = contents.length * 2 + 2;
+                        if (fileLen != rangeMax || byteRange[0] != 0 || byteRange[1] + contentLen != byteRange[2])
                         {
-                            System.err.println("Signature byteRange must have 4 items");
+                            // a false result doesn't necessarily mean that the PDF is a fake
+                            // see this answer why:
+                            // https://stackoverflow.com/a/48185913/535646
+                            System.out.println("Signature does not cover whole document");
                         }
                         else
                         {
-                            long fileLen = infile.length();
-                            long rangeMax = byteRange[2] + (long) byteRange[3];
-                            // multiply content length with 2 (because it is in hex in the PDF) and add 2 for < and >
-                            int contentLen = contents.length * 2 + 2;
-                            if (fileLen != rangeMax || byteRange[0] != 0 || byteRange[1] + contentLen != byteRange[2])
-                            {
-                                // a false result doesn't necessarily mean that the PDF is a fake
-                                // see this answer why:
-                                // https://stackoverflow.com/a/48185913/535646
-                                System.out.println("Signature does not cover whole document");
-                            }
-                            else
-                            {
-                                System.out.println("Signature covers whole document");
-                            }
-                            checkContentValueWithFile(infile, byteRange, contents);
+                            System.out.println("Signature covers whole document");
                         }
+                        checkContentValueWithFile(infile, byteRange, contents);
                     }
                 }
                 analyseDSS(document);
             }
-            catch (CMSException | OperatorCreationException ex)
+            catch (CMSException ex)
             {
                 throw new IOException(ex);
             }
+            catch (OperatorCreationException ex)
+            {
+                throw new IOException(ex);
+            }
+            finally
+            {
+                if (document != null)
+                {
+                    document.close();
+                }
+            }
             System.out.println("Analyzed: " + args[1]);
         }
     }
@@ -286,65 +302,56 @@ public final class ShowSignature
         // comment by mkl: check whether gap contains a hex value equal
         // byte-by-byte to the Content value, to prevent attacker from using a literal string
         // to allow extra space
-        try (RandomAccessReadBufferedFile raf = new RandomAccessReadBufferedFile(file))
+        RandomAccessBufferedFileInputStream raf = new RandomAccessBufferedFileInputStream(file);
+        raf.seek(byteRange[1]);
+        int c = raf.read();
+        if (c != '<')
         {
-            raf.seek(byteRange[1]);
-            int c = raf.read();
-            if (c != '<')
-            {
-                System.err.println("'<' expected at offset " + byteRange[1] + ", but got " + (char) c);
-            }
-            byte[] contentFromFile = new byte[byteRange[2] - byteRange[1] - 2];
-            int contentLength = contentFromFile.length;
-            int contentBytesRead = raf.read(contentFromFile);
-            while (contentBytesRead > -1 && contentBytesRead < contentLength)
-            {
-                contentBytesRead += raf.read(contentFromFile,
-                        contentBytesRead,
-                        contentLength - contentBytesRead);
-            }
-            byte[] contentAsHex = Hex.getString(contents).getBytes(StandardCharsets.US_ASCII);
-            if (contentBytesRead != contentAsHex.length)
-            {
-                System.err.println("Raw content length from file is " +
-                        contentBytesRead +
-                        ", but internal content string in hex has length " +
-                        contentAsHex.length);
-            }
-            // Compare the two, we can't do byte comparison because of upper/lower case
-            // also check that it is really hex
-            for (int i = 0; i < contentBytesRead; ++i)
+            System.err.println("'<' expected at offset " + byteRange[1] + ", but got " + (char) c);
+        }
+        byte[] contentFromFile = raf.readFully(byteRange[2] - byteRange[1] - 2);
+        byte[] contentAsHex = Hex.getString(contents).getBytes(Charsets.US_ASCII);
+        if (contentFromFile.length != contentAsHex.length)
+        {
+            System.err.println("Raw content length from file is " +
+                    contentFromFile.length +
+                    ", but internal content string in hex has length " +
+                    contentAsHex.length);
+        }
+        // Compare the two, we can't do byte comparison because of upper/lower case
+        // also check that it is really hex
+        for (int i = 0; i < contentFromFile.length; ++i)
+        {
+            try
             {
-                try
-                {
-                    if (Integer.parseInt(String.valueOf((char) contentFromFile[i]), 16) !=
-                        Integer.parseInt(String.valueOf((char) contentAsHex[i]), 16))
-                    {
-                        System.err.println("Possible manipulation at file offset " +
-                                (byteRange[1] + i + 1) + " in signature content");
-                        break;
-                    }
-                }
-                catch (NumberFormatException ex)
+                if (Integer.parseInt(String.valueOf((char) contentFromFile[i]), 16) !=
+                    Integer.parseInt(String.valueOf((char) contentAsHex[i]), 16))
                 {
-                    System.err.println("Incorrect hex value");
                     System.err.println("Possible manipulation at file offset " +
-                            (byteRange[1] + i + 1) + " in signature content");
+                                       (byteRange[1] + i + 1) + " in signature content");
                     break;
                 }
             }
-            c = raf.read();
-            if (c != '>')
+            catch (NumberFormatException ex)
             {
-                System.err.println("'>' expected at offset " + byteRange[2] + ", but got " + (char) c);
+                System.err.println("Incorrect hex value");
+                System.err.println("Possible manipulation at file offset " +
+                                    (byteRange[1] + i + 1) + " in signature content");
+                break;
             }
         }
+        c = raf.read();
+        if (c != '>')
+        {
+            System.err.println("'>' expected at offset " + byteRange[2] + ", but got " + (char) c);
+        }
+        raf.close();
     }
 
     /**
      * Verify ETSI.RFC3161 TimeStampToken
      *
-     * @param signedContentAsStream the byte sequence that has been signed
+     * @param buf the byte sequence that has been signed
      * @param contents the /Contents field as a COSString
      * @throws CMSException
      * @throws NoSuchAlgorithmException
@@ -354,9 +361,9 @@ public final class ShowSignature
      * @throws CertificateVerificationException
      * @throws CertificateException 
      */
-    private void verifyETSIdotRFC3161(InputStream signedContentAsStream, byte[] contents)
-            throws CMSException, NoSuchAlgorithmException, IOException, TSPException,
-            OperatorCreationException, CertificateVerificationException, CertificateException
+    private void verifyETSIdotRFC3161(byte[] buf, byte[] contents)
+            throws CertificateException, CMSException, IOException, OperatorCreationException,
+            TSPException, NoSuchAlgorithmException, CertificateVerificationException
     {
         TimeStampToken timeStampToken = new TimeStampToken(new CMSSignedData(contents));
         TimeStampTokenInfo timeStampInfo = timeStampToken.getTimeStampInfo();
@@ -373,15 +380,7 @@ public final class ShowSignature
         
         String hashAlgorithm = timeStampInfo.getMessageImprintAlgOID().getId();
         // compare the hash of the signed content with the hash in the timestamp
-        MessageDigest md = MessageDigest.getInstance(hashAlgorithm);
-        try (DigestInputStream dis = new DigestInputStream(signedContentAsStream, md))
-        {
-            while (dis.read() != -1)
-            {
-                // do nothing
-            }
-        }
-        if (Arrays.equals(md.digest(),
+        if (Arrays.equals(MessageDigest.getInstance(hashAlgorithm).digest(buf),
                 timeStampInfo.getMessageImprintDigest()))
         {
             System.out.println("ETSI.RFC3161 timestamp signature verified");
@@ -394,7 +393,7 @@ public final class ShowSignature
         X509Certificate certFromTimeStamp = (X509Certificate) certs.iterator().next();
         SigUtils.checkTimeStampCertificateUsage(certFromTimeStamp);
         SigUtils.validateTimestampToken(timeStampToken);
-        SigUtils.verifyCertificateChain(timeStampToken.getCertificates(),
+                SigUtils.verifyCertificateChain(timeStampToken.getCertificates(),
                 certFromTimeStamp,
                 timeStampInfo.getGenTime());
     }
@@ -402,24 +401,25 @@ public final class ShowSignature
     /**
      * Verify a PKCS7 signature.
      *
-     * @param signedContentAsStream the byte sequence that has been signed
+     * @param byteArray the byte sequence that has been signed
      * @param contents the /Contents field as a COSString
      * @param sig the PDF signature (the /V dictionary)
      * @throws CMSException
      * @throws OperatorCreationException
+     * @throws IOException
      * @throws GeneralSecurityException
-     * @throws CertificateVerificationException
+     * @throws TSPException
      */
-    private void verifyPKCS7(InputStream signedContentAsStream, byte[] contents, PDSignature sig)
+    private void verifyPKCS7(byte[] byteArray, byte[] contents, PDSignature sig)
             throws CMSException, OperatorCreationException,
-                   CertificateVerificationException, GeneralSecurityException,
-                   TSPException, IOException
+                   IOException, GeneralSecurityException, TSPException, CertificateVerificationException
     {
         // inspiration:
         // http://stackoverflow.com/a/26702631/535646
         // http://stackoverflow.com/a/9261365/535646
-        CMSProcessable signedContent = new CMSProcessableInputStream(signedContentAsStream);
+        CMSProcessable signedContent = new CMSProcessableByteArray(byteArray);
         CMSSignedData signedData = new CMSSignedData(signedContent, contents);
+        @SuppressWarnings("unchecked")
         Store<X509CertificateHolder> certificatesStore = signedData.getCertificates();
         if (certificatesStore.getMatches(null).isEmpty())
         {
@@ -445,7 +445,7 @@ public final class ShowSignature
         System.out.println("certFromSignedData: " + certFromSignedData);
 
         SigUtils.checkCertificateUsage(certFromSignedData);
-        
+
         // Embedded timestamp
         TimeStampToken timeStampToken = SigUtils.extractTimeStampTokenFromSignerInformation(signerInformation);
         if (timeStampToken != null)
@@ -454,12 +454,16 @@ public final class ShowSignature
             // https://www.quovadisglobal.com/~/media/Files/Repository/QV_RCA1_RCA3_CPCPS_V4_11.ashx
             // also 021496.pdf and 036351.pdf from digitalcorpora
             SigUtils.validateTimestampToken(timeStampToken);
-            X509Certificate certFromTimeStamp = SigUtils.getCertificateFromTimeStampToken(timeStampToken);
+            @SuppressWarnings("unchecked") // TimeStampToken.getSID() is untyped
+            Collection<X509CertificateHolder> tstMatches =
+                timeStampToken.getCertificates().getMatches((Selector<X509CertificateHolder>) timeStampToken.getSID());
+            X509CertificateHolder tstCertHolder = tstMatches.iterator().next();
+            X509Certificate certFromTimeStamp = new JcaX509CertificateConverter().getCertificate(tstCertHolder);
             // merge both stores using a set to remove duplicates
-            HashSet<X509CertificateHolder> certificateHolderSet = new HashSet<>();
+            HashSet<X509CertificateHolder> certificateHolderSet = new HashSet<X509CertificateHolder>();
             certificateHolderSet.addAll(certificatesStore.getMatches(null));
             certificateHolderSet.addAll(timeStampToken.getCertificates().getMatches(null));
-            SigUtils.verifyCertificateChain(new CollectionStore<>(certificateHolderSet),
+            SigUtils.verifyCertificateChain(new CollectionStore<X509CertificateHolder>(certificateHolderSet),
                     certFromTimeStamp,
                     timeStampToken.getTimeStampInfo().getGenTime());
             SigUtils.checkTimeStampCertificateUsage(certFromTimeStamp);
@@ -552,50 +556,10 @@ public final class ShowSignature
         }
     }
 
-    // for later use: get all root certificates. Will be used to check
-    // whether we trust the root in the certificate chain.
-    private Set<X509Certificate> getRootCertificates()
-            throws GeneralSecurityException, IOException
-    {
-        Set<X509Certificate> rootCertificates = new HashSet<>();
-
-        // https://stackoverflow.com/questions/3508050/
-        String filename = System.getProperty("java.home") + "/lib/security/cacerts";
-        KeyStore keystore;
-        try (FileInputStream is = new FileInputStream(filename))
-        {
-            keystore = KeyStore.getInstance(KeyStore.getDefaultType());
-            keystore.load(is, null);
-        }
-        PKIXParameters params = new PKIXParameters(keystore);
-        for (TrustAnchor trustAnchor : params.getTrustAnchors())
-        {
-            rootCertificates.add(trustAnchor.getTrustedCert());
-        }
-
-        // https://www.oracle.com/technetwork/articles/javase/security-137537.html
-        try
-        {
-            keystore = KeyStore.getInstance("Windows-ROOT");
-            keystore.load(null, null);
-            params = new PKIXParameters(keystore);
-            for (TrustAnchor trustAnchor : params.getTrustAnchors())
-            {
-                rootCertificates.add(trustAnchor.getTrustedCert());
-            }
-        }
-        catch (InvalidAlgorithmParameterException | KeyStoreException ex)
-        {
-            // empty or not windows
-        }
-
-        return rootCertificates;
-    }
-
     /**
-     * Analyzes the DSS-Dictionary (Document Security Store) of the document. Which is used for signature validation.
-     * The DSS is defined in PAdES Part 4 - Long Term Validation.
-     * 
+     * Analyzes the DSS-Dictionary (Document Security Store) of the document. Which is used for
+     * signature validation. The DSS is defined in PAdES Part 4 - Long Term Validation.
+     *
      * @param document PDDocument, to get the DSS from
      */
     private void analyseDSS(PDDocument document) throws IOException
@@ -628,7 +592,7 @@ public final class ShowSignature
 
     /**
      * Go through the elements of a COSArray containing each an COSStream to print in Hex.
-     * 
+     *
      * @param elements COSArray of elements containing a COS Stream
      * @param description to append on Print
      * @throws IOException
@@ -641,12 +605,13 @@ public final class ShowSignature
             if (streamObj.getObject() instanceof COSStream)
             {
                 COSStream cosStream = (COSStream) streamObj.getObject();
-                try (InputStream is = cosStream.createInputStream())
-                {
-                    byte[] streamBytes = IOUtils.toByteArray(is);
-                    System.out.println(description + " (" + elements.indexOf(streamObj) + "): "
+
+                InputStream input = cosStream.createInputStream();
+                byte[] streamBytes = IOUtils.toByteArray(input);
+                input.close();
+
+                System.out.println(description + " (" + elements.indexOf(streamObj) + "): "
                         + Hex.getString(streamBytes));
-                }
             }
         }
     }

+ 50 - 85
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/SigUtils.java

@@ -16,14 +16,25 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
-import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerificationException;
-import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerifier;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
+import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerificationException;
+import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerifier;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
@@ -45,17 +56,6 @@ import org.bouncycastle.tsp.TimeStampToken;
 import org.bouncycastle.util.Selector;
 import org.bouncycastle.util.Store;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.*;
-
 /**
  * Utility class for the signature / timestamp examples.
  * 
@@ -81,25 +81,27 @@ public class SigUtils
      */
     public static int getMDPPermission(PDDocument doc)
     {
-        COSDictionary permsDict = doc.getDocumentCatalog().getCOSObject()
-                .getCOSDictionary(COSName.PERMS);
-        if (permsDict != null)
+        COSBase base = doc.getDocumentCatalog().getCOSObject().getDictionaryObject(COSName.PERMS);
+        if (base instanceof COSDictionary)
         {
-            COSDictionary signatureDict = permsDict.getCOSDictionary(COSName.DOCMDP);
-            if (signatureDict != null)
+            COSDictionary permsDict = (COSDictionary) base;
+            base = permsDict.getDictionaryObject(COSName.DOCMDP);
+            if (base instanceof COSDictionary)
             {
-                COSArray refArray = signatureDict.getCOSArray(COSName.REFERENCE);
-                if (refArray instanceof COSArray)
+                COSDictionary signatureDict = (COSDictionary) base;
+                base = signatureDict.getDictionaryObject("Reference");
+                if (base instanceof COSArray)
                 {
+                    COSArray refArray = (COSArray) base;
                     for (int i = 0; i < refArray.size(); ++i)
                     {
-                        COSBase base = refArray.getObject(i);
+                        base = refArray.getObject(i);
                         if (base instanceof COSDictionary)
                         {
                             COSDictionary sigRefDict = (COSDictionary) base;
-                            if (COSName.DOCMDP.equals(sigRefDict.getDictionaryObject(COSName.TRANSFORM_METHOD)))
+                            if (COSName.DOCMDP.equals(sigRefDict.getDictionaryObject("TransformMethod")))
                             {
-                                base = sigRefDict.getDictionaryObject(COSName.TRANSFORM_PARAMS);
+                                base = sigRefDict.getDictionaryObject("TransformParams");
                                 if (base instanceof COSDictionary)
                                 {
                                     COSDictionary transformDict = (COSDictionary) base;
@@ -151,21 +153,21 @@ public class SigUtils
 
         // DocMDP specific stuff
         COSDictionary transformParameters = new COSDictionary();
-        transformParameters.setItem(COSName.TYPE, COSName.TRANSFORM_PARAMS);
+        transformParameters.setItem(COSName.TYPE, COSName.getPDFName("TransformParams"));
         transformParameters.setInt(COSName.P, accessPermissions);
         transformParameters.setName(COSName.V, "1.2");
         transformParameters.setNeedToBeUpdated(true);
 
         COSDictionary referenceDict = new COSDictionary();
-        referenceDict.setItem(COSName.TYPE, COSName.SIG_REF);
-        referenceDict.setItem(COSName.TRANSFORM_METHOD, COSName.DOCMDP);
-        referenceDict.setItem(COSName.DIGEST_METHOD, COSName.getPDFName("SHA1"));
-        referenceDict.setItem(COSName.TRANSFORM_PARAMS, transformParameters);
+        referenceDict.setItem(COSName.TYPE, COSName.getPDFName("SigRef"));
+        referenceDict.setItem("TransformMethod", COSName.DOCMDP);
+        referenceDict.setItem("DigestMethod", COSName.getPDFName("SHA1"));
+        referenceDict.setItem("TransformParams", transformParameters);
         referenceDict.setNeedToBeUpdated(true);
 
         COSArray referenceArray = new COSArray();
         referenceArray.add(referenceDict);
-        sigDict.setItem(COSName.REFERENCE, referenceArray);
+        sigDict.setItem("Reference", referenceArray);
         referenceArray.setNeedToBeUpdated(true);
 
         // Catalog
@@ -182,7 +184,7 @@ public class SigUtils
      * anyway results in Adobe Reader failing to validate the PDF.
      *
      * @param x509Certificate 
-     * @throws CertificateParsingException
+     * @throws java.security.cert.CertificateParsingException 
      */
     public static void checkCertificateUsage(X509Certificate x509Certificate)
             throws CertificateParsingException
@@ -216,8 +218,8 @@ public class SigUtils
     /**
      * Log if the certificate is not valid for timestamping.
      *
-     * @param x509Certificate
-     * @throws CertificateParsingException
+     * @param x509Certificate 
+     * @throws java.security.cert.CertificateParsingException 
      */
     public static void checkTimeStampCertificateUsage(X509Certificate x509Certificate)
             throws CertificateParsingException
@@ -234,8 +236,8 @@ public class SigUtils
     /**
      * Log if the certificate is not valid for responding.
      *
-     * @param x509Certificate
-     * @throws CertificateParsingException
+     * @param x509Certificate 
+     * @throws java.security.cert.CertificateParsingException 
      */
     public static void checkResponderCertificateUsage(X509Certificate x509Certificate)
             throws CertificateParsingException
@@ -254,22 +256,19 @@ public class SigUtils
      * 
      * @param document to get its last signature
      * @return last signature or null when none found
+     * @throws IOException
      */
-    public static PDSignature getLastRelevantSignature(PDDocument document)
+    public static PDSignature getLastRelevantSignature(PDDocument document) throws IOException
     {
-        Comparator<PDSignature> comparatorByOffset =
-                Comparator.comparing(sig -> sig.getByteRange()[1]);
-
-        // we can't use getLastSignatureDictionary() because this will fail (see PDFBOX-3978) 
-        // if a signature is assigned to a pre-defined empty signature field that isn't the last.
-        // we get the last in time by looking at the offset in the PDF file.
-        Optional<PDSignature> optLastSignature =
-                document.getSignatureDictionaries().stream().
-                sorted(comparatorByOffset.reversed()).
-                findFirst();
-        if (optLastSignature.isPresent())
+        SortedMap<Integer, PDSignature> sortedMap = new TreeMap<Integer, PDSignature>();
+        for (PDSignature signature : document.getSignatureDictionaries())
         {
-            PDSignature lastSignature = optLastSignature.get();
+            int sigOffset = signature.getByteRange()[1];
+            sortedMap.put(sigOffset, signature);
+        }
+        if (sortedMap.size() > 0)
+        {
+            PDSignature lastSignature = sortedMap.get(sortedMap.lastKey());
             COSBase type = lastSignature.getCOSObject().getItem(COSName.TYPE);
             if (type == null || COSName.SIG.equals(type) || COSName.DOC_TIME_STAMP.equals(type))
             {
@@ -279,7 +278,7 @@ public class SigUtils
         return null;
     }
 
-    public static TimeStampToken extractTimeStampTokenFromSignerInformation(SignerInformation signerInformation)
+    static public TimeStampToken extractTimeStampTokenFromSignerInformation(SignerInformation signerInformation)
             throws CMSException, IOException, TSPException
     {
         if (signerInformation.getUnsignedAttributes() == null)
@@ -300,7 +299,7 @@ public class SigUtils
     }
 
     public static void validateTimestampToken(TimeStampToken timeStampToken)
-            throws TSPException, CertificateException, OperatorCreationException, IOException
+            throws IOException, CertificateException, TSPException, OperatorCreationException
     {
         // https://stackoverflow.com/questions/42114742/
         @SuppressWarnings("unchecked") // TimeStampToken.getSID() is untyped
@@ -312,6 +311,7 @@ public class SigUtils
         timeStampToken.validate(siv);
     }
 
+
     /**
      * Verify the certificate chain up to the root, including OCSP or CRL. However this does not
      * test whether the root certificate is in a trusted list.<br><br>
@@ -329,7 +329,7 @@ public class SigUtils
             throws CertificateVerificationException, CertificateException
     {
         Collection<X509CertificateHolder> certificateHolders = certificatesStore.getMatches(null);
-        Set<X509Certificate> additionalCerts = new HashSet<>();
+        Set<X509Certificate> additionalCerts = new HashSet<X509Certificate>();
         JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
         for (X509CertificateHolder certHolder : certificateHolders)
         {
@@ -345,39 +345,4 @@ public class SigUtils
         // https://ec.europa.eu/digital-single-market/en/eu-trusted-lists-trust-service-providers
         // ( getRootCertificates() is not helpful because these are SSL certificates)
     }
-
-    /**
-     * Get certificate of a TSA.
-     * 
-     * @param tsaUrl URL
-     * @return the X.509 certificate.
-     *
-     * @throws GeneralSecurityException
-     * @throws IOException 
-     */
-    public static X509Certificate getTsaCertificate(String tsaUrl)
-            throws GeneralSecurityException, IOException
-    {
-        MessageDigest digest = MessageDigest.getInstance("SHA-256");
-        TSAClient tsaClient = new TSAClient(new URL(tsaUrl), null, null, digest);
-        InputStream emptyStream = new ByteArrayInputStream(new byte[0]);
-        TimeStampToken timeStampToken = tsaClient.getTimeStampToken(emptyStream);
-        return getCertificateFromTimeStampToken(timeStampToken);
-    }
-
-    /**
-     * Extract X.509 certificate from a timestamp
-     * @param timeStampToken
-     * @return the X.509 certificate.
-     * @throws CertificateException 
-     */
-    public static X509Certificate getCertificateFromTimeStampToken(TimeStampToken timeStampToken)
-            throws CertificateException
-    {
-        @SuppressWarnings("unchecked") // TimeStampToken.getSID() is untyped
-        Collection<X509CertificateHolder> tstMatches =
-                timeStampToken.getCertificates().getMatches(timeStampToken.getSID());
-        X509CertificateHolder tstCertHolder = tstMatches.iterator().next();
-        return new JcaX509CertificateConverter().getCertificate(tstCertHolder);
-    }
 }

+ 63 - 28
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/TSAClient.java

@@ -16,25 +16,29 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.pdfbox.io.IOUtils;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
-import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
-import org.bouncycastle.tsp.*;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.math.BigInteger;
 import java.net.URL;
 import java.net.URLConnection;
-import java.security.DigestInputStream;
 import java.security.MessageDigest;
 import java.security.SecureRandom;
 import java.util.Random;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.io.IOUtils;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.tsp.TSPException;
+import org.bouncycastle.tsp.TimeStampRequest;
+import org.bouncycastle.tsp.TimeStampRequestGenerator;
+import org.bouncycastle.tsp.TimeStampResponse;
+import org.bouncycastle.tsp.TimeStampToken;
+
 /**
  * Time Stamping Authority (TSA) Client [RFC 3161].
  * @author Vakhtang Koroghlishvili
@@ -44,9 +48,6 @@ public class TSAClient
 {
     private static final Log LOG = LogFactory.getLog(TSAClient.class);
 
-    private static final DigestAlgorithmIdentifierFinder ALGORITHM_OID_FINDER =
-            new DefaultDigestAlgorithmIdentifierFinder();
-
     private final URL url;
     private final String username;
     private final String password;
@@ -77,15 +78,10 @@ public class TSAClient
      * @throws IOException if there was an error with the connection or data from the TSA server,
      *                     or if the time stamp response could not be validated
      */
-    public TimeStampToken getTimeStampToken(InputStream content) throws IOException
+    public TimeStampToken getTimeStampToken(byte[] content) throws IOException
     {
         digest.reset();
-        DigestInputStream dis = new DigestInputStream(content, digest);
-        while (dis.read() != -1)
-        {
-            // do nothing
-        }
-        byte[] hash = digest.digest();
+        byte[] hash = digest.digest(content);
 
         // 32-bit cryptographic nonce
         int nonce = RANDOM.nextInt();
@@ -93,7 +89,7 @@ public class TSAClient
         // generate TSA request
         TimeStampRequestGenerator tsaGenerator = new TimeStampRequestGenerator();
         tsaGenerator.setCertReq(true);
-        ASN1ObjectIdentifier oid = ALGORITHM_OID_FINDER.find(digest.getAlgorithm()).getAlgorithm();
+        ASN1ObjectIdentifier oid = getHashObjectIdentifier(digest.getAlgorithm());
         TimeStampRequest request = tsaGenerator.generate(oid, hash, BigInteger.valueOf(nonce));
 
         // get TSA response
@@ -142,31 +138,70 @@ public class TSAClient
         }
 
         // read response
-        try (OutputStream output = connection.getOutputStream())
+        OutputStream output = null;
+        try
         {
+            output = connection.getOutputStream();
             output.write(request);
         }
-        catch (IOException ex)
+        finally
         {
-            LOG.error("Exception when writing to " + this.url, ex);
-            throw ex;
+            IOUtils.closeQuietly(output);
         }
 
         LOG.debug("Waiting for response from TSA server");
 
+        InputStream input = null;
         byte[] response;
-        try (InputStream input = connection.getInputStream())
+        try
         {
+            input = connection.getInputStream();
             response = IOUtils.toByteArray(input);
         }
-        catch (IOException ex)
+        finally
         {
-            LOG.error("Exception when reading from " + this.url, ex);
-            throw ex;
+            IOUtils.closeQuietly(input);
         }
 
         LOG.debug("Received response from TSA server");
 
         return response;
     }
+
+    // returns the ASN.1 OID of the given hash algorithm
+    private ASN1ObjectIdentifier getHashObjectIdentifier(String algorithm)
+    {
+        if (algorithm.equals("MD2"))
+        {
+            return new ASN1ObjectIdentifier(PKCSObjectIdentifiers.md2.getId());
+        }
+        else if (algorithm.equals("MD5"))
+        {
+            return new ASN1ObjectIdentifier(PKCSObjectIdentifiers.md5.getId());
+        }
+        else if (algorithm.equals("SHA-1"))
+        {
+            return new ASN1ObjectIdentifier(OIWObjectIdentifiers.idSHA1.getId());
+        }
+        else if (algorithm.equals("SHA-224"))
+        {
+            return new ASN1ObjectIdentifier(NISTObjectIdentifiers.id_sha224.getId());
+        }
+        else if (algorithm.equals("SHA-256"))
+        {
+            return new ASN1ObjectIdentifier(NISTObjectIdentifiers.id_sha256.getId());
+        }
+        else if (algorithm.equals("SHA-384"))
+        {
+            return new ASN1ObjectIdentifier(NISTObjectIdentifiers.id_sha384.getId());
+        }
+        else if (algorithm.equals("SHA-512"))
+        {
+            return new ASN1ObjectIdentifier(NISTObjectIdentifiers.id_sha512.getId());
+        }
+        else
+        {
+            return new ASN1ObjectIdentifier(algorithm);
+        }
+    }
 }

+ 18 - 15
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/ValidationTimeStamp.java

@@ -17,17 +17,6 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
 
-import org.bouncycastle.asn1.*;
-import org.bouncycastle.asn1.cms.Attribute;
-import org.bouncycastle.asn1.cms.AttributeTable;
-import org.bouncycastle.asn1.cms.Attributes;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.SignerInformationStore;
-import org.bouncycastle.tsp.TimeStampToken;
-
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
@@ -37,6 +26,21 @@ import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.pdfbox.io.IOUtils;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.Attributes;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.tsp.TimeStampToken;
+
 /**
  * This class wraps the TSAClient and the work that has to be done with it. Like Adding Signed
  * TimeStamps to a signature, or creating a CMS timestamp attribute (with a signed timestamp)
@@ -71,7 +75,7 @@ public class ValidationTimeStamp
      */
     public byte[] getTimeStampToken(InputStream content) throws IOException
     {
-        TimeStampToken timeStampToken = tsaClient.getTimeStampToken(content);
+        TimeStampToken timeStampToken = tsaClient.getTimeStampToken(IOUtils.toByteArray(content));
         return timeStampToken.getEncoded();
     }
 
@@ -86,7 +90,7 @@ public class ValidationTimeStamp
             throws IOException
     {
         SignerInformationStore signerStore = signedData.getSignerInfos();
-        List<SignerInformation> newSigners = new ArrayList<>();
+        List<SignerInformation> newSigners = new ArrayList<SignerInformation>();
 
         for (SignerInformation signer : signerStore.getSigners())
         {
@@ -117,8 +121,7 @@ public class ValidationTimeStamp
             vector = unsignedAttributes.toASN1EncodableVector();
         }
 
-        TimeStampToken timeStampToken = tsaClient.getTimeStampToken(
-                new ByteArrayInputStream(signer.getSignature()));
+        TimeStampToken timeStampToken = tsaClient.getTimeStampToken(signer.getSignature());
         byte[] token = timeStampToken.getEncoded();
         ASN1ObjectIdentifier oid = PKCSObjectIdentifiers.id_aa_signatureTimeStampToken;
         ASN1Encodable signatureTimeStamp = new Attribute(oid,

+ 55 - 38
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/cert/CRLVerifier.java

@@ -19,29 +19,44 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.cert.CRLException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLEntry;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
+
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
 import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.*;
-
-import javax.naming.Context;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.InitialDirContext;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.cert.*;
-import java.util.*;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
 
 /**
  * Copied from Apache CXF 2.4.9, initial version:
@@ -69,7 +84,6 @@ public final class CRLVerifier
      * @throws CertificateVerificationException if the certificate could not be verified
      * @throws RevokedCertificateException if the certificate is revoked
      */
-    @SuppressWarnings({"squid:S1141"}) // nested exception needed to try several distribution points
     public static void verifyCertificateCRLs(X509Certificate cert, Date signDate,
             Set<X509Certificate> additionalCerts)
             throws CertificateVerificationException, RevokedCertificateException
@@ -82,16 +96,14 @@ public final class CRLVerifier
             for (String crlDistributionPointsURL : crlDistributionPointsURLs)
             {
                 LOG.info("Checking distribution point URL: " + crlDistributionPointsURL);
-
                 X509CRL crl;
                 try
                 {
                     crl = downloadCRL(crlDistributionPointsURL);
                 }
-                catch (IOException | GeneralSecurityException | CertificateVerificationException | NamingException ex)
+                catch (Exception ex)
                 {
                     // e.g. LDAP behind corporate proxy
-                    // but couldn't get LDAP to work at all, see e.g. file from PDFBOX-1452
                     LOG.warn("Caught " + ex.getClass().getSimpleName() + " downloading CRL, will try next distribution point if available");
                     if (firstException == null)
                     {
@@ -111,7 +123,7 @@ public final class CRLVerifier
                 {
                     try
                     {
-                        cert.verify(possibleCert.getPublicKey(), SecurityProvider.getProvider());
+                        cert.verify(possibleCert.getPublicKey(), SecurityProvider.getProvider().getName());
                         crlIssuerCert = possibleCert;
                         break;
                     }
@@ -127,7 +139,7 @@ public final class CRLVerifier
                             "not found in certificate chain, so the CRL at " +
                             crlDistributionPointsURL + " could not be verified");
                 }
-                crl.verify(crlIssuerCert.getPublicKey(), SecurityProvider.getProvider());
+                crl.verify(crlIssuerCert.getPublicKey(), SecurityProvider.getProvider().getName());
                 //TODO these should be exceptions, but for that we need a test case where
                 // a PDF has a broken OCSP and a working CRL
                 if (crl.getThisUpdate().after(now))
@@ -166,7 +178,11 @@ public final class CRLVerifier
                 throw firstException;
             }
         }
-        catch (RevokedCertificateException | CertificateVerificationException ex)
+        catch (CertificateVerificationException ex)
+        {
+            throw ex;
+        }
+        catch (RevokedCertificateException ex)
         {
             throw ex;
         }
@@ -245,7 +261,7 @@ public final class CRLVerifier
             CertificateVerificationException
     {
         @SuppressWarnings({"squid:S1149"})
-        Hashtable<String, String> env = new Hashtable<>();
+        Hashtable<String, String> env = new Hashtable<String, String>();
         env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
         env.put(Context.PROVIDER_URL, ldapURL);
 
@@ -276,10 +292,15 @@ public final class CRLVerifier
     public static X509CRL downloadCRLFromWeb(String crlURL)
             throws IOException, CertificateException, CRLException
     {
-        try (InputStream crlStream = new URL(crlURL).openStream())
+        InputStream crlStream = new URL(crlURL).openStream();
+        try
         {
             return (X509CRL) CertificateFactory.getInstance("X.509").generateCRL(crlStream);
         }
+        finally
+        {
+            crlStream.close();
+        }
     }
 
     /**
@@ -288,7 +309,7 @@ public final class CRLVerifier
      * extension is unavailable, returns an empty list.
      * @param cert
      * @return List of CRL distribution point URLs.
-     * @throws IOException
+     * @throws java.io.IOException
      */
     public static List<String> getCrlDistributionPoints(X509Certificate cert)
             throws IOException
@@ -296,29 +317,25 @@ public final class CRLVerifier
         byte[] crldpExt = cert.getExtensionValue(Extension.cRLDistributionPoints.getId());
         if (crldpExt == null)
         {
-            return new ArrayList<>();
-        }
-        ASN1Primitive derObjCrlDP;
-        try (ASN1InputStream oAsnInStream = new ASN1InputStream(crldpExt))
-        {
-            derObjCrlDP = oAsnInStream.readObject();
+            return new ArrayList<String>();
         }
+        ASN1InputStream oAsnInStream = new ASN1InputStream(crldpExt);
+        ASN1Primitive derObjCrlDP = oAsnInStream.readObject();
+        oAsnInStream.close();
         if (!(derObjCrlDP instanceof ASN1OctetString))
         {
             LOG.warn("CRL distribution points for certificate subject " +
                     cert.getSubjectX500Principal().getName() +
                     " should be an octet string, but is " + derObjCrlDP);
-            return new ArrayList<>();
+            return new ArrayList<String>();
         }
         ASN1OctetString dosCrlDP = (ASN1OctetString) derObjCrlDP;
         byte[] crldpExtOctets = dosCrlDP.getOctets();
-        ASN1Primitive derObj2;
-        try (ASN1InputStream oAsnInStream2 = new ASN1InputStream(crldpExtOctets))
-        {
-            derObj2 = oAsnInStream2.readObject();
-        }
+        ASN1InputStream oAsnInStream2 = new ASN1InputStream(crldpExtOctets);
+        ASN1Primitive derObj2 = oAsnInStream2.readObject();
+        oAsnInStream2.close();
         CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2);
-        List<String> crlUrls = new ArrayList<>();
+        List<String> crlUrls = new ArrayList<String>();
         for (DistributionPoint dp : distPoint.getDistributionPoints())
         {
             DistributionPointName dpn = dp.getDistributionPoint();

+ 1 - 1
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/cert/CertificateVerificationResult.java

@@ -28,7 +28,7 @@ import java.security.cert.PKIXCertPathBuilderResult;
  */
 public class CertificateVerificationResult
 {
-    private final boolean valid;
+    private boolean valid;
     private PKIXCertPathBuilderResult result;
     private Throwable exception;
 

+ 80 - 32
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/cert/CertificateVerifier.java

@@ -19,10 +19,41 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXCertPathBuilderResult;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509Extension;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
-import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.GeneralName;
@@ -34,16 +65,6 @@ import org.bouncycastle.cert.ocsp.BasicOCSPResp;
 import org.bouncycastle.cert.ocsp.OCSPException;
 import org.bouncycastle.cert.ocsp.OCSPResp;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.security.cert.*;
-import java.util.*;
-
 /**
  * Copied from Apache CXF 2.4.9, initial version:
  * https://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.9/distribution/src/main/release/samples/sts_issue_operation/src/main/java/demo/sts/provider/cert/
@@ -92,19 +113,19 @@ public final class CertificateVerifier
                 throw new CertificateVerificationException("The certificate is self-signed.");
             }
 
-            Set<X509Certificate> certSet = new HashSet<>(additionalCerts);
+            Set<X509Certificate> certSet = new HashSet<X509Certificate>(additionalCerts);
 
             // Download extra certificates. However, each downloaded certificate can lead to
             // more extra certificates, e.g. with the file from PDFBOX-4091, which has
             // an incomplete chain.
             // You can skip this block if you know that the certificate chain is complete
-            Set<X509Certificate> certsToTrySet = new HashSet<>();
+            Set<X509Certificate> certsToTrySet = new HashSet<X509Certificate>();
             certsToTrySet.add(cert);
             certsToTrySet.addAll(additionalCerts);
             int downloadSize = 0;
             while (!certsToTrySet.isEmpty())
             {
-                Set<X509Certificate> nextCertsToTrySet = new HashSet<>();
+                Set<X509Certificate> nextCertsToTrySet = new HashSet<X509Certificate>();
                 for (X509Certificate tryCert : certsToTrySet)
                 {
                     Set<X509Certificate> downloadedExtraCertificatesSet =
@@ -128,8 +149,8 @@ public final class CertificateVerifier
 
             // Prepare a set of trust anchors (set of root CA certificates)
             // and a set of intermediate certificates
-            Set<X509Certificate> intermediateCerts = new HashSet<>();
-            Set<TrustAnchor> trustAnchors = new HashSet<>();
+            Set<X509Certificate> intermediateCerts = new HashSet<X509Certificate>();
+            Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
             for (X509Certificate additionalCert : certSet)
             {
                 if (isSelfSigned(additionalCert))
@@ -168,7 +189,7 @@ public final class CertificateVerifier
         {
             throw cvex;
         }
-        catch (IOException | GeneralSecurityException | RevokedCertificateException | OCSPException ex)
+        catch (Exception ex)
         {
             throw new CertificateVerificationException(
                     "Error verifying the certificate: "
@@ -192,7 +213,7 @@ public final class CertificateVerifier
         {
             try
             {
-                cert.verify(additionalCert.getPublicKey(), SecurityProvider.getProvider());
+                cert.verify(additionalCert.getPublicKey(), SecurityProvider.getProvider().getName());
                 issuerCert = additionalCert;
                 break;
             }
@@ -213,11 +234,17 @@ public final class CertificateVerifier
             {
                 verifyOCSP(ocspHelper, additionalCerts);
             }
-            catch (IOException | OCSPException ex)
+            catch (IOException ex)
+            {
+                // happens with 021496.pdf because OCSP responder no longer exists
+                LOG.warn("IOException trying OCSP, will try CRL", ex);
+                LOG.warn("Certificate# to check: " + cert.getSerialNumber().toString(16));
+                CRLVerifier.verifyCertificateCRLs(cert, signDate, additionalCerts);
+            }
+            catch (OCSPException ex)
             {
-                // IOException happens with 021496.pdf because OCSP responder no longer exists
-                // OCSPException happens with QV_RCA1_RCA3_CPCPS_V4_11.pdf
-                LOG.warn("Exception trying OCSP, will try CRL", ex);
+                // happens with QV_RCA1_RCA3_CPCPS_V4_11.pdf
+                LOG.warn("OCSPException trying OCSP, will try CRL", ex);
                 LOG.warn("Certificate# to check: " + cert.getSerialNumber().toString(16));
                 CRLVerifier.verifyCertificateCRLs(cert, signDate, additionalCerts);
             }
@@ -239,7 +266,7 @@ public final class CertificateVerifier
      * Checks whether given X.509 certificate is self-signed.
      * @param cert The X.509 certificate to check.
      * @return true if the certificate is self-signed, false if not.
-     * @throws GeneralSecurityException
+     * @throws java.security.GeneralSecurityException 
      */
     public static boolean isSelfSigned(X509Certificate cert) throws GeneralSecurityException
     {
@@ -247,10 +274,22 @@ public final class CertificateVerifier
         {
             // Try to verify certificate signature with its own public key
             PublicKey key = cert.getPublicKey();
-            cert.verify(key, SecurityProvider.getProvider());
+            cert.verify(key, SecurityProvider.getProvider().getName());
             return true;
         }
-        catch (SignatureException | InvalidKeyException | IOException ex)
+        catch (SignatureException ex)
+        {
+            // Invalid signature --> not self-signed
+            LOG.debug("Couldn't get signature information - returning false", ex);
+            return false;
+        }
+        catch (InvalidKeyException ex)
+        {
+            // Invalid signature --> not self-signed
+            LOG.debug("Couldn't get signature information - returning false", ex);
+            return false;
+        }
+        catch (IOException ex)
         {
             // Invalid signature --> not self-signed
             LOG.debug("Couldn't get signature information - returning false", ex);
@@ -271,7 +310,7 @@ public final class CertificateVerifier
         // https://tools.ietf.org/html/rfc2459#section-4.2.2.1
         // https://tools.ietf.org/html/rfc3280#section-4.2.2.1
         // https://tools.ietf.org/html/rfc4325
-        Set<X509Certificate> resultSet = new HashSet<>();
+        Set<X509Certificate> resultSet = new HashSet<X509Certificate>();
         byte[] authorityExtensionValue = ext.getExtensionValue(Extension.authorityInfoAccess.getId());
         if (authorityExtensionValue == null)
         {
@@ -306,12 +345,17 @@ public final class CertificateVerifier
             ASN1TaggedObject location = (ASN1TaggedObject) obj.getObjectAt(1);
             ASN1OctetString uri = (ASN1OctetString) location.getObject();
             String urlString = new String(uri.getOctets());
-            LOG.info("CA issuers URL: " + urlString);
-            try (InputStream in = new URL(urlString).openStream())
+            InputStream in = null;
+            try
             {
+                LOG.info("CA issuers URL: " + urlString);
+                in = new URL(urlString).openStream();
                 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
                 Collection<? extends Certificate> altCerts = certFactory.generateCertificates(in);
-                altCerts.forEach(altCert -> resultSet.add((X509Certificate) altCert));
+                for (Certificate altCert : altCerts)
+                {
+                    resultSet.add((X509Certificate) altCert);
+                }
                 LOG.info("CA issuers URL: " + altCerts.size() + " certificate(s) downloaded");
             }
             catch (IOException ex)
@@ -322,6 +366,10 @@ public final class CertificateVerifier
             {
                 LOG.warn(ex.getMessage(), ex);
             }
+            finally
+            {
+                IOUtils.closeQuietly(in);
+            }
         }
         LOG.info("CA issuers: Downloaded " + resultSet.size() + " certificate(s) total");
         return resultSet;
@@ -420,8 +468,8 @@ public final class CertificateVerifier
     }
 
     /**
-     * Verify whether the certificate has been revoked at signing date, and verify whether
-     * the certificate of the responder has been revoked now.
+     * Verify whether the certificate has been revoked at signing date, and verify whether the
+     * certificate of the responder has been revoked now.
      *
      * @param ocspHelper the OCSP helper.
      * @param additionalCerts
@@ -462,7 +510,7 @@ public final class CertificateVerifier
         }
 
         LOG.info("Check of OCSP responder certificate");
-        Set<X509Certificate> additionalCerts2 = new HashSet<>(additionalCerts);
+        Set<X509Certificate> additionalCerts2 = new HashSet<X509Certificate>(additionalCerts);
         JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
         for (X509CertificateHolder certHolder : basicResponse.getCerts())
         {

+ 45 - 22
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/cert/OcspHelper.java

@@ -16,9 +16,30 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert;
 
-import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.SigUtils;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Random;
+import java.util.Set;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.SigUtils;
+import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DLSequence;
@@ -34,28 +55,20 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
-import org.bouncycastle.cert.ocsp.*;
+import org.bouncycastle.cert.ocsp.BasicOCSPResp;
+import org.bouncycastle.cert.ocsp.CertificateID;
+import org.bouncycastle.cert.ocsp.CertificateStatus;
+import org.bouncycastle.cert.ocsp.OCSPException;
+import org.bouncycastle.cert.ocsp.OCSPReq;
+import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
+import org.bouncycastle.cert.ocsp.OCSPResp;
+import org.bouncycastle.cert.ocsp.RevokedStatus;
+import org.bouncycastle.cert.ocsp.SingleResp;
 import org.bouncycastle.operator.ContentVerifierProvider;
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.*;
-
 /**
  * Helper Class for OCSP-Operations with bouncy castle.
  * 
@@ -73,7 +86,7 @@ public class OcspHelper
     private DEROctetString encodedNonce;
     private X509Certificate ocspResponderCertificate;
     private final JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
-    
+
     // SecureRandom.getInstanceStrong() would be better, but sometimes blocks on Linux
     private static final Random RANDOM = new SecureRandom();
 
@@ -234,7 +247,7 @@ public class OcspHelper
         }
     }
 
-    private byte[] getKeyHashFromCertHolder(X509CertificateHolder certHolder)
+    private byte[] getKeyHashFromCertHolder(X509CertificateHolder certHolder) throws IOException
     {
         // https://tools.ietf.org/html/rfc2560#section-4.2.1
         // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
@@ -449,10 +462,15 @@ public class OcspHelper
             httpConnection.setRequestProperty("Content-Type", "application/ocsp-request");
             httpConnection.setRequestProperty("Accept", "application/ocsp-response");
             httpConnection.setDoOutput(true);
-            try (OutputStream out = httpConnection.getOutputStream())
+            OutputStream out = httpConnection.getOutputStream();
+            try
             {
                 out.write(request.getEncoded());
             }
+            finally
+            {
+                IOUtils.closeQuietly(out);
+            }
 
             if (httpConnection.getResponseCode() != 200)
             {
@@ -460,10 +478,15 @@ public class OcspHelper
                         + httpConnection.getResponseCode());
             }
             // Get response
-            try (InputStream in = (InputStream) httpConnection.getContent())
+            InputStream in = (InputStream) httpConnection.getContent();
+            try
             {
                 return new OCSPResp(in);
             }
+            finally
+            {
+                IOUtils.closeQuietly(in);
+            }
         }
         finally
         {

+ 107 - 45
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/validation/AddValidationInformation.java

@@ -17,15 +17,38 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature.validation;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.cos.COSUpdateInfo;
 import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.SigUtils;
 import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CRLVerifier;
 import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerificationException;
 import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.OcspHelper;
 import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.RevokedCertificateException;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.pdfbox.Loader;
-import org.apache.pdfbox.cos.*;
+import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.validation.CertInformationCollector.CertSignatureInformation;
+import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
 import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
@@ -36,18 +59,6 @@ import org.bouncycastle.cert.ocsp.BasicOCSPResp;
 import org.bouncycastle.cert.ocsp.OCSPException;
 import org.bouncycastle.cert.ocsp.OCSPResp;
 
-import java.io.*;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.Security;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509CRL;
-import java.security.cert.X509Certificate;
-import java.util.Calendar;
-import java.util.HashSet;
-import java.util.Set;
-
 /**
  * An example for adding Validation Information to a signed PDF, inspired by ETSI TS 102 778-4
  * V1.1.2 (2009-12), Part 4: PAdES Long Term - PAdES-LTV Profile. This procedure appends the
@@ -70,9 +81,9 @@ public class AddValidationInformation
     private COSArray crls;
     private COSArray certs;
     private PDDocument document;
-    private final Set<X509Certificate> foundRevocationInformation = new HashSet<>();
+    private final Set<X509Certificate> foundRevocationInformation = new HashSet<X509Certificate>();
     private Calendar signDate;
-    private final Set<X509Certificate> ocspChecked = new HashSet<>();
+    private final Set<X509Certificate> ocspChecked = new HashSet<X509Certificate>();
     //TODO foundRevocationInformation and ocspChecked have a similar purpose. One of them should likely
     // be removed and the code improved. When doing so, keep in mind that ocspChecked was added last,
     // because of a problem with freetsa.
@@ -100,19 +111,19 @@ public class AddValidationInformation
             throw new FileNotFoundException(err);
         }
 
-        try (PDDocument doc = Loader.loadPDF(inFile);
-             FileOutputStream fos = new FileOutputStream(outFile))
+        PDDocument doc = PDDocument.load(inFile);
+        FileOutputStream fos = new FileOutputStream(outFile);
+        int accessPermissions = SigUtils.getMDPPermission(doc);
+        if (accessPermissions == 1)
         {
-            int accessPermissions = SigUtils.getMDPPermission(doc);
-            if (accessPermissions == 1)
-            {
-                System.out.println("PDF is certified to forbid changes, "
+            System.out.println("PDF is certified to forbid changes, "
                         + "some readers may report the document as invalid despite that "
                         + "the PDF specification allows DSS additions");
-            }
-            document = doc;
-            doValidation(inFile.getAbsolutePath(), fos);
         }
+        document = doc;
+        doValidation(inFile.getAbsolutePath(), fos);
+        fos.close();
+        doc.close();
     }
 
     /**
@@ -126,7 +137,7 @@ public class AddValidationInformation
     private void doValidation(String filename, OutputStream output) throws IOException
     {
         certInformationHelper = new CertInformationCollector();
-        CertInformationCollector.CertSignatureInformation certInfo = null;
+        CertSignatureInformation certInfo = null;
         try
         {
             PDSignature signature = SigUtils.getLastRelevantSignature(document);
@@ -201,9 +212,29 @@ public class AddValidationInformation
             {
                 result = clazz.getDeclaredConstructor().newInstance();
             }
-            catch (ReflectiveOperationException | SecurityException e)
+            catch (InstantiationException ex)
+            {
+                throw new IOException("Failed to create new instance of " + clazz.getCanonicalName(), ex);
+            }
+            catch (IllegalAccessException ex)
+            {
+                throw new IOException("Failed to create new instance of " + clazz.getCanonicalName(), ex);
+            }
+            catch (NoSuchMethodException ex)
             {
-                throw new IOException("Failed to create new instance of " + clazz.getCanonicalName(), e);
+                throw new IOException("Failed to create new instance of " + clazz.getCanonicalName(), ex);
+            }
+            catch (SecurityException ex)
+            {
+                throw new IOException("Failed to create new instance of " + clazz.getCanonicalName(), ex);
+            }
+            catch (IllegalArgumentException ex)
+            {
+                throw new IOException("Failed to create new instance of " + clazz.getCanonicalName(), ex);
+            }
+            catch (InvocationTargetException ex)
+            {
+                throw new IOException("Failed to create new instance of " + clazz.getCanonicalName(), ex);
             }
             result.setDirect(false);
             parent.setItem(COSName.getPDFName(name), result);
@@ -218,7 +249,7 @@ public class AddValidationInformation
      * chains.
      * @throws IOException
      */
-    private void addRevocationData(CertInformationCollector.CertSignatureInformation certInfo) throws IOException
+    private void addRevocationData(CertSignatureInformation certInfo) throws IOException
     {
         COSDictionary vri = new COSDictionary();
         vriBase.setItem(certInfo.getSignatureHash(), vri);
@@ -241,7 +272,7 @@ public class AddValidationInformation
      * chains.
      * @throws IOException when failed to fetch an revocation data.
      */
-    private void addRevocationDataRecursive(CertInformationCollector.CertSignatureInformation certInfo) throws IOException
+    private void addRevocationDataRecursive(CertSignatureInformation certInfo) throws IOException
     {
         if (certInfo.isSelfSigned())
         {
@@ -290,14 +321,24 @@ public class AddValidationInformation
      * @return true when the OCSP data has successfully been fetched and added
      * @throws IOException when Certificate is revoked.
      */
-    private boolean fetchOcspData(CertInformationCollector.CertSignatureInformation certInfo) throws IOException
+    private boolean fetchOcspData(CertSignatureInformation certInfo) throws IOException
     {
         try
         {
             addOcspData(certInfo);
             return true;
         }
-        catch (OCSPException | CertificateProccessingException | IOException e)
+        catch (OCSPException e)
+        {
+            LOG.warn("Failed fetching Ocsp", e);
+            return false;
+        }
+        catch (CertificateProccessingException e)
+        {
+            LOG.warn("Failed fetching Ocsp", e);
+            return false;
+        }
+        catch (IOException e)
         {
             LOG.warn("Failed fetching Ocsp", e);
             return false;
@@ -315,13 +356,28 @@ public class AddValidationInformation
      * @throws IOException when failed to fetch, because no validation data could be fetched for
      * data.
      */
-    private void fetchCrlData(CertInformationCollector.CertSignatureInformation certInfo) throws IOException
+    private void fetchCrlData(CertSignatureInformation certInfo) throws IOException
     {
         try
         {
             addCrlRevocationInfo(certInfo);
         }
-        catch (GeneralSecurityException | IOException | RevokedCertificateException | CertificateVerificationException e)
+        catch (GeneralSecurityException e)
+        {
+            LOG.warn("Failed fetching CRL", e);
+            throw new IOException(e);
+        }
+        catch (RevokedCertificateException e)
+        {
+            LOG.warn("Failed fetching CRL", e);
+            throw new IOException(e);
+        }
+        catch (IOException e)
+        {
+            LOG.warn("Failed fetching CRL", e);
+            throw new IOException(e);
+        }
+        catch (CertificateVerificationException e)
         {
             LOG.warn("Failed fetching CRL", e);
             throw new IOException(e);
@@ -337,7 +393,7 @@ public class AddValidationInformation
      * @throws CertificateProccessingException
      * @throws RevokedCertificateException
      */
-    private void addOcspData(CertInformationCollector.CertSignatureInformation certInfo) throws IOException, OCSPException,
+    private void addOcspData(CertSignatureInformation certInfo) throws IOException, OCSPException,
             CertificateProccessingException, RevokedCertificateException
     {
         if (ocspChecked.contains(certInfo.getCertificate()))
@@ -349,7 +405,7 @@ public class AddValidationInformation
                 certInfo.getCertificate(),
                 signDate.getTime(),
                 certInfo.getIssuerCertificate(),
-                new HashSet<>(certInformationHelper.getCertificateSet()),
+                new HashSet<X509Certificate>(certInformationHelper.getCertificateSet()),
                 certInfo.getOcspUrl());
         OCSPResp ocspResp = ocspHelper.getResponseOcsp();
         ocspChecked.add(certInfo.getCertificate());
@@ -374,7 +430,7 @@ public class AddValidationInformation
 
             COSDictionary vri = new COSDictionary();
             vriBase.setItem(signatureHashHex, vri);
-            CertInformationCollector.CertSignatureInformation ocspCertInfo = certInformationHelper.getCertInfo(ocspResponderCertificate);
+            CertSignatureInformation ocspCertInfo = certInformationHelper.getCertInfo(ocspResponderCertificate);
 
             updateVRI(ocspCertInfo, vri);
 
@@ -402,7 +458,7 @@ public class AddValidationInformation
      * @throws GeneralSecurityException
      * @throws CertificateVerificationException 
      */
-    private void addCrlRevocationInfo(CertInformationCollector.CertSignatureInformation certInfo)
+    private void addCrlRevocationInfo(CertSignatureInformation certInfo)
             throws IOException, RevokedCertificateException, GeneralSecurityException,
             CertificateVerificationException
     {
@@ -445,7 +501,7 @@ public class AddValidationInformation
                 COSDictionary vri = new COSDictionary();
                 vriBase.setItem(signatureHashHex, vri);
 
-                CertInformationCollector.CertSignatureInformation crlCertInfo;
+                CertSignatureInformation crlCertInfo;
                 try
                 {
                     crlCertInfo = certInformationHelper.getCertInfo(issuerCertificate);
@@ -464,7 +520,7 @@ public class AddValidationInformation
         foundRevocationInformation.add(certInfo.getCertificate());
     }
 
-    private void updateVRI(CertInformationCollector.CertSignatureInformation certInfo, COSDictionary vri) throws IOException
+    private void updateVRI(CertSignatureInformation certInfo, COSDictionary vri) throws IOException
     {
         if (certInfo.getCertificate().getExtensionValue(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck.getId()) == null)
         {
@@ -482,7 +538,7 @@ public class AddValidationInformation
         }
 
         COSArray correspondingCerts = new COSArray();
-        CertInformationCollector.CertSignatureInformation ci = certInfo;
+        CertSignatureInformation ci = certInfo;
         do
         {
             X509Certificate cert = ci.getCertificate();
@@ -542,10 +598,16 @@ public class AddValidationInformation
     private COSStream writeDataToStream(byte[] data) throws IOException
     {
         COSStream stream = document.getDocument().createCOSStream();
-        try (OutputStream os = stream.createOutputStream(COSName.FLATE_DECODE))
+        OutputStream os = null;
+        try
         {
+            os = stream.createOutputStream(COSName.FLATE_DECODE);
             os.write(data);
         }
+        finally
+        {
+            IOUtils.closeQuietly(os);
+        }
         return stream;
     }
 
@@ -571,7 +633,7 @@ public class AddValidationInformation
         catalog.setVersion("1.7");
     }
 
-    public static void main(String[] args) throws IOException
+    public static void main(String[] args) throws IOException, GeneralSecurityException
     {
         if (args.length != 1)
         {

+ 38 - 28
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/validation/CertInformationCollector.java

@@ -17,9 +17,23 @@
 
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature.validation;
 
-import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerifier;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.cert.CertificateVerifier;
+import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
 import org.bouncycastle.asn1.ASN1Encodable;
@@ -36,19 +50,6 @@ import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.util.Selector;
 import org.bouncycastle.util.Store;
 
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
 /**
  * This class helps to extract data/information from a signature. The information is held in
  * CertSignatureInformation. Some information is needed for validation processing of the
@@ -63,8 +64,8 @@ public class CertInformationCollector
 
     private static final int MAX_CERTIFICATE_CHAIN_DEPTH = 5;
 
-    private final Set<X509Certificate> certificateSet = new HashSet<>();
-    private final Set<String> urlSet = new HashSet<>();
+    private final Set<X509Certificate> certificateSet = new HashSet<X509Certificate>();
+    private final Set<String> urlSet = new HashSet<String>();
 
     private final JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter();
 
@@ -82,11 +83,17 @@ public class CertInformationCollector
     public CertSignatureInformation getLastCertInfo(PDSignature signature, String fileName)
             throws CertificateProccessingException, IOException
     {
-        try (FileInputStream documentInput = new FileInputStream(fileName))
+        FileInputStream documentInput = null;
+        try
         {
+            documentInput = new FileInputStream(fileName);
             byte[] signatureContent = signature.getContents(documentInput);
             return getCertInfo(signatureContent);
         }
+        finally
+        {
+            IOUtils.closeQuietly(documentInput);
+        }
     }
 
     /**
@@ -244,7 +251,7 @@ public class CertInformationCollector
         {
             try
             {
-                certificate.verify(issuer.getPublicKey(), SecurityProvider.getProvider());
+                certificate.verify(issuer.getPublicKey(), SecurityProvider.getProvider().getName());
                 LOG.info("Found the right Issuer Cert! for Cert: " + certificate.getSubjectX500Principal()
                     + "\n" + issuer.getSubjectX500Principal());
                 certInfo.issuerCertificate = issuer;
@@ -289,17 +296,20 @@ public class CertInformationCollector
         {
             URL certUrl = new URL(certInfo.issuerUrl);
             CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
-            try (InputStream in = certUrl.openStream())
-            {
-                X509Certificate altIssuerCert = (X509Certificate) certFactory
-                        .generateCertificate(in);
-                certificateSet.add(altIssuerCert);
+            InputStream in = certUrl.openStream();
 
-                certInfo.alternativeCertChain = new CertSignatureInformation();
-                traverseChain(altIssuerCert, certInfo.alternativeCertChain, maxDepth - 1);
-            }
+            X509Certificate altIssuerCert = (X509Certificate) certFactory.generateCertificate(in);
+            certificateSet.add(altIssuerCert);
+
+            certInfo.alternativeCertChain = new CertSignatureInformation();
+            traverseChain(altIssuerCert, certInfo.alternativeCertChain, maxDepth - 1);
+            in.close();
         }
-        catch (IOException | CertificateException e)
+        catch (IOException e)
+        {
+            LOG.error("Error getting alternative issuer certificate from " + certInfo.issuerUrl, e);
+        }
+        catch (CertificateException e)
         {
             LOG.error("Error getting alternative issuer certificate from " + certInfo.issuerUrl, e);
         }
@@ -395,7 +405,7 @@ public class CertInformationCollector
     /**
      * Data class to hold Signature, Certificate (and its chain(s)) and revocation Information
      */
-    public static class CertSignatureInformation
+    public class CertSignatureInformation
     {
         private X509Certificate certificate;
         private String signatureHash;

+ 7 - 6
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/signature/validation/CertInformationHelper.java

@@ -16,8 +16,14 @@
  */
 package com.x.processplatform.assemble.surface.jaxrs.attachment.signature.validation;
 
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Enumeration;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import com.x.processplatform.assemble.surface.jaxrs.attachment.signature.validation.CertInformationCollector.CertSignatureInformation;
 import org.apache.pdfbox.util.Hex;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -27,11 +33,6 @@ import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
 
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Enumeration;
-
 public class CertInformationHelper
 {
     private static final Log LOG = LogFactory.getLog(CertInformationHelper.class);
@@ -69,7 +70,7 @@ public class CertInformationHelper
      * @throws IOException when there is a problem with the extensionValue
      */
     protected static void getAuthorityInfoExtensionValue(byte[] extensionValue,
-            CertInformationCollector.CertSignatureInformation certInfo) throws IOException
+            CertSignatureInformation certInfo) throws IOException
     {
         ASN1Sequence asn1Seq = (ASN1Sequence) JcaX509ExtensionUtils.parseExtensionValue(extensionValue);
         Enumeration<?> objects = asn1Seq.getObjects();

二進制
o2server/x_processplatform_assemble_surface/src/main/resources/myKeystore.p12


二進制
o2server/x_processplatform_assemble_surface/src/main/resources/sig.jpg


二進制
o2server/x_processplatform_assemble_surface/src/main/resources/sig.png