You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by tb...@apache.org on 2012/04/07 00:06:48 UTC

svn commit: r1310605 - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption: PublicKeySecurityHandler.java SecurityHandler.java StandardSecurityHandler.java

Author: tboehme
Date: Fri Apr  6 22:06:48 2012
New Revision: 1310605

URL: http://svn.apache.org/viewvc?rev=1310605&view=rev
Log:
second change-set from PDFBOX-1199; moved decryption initialization into prepareForDecryption method which allows decryption of single objects

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java?rev=1310605&r1=1310604&r2=1310605&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java Fri Apr  6 22:06:48 2012
@@ -56,6 +56,7 @@ import org.bouncycastle.cms.CMSEnveloped
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.RecipientInformation;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSString;
 import org.apache.pdfbox.exceptions.CryptographyException;
 import org.apache.pdfbox.pdmodel.PDDocument;
@@ -117,118 +118,137 @@ public class PublicKeySecurityHandler ex
 
         PDEncryptionDictionary dictionary = doc.getEncryptionDictionary();
 
-        if(dictionary.getLength() != 0)
-        {
-            this.keyLength = dictionary.getLength();
-        }
-
-        if(!(decryptionMaterial instanceof PublicKeyDecryptionMaterial))
-        {
-            throw new CryptographyException(
-                "Provided decryption material is not compatible with the document");
-        }
-
-        PublicKeyDecryptionMaterial material = (PublicKeyDecryptionMaterial)decryptionMaterial;
-
-        try
-        {
-            boolean foundRecipient = false;
-
-            // the decrypted content of the enveloped data that match
-            // the certificate in the decryption material provided
-            byte[] envelopedData = null;
-
-            // the bytes of each recipient in the recipients array
-            byte[][] recipientFieldsBytes = new byte[dictionary.getRecipientsLength()][];
-
-            int recipientFieldsLength = 0;
-
-            for(int i=0; i<dictionary.getRecipientsLength(); i++)
-            {
-                COSString recipientFieldString = dictionary.getRecipientStringAt(i);
-                byte[] recipientBytes = recipientFieldString.getBytes();
-                CMSEnvelopedData data = new CMSEnvelopedData(recipientBytes);
-                Iterator recipCertificatesIt = data.getRecipientInfos().getRecipients().iterator();
-                while(recipCertificatesIt.hasNext())
-                {
-                    RecipientInformation ri =
-                        (RecipientInformation)recipCertificatesIt.next();
-                    // Impl: if a matching certificate was previously found it is an error,
-                    // here we just don't care about it
-                    if(ri.getRID().match(material.getCertificate()) && !foundRecipient)
-                    {
-                        foundRecipient = true;
-                        envelopedData = ri.getContent(material.getPrivateKey(), "BC");
-                    }
-                }
-                recipientFieldsBytes[i] = recipientBytes;
-                recipientFieldsLength += recipientBytes.length;
-            }
-            if(!foundRecipient || envelopedData == null)
-            {
-                throw new CryptographyException("The certificate matches no recipient entry");
-            }
-            if(envelopedData.length != 24)
-            {
-                throw new CryptographyException("The enveloped data does not contain 24 bytes");
-            }
-            // now envelopedData contains:
-            // - the 20 bytes seed
-            // - the 4 bytes of permission for the current user
-
-            byte[] accessBytes = new byte[4];
-            System.arraycopy(envelopedData, 20, accessBytes, 0, 4);
-
-            currentAccessPermission = new AccessPermission(accessBytes);
-            currentAccessPermission.setReadOnly();
-
-             // what we will put in the SHA1 = the seed + each byte contained in the recipients array
-            byte[] sha1Input = new byte[recipientFieldsLength + 20];
-
-            // put the seed in the sha1 input
-            System.arraycopy(envelopedData, 0, sha1Input, 0, 20);
-
-            // put each bytes of the recipients array in the sha1 input
-            int sha1InputOffset = 20;
-            for(int i=0; i<recipientFieldsBytes.length; i++)
-            {
-                System.arraycopy(
-                    recipientFieldsBytes[i], 0,
-                    sha1Input, sha1InputOffset, recipientFieldsBytes[i].length);
-                sha1InputOffset += recipientFieldsBytes[i].length;
-            }
-
-            MessageDigest md = MessageDigest.getInstance("SHA-1");
-            byte[] mdResult = md.digest(sha1Input);
-
-            // we have the encryption key ...
-            encryptionKey = new byte[this.keyLength/8];
-            System.arraycopy(mdResult, 0, encryptionKey, 0, this.keyLength/8);
-
-            proceedDecryption();
-
-
-        }
-        catch(CMSException e)
-        {
-            throw new CryptographyException(e);
-        }
-        catch(KeyStoreException e)
-        {
-            throw new CryptographyException(e);
-        }
-        catch(NoSuchProviderException e)
-        {
-            throw new CryptographyException(e);
-        }
-        catch(NoSuchAlgorithmException e)
-        {
-            throw new CryptographyException(e);
-        }
-
+        prepareForDecryption( dictionary, doc.getDocument().getDocumentID(),
+        											decryptionMaterial );
+        
+        proceedDecryption();
     }
 
     /**
+     * Prepares everything to decrypt the document.
+     *
+     * If {@link #decryptDocument(PDDocument, DecryptionMaterial)} is used, this method is
+     * called from there. Only if decryption of single objects is needed this should be called instead.
+     *
+     * @param encDictionary  encryption dictionary, can be retrieved via {@link PDDocument#getEncryptionDictionary()}
+     * @param documentIDArray  document id which is returned via {@link COSDocument#getDocumentID()} (not used by this handler)
+     * @param decryptionMaterial Information used to decrypt the document.
+     *
+     * @throws IOException If there is an error accessing data.
+     * @throws CryptographyException If there is an error with decryption.
+     */
+    public void prepareForDecryption(PDEncryptionDictionary encDictionary, COSArray documentIDArray,
+				 														 DecryptionMaterial decryptionMaterial)
+    throws CryptographyException, IOException
+    {
+    	
+	      if(encDictionary.getLength() != 0)
+	      {
+	          this.keyLength = encDictionary.getLength();
+	      }
+	
+	      if(!(decryptionMaterial instanceof PublicKeyDecryptionMaterial))
+	      {
+	          throw new CryptographyException(
+	              "Provided decryption material is not compatible with the document");
+	      }
+	
+	      PublicKeyDecryptionMaterial material = (PublicKeyDecryptionMaterial)decryptionMaterial;
+	
+	      try
+	      {
+	          boolean foundRecipient = false;
+	
+	          // the decrypted content of the enveloped data that match
+	          // the certificate in the decryption material provided
+	          byte[] envelopedData = null;
+	
+	          // the bytes of each recipient in the recipients array
+	          byte[][] recipientFieldsBytes = new byte[encDictionary.getRecipientsLength()][];
+	
+	          int recipientFieldsLength = 0;
+	
+	          for(int i=0; i<encDictionary.getRecipientsLength(); i++)
+	          {
+	              COSString recipientFieldString = encDictionary.getRecipientStringAt(i);
+	              byte[] recipientBytes = recipientFieldString.getBytes();
+	              CMSEnvelopedData data = new CMSEnvelopedData(recipientBytes);
+	              Iterator recipCertificatesIt = data.getRecipientInfos().getRecipients().iterator();
+	              while(recipCertificatesIt.hasNext())
+	              {
+	                  RecipientInformation ri =
+	                      (RecipientInformation)recipCertificatesIt.next();
+	                  // Impl: if a matching certificate was previously found it is an error,
+	                  // here we just don't care about it
+	                  if(ri.getRID().match(material.getCertificate()) && !foundRecipient)
+	                  {
+	                      foundRecipient = true;
+	                      envelopedData = ri.getContent(material.getPrivateKey(), "BC");
+	                  }
+	              }
+	              recipientFieldsBytes[i] = recipientBytes;
+	              recipientFieldsLength += recipientBytes.length;
+	          }
+	          if(!foundRecipient || envelopedData == null)
+	          {
+	              throw new CryptographyException("The certificate matches no recipient entry");
+	          }
+	          if(envelopedData.length != 24)
+	          {
+	              throw new CryptographyException("The enveloped data does not contain 24 bytes");
+	          }
+	          // now envelopedData contains:
+	          // - the 20 bytes seed
+	          // - the 4 bytes of permission for the current user
+	
+	          byte[] accessBytes = new byte[4];
+	          System.arraycopy(envelopedData, 20, accessBytes, 0, 4);
+	
+	          currentAccessPermission = new AccessPermission(accessBytes);
+	          currentAccessPermission.setReadOnly();
+	
+	           // what we will put in the SHA1 = the seed + each byte contained in the recipients array
+	          byte[] sha1Input = new byte[recipientFieldsLength + 20];
+	
+	          // put the seed in the sha1 input
+	          System.arraycopy(envelopedData, 0, sha1Input, 0, 20);
+	
+	          // put each bytes of the recipients array in the sha1 input
+	          int sha1InputOffset = 20;
+	          for(int i=0; i<recipientFieldsBytes.length; i++)
+	          {
+	              System.arraycopy(
+	                  recipientFieldsBytes[i], 0,
+	                  sha1Input, sha1InputOffset, recipientFieldsBytes[i].length);
+	              sha1InputOffset += recipientFieldsBytes[i].length;
+	          }
+	
+	          MessageDigest md = MessageDigest.getInstance("SHA-1");
+	          byte[] mdResult = md.digest(sha1Input);
+	
+	          // we have the encryption key ...
+	          encryptionKey = new byte[this.keyLength/8];
+	          System.arraycopy(mdResult, 0, encryptionKey, 0, this.keyLength/8);
+	      }
+	      catch(CMSException e)
+	      {
+	          throw new CryptographyException(e);
+	      }
+	      catch(KeyStoreException e)
+	      {
+	          throw new CryptographyException(e);
+	      }
+	      catch(NoSuchProviderException e)
+	      {
+	          throw new CryptographyException(e);
+	      }
+	      catch(NoSuchAlgorithmException e)
+	      {
+	          throw new CryptographyException(e);
+	      }
+    }
+    
+    /**
      * Prepare the document for encryption.
      *
      * @param doc The document that will be encrypted.

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java?rev=1310605&r1=1310604&r2=1310605&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java Fri Apr  6 22:06:48 2012
@@ -42,6 +42,7 @@ import javax.crypto.spec.SecretKeySpec;
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSDocument;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSObject;
 import org.apache.pdfbox.cos.COSStream;
@@ -128,6 +129,23 @@ public abstract class SecurityHandler
     public abstract void prepareDocumentForEncryption(PDDocument doc) throws CryptographyException, IOException;
 
     /**
+     * Prepares everything to decrypt the document.
+     * 
+     * If {@link #decryptDocument(PDDocument, DecryptionMaterial)} is used, this method is
+     * called from there. Only if decryption of single objects is needed this should be called instead.
+     *
+     * @param encDictionary  encryption dictionary, can be retrieved via {@link PDDocument#getEncryptionDictionary()}
+     * @param documentIDArray  document id which is returned via {@link COSDocument#getDocumentID()}
+     * @param decryptionMaterial Information used to decrypt the document.
+     *
+     * @throws IOException If there is an error accessing data.
+     * @throws CryptographyException If there is an error with decryption.
+     */
+    public abstract void prepareForDecryption(PDEncryptionDictionary encDictionary, COSArray documentIDArray,
+    																 					DecryptionMaterial decryptionMaterial)
+    throws CryptographyException, IOException;
+    
+    /**
      * Prepare the document for decryption.
      *
      * @param doc The document to decrypt.

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java?rev=1310605&r1=1310604&r2=1310605&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java Fri Apr  6 22:06:48 2012
@@ -149,6 +149,30 @@ public class StandardSecurityHandler ext
         document = doc;
 
         PDEncryptionDictionary dictionary = document.getEncryptionDictionary();
+        COSArray documentIDArray = document.getDocument().getDocumentID();
+        
+        prepareForDecryption(dictionary, documentIDArray, decryptionMaterial);
+        
+        this.proceedDecryption();
+    }
+
+    /**
+     * Prepares everything to decrypt the document.
+     *
+     * If {@link #decryptDocument(PDDocument, DecryptionMaterial)} is used, this method is
+     * called from there. Only if decryption of single objects is needed this should be called instead.
+     *
+     * @param encDictionary  encryption dictionary, can be retrieved via {@link PDDocument#getEncryptionDictionary()}
+     * @param documentIDArray  document id which is returned via {@link COSDocument#getDocumentID()}
+     * @param decryptionMaterial Information used to decrypt the document.
+     *
+     * @throws IOException If there is an error accessing data.
+     * @throws CryptographyException If there is an error with decryption.
+     */
+    public void prepareForDecryption(PDEncryptionDictionary encDictionary, COSArray documentIDArray,
+    																 DecryptionMaterial decryptionMaterial)
+        throws CryptographyException, IOException
+    {
         if(!(decryptionMaterial instanceof StandardDecryptionMaterial))
         {
             throw new CryptographyException("Provided decryption material is not compatible with the document");
@@ -162,13 +186,12 @@ public class StandardSecurityHandler ext
             password = "";
         }
 
-        int dicPermissions = dictionary.getPermissions();
-        int dicRevision = dictionary.getRevision();
-        int dicLength = dictionary.getLength()/8;
+        int dicPermissions = encDictionary.getPermissions();
+        int dicRevision = encDictionary.getRevision();
+        int dicLength = encDictionary.getLength()/8;
 
         //some documents may have not document id, see
         //test\encryption\encrypted_doc_no_id.pdf
-        COSArray documentIDArray = document.getDocument().getDocumentID();
         byte[] documentIDBytes = null;
         if( documentIDArray != null && documentIDArray.size() >= 1 )
         {
@@ -181,10 +204,10 @@ public class StandardSecurityHandler ext
         }
 
         // we need to know whether the meta data was encrypted for password calculation
-        boolean encryptMetadata = dictionary.isEncryptMetaData();
+        boolean encryptMetadata = encDictionary.isEncryptMetaData();
         
-        byte[] u = dictionary.getUserKey();
-        byte[] o = dictionary.getOwnerKey();
+        byte[] u = encDictionary.getUserKey();
+        byte[] o = encDictionary.getOwnerKey();
 
         boolean isUserPassword =
             isUserPassword(
@@ -242,8 +265,7 @@ public class StandardSecurityHandler ext
 
         // detect whether AES encryption is used. This assumes that the encryption algo is 
         // stored in the PDCryptFilterDictionary
-        PDCryptFilterDictionary stdCryptFilterDictionary =  doc.getEncryptionDictionary().
-                getStdCryptFilterDictionary();
+        PDCryptFilterDictionary stdCryptFilterDictionary =  encDictionary.getStdCryptFilterDictionary();
 
         if (stdCryptFilterDictionary != null)
         {
@@ -253,10 +275,8 @@ public class StandardSecurityHandler ext
                 setAES("AESV2".equalsIgnoreCase(cryptFilterMethod.getName()));
             }
         }
-        
-        this.proceedDecryption();
     }
-
+    
     /**
      * Prepare document for encryption.
      *