Skip to content

Commit dc8d3fe

Browse files
committed
Support encrypted documents in SignatureValidator
DEVSIX-8671
1 parent 9fe2316 commit dc8d3fe

File tree

15 files changed

+387
-20
lines changed

15 files changed

+387
-20
lines changed

kernel/src/main/java/com/itextpdf/kernel/logs/KernelLogMessageConstant.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ public final class KernelLogMessageConstant {
107107
public static final String ALGORITHM_NOT_FROM_SPEC =
108108
"Requested algorithm might not be supported by the pdf specification.";
109109

110+
public static final String MEMORYLIMITAWAREHANDLER_OVERRIDE_CREATENEWINSTANCE_METHOD =
111+
"MemoryLimitsAwareHandler#createNewInstance method must be overriden.";
112+
110113
private KernelLogMessageConstant() {
111114
//Private constructor will prevent the instantiation of this class directly
112115
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ This file is part of the iText (R) project.
2424

2525
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
2626
import com.itextpdf.kernel.exceptions.MemoryLimitsAwareException;
27+
import com.itextpdf.kernel.logs.KernelLogMessageConstant;
2728

2829
import java.util.HashSet;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
2932

3033
/**
3134
* A {@link MemoryLimitsAwareHandler} handles memory allocation and prevents decompressed
@@ -38,6 +41,7 @@ This file is part of the iText (R) project.
3841
*/
3942
public class MemoryLimitsAwareHandler {
4043

44+
private static final Logger LOGGER = LoggerFactory.getLogger(MemoryLimitsAwareHandler.class);
4145

4246
private static final int SINGLE_SCALE_COEFFICIENT = 100;
4347
private static final int SUM_SCALE_COEFFICIENT = 500;
@@ -88,6 +92,25 @@ private MemoryLimitsAwareHandler(int maxSizeOfSingleDecompressedPdfStream, long
8892
this.maxXObjectsSizePerPage = maxXObjectsSizePerPage;
8993
}
9094

95+
/**
96+
* Creates a new instance of {@link MemoryLimitsAwareHandler} by copying settings from this instance
97+
* of {@link MemoryLimitsAwareHandler}.
98+
*
99+
* @return a new instance of {@link MemoryLimitsAwareHandler}.
100+
*/
101+
public MemoryLimitsAwareHandler createNewInstance() {
102+
MemoryLimitsAwareHandler to = new MemoryLimitsAwareHandler();
103+
to.maxSizeOfSingleDecompressedPdfStream = this.maxSizeOfSingleDecompressedPdfStream;
104+
to.maxSizeOfDecompressedPdfStreamsSum = this.maxSizeOfDecompressedPdfStreamsSum;
105+
to.maxNumberOfElementsInXrefStructure = this.maxNumberOfElementsInXrefStructure;
106+
to.maxXObjectsSizePerPage = this.maxXObjectsSizePerPage;
107+
if (this.getClass() != MemoryLimitsAwareHandler.class) {
108+
LOGGER.warn(KernelLogMessageConstant.MEMORYLIMITAWAREHANDLER_OVERRIDE_CREATENEWINSTANCE_METHOD);
109+
}
110+
111+
return to;
112+
}
113+
91114
/**
92115
* Gets the maximum allowed size which can be occupied by a single decompressed pdf stream.
93116
*

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,15 @@ public boolean isEncrypted() {
732732
return encrypted;
733733
}
734734

735+
/**
736+
* Gets a copy of {@link ReaderProperties} used to create this instance of {@link PdfReader}.
737+
*
738+
* @return a copy of {@link ReaderProperties} used to create this instance of {@link PdfReader}
739+
*/
740+
public ReaderProperties getPropertiesCopy() {
741+
return new ReaderProperties(properties);
742+
}
743+
735744
/**
736745
* Parses the entire PDF
737746
*

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public List<DocumentRevision> getAllRevisions() throws IOException {
7777
raf.createSourceView(), 0, raf.length());
7878

7979
try (InputStream inputStream = new RASInputStream(source);
80-
PdfReader newReader = new PdfReader(inputStream);
80+
PdfReader newReader = new PdfReader(inputStream, reader.getPropertiesCopy());
8181
PdfDocument newDocument = new PdfDocument(newReader,
8282
new DocumentProperties().setEventCountingMetaInfo(metaInfo))) {
8383
newDocument.getXref().unmarkReadingCompleted();

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

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,38 @@ This file is part of the iText (R) project.
2626

2727
import java.security.Key;
2828
import java.security.cert.Certificate;
29+
import java.util.Arrays;
2930

31+
/**
32+
* The class representing various properties used to read PDF documents.
33+
*/
3034
public class ReaderProperties {
3135

32-
33-
//added by ujihara for decryption
3436
protected byte[] password;
35-
36-
//added by Aiken Sam for certificate decryption
3737
protected Key certificateKey;
38-
//added by Aiken Sam for certificate decryption
3938
protected Certificate certificate;
40-
//added by Aiken Sam for certificate decryption
4139
protected String certificateKeyProvider;
4240
protected IExternalDecryptionProcess externalDecryptionProcess;
43-
4441
protected MemoryLimitsAwareHandler memoryLimitsAwareHandler;
4542

43+
/**
44+
* Creates an instance of {@link ReaderProperties}.
45+
*/
46+
public ReaderProperties() {
47+
// Empty constructor
48+
}
49+
50+
ReaderProperties(ReaderProperties readerProperties) {
51+
this.password = readerProperties.password == null ? null :
52+
Arrays.copyOf(readerProperties.password, readerProperties.password.length);
53+
this.certificateKey = readerProperties.certificateKey;
54+
this.certificate = readerProperties.certificate;
55+
this.certificateKeyProvider = readerProperties.certificateKeyProvider;
56+
this.externalDecryptionProcess = readerProperties.externalDecryptionProcess;
57+
this.memoryLimitsAwareHandler = readerProperties.memoryLimitsAwareHandler == null ? null :
58+
readerProperties.memoryLimitsAwareHandler.createNewInstance();
59+
}
60+
4661
/**
4762
* Defines the password which will be used if the document is encrypted with standard encryption.
4863
* This could be either user or owner password.
@@ -95,14 +110,6 @@ public ReaderProperties setPublicKeySecurityParams(Certificate certificate, IExt
95110
return this;
96111
}
97112

98-
private void clearEncryptionParams() {
99-
this.password = null;
100-
this.certificate = null;
101-
this.certificateKey = null;
102-
this.certificateKeyProvider = null;
103-
this.externalDecryptionProcess = null;
104-
}
105-
106113
/**
107114
* Sets the memory handler which will be used to handle decompressed PDF streams.
108115
*
@@ -114,4 +121,11 @@ public ReaderProperties setMemoryLimitsAwareHandler(MemoryLimitsAwareHandler mem
114121
return this;
115122
}
116123

124+
private void clearEncryptionParams() {
125+
this.password = null;
126+
this.certificate = null;
127+
this.certificateKey = null;
128+
this.certificateKeyProvider = null;
129+
this.externalDecryptionProcess = null;
130+
}
117131
}

kernel/src/test/java/com/itextpdf/kernel/pdf/MemoryLimitsAwareHandlerTest.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,17 @@ This file is part of the iText (R) project.
2525

2626
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
2727
import com.itextpdf.kernel.exceptions.MemoryLimitsAwareException;
28+
import com.itextpdf.kernel.logs.KernelLogMessageConstant;
2829
import com.itextpdf.test.AssertUtil;
2930
import com.itextpdf.test.ExtendedITextTest;
31+
import com.itextpdf.test.annotations.LogMessage;
32+
import com.itextpdf.test.annotations.LogMessages;
33+
3034
import org.junit.jupiter.api.Assertions;
3135
import org.junit.jupiter.api.Test;
3236
import org.junit.jupiter.api.Tag;
37+
import org.slf4j.Logger;
38+
import org.slf4j.LoggerFactory;
3339

3440
@Tag("UnitTest")
3541
public class MemoryLimitsAwareHandlerTest extends ExtendedITextTest {
@@ -67,7 +73,6 @@ public boolean isMemoryLimitsAwarenessRequiredOnDecompression(PdfArray filters)
6773

6874
Assertions.assertFalse(defaultHandler.isMemoryLimitsAwarenessRequiredOnDecompression(filters));
6975
Assertions.assertTrue(customHandler.isMemoryLimitsAwarenessRequiredOnDecompression(filters));
70-
7176
}
7277

7378
@Test
@@ -161,6 +166,53 @@ public void checkCapacityTest() {
161166
AssertUtil.doesNotThrow(() -> memoryLimitsAwareHandler.checkIfXrefStructureExceedsTheLimit(capacityToSet));
162167
}
163168

169+
@Test
170+
@LogMessages(messages = {@LogMessage(messageTemplate =
171+
KernelLogMessageConstant.MEMORYLIMITAWAREHANDLER_OVERRIDE_CREATENEWINSTANCE_METHOD)})
172+
public void createCopyMemoryHandlerWarningTest() {
173+
MemoryLimitsAwareHandler customHandler = new MemoryLimitsAwareHandler() {};
174+
175+
customHandler.setMaxNumberOfElementsInXrefStructure(1);
176+
customHandler.setMaxXObjectsSizePerPage(2);
177+
customHandler.setMaxSizeOfDecompressedPdfStreamsSum(3);
178+
customHandler.setMaxSizeOfSingleDecompressedPdfStream(4);
179+
180+
MemoryLimitsAwareHandler copy = customHandler.createNewInstance();
181+
182+
Assertions.assertEquals(1, copy.getMaxNumberOfElementsInXrefStructure());
183+
Assertions.assertEquals(2, copy.getMaxXObjectsSizePerPage());
184+
Assertions.assertEquals(3, copy.getMaxSizeOfDecompressedPdfStreamsSum());
185+
Assertions.assertEquals(4, copy.getMaxSizeOfSingleDecompressedPdfStream());
186+
}
187+
188+
@Test
189+
public void createCopyMemoryHandlerNoWarningTest() {
190+
MemoryLimitsAwareHandler customHandler = new MemoryLimitsAwareHandler() {
191+
@Override
192+
public MemoryLimitsAwareHandler createNewInstance() {
193+
MemoryLimitsAwareHandler to = new MemoryLimitsAwareHandler();
194+
to.setMaxSizeOfSingleDecompressedPdfStream(this.getMaxSizeOfSingleDecompressedPdfStream());
195+
to.setMaxSizeOfDecompressedPdfStreamsSum(this.getMaxSizeOfDecompressedPdfStreamsSum());
196+
to.setMaxNumberOfElementsInXrefStructure(this.getMaxNumberOfElementsInXrefStructure());
197+
to.setMaxXObjectsSizePerPage(this.getMaxXObjectsSizePerPage());
198+
199+
return to;
200+
}
201+
};
202+
203+
customHandler.setMaxNumberOfElementsInXrefStructure(1);
204+
customHandler.setMaxXObjectsSizePerPage(2);
205+
customHandler.setMaxSizeOfDecompressedPdfStreamsSum(3);
206+
customHandler.setMaxSizeOfSingleDecompressedPdfStream(4);
207+
208+
MemoryLimitsAwareHandler copy = customHandler.createNewInstance();
209+
210+
Assertions.assertEquals(1, copy.getMaxNumberOfElementsInXrefStructure());
211+
Assertions.assertEquals(2, copy.getMaxXObjectsSizePerPage());
212+
Assertions.assertEquals(3, copy.getMaxSizeOfDecompressedPdfStreamsSum());
213+
Assertions.assertEquals(4, copy.getMaxSizeOfSingleDecompressedPdfStream());
214+
}
215+
164216
private static void testSingleStream(MemoryLimitsAwareHandler handler) {
165217
String expectedExceptionMessage = KernelExceptionMessageConstant.DURING_DECOMPRESSION_SINGLE_STREAM_OCCUPIED_MORE_MEMORY_THAN_ALLOWED;
166218
int expectedFailureIndex = 10;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2024 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
package com.itextpdf.kernel.pdf;
24+
25+
import com.itextpdf.test.ExtendedITextTest;
26+
27+
import java.nio.charset.StandardCharsets;
28+
import org.junit.jupiter.api.Assertions;
29+
import org.junit.jupiter.api.Tag;
30+
import org.junit.jupiter.api.Test;
31+
32+
@Tag("UnitTest")
33+
public class ReaderPropertiesTest extends ExtendedITextTest {
34+
35+
@Test
36+
public void copyConstructorTest() {
37+
MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler();
38+
handler.setMaxXObjectsSizePerPage(10);
39+
ReaderProperties properties = new ReaderProperties().setPassword("123".getBytes(StandardCharsets.ISO_8859_1))
40+
.setMemoryLimitsAwareHandler(handler);
41+
42+
ReaderProperties copy = new ReaderProperties(properties);
43+
44+
Assertions.assertArrayEquals(copy.password, properties.password);
45+
46+
Assertions.assertNotEquals(copy.memoryLimitsAwareHandler, properties.memoryLimitsAwareHandler);
47+
Assertions.assertEquals(copy.memoryLimitsAwareHandler.getMaxXObjectsSizePerPage(),
48+
properties.memoryLimitsAwareHandler.getMaxXObjectsSizePerPage());
49+
Assertions.assertEquals(10, copy.memoryLimitsAwareHandler.getMaxXObjectsSizePerPage());
50+
}
51+
}

sign/src/main/java/com/itextpdf/signatures/validation/DocumentRevisionsValidator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,8 @@ private boolean revisionContainsSignature(DocumentRevision revision, String sign
575575
private boolean createDocumentAndPerformOperation(DocumentRevision revision, PdfDocument originalDocument,
576576
ValidationReport report, Function<PdfDocument, Boolean> operation) {
577577
try (InputStream inputStream = createInputStreamFromRevision(originalDocument, revision);
578-
PdfReader reader = new PdfReader(inputStream).setStrictnessLevel(StrictnessLevel.CONSERVATIVE);
578+
PdfReader reader = new PdfReader(inputStream, originalDocument.getReader().getPropertiesCopy())
579+
.setStrictnessLevel(StrictnessLevel.CONSERVATIVE);
579580
PdfDocument documentWithRevision = new PdfDocument(reader,
580581
new DocumentProperties().setEventCountingMetaInfo(metaInfo))) {
581582
return (boolean) operation.apply(documentWithRevision);

sign/src/main/java/com/itextpdf/signatures/validation/SignatureValidator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,9 @@ private ValidationReport validate(String signatureName) {
275275

276276
for (String fieldName : signatureNames) {
277277
ValidationReport subReport = new ValidationReport();
278-
try (PdfDocument doc = new PdfDocument(new PdfReader(util.extractRevision(fieldName))
279-
.setStrictnessLevel(PdfReader.StrictnessLevel.CONSERVATIVE),
278+
try (PdfDocument doc = new PdfDocument(
279+
new PdfReader(util.extractRevision(fieldName), originalDocument.getReader().getPropertiesCopy())
280+
.setStrictnessLevel(PdfReader.StrictnessLevel.CONSERVATIVE),
280281
new DocumentProperties().setEventCountingMetaInfo(metaInfo))) {
281282
subReport.merge(validateLatestSignature(doc));
282283
} catch (IOException | RuntimeException e) {

0 commit comments

Comments
 (0)