You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2010/12/02 20:41:14 UTC

svn commit: r1041553 - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox: cos/COSName.java pdmodel/encryption/PDEncryptionDictionary.java pdmodel/encryption/SecurityHandler.java pdmodel/encryption/StandardSecurityHandler.java

Author: lehmi
Date: Thu Dec  2 19:41:13 2010
New Revision: 1041553

URL: http://svn.apache.org/viewvc?rev=1041553&view=rev
Log:
PDFBOX-907: added support for encrypted pdfs containing metadata which aren't encrypted as proposed by Martijn Brinkers

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.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/cos/COSName.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java?rev=1041553&r1=1041552&r2=1041553&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java Thu Dec  2 19:41:13 2010
@@ -382,6 +382,9 @@ public final class COSName extends COSBa
     /** "Encrypt" */
     public static final COSName ENCRYPT = new COSName( "Encrypt" );
 
+    /** "EncryptMetaData" */
+    public static final COSName ENCRYPT_META_DATA = new COSName( "EncryptMetadata" );
+    
     /** "ExtGState" */
     public static final COSName EXT_G_STATE = new COSName( "ExtGState" );
 

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java?rev=1041553&r1=1041552&r2=1041553&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java Thu Dec  2 19:41:13 2010
@@ -20,6 +20,8 @@ package org.apache.pdfbox.pdmodel.encryp
 import java.io.IOException;
 
 import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSBoolean;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSString;
@@ -302,6 +304,25 @@ public class PDEncryptionDictionary
     }
 
     /**
+     * Will get the EncryptMetaData dictionary info.
+     * 
+     * @return true if EncryptMetaData is explicitly set to false (the default is true)
+     */
+    public boolean isEncryptMetaData()
+    {
+        // default is true (see 7.6.3.2 Standard Encryption Dictionary PDF 32000-1:2008)
+        boolean encryptMetaData = true;
+        
+        COSBase value = encryptionDictionary.getDictionaryObject(COSName.ENCRYPT_META_DATA);
+        
+        if (value instanceof COSBoolean) {
+            encryptMetaData = ((COSBoolean)value).getValue();
+        }
+        
+        return encryptMetaData;
+    }
+    
+    /**
      * This will set the Recipients field of the dictionary. This field contains an array
      * of string.
      * @param recipients the array of bytes arrays to put in the Recipients field.

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=1041553&r1=1041552&r2=1041553&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 Thu Dec  2 19:41:13 2010
@@ -223,6 +223,10 @@ public abstract class SecurityHandler
     public void encryptData(long objectNumber, long genNumber, InputStream data, OutputStream output, boolean decrypt) 
     throws CryptographyException, IOException
     {
+        if (aes && !decrypt) {
+            throw new IllegalArgumentException("AES encryption is not yet implemented.");
+        }
+        
         byte[] newKey = new byte[ encryptionKey.length + 5 ];
         System.arraycopy( encryptionKey, 0, newKey, 0, encryptionKey.length );
         //PDF 1.4 reference pg 73

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=1041553&r1=1041552&r2=1041553&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 Thu Dec  2 19:41:13 2010
@@ -180,6 +180,9 @@ public class StandardSecurityHandler ext
             documentIDBytes = new byte[0];
         }
 
+        // we need to know whether the meta data was encrypted for password calculation
+        boolean encryptMetadata = dictionary.isEncryptMetaData();
+        
         byte[] u = dictionary.getUserKey();
         byte[] o = dictionary.getOwnerKey();
 
@@ -191,7 +194,8 @@ public class StandardSecurityHandler ext
                 dicPermissions,
                 documentIDBytes,
                 dicRevision,
-                dicLength );
+                dicLength,
+                encryptMetadata);
         boolean isOwnerPassword =
             isOwnerPassword(
                 password.getBytes(),
@@ -200,7 +204,8 @@ public class StandardSecurityHandler ext
                 dicPermissions,
                 documentIDBytes,
                 dicRevision,
-                dicLength );
+                dicLength,
+                encryptMetadata);
 
         if( isUserPassword )
         {
@@ -212,7 +217,8 @@ public class StandardSecurityHandler ext
                     dicPermissions,
                     documentIDBytes,
                     dicRevision,
-                    dicLength );
+                    dicLength,
+                    encryptMetadata );
         }
         else if( isOwnerPassword )
         {
@@ -225,7 +231,8 @@ public class StandardSecurityHandler ext
                     dicPermissions,
                     documentIDBytes,
                     dicRevision,
-                    dicLength );
+                    dicLength,
+                    encryptMetadata);
         }
         else
         {
@@ -330,10 +337,10 @@ public class StandardSecurityHandler ext
 
         byte[] u = computeUserPassword(
             userPassword.getBytes("ISO-8859-1"),
-            o, permissionInt, id.getBytes(), revision, length);
+            o, permissionInt, id.getBytes(), revision, length, true);
 
         encryptionKey = computeEncryptedKey(
-            userPassword.getBytes("ISO-8859-1"), o, permissionInt, id.getBytes(), revision, length);
+            userPassword.getBytes("ISO-8859-1"), o, permissionInt, id.getBytes(), revision, length, true);
 
         encryptionDictionary.setOwnerKey(o);
         encryptionDictionary.setUserKey(u);
@@ -366,11 +373,12 @@ public class StandardSecurityHandler ext
             int permissions,
             byte[] id,
             int encRevision,
-            int length)
+            int length,
+            boolean encryptMetadata)
             throws CryptographyException, IOException
     {
         byte[] userPassword = getUserPassword( ownerPassword, o, encRevision, length );
-        return isUserPassword( userPassword, u, o, permissions, id, encRevision, length );
+        return isUserPassword( userPassword, u, o, permissions, id, encRevision, length, encryptMetadata );
     }
 
     /**
@@ -501,7 +509,8 @@ public class StandardSecurityHandler ext
             int permissions,
             byte[] id,
             int encRevision,
-            int length )
+            int length,
+            boolean encryptMetadata)
             throws CryptographyException
         {
             byte[] result = new byte[ length ];
@@ -531,6 +540,15 @@ public class StandardSecurityHandler ext
 
                 //step 5
                 md.update( id );
+                
+                //(Security handlers of revision 4 or greater) If document metadata is not being encrypted, 
+                //pass 4 bytes with the value 0xFFFFFFFF to the MD5 hash function.
+                //see 7.6.3.3 Algorithm 2 Step f of PDF 32000-1:2008
+                if( encRevision == 4 && !encryptMetadata)
+                {
+                    md.update(new byte[]{(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff});
+                }
+                
                 byte[] digest = md.digest();
 
                 //step 6
@@ -581,12 +599,13 @@ public class StandardSecurityHandler ext
             int permissions,
             byte[] id,
             int encRevision,
-            int length )
+            int length,
+            boolean encryptMetadata)
             throws CryptographyException, IOException
         {
             ByteArrayOutputStream result = new ByteArrayOutputStream();
             //STEP 1
-            byte[] encryptionKey = computeEncryptedKey( password, o, permissions, id, encRevision, length );
+            byte[] encryptionKey = computeEncryptedKey( password, o, permissions, id, encRevision, length, encryptMetadata );
 
             if( encRevision == 2 )
             {
@@ -764,12 +783,14 @@ public class StandardSecurityHandler ext
             int permissions,
             byte[] id,
             int encRevision,
-            int length)
+            int length,
+            boolean encryptMetadata)
             throws CryptographyException, IOException
         {
             boolean matches = false;
             //STEP 1
-            byte[] computedValue = computeUserPassword( password, o, permissions, id, encRevision, length );
+            byte[] computedValue = computeUserPassword( password, o, permissions, id, encRevision, length, 
+                    encryptMetadata );
             if( encRevision == 2 )
             {
                 //STEP 2
@@ -810,11 +831,12 @@ public class StandardSecurityHandler ext
             int permissions,
             byte[] id,
             int encRevision,
-            int length)
+            int length,
+            boolean encryptMetadata )
             throws CryptographyException, IOException
             {
                 return isUserPassword(password.getBytes(),
-                        u,o,permissions, id, encRevision, length);
+                        u,o,permissions, id, encRevision, length, encryptMetadata);
             }
 
     /**
@@ -840,11 +862,12 @@ public class StandardSecurityHandler ext
             int permissions,
             byte[] id,
             int encRevision,
-            int length)
+            int length,
+            boolean encryptMetadata)
             throws CryptographyException, IOException
             {
                 return isOwnerPassword(password.getBytes(),
-                        u,o,permissions, id, encRevision, length);
+                        u,o,permissions, id, encRevision, length, encryptMetadata);
             }
 
     private static final boolean arraysEqual( byte[] first, byte[] second, int count )