Skip to content

Commit dc0cad3

Browse files
committed
fix id pad in preserve encryption mode. add tests. move preserve tests
DEVSIX-7597
1 parent 08a48fa commit dc0cad3

File tree

85 files changed

+390
-181
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+390
-181
lines changed

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocument.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ public void close() {
10181018
// either writer properties, or in the writer init section on document open or from pdfreader. So we
10191019
// shouldn't worry about it being null next
10201020
PdfObject fileId = PdfEncryption.createInfoId(ByteUtils.getIsoBytes(originalDocumentId.getValue()),
1021-
ByteUtils.getIsoBytes(modifiedDocumentId.getValue()));
1021+
ByteUtils.getIsoBytes(modifiedDocumentId.getValue()), this.properties.preserveEncryption);
10221022
xref.writeXrefTableAndTrailer(this, fileId, crypto);
10231023
writer.flush();
10241024
if (writer.getOutputStream() instanceof CountOutputStream) {

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfEncryption.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This file is part of the iText (R) project.
2323
package com.itextpdf.kernel.pdf;
2424

2525
import com.itextpdf.commons.utils.SystemUtil;
26+
import com.itextpdf.io.source.ByteBuffer;
2627
import com.itextpdf.kernel.crypto.IDecryptor;
2728
import com.itextpdf.kernel.crypto.OutputStreamEncryption;
2829
import com.itextpdf.kernel.crypto.securityhandler.PubKeySecurityHandler;
@@ -290,20 +291,39 @@ public static PdfObject createInfoId(byte[] id, boolean modified) {
290291
*
291292
* @param firstId the first id
292293
* @param secondId the second id
294+
*
293295
* @return PdfObject containing the two entries.
296+
* @deprecated Use {@link #createInfoId(byte[], byte[], boolean)} instead
294297
*/
298+
@Deprecated
295299
public static PdfObject createInfoId(byte[] firstId, byte[] secondId) {
296-
if ( firstId.length < 16 ) {
297-
firstId = padByteArrayTo16(firstId);
298-
}
300+
return createInfoId(firstId, secondId, false);
301+
}
302+
303+
/**
304+
* Creates a PdfLiteral that contains an array of two id entries. These entries are both hexadecimal
305+
* strings containing up to 16 hex characters. The first entry is the original id, the second entry
306+
* should be different from the first one if the document has changed.
307+
*
308+
* @param firstId the first id
309+
* @param secondId the second id
310+
* @param preserveEncryption the encryption preserve
311+
*
312+
* @return PdfObject containing the two entries.
313+
*/
314+
public static PdfObject createInfoId(byte[] firstId, byte[] secondId, boolean preserveEncryption) {
315+
if (!preserveEncryption) {
316+
if (firstId.length < 16) {
317+
firstId = padByteArrayTo16(firstId);
318+
}
299319

300-
if ( secondId.length < 16 ) {
301-
secondId = padByteArrayTo16(secondId);
320+
if (secondId.length < 16) {
321+
secondId = padByteArrayTo16(secondId);
322+
}
302323
}
303324

304-
com.itextpdf.io.source.ByteBuffer buf = new com.itextpdf.io.source.ByteBuffer(90);
325+
ByteBuffer buf = new ByteBuffer(90);
305326
buf.append('[').append('<');
306-
307327
for (int k = 0; k < firstId.length; ++k)
308328
buf.appendHex(firstId[k]);
309329
buf.append('>').append('<');

kernel/src/test/java/com/itextpdf/kernel/crypto/PdfEncryptionTest.java renamed to kernel/src/test/java/com/itextpdf/kernel/crypto/pdfencryption/PdfEncryptionTest.java

Lines changed: 57 additions & 166 deletions
Large diffs are not rendered by default.
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.itextpdf.kernel.crypto.pdfencryption;
2+
3+
import com.itextpdf.io.font.constants.StandardFonts;
4+
import com.itextpdf.kernel.font.PdfFontFactory;
5+
import com.itextpdf.kernel.pdf.*;
6+
import com.itextpdf.kernel.utils.CompareTool;
7+
import org.junit.Assert;
8+
9+
import java.io.IOException;
10+
import java.nio.charset.StandardCharsets;
11+
12+
public class PdfEncryptionTestUtils {
13+
14+
private final String destinationFolder;
15+
16+
private final String sourceFolder;
17+
18+
public static final String PAGE_TEXT_CONTENT = "Hello world!";
19+
20+
public static final String CUSTOM_INFO_ENTRY_KEY = "Custom";
21+
22+
public static final String CUSTOM_INFO_ENTRY_VALUE = "String";
23+
24+
/**
25+
* User password.
26+
*/
27+
public static byte[] USER = "Hello".getBytes(StandardCharsets.ISO_8859_1);
28+
29+
/**
30+
* Owner password.
31+
*/
32+
public static byte[] OWNER = "World".getBytes(StandardCharsets.ISO_8859_1);
33+
34+
35+
public PdfEncryptionTestUtils(String destinationFolder, String sourceFolder) {
36+
this.destinationFolder = destinationFolder;
37+
this.sourceFolder = sourceFolder;
38+
}
39+
40+
public void compareEncryptedPdf(String filename) throws IOException, InterruptedException {
41+
checkDecryptedWithPasswordContent(destinationFolder + filename, OWNER, PAGE_TEXT_CONTENT);
42+
checkDecryptedWithPasswordContent(destinationFolder + filename, USER, PAGE_TEXT_CONTENT);
43+
44+
CompareTool compareTool = new CompareTool().enableEncryptionCompare();
45+
String compareResult = compareTool.compareByContent(destinationFolder + filename,
46+
sourceFolder + "cmp_" + filename, destinationFolder, "diff_", USER, USER);
47+
if (compareResult != null) {
48+
Assert.fail(compareResult);
49+
}
50+
}
51+
52+
public void checkDecryptedWithPasswordContent(String src, byte[] password, String pageContent)
53+
throws IOException {
54+
checkDecryptedWithPasswordContent(src, password, pageContent, false);
55+
}
56+
57+
public void checkDecryptedWithPasswordContent(String src, byte[] password, String pageContent,
58+
boolean expectError) throws IOException {
59+
PdfReader reader = CompareTool.createOutputReader(src, new ReaderProperties().setPassword(password));
60+
PdfDocument document = new PdfDocument(reader);
61+
PdfPage page = document.getPage(1);
62+
63+
boolean expectedContentFound = new String(page.getStreamBytes(0)).contains(pageContent);
64+
String actualCustomInfoEntry = document.getTrailer().getAsDictionary(PdfName.Info)
65+
.getAsString(new PdfName(CUSTOM_INFO_ENTRY_KEY)).toUnicodeString();
66+
67+
if (!expectError) {
68+
Assert.assertTrue("Expected content: \n" + pageContent, expectedContentFound);
69+
Assert.assertEquals("Encrypted custom", CUSTOM_INFO_ENTRY_VALUE, actualCustomInfoEntry);
70+
} else {
71+
Assert.assertFalse("Expected content: \n" + pageContent, expectedContentFound);
72+
Assert.assertNotEquals("Encrypted custom", CUSTOM_INFO_ENTRY_VALUE, actualCustomInfoEntry);
73+
}
74+
75+
document.close();
76+
}
77+
78+
public static void writeTextBytesOnPageContent(PdfPage page, String text) throws IOException {
79+
page.getFirstContentStream().getOutputStream().writeBytes(("q\n" +
80+
"BT\n" +
81+
"36 706 Td\n" +
82+
"0 0 Td\n" +
83+
"/F1 24 Tf\n" +
84+
"(" + text + ")Tj\n" +
85+
"0 0 Td\n" +
86+
"ET\n" +
87+
"Q ").getBytes(StandardCharsets.ISO_8859_1));
88+
page.getResources().addFont(page.getDocument(), PdfFontFactory.createFont(StandardFonts.HELVETICA));
89+
}
90+
91+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.itextpdf.kernel.crypto.pdfencryption;
2+
3+
import com.itextpdf.kernel.logs.KernelLogMessageConstant;
4+
import com.itextpdf.kernel.pdf.*;
5+
import com.itextpdf.kernel.utils.CompareTool;
6+
import com.itextpdf.test.ExtendedITextTest;
7+
import com.itextpdf.test.annotations.LogMessage;
8+
import com.itextpdf.test.annotations.LogMessages;
9+
import com.itextpdf.test.annotations.type.IntegrationTest;
10+
import org.junit.AfterClass;
11+
import org.junit.Assert;
12+
import org.junit.BeforeClass;
13+
import org.junit.Test;
14+
import org.junit.experimental.categories.Category;
15+
16+
import java.io.IOException;
17+
18+
19+
20+
@Category(IntegrationTest.class)
21+
public class PdfPreserveEncryptionTest extends ExtendedITextTest {
22+
23+
public static final String destinationFolder = "./target/test/com/itextpdf/kernel/crypto/pdfencryption/PdfPreserveEncryptionTest/";
24+
25+
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/crypto/pdfencryption/PdfPreserveEncryptionTest/";
26+
27+
public PdfEncryptionTestUtils encryptionUtil = new PdfEncryptionTestUtils(destinationFolder, sourceFolder);
28+
29+
@BeforeClass
30+
public static void beforeClass() {
31+
createOrClearDestinationFolder(destinationFolder);
32+
}
33+
34+
@AfterClass
35+
public static void afterClass() {
36+
CompareTool.cleanup(destinationFolder);
37+
}
38+
39+
@Test
40+
@LogMessages(messages = {
41+
@LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT, ignore = true),
42+
@LogMessage(messageTemplate = VersionConforming.DEPRECATED_ENCRYPTION_ALGORITHMS)})
43+
public void stampAndUpdateVersionPreserveStandard40() throws InterruptedException, IOException {
44+
String filename = "stampAndUpdateVersionPreserveStandard40.pdf";
45+
PdfDocument doc = new PdfDocument(
46+
new PdfReader(sourceFolder + "encryptedWithPasswordStandard40.pdf",
47+
new ReaderProperties().setPassword(PdfEncryptionTestUtils.OWNER)),
48+
CompareTool.createTestPdfWriter(destinationFolder + filename,
49+
new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0)),
50+
new StampingProperties().preserveEncryption());
51+
doc.close();
52+
53+
encryptionUtil.compareEncryptedPdf(filename);
54+
}
55+
56+
@Test
57+
@LogMessages(messages = {
58+
@LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT, ignore = true),
59+
@LogMessage(messageTemplate = VersionConforming.DEPRECATED_AES256_REVISION)})
60+
public void stampAndUpdateVersionPreserveAes256() throws InterruptedException, IOException {
61+
String filename = "stampAndUpdateVersionPreserveAes256.pdf";
62+
PdfDocument doc = new PdfDocument(
63+
new PdfReader(sourceFolder + "encryptedWithPasswordAes256.pdf",
64+
new ReaderProperties().setPassword(PdfEncryptionTestUtils.OWNER)),
65+
CompareTool.createTestPdfWriter(destinationFolder + filename,
66+
new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0)),
67+
new StampingProperties().preserveEncryption());
68+
doc.close();
69+
encryptionUtil.compareEncryptedPdf(filename);
70+
}
71+
72+
@Test
73+
@LogMessages(messages = @LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT,
74+
ignore = true))
75+
public void encryptAes256EncryptedStampingPreserve() throws InterruptedException, IOException {
76+
String filename = "encryptAes256EncryptedStampingPreserve.pdf";
77+
String src = sourceFolder + "encryptedWithPlainMetadata.pdf";
78+
String out = destinationFolder + filename;
79+
80+
PdfDocument pdfDoc = new PdfDocument(
81+
new PdfReader(src, new ReaderProperties().setPassword(PdfEncryptionTestUtils.OWNER)),
82+
CompareTool.createTestPdfWriter(out, new WriterProperties()),
83+
new StampingProperties().preserveEncryption());
84+
85+
pdfDoc.close();
86+
87+
CompareTool compareTool = new CompareTool().enableEncryptionCompare();
88+
String compareResult = compareTool.compareByContent(out, sourceFolder + "cmp_" + filename, destinationFolder,
89+
"diff_", PdfEncryptionTestUtils.USER, PdfEncryptionTestUtils.USER);
90+
if (compareResult != null) {
91+
Assert.fail(compareResult);
92+
}
93+
}
94+
95+
@Test
96+
public void preserveEncryptionShorterDocumentId() throws IOException, InterruptedException {
97+
String filename = "preserveEncryptionWithShortId.pdf";
98+
String src = sourceFolder + "encryptedWithShortId.pdf";
99+
String out = destinationFolder + filename;
100+
101+
PdfDocument pdfDoc = new PdfDocument(
102+
new PdfReader(src, new ReaderProperties().setPassword(PdfEncryptionTestUtils.OWNER)),
103+
new PdfWriter(out, new WriterProperties()),
104+
new StampingProperties().preserveEncryption());
105+
106+
pdfDoc.close();
107+
CompareTool compareTool = new CompareTool().enableEncryptionCompare();
108+
String compareResult = compareTool.compareByContent(out, sourceFolder + "cmp_" + filename, destinationFolder,
109+
"diff_", null, null);
110+
if (compareResult != null) {
111+
Assert.fail(compareResult);
112+
}
113+
}
114+
}

kernel/src/test/java/com/itextpdf/kernel/crypto/UnicodeBasedPasswordEncryptionTest.java renamed to kernel/src/test/java/com/itextpdf/kernel/crypto/pdfencryption/UnicodeBasedPasswordEncryptionTest.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This file is part of the iText (R) project.
2020
You should have received a copy of the GNU Affero General Public License
2121
along with this program. If not, see <https://www.gnu.org/licenses/>.
2222
*/
23-
package com.itextpdf.kernel.crypto;
23+
package com.itextpdf.kernel.crypto.pdfencryption;
2424

2525
import com.itextpdf.kernel.logs.KernelLogMessageConstant;
2626
import com.itextpdf.kernel.pdf.EncryptionConstants;
@@ -50,11 +50,13 @@ This file is part of the iText (R) project.
5050
@Category(BouncyCastleIntegrationTest.class)
5151
public class UnicodeBasedPasswordEncryptionTest extends ExtendedITextTest {
5252

53-
public static final String destinationFolder = "./target/test/com/itextpdf/kernel/crypto/UnicodeBasedPasswordEncryptionTest/";
54-
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/crypto/UnicodeBasedPasswordEncryptionTest/";
53+
public static final String destinationFolder = "./target/test/com/itextpdf/kernel/crypto/pdfencryption/UnicodeBasedPasswordEncryptionTest/";
54+
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/crypto/pdfencryption/UnicodeBasedPasswordEncryptionTest/";
5555

5656
private static Map<String, SaslPreparedString> nameToSaslPrepared;
5757

58+
PdfEncryptionTestUtils encryptionUtil = new PdfEncryptionTestUtils(destinationFolder,sourceFolder);
59+
5860
static {
5961
// values are calculated with com.ibm.icu.text.StringPrep class in icu4j v58.2 lib
6062
nameToSaslPrepared = new LinkedHashMap<>();
@@ -200,18 +202,18 @@ public void aes256EncryptedPdfWithUnicodeBasedPassword() throws IOException, Int
200202
private void encryptAes256AndCheck(String filename, byte[] ownerPassword) throws IOException, InterruptedException {
201203
int permissions = EncryptionConstants.ALLOW_SCREENREADERS;
202204
WriterProperties writerProperties = new WriterProperties()
203-
.setStandardEncryption(PdfEncryptionTest.USER, ownerPassword, permissions, EncryptionConstants.ENCRYPTION_AES_256)
205+
.setStandardEncryption(PdfEncryptionTestUtils.USER, ownerPassword, permissions, EncryptionConstants.ENCRYPTION_AES_256)
204206
.setPdfVersion(PdfVersion.PDF_2_0);
205207
PdfWriter writer = CompareTool.createTestPdfWriter(destinationFolder + filename, writerProperties.addXmpMetadata());
206208
PdfDocument document = new PdfDocument(writer);
207-
document.getDocumentInfo().setMoreInfo(PdfEncryptionTest.customInfoEntryKey, PdfEncryptionTest.customInfoEntryValue);
209+
document.getDocumentInfo().setMoreInfo(PdfEncryptionTestUtils.CUSTOM_INFO_ENTRY_KEY, PdfEncryptionTestUtils.CUSTOM_INFO_ENTRY_VALUE);
208210
PdfPage page = document.addNewPage();
209-
PdfEncryptionTest.writeTextBytesOnPageContent(page, PdfEncryptionTest.pageTextContent);
211+
PdfEncryptionTestUtils.writeTextBytesOnPageContent(page, PdfEncryptionTestUtils.PAGE_TEXT_CONTENT);
210212

211213
page.flush();
212214
document.close();
213215

214-
PdfEncryptionTest.checkDecryptedWithPasswordContent(destinationFolder + filename, ownerPassword, PdfEncryptionTest.pageTextContent);
216+
encryptionUtil.checkDecryptedWithPasswordContent(destinationFolder + filename, ownerPassword, PdfEncryptionTestUtils.PAGE_TEXT_CONTENT);
215217

216218
CompareTool compareTool = new CompareTool().enableEncryptionCompare();
217219
String compareResult = compareTool.compareByContent(destinationFolder + filename, sourceFolder + "cmp_" + filename, destinationFolder, "diff_", ownerPassword, ownerPassword);

0 commit comments

Comments
 (0)