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 2013/10/07 20:07:21 UTC
svn commit: r1530018 -
/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
Author: lehmi
Date: Mon Oct 7 18:07:21 2013
New Revision: 1530018
URL: http://svn.apache.org/r1530018
Log:
PDFBOX-1576: COSArray have to be decrypted
Modified:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
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=1530018&r1=1530017&r2=1530018&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 Mon Oct 7 18:07:21 2013
@@ -59,23 +59,22 @@ import org.apache.pdfbox.pdmodel.PDDocum
* @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
* @author Benoit Guillon (benoit.guillon@snv.jussieu.fr)
*
- * @version $Revision: 1.4 $
*/
public abstract class SecurityHandler
{
- /* ------------------------------------------------
- * CONSTANTS
- -------------------------------------------------- */
+ /**
+ * CONSTANTS.
+ */
private static final int DEFAULT_KEY_LENGTH = 40;
/*
* See 7.6.2, page 58, PDF 32000-1:2008
*/
- private final static byte[] AES_SALT = {(byte) 0x73, (byte) 0x41, (byte) 0x6c, (byte) 0x54};
-
+ private static final byte[] AES_SALT = { (byte) 0x73, (byte) 0x41, (byte) 0x6c, (byte) 0x54 };
+
/**
* The value of V field of the Encryption dictionary.
*/
@@ -106,11 +105,11 @@ public abstract class SecurityHandler
private Set<COSDictionary> potentialSignatures = new HashSet<COSDictionary>();
- /*
- * If true, AES will be used
+ /**
+ * If true, AES will be used.
*/
private boolean aes;
-
+
/**
* The access permission granted to the current user for the document. These
* permissions are computed during decryption and are in read only mode.
@@ -142,9 +141,8 @@ public abstract class SecurityHandler
* @throws CryptographyException If there is an error with decryption.
*/
public abstract void prepareForDecryption(PDEncryptionDictionary encDictionary, COSArray documentIDArray,
- DecryptionMaterial decryptionMaterial)
- throws CryptographyException, IOException;
-
+ DecryptionMaterial decryptionMaterial) throws CryptographyException, IOException;
+
/**
* Prepare the document for decryption.
*
@@ -153,9 +151,8 @@ public abstract class SecurityHandler
* @throws CryptographyException If there is an error while preparing.
* @throws IOException If there is an error with the document.
*/
- public abstract void decryptDocument(PDDocument doc, DecryptionMaterial mat)
- throws CryptographyException, IOException;
-
+ public abstract void decryptDocument(PDDocument doc, DecryptionMaterial mat) throws CryptographyException,
+ IOException;
/**
* This method must be called by an implementation of this class to really proceed
@@ -168,18 +165,18 @@ public abstract class SecurityHandler
{
COSDictionary trailer = document.getDocument().getTrailer();
- COSArray fields = (COSArray)trailer.getObjectFromPath( "Root/AcroForm/Fields" );
+ COSArray fields = (COSArray) trailer.getObjectFromPath("Root/AcroForm/Fields");
- //We need to collect all the signature dictionaries, for some
- //reason the 'Contents' entry of signatures is not really encrypted
- if( fields != null )
+ // We need to collect all the signature dictionaries, for some
+ // reason the 'Contents' entry of signatures is not really encrypted
+ if (fields != null)
{
- for( int i=0; i<fields.size(); i++ )
+ for (int i = 0; i < fields.size(); i++)
{
- COSDictionary field = (COSDictionary)fields.getObject( i );
- if (field!= null)
+ COSDictionary field = (COSDictionary) fields.getObject(i);
+ if (field != null)
{
- addDictionaryAndSubDictionary( potentialSignatures, field );
+ addDictionaryAndSubDictionary(potentialSignatures, field);
}
else
{
@@ -190,29 +187,29 @@ public abstract class SecurityHandler
List<COSObject> allObjects = document.getDocument().getObjects();
Iterator<COSObject> objectIter = allObjects.iterator();
- while( objectIter.hasNext() )
+ while (objectIter.hasNext())
{
- decryptObject( objectIter.next() );
+ decryptObject(objectIter.next());
}
- document.setEncryptionDictionary( null );
+ document.setEncryptionDictionary(null);
}
- private void addDictionaryAndSubDictionary( Set<COSDictionary> set, COSDictionary dic )
+ private void addDictionaryAndSubDictionary(Set<COSDictionary> set, COSDictionary dic)
{
- if ( dic != null ) // in case dictionary is part of object stream we have null value here
- {
- set.add( dic );
- COSArray kids = (COSArray)dic.getDictionaryObject( COSName.KIDS );
- for( int i=0; kids != null && i<kids.size(); i++ )
- {
- addDictionaryAndSubDictionary( set, (COSDictionary)kids.getObject( i ) );
- }
- COSBase value = dic.getDictionaryObject( COSName.V );
- if( value instanceof COSDictionary )
- {
- addDictionaryAndSubDictionary( set, (COSDictionary)value );
- }
- }
+ if (dic != null) // in case dictionary is part of object stream we have null value here
+ {
+ set.add(dic);
+ COSArray kids = (COSArray) dic.getDictionaryObject(COSName.KIDS);
+ for (int i = 0; kids != null && i < kids.size(); i++)
+ {
+ addDictionaryAndSubDictionary(set, (COSDictionary) kids.getObject(i));
+ }
+ COSBase value = dic.getDictionaryObject(COSName.V);
+ if (value instanceof COSDictionary)
+ {
+ addDictionaryAndSubDictionary(set, (COSDictionary) value);
+ }
+ }
}
/**
@@ -230,7 +227,7 @@ public abstract class SecurityHandler
* reasons and will be removed in the future.
*/
public void encryptData(long objectNumber, long genNumber, InputStream data, OutputStream output)
- throws CryptographyException, IOException
+ throws CryptographyException, IOException
{
// default to encrypting since the function is named "encryptData"
encryptData(objectNumber, genNumber, data, output, false);
@@ -248,97 +245,106 @@ public abstract class SecurityHandler
* @throws CryptographyException If there is an error during the encryption.
* @throws IOException If there is an error reading the data.
*/
- public void encryptData(long objectNumber, long genNumber, InputStream data, OutputStream output, boolean decrypt)
- throws CryptographyException, IOException
+ public void encryptData(long objectNumber, long genNumber, InputStream data, OutputStream output, boolean decrypt)
+ throws CryptographyException, IOException
{
- if (aes && !decrypt) {
+ 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
- //step 1
- //we have the reference
-
- //step 2
- newKey[newKey.length -5] = (byte)(objectNumber & 0xff);
- newKey[newKey.length -4] = (byte)((objectNumber >> 8) & 0xff);
- newKey[newKey.length -3] = (byte)((objectNumber >> 16) & 0xff);
- newKey[newKey.length -2] = (byte)(genNumber & 0xff);
- newKey[newKey.length -1] = (byte)((genNumber >> 8) & 0xff);
+ byte[] newKey = new byte[encryptionKey.length + 5];
+ System.arraycopy(encryptionKey, 0, newKey, 0, encryptionKey.length);
+ // PDF 1.4 reference pg 73
+ // step 1
+ // we have the reference
+
+ // step 2
+ newKey[newKey.length - 5] = (byte) (objectNumber & 0xff);
+ newKey[newKey.length - 4] = (byte) ((objectNumber >> 8) & 0xff);
+ newKey[newKey.length - 3] = (byte) ((objectNumber >> 16) & 0xff);
+ newKey[newKey.length - 2] = (byte) (genNumber & 0xff);
+ newKey[newKey.length - 1] = (byte) ((genNumber >> 8) & 0xff);
- //step 3
+ // step 3
byte[] digestedKey = null;
try
{
- MessageDigest md = MessageDigest.getInstance( "MD5" );
+ MessageDigest md = MessageDigest.getInstance("MD5");
md.update(newKey);
- if (aes) {
+ if (aes)
+ {
md.update(AES_SALT);
}
digestedKey = md.digest();
}
- catch( NoSuchAlgorithmException e )
+ catch (NoSuchAlgorithmException e)
{
- throw new CryptographyException( e );
+ throw new CryptographyException(e);
}
- //step 4
- int length = Math.min( newKey.length, 16 );
- byte[] finalKey = new byte[ length ];
- System.arraycopy( digestedKey, 0, finalKey, 0, length );
+ // step 4
+ int length = Math.min(newKey.length, 16);
+ byte[] finalKey = new byte[length];
+ System.arraycopy(digestedKey, 0, finalKey, 0, length);
if (aes)
{
byte[] iv = new byte[16];
-
+
data.read(iv);
-
- try {
+
+ try
+ {
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-
+
SecretKey aesKey = new SecretKeySpec(finalKey, "AES");
-
+
IvParameterSpec ips = new IvParameterSpec(iv);
-
+
decryptCipher.init(decrypt ? Cipher.DECRYPT_MODE : Cipher.ENCRYPT_MODE, aesKey, ips);
-
+
CipherInputStream cipherStream = new CipherInputStream(data, decryptCipher);
-
- try {
- byte buffer[] = new byte[4096];
- for(int n = 0; -1 != (n = cipherStream.read(buffer));)
+
+ try
+ {
+ byte[] buffer = new byte[4096];
+ for (int n = 0; -1 != (n = cipherStream.read(buffer));)
{
output.write(buffer, 0, n);
}
}
- finally {
+ finally
+ {
cipherStream.close();
}
}
- catch (InvalidKeyException e) {
+ catch (InvalidKeyException e)
+ {
throw new WrappedIOException(e);
}
- catch (InvalidAlgorithmParameterException e) {
+ catch (InvalidAlgorithmParameterException e)
+ {
throw new WrappedIOException(e);
}
- catch (NoSuchAlgorithmException e) {
+ catch (NoSuchAlgorithmException e)
+ {
throw new WrappedIOException(e);
}
- catch (NoSuchPaddingException e) {
+ catch (NoSuchPaddingException e)
+ {
throw new WrappedIOException(e);
}
}
- else {
- rc4.setKey( finalKey );
- rc4.write( data, output );
+ else
+ {
+ rc4.setKey(finalKey);
+ rc4.write(data, output);
}
-
+
output.flush();
}
-
+
/**
* This will decrypt an object in the document.
*
@@ -347,13 +353,12 @@ public abstract class SecurityHandler
* @throws CryptographyException If there is an error decrypting the stream.
* @throws IOException If there is an error getting the stream data.
*/
- private void decryptObject( COSObject object )
- throws CryptographyException, IOException
+ private void decryptObject(COSObject object) throws CryptographyException, IOException
{
long objNum = object.getObjectNumber().intValue();
long genNum = object.getGenerationNumber().intValue();
COSBase base = object.getObject();
- decrypt( base, objNum, genNum );
+ decrypt(base, objNum, genNum);
}
/**
@@ -366,28 +371,27 @@ public abstract class SecurityHandler
* @throws CryptographyException If there is an error decrypting the stream.
* @throws IOException If there is an error getting the stream data.
*/
- private void decrypt( COSBase obj, long objNum, long genNum )
- throws CryptographyException, IOException
+ private void decrypt(COSBase obj, long objNum, long genNum) throws CryptographyException, IOException
{
- if( !objects.contains( obj ) )
+ if (!objects.contains(obj))
{
- objects.add( obj );
+ objects.add(obj);
- if( obj instanceof COSString )
+ if (obj instanceof COSString)
{
- decryptString( (COSString)obj, objNum, genNum );
+ decryptString((COSString) obj, objNum, genNum);
}
- else if( obj instanceof COSStream )
+ else if (obj instanceof COSStream)
{
- decryptStream( (COSStream)obj, objNum, genNum );
+ decryptStream((COSStream) obj, objNum, genNum);
}
- else if( obj instanceof COSDictionary )
+ else if (obj instanceof COSDictionary)
{
- decryptDictionary( (COSDictionary)obj, objNum, genNum );
+ decryptDictionary((COSDictionary) obj, objNum, genNum);
}
- else if( obj instanceof COSArray )
+ else if (obj instanceof COSArray)
{
- decryptArray( (COSArray)obj, objNum, genNum );
+ decryptArray((COSArray) obj, objNum, genNum);
}
}
}
@@ -402,18 +406,13 @@ public abstract class SecurityHandler
* @throws CryptographyException If there is an error getting the stream.
* @throws IOException If there is an error getting the stream data.
*/
- public void decryptStream( COSStream stream, long objNum, long genNum )
- throws CryptographyException, IOException
+ public void decryptStream(COSStream stream, long objNum, long genNum) throws CryptographyException, IOException
{
- decryptDictionary( stream, objNum, genNum );
+ decryptDictionary(stream, objNum, genNum);
InputStream encryptedStream = stream.getFilteredStream();
- encryptData( objNum,
- genNum,
- encryptedStream,
- stream.createFilteredStream(),
- true /* decrypt */);
+ encryptData(objNum, genNum, encryptedStream, stream.createFilteredStream(), true /* decrypt */);
}
-
+
/**
* This will encrypt a stream, but not the dictionary as the dictionary is
* encrypted by visitFromString() in COSWriter and we don't want to encrypt
@@ -426,15 +425,10 @@ public abstract class SecurityHandler
* @throws CryptographyException If there is an error getting the stream.
* @throws IOException If there is an error getting the stream data.
*/
- public void encryptStream( COSStream stream, long objNum, long genNum )
- throws CryptographyException, IOException
+ public void encryptStream(COSStream stream, long objNum, long genNum) throws CryptographyException, IOException
{
InputStream encryptedStream = stream.getFilteredStream();
- encryptData( objNum,
- genNum,
- encryptedStream,
- stream.createFilteredStream(),
- false /* encrypt */);
+ encryptData(objNum, genNum, encryptedStream, stream.createFilteredStream(), false /* encrypt */);
}
/**
@@ -447,22 +441,22 @@ public abstract class SecurityHandler
* @throws CryptographyException If there is an error decrypting the document.
* @throws IOException If there is an error creating a new string.
*/
- private void decryptDictionary( COSDictionary dictionary, long objNum, long genNum )
- throws CryptographyException, IOException
+ private void decryptDictionary(COSDictionary dictionary, long objNum, long genNum) throws CryptographyException,
+ IOException
{
- for( Map.Entry<COSName, COSBase> entry : dictionary.entrySet() )
+ for (Map.Entry<COSName, COSBase> entry : dictionary.entrySet())
{
COSBase value = entry.getValue();
// within a dictionary only strings and streams have to be decrypted
- if (!(value instanceof COSString) && !(value instanceof COSStream))
- continue;
- //if we are a signature dictionary and contain a Contents entry then
- //we don't decrypt it.
- if( !(entry.getKey().getName().equals( "Contents" ) &&
- value instanceof COSString &&
- potentialSignatures.contains( dictionary )))
+ if (value instanceof COSString || value instanceof COSStream || value instanceof COSArray)
{
- decrypt( entry.getValue(), objNum, genNum );
+ // if we are a signature dictionary and contain a Contents entry then
+ // we don't decrypt it.
+ if (!(entry.getKey().getName().equals("Contents") && value instanceof COSString && potentialSignatures
+ .contains(dictionary)))
+ {
+ decrypt(entry.getValue(), objNum, genNum);
+ }
}
}
}
@@ -477,14 +471,13 @@ public abstract class SecurityHandler
* @throws CryptographyException If an error occurs during decryption.
* @throws IOException If an error occurs writing the new string.
*/
- public void decryptString( COSString string, long objNum, long genNum )
- throws CryptographyException, IOException
+ public void decryptString(COSString string, long objNum, long genNum) throws CryptographyException, IOException
{
- ByteArrayInputStream data = new ByteArrayInputStream( string.getBytes() );
+ ByteArrayInputStream data = new ByteArrayInputStream(string.getBytes());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- encryptData( objNum, genNum, data, buffer, true /* decrypt */ );
+ encryptData(objNum, genNum, data, buffer, true /* decrypt */);
string.reset();
- string.append( buffer.toByteArray() );
+ string.append(buffer.toByteArray());
}
/**
@@ -497,12 +490,11 @@ public abstract class SecurityHandler
* @throws CryptographyException If an error occurs during decryption.
* @throws IOException If there is an error accessing the data.
*/
- private void decryptArray( COSArray array, long objNum, long genNum )
- throws CryptographyException, IOException
+ private void decryptArray(COSArray array, long objNum, long genNum) throws CryptographyException, IOException
{
- for( int i=0; i<array.size(); i++ )
+ for (int i = 0; i < array.size(); i++)
{
- decrypt( array.get( i ), objNum, genNum );
+ decrypt(array.get(i), objNum, genNum);
}
}
@@ -537,17 +529,24 @@ public abstract class SecurityHandler
return currentAccessPermission;
}
- /*
+ /**
* True if AES is used for encryption and decryption.
+ *
+ * @return true if AEs is used
*/
- public boolean isAES() {
+ public boolean isAES()
+ {
return aes;
}
- /*
+ /**
* Set to true if AES for encryption and decryption should be used.
+ *
+ * @param aes if true AES will be used
+ *
*/
- public void setAES(boolean aes) {
- this.aes = aes;
+ public void setAES(boolean aesValue)
+ {
+ aes = aesValue;
}
}