/* DigiDoc4J library * * This software is released under either the GNU Library General Public * License (see LICENSE.LGPL). * * Note that the only valid version of the LGPL license as far as this * project is concerned is the original GNU Library General Public License * Version 2.1, February 1999 */ package org.digidoc4j.impl.bdoc; import static org.digidoc4j.ContainerBuilder.BDOC_CONTAINER_TYPE; import static org.digidoc4j.DigestAlgorithm.SHA1; import static org.digidoc4j.DigestAlgorithm.SHA224; import static org.digidoc4j.DigestAlgorithm.SHA256; import static org.digidoc4j.SignatureProfile.B_BES; import static org.digidoc4j.SignatureProfile.LT; import static org.digidoc4j.SignatureProfile.LTA; import static org.digidoc4j.SignatureProfile.LT_TM; import static org.digidoc4j.testutils.TestDataBuilder.PKCS12_SIGNER; import static org.digidoc4j.testutils.TestDataBuilder.createEmptyBDocContainer; import static org.digidoc4j.testutils.TestDataBuilder.signContainer; import static org.digidoc4j.testutils.TestSigningHelper.getSigningCert; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.List; import java.util.zip.ZipFile; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.digidoc4j.Configuration; import org.digidoc4j.Container; import org.digidoc4j.ContainerBuilder; import org.digidoc4j.ContainerOpener; import org.digidoc4j.DataFile; import org.digidoc4j.DataToSign; import org.digidoc4j.DigestAlgorithm; import org.digidoc4j.EncryptionAlgorithm; import org.digidoc4j.Signature; import org.digidoc4j.SignatureBuilder; import org.digidoc4j.SignatureProfile; import org.digidoc4j.ValidationResult; import org.digidoc4j.exceptions.DigiDoc4JException; import org.digidoc4j.exceptions.DuplicateDataFileException; import org.digidoc4j.exceptions.InvalidSignatureException; import org.digidoc4j.exceptions.OCSPRequestFailedException; import org.digidoc4j.impl.DigiDoc4JTestHelper; import org.digidoc4j.signers.PKCS12SignatureToken; import org.digidoc4j.testutils.TestDataBuilder; import org.digidoc4j.testutils.TestSigningHelper; import org.digidoc4j.utils.Helper; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import eu.europa.esig.dss.DSSDocument; import eu.europa.esig.dss.xades.DSSXMLUtils; import eu.europa.esig.dss.xades.XPathQueryHolder; import eu.europa.esig.dss.xades.validation.XAdESSignature; public class BDocContainerTest extends DigiDoc4JTestHelper { @Rule public TemporaryFolder testFolder = new TemporaryFolder(); @AfterClass public static void deleteTemporaryFiles() { try { DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(".")); for (Path item : directoryStream) { String fileName = item.getFileName().toString(); if (fileName.endsWith("bdoc") && fileName.startsWith("test")) Files.deleteIfExists(item); } } catch (IOException e) { e.printStackTrace(); } } @Test public void testSetDigestAlgorithmToSHA256() throws Exception { assertSettingDigestAlgorithm("http://www.w3.org/2001/04/xmlenc#sha256", SHA256); } @Test public void testSetDigestAlgorithmToSHA1() throws Exception { assertSettingDigestAlgorithm("http://www.w3.org/2000/09/xmldsig#sha1", SHA1); } @Test public void testSetDigestAlgorithmToSHA224() throws Exception { assertSettingDigestAlgorithm("http://www.w3.org/2001/04/xmldsig-more#sha224", SHA224); } private void assertSettingDigestAlgorithm(String expectedDigestAlgorithm, DigestAlgorithm actualDigestAlgorithm) throws IOException { Container container = TestDataBuilder.createContainerWithFile(testFolder); BDocSignature signature = (BDocSignature)SignatureBuilder. aSignature(container). withSignatureDigestAlgorithm(actualDigestAlgorithm). withSignatureToken(PKCS12_SIGNER). invokeSigning(); container.addSignature(signature); assertEquals(expectedDigestAlgorithm, signature.getSignatureDigestAlgorithm().getXmlId()); } @Test public void testDefaultDigestAlgorithm() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder); BDocSignature signature = (BDocSignature)SignatureBuilder. aSignature(container). withSignatureToken(PKCS12_SIGNER). invokeSigning(); container.addSignature(signature); assertEquals("http://www.w3.org/2001/04/xmlenc#sha256", signature.getSignatureDigestAlgorithm().getXmlId()); } @Test public void testOpenBDocDocument() throws Exception { Container container = open("testFiles/one_signature.bdoc"); container.validate(); } @Test public void testOpenBDocDocumentWithTwoSignatures() throws Exception { Container container = open("testFiles/two_signatures.bdoc"); container.validate(); } @Test(expected = DigiDoc4JException.class) public void testAddDataFileWhenFileDoesNotExist() throws Exception { Container container = createContainerWithFile("notExisting.txt", "text/plain"); } @Test(expected = DigiDoc4JException.class) public void testAddDataFileFromInputStreamWithByteArrayConversionFailure() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile(new MockInputStream(), "test.txt", "text/plain"); } @Test(expected = InvalidSignatureException.class) public void testAddRawSignature() throws Exception { Container container = createEmptyBDocContainer(); container.addRawSignature(new byte[]{}); } @Test public void testAddUnknownFileTypeKeepsMimeType() { Container container = createContainerWithFile("testFiles/test.unknown_type", "text/test_type"); signContainer(container); container.save("test_add_unknown_datafile_type.bdoc"); Container open = ContainerOpener.open("test_add_unknown_datafile_type.bdoc"); assertEquals("text/test_type", open.getDataFiles().get(0).getMediaType()); } @Test public void testSaveBDocDocumentWithTwoSignatures() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); signContainer(container); container.save("testTwoSignatures.bdoc"); assertEquals(2, container.getSignatures().size()); assertEquals("530be41bbc597c44570e2b7c13bcfa0c", container.getSignatures().get(0).getSigningCertificate().getSerial()); assertEquals("530be41bbc597c44570e2b7c13bcfa0c", container.getSignatures().get(1).getSigningCertificate().getSerial()); Container openedContainer = open("testTwoSignatures.bdoc"); assertEquals(2, openedContainer.getSignatures().size()); assertEquals("530be41bbc597c44570e2b7c13bcfa0c", openedContainer.getSignatures().get(0).getSigningCertificate().getSerial()); assertEquals("530be41bbc597c44570e2b7c13bcfa0c", openedContainer.getSignatures().get(1).getSigningCertificate().getSerial()); } @Test public void saveContainerWithoutSignatures() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); String path = testFolder.newFile("container.bdoc").getPath(); container.saveAsFile(path); container = open(path); assertEquals(1, container.getDataFiles().size()); assertEquals("test.txt", container.getDataFiles().get(0).getName()); } @Test public void openContainer_withoutSignatures_andAddMoreDataFiles() throws Exception { Container container = open("testFiles/container_without_signatures.bdoc"); assertEquals(1, container.getDataFiles().size()); container.addDataFile("testFiles/test.xml", "text/xml"); container.addDataFile("testFiles/word_file.docx", "application/octet-stream"); assertEquals(3, container.getDataFiles().size()); String path = testFolder.newFile("container.bdoc").getPath(); container.saveAsFile(path); container = open(path); assertEquals(3, container.getDataFiles().size()); } @Test public void openContainerFromStream_withoutSignatures_andAddMoreDataFiles() throws Exception { FileInputStream stream = new FileInputStream("testFiles/container_without_signatures.bdoc"); Container container = open(stream); assertEquals(1, container.getDataFiles().size()); container.addDataFile("testFiles/test.xml", "text/xml"); container.addDataFile("testFiles/word_file.docx", "application/octet-stream"); assertEquals(3, container.getDataFiles().size()); String path = testFolder.newFile("container.bdoc").getPath(); container.saveAsFile(path); stream = new FileInputStream(path); container = open(stream); assertEquals(3, container.getDataFiles().size()); } @Test public void openContainerWithoutSignatures_addDataFileAndSignContainer() throws Exception { Container container = open("testFiles/container_without_signatures.bdoc"); assertEquals(1, container.getDataFiles().size()); container.addDataFile("testFiles/test.xml", "text/xml"); signContainer(container); assertEquals(1, container.getSignatures().size()); assertTrue(container.validate().isValid()); String path = testFolder.newFile("container.bdoc").getPath(); container.saveAsFile(path); container = open(path); assertTrue(container.validate().isValid()); } @Test public void testGetDefaultSignatureParameters() { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); container.save("test.bdoc"); container = open("test.bdoc"); Signature signature = container.getSignature(0); assertNull(signature.getPostalCode()); assertNull(signature.getCity()); assertNull(signature.getStateOrProvince()); assertNull(signature.getCountryName()); assertThat(signature.getSignerRoles(), is(empty())); } @Test public void getSignatureByIndex() { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); signContainer(container); assertEquals("530be41bbc597c44570e2b7c13bcfa0c", container.getSignature(1).getSigningCertificate().getSerial()); } @Test public void notThrowingNPEWhenDOCXFileIsAddedToContainer() { Container container = createContainerWithFile("testFiles/word_file.docx", "text/xml"); signContainer(container); assertEquals(1, container.getSignatures().size()); } @Test public void signPdfDataFile() throws Exception { Container container = createContainerWithFile("testFiles/special-char-files/dds_acrobat.pdf", "application/pdf"); signContainer(container); assertEquals(1, container.getDataFiles().size()); assertEquals(1, container.getSignatures().size()); String containerPath = testFolder.newFile("container.bdoc").getPath(); container.saveAsFile(containerPath); container = open(containerPath); assertEquals(1, container.getDataFiles().size()); assertTrue(container.validate().isValid()); } @Test public void testAddSignaturesToExistingDocument() throws Exception { Container container = open("testFiles/asics_testing_two_signatures.bdoc"); signContainer(container); container.save("testAddMultipleSignatures.bdoc"); assertEquals(3, container.getSignatures().size()); assertEquals("530be41bbc597c44570e2b7c13bcfa0c", container.getSignatures().get(2).getSigningCertificate().getSerial()); Container openedContainer = open("testAddMultipleSignatures.bdoc"); assertEquals(3, openedContainer.getSignatures().size()); assertEquals("530be41bbc597c44570e2b7c13bcfa0c", openedContainer.getSignatures().get(2).getSigningCertificate().getSerial()); ValidationResult validationResult = openedContainer.validate(); assertEquals(0, validationResult.getErrors().size()); } @Test public void testRemoveSignatureWhenOneSignatureExists() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); container.removeSignature(0); container.save("testRemoveSignature.bdoc"); assertEquals(0, container.getSignatures().size()); container = open("testRemoveSignature.bdoc"); assertEquals(0, container.getSignatures().size()); } @Test public void testAddFilesWithSpecialCharactersIntoContainer() throws Exception { Container container = createContainerWithFile("testFiles/special-char-files/dds_dds_JÜRIÖÖ € žŠ päev.txt", "text/plain"); container.addDataFile("testFiles/special-char-files/dds_колючей стерне.docx", "text/plain"); signContainer(container); container.saveAsFile("testWithSpecialCharFiles.bdoc"); assertEquals(0, container.validate().getContainerErrors().size()); } @Test public void testRemoveSignatureWhenTwoSignaturesExist() throws Exception { Container container = open("testFiles/asics_testing_two_signatures.bdoc"); assertEquals(2, container.getSignatures().size()); container.removeSignature(0); container.save("testRemoveSignature.bdoc"); container = open("testRemoveSignature.bdoc"); assertEquals(1, container.getSignatures().size()); } @Test public void testRemoveSignatureWhenThreeSignaturesExist() throws Exception { Container container = open("testFiles/asics_testing_two_signatures.bdoc"); signContainer(container); container.save("testThreeSignatures.bdoc"); container = open("testThreeSignatures.bdoc"); assertEquals(3, container.getSignatures().size()); container.removeSignature(1); container.save("testRemoveSignature.bdoc"); container = open("testRemoveSignature.bdoc"); assertEquals(2, container.getSignatures().size()); } @Test public void removeNewlyAddedSignatureFromExistingContainer() throws Exception { Container container = open("testFiles/asics_testing_two_signatures.bdoc"); assertEquals(2, container.getSignatures().size()); signContainer(container); assertEquals(3, container.getSignatures().size()); container.removeSignature(container.getSignatures().get(0)); assertEquals(2, container.getSignatures().size()); } @Test public void removeSignatureFromExistingContainer() throws Exception { Container container = open("testFiles/asics_testing_two_signatures.bdoc"); assertEquals(2, container.getSignatures().size()); container.removeSignature(container.getSignatures().get(0)); assertEquals(1, container.getSignatures().size()); String path = testFolder.newFile("container.bdoc").getPath(); container.saveAsFile(path); container = open(path); assertEquals(1, container.getSignatures().size()); } @Test public void testSaveDocumentWithOneSignature() throws Exception { createSignedBDocDocument("testSaveBDocDocumentWithOneSignature.bdoc"); assertTrue(Files.exists(Paths.get("testSaveBDocDocumentWithOneSignature.bdoc"))); } @Test(expected = DigiDoc4JException.class) public void testRemoveDataFileAfterSigning() throws Exception { createSignedBDocDocument("testRemoveDataFile.bdoc"); Container container = open("testRemoveDataFile.bdoc"); assertEquals("test.txt", container.getDataFiles().get(0).getName()); assertEquals(1, container.getDataFiles().size()); container.removeDataFile("test.txt"); assertEquals(0, container.getDataFiles().size()); } @Test public void testRemoveDataFile() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); assertEquals("test.txt", container.getDataFiles().get(0).getName()); assertEquals(1, container.getDataFiles().size()); container.removeDataFile("test.txt"); assertEquals(0, container.getDataFiles().size()); } @Test(expected = DigiDoc4JException.class) public void testAddDataFileAfterSigning() throws Exception { createSignedBDocDocument("testAddDataFile.bdoc"); Container container = open("testAddDataFile.bdoc"); container.addDataFile("testFiles/test.txt", "text/plain"); } @Test(expected = DigiDoc4JException.class) public void testRemovingNonExistingFile() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); container.removeDataFile("test1.txt"); } @Test(expected = DuplicateDataFileException.class) public void testAddingSameFileSeveralTimes() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); container.addDataFile("testFiles/test.txt", "text/plain"); } @Test(expected = DuplicateDataFileException.class) public void testAddingSamePreCreatedFileSeveralTimes() { Container container = createEmptyBDocContainer(); DataFile dataFile = new DataFile("Hello world!".getBytes(), "test-file.txt", "text/plain"); container.addDataFile(dataFile); container.addDataFile(dataFile); } @Test public void testAddingDifferentPreCreatedFiles() { Container container = createEmptyBDocContainer(); container.addDataFile(new DataFile("Hello world!".getBytes(), "hello.txt", "text/plain")); container.addDataFile(new DataFile("Goodbye world!".getBytes(), "goodbye.txt", "text/plain")); } @Test(expected = DuplicateDataFileException.class) public void testAddingSameFileSeveralTimesViaInputStream() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile(new ByteArrayInputStream("test".getBytes()), "testFiles/test.txt", "text/plain"); container.addDataFile(new ByteArrayInputStream("test".getBytes()), "testFiles/test.txt", "text/plain"); } @Test public void testAddDateFileViaInputStream() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile(new ByteArrayInputStream("test".getBytes()), "testFiles/test.txt", "text/plain"); signContainer(container); assertTrue(container.validate().isValid()); } @Test(expected = DuplicateDataFileException.class) public void testAddingSameFileInDifferentContainerSeveralTimes() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); container.addDataFile("testFiles/sub/test.txt", "text/plain"); signContainer(container); container.save("testAddSameFile.bdoc"); } @Test(expected = DigiDoc4JException.class) public void testAddingNotExistingFile() throws Exception { Container container = createContainerWithFile("notExistingFile.txt", "text/plain"); } @Test public void testAddFileAsStream() throws Exception { Container container = createEmptyBDocContainer(); ByteArrayInputStream stream = new ByteArrayInputStream("tere, tere".getBytes()); container.addDataFile(stream, "test1.txt", "text/plain"); signContainer(container); container.save("testAddFileAsStream.bdoc"); Container containerToTest = open("testAddFileAsStream.bdoc"); assertEquals("test1.txt", containerToTest.getDataFiles().get(0).getName()); } @Test public void setsSignatureId() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); Signature signature1 = SignatureBuilder. aSignature(container). withSignatureId("SIGNATURE-1"). withSignatureToken(PKCS12_SIGNER). invokeSigning(); container.addSignature(signature1); Signature signature2 = SignatureBuilder. aSignature(container). withSignatureId("SIGNATURE-2"). withSignatureToken(PKCS12_SIGNER). invokeSigning(); container.addSignature(signature2); container.saveAsFile("setsSignatureId.bdoc"); container = open("setsSignatureId.bdoc"); assertEquals("SIGNATURE-1", container.getSignature(0).getId()); assertEquals("SIGNATURE-2", container.getSignature(1).getId()); ZipFile zip = new ZipFile("setsSignatureId.bdoc"); assertNotNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); } @Test public void setsDefaultSignatureId() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); signContainer(container); container.save("testSetsDefaultSignatureId.bdoc"); container = open("testSetsDefaultSignatureId.bdoc"); String signature1Id = container.getSignatures().get(0).getId(); String signature2Id = container.getSignatures().get(1).getId(); assertFalse(StringUtils.equals(signature1Id, signature2Id)); assertTrue(signature1Id.startsWith("id-")); assertTrue(signature2Id.startsWith("id-")); ZipFile zip = new ZipFile("testSetsDefaultSignatureId.bdoc"); assertNotNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); } @Test public void getDataFileByIndex() { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); assertEquals("test.txt", container.getDataFile(0).getName()); } @Test(expected = DigiDoc4JException.class) public void openNonExistingFileThrowsError() { open("non-existing.bdoc"); } @Test(expected = DigiDoc4JException.class) public void openClosedStreamThrowsException() throws IOException { FileInputStream stream = new FileInputStream(new File("testFiles/test.txt")); stream.close(); open(stream, false); } @Test public void testLargeFileSigning() throws Exception { BDocContainer container = (BDocContainer) ContainerBuilder. aContainer(BDOC_CONTAINER_TYPE). withConfiguration(new Configuration(Configuration.Mode.TEST)). build(); container.getConfiguration().enableBigFilesSupport(10); String path = createLargeFile((container.getConfiguration().getMaxDataFileCachedInBytes()) + 100); container.addDataFile(path, "text/plain"); signContainer(container); } @Test public void openLargeFileFromStream() throws FileNotFoundException { BDocContainer container = (BDocContainer) ContainerBuilder. aContainer(BDOC_CONTAINER_TYPE). withConfiguration(new Configuration(Configuration.Mode.TEST)). build(); container.getConfiguration().enableBigFilesSupport(0); String path = createLargeFile((container.getConfiguration().getMaxDataFileCachedInBytes()) + 100); container.addDataFile(path, "text/plain"); signContainer(container); container.save("test-large-file.bdoc"); File file = new File("test-large-file.bdoc"); FileInputStream fileInputStream = new FileInputStream(file); open(fileInputStream, true); IOUtils.closeQuietly(fileInputStream); assertEquals(1, container.getSignatures().size()); } @Test public void openAddFileFromStream() throws IOException { BDocContainer container = (BDocContainer) ContainerBuilder. aContainer(BDOC_CONTAINER_TYPE). withConfiguration(new Configuration(Configuration.Mode.TEST)). build(); container.getConfiguration().enableBigFilesSupport(0); String path = createLargeFile((container.getConfiguration().getMaxDataFileCachedInBytes()) + 100); try (FileInputStream stream = new FileInputStream(new File(path))) { container.addDataFile(stream, "fileName", "text/plain"); signContainer(container); container.save("test-large-file.bdoc"); File file = new File("test-large-file.bdoc"); FileInputStream fileInputStream = new FileInputStream(file); open(fileInputStream, true); IOUtils.closeQuietly(fileInputStream); } assertEquals(1, container.getSignatures().size()); } private String createLargeFile(long size) { String fileName = "test_large_file.bdoc"; try { RandomAccessFile largeFile = new RandomAccessFile(fileName, "rw"); largeFile.setLength(size);//todo create large file correctly } catch (Exception e) { e.printStackTrace(); } return fileName; } @Test public void testGetDocumentType() throws Exception { createSignedBDocDocument("testGetDocumentType.bdoc"); Container container = open("testGetDocumentType.bdoc"); assertEquals(Container.DocumentType.BDOC, container.getDocumentType()); } @Test public void testAddTwoFilesAsStream() throws Exception { Container container = createEmptyBDocContainer(); ByteArrayInputStream stream = new ByteArrayInputStream("tere, tere".getBytes()); container.addDataFile(stream, "test1.txt", "text/plain"); container.addDataFile(stream, "test2.txt", "text/plain"); } @Test public void testAddTwoFilesAsFileWithoutOCSP() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile("testFiles/test.txt", "text/plain"); container.addDataFile("testFiles/test.xml", "text/xml"); signContainer(container, B_BES); container.save("testTwoFilesSigned.bdoc"); container = open("testTwoFilesSigned.bdoc"); assertEquals(2, container.getDataFiles().size()); } @Test public void testGetFileNameAndID() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); container.addDataFile("testFiles/test.xml", "text/xml"); signContainer(container); container.save("testTwoFilesSigned.bdoc"); container = open("testTwoFilesSigned.bdoc"); assertEquals("test.txt", container.getDataFile(0).getName()); assertEquals("test.xml", container.getDataFile(1).getName()); assertEquals("test.txt", container.getDataFile(0).getId()); assertEquals("test.xml", container.getDataFile(1).getId()); } @Test public void testAddTwoFilesAsFileWithOCSP() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); container.addDataFile("testFiles/test.xml", "text/xml"); signContainer(container); container.save("testTwoFilesSigned.bdoc"); container = open("testTwoFilesSigned.bdoc"); assertEquals(2, container.getDataFiles().size()); } @Test public void saveToStream() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile(new ByteArrayInputStream(new byte[]{0x42}), "test_bytes.txt", "text/plain"); signContainer(container); File expectedContainerAsFile = new File("testSaveToStreamTest.bdoc"); OutputStream out = new FileOutputStream(expectedContainerAsFile); container.save(out); assertTrue(Files.exists(expectedContainerAsFile.toPath())); Container containerToTest = open(expectedContainerAsFile.getName()); assertArrayEquals(new byte[]{0x42}, containerToTest.getDataFiles().get(0).getBytes()); } @Test public void saveExistingContainerToStream() throws Exception { Container container = open("testFiles/asics_testing_two_signatures.bdoc"); signContainer(container); assertEquals(3, container.getSignatures().size()); InputStream inputStream = container.saveAsStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); IOUtils.copy(inputStream, outputStream); ByteArrayInputStream savedContainerStream = new ByteArrayInputStream(outputStream.toByteArray()); container = open(savedContainerStream); assertEquals(3, container.getSignatures().size()); assertEquals(1, container.getDataFiles().size()); } @Test(expected = DigiDoc4JException.class) public void saveToStreamThrowsException() throws IOException { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); File expectedContainerAsFile = new File("testSaveToStreamTest.bdoc"); OutputStream out = new FileOutputStream(expectedContainerAsFile); out.close(); container.save(out); } @Test public void saveExistingContainer() throws Exception { Container container = open("testFiles/asics_testing_two_signatures.bdoc"); String containerPath = testFolder.newFile("test-container.asice").getPath(); container.saveAsFile(containerPath); Container savedContainer = open(containerPath); assertTrue(savedContainer.validate().isValid()); assertEquals(1, savedContainer.getDataFiles().size()); assertEquals(2, savedContainer.getSignatures().size()); ZipFile zip = new ZipFile(containerPath); assertNotNull(zip.getEntry("mimetype")); assertNotNull(zip.getEntry("test.txt")); assertNotNull(zip.getEntry("META-INF/manifest.xml")); assertNotNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); } @Test public void containerIsLT() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container, LT); container.saveAsFile("testLT.bdoc"); container = open("testLT.bdoc"); assertEquals(1, container.getSignatures().size()); assertNotNull(container.getSignature(0).getOCSPCertificate()); } @Test public void verifySignatureProfileIsTS() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container, LT); container.saveAsFile("testAddConfirmation.bdoc"); assertEquals(1, container.getSignatures().size()); assertNotNull(container.getSignature(0).getOCSPCertificate()); } @Test(expected = DigiDoc4JException.class) public void signWithoutDataFile() throws Exception { Container container = createEmptyBDocContainer(); signContainer(container); } @Test public void nonStandardMimeType() { Container container = ContainerBuilder.aContainer(BDOC_CONTAINER_TYPE).build(); container.addDataFile("testFiles/test.txt", "text/newtype"); signContainer(container); container.save("testNonStandardMimeType.bdoc"); container = ContainerOpener.open("testNonStandardMimeType.bdoc"); ValidationResult result = container.validate(); assertEquals(0, result.getErrors().size()); assertEquals("text/newtype", container.getDataFile(0).getMediaType()); } @Test public void getVersion() { Container container = createEmptyBDocContainer(); assertNull(container.getVersion()); } @Test public void twoStepSigning() throws IOException { Container container = createEmptyBDocContainer(); container.addDataFile("testFiles/test.txt", "text/plain"); X509Certificate signerCert = getSigningCert(); DataToSign dataToSign = SignatureBuilder. aSignature(container). withSigningCertificate(signerCert). buildDataToSign(); byte[] signatureValue = TestSigningHelper.sign(dataToSign.getDigestToSign(), dataToSign.getDigestAlgorithm()); Signature signature = dataToSign.finalize(signatureValue); container.addSignature(signature); container.saveAsFile("test.bdoc"); container = ContainerOpener.open("test.bdoc"); ValidationResult validate = container.validate(); assertTrue(validate.isValid()); assertEquals(1, container.getSignatures().size()); Signature resultSignature = container.getSignature(0); assertEquals("http://www.w3.org/2001/04/xmlenc#sha256", resultSignature.getSignatureMethod()); assertThat(resultSignature.getSignerRoles(), is(empty())); assertNull(resultSignature.getCity()); assertTrue(StringUtils.isNotBlank(resultSignature.getId())); assertNotNull(resultSignature.getOCSPCertificate()); assertNotNull(resultSignature.getSigningCertificate()); assertNotNull(resultSignature.getAdESSignature().length); assertEquals(LT, resultSignature.getProfile()); assertNotNull(resultSignature.getTimeStampTokenCertificate()); List<DataFile> dataFiles = container.getDataFiles(); assertEquals(1, dataFiles.size()); DataFile dataFile = dataFiles.get(0); assertEquals("test.txt", dataFile.getName()); dataFile.calculateDigest(DigestAlgorithm.SHA384); assertEquals("text/plain", dataFile.getMediaType()); assertEquals(new String(Files.readAllBytes(Paths.get("testFiles/test.txt"))), new String(dataFile.getBytes())); assertEquals(15, dataFile.getFileSize()); assertEquals("test.txt", dataFile.getId()); } @Test public void twoStepSigningVerifySignatureParameters() { Container container = ContainerBuilder.aContainer(BDOC_CONTAINER_TYPE).build(); container.addDataFile("testFiles/test.txt", "text/plain"); X509Certificate signerCert = getSigningCert(); DataToSign dataToSign = SignatureBuilder. aSignature(container). withSignatureDigestAlgorithm(DigestAlgorithm.SHA512). withSigningCertificate(signerCert). withSignatureId("S99"). withRoles("manager", "employee"). withCity("city"). withStateOrProvince("state"). withPostalCode("postalCode"). withCountry("country"). buildDataToSign(); byte[] signatureValue = TestSigningHelper.sign(dataToSign.getDigestToSign(), dataToSign.getDigestAlgorithm()); Signature signature = dataToSign.finalize(signatureValue); container.addSignature(signature); container.saveAsFile("test.bdoc"); container = ContainerOpener.open("test.bdoc"); assertEquals(1, container.getSignatures().size()); Signature resultSignature = container.getSignature(0); assertEquals("http://www.w3.org/2001/04/xmlenc#sha512", resultSignature.getSignatureMethod()); assertEquals("employee", resultSignature.getSignerRoles().get(1)); assertEquals("city", resultSignature.getCity()); assertEquals("S99", resultSignature.getId()); } @Test public void testContainerCreationAsTSA() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile("testFiles/test.txt", "text/plain"); signContainer(container, LTA); assertNotNull(container.getSignature(0).getOCSPCertificate()); } private Container createSignedBDocDocument(String fileName) { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); container.save(fileName); return container; } private class MockInputStream extends InputStream { public MockInputStream() { } @Override public int read() throws IOException { return 0; } @Override public int read(@SuppressWarnings("NullableProblems") byte b[], int off, int len) throws IOException { throw new IOException(); } @Override public void close() throws IOException { throw new IOException(); } } static X509Certificate getSignerCert(String certFile) { try { KeyStore keyStore = KeyStore.getInstance("PKCS12"); try (FileInputStream stream = new FileInputStream(certFile)) { keyStore.load(stream, "test".toCharArray()); } return (X509Certificate) keyStore.getCertificate("1"); } catch (Exception e) { throw new DigiDoc4JException("Loading signer cert failed"); } } @Test public void testBDocTM() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile("testFiles/test.txt", "text/plain"); signContainer(container, LT_TM); assertTrue(container.validate().isValid()); } @Test public void testBDocTS() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile("testFiles/test.txt", "text/plain"); signContainer(container, LT); assertTrue(container.validate().isValid()); } @Test public void containerWithBESProfileHasNoValidationErrors() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile("testFiles/test.txt", "text/plain"); signContainer(container, B_BES); assertEquals(B_BES, container.getSignatures().get(0).getProfile()); assertNull(container.getSignature(0).getOCSPCertificate()); ValidationResult result = container.validate(); assertFalse(result.isValid()); } @Test public void signWithECCCertificate() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); Signature signature = SignatureBuilder. aSignature(container). withSignatureToken(new PKCS12SignatureToken("testFiles/ec-digiid.p12", "inno".toCharArray())). withEncryptionAlgorithm(EncryptionAlgorithm.ECDSA). invokeSigning(); container.addSignature(signature); assertEquals(1, container.getSignatures().size()); assertTrue(container.validate().isValid()); } @Test public void zipFileComment() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); container.save("testZipFileComment.bdoc"); String expectedComment = Helper.createBDocUserAgent(SignatureProfile.LT_TM); ZipFile zipFile = new ZipFile("testZipFileComment.bdoc"); assertEquals(expectedComment, zipFile.getEntry("mimetype").getComment()); assertEquals(expectedComment, zipFile.getEntry("META-INF/manifest.xml").getComment()); assertEquals(expectedComment, zipFile.getEntry("META-INF/manifest.xml").getComment()); assertEquals(expectedComment, zipFile.getEntry("META-INF/signatures0.xml").getComment()); assertEquals(expectedComment, zipFile.getEntry("test.txt").getComment()); } @Test public void signingMoreThanTwoFiles() throws Exception { Container container = createContainerWithFile("testFiles/special-char-files/dds_dds_JÜRIÖÖ € žŠ päev.txt", "text/plain"); container.addDataFile("testFiles/special-char-files/dds_колючей стерне.docx", "text/plain"); container.addDataFile("testFiles/special-char-files/dds_pakitud.zip", "text/plain"); container.addDataFile("testFiles/special-char-files/dds_SK.jpg", "text/plain"); container.addDataFile("testFiles/special-char-files/dds_acrobat.pdf", "text/plain"); signContainer(container); BDocSignature signature = (BDocSignature)container.getSignature(0); assertSignatureContains(signature, "dds_dds_JÜRIÖÖ € žŠ päev.txt"); assertSignatureContains(signature, "dds_колючей стерне.docx"); assertSignatureContains(signature, "dds_pakitud.zip"); assertSignatureContains(signature, "dds_SK.jpg"); assertSignatureContains(signature, "dds_acrobat.pdf"); } @Test public void signatureFileNamesShouldBeInSequence() throws Exception { Container container = createContainerWithFile("testFiles/test.txt", "text/plain"); signContainer(container); signContainer(container); signContainer(container); String containerPath = testFolder.newFile().getPath(); container.saveAsFile(containerPath); ZipFile zip = new ZipFile(containerPath); assertNotNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); assertNotNull(zip.getEntry("META-INF/signatures2.xml")); } @Test public void whenSigningExistingContainer_withTwoSignatures_shouldCreateSignatureFileName_signatures2() throws Exception { ZipFile zip = new ZipFile("testFiles/asics_testing_two_signatures.bdoc"); assertNotNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); Container container = open("testFiles/asics_testing_two_signatures.bdoc"); signContainer(container); String containerPath = testFolder.newFile().getPath(); container.saveAsFile(containerPath); zip = new ZipFile(containerPath); assertNotNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); assertNotNull(zip.getEntry("META-INF/signatures2.xml")); } @Test public void whenSigningExistingContainer_with_signatures1_xml_shouldCreateSignatureFileName_signatures2() throws Exception { ZipFile zip = new ZipFile("testFiles/DigiDocService_spec_est.pdf-TM-j.bdoc"); assertNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); Container container = open("testFiles/DigiDocService_spec_est.pdf-TM-j.bdoc"); signContainer(container); String containerPath = testFolder.newFile().getPath(); container.saveAsFile(containerPath); zip = new ZipFile(containerPath); assertNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); assertNotNull(zip.getEntry("META-INF/signatures2.xml")); } @Test public void whenSigningContainer_withSignatureNameContainingNonNumericCharacters_shouldCreateSignatureFileName_inSequence() throws Exception { ZipFile zip = new ZipFile("testFiles/valid-containers/valid-bdoc-ts-signature-file-name-with-non-numeric-characters.asice"); assertNotNull(zip.getEntry("META-INF/l77Tsignaturesn00B.xml")); assertNull(zip.getEntry("META-INF/signatures0.xml")); assertNull(zip.getEntry("META-INF/signatures1.xml")); Container container = open("testFiles/valid-containers/valid-bdoc-ts-signature-file-name-with-non-numeric-characters.asice"); signContainer(container, SignatureProfile.LT); signContainer(container, SignatureProfile.LT); String containerPath = testFolder.newFile("test-container.asice").getPath(); container.saveAsFile(containerPath); zip = new ZipFile(containerPath); assertNotNull(zip.getEntry("META-INF/l77Tsignaturesn00B.xml")); assertNotNull(zip.getEntry("META-INF/signatures0.xml")); assertNotNull(zip.getEntry("META-INF/signatures1.xml")); } @Test(expected = DuplicateDataFileException.class) public void whenOpeningContainer_withTwoDataFilesWithSameName_andWithSingleReferenceInManifest_shouldThrowException() { Container container = ContainerBuilder.aContainer() .fromExistingFile("testFiles/KS-19_IB-3721_bdoc21-TM-2fil-samename-1sig3.bdoc") .withConfiguration(new Configuration(Configuration.Mode.TEST)) .build(); } @Test(expected = OCSPRequestFailedException.class) public void signingContainer_withFailedOcspResponse_shouldThrowException() throws Exception { Configuration configuration = new Configuration(Configuration.Mode.TEST); configuration.setSignOCSPRequests(true); configuration.setOCSPAccessCertificateFileName("testFiles/signout.p12"); configuration.setOCSPAccessCertificatePassword("test".toCharArray()); Container container = ContainerBuilder. aContainer(BDOC_CONTAINER_TYPE). withConfiguration(configuration). withDataFile("testFiles/test.txt", "text/plain"). build(); signContainer(container, LT_TM); } /** * This is necessary for jDigidoc compatibility. This requirement not in BDoc specification */ @Test public void bdocTM_OcspResponderCert_shouldContainResponderCertIdAttribute() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile("testFiles/test.txt", "text/plain"); BDocSignature signature = (BDocSignature) signContainer(container, LT_TM); XAdESSignature xAdESSignature = signature.getOrigin().getDssSignature(); assertEquals(1, countOcspResponderCertificates(xAdESSignature)); } @Test public void savingContainerWithoutSignatures_shouldNotThrowException() throws Exception { Container container = createEmptyBDocContainer(); container.addDataFile("testFiles/test.txt", "text/plain"); assertTrue(container.getSignatures().isEmpty()); assertEquals(1, container.getDataFiles().size()); assertTrue(container.validate().isValid()); String containerPath = testFolder.newFile().getPath(); container.saveAsFile(containerPath); Container savedContainer = open(containerPath); assertTrue(savedContainer.getSignatures().isEmpty()); assertEquals(1, container.getDataFiles().size()); byte[] expectedDataFileBytes = FileUtils.readFileToByteArray(new File("testFiles/test.txt")); byte[] actualDataFileBytes = savedContainer.getDataFiles().get(0).getBytes(); Assert.assertArrayEquals(expectedDataFileBytes, actualDataFileBytes); } @Test public void openBDoc_withoutCAConfiguration_shouldNotThrowException() throws Exception { Configuration configuration = new Configuration(Configuration.Mode.TEST); configuration.loadConfiguration("testFiles/digidoc_test_conf_no_ca.yaml"); ExistingBDocContainer container = new ExistingBDocContainer("testFiles/valid-containers/valid-bdoc-tm.bdoc", configuration); assertTrue(container.validate().isValid()); } private void assertSignatureContains(BDocSignature signature, String name) { assertNotNull(findSignedFile(signature, name)); } private DSSDocument findSignedFile(BDocSignature signature, String name) { List<DSSDocument> signedFiles = signature.getOrigin().getDssSignature().getDetachedContents(); for (DSSDocument signedFile : signedFiles) { if(name.equals(signedFile.getName())) { return signedFile; } } return null; } private Container open(String path) { Container container = ContainerBuilder. aContainer(BDOC_CONTAINER_TYPE). fromExistingFile(path). build(); return container; } private Container open(String path, Configuration configuration) { Container container = ContainerBuilder. aContainer(BDOC_CONTAINER_TYPE). withConfiguration(configuration). fromExistingFile(path). build(); return container; } private Container open(InputStream fileInputStream, boolean actAsBigFilesSupportEnabled) { return open(fileInputStream); } private Container open(InputStream fileInputStream) { Container container = ContainerBuilder. aContainer(BDOC_CONTAINER_TYPE). fromStream(fileInputStream). build(); return container; } private Container createContainerWithFile(String path, String mimeType) { Container container = ContainerBuilder. aContainer(BDOC_CONTAINER_TYPE). withDataFile(path, mimeType). build(); return container; } private int countOcspResponderCertificates(XAdESSignature xAdESSignature) { XPathQueryHolder xPathQueryHolder = xAdESSignature.getXPathQueryHolder(); String xPath = xPathQueryHolder.XPATH_CERTIFICATE_VALUES; Element certificateValues = DSSXMLUtils.getElement(xAdESSignature.getSignatureElement(), xPath); return countResponderCertIdInsCertificateValues(certificateValues); } private int countResponderCertIdInsCertificateValues(Element certificateValues) { int responderCertCount = 0; NodeList certificates = certificateValues.getChildNodes(); for(int i = 0;i < certificates.getLength(); i++) { Node cert = certificates.item(i); Node certId = cert.getAttributes().getNamedItem("Id"); if(certId != null) { String idValue = certId.getNodeValue(); if(StringUtils.containsIgnoreCase(idValue, "RESPONDER_CERT")) { responderCertCount++; } } } return responderCertCount; } }