Skip to content

Commit daafb56

Browse files
committed
Process SignMetaInfo to ignore multiple document creation events per one signing operation
Make sure that it is possible to pass metainfo to the newly added sign classes Introduce isEventCountingMetaInfoSet check DEVSIX-8216
1 parent ee1e118 commit daafb56

File tree

12 files changed

+484
-22
lines changed

12 files changed

+484
-22
lines changed

commons/src/main/java/com/itextpdf/commons/actions/ProductNameConstant.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public final class ProductNameConstant {
3535
* itext-core constant.
3636
*/
3737
public static final String ITEXT_CORE = "itext-core";
38+
/**
39+
* itext-core sign module constant.
40+
*/
41+
public static final String ITEXT_CORE_SIGN = "itext-core-sign";
3842
/**
3943
* pdfhtml constant.
4044
*/

commons/src/main/java/com/itextpdf/commons/actions/contexts/AbstractContextManagerConfigurationEvent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected void registerGenericContext(Collection<String> namespaces, Collection<
5050
/**
5151
* Unregisters certain namespaces.
5252
*
53-
* @param namespaces the namespaces to be unregisted
53+
* @param namespaces the namespaces to be unregistered
5454
*/
5555
protected void unregisterContext(Collection<String> namespaces) {
5656
ContextManager.getInstance().unregisterContext(namespaces);

commons/src/main/java/com/itextpdf/commons/actions/contexts/ContextManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public class ContextManager {
4444
local.registerGenericContext(NamespaceConstant.ITEXT_CORE_NAMESPACES,
4545
Collections.singleton(ProductNameConstant.ITEXT_CORE));
4646

47+
local.registerGenericContext(Collections.singleton(NamespaceConstant.CORE_SIGN),
48+
Collections.singleton(ProductNameConstant.ITEXT_CORE_SIGN));
49+
4750
local.registerGenericContext(Collections.singletonList(NamespaceConstant.PDF_HTML),
4851
Collections.singleton(ProductNameConstant.PDF_HTML));
4952

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,13 @@ public DocumentProperties setEventCountingMetaInfo(IMetaInfo metaInfo) {
5858
this.metaInfo = metaInfo;
5959
return this;
6060
}
61+
62+
/**
63+
* Checks if the document event counting meta info was already set.
64+
*
65+
* @return true if the document event counting meta info is set, false otherwise.
66+
*/
67+
public boolean isEventCountingMetaInfoSet() {
68+
return this.metaInfo != null;
69+
}
6170
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.commons.actions.contexts.IMetaInfo;
26+
import com.itextpdf.test.ExtendedITextTest;
27+
import com.itextpdf.test.annotations.type.UnitTest;
28+
import org.junit.Assert;
29+
import org.junit.Test;
30+
import org.junit.experimental.categories.Category;
31+
32+
@Category(UnitTest.class)
33+
public class DocumentPropertiesUnitTest extends ExtendedITextTest {
34+
35+
@Test
36+
public void setEventCountingMetaInfoTest() {
37+
DocumentProperties documentProperties = new DocumentProperties();
38+
documentProperties.setEventCountingMetaInfo(new TestMetaInfo());
39+
Assert.assertTrue(documentProperties.isEventCountingMetaInfoSet());
40+
}
41+
42+
@Test
43+
public void metaInfoIsNotSetTest() {
44+
DocumentProperties documentProperties = new DocumentProperties();
45+
Assert.assertFalse(documentProperties.isEventCountingMetaInfoSet());
46+
}
47+
48+
private static class TestMetaInfo implements IMetaInfo {
49+
}
50+
}

sign/src/main/java/com/itextpdf/signatures/PadesTwoPhaseSigningHelper.java

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,15 @@ This file is part of the iText (R) project.
6161
*/
6262
public class PadesTwoPhaseSigningHelper {
6363
private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.getFactory();
64-
64+
6565
private IOcspClient ocspClient;
6666
private ICrlClient crlClient;
6767
private ITSAClient tsaClient;
6868
private String temporaryDirectoryPath;
6969
private String timestampSignatureName;
7070
private StampingProperties stampingProperties = new StampingProperties().useAppendMode();
71+
private StampingProperties stampingPropertiesWithMetaInfo = (StampingProperties) new StampingProperties()
72+
.useAppendMode().setEventCountingMetaInfo(new SignMetaInfo());
7173
private IIssuingCertificateRetriever issuingCertificateRetriever = new IssuingCertificateRetriever();
7274
private int estimatedSize = -1;
7375

@@ -92,7 +94,7 @@ public PadesTwoPhaseSigningHelper() {
9294
*
9395
* @return same instance of {@link PadesTwoPhaseSigningHelper}
9496
*/
95-
public PadesTwoPhaseSigningHelper setOcspClient(IOcspClient ocspClient){
97+
public PadesTwoPhaseSigningHelper setOcspClient(IOcspClient ocspClient) {
9698
this.ocspClient = ocspClient;
9799
return this;
98100
}
@@ -121,7 +123,7 @@ public PadesTwoPhaseSigningHelper setTrustedCertificates(List<Certificate> certi
121123
*
122124
* @return same instance of {@link PadesTwoPhaseSigningHelper}
123125
*/
124-
public PadesTwoPhaseSigningHelper setCrlClient(ICrlClient crlClient){
126+
public PadesTwoPhaseSigningHelper setCrlClient(ICrlClient crlClient) {
125127
this.crlClient = crlClient;
126128
return this;
127129
}
@@ -215,6 +217,9 @@ public PadesTwoPhaseSigningHelper setTimestampSignatureName(String timestampSign
215217
*/
216218
public PadesTwoPhaseSigningHelper setStampingProperties(StampingProperties stampingProperties) {
217219
this.stampingProperties = stampingProperties;
220+
if (stampingProperties.isEventCountingMetaInfoSet()) {
221+
this.stampingPropertiesWithMetaInfo = stampingProperties;
222+
}
218223
return this;
219224
}
220225

@@ -238,8 +243,9 @@ public CMSContainer createCMSContainerWithoutSignature(Certificate[] certificate
238243
throws IOException, GeneralSecurityException {
239244
Certificate[] fullChain = issuingCertificateRetriever.retrieveMissingCertificates(certificates);
240245
X509Certificate[] x509FullChain = Arrays.asList(fullChain).toArray(new X509Certificate[0]);
241-
PdfTwoPhaseSigner pdfTwoPhaseSigner = new PdfTwoPhaseSigner(inputDocument, outputStream);
242-
246+
PdfTwoPhaseSigner pdfTwoPhaseSigner = new PdfTwoPhaseSigner(inputDocument, outputStream);
247+
pdfTwoPhaseSigner.setStampingProperties(stampingProperties);
248+
243249
CMSContainer cms = new CMSContainer();
244250
SignerInfo signerInfo = new SignerInfo();
245251
String digestAlgorithmOid = DigestAlgorithms.getAllowedDigest(digestAlgorithm);
@@ -254,11 +260,11 @@ public CMSContainer createCMSContainerWithoutSignature(Certificate[] certificate
254260
realSignatureSize += tsaClient.getTokenSizeEstimate();
255261
}
256262
int expectedSignatureSize = estimatedSize < 0 ? realSignatureSize : estimatedSize;
257-
263+
258264
byte[] digestedDocumentBytes = pdfTwoPhaseSigner.prepareDocumentForSignature(signerProperties, digestAlgorithm,
259265
PdfName.Adobe_PPKLite, PdfName.ETSI_CAdES_DETACHED, expectedSignatureSize, true);
260266
signerInfo.setMessageDigest(digestedDocumentBytes);
261-
267+
262268
return cms;
263269
}
264270

@@ -277,7 +283,7 @@ public void signCMSContainerWithBaselineBProfile(IExternalSignature externalSign
277283
OutputStream outputStream, String signatureFieldName, CMSContainer cmsContainer) throws Exception {
278284
setSignatureAlgorithmAndSignature(externalSignature, cmsContainer);
279285

280-
try (PdfDocument document = new PdfDocument(inputDocument)) {
286+
try (PdfDocument document = new PdfDocument(inputDocument, stampingProperties)) {
281287
PdfTwoPhaseSigner.addSignatureToPreparedDocument(document, signatureFieldName, outputStream, cmsContainer);
282288
} finally {
283289
outputStream.close();
@@ -311,7 +317,7 @@ public void signCMSContainerWithBaselineTProfile(IExternalSignature externalSign
311317
cmsContainer.getSignerInfo().addUnSignedAttribute(timestampAttribute);
312318
}
313319

314-
try (PdfDocument document = new PdfDocument(inputDocument)) {
320+
try (PdfDocument document = new PdfDocument(inputDocument, stampingProperties)) {
315321
PdfTwoPhaseSigner.addSignatureToPreparedDocument(document, signatureFieldName, outputStream, cmsContainer);
316322
} finally {
317323
outputStream.close();
@@ -337,8 +343,8 @@ public void signCMSContainerWithBaselineLTProfile(IExternalSignature externalSig
337343
signCMSContainerWithBaselineTProfile(externalSignature, inputDocument, tempOutput, signatureFieldName,
338344
cmsContainer);
339345
try (InputStream inputStream = padesSigner.createInputStream();
340-
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputStream),
341-
new PdfWriter(outputStream), new StampingProperties().useAppendMode())) {
346+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputStream), new PdfWriter(outputStream),
347+
stampingPropertiesWithMetaInfo)) {
342348
padesSigner.performLtvVerification(pdfDocument,
343349
Collections.singletonList(signatureFieldName),
344350
LtvVerification.RevocationDataNecessity.REQUIRED_FOR_SIGNING_CERTIFICATE);
@@ -367,9 +373,8 @@ public void signCMSContainerWithBaselineLTAProfile(IExternalSignature externalSi
367373
signCMSContainerWithBaselineTProfile(externalSignature, inputDocument, tempOutput, signatureFieldName,
368374
cmsContainer);
369375
try (InputStream inputStream = padesSigner.createInputStream();
370-
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputStream),
371-
new PdfWriter(padesSigner.createOutputStream()),
372-
new StampingProperties().useAppendMode())) {
376+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputStream),
377+
new PdfWriter(padesSigner.createOutputStream()), stampingPropertiesWithMetaInfo)) {
373378
padesSigner.performLtvVerification(pdfDocument,
374379
Collections.singletonList(signatureFieldName),
375380
LtvVerification.RevocationDataNecessity.REQUIRED_FOR_SIGNING_CERTIFICATE);
@@ -399,13 +404,13 @@ private byte[] setSignatureAlgorithmAndSignature(IExternalSignature externalSign
399404
SignatureMechanisms.getSignatureMechanismOid(providedSignatureAlgorithm, signatureDigest),
400405
signatureMechanismParams.toEncodable().toASN1Primitive()));
401406
}
402-
407+
403408
byte[] signedAttributes = cmsContainer.getSerializedSignedAttributes();
404409
byte[] signature = externalSignature.sign(signedAttributes);
405410
cmsContainer.getSignerInfo().setSignature(signature);
406411
return signature;
407412
}
408-
413+
409414
private PdfPadesSigner createPadesSigner(PdfReader inputDocument, OutputStream outputStream) {
410415
PdfPadesSigner padesSigner = new PdfPadesSigner(inputDocument, outputStream);
411416
padesSigner.setOcspClient(ocspClient);

sign/src/main/java/com/itextpdf/signatures/PdfPadesSigner.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public class PdfPadesSigner {
7070
private String temporaryDirectoryPath = null;
7171
private IExternalDigest externalDigest = new BouncyCastleDigest();
7272
private StampingProperties stampingProperties = new StampingProperties().useAppendMode();
73+
private StampingProperties stampingPropertiesWithMetaInfo = (StampingProperties) new StampingProperties()
74+
.useAppendMode().setEventCountingMetaInfo(new SignMetaInfo());
7375

7476
private ByteArrayOutputStream tempOutputStream;
7577
private File tempFile;
@@ -173,7 +175,7 @@ public void signWithBaselineLTProfile(SignerProperties signerProperties, Certifi
173175
performSignDetached(signerProperties, false, externalSignature, chain, tsaClient);
174176
try (InputStream inputStream = createInputStream();
175177
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputStream),
176-
new PdfWriter(outputStream), new StampingProperties().useAppendMode())) {
178+
new PdfWriter(outputStream), stampingPropertiesWithMetaInfo)) {
177179
performLtvVerification(pdfDocument, Collections.singletonList(signerProperties.getFieldName()),
178180
LtvVerification.RevocationDataNecessity.REQUIRED_FOR_SIGNING_CERTIFICATE);
179181
}
@@ -218,7 +220,7 @@ public void signWithBaselineLTAProfile(SignerProperties signerProperties, Certif
218220
performSignDetached(signerProperties, false, externalSignature, chain, tsaClient);
219221
try (InputStream inputStream = createInputStream();
220222
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputStream),
221-
new PdfWriter(createOutputStream()), new StampingProperties().useAppendMode())) {
223+
new PdfWriter(createOutputStream()), stampingPropertiesWithMetaInfo)) {
222224
performLtvVerification(pdfDocument, Collections.singletonList(signerProperties.getFieldName()),
223225
LtvVerification.RevocationDataNecessity.REQUIRED_FOR_SIGNING_CERTIFICATE);
224226
performTimestamping(pdfDocument, outputStream, tsaClient);
@@ -259,7 +261,7 @@ public void prolongSignatures(ITSAClient tsaClient)
259261
throws IOException, GeneralSecurityException {
260262
OutputStream documentOutputStream = tsaClient == null ? outputStream : createOutputStream();
261263
try (PdfDocument pdfDocument = new PdfDocument(reader, new PdfWriter(documentOutputStream),
262-
new StampingProperties().useAppendMode())) {
264+
stampingProperties)) {
263265
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
264266
List<String> signatureNames = signatureUtil.getSignatureNames();
265267
if (signatureNames.isEmpty()) {
@@ -326,6 +328,9 @@ public PdfPadesSigner setTimestampSignatureName(String timestampSignatureName) {
326328
*/
327329
public PdfPadesSigner setStampingProperties(StampingProperties stampingProperties) {
328330
this.stampingProperties = stampingProperties;
331+
if (stampingProperties.isEventCountingMetaInfoSet()) {
332+
this.stampingPropertiesWithMetaInfo = stampingProperties;
333+
}
329334
return this;
330335
}
331336

sign/src/main/java/com/itextpdf/signatures/PdfTwoPhaseSigner.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,28 @@ This file is part of the iText (R) project.
4242
import java.util.HashMap;
4343
import java.util.Map;
4444

45+
/**
46+
* Class that prepares document and adds the signature to it while performing signing operation in two steps
47+
* (see {@link PadesTwoPhaseSigningHelper} for more info).
48+
*
49+
* <p>
50+
* Firstly, this class allows to prepare the document for signing and calculate the document digest to sign.
51+
* Secondly, it adds an existing signature to a PDF where space was already reserved.
52+
*/
4553
public class PdfTwoPhaseSigner {
4654

47-
4855
private final PdfReader reader;
4956
private final OutputStream outputStream;
5057
private IExternalDigest externalDigest;
5158
private StampingProperties stampingProperties = new StampingProperties().useAppendMode();
5259
private boolean closed;
5360

61+
/**
62+
* Creates new {@link PdfTwoPhaseSigner} instance.
63+
*
64+
* @param reader {@link PdfReader} instance to read the original PDF file
65+
* @param outputStream {@link OutputStream} output stream to write the resulting PDF file into
66+
*/
5467
public PdfTwoPhaseSigner(PdfReader reader, OutputStream outputStream) {
5568
this.reader = reader;
5669
this.outputStream = outputStream;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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.signatures;
24+
25+
import com.itextpdf.commons.actions.contexts.IMetaInfo;
26+
27+
class SignMetaInfo implements IMetaInfo {
28+
}

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.signatures.validation;
2424

25+
import com.itextpdf.commons.actions.contexts.IMetaInfo;
2526
import com.itextpdf.commons.utils.MessageFormatUtil;
2627
import com.itextpdf.io.source.RASInputStream;
2728
import com.itextpdf.io.source.RandomAccessFileOrArray;
2829
import com.itextpdf.io.source.WindowRandomAccessSource;
30+
import com.itextpdf.kernel.pdf.DocumentProperties;
2931
import com.itextpdf.kernel.pdf.DocumentRevision;
3032
import com.itextpdf.kernel.pdf.PdfArray;
3133
import com.itextpdf.kernel.pdf.PdfDictionary;
@@ -69,16 +71,28 @@ class DocumentRevisionsValidator {
6971
static final String UNEXPECTED_ENTRY_IN_XREF =
7072
"New PDF document revision contains unexpected entry \"{0}\" in XREF table.";
7173

74+
private IMetaInfo metaInfo;
75+
7276
DocumentRevisionsValidator() {
7377
// Empty constructor.
7478
}
7579

80+
/**
81+
* Sets the {@link IMetaInfo} that will be used during {@link PdfDocument} creation.
82+
*
83+
* @param metaInfo meta info to set
84+
*/
85+
public void setEventCountingMetaInfo(IMetaInfo metaInfo) {
86+
this.metaInfo = metaInfo;
87+
}
88+
7689
ValidationReport validateRevision(PdfDocument originalDocument, PdfDocument documentWithoutRevision,
7790
DocumentRevision revision) throws IOException {
7891
ValidationReport validationReport = new ValidationReport();
7992
try (InputStream inputStream = createInputStreamFromRevision(originalDocument, revision);
8093
PdfReader newReader = new PdfReader(inputStream);
81-
PdfDocument documentWithRevision = new PdfDocument(newReader)) {
94+
PdfDocument documentWithRevision = new PdfDocument(newReader,
95+
new DocumentProperties().setEventCountingMetaInfo(metaInfo))) {
8296
Set<PdfIndirectReference> indirectReferences = revision.getModifiedObjects();
8397
if (!compareCatalogs(documentWithoutRevision, documentWithRevision, validationReport)) {
8498
return validationReport;

0 commit comments

Comments
 (0)