You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2016/06/07 16:32:22 UTC

svn commit: r1747268 - /pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java

Author: tilman
Date: Tue Jun  7 16:32:22 2016
New Revision: 1747268

URL: http://svn.apache.org/viewvc?rev=1747268&view=rev
Log:
PDFBOX-3017: add simple verify for adbe.pkcs7.detached signatures

Modified:
    pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java

Modified: pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java?rev=1747268&r1=1747267&r2=1747268&view=diff
==============================================================================
--- pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java (original)
+++ pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/ShowSignature.java Tue Jun  7 16:32:22 2016
@@ -19,16 +19,33 @@ package org.apache.pdfbox.examples.signa
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SignatureException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
 import java.util.Collection;
 
-import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSString;
 import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.bouncycastle.cert.X509CertificateHolder;
+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;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Store;
 
 /**
  * This will read a document from the filesystem, decrypt it and do something with the signature.
@@ -37,6 +54,8 @@ import org.apache.pdfbox.pdmodel.PDDocum
  */
 public final class ShowSignature
 {
+    private static SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
+    
     private ShowSignature()
     {
     }
@@ -48,14 +67,22 @@ public final class ShowSignature
      *
      * @throws IOException If there is an error reading the file.
      * @throws CertificateException
+     * @throws java.security.NoSuchAlgorithmException
+     * @throws java.security.InvalidKeyException
+     * @throws java.security.NoSuchProviderException
+     * @throws java.security.SignatureException
      */
-    public static void main( String[] args ) throws IOException, CertificateException
+    public static void main(String[] args) throws IOException, CertificateException,
+                                                  NoSuchAlgorithmException, InvalidKeyException, 
+                                                  NoSuchProviderException, SignatureException
     {
         ShowSignature show = new ShowSignature();
         show.showSignature( args );
     }
 
-    private void showSignature( String[] args ) throws IOException, CertificateException
+    private void showSignature(String[] args) throws IOException, CertificateException,
+                                                     NoSuchAlgorithmException, InvalidKeyException,
+                                                     NoSuchProviderException, SignatureException
     {
         if( args.length != 2 )
         {
@@ -68,71 +95,106 @@ public final class ShowSignature
             PDDocument document = null;
             try
             {
-                document = PDDocument.load( new File(infile), password );
-                if( !document.isEncrypted() )
+                document = PDDocument.load(new File(infile), password);
+                for (PDSignature sig : document.getSignatureDictionaries())
                 {
-                    System.err.println( "Warning: Document is not encrypted." );
-                }
+                    COSDictionary sigDict = sig.getCOSObject();
+                    COSString contents = (COSString) sigDict.getDictionaryObject(COSName.CONTENTS);
 
-                COSDictionary trailer = document.getDocument().getTrailer();
-                COSDictionary root = (COSDictionary)trailer.getDictionaryObject( COSName.ROOT );
-                COSDictionary acroForm = (COSDictionary)root.getDictionaryObject( COSName.ACRO_FORM );
-                COSArray fields = (COSArray)acroForm.getDictionaryObject( COSName.FIELDS );
-                for( int i=0; i<fields.size(); i++ )
-                {
-                    COSDictionary field = (COSDictionary)fields.getObject( i );
-                    COSName type = field.getCOSName( COSName.FT );
-                    if( COSName.SIG.equals( type ) )
+                    // download the signed content, described in /ByteRange COSArray:
+                    // [offset1 len1 offset2 len2]
+                    int[] byteRange = sig.getByteRange();
+                    byte[] buf = new byte[byteRange[1] + byteRange[3]];
+                    RandomAccessFile raf = new RandomAccessFile(infile, "r");
+                    raf.seek(byteRange[0]);
+                    raf.readFully(buf, byteRange[0], byteRange[1]);
+                    raf.seek(byteRange[2]);
+                    raf.readFully(buf, byteRange[1], byteRange[3]);
+                    raf.close();
+
+                    System.out.println("Signature found");
+                    System.out.println("Name:     " + sig.getName());
+                    System.out.println("Modified: " + sdf.format(sig.getSignDate().getTime()));
+                    String subFilter = sig.getSubFilter();
+                    if (subFilter != null)
                     {
-                        COSDictionary cert = (COSDictionary)field.getDictionaryObject( COSName.V );
-                        if( cert != null )
+                        if (subFilter.equals("adbe.pkcs7.detached"))
                         {
-                            System.out.println( "Certificate found" );
-                            System.out.println( "Name=" + cert.getDictionaryObject( COSName.NAME ) );
-                            System.out.println( "Modified=" + cert.getDictionaryObject( COSName.M ) );
-                            COSName subFilter = (COSName)cert.getDictionaryObject( COSName.SUB_FILTER );
-                            if( subFilter != null )
+                            CMSProcessable signedContent = new CMSProcessableByteArray(buf);
+
+                            // inspiration:
+                            // http://stackoverflow.com/a/26702631/535646
+                            // http://stackoverflow.com/a/9261365/535646
+                            CMSSignedData signedData = new CMSSignedData(signedContent, contents.getBytes());
+                            Store certificatesStore = signedData.getCertificates();
+                            Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
+                            SignerInformation signerInformation = signers.iterator().next();
+                            Collection matches = certificatesStore.getMatches(signerInformation.getSID());
+                            X509CertificateHolder certificateHolder = (X509CertificateHolder) matches.iterator().next();
+                            X509Certificate certFromSignedData = new JcaX509CertificateConverter().getCertificate(certificateHolder);
+                            System.out.println("certFromSignedData: " + certFromSignedData);
+                            certFromSignedData.checkValidity(sig.getSignDate().getTime());
+
+                            // CMSVerifierCertificateNotValidException means that the keystore wasn't valid at signing time
+                            if (signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certFromSignedData)))
                             {
-                                if( subFilter.getName().equals( "adbe.x509.rsa_sha1" ) )
-                                {
-                                    COSString certString = (COSString)cert.getDictionaryObject(
-                                        COSName.getPDFName( "Cert" ) );
-                                    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 );
-                                }
-                                else if( subFilter.getName().equals( "adbe.pkcs7.sha1" ) )
-                                {
-                                    COSString certString = (COSString)cert.getDictionaryObject(
-                                        COSName.CONTENTS );
-                                    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 );
-                                }
-                                else
-                                {
-                                    System.err.println( "Unknown certificate type:" + subFilter );
-                                }
+                                System.out.println("Signature verified");
                             }
                             else
                             {
-                                throw new IOException( "Missing subfilter for cert dictionary" );
+                                System.out.println("Signature verification failed");
                             }
+                            
+                            //TODO check certificate chain, revocation lists, timestamp...
+                        }
+                        else if (subFilter.equals("adbe.x509.rsa_sha1"))
+                        {
+                            // PDFBOX-2693.pdf
+                            COSString certString = (COSString) sigDict.getDictionaryObject(
+                                    COSName.getPDFName("Cert"));
+                            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);
+                            
+                            //TODO verify signature
+                        }
+                        else if (subFilter.equals("adbe.pkcs7.sha1"))
+                        {
+                            // PDFBOX-1452.pdf
+                            COSString certString = (COSString) sigDict.getDictionaryObject(
+                                    COSName.CONTENTS);
+                            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);
+
+                            //TODO verify signature
                         }
                         else
                         {
-                            System.out.println( "Signature found, but no certificate" );
+                            System.err.println("Unknown certificate type: " + subFilter);
                         }
                     }
+                    else
+                    {
+                        throw new IOException("Missing subfilter for cert dictionary");
+                    }
                 }
             }
+            catch (CMSException ex)
+            {
+                throw new IOException(ex);
+            }
+            catch (OperatorCreationException ex)
+            {
+                throw new IOException(ex);
+            }
             finally
             {
-                if( document != null )
+                if (document != null)
                 {
                     document.close();
                 }