Author: tilman
Date: Tue Mar  6 21:00:28 2018
New Revision: 1826043

URL: http://svn.apache.org/viewvc?rev=1826043&view=rev
Log:
PDFBOX-4020: embed TimeStamp into existing signature + show DSS + refactor, by 
Alexis Suter

Added:
    
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateEmbeddedTimeStamp.java
   (with props)
    
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ValidationTimeStamp.java
   (with props)
Modified:
    
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
    
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java
    
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignedTimeStamp.java
    
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
    
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature2.java
    
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java
    
pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java

Added: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateEmbeddedTimeStamp.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateEmbeddedTimeStamp.java?rev=1826043&view=auto
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateEmbeddedTimeStamp.java
 (added)
+++ 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateEmbeddedTimeStamp.java
 Tue Mar  6 21:00:28 2018
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.signature;
+
+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.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSName;
+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 org.bouncycastle.tsp.TSPException;
+
+/**
+ * An example for timestamp-singing 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
+ * are signed by a Time Stamp Authority (TSA)).
+ *
+ * This method only changes the unsigned parameters of a signature, so that it 
is kept valid.
+ *
+ * Use case: sign offline to avoid zero-day attacks against the signing 
machine. Once the signature
+ * is there and the pdf is transferred to a network connected machine, one is 
likely to want to add
+ * a timestamp. (Ralf Hauser)
+ *
+ * @author Alexis Suter
+ */
+public class CreateEmbeddedTimeStamp
+{
+    private final String tsaUrl;
+    private PDDocument document;
+    private PDSignature signature;
+    private byte[] changedEncodedSignature;
+
+    public CreateEmbeddedTimeStamp(String tsaUrl)
+    {
+        this.tsaUrl = tsaUrl;
+    }
+
+    /**
+     * Embeds the given PDF file with signed timestamp(s). Alters the original 
file on disk.
+     * 
+     * @param file the PDF file to sign and to overwrite
+     * @throws IOException
+     */
+    public void embedTimeStamp(File file) throws IOException
+    {
+        embedTimeStamp(file, file);
+    }
+
+    /**
+     * Embeds signed timestamp(s) into existing signatures of the given 
document
+     * 
+     * @param inFile The pdf file possibly containing signatures
+     * @param outFile Where the changed document will be saved
+     * @throws IOException
+     */
+    public void embedTimeStamp(File inFile, File outFile) throws IOException
+    {
+        if (inFile == null || !inFile.exists())
+        {
+            throw new FileNotFoundException("Document for signing does not 
exist");
+        }
+
+        // sign
+        PDDocument doc = PDDocument.load(inFile);
+        document = doc;
+        processTimeStamping(outFile, inFile.getAbsolutePath());
+        doc.close();
+    }
+
+    /**
+     * Processes the timestamping of the Signature.
+     * 
+     * @param output Where the new file will be written to
+     * @param fileName of the existing file containing the pdf
+     * @throws IOException
+     */
+    private void processTimeStamping(File outFile, String fileName) throws 
IOException
+    {
+        int accessPermissions = SigUtils.getMDPPermission(document);
+        if (accessPermissions == 1)
+        {
+            throw new IllegalStateException(
+                    "No changes to the document are permitted due to DocMDP 
transform parameters dictionary");
+        }
+
+        try
+        {
+            byte[] documentBytes;
+            FileInputStream fis = new FileInputStream(fileName);
+            documentBytes = IOUtils.toByteArray(fis);
+            fis.close();
+            processRelevantSignatures(documentBytes);
+
+            if (changedEncodedSignature != null)
+            {
+                FileOutputStream output = new FileOutputStream(outFile);
+                embedNewSignatureIntoDocument(documentBytes, output);
+                output.close();
+            }
+        }
+        catch (IOException e)
+        {
+            throw new IOException(e);
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new IOException(e);
+        }
+        catch (CMSException e)
+        {
+            throw new IOException(e);
+        }
+        catch (TSPException e)
+        {
+            throw new IOException(e);
+        }
+    }
+
+    /**
+     * Create changed Signature with embedded TimeStamp from TSA
+     * 
+     * @param documentInput Stream of the input file
+     * @throws IOException
+     * @throws CMSException
+     * @throws NoSuchAlgorithmException
+     * @throws TSPException
+     */
+    private void processRelevantSignatures(byte[] documentBytes)
+            throws IOException, CMSException, NoSuchAlgorithmException, 
TSPException
+    {
+        getRelevantSignature(document);
+        if (signature != null)
+        {
+            byte[] sigBlock = signature.getContents(documentBytes);
+            CMSSignedData signedData = new CMSSignedData(sigBlock);
+
+            // System.out.println("INFO: Old Signature: " + 
Hex.getString(sigBlock));
+            System.out.println("INFO: Byte Range: " + 
Arrays.toString(signature.getByteRange()));
+
+            if (tsaUrl != null && tsaUrl.length() > 0)
+            {
+                ValidationTimeStamp validation = new 
ValidationTimeStamp(tsaUrl);
+                signedData = validation.addSignedTimeStamp(signedData);
+            }
+
+            byte[] newEncoded = Hex.getBytes(signedData.getEncoded());
+            int maxSize = signature.getByteRange()[2] - 
signature.getByteRange()[1];
+            System.out.println(
+                    "INFO: New Signature has Size: " + newEncoded.length + " 
maxSize: " + maxSize);
+
+            if (newEncoded.length > maxSize - 2)
+            {
+                throw new IOException(
+                        "New Signature is too big for existing 
Signature-Placeholder. Max Place: "
+                                + maxSize);
+            }
+            else
+            {
+                changedEncodedSignature = newEncoded;
+            }
+        }
+    }
+
+    /**
+     * Extracts last Document-Signature from the document
+     * 
+     * @param document to get the Signature from
+     * @return the Signature, where a TimeStamp will be added. 
<code>null</code> when no Document-Signature available.
+     * @throws IOException
+     */
+    private void getRelevantSignature(PDDocument document) throws IOException
+    {
+        SortedMap<Integer, PDSignature> sortedMap = new TreeMap<Integer, 
PDSignature>();
+        for (PDSignature sig : document.getSignatureDictionaries())
+        {
+            int sigOffset = sig.getByteRange()[1];
+            sortedMap.put(sigOffset, sig);
+        }
+        if (sortedMap.size() > 0)
+        {
+            PDSignature lastSignature = sortedMap.get(sortedMap.lastKey());
+            COSBase type = lastSignature.getCOSObject().getItem(COSName.TYPE);
+            if (type.equals(COSName.SIG))
+            {
+                signature = lastSignature;
+            }
+        }
+    }
+
+    /**
+     * Embeds the new signature into the document, by copying the rest of the 
document
+     * 
+     * @param docBytes byte array of the document
+     * @param output target, where the file will be written
+     * @throws IOException
+     */
+    private void embedNewSignatureIntoDocument(byte[] docBytes, OutputStream 
output)
+            throws IOException
+    {
+        int[] byteRange = signature.getByteRange();
+        output.write(docBytes, byteRange[0], byteRange[1] + 1);
+        output.write(changedEncodedSignature);
+        int addingLength = byteRange[2] - byteRange[1] - 2 - 
changedEncodedSignature.length;
+        byte[] zeroes = Hex.getBytes(new byte[(addingLength + 1) / 2]);
+        output.write(zeroes);
+        output.write(docBytes, byteRange[2] - 1, byteRange[3] + 1);
+    }
+
+    public static void main(String[] args) throws IOException, 
GeneralSecurityException
+    {
+        if (args.length != 3)
+        {
+            usage();
+            System.exit(1);
+        }
+
+        String tsaUrl = null;
+        for (int i = 0; i < args.length; i++)
+        {
+            if (args[i].equals("-tsa"))
+            {
+                i++;
+                if (i >= args.length)
+                {
+                    usage();
+                    System.exit(1);
+                }
+                tsaUrl = args[i];
+            }
+        }
+
+        File inFile = new File(args[0]);
+        System.out.println("Input File: " + args[0]);
+        String name = inFile.getName();
+        String substring = name.substring(0, name.lastIndexOf('.'));
+
+        File outFile = new File(inFile.getParent(), substring + "_eTs.pdf");
+        System.out.println("Output File: " + outFile.getAbsolutePath());
+
+        // Embed TimeStamp
+        CreateEmbeddedTimeStamp signing = new CreateEmbeddedTimeStamp(tsaUrl);
+        signing.embedTimeStamp(inFile, outFile);
+    }
+
+    private static void usage()
+    {
+        System.err.println("usage: java " + 
CreateEmbeddedTimeStamp.class.getName() + " "
+                + "<pdf_to_sign>\n" + "mandatory option:\n"
+                + "  -tsa <url>    sign timestamp using the given TSA 
server\n");
+    }
+}

Propchange: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateEmbeddedTimeStamp.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java?rev=1826043&r1=1826042&r2=1826043&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
 (original)
+++ 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
 Tue Mar  6 21:00:28 2018
@@ -22,11 +22,9 @@ import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.net.URL;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
@@ -35,6 +33,7 @@ import java.util.Calendar;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import 
org.apache.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
 
 /**
  * An example for singing a PDF with bouncy castle.
@@ -92,29 +91,29 @@ public class CreateSignature extends Cre
      * Signs the given PDF file.
      * @param inFile input PDF file
      * @param outFile output PDF file
-     * @param tsaClient optional TSA client
+     * @param tsaUrl optional TSA url
      * @throws IOException if the input file could not be read
      */
-    public void signDetached(File inFile, File outFile, TSAClient tsaClient) 
throws IOException
+    public void signDetached(File inFile, File outFile, String tsaUrl) throws 
IOException
     {
         if (inFile == null || !inFile.exists())
         {
             throw new FileNotFoundException("Document for signing does not 
exist");
         }
 
+        setTsaUrl(tsaUrl);
+
         FileOutputStream fos = new FileOutputStream(outFile);
 
         // sign
         PDDocument doc = PDDocument.load(inFile);
-        signDetached(doc, fos, tsaClient);
+        signDetached(doc, fos);
         doc.close();
     }
 
-    public void signDetached(PDDocument document, OutputStream output, 
TSAClient tsaClient)
+    public void signDetached(PDDocument document, OutputStream output)
             throws IOException
     {
-        setTsaClient(tsaClient);
-
         int accessPermissions = SigUtils.getMDPPermission(document);
         if (accessPermissions == 1)
         {
@@ -152,8 +151,11 @@ public class CreateSignature extends Cre
         }
         else
         {
+            SignatureOptions signatureOptions = new SignatureOptions();
+            // Size can vary, but should be enough for purpose.
+            
signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE
 * 2);
             // register signature dictionary and sign interface
-            document.addSignature(signature, this);
+            document.addSignature(signature, this, signatureOptions);
 
             // write incremental (only for signing purpose)
             document.saveIncremental(output);
@@ -194,14 +196,6 @@ public class CreateSignature extends Cre
         keystore.load(new FileInputStream(args[0]), password);
         // TODO alias command line argument
 
-        // TSA client
-        TSAClient tsaClient = null;
-        if (tsaUrl != null)
-        {
-            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            tsaClient = new TSAClient(new URL(tsaUrl), null, null, digest);
-        }
-
         // sign PDF
         CreateSignature signing = new CreateSignature(keystore, password);
         signing.setExternalSigning(externalSig);
@@ -211,7 +205,7 @@ public class CreateSignature extends Cre
         String substring = name.substring(0, name.lastIndexOf('.'));
 
         File outFile = new File(inFile.getParent(), substring + "_signed.pdf");
-        signing.signDetached(inFile, outFile, tsaClient);
+        signing.signDetached(inFile, outFile, tsaUrl);
     }
 
     private static void usage()

Modified: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java?rev=1826043&r1=1826042&r2=1826043&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java
 (original)
+++ 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignatureBase.java
 Tue Mar  6 21:00:28 2018
@@ -32,22 +32,11 @@ import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import 
org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
-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.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaCertStore;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSSignedData;
 import org.bouncycastle.cms.CMSSignedDataGenerator;
-import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.SignerInformationStore;
 import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
 import org.bouncycastle.operator.ContentSigner;
 import org.bouncycastle.operator.OperatorCreationException;
@@ -59,7 +48,7 @@ public abstract class CreateSignatureBas
 {
     private PrivateKey privateKey;
     private Certificate[] certificateChain;
-    private TSAClient tsaClient;
+    private String tsaUrl;
     private boolean externalSigning;
 
     /**
@@ -118,71 +107,14 @@ public abstract class CreateSignatureBas
         this.certificateChain = certificateChain;
     }
 
-    public void setTsaClient(TSAClient tsaClient)
+    public String getTsaUrl()
     {
-        this.tsaClient = tsaClient;
+        return tsaUrl;
     }
 
-    public TSAClient getTsaClient()
+    public void setTsaUrl(String tsaUrl)
     {
-        return tsaClient;
-    }
-
-    /**
-     * We just extend CMS signed Data
-     *
-     * @param signedData Generated CMS signed data
-     * @return CMSSignedData Extended CMS signed data
-     * @throws IOException
-     */
-    private CMSSignedData signTimeStamps(CMSSignedData signedData)
-            throws IOException
-    {
-        SignerInformationStore signerStore = signedData.getSignerInfos();
-        List<SignerInformation> newSigners = new 
ArrayList<SignerInformation>();
-
-        for (SignerInformation signer : signerStore.getSigners())
-        {
-            newSigners.add(signTimeStamp(signer));
-        }
-
-        // TODO do we have to return a new store?
-        return CMSSignedData.replaceSigners(signedData, new 
SignerInformationStore(newSigners));
-    }
-
-    /**
-     * We are extending CMS Signature
-     *
-     * @param signer information about signer
-     * @return information about SignerInformation
-     */
-    private SignerInformation signTimeStamp(SignerInformation signer) throws 
IOException
-    {
-        AttributeTable unsignedAttributes = signer.getUnsignedAttributes();
-
-        ASN1EncodableVector vector = new ASN1EncodableVector();
-        if (unsignedAttributes != null)
-        {
-            vector = unsignedAttributes.toASN1EncodableVector();
-        }
-
-        byte[] token = getTsaClient().getTimeStampToken(signer.getSignature());
-        ASN1ObjectIdentifier oid = 
PKCSObjectIdentifiers.id_aa_signatureTimeStampToken;
-        ASN1Encodable signatureTimeStamp = new Attribute(oid, new 
DERSet(ASN1Primitive.fromByteArray(token)));
-
-        vector.add(signatureTimeStamp);
-        Attributes signedAttributes = new Attributes(vector);
-
-        SignerInformation newSigner = 
SignerInformation.replaceUnsignedAttributes(
-                signer, new AttributeTable(signedAttributes));
-
-        // TODO can this actually happen?
-        if (newSigner == null)
-        {
-            return signer;
-        }
-
-        return newSigner;
+        this.tsaUrl = tsaUrl;
     }
 
     /**
@@ -200,7 +132,7 @@ public abstract class CreateSignatureBas
     @Override
     public byte[] sign(InputStream content) throws IOException
     {
-        //TODO this method should be private
+        // cannot be done private (interface)
         try
         {
             List<Certificate> certList = new ArrayList<Certificate>();
@@ -213,9 +145,10 @@ public abstract class CreateSignatureBas
             gen.addCertificates(certs);
             CMSProcessableInputStream msg = new 
CMSProcessableInputStream(content);
             CMSSignedData signedData = gen.generate(msg, false);
-            if (tsaClient != null)
+            if (tsaUrl != null && tsaUrl.length() > 0)
             {
-                signedData = signTimeStamps(signedData);
+                ValidationTimeStamp validation = new 
ValidationTimeStamp(tsaUrl);
+                signedData = validation.addSignedTimeStamp(signedData);
             }
             return signedData.getEncoded();
         }

Modified: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignedTimeStamp.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignedTimeStamp.java?rev=1826043&r1=1826042&r2=1826043&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignedTimeStamp.java
 (original)
+++ 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignedTimeStamp.java
 Tue Mar  6 21:00:28 2018
@@ -20,32 +20,42 @@ 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.net.URL;
 import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 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;
 
 /**
- * An example for timestamp-singing a PDF for PADeS-Specification. The 
document will only be
- * extended by a signed Timestamp (Signed Timestamp and Hash-Value of the 
document are signed by a
- * Time Stamp Authority (TSA)).
+ * An example for timestamp-singing 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
+ * are signed by a Time Stamp Authority (TSA)).
  *
  * @author Thomas Chojecki
  * @author Vakhtang Koroghlishvili
  * @author John Hewson
+ * @author Alexis Suter
  */
-public class CreateSignedTimeStamp extends CreateSignedTimestampBase
+public class CreateSignedTimeStamp implements SignatureInterface
 {
+    private static final Log LOG = 
LogFactory.getLog(CreateSignedTimeStamp.class);
+    
+    private final String tsaUrl;
 
     /**
      * Initialize the signed timestamp creator
+     * 
+     * @param tsaUrl The url where TS-Request will be done.
      */
-    public CreateSignedTimeStamp()
+    public CreateSignedTimeStamp(String tsaUrl)
     {
+        this.tsaUrl = tsaUrl;
     }
 
     /**
@@ -56,7 +66,7 @@ public class CreateSignedTimeStamp exten
      */
     public void signDetached(File file) throws IOException
     {
-        signDetached(file, file, null);
+        signDetached(file, file);
     }
 
     /**
@@ -68,19 +78,6 @@ public class CreateSignedTimeStamp exten
      */
     public void signDetached(File inFile, File outFile) throws IOException
     {
-        signDetached(inFile, outFile, null);
-    }
-
-    /**
-     * Signs the given PDF file.
-     * 
-     * @param inFile input PDF file
-     * @param outFile output PDF file
-     * @param tsaClient optional TSA client
-     * @throws IOException if the input file could not be read
-     */
-    public void signDetached(File inFile, File outFile, TSAClient tsaClient) 
throws IOException
-    {
         if (inFile == null || !inFile.exists())
         {
             throw new FileNotFoundException("Document for signing does not 
exist");
@@ -90,15 +87,19 @@ public class CreateSignedTimeStamp exten
 
         // sign
         PDDocument doc = PDDocument.load(inFile);
-        signDetached(doc, fos, tsaClient);
+        signDetached(doc, fos);
         doc.close();
     }
 
-    public void signDetached(PDDocument document, OutputStream output, 
TSAClient tsaClient)
-            throws IOException
+    /**
+     * Prepares the TimeStamp-Signature and starts the saving-process.
+     * 
+     * @param document given Pdf
+     * @param output Where the file will be written
+     * @throws IOException
+     */
+    public void signDetached(PDDocument document, OutputStream output) throws 
IOException
     {
-        setTsaClient(tsaClient);
-
         int accessPermissions = SigUtils.getMDPPermission(document);
         if (accessPermissions == 1)
         {
@@ -124,6 +125,22 @@ public class CreateSignedTimeStamp exten
         document.saveIncremental(output);
     }
 
+    @Override
+    public byte[] sign(InputStream content) throws IOException
+    {
+        ValidationTimeStamp validation;
+        try
+        {
+            validation = new ValidationTimeStamp(tsaUrl);
+            return validation.getTimeStampToken(content);
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            LOG.error("Hashing-Algorithm not found for TimeStamping", e);
+        }
+        return new byte[] {};
+    }
+
     public static void main(String[] args) throws IOException, 
GeneralSecurityException
     {
         if (args.length != 3)
@@ -143,29 +160,21 @@ public class CreateSignedTimeStamp exten
             System.exit(1);
         }
 
-        // TSA client
-        TSAClient tsaClient = null;
-        if (tsaUrl != null)
-        {
-            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            tsaClient = new TSAClient(new URL(tsaUrl), null, null, digest);
-        }
-
         // sign PDF
-        CreateSignedTimeStamp signing = new CreateSignedTimeStamp();
+        CreateSignedTimeStamp signing = new CreateSignedTimeStamp(tsaUrl);
 
         File inFile = new File(args[0]);
         String name = inFile.getName();
         String substring = name.substring(0, name.lastIndexOf('.'));
 
         File outFile = new File(inFile.getParent(), substring + 
"_timestamped.pdf");
-        signing.signDetached(inFile, outFile, tsaClient);
+        signing.signDetached(inFile, outFile);
     }
 
     private static void usage()
     {
         System.err.println("usage: java " + 
CreateSignedTimeStamp.class.getName() + " "
-                + "<pdf_to_sign>\n" + "options:\n"
+                + "<pdf_to_sign>\n" + "mandatory options:\n"
                 + "  -tsa <url>    sign timestamp using the given TSA 
server\n");
     }
 }

Modified: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java?rev=1826043&r1=1826042&r2=1826043&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
 (original)
+++ 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
 Tue Mar  6 21:00:28 2018
@@ -162,12 +162,12 @@ public class CreateVisibleSignature exte
      *
      * @param inputFile The source pdf document file.
      * @param signedFile The file to be signed.
-     * @param tsaClient optional TSA client
+     * @param tsaUrl optional TSA url
      * @throws IOException
      */
-    public void signPDF(File inputFile, File signedFile, TSAClient tsaClient) 
throws IOException
+    public void signPDF(File inputFile, File signedFile, String tsaUrl) throws 
IOException
     {
-        this.signPDF(inputFile, signedFile, tsaClient, null);
+        this.signPDF(inputFile, signedFile, tsaUrl, null);
     }
 
     /**
@@ -175,19 +175,19 @@ public class CreateVisibleSignature exte
      *
      * @param inputFile The source pdf document file.
      * @param signedFile The file to be signed.
-     * @param tsaClient optional TSA client
+     * @param tsaUrl optional TSA url
      * @param signatureFieldName optional name of an existing (unsigned) 
signature field
      * @throws IOException
      */
-    public void signPDF(File inputFile, File signedFile, TSAClient tsaClient, 
String signatureFieldName) throws IOException
+    public void signPDF(File inputFile, File signedFile, String tsaUrl, String 
signatureFieldName) throws IOException
     {
-        setTsaClient(tsaClient);
-
         if (inputFile == null || !inputFile.exists())
         {
             throw new IOException("Document for signing does not exist");
         }
 
+        setTsaUrl(tsaUrl);
+
         // creating output document and prepare the IO streams.
         FileOutputStream fos = new FileOutputStream(signedFile);
 
@@ -411,14 +411,6 @@ public class CreateVisibleSignature exte
         char[] pin = args[1].toCharArray();
         keystore.load(new FileInputStream(ksFile), pin);
 
-        // TSA client
-        TSAClient tsaClient = null;
-        if (tsaUrl != null)
-        {
-            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            tsaClient = new TSAClient(new URL(tsaUrl), null, null, digest);
-        }
-
         File documentFile = new File(args[2]);
 
         CreateVisibleSignature signing = new CreateVisibleSignature(keystore, 
pin.clone());
@@ -435,7 +427,7 @@ public class CreateVisibleSignature exte
         imageStream.close();
         signing.setVisibleSignatureProperties("name", "location", "Security", 
0, page, true);
         signing.setExternalSigning(externalSig);
-        signing.signPDF(documentFile, signedDocumentFile, tsaClient);
+        signing.signPDF(documentFile, signedDocumentFile, tsaUrl);
     }
 
     /**

Modified: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature2.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature2.java?rev=1826043&r1=1826042&r2=1826043&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature2.java
 (original)
+++ 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature2.java
 Tue Mar  6 21:00:28 2018
@@ -130,12 +130,12 @@ public class CreateVisibleSignature2 ext
      * @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 tsaClient optional TSA client
+     * @param tsaUrl optional TSA url
      * @throws IOException
      */
-    public void signPDF(File inputFile, File signedFile, Rectangle2D 
humanRect, TSAClient tsaClient) throws IOException
+    public void signPDF(File inputFile, File signedFile, Rectangle2D 
humanRect, String tsaUrl) throws IOException
     {
-        this.signPDF(inputFile, signedFile, humanRect, tsaClient, null);
+        this.signPDF(inputFile, signedFile, humanRect, tsaUrl, null);
     }
 
     /**
@@ -144,18 +144,18 @@ public class CreateVisibleSignature2 ext
      * @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 tsaClient optional TSA client
+     * @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, TSAClient tsaClient, String signatureFieldName) throws IOException
+    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");
         }
 
-        setTsaClient(tsaClient);
+        setTsaUrl(tsaUrl);
 
         // creating output document and prepare the IO streams.
         FileOutputStream fos = new FileOutputStream(signedFile);
@@ -521,14 +521,6 @@ public class CreateVisibleSignature2 ext
         KeyStore keystore = KeyStore.getInstance("PKCS12");
         char[] pin = args[1].toCharArray();
         keystore.load(new FileInputStream(ksFile), pin);
-        
-        // TSA client
-        TSAClient tsaClient = null;
-        if (tsaUrl != null)
-        {
-            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            tsaClient = new TSAClient(new URL(tsaUrl), null, null, digest);
-        }
 
         File documentFile = new File(args[2]);
 
@@ -550,7 +542,7 @@ public class CreateVisibleSignature2 ext
         // regardless of page rotation.
         Rectangle2D humanRect = new Rectangle2D.Float(100, 200, 150, 50);
 
-        signing.signPDF(documentFile, signedDocumentFile, humanRect, 
tsaClient, "Signature1");
+        signing.signPDF(documentFile, signedDocumentFile, humanRect, tsaUrl, 
"Signature1");
     }
 
     /**

Modified: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java?rev=1826043&r1=1826042&r2=1826043&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java
 (original)
+++ 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java
 Tue Mar  6 21:00:28 2018
@@ -32,12 +32,20 @@ import java.security.cert.CertificateFac
 import java.security.cert.X509Certificate;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
 
 import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInputStream;
 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 org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.util.Hex;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.cms.CMSException;
@@ -218,6 +226,7 @@ public final class ShowSignature
                         throw new IOException("Missing subfilter for cert 
dictionary");
                     }
                 }
+                analyseDSS(document);
             }
             catch (CMSException ex)
             {
@@ -234,6 +243,7 @@ public final class ShowSignature
                     document.close();
                 }
             }
+            System.out.println("Analyzed: " + args[1]);
         }
     }
 
@@ -286,6 +296,65 @@ public final class ShowSignature
         }
     }
 
+    /**
+     * 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
+     */
+    private void analyseDSS(PDDocument document) throws IOException
+    {
+        PDDocumentCatalog catalog = document.getDocumentCatalog();
+        COSBase dssElement = catalog.getCOSObject().getDictionaryObject("DSS");
+
+        if (dssElement instanceof COSDictionary)
+        {
+            COSDictionary dss = (COSDictionary) dssElement;
+            System.out.println("DSS Dictionary: " + dss);
+            COSBase certsElement = dss.getDictionaryObject("Certs");
+            if (certsElement instanceof COSArray)
+            {
+                printStreamsFromArray((COSArray) certsElement, "Cert");
+            }
+            COSBase ocspsElement = dss.getDictionaryObject("OCSPs");
+            if (ocspsElement instanceof COSArray)
+            {
+                printStreamsFromArray((COSArray) ocspsElement, "Ocsp");
+            }
+            COSBase crlElement = dss.getDictionaryObject("CRLs");
+            if (crlElement instanceof COSArray)
+            {
+                printStreamsFromArray((COSArray) crlElement, "CRL");
+            }
+            // TODO: go through VRIs (wich indirectly point to the DSS-Data)
+        }
+    }
+
+    /**
+     * 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
+     */
+    private void printStreamsFromArray(COSArray elements, String description) 
throws IOException
+    {
+        for (COSBase baseElem : elements)
+        {
+            COSObject streamObj = (COSObject) baseElem;
+            if (streamObj.getObject() instanceof COSStream)
+            {
+                COSStream cosStream = (COSStream) streamObj.getObject();
+
+                COSInputStream input = cosStream.createInputStream();
+                byte[] streamBytes = IOUtils.toByteArray(input);
+
+                System.out.println(description + " (" + 
elements.indexOf(streamObj) + "): "
+                        + Hex.getString(streamBytes));
+            }
+        }
+    }
+
     // 
https://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.1/distribution/src/main/release/samples/sts_issue_operation/src/main/java/demo/sts/provider/cert/CertificateVerifier.java
     
     /**

Added: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ValidationTimeStamp.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ValidationTimeStamp.java?rev=1826043&view=auto
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ValidationTimeStamp.java
 (added)
+++ 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ValidationTimeStamp.java
 Tue Mar  6 21:00:28 2018
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.signature;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.MessageDigest;
+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;
+
+/**
+ * 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)
+ *
+ * @author Others
+ * @author Alexis Suter
+ */
+public class ValidationTimeStamp
+{
+    private TSAClient tsaClient;
+
+    /**
+     * @param tsaUrl The url where TS-Request will be done.
+     * @throws NoSuchAlgorithmException
+     * @throws MalformedURLException
+     */
+    public ValidationTimeStamp(String tsaUrl) throws NoSuchAlgorithmException, 
MalformedURLException
+    {
+        if (tsaUrl != null)
+        {
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            this.tsaClient = new TSAClient(new URL(tsaUrl), null, null, 
digest);
+        }
+    }
+
+    /**
+     * Creates a signed timestamp token by the given input stream.
+     * 
+     * @param content InputStream of the content to sign
+     * @return the byte[] of the timestamp token
+     * @throws IOException
+     */
+    public byte[] getTimeStampToken(InputStream content) throws IOException
+    {
+        return tsaClient.getTimeStampToken(IOUtils.toByteArray(content));
+    }
+
+    /**
+     * Extend cms signed data with TimeStamp first or to all signers
+     *
+     * @param signedData Generated CMS signed data
+     * @return CMSSignedData Extended CMS signed data
+     * @throws IOException
+     */
+    public CMSSignedData addSignedTimeStamp(CMSSignedData signedData)
+            throws IOException
+    {
+        SignerInformationStore signerStore = signedData.getSignerInfos();
+        List<SignerInformation> newSigners = new 
ArrayList<SignerInformation>();
+
+        for (SignerInformation signer : signerStore.getSigners())
+        {
+            // This adds a timestamp to every signer (into his unsigned 
attributes) in the signature.
+            newSigners.add(signTimeStamp(signer));
+        }
+
+        // Because new SignerInformation is created, new SignerInfoStore has 
to be created 
+        // and also be replaced in signedData. Which creates a new signedData 
object.
+        return CMSSignedData.replaceSigners(signedData, new 
SignerInformationStore(newSigners));
+    }
+
+    /**
+     * Extend CMS Signer Information with the TimeStampToken into the unsigned 
Attributes.
+     *
+     * @param signer information about signer
+     * @return information about SignerInformation
+     * @throws IOException
+     */
+    private SignerInformation signTimeStamp(SignerInformation signer)
+            throws IOException
+    {
+        AttributeTable unsignedAttributes = signer.getUnsignedAttributes();
+
+        ASN1EncodableVector vector = new ASN1EncodableVector();
+        if (unsignedAttributes != null)
+        {
+            vector = unsignedAttributes.toASN1EncodableVector();
+        }
+
+        byte[] token = tsaClient.getTimeStampToken(signer.getSignature());
+        ASN1ObjectIdentifier oid = 
PKCSObjectIdentifiers.id_aa_signatureTimeStampToken;
+        ASN1Encodable signatureTimeStamp = new Attribute(oid,
+                new DERSet(ASN1Primitive.fromByteArray(token)));
+
+        vector.add(signatureTimeStamp);
+        Attributes signedAttributes = new Attributes(vector);
+
+        // There is no other way changing the unsigned attributes of the 
signer information.
+        // result is never null, new SignerInformation always returned, 
+        // see source code of replaceUnsignedAttributes
+        return SignerInformation.replaceUnsignedAttributes(signer, new 
AttributeTable(signedAttributes));
+    }
+}

Propchange: 
pdfbox/branches/2.0/examples/src/main/java/org/apache/pdfbox/examples/signature/ValidationTimeStamp.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java?rev=1826043&r1=1826042&r2=1826043&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
 (original)
+++ 
pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
 Tue Mar  6 21:00:28 2018
@@ -21,7 +21,6 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
@@ -42,7 +41,6 @@ import org.apache.pdfbox.cos.COSString;
 import org.apache.pdfbox.examples.signature.CreateEmptySignatureForm;
 import org.apache.pdfbox.examples.signature.CreateSignature;
 import org.apache.pdfbox.examples.signature.CreateVisibleSignature;
-import org.apache.pdfbox.examples.signature.TSAClient;
 import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
@@ -161,10 +159,6 @@ public class TestCreateSignature
         response.setMockResponseCode(200);
         mockServer.setMockHttpServerResponses(response);
 
-        // TSA client
-        MessageDigest digest = MessageDigest.getInstance("SHA-256");
-        TSAClient tsaClient = new TSAClient(new URL(tsaUrl), null, null, 
digest);
-
         // load the keystore
         KeyStore keystore = KeyStore.getInstance("PKCS12");
         keystore.load(new FileInputStream(keystorePath), 
password.toCharArray());
@@ -176,7 +170,7 @@ public class TestCreateSignature
             String outPath = outDir + getOutputFileName("signed{0}_tsa.pdf");
             CreateSignature signing = new CreateSignature(keystore, 
password.toCharArray());
             signing.setExternalSigning(externallySign);
-            signing.signDetached(new File(inPath), new File(outPath), 
tsaClient);
+            signing.signDetached(new File(inPath), new File(outPath), tsaUrl);
         }
         catch (IOException e)
         {


Reply via email to