org.digidoc4j.impl.BDocContainerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.digidoc4j.impl.BDocContainerTest.java

Source

/* 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;

import eu.europa.ec.markt.dss.DSSUtils;
import eu.europa.ec.markt.dss.exception.DSSException;
import eu.europa.ec.markt.dss.signature.DSSDocument;
import eu.europa.ec.markt.dss.signature.asic.ASiCService;
import eu.europa.ec.markt.dss.signature.token.Constants;
import eu.europa.ec.markt.dss.validation102853.CommonCertificateVerifier;
import eu.europa.ec.markt.dss.validation102853.rules.MessageTag;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.digidoc4j.*;
import org.digidoc4j.exceptions.*;
import org.digidoc4j.signers.ExternalSigner;
import org.digidoc4j.signers.PKCS12Signer;
import org.digidoc4j.utils.Helper;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;

import java.io.*;
import java.net.URI;
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.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipFile;

import static java.util.Arrays.asList;
import static org.digidoc4j.Container.*;
import static org.digidoc4j.Container.SignatureProfile.*;
import static org.digidoc4j.DigestAlgorithm.SHA1;
import static org.digidoc4j.DigestAlgorithm.SHA256;
import static org.digidoc4j.EncryptionAlgorithm.ECDSA;
import static org.digidoc4j.utils.Helper.deserializer;
import static org.digidoc4j.utils.Helper.serialize;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

public class BDocContainerTest extends DigiDoc4JTestHelper {

    private PKCS12Signer PKCS12_SIGNER;

    @Before
    public void setUp() throws Exception {
        PKCS12_SIGNER = new PKCS12Signer("testFiles/signout.p12", "test".toCharArray());
    }

    @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 {
        BDocContainer container = new BDocContainer();
        SignatureParameters signatureParameters = new SignatureParameters();
        signatureParameters.setDigestAlgorithm(SHA256);
        container.setSignatureParameters(signatureParameters);
        assertEquals("http://www.w3.org/2001/04/xmlenc#sha256", container.getDigestAlgorithm().toString());
    }

    @Test
    public void testSetDigestAlgorithmToSHA1() throws Exception {
        BDocContainer container = new BDocContainer();
        SignatureParameters signatureParameters = new SignatureParameters();
        signatureParameters.setDigestAlgorithm(SHA1);
        container.setSignatureParameters(signatureParameters);
        assertEquals("http://www.w3.org/2000/09/xmldsig#sha1", container.getDigestAlgorithm().toString());
    }

    @Test
    public void testSetDigestAlgorithmToNotImplementedDigest() throws Exception {
        BDocContainer container = new BDocContainer();
        SignatureParameters signatureParameters = new SignatureParameters();
        signatureParameters.setDigestAlgorithm(SHA256);
        container.setSignatureParameters(signatureParameters);
        assertEquals("http://www.w3.org/2001/04/xmlenc#sha256", container.getDigestAlgorithm().toString());
    }

    @Test
    public void testDefaultDigestAlgorithm() throws Exception {
        BDocContainer container = new BDocContainer();
        assertEquals("http://www.w3.org/2001/04/xmlenc#sha256", container.getDigestAlgorithm().toString());
    }

    @Test
    public void testOpenBDocDocument() throws Exception {
        BDocContainer container = new BDocContainer("testFiles/one_signature.bdoc");
        container.verify();
    }

    @Test
    public void testOpenBDocDocumentWithTwoSignatures() throws Exception {
        BDocContainer container = new BDocContainer("testFiles/two_signatures.bdoc");
        container.verify();
    }

    @Test(expected = DigiDoc4JException.class)
    public void testAddDataFileWhenFileDoesNotExist() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("notExisting.txt", "text/plain");
    }

    @Test(expected = DigiDoc4JException.class)
    public void testAddDataFileFromInputStreamWithByteArrayConversionFailure() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile(new MockInputStream(), "test.txt", "text/plain");
    }

    @Test(expected = DigiDoc4JException.class)
    public void testAddRawSignature() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addRawSignature(new byte[] {});
    }

    @Test(expected = NotYetImplementedException.class)
    public void testAddRawSignatureFromInputStream() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.addRawSignature(new ByteArrayInputStream(Signatures.XADES_SIGNATURE.getBytes()));
        container.save("test_add_raw_signature.bdoc");

        Container openedContainer = open("test_add_raw_signature.bdoc");
        assertEquals(1, openedContainer.getSignatures().size());
    }

    @Test
    public void testAddUnknownFileTypeKeepsMimeType() {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.unknown_type", "text/test_type");
        container.sign(PKCS12_SIGNER);
        container.save("test_add_unknown_datafile_type.bdoc");

        Container open = Container.open("test_add_unknown_datafile_type.bdoc");
        assertEquals(open.getDataFile(0).getMediaType(), "text/test_type");
    }

    @Test
    public void testSaveBDocDocumentWithTwoSignatures() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.sign(PKCS12_SIGNER);
        container.save("testTwoSignatures.bdoc");

        assertEquals(2, container.getSignatures().size());
        assertEquals("497c5a2bfa9361a8534fbed9f48e7a12",
                container.getSignatures().get(0).getSigningCertificate().getSerial());
        assertEquals("497c5a2bfa9361a8534fbed9f48e7a12",
                container.getSignatures().get(1).getSigningCertificate().getSerial());

        Container openedContainer = open("testTwoSignatures.bdoc");

        assertEquals(2, openedContainer.getSignatures().size());
        assertEquals("497c5a2bfa9361a8534fbed9f48e7a12",
                openedContainer.getSignatures().get(0).getSigningCertificate().getSerial());
        assertEquals("497c5a2bfa9361a8534fbed9f48e7a12",
                openedContainer.getSignatures().get(1).getSigningCertificate().getSerial());
    }

    @Test
    public void testGetDefaultSignatureParameters() {
        Container container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        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());
        assertNull(signature.getSignerRoles());
    }

    @Test
    public void getSignatureByIndex() {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.sign(PKCS12_SIGNER);

        assertEquals("497c5a2bfa9361a8534fbed9f48e7a12",
                container.getSignature(1).getSigningCertificate().getSerial());
    }

    @Test
    public void notThrowingNPEWhenDOCXFileIsAddedToContainer() {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/word_file.docx", "text/xml");
        container.sign(PKCS12_SIGNER);
        assertEquals(1, container.getSignatures().size());
    }

    @Test
    public void testAddSignaturesToExistingDocument() throws Exception {
        Container container = open("testFiles/asics_testing_two_signatures.bdoc");
        container.sign(PKCS12_SIGNER);
        container.save("testAddMultipleSignatures.bdoc");

        assertEquals(3, container.getSignatures().size());
        assertEquals("497c5a2bfa9361a8534fbed9f48e7a12",
                container.getSignatures().get(2).getSigningCertificate().getSerial());

        Container openedContainer = open("testAddMultipleSignatures.bdoc");

        assertEquals(3, openedContainer.getSignatures().size());
        assertEquals("497c5a2bfa9361a8534fbed9f48e7a12",
                openedContainer.getSignatures().get(2).getSigningCertificate().getSerial());

        ValidationResult validationResult = openedContainer.validate();
        assertEquals(0, validationResult.getErrors().size());
    }

    @Test
    public void testRemoveSignatureWhenOneSignatureExists() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.removeSignature(0);
        container.save("testRemoveSignature.bdoc");
        assertEquals(0, container.getSignatures().size());

        container = new BDocContainer("testRemoveSignature.bdoc");
        assertEquals(0, container.getSignatures().size());
    }

    @Test
    public void testRemoveSignatureWhenTwoSignaturesExist() throws Exception {
        Container container = open("testFiles/asics_testing_two_signatures.bdoc");
        container.removeSignature(0);
        container.save("testRemoveSignature.bdoc");

        container = new BDocContainer("testRemoveSignature.bdoc");
        assertEquals(1, container.getSignatures().size());
    }

    @Test
    public void testRemoveSignatureWhenThreeSignaturesExist() throws Exception {
        Container container = open("testFiles/asics_testing_two_signatures.bdoc");

        container.sign(PKCS12_SIGNER);
        container.save("testThreeSignatures.bdoc");
        container = new BDocContainer("testThreeSignatures.bdoc");
        assertEquals(3, container.getSignatures().size());

        container.removeSignature(1);

        container.save("testRemoveSignature.bdoc");

        container = new BDocContainer("testRemoveSignature.bdoc");
        assertEquals(2, container.getSignatures().size());
    }

    @Test
    public void testSaveDocumentWithOneSignature() throws Exception {
        createSignedBDocDocument("testSaveBDocDocumentWithOneSignature.bdoc");
        assertTrue(Files.exists(Paths.get("testSaveBDocDocumentWithOneSignature.bdoc")));
    }

    @Test
    public void testVerifySignedDocument() throws Exception {
        BDocContainer container = (BDocContainer) createSignedBDocDocument(
                "testSaveBDocDocumentWithOneSignature.bdoc");
        ValidationResult result = container.verify();
        assertFalse(result.hasErrors());
    }

    @Test
    public void testTestVerifyOnInvalidDocument() throws Exception {
        BDocContainer container = new BDocContainer("testFiles/invalid_container.bdoc");
        assertTrue(container.verify().hasErrors());
    }

    @Test(expected = DigiDoc4JException.class)
    public void testRemoveDataFileAfterSigning() throws Exception {
        createSignedBDocDocument("testRemoveDataFile.bdoc");
        Container container = new BDocContainer("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 = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        assertEquals("test.txt", container.getDataFiles().get(0).getName());
        assertEquals(1, container.getDataFiles().size());
        container.removeDataFile("testFiles/test.txt");
        assertEquals(0, container.getDataFiles().size());
    }

    @Test(expected = DigiDoc4JException.class)
    public void testAddDataFileAfterSigning() throws Exception {
        createSignedBDocDocument("testAddDataFile.bdoc");
        Container container = new BDocContainer("testAddDataFile.bdoc");
        container.addDataFile("testFiles/test.txt", "text/plain");
    }

    @Test(expected = DigiDoc4JException.class)
    public void testRemovingNonExistingFile() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.removeDataFile("test1.txt");
    }

    @Test(expected = DigiDoc4JException.class)
    public void testAddingSameFileSeveralTimes() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.addDataFile("testFiles/test.txt", "text/plain");
    }

    @Test(expected = DigiDoc4JException.class)
    public void testAddingSameFileSeveralTimesViaInputStream() throws Exception {
        BDocContainer container = new BDocContainer();
        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 {
        BDocContainer container = new BDocContainer();
        container.addDataFile(new ByteArrayInputStream("test".getBytes()), "testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        assertTrue(container.validate().isValid());
    }

    @Test(expected = DigiDoc4JException.class)
    public void testAddingSameFileInDifferentContainerSeveralTimes() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.addDataFile("testFiles/sub/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.save("testAddSameFile.bdoc");
    }

    @Test(expected = DigiDoc4JException.class)
    public void testAddingNotExistingFile() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("notExistingFile.txt", "text/plain");
    }

    @Test
    public void testAddFileAsStream() throws Exception {
        BDocContainer container = new BDocContainer();
        ByteArrayInputStream stream = new ByteArrayInputStream("tere, tere".getBytes());
        container.addDataFile(stream, "test1.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.save("testAddFileAsStream.bdoc");

        Container containerToTest = new BDocContainer("testAddFileAsStream.bdoc");
        assertEquals("test1.txt", containerToTest.getDataFiles().get(0).getName());
    }

    @Test
    public void setsSignatureId() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");

        SignatureParameters signatureParameters = new SignatureParameters();
        signatureParameters.setSignatureId("SIGNATURE-1");
        container.setSignatureParameters(signatureParameters);
        container.sign(PKCS12_SIGNER);

        signatureParameters.setSignatureId("SIGNATURE-2");
        container.setSignatureParameters(signatureParameters);
        container.sign(PKCS12_SIGNER);
        container.save("setsSignatureId.bdoc");

        container = new BDocContainer("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 {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.sign(PKCS12_SIGNER);
        container.save("testSetsDefaultSignatureId.bdoc");

        container = new BDocContainer("testSetsDefaultSignatureId.bdoc");
        assertEquals("S0", container.getSignature(0).getId());
        assertEquals("S1", container.getSignature(1).getId());

        ZipFile zip = new ZipFile("testSetsDefaultSignatureId.bdoc");
        assertNotNull(zip.getEntry("META-INF/signatures0.xml"));
        assertNotNull(zip.getEntry("META-INF/signatures1.xml"));
    }

    @Test
    public void getDataFileByIndex() {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);

        assertEquals("test.txt", container.getDataFile(0).getName());
    }

    @Test
    public void rawSignatureDoesNotThrowExceptionInCloseError() throws IOException {
        BDocContainer container = spy(new BDocContainer());
        byte[] signature = { 0x41 };
        MockInputStream value = new MockInputStream();

        doNothing().when(container).addRawSignature(value);
        when(container.getByteArrayInputStream(signature)).thenReturn(value);

        container.addRawSignature(signature);
    }

    @Test(expected = SignatureNotFoundException.class)
    public void testSignatureNotFoundException() throws Exception {
        BDocContainer container = new BDocContainer();
        BDocContainer spy = spy(container);

        eu.europa.ec.markt.dss.parameter.SignatureParameters signatureParameters = new eu.europa.ec.markt.dss.parameter.SignatureParameters();
        signatureParameters.setDeterministicId("NotPresentSignature");
        when(spy.getDssSignatureParameters()).thenReturn(signatureParameters);

        spy.addDataFile("testFiles/test.txt", "text/plain");
        spy.sign(PKCS12_SIGNER);
    }

    @Test(expected = DigiDoc4JException.class)
    public void openNonExistingFileThrowsError() {
        new BDocContainer("non-existing.bdoc");
    }

    @Test(expected = DigiDoc4JException.class)
    public void openClosedStreamThrowsException() throws IOException {
        FileInputStream stream = new FileInputStream(new File("testFiles/test.txt"));
        stream.close();
        new BDocContainer(stream, false);
    }

    @Test
    public void testLargeFileSigning() throws Exception {
        BDocContainer container = new BDocContainer();
        container.configuration.enableBigFilesSupport(10);
        String path = createLargeFile((container.configuration.getMaxDataFileCachedInBytes()) + 100);
        container.addDataFile(path, "text/plain");
        container.sign(PKCS12_SIGNER);
    }

    @Test
    public void openLargeFileFromStream() throws FileNotFoundException {

        BDocContainer container = new BDocContainer();
        container.configuration.enableBigFilesSupport(0);

        String path = createLargeFile((container.configuration.getMaxDataFileCachedInBytes()) + 100);
        container.addDataFile(path, "text/plain");
        container.sign(PKCS12_SIGNER);
        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 = new BDocContainer();
        container.configuration.enableBigFilesSupport(0);

        String path = createLargeFile((container.configuration.getMaxDataFileCachedInBytes()) + 100);
        try (FileInputStream stream = new FileInputStream(new File(path))) {
            container.addDataFile(stream, "fileName", "text/plain");
            container.sign(PKCS12_SIGNER);
            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");
        BDocContainer container = new BDocContainer("testGetDocumentType.bdoc");
        assertEquals(DocumentType.BDOC, container.getDocumentType());
    }

    @Test
    public void testAddTwoFilesAsStream() throws Exception {
        BDocContainer container = new BDocContainer();
        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 {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(B_BES);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.addDataFile("testFiles/test.xml", "text/xml");
        container.sign(PKCS12_SIGNER);
        container.save("testTwoFilesSigned.bdoc");

        container = new BDocContainer("testTwoFilesSigned.bdoc");
        assertEquals(2, container.getDataFiles().size());
    }

    @Test
    public void testGetFileNameAndID() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.addDataFile("testFiles/test.xml", "text/xml");
        container.sign(PKCS12_SIGNER);
        container.save("testTwoFilesSigned.bdoc");

        container = new BDocContainer("testTwoFilesSigned.bdoc");

        assertEquals("test.xml", container.getDataFile(0).getName());
        assertEquals("test.txt", container.getDataFile(1).getName());
        assertEquals("test.xml", container.getDataFile(0).getId());
        assertEquals("test.txt", container.getDataFile(1).getId());
    }

    @Test
    public void testAddTwoFilesAsFileWithOCSP() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.addDataFile("testFiles/test.xml", "text/xml");
        container.sign(PKCS12_SIGNER);
        container.save("testTwoFilesSigned.bdoc");

        container = new BDocContainer("testTwoFilesSigned.bdoc");
        assertEquals(2, container.getDataFiles().size());
    }

    @Test(expected = NotYetImplementedException.class)
    public void testValidateEmptyDocument() {
        BDocContainer container = new BDocContainer();
        container.validate();
    }

    @Test
    public void testValidate() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        ValidationResult validationResult = container.validate();
        assertEquals(0, validationResult.getErrors().size());
    }

    @Test
    public void testLoadConfiguration() throws Exception {
        BDocContainer container = new BDocContainer();
        assertFalse(container.configuration.isBigFilesSupportEnabled());
        container.loadConfiguration("testFiles/digidoc_test_conf.yaml");
        assertTrue(container.configuration.isBigFilesSupportEnabled());
        assertEquals(8192, container.configuration.getMaxDataFileCachedInMB());
    }

    @Test
    public void saveToStream() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile(new ByteArrayInputStream(new byte[] { 0x42 }), "test_bytes.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        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(expected = DigiDoc4JException.class)
    public void saveToStreamThrowsException() throws IOException {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        File expectedContainerAsFile = new File("testSaveToStreamTest.bdoc");
        OutputStream out = new FileOutputStream(expectedContainerAsFile);
        out.close();
        container.save(out);
    }

    @Test
    public void configurationImmutabilityWhenLoadingFromFile() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.save("test_immutable.bdoc");

        Configuration configuration = new Configuration(Configuration.Mode.TEST);
        String tspSource = configuration.getTspSource();

        container = new BDocContainer("test_immutable.bdoc", configuration);
        configuration.setTspSource("changed_tsp_source");

        assertEquals(tspSource, container.configuration.getTspSource());
    }

    @Test
    public void TSLIsLoadedAfterSettingNewTSLLocation() {
        Configuration configuration = new Configuration();
        configuration.setTslLocation("file:test-tsl/trusted-test-mp.xml");
        BDocContainer container = new BDocContainer(configuration);
        container.configuration.getTSL();
        assertEquals(6, container.configuration.getTSL().getCertificates().size());

        configuration.setTslLocation("http://10.0.25.57/tsl/trusted-test-mp.xml");
        container = new BDocContainer(configuration);
        assertNotEquals(6, container.configuration.getTSL().getCertificates().size());
    }

    @Test(expected = DigiDoc4JException.class)
    public void TSLFileNotFoundThrowsException() {
        Configuration configuration = new Configuration();
        configuration.setTslLocation("file:test-tsl/NotExisting.xml");
        BDocContainer container = new BDocContainer(configuration);
        container.configuration.getTSL();
    }

    @Test(expected = DigiDoc4JException.class)
    public void TSLConnectionFailureThrowsException() {
        Configuration configuration = new Configuration();
        configuration.setTslLocation("http://127.0.0.1/tsl/incorrect.xml");
        BDocContainer container = new BDocContainer(configuration);
        container.configuration.getTSL();
    }

    @Test(expected = DigiDoc4JException.class)
    @Ignore // Not running by default to prevent system admin from getting worried
    public void TSLConnectionFailureIncorrectFileName() {
        Configuration configuration = new Configuration();
        configuration.setTslLocation("http://10.0.25.57/tsl/incorrect.xml");
        BDocContainer container = new BDocContainer(configuration);
        container.configuration.getTSL();
    }

    @Test
    public void extendFromB_BESToTS() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(B_BES);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.save("testExtendTo.bdoc");

        assertEquals(1, container.getSignatures().size());
        assertNull(container.getSignature(0).getOCSPCertificate());

        container = new BDocContainer("testExtendTo.bdoc");
        container.extendTo(SignatureProfile.LT);
        container.save("testExtendToContainsIt.bdoc");

        assertEquals(1, container.getSignatures().size());
        assertNotNull(container.getSignature(0).getOCSPCertificate());
    }

    @Test
    public void extendFromB_BESToLTA() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(B_BES);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.save("testExtendTo.bdoc");

        assertEquals(1, container.getSignatures().size());
        assertNull(container.getSignature(0).getOCSPCertificate());

        container = new BDocContainer("testExtendTo.bdoc");
        container.extendTo(SignatureProfile.LTA);
        container.save("testExtendToContainsIt.bdoc");

        assertEquals(1, container.getSignatures().size());
        assertNotNull(container.getSignature(0).getOCSPCertificate());
    }

    @Test(expected = DigiDoc4JException.class)
    public void extendFromB_BESToLT_TMThrowsException() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(B_BES);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.extendTo(SignatureProfile.LT_TM);
    }

    @Test(expected = DigiDoc4JException.class)
    public void extendFromLTToLT_TMThrowsException() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(LT);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.extendTo(SignatureProfile.LT_TM);
    }

    @Test(expected = DigiDoc4JException.class)
    public void extendFromLTAToLT_TMThrowsException() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(LTA);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.extendTo(SignatureProfile.LT_TM);
    }

    @Test
    public void containerIsLT() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(SignatureProfile.LT);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.save("testLT.bdoc");

        container = new BDocContainer("testLT.bdoc");
        assertEquals(1, container.getSignatures().size());
        assertNotNull(container.getSignature(0).getOCSPCertificate());
    }

    @Test
    public void verifySignatureProfileIsTS() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(SignatureProfile.LT);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.save("testAddConfirmation.bdoc");

        assertEquals(1, container.getSignatures().size());
        assertNotNull(container.getSignature(0).getOCSPCertificate());
    }

    @Test(expected = DigiDoc4JException.class)
    public void extendToWhenConfirmationAlreadyExists() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.setSignatureProfile(B_BES);
        container.sign(PKCS12_SIGNER);
        container.save("testExtendTo.bdoc");

        assertEquals(1, container.getSignatures().size());
        assertNull(container.getSignature(0).getOCSPCertificate());

        container = new BDocContainer("testExtendTo.bdoc");
        container.extendTo(LT);
        container.extendTo(LT);
    }

    @Test(expected = DigiDoc4JException.class)
    public void signWithoutDataFile() throws Exception {
        BDocContainer container = new BDocContainer();
        container.sign(PKCS12_SIGNER);
    }

    @Test
    public void extendToWithMultipleSignatures() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.setSignatureProfile(B_BES);
        container.sign(PKCS12_SIGNER);
        container.sign(PKCS12_SIGNER);
        container.save("testExtendTo.bdoc");

        assertEquals(2, container.getSignatures().size());
        assertNull(container.getSignature(0).getOCSPCertificate());
        assertNull(container.getSignature(1).getOCSPCertificate());

        container = new BDocContainer("testExtendTo.bdoc");
        container.extendTo(LT);
        container.save("testExtendToContainsIt.bdoc");

        container = new BDocContainer("testExtendToContainsIt.bdoc");
        assertEquals(2, container.getSignatures().size());
        assertNotNull(container.getSignature(0).getOCSPCertificate());
        assertNotNull(container.getSignature(1).getOCSPCertificate());
    }

    @Test
    public void extendToWithMultipleSignaturesAndMultipleFiles() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(B_BES);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.addDataFile("testFiles/test.xml", "text/xml");
        container.sign(PKCS12_SIGNER);
        container.sign(PKCS12_SIGNER);
        container.save("testAddConfirmation.bdoc");

        assertEquals(2, container.getSignatures().size());
        assertEquals(2, container.getDataFiles().size());
        assertNull(container.getSignature(0).getOCSPCertificate());
        assertNull(container.getSignature(1).getOCSPCertificate());

        container = new BDocContainer("testAddConfirmation.bdoc");
        container.extendTo(LT);
        container.save("testAddConfirmationContainsIt.bdoc");

        assertEquals(2, container.getSignatures().size());
        assertEquals(2, container.getDataFiles().size());
        assertNotNull(container.getSignature(0).getOCSPCertificate());
        assertNotNull(container.getSignature(1).getOCSPCertificate());
    }

    @Test(expected = UnsupportedFormatException.class)
    public void notBDocThrowsException() {
        new BDocContainer("testFiles/notABDoc.bdoc");
    }

    @Test(expected = UnsupportedFormatException.class)
    public void incorrectMimetypeThrowsException() {
        new BDocContainer("testFiles/incorrectMimetype.bdoc");
    }

    @Test
    public void nonStandardMimeType() {
        Container container = Container.create(DocumentType.BDOC);
        container.addDataFile("testFiles/test.txt", "text/newtype");
        container.sign(PKCS12_SIGNER);
        container.save("testNonStandardMimeType.bdoc");
        container = Container.open("testNonStandardMimeType.bdoc");
        ValidationResult result = container.validate();
        assertEquals(0, result.getErrors().size());
        assertEquals("text/newtype", container.getDataFile(0).getMediaType());
    }

    @Test(expected = DigiDoc4JException.class)
    public void signingThrowsNormalDSSException() {
        MockBDocContainer container = new MockBDocContainer("Normal DSS Exception");
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
    }

    @Test(expected = OCSPRequestFailedException.class)
    public void signingThrowsOCSPException() {
        MockBDocContainer container = new MockBDocContainer("OCSP request failed");
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
    }

    @Test
    public void getVersion() {
        BDocContainer container = new BDocContainer();
        assertNull(container.getVersion());
    }

    @Test
    public void testContainerExtensionFromLTtoLTA() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);

        container.extendTo(LTA);
        assertNotNull(container.getSignature(0).getOCSPCertificate());
    }

    @Test
    public void twoStepSigning() throws IOException {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        X509Certificate signerCert = getSignerCert();
        SignedInfo signedInfo = container.prepareSigning(signerCert);
        byte[] signature = getExternalSignature(container, signerCert, signedInfo, SHA256);
        container.signRaw(signature);
        container.save("test.bdoc");

        container = open("test.bdoc");

        assertEquals(SHA256, container.getDigestAlgorithm());
        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());
        assertNull(resultSignature.getSignerRoles());
        assertNull(resultSignature.getCity());
        assertEquals("S0", resultSignature.getId());

        assertNotNull(resultSignature.getOCSPCertificate());
        assertNotNull(resultSignature.getSigningCertificate());
        assertNotNull(resultSignature.getRawSignature().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() {
        SignatureParameters signatureParameters = new SignatureParameters();
        signatureParameters.setDigestAlgorithm(DigestAlgorithm.SHA512);
        signatureParameters.setRoles(asList("manager", "employee"));
        signatureParameters
                .setProductionPlace(new SignatureProductionPlace("city", "state", "postalCode", "country"));
        signatureParameters.setSignatureId("S99");

        Container container = create();
        container.setSignatureParameters(signatureParameters);
        container.addDataFile("testFiles/test.txt", "text/plain");
        X509Certificate signerCert = getSignerCert();
        SignedInfo signedInfo = container.prepareSigning(signerCert);
        byte[] signature = getExternalSignature(container, signerCert, signedInfo,
                signatureParameters.getDigestAlgorithm());
        container.signRaw(signature);
        container.save("test.bdoc");

        container = 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 twoStepSigningWithSerialization() throws IOException, ClassNotFoundException {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        X509Certificate signerCert = getSignerCert();
        SignedInfo signedInfo = container.prepareSigning(signerCert);

        serialize(container, "container.bin");
        byte[] signature = getExternalSignature(container, signerCert, signedInfo, SHA256);

        container = deserializer("container.bin");
        container.signRaw(signature);
        container.save("test.bdoc");

        container = open("test.bdoc");

        ValidationResult validate = container.validate();
        assertTrue(validate.isValid());

        assertEquals(1, container.getSignatures().size());
    }

    @Test
    public void testContainerCreationAsTSA() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(LTA);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);

        assertNotNull(container.getSignature(0).getOCSPCertificate());
    }

    @Test(expected = DigiDoc4JException.class)
    public void extensionNotPossibleWhenSignatureLevelIsSame() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(LTA);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.extendTo(LTA);
    }

    private Container createSignedBDocDocument(String fileName) {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        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();
        }
    }

    private class MockBDocContainer extends BDocContainer {
        private String expected;

        public MockBDocContainer(String expected) {
            super();
            this.expected = expected;
        }

        @Override
        public Signature sign(Signer signer) {
            super.asicService = spy(new ASiCService(new CommonCertificateVerifier()));
            doThrow(new DSSException(expected)).when(super.asicService).signDocument(Mockito.any(DSSDocument.class),
                    Mockito.any(eu.europa.ec.markt.dss.parameter.SignatureParameters.class),
                    Mockito.any(byte[].class));
            return super.sign(signer);
        }
    }

    static byte[] getExternalSignature(Container container, final X509Certificate signerCert,
            SignedInfo prepareSigningSignature, final DigestAlgorithm digestAlgorithm) {
        Signer externalSigner = new ExternalSigner(signerCert) {
            @Override
            public byte[] sign(Container container, byte[] dataToSign) {
                try {
                    KeyStore keyStore = KeyStore.getInstance("PKCS12");
                    try (FileInputStream stream = new FileInputStream("testFiles/signout.p12")) {
                        keyStore.load(stream, "test".toCharArray());
                    }
                    PrivateKey privateKey = (PrivateKey) keyStore.getKey("1", "test".toCharArray());
                    final String javaSignatureAlgorithm = "NONEwith" + privateKey.getAlgorithm();

                    return DSSUtils.encrypt(javaSignatureAlgorithm, privateKey, addPadding(dataToSign));
                } catch (Exception e) {
                    throw new DigiDoc4JException("Loading private key failed");
                }
            }

            private byte[] addPadding(byte[] digest) {
                byte[] signatureDigest;
                switch (digestAlgorithm) {
                case SHA512:
                    signatureDigest = Constants.SHA512_DIGEST_INFO_PREFIX;
                    break;
                case SHA256:
                    signatureDigest = Constants.SHA256_DIGEST_INFO_PREFIX;
                    break;
                default:
                    throw new NotYetImplementedException();
                }
                return ArrayUtils.addAll(signatureDigest, digest);
            }
        };

        return externalSigner.sign(container, prepareSigningSignature.getDigest());
    }

    static X509Certificate getSignerCert() {
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            try (FileInputStream stream = new FileInputStream("testFiles/signout.p12")) {
                keyStore.load(stream, "test".toCharArray());
            }
            return (X509Certificate) keyStore.getCertificate("1");
        } catch (Exception e) {
            throw new DigiDoc4JException("Loading signer cert failed");
        }
    }

    @Test
    public void verifySerialization() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);

        serialize(container, "container.bin");

        Container deserializedContainer = deserializer("container.bin");

        assertTrue(deserializedContainer.validate().isValid());
    }

    @Test
    public void serializationVerifySpecifiedSignatureParameters() throws Exception {
        SignatureParameters signatureParameters = new SignatureParameters();
        signatureParameters.setDigestAlgorithm(DigestAlgorithm.SHA512);
        signatureParameters.setRoles(asList("manager", "employee"));
        signatureParameters
                .setProductionPlace(new SignatureProductionPlace("city", "state", "postalCode", "country"));
        signatureParameters.setSignatureId("S99");

        Container container = create();
        container.setSignatureParameters(signatureParameters);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);

        serialize(container, "container.bin");

        Container deserializedContainer = deserializer("container.bin");

        Signature signature = deserializedContainer.getSignature(0);
        assertEquals("postalCode", signature.getPostalCode());
        assertEquals("city", signature.getCity());
        assertEquals("state", signature.getStateOrProvince());
        assertEquals("country", signature.getCountryName());
        assertEquals("employee", signature.getSignerRoles().get(1));
        assertEquals("S99", signature.getId());
        assertEquals("http://www.w3.org/2001/04/xmlenc#sha512", signature.getSignatureMethod());
    }

    @Test
    public void serializationVerifyDefaultSignatureParameters() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        Signature signature = deserializedContainer.getSignature(0);

        assertNull(signature.getCity());
        assertNull(signature.getSignerRoles());
        assertEquals("S0", signature.getId());
        assertEquals("http://www.w3.org/2001/04/xmlenc#sha256", signature.getSignatureMethod());
    }

    @Test
    public void serializationGetDigestAlgorithm() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        assertEquals(SHA256, deserializedContainer.getDigestAlgorithm());
    }

    @Test
    public void serializationGetDocumentType() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        assertEquals(container.getDocumentType(), deserializedContainer.getDocumentType());
    }

    @Test
    public void serializationGetOCSPCertificate() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        byte[] ocspCertBeforeSerialization = container.getSignature(0).getOCSPCertificate().getX509Certificate()
                .getEncoded();
        byte[] ocspCertAfterSerialization = deserializedContainer.getSignature(0).getOCSPCertificate()
                .getX509Certificate().getEncoded();

        assertArrayEquals(ocspCertBeforeSerialization, ocspCertAfterSerialization);
    }

    @Test
    public void serializationGetSigningTime() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        Date signingTimeBeforeSerialization = container.getSignature(0).getSigningTime();
        Date signingTimeAfterSerialization = deserializedContainer.getSignature(0).getSigningTime();

        assertEquals(signingTimeBeforeSerialization, signingTimeAfterSerialization);
    }

    @Test(expected = NotYetImplementedException.class)
    public void serializationGetPolicy() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        String signaturePolicyBeforeSerialization = container.getSignature(0).getPolicy();
        String signaturePolicyAfterSerialization = deserializedContainer.getSignature(0).getPolicy();

        assertEquals(signaturePolicyBeforeSerialization, signaturePolicyAfterSerialization);
    }

    @Test(expected = NotYetImplementedException.class)
    public void serializationGetSignaturePolicyURI() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        URI signaturePolicyURIBeforeSerialization = container.getSignature(0).getSignaturePolicyURI();
        URI signaturePolicyURIAfterSerialization = deserializedContainer.getSignature(0).getSignaturePolicyURI();

        assertEquals(signaturePolicyURIBeforeSerialization, signaturePolicyURIAfterSerialization);
    }

    @Test
    public void serializationGetSigningCertificate() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        byte[] signingCertBeforeSerialization = container.getSignature(0).getSigningCertificate()
                .getX509Certificate().getEncoded();
        byte[] singingCertAfterSerialization = deserializedContainer.getSignature(0).getSigningCertificate()
                .getX509Certificate().getEncoded();

        assertArrayEquals(signingCertBeforeSerialization, singingCertAfterSerialization);
    }

    @Test
    public void serializationGetRawSignature() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        byte[] rawSignatureBeforeSerialization = container.getSignature(0).getRawSignature();
        byte[] rawSignatureAfterSerialization = deserializedContainer.getSignature(0).getRawSignature();

        assertArrayEquals(rawSignatureBeforeSerialization, rawSignatureAfterSerialization);
    }

    @Test
    public void serializationGetTimeStampTokenCertificate() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        byte[] timeStampTokenCertificateBeforeSerialization = container.getSignature(0)
                .getTimeStampTokenCertificate().getX509Certificate().getEncoded();
        byte[] timeStampTokenCertificateAfterSerialization = deserializedContainer.getSignature(0)
                .getTimeStampTokenCertificate().getX509Certificate().getEncoded();

        assertArrayEquals(timeStampTokenCertificateBeforeSerialization,
                timeStampTokenCertificateAfterSerialization);
    }

    @Test
    public void serializationGetProfile() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        SignatureProfile signatureProfileBeforeSerialization = container.getSignature(0).getProfile();
        SignatureProfile signatureProfileAfterSerialization = deserializedContainer.getSignature(0).getProfile();

        assertEquals(signatureProfileBeforeSerialization, signatureProfileAfterSerialization);
    }

    @Test
    public void serializationGetDataFiles() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        int nrOfDataFilesBeforeSerialization = container.getDataFiles().size();
        int nrOfDataFilesAfterSerialization = deserializedContainer.getDataFiles().size();

        assertEquals(nrOfDataFilesBeforeSerialization, nrOfDataFilesAfterSerialization);
    }

    @Test
    public void serializationDataFileCheck() throws Exception {
        Container container = create();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        serialize(container, "container.bin");
        Container deserializedContainer = deserializer("container.bin");

        DataFile dataFileBeforeSerialization = container.getDataFile(0);
        DataFile dataFileAfterSerialization = deserializedContainer.getDataFile(0);

        assertEquals(dataFileBeforeSerialization.getFileSize(), dataFileAfterSerialization.getFileSize());
        assertArrayEquals(dataFileBeforeSerialization.getBytes(), dataFileAfterSerialization.getBytes());
        assertEquals(dataFileBeforeSerialization.getId(), dataFileAfterSerialization.getId());
        assertEquals(dataFileBeforeSerialization.getName(), dataFileAfterSerialization.getName());
        assertEquals(dataFileBeforeSerialization.getMediaType(), dataFileAfterSerialization.getMediaType());

        byte[] bytesBeforeSerialization = IOUtils.toByteArray(dataFileBeforeSerialization.getStream());
        byte[] bytesAfterSerialization = IOUtils.toByteArray(dataFileAfterSerialization.getStream());

        assertArrayEquals(bytesBeforeSerialization, bytesAfterSerialization);

        assertArrayEquals(dataFileAfterSerialization.calculateDigest(),
                dataFileBeforeSerialization.calculateDigest());
    }

    @Test
    public void testOCSPRevoked() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setOCSPAccessCertificateFileName("testFiles/ocsp_juurdepaasutoend.p12d");
        configuration.setOCSPAccessCertificatePassword("0vRsI0XQ".toCharArray());

        Container container = Container.open("testFiles/EE-TS-BpLT-R-001.asice", configuration);
        ValidationResult result = container.validate();
        assertFalse(result.isValid());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void signatureFileContainsIncorrectFileName() {
        Container container = Container.open("testFiles/filename_mismatch_signature.asice");
        ValidationResult validate = container.validate();
        assertEquals(1, validate.getErrors().size());
        assertEquals("The reference data object(s) not found!", validate.getErrors().get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void secondSignatureFileContainsIncorrectFileName() {
        Container container = Container.open("testFiles/filename_mismatch_second_signature.asice");
        ValidationResult validate = container.validate();
        List<DigiDoc4JException> errors = validate.getErrors();
        assertEquals(3, errors.size());
        assertEquals(
                "Manifest file has an entry for file test.txt with mimetype text/plain but the signature file for "
                        + "signature S1 does not have an entry for this file",
                errors.get(0).toString());
        assertEquals("Container contains a file named test.txt which is not found in the signature file",
                errors.get(1).toString());
        assertEquals("The reference data object(s) is not intact!", errors.get(2).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void manifestFileContainsIncorrectFileName() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");
        Container container = Container.open("testFiles/filename_mismatch_manifest.asice", configuration);
        ValidationResult validate = container.validate();
        assertEquals(2, validate.getErrors().size());
        assertEquals(
                "Manifest file has an entry for file incorrect.txt with mimetype text/plain but the signature file "
                        + "for signature S0 does not have an entry for this file",
                validate.getErrors().get(0).toString());
        assertEquals(
                "The signature file for signature S0 has an entry for file RELEASE-NOTES.txt with mimetype "
                        + "text/plain but the manifest file does not have an entry for this file",
                validate.getErrors().get(1).toString());
    }

    @Test
    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    public void revocationAndTimeStampDifferenceTooLarge() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");
        Container container = Container.open("testFiles/revocation_timestamp_delta_26h.asice", configuration);
        ValidationResult validate = container.validate();
        assertEquals(1, validate.getErrors().size());
        assertEquals("The difference between the revocation time and the signature time stamp is too large",
                validate.getErrors().get(0).toString());
    }

    @Test
    public void revocationAndTimeStampDifferenceNotTooLarge() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint_SigningTimeCreationTimeDeltaIs27H.xml");
        Container container = Container.open("testFiles/revocation_timestamp_delta_26h.asice", configuration);
        ValidationResult validate = container.validate();
        assertEquals(0, validate.getErrors().size());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void signatureFileAndManifestFileContainDifferentMimeTypeForFile() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");
        Container container = Container.open("testFiles/mimetype_mismatch.asice", configuration);
        ValidationResult validate = container.validate();
        assertEquals(1, validate.getErrors().size());
        assertEquals(
                "Manifest file has an entry for file RELEASE-NOTES.txt with mimetype application/pdf but the "
                        + "signature file for signature S0 indicates the mimetype is text/plain",
                validate.getErrors().get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void dssReturnsEmptySignatureList() {
        Container container = Container.open("testFiles/filename_mismatch_signature.asice");
        ValidationResult validate = container.validate();
        assertEquals(1, validate.getErrors().size());
        assertEquals("The reference data object(s) not found!", validate.getErrors().get(0).toString());
    }

    @Test(expected = DigiDoc4JException.class)
    public void duplicateFileThrowsException() {
        Container container = Container.open("testFiles/22902_data_files_with_same_names.bdoc");
        container.validate();
    }

    @Test(expected = DigiDoc4JException.class)
    public void duplicateSignatureFileThrowsException() {
        Container container = Container.open("testFiles/22913_signatures_xml_double.bdoc");
        container.validate();
    }

    @Test(expected = DigiDoc4JException.class)
    public void missingManifestFile() {
        Container container = Container.open("testFiles/missing_manifest.asice");
        container.validate();
    }

    @Test(expected = DigiDoc4JException.class)
    public void missingMimeTypeFile() {
        Container.open("testFiles/missing_mimetype_file.asice");
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void containerHasFileWhichIsNotInManifestAndNotInSignatureFile() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");
        Container container = Container.open("testFiles/extra_file_in_container.asice", configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Container contains a file named AdditionalFile.txt which is not found in the signature file",
                errors.get(0).getMessage());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void containerMissesFileWhichIsInManifestAndSignatureFile() {
        Container container = Container.open("testFiles/zip_misses_file_which_is_in_manifest.asice");
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("The reference data object(s) not found!", errors.get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void containerMissingOCSPData() {
        Container container = Container.open("testFiles/TS-06_23634_TS_missing_OCSP_adjusted.asice");
        List<DigiDoc4JException> errors = container.validate().getErrors();

        assertEquals("ASiC_E_BASELINE_LT", container.getSignatureProfile());
        assertEquals(2, errors.size());
        assertTrue(errors.get(1).toString().contains("No revocation data for the certificate"));
    }

    @Test(expected = DigiDoc4JException.class)
    public void corruptedOCSPDataThrowsException() {
        Container.open("testFiles/corrupted_ocsp_data.asice");
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void invalidNoncePolicyOid() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/23608_bdoc21-invalid-nonce-policy-oid.bdoc", configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Wrong policy identifier: urn:oid:1.3.6.1.4.1.10015.1000.3.4.3", errors.get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void noNoncePolicy() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/23608_bdoc21-no-nonce-policy.bdoc", configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Policy url is missing for identifier: urn:oid:1.3.6.1.4.1.10015.1000.3.2.1",
                errors.get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void badNonceContent() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/bdoc21-bad-nonce-content.bdoc", configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Nonce is invalid", errors.get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void noSignedPropRefTM() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/REF-03_bdoc21-TM-no-signedpropref.bdoc", configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Signed properties missing", errors.get(0).toString());
        assertEquals(1, container.getSignatures().get(0).validate().size());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void noSignedPropRefTS() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/REF-03_bdoc21-TS-no-signedpropref.asice", configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Signed properties missing", errors.get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void multipleSignedProperties() {
        Container container = Container.open("testFiles/multiple_signed_properties.asice");
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(2, errors.size());
        assertEquals("The signature is not intact!", errors.get(0).toString());
        assertEquals("Multiple signed properties", errors.get(1).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void incorrectSignedPropertiesReference() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/signed_properties_reference_not_found.asice",
                configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("The reference data object(s) not found!", errors.get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void nonceIncorrectContent() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/nonce-vale-sisu.bdoc", configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(3, errors.size());
        assertEquals("Nonce is invalid", errors.get(1).toString());
        assertEquals("Wrong policy identifier: urn:oid:1.3.6.1.4.1.10015.1000.2.10.10", errors.get(2).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void badNoncePolicyOidQualifier() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/SP-03_bdoc21-bad-nonce-policy-oidasuri.bdoc",
                configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Wrong policy identifier qualifier: OIDAsURI", errors.get(0).toString());
        assertEquals(1, container.getSignatures().get(0).validate().size());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void invalidNonce() {
        Container container = Container.open("testFiles/23200_weakdigest-wrong-nonce.asice");
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Nonce is invalid", errors.get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void noPolicyURI() {
        Configuration configuration = new Configuration(Configuration.Mode.PROD);
        configuration.setValidationPolicy("conf/test_constraint.xml");

        Container container = Container.open("testFiles/SP-06_bdoc21-no-uri.bdoc", configuration);
        ValidationResult result = container.validate();
        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals("Policy url is missing for identifier: urn:oid:1.3.6.1.4.1.10015.1000.3.2.1",
                errors.get(0).toString());
    }

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
    @Test
    public void brokenTS() {
        Container container = Container.open("testFiles/TS_broken_TS.asice");
        ValidationResult result = container.validate();

        List<DigiDoc4JException> errors = result.getErrors();
        assertEquals(1, errors.size());
        assertEquals(MessageTag.ADEST_TSSIG_ANS.getMessage(), errors.get(0).toString());
    }

    @Test
    public void testBDocTM() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(LT_TM);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        assertTrue(container.validate().isValid());
    }

    @Test
    public void containerWithBESProfileHasNoValidationErrors() throws Exception {
        BDocContainer container = new BDocContainer();
        container.setSignatureProfile(B_BES);
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);

        assertEquals("ASiC_E_BASELINE_B", container.getSignatureProfile());
        assertNull(container.getSignature(0).getOCSPCertificate());
        ValidationResult result = container.validate();
        assertEquals(0, result.getErrors().size());
    }

    @Test
    public void signWithECCCertificate() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        SignatureParameters signatureParameters = new SignatureParameters();
        signatureParameters.setEncryptionAlgorithm(ECDSA);
        container.setSignatureParameters(signatureParameters);
        container.sign(new PKCS12Signer("testFiles/ec-digiid.p12", "inno".toCharArray()));

        assertTrue(container.validate().isValid());
    }

    @Test
    public void zipFileComment() throws Exception {
        BDocContainer container = new BDocContainer();
        container.addDataFile("testFiles/test.txt", "text/plain");
        container.sign(PKCS12_SIGNER);
        container.save("testZipFileComment.bdoc");

        ZipFile zipFile = new ZipFile("testZipFileComment.bdoc");
        String expectedComment = Helper.createUserAgent(container);
        assertEquals(expectedComment, zipFile.getComment());
    }
}