Skip to content

Commit 9dfc503

Browse files
author
Alexandr Pliushchou
committed
Fix producer line when LTA signatures are used
DEVSIX-9024
1 parent f6eba77 commit 9dfc503

File tree

8 files changed

+132
-29
lines changed

8 files changed

+132
-29
lines changed

commons/src/main/java/com/itextpdf/commons/actions/producer/ProducerBuilder.java

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,31 @@ public static String modifyProducer(List<? extends AbstractProductProcessITextEv
109109
}
110110

111111
final String newProducer = buildProducer(confirmedEvents);
112-
if (oldProducer == null || oldProducer.isEmpty()) {
113-
return newProducer;
112+
//if the last time document was modified or created with the itext of the same version,
113+
//then no changes occur.
114+
return mergeProducerLines(oldProducer, newProducer);
115+
}
116+
117+
/**
118+
* Merges two producer lines. If first producer line <code>null</code> or empty, it will be replaced with the second one.
119+
* Otherwise second producer line will be attached with <code>modified using</code> prefix. If first producer line
120+
* already contains <code>modified using</code> substring with the second producer line at the end, first producer
121+
* line will be returned unchanged.
122+
*
123+
* @param firstProducer first producer line
124+
* @param secondProducer second producer line
125+
126+
* @return modified producer line
127+
*/
128+
public static String mergeProducerLines(String firstProducer, String secondProducer) {
129+
if (firstProducer == null || firstProducer.isEmpty()) {
130+
return secondProducer;
114131
} else {
115-
//if the last time document was modified or created with the itext of the same version,
116-
//then no changes occur.
117-
if (oldProducer.equals(newProducer)
118-
|| oldProducer.endsWith(MODIFIED_USING + newProducer)) {
119-
return oldProducer;
132+
if (firstProducer.equals(secondProducer)
133+
|| firstProducer.endsWith(MODIFIED_USING + secondProducer)) {
134+
return firstProducer;
120135
} else {
121-
return oldProducer + MODIFIED_USING + newProducer;
136+
return firstProducer + MODIFIED_USING + secondProducer;
122137
}
123138
}
124139
}

commons/src/test/java/com/itextpdf/commons/actions/producer/ProducerBuilderTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,29 @@ public void oldProducerEqualsCurrentProducerTest() {
220220
Assertions.assertEquals("some Author", newProducerLine);
221221
}
222222

223+
@Test
224+
public void mergeEquivalentProducersTest() {
225+
String producerLine = "some producer";
226+
String result = ProducerBuilder.mergeProducerLines(producerLine, producerLine);
227+
Assertions.assertEquals(producerLine, result);
228+
}
229+
230+
@Test
231+
public void mergeDifferentProducersTest() {
232+
String producerLine = "some producer";
233+
String secondProducerLine = "another producer";
234+
String result = ProducerBuilder.mergeProducerLines(producerLine, secondProducerLine);
235+
Assertions.assertEquals(producerLine + "; modified using " + secondProducerLine, result);
236+
}
237+
238+
@Test
239+
public void mergeProducerEndsWithSecondProducerTest() {
240+
String producerLine = "some producer; modified using another producer";
241+
String secondProducerLine = "another producer";
242+
String result = ProducerBuilder.mergeProducerLines(producerLine, secondProducerLine);
243+
Assertions.assertEquals(producerLine, result);
244+
}
245+
223246
private List<ConfirmedEventWrapper> getEvents(String initialProducerLine, int ... indexes) {
224247
List<ConfirmedEventWrapper> events = new ArrayList<>();
225248

kernel/src/main/java/com/itextpdf/kernel/actions/events/FlushPdfDocumentEvent.java

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,33 +74,35 @@ protected void doAction() {
7474
}
7575
List<AbstractProductProcessITextEvent> events = getEvents(pdfDocument.getDocumentIdWrapper());
7676

77+
final String oldProducer = pdfDocument.getDocumentInfo().getProducer();
78+
String newProducer;
7779
if (events == null || events.isEmpty()) {
7880
final ProductData productData = ITextCoreProductData.getInstance();
79-
final String noEventProducer = "iText\u00ae \u00a9" + productData.getSinceCopyrightYear() + "-"
80-
+ productData.getToCopyrightYear() + " Apryse Group NV (no registered products)";
81-
pdfDocument.getDocumentInfo().setProducer(noEventProducer);
82-
return;
83-
}
84-
85-
final Set<String> products = new HashSet<>();
86-
for (final AbstractProductProcessITextEvent event : events) {
87-
pdfDocument.getFingerPrint().registerProduct(event.getProductData());
88-
if (event.getConfirmationType() == EventConfirmationType.ON_CLOSE) {
89-
EventManager.getInstance().onEvent(new ConfirmEvent(pdfDocument.getDocumentIdWrapper(), event));
81+
final String noEventProducer = "iText\u00ae " + productData.getPublicProductName() + " " +
82+
productData.getVersion() + " \u00a9" + productData.getSinceCopyrightYear() + "-"
83+
+ productData.getToCopyrightYear() + " Apryse Group NV";
84+
newProducer = ProducerBuilder.mergeProducerLines(oldProducer, noEventProducer);
85+
} else {
86+
final Set<String> products = new HashSet<>();
87+
for (final AbstractProductProcessITextEvent event : events) {
88+
pdfDocument.getFingerPrint().registerProduct(event.getProductData());
89+
if (event.getConfirmationType() == EventConfirmationType.ON_CLOSE) {
90+
EventManager.getInstance().onEvent(new ConfirmEvent(pdfDocument.getDocumentIdWrapper(), event));
91+
}
92+
products.add(event.getProductName());
9093
}
91-
products.add(event.getProductName());
92-
}
9394

94-
for (final String product : products) {
95-
final ITextProductEventProcessor processor = getActiveProcessor(product);
96-
if (processor == null && LOGGER.isWarnEnabled()) {
97-
LOGGER.warn(MessageFormatUtil.format(KernelLogMessageConstant.UNKNOWN_PRODUCT_INVOLVED, product));
95+
for (final String product : products) {
96+
final ITextProductEventProcessor processor = getActiveProcessor(product);
97+
if (processor == null && LOGGER.isWarnEnabled()) {
98+
LOGGER.warn(MessageFormatUtil.format(KernelLogMessageConstant.UNKNOWN_PRODUCT_INVOLVED, product));
99+
}
98100
}
101+
102+
newProducer = ProducerBuilder.modifyProducer(getConfirmedEvents(pdfDocument.getDocumentIdWrapper()),
103+
oldProducer);
99104
}
100105

101-
final String oldProducer = pdfDocument.getDocumentInfo().getProducer();
102-
final String newProducer =
103-
ProducerBuilder.modifyProducer(getConfirmedEvents(pdfDocument.getDocumentIdWrapper()), oldProducer);
104106
pdfDocument.getDocumentInfo().setProducer(newProducer);
105107
}
106108

kernel/src/test/java/com/itextpdf/kernel/actions/events/FlushPdfDocumentEventTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,14 @@ public void doActionNullDocumentTest() {
142142

143143
@Test
144144
public void doActionNullEventMapTest() throws IOException {
145+
final ProductData productData = ITextCoreProductData.getInstance();
146+
final String expectedProducer = "iText\u00ae " + productData.getPublicProductName() + " " +
147+
productData.getVersion() + " \u00a9" + productData.getSinceCopyrightYear() + "-"
148+
+ productData.getToCopyrightYear() + " Apryse Group NV";
145149
try (PdfDocument document = new DummyPdfDocument(new PdfReader(SOURCE_FOLDER + "hello.pdf"))) {
146150
AssertUtil.doesNotThrow(() -> new FlushPdfDocumentEvent(document).doAction());
147151
Assertions.assertTrue(document.getDocumentInfo().getProducer()
148-
.contains("Apryse Group NV (no registered products)"));
152+
.contains(expectedProducer));
149153
}
150154
}
151155

sign/src/test/java/com/itextpdf/signatures/sign/PdfPadesSignerTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,21 @@ This file is part of the iText (R) project.
2323
package com.itextpdf.signatures.sign;
2424

2525
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
26+
import com.itextpdf.commons.actions.data.ProductData;
2627
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
2728
import com.itextpdf.commons.bouncycastle.operator.AbstractOperatorCreationException;
2829
import com.itextpdf.commons.bouncycastle.pkcs.AbstractPKCSException;
2930
import com.itextpdf.commons.utils.FileUtil;
3031
import com.itextpdf.commons.utils.MessageFormatUtil;
3132
import com.itextpdf.forms.form.element.SignatureFieldAppearance;
33+
import com.itextpdf.io.source.ByteArrayOutputStream;
34+
import com.itextpdf.kernel.actions.data.ITextCoreProductData;
3235
import com.itextpdf.kernel.crypto.DigestAlgorithms;
3336
import com.itextpdf.kernel.exceptions.PdfException;
3437
import com.itextpdf.kernel.geom.Rectangle;
38+
import com.itextpdf.kernel.pdf.PdfDocument;
3539
import com.itextpdf.kernel.pdf.PdfReader;
40+
import com.itextpdf.kernel.pdf.PdfWriter;
3641
import com.itextpdf.signatures.ICrlClient;
3742
import com.itextpdf.signatures.IExternalSignature;
3843
import com.itextpdf.signatures.PdfPadesSigner;
@@ -48,6 +53,7 @@ This file is part of the iText (R) project.
4853
import com.itextpdf.test.ExtendedITextTest;
4954
import com.itextpdf.test.TestUtil;
5055

56+
import java.io.ByteArrayInputStream;
5157
import java.io.IOException;
5258
import java.security.GeneralSecurityException;
5359
import java.security.PrivateKey;
@@ -56,6 +62,7 @@ This file is part of the iText (R) project.
5662
import java.security.cert.CertificateException;
5763
import java.security.cert.X509Certificate;
5864
import java.util.Arrays;
65+
5966
import org.junit.jupiter.api.Assertions;
6067
import org.junit.jupiter.api.Assumptions;
6168
import org.junit.jupiter.api.BeforeAll;
@@ -298,6 +305,58 @@ private SignerProperties createSignerProperties() {
298305
return signerProperties;
299306
}
300307

308+
@Test
309+
public void producerLineWithMetaInfoUsedTest()
310+
throws IOException, GeneralSecurityException, AbstractOperatorCreationException, AbstractPKCSException {
311+
String fileName = "producerLineWithMetaInfoUsed.pdf";
312+
String outFileName = destinationFolder + fileName;
313+
String srcFileName = sourceFolder + "helloWorldDoc.pdf";
314+
String signCertFileName = certsSrc + "signCertRsa01.pem";
315+
String tsaCertFileName = certsSrc + "tsCertRsa.pem";
316+
String caCertFileName = certsSrc + "rootRsa.pem";
317+
318+
Certificate[] signRsaChain = PemFileHelper.readFirstChain(signCertFileName);
319+
PrivateKey signRsaPrivateKey = PemFileHelper.readFirstKey(signCertFileName, password);
320+
IExternalSignature pks =
321+
new PrivateKeySignature(signRsaPrivateKey, DigestAlgorithms.SHA256, FACTORY.getProviderName());
322+
Certificate[] tsaChain = PemFileHelper.readFirstChain(tsaCertFileName);
323+
PrivateKey tsaPrivateKey = PemFileHelper.readFirstKey(tsaCertFileName, password);
324+
X509Certificate caCert = (X509Certificate) PemFileHelper.readFirstChain(caCertFileName)[0];
325+
PrivateKey caPrivateKey = PemFileHelper.readFirstKey(caCertFileName, password);
326+
327+
SignerProperties signerProperties = new SignerProperties();
328+
329+
PdfPadesSigner padesSigner = createPdfPadesSigner(srcFileName, outFileName);
330+
331+
TestTsaClient testTsa = new TestTsaClient(Arrays.asList(tsaChain), tsaPrivateKey);
332+
ICrlClient crlClient = new TestCrlClient().addBuilderForCertIssuer(caCert, caPrivateKey);
333+
TestOcspClient ocspClient = new TestOcspClient().addBuilderForCertIssuer(caCert, caPrivateKey);
334+
335+
padesSigner.setOcspClient(ocspClient).setCrlClient(crlClient);
336+
337+
padesSigner.signWithBaselineLTAProfile(signerProperties, signRsaChain, pks, testTsa);
338+
339+
byte[] docBytes;
340+
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
341+
new PdfDocument(new PdfReader(srcFileName) ,new PdfWriter(outputStream)).close();
342+
docBytes = outputStream.toByteArray();
343+
}
344+
345+
try (PdfDocument signedPdf = new PdfDocument(new PdfReader(outFileName))) {
346+
try (PdfDocument regularPdf = new PdfDocument(new PdfReader(new ByteArrayInputStream(docBytes)))) {
347+
ProductData productData = ITextCoreProductData.getInstance();
348+
String newlyAddedProducer = "iText\u00ae " + productData.getPublicProductName() + " " +
349+
productData.getVersion() + " \u00a9" + productData.getSinceCopyrightYear() + "-"
350+
+ productData.getToCopyrightYear() + " Apryse Group NV";
351+
String actualProducerLine = signedPdf.getDocumentInfo().getProducer();
352+
String regularProducerLine = regularPdf.getDocumentInfo().getProducer();
353+
354+
Assertions.assertTrue(actualProducerLine.contains(regularProducerLine));
355+
Assertions.assertTrue(actualProducerLine.contains(newlyAddedProducer));
356+
}
357+
}
358+
}
359+
301360
private PdfPadesSigner createPdfPadesSigner(String srcFileName, String outFileName) throws IOException {
302361
return new PdfPadesSigner(new PdfReader(FileUtil.getInputStreamForFile(srcFileName)),
303362
FileUtil.getFileOutputStream(outFileName));

0 commit comments

Comments
 (0)