EncryptedEmbeddedStreamsHandlerTest.java
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2025 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itextpdf.kernel.pdf;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.logs.KernelLogMessageConstant;
import com.itextpdf.kernel.pdf.annot.PdfFileAttachmentAnnotation;
import com.itextpdf.kernel.pdf.filespec.PdfFileSpec;
import com.itextpdf.kernel.utils.CompareTool;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.TestUtil;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;
import java.io.IOException;
@Tag("BouncyCastleIntegrationTest")
public class EncryptedEmbeddedStreamsHandlerTest extends ExtendedITextTest {
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/pdf/EncryptedEmbeddedStreamsHandlerTest/";
public static final String destinationFolder = TestUtil.getOutputPath() + "/kernel/pdf/EncryptedEmbeddedStreamsHandlerTest/";
@BeforeAll
public static void beforeClass() {
createDestinationFolder(destinationFolder);
}
@AfterAll
public static void afterClass() {
CompareTool.cleanup(destinationFolder);
}
@Test
@LogMessages(messages = @LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT,
ignore = true))
public void noReaderStandardEncryptionAddFileAttachment() throws IOException, InterruptedException {
// Test result here is wrong. Note that createEncryptedDocument adds EncryptionConstants.EMBEDDED_FILES_ONLY.
String outFileName = destinationFolder + "noReaderStandardEncryptionAddFileAttachment.pdf";
String cmpFileName = sourceFolder + "cmp_noReaderStandardEncryptionAddFileAttachment.pdf";
PdfDocument pdfDocument = createEncryptedDocument(EncryptionConstants.STANDARD_ENCRYPTION_128, outFileName);
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(
pdfDocument, "file".getBytes(), "description", "file.txt", null, null, null);
pdfDocument.addFileAttachment("file.txt", fs);
pdfDocument.addNewPage();
pdfDocument.close();
Assertions.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, "diff", "password".getBytes(), "password".getBytes()));
}
@Test
@LogMessages(messages = @LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT,
ignore = true))
public void noReaderAesEncryptionAddFileAttachment() throws IOException, InterruptedException {
// Test result here is wrong. Note that createEncryptedDocument adds EncryptionConstants.EMBEDDED_FILES_ONLY.
String outFileName = destinationFolder + "noReaderAesEncryptionAddFileAttachment.pdf";
String cmpFileName = sourceFolder + "cmp_noReaderAesEncryptionAddFileAttachment.pdf";
PdfDocument pdfDocument = createEncryptedDocument(EncryptionConstants.ENCRYPTION_AES_128, outFileName);
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(
pdfDocument, "file".getBytes(), "description", "file.txt", null, null, null);
pdfDocument.addFileAttachment("file.txt", fs);
pdfDocument.addNewPage();
pdfDocument.close();
Assertions.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, "diff", "password".getBytes(), "password".getBytes()));
}
@Test
@LogMessages(messages = @LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT,
ignore = true))
public void withReaderStandardEncryptionAddFileAttachment() throws IOException, InterruptedException {
String outFileName = destinationFolder + "withReaderStandardEncryptionAddFileAttachment.pdf";
String cmpFileName = sourceFolder + "cmp_withReaderStandardEncryptionAddFileAttachment.pdf";
PdfReader reader = new PdfReader(sourceFolder + "pdfWithFileAttachments.pdf", new ReaderProperties().setPassword("password".getBytes()));
// Setting compression level to zero doesn't affect the encryption at any level.
// We do it to simplify observation of the resultant PDF.
PdfDocument pdfDocument = new PdfDocument(reader, CompareTool.createTestPdfWriter(outFileName).setCompressionLevel(0));
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(
pdfDocument, "file".getBytes(), "description", "file.txt", null, null, null);
pdfDocument.addFileAttachment("file.txt", fs);
pdfDocument.addNewPage();
pdfDocument.close();
Assertions.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, "diff"));
}
@Test
@LogMessages(messages = @LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT,
ignore = true))
public void noReaderStandardEncryptionAddAnnotation() throws IOException, InterruptedException {
// Test result here is wrong. Note that createEncryptedDocument adds EncryptionConstants.EMBEDDED_FILES_ONLY.
String outFileName = destinationFolder + "noReaderStandardEncryptionAddAnnotation.pdf";
String cmpFileName = sourceFolder + "cmp_noReaderStandardEncryptionAddAnnotation.pdf";
PdfDocument pdfDocument = createEncryptedDocument(EncryptionConstants.STANDARD_ENCRYPTION_128, outFileName);
pdfDocument.addNewPage();
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(
pdfDocument, "file".getBytes(), "description", "file.txt", null, null, null);
pdfDocument.getPage(1).addAnnotation(new PdfFileAttachmentAnnotation(new Rectangle(100, 100), fs));
pdfDocument.close();
Assertions.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, "diff", "password".getBytes(), "password".getBytes()));
}
@Test
@LogMessages(messages = @LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT,
ignore = true))
public void withReaderStandardEncryptionAddAnnotation() throws IOException, InterruptedException {
String outFileName = destinationFolder + "withReaderStandardEncryptionAddAnnotation.pdf";
String cmpFileName = sourceFolder + "cmp_withReaderStandardEncryptionAddAnnotation.pdf";
PdfReader reader = new PdfReader(sourceFolder + "pdfWithFileAttachmentAnnotations.pdf", new ReaderProperties().setPassword("password".getBytes()));
// Setting compression level to zero doesn't affect the encryption at any level.
// We do it to simplify observation of the resultant PDF.
PdfDocument pdfDocument = new PdfDocument(reader, CompareTool.createTestPdfWriter(outFileName).setCompressionLevel(0));
pdfDocument.addNewPage();
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(
pdfDocument, "file".getBytes(), "description", "file.txt", null, null, null);
pdfDocument.getPage(1).addAnnotation(new PdfFileAttachmentAnnotation(new Rectangle(100, 100), fs));
pdfDocument.close();
Assertions.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, "diff"));
}
@Test
@LogMessages(messages = @LogMessage(messageTemplate = KernelLogMessageConstant.MD5_IS_NOT_FIPS_COMPLIANT,
ignore = true))
public void readerWithoutEncryptionWriterStandardEncryption() throws IOException, InterruptedException {
// Test result here is wrong. Note that createEncryptedDocument adds EncryptionConstants.EMBEDDED_FILES_ONLY.
String outFileName = destinationFolder + "readerWithoutEncryptionWriterStandardEncryption.pdf";
String cmpFileName = sourceFolder + "cmp_readerWithoutEncryptionWriterStandardEncryption.pdf";
PdfReader reader = new PdfReader(sourceFolder + "pdfWithUnencryptedAttachmentAnnotations.pdf");
PdfDocument pdfDocument = createEncryptedDocument(reader, EncryptionConstants.STANDARD_ENCRYPTION_128, outFileName);
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(
pdfDocument, "file".getBytes(), "description", "file.txt", null, null, null);
pdfDocument.addFileAttachment("new attachment", fs);
pdfDocument.close();
Assertions.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, "diff", "password".getBytes(), "password".getBytes()));
}
private PdfDocument createEncryptedDocument(int encryptionAlgorithm, String outFileName) throws IOException {
PdfWriter writer = CompareTool.createTestPdfWriter(outFileName,
new WriterProperties().setStandardEncryption(
"password".getBytes(), "password".getBytes(), 0, encryptionAlgorithm | EncryptionConstants.EMBEDDED_FILES_ONLY));
// Setting compression level to zero doesn't affect the encryption at any level.
// We do it to simplify observation of the resultant PDF.
writer.setCompressionLevel(0);
return new PdfDocument(writer);
}
private PdfDocument createEncryptedDocument(PdfReader reader, int encryptionAlgorithm, String outFileName) throws IOException {
PdfWriter writer = CompareTool.createTestPdfWriter(outFileName,
new WriterProperties().setStandardEncryption(
"password".getBytes(), "password".getBytes(), 0, encryptionAlgorithm | EncryptionConstants.EMBEDDED_FILES_ONLY));
// Setting compression level to zero doesn't affect the encryption at any level.
// We do it to simplify observation of the resultant PDF.
writer.setCompressionLevel(0);
return new PdfDocument(reader, writer);
}
}