diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java index 7adecd6a484..ec36761ebcd 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java @@ -441,6 +441,28 @@ private SecureRandom getSecureRandom() return new SecureRandom(); } + /** + * This will decrypt a string if it is not in set of the objects. + * + * @param string The string to decrypt. + * @param objNum The object number. + * @param genNum The object generation Number. + * + * @return the encrypted/decrypted COS object + */ + private COSBase decryptStringIfAbsent(COSString string, long objNum, long genNum) + { + // PDFBOX-4477: only cache strings and streams, this improves speed and memory footprint + if (objects.contains(string)) + { + return string; + } + // replace the given COSString object with the encrypted/decrypted version + COSBase decryptedString = decryptString(string, objNum, genNum); + objects.add(decryptedString); + return decryptedString; + } + /** * This will dispatch to the correct method. * @@ -457,35 +479,43 @@ public COSBase decrypt(COSBase obj, long objNum, long genNum) throws IOException // PDFBOX-4477: only cache strings and streams, this improves speed and memory footprint if (obj instanceof COSString) { - if (objects.contains(obj)) - { - return obj; - } - // replace the given COSString object with the encrypted/decrypted version - COSBase decryptedString = decryptString((COSString) obj, objNum, genNum); - objects.add(decryptedString); - return decryptedString; + return decryptStringIfAbsent((COSString)obj, objNum, genNum); } - if (obj instanceof COSStream) + else if (obj instanceof COSStream) { - if (objects.contains(obj)) - { - return obj; - } - objects.add(obj); - decryptStream((COSStream) obj, objNum, genNum); + return decryptStreamIfAbsent((COSStream)obj, objNum, genNum); } else if (obj instanceof COSDictionary) { - decryptDictionary((COSDictionary) obj, objNum, genNum); + return decryptDictionary((COSDictionary) obj, objNum, genNum); } else if (obj instanceof COSArray) { - decryptArray((COSArray) obj, objNum, genNum); + return decryptArray((COSArray) obj, objNum, genNum); } return obj; } + /** + * This will decrypt a stream if it is not in set of the objects. + * + * @param stream The stream to decrypt. + * @param objNum The object number. + * @param genNum The object generation Number. + * + * @return the encrypted/decrypted COS object + */ + private COSBase decryptStreamIfAbsent(COSStream stream, long objNum, long genNum) throws IOException + { + if (!objects.contains(stream)) + { + objects.add(stream); + decryptStream(stream, objNum, genNum); + } + + return stream; + } + /** * This will decrypt a stream. * @@ -589,14 +619,16 @@ public void encryptStream(COSStream stream, long objNum, int genNum) throws IOEx * @param objNum The object number. * @param genNum The object generation number. * + * @return the encrypted/decrypted COS object + * * @throws IOException If there is an error creating a new string. */ - private void decryptDictionary(COSDictionary dictionary, long objNum, long genNum) throws IOException + private COSBase decryptDictionary(COSDictionary dictionary, long objNum, long genNum) throws IOException { if (dictionary.getItem(COSName.CF) != null) { // PDFBOX-2936: avoid orphan /CF dictionaries found in US govt "I-" files - return; + return dictionary; } COSName type = dictionary.getCOSName(COSName.TYPE); boolean isSignature = COSName.SIG.equals(type) || COSName.DOC_TIME_STAMP.equals(type) || @@ -613,11 +645,25 @@ private void decryptDictionary(COSDictionary dictionary, long objNum, long genNu } COSBase value = entry.getValue(); // within a dictionary only the following kind of COS objects have to be decrypted - if (value instanceof COSString || value instanceof COSArray || value instanceof COSDictionary) + if (value instanceof COSString) + { + entry.setValue(decryptStringIfAbsent((COSString)value, objNum, genNum)); + } + else if (value instanceof COSArray) { - entry.setValue(decrypt(value, objNum, genNum)); + entry.setValue(decryptArray((COSArray) value, objNum, genNum)); + } + else if (value instanceof COSStream) + { + entry.setValue(decryptStreamIfAbsent((COSStream)value, objNum, genNum)); + } + else if (value instanceof COSDictionary) + { + entry.setValue(decryptDictionary((COSDictionary)value, objNum, genNum)); } } + + return dictionary; } /** @@ -678,14 +724,18 @@ public COSBase encryptString(COSString string, long objNum, int genNum) throws I * @param objNum The object number. * @param genNum The object generation number. * + * @return the encrypted/decrypted COS object + * * @throws IOException If there is an error accessing the data. */ - private void decryptArray(COSArray array, long objNum, long genNum) throws IOException + private COSBase decryptArray(COSArray array, long objNum, long genNum) throws IOException { for (int i = 0; i < array.size(); i++) { array.set(i, decrypt(array.get(i), objNum, genNum)); } + + return array; } /**