/* 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; import static org.digidoc4j.ContainerBuilder.BDOC_CONTAINER_TYPE; import static org.digidoc4j.ContainerBuilder.DDOC_CONTAINER_TYPE; import static org.digidoc4j.testutils.TestSigningHelper.getSigningCert; 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.assertSame; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.security.cert.X509Certificate; import java.util.List; import org.apache.commons.io.FileUtils; import org.digidoc4j.exceptions.InvalidSignatureException; import org.digidoc4j.exceptions.NotSupportedException; import org.digidoc4j.exceptions.SignatureTokenMissingException; import org.digidoc4j.impl.DigiDoc4JTestHelper; import org.digidoc4j.impl.bdoc.BDocSignature; import org.digidoc4j.impl.bdoc.xades.validation.XadesSignatureValidator; import org.digidoc4j.signers.PKCS12SignatureToken; import org.digidoc4j.testutils.TestContainer; import org.digidoc4j.testutils.TestDataBuilder; import org.digidoc4j.testutils.TestSignatureBuilder; import org.digidoc4j.testutils.TestSigningHelper; import org.digidoc4j.utils.TokenAlgorithmSupport; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import eu.europa.esig.dss.validation.TimestampToken; import eu.europa.esig.dss.x509.SignaturePolicy; public class SignatureBuilderTest extends DigiDoc4JTestHelper { @Rule public TemporaryFolder testFolder = new TemporaryFolder(); private final PKCS12SignatureToken testSignatureToken = new PKCS12SignatureToken("testFiles/signout.p12", "test".toCharArray()); @After public void tearDown() throws Exception { ContainerBuilder.removeCustomContainerImplementations(); SignatureBuilder.removeCustomSignatureBuilders(); } @Test public void buildingDataToSign_shouldReturnDigestToSign() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder); X509Certificate signerCert = getSigningCert(); SignatureBuilder builder = SignatureBuilder. aSignature(container). withSigningCertificate(signerCert); DataToSign dataToSign = builder.buildDataToSign(); assertNotNull(dataToSign); assertNotNull(dataToSign.getDigestToSign()); assertNotNull(dataToSign.getSignatureParameters()); assertEquals(32, dataToSign.getDigestToSign().length); //SHA256 is always 256 bits long, equivalent to 32 bytes assertEquals(DigestAlgorithm.SHA256, dataToSign.getDigestAlgorithm()); } @Test public void buildingDataToSign_shouldContainSignatureParameters() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder); X509Certificate signerCert = getSigningCert(); SignatureBuilder builder = SignatureBuilder. aSignature(container). withCity("San Pedro"). withStateOrProvince("Puerto Vallarta"). withPostalCode("13456"). withCountry("Val Verde"). withRoles("Manager", "Suspicious Fisherman"). withSignatureDigestAlgorithm(DigestAlgorithm.SHA256). withSignatureProfile(SignatureProfile.LT_TM). withSignatureId("S0"). withSigningCertificate(signerCert); DataToSign dataToSign = builder.buildDataToSign(); SignatureParameters parameters = dataToSign.getSignatureParameters(); assertEquals("San Pedro", parameters.getCity()); assertEquals("Puerto Vallarta", parameters.getStateOrProvince()); assertEquals("13456", parameters.getPostalCode()); assertEquals("Val Verde", parameters.getCountry()); assertEquals("Manager", parameters.getRoles().get(0)); assertEquals(DigestAlgorithm.SHA256, parameters.getDigestAlgorithm()); assertEquals(SignatureProfile.LT_TM, parameters.getSignatureProfile()); assertEquals("S0", parameters.getSignatureId()); assertSame(signerCert, parameters.getSigningCertificate()); byte[] bytesToSign = dataToSign.getDigestToSign(); assertNotNull(bytesToSign); assertTrue(bytesToSign.length > 1); } @Test public void signDocumentExternallyTwice() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder); DataToSign dataToSign = TestDataBuilder.buildDataToSign(container, "S0"); Signature signature = TestDataBuilder.makeSignature(container, dataToSign); assertSignatureIsValid(signature); DataToSign dataToSign2 = TestDataBuilder.buildDataToSign(container, "S1"); Signature signature2 = TestDataBuilder.makeSignature(container, dataToSign2); assertSignatureIsValid(signature2); container.saveAsFile(testFolder.newFile("test-container.bdoc").getPath()); } @Test public void signContainerWithSignatureToken() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder); Signature signature = SignatureBuilder. aSignature(container). withCity("Tallinn"). withStateOrProvince("Harjumaa"). withPostalCode("13456"). withCountry("Estonia"). withRoles("Manager", "Suspicious Fisherman"). withSignatureDigestAlgorithm(DigestAlgorithm.SHA256). withSignatureProfile(SignatureProfile.LT_TM). withSignatureToken(testSignatureToken). invokeSigning(); container.addSignature(signature); assertTrue(signature.validateSignature().isValid()); container.saveAsFile(testFolder.newFile("test-container2.bdoc").getPath()); assertSignatureIsValid(signature); assertEquals("Tallinn", signature.getCity()); assertEquals("Harjumaa", signature.getStateOrProvince()); assertEquals("13456", signature.getPostalCode()); assertEquals("Estonia", signature.getCountryName()); assertEquals(2, signature.getSignerRoles().size()); assertEquals("Manager", signature.getSignerRoles().get(0)); assertEquals("Suspicious Fisherman", signature.getSignerRoles().get(1)); } @Test public void createTimeMarkSignature_shouldNotContainTimestamp() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder); BDocSignature signature = (BDocSignature) SignatureBuilder. aSignature(container). withSignatureProfile(SignatureProfile.LT_TM). withSignatureToken(testSignatureToken). invokeSigning(); container.addSignature(signature); List<TimestampToken> signatureTimestamps = signature.getOrigin().getDssSignature().getSignatureTimestamps(); assertTrue(signatureTimestamps == null || signatureTimestamps.isEmpty()); } @Test public void signDDocContainerWithSignatureToken() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder, "DDOC"); assertEquals("DDOC", container.getType()); Signature signature = SignatureBuilder. aSignature(container). withSignatureDigestAlgorithm(DigestAlgorithm.SHA1). withSignatureToken(testSignatureToken). invokeSigning(); container.addSignature(signature); } @Test(expected = SignatureTokenMissingException.class) public void signContainerWithMissingSignatureToken_shouldThrowException() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder); SignatureBuilder. aSignature(container). invokeSigning(); } @Test public void signDDocContainer() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder, "DDOC"); X509Certificate signingCert = getSigningCert(); DataToSign dataToSign = SignatureBuilder. aSignature(container). withSigningCertificate(signingCert). buildDataToSign(); assertEquals(DigestAlgorithm.SHA1, dataToSign.getDigestAlgorithm()); byte[] bytesToSign = dataToSign.getDigestToSign(); assertNotNull(bytesToSign); assertTrue(bytesToSign.length > 1); byte[] signatureValue = TestSigningHelper.sign(dataToSign.getDigestToSign(), dataToSign.getDigestAlgorithm()); assertNotNull(signatureValue); assertTrue(signatureValue.length > 1); Signature signature = dataToSign.finalize(signatureValue); assertNotNull(signature); assertNotNull(signature.getClaimedSigningTime()); container.addSignature(signature); container.saveAsFile(testFolder.newFile("test-container.bdoc").getPath()); } @Test public void signatureProfileShouldBeSetProperlyForBDoc() throws Exception { Signature signature = createBDocSignatureWithProfile(SignatureProfile.B_BES); assertEquals(SignatureProfile.B_BES, signature.getProfile()); assertTrue(signature.getSignerRoles().isEmpty()); } @Test public void signatureProfileShouldBeSetProperlyForBDocTS() throws Exception { Signature signature = createBDocSignatureWithProfile(SignatureProfile.LT); assertEquals(SignatureProfile.LT, signature.getProfile()); } @Test public void signatureProfileShouldBeSetProperlyForBDocTM() throws Exception { Signature signature = createBDocSignatureWithProfile(SignatureProfile.LT_TM); assertEquals(SignatureProfile.LT_TM, signature.getProfile()); } @Test public void signatureProfileShouldBeSetProperlyForBEpes() throws Exception { Signature signature = createBDocSignatureWithProfile(SignatureProfile.B_EPES); assertEquals(SignatureProfile.B_EPES, signature.getProfile()); assertNull(signature.getTrustedSigningTime()); assertNull(signature.getOCSPCertificate()); assertNull(signature.getOCSPResponseCreationTime()); assertNull(signature.getTimeStampTokenCertificate()); assertNull(signature.getTimeStampCreationTime()); BDocSignature bDocSignature = (BDocSignature) signature; SignaturePolicy policyId = bDocSignature.getOrigin().getDssSignature().getPolicyId(); assertEquals(XadesSignatureValidator.TM_POLICY, policyId.getIdentifier()); } @Test public void signWithEccCertificate() throws Exception { PKCS12SignatureToken eccSignatureToken = new PKCS12SignatureToken("testFiles/ec-digiid.p12", "inno".toCharArray()); Container container = TestDataBuilder.createContainerWithFile(testFolder, "BDOC"); Signature signature = SignatureBuilder. aSignature(container). withSignatureToken(eccSignatureToken). withEncryptionAlgorithm(EncryptionAlgorithm.ECDSA). invokeSigning(); assertTrue(signature.validateSignature().isValid()); } @Test public void signWithEccCertificate_determiningEncryptionAlgorithmAutomatically() throws Exception { PKCS12SignatureToken eccSignatureToken = new PKCS12SignatureToken("testFiles/ec-digiid.p12", "inno".toCharArray()); Container container = TestDataBuilder.createContainerWithFile(testFolder, "BDOC"); Signature signature = SignatureBuilder. aSignature(container). withSignatureToken(eccSignatureToken). invokeSigning(); assertTrue(signature.validateSignature().isValid()); } @Test public void signWithDeterminedSignatureDigestAlgorithm() throws Exception { Container container = TestDataBuilder.createContainerWithFile(testFolder); X509Certificate certificate = testSignatureToken.getCertificate(); DigestAlgorithm digestAlgorithm = TokenAlgorithmSupport.determineSignatureDigestAlgorithm(certificate); DataToSign dataToSign = SignatureBuilder. aSignature(container). withSignatureDigestAlgorithm(digestAlgorithm). withSigningCertificate(certificate). buildDataToSign(); SignatureParameters signatureParameters = dataToSign.getSignatureParameters(); assertEquals(DigestAlgorithm.SHA256, signatureParameters.getDigestAlgorithm()); Signature signature = TestDataBuilder.makeSignature(container, dataToSign); assertEquals(DigestAlgorithm.SHA256.toString(), signature.getSignatureMethod()); assertTrue(container.validate().isValid()); } @Test(expected = InvalidSignatureException.class) public void openSignatureFromNull_shouldThrowException() throws Exception { Container container = TestDataBuilder.createContainerWithFile("testFiles/test.txt"); SignatureBuilder. aSignature(container). openAdESSignature(null); } @Test public void openSignatureFromExistingSignatureDocument() throws Exception { Container container = TestDataBuilder.createContainerWithFile("testFiles/test.txt"); Signature signature = openSignatureFromExistingSignatureDocument(container); assertTrue(signature.validateSignature().isValid()); } @Test public void openSignatureForDDocFromExistingSignatureDocument() throws Exception { Container container = ContainerBuilder. aContainer(DDOC_CONTAINER_TYPE). withDataFile("testFiles/test.txt", "text/plain"). build(); openSignatureFromExistingSignatureDocument(container); } @Test(expected = InvalidSignatureException.class) public void openSignatureFromInvalidSignatureDocument() throws Exception { Container container = TestDataBuilder.createContainerWithFile("testFiles/test.txt"); byte[] signatureBytes = FileUtils.readFileToByteArray(new File("testFiles/test.txt")); SignatureBuilder. aSignature(container). openAdESSignature(signatureBytes); } @Test public void openSignature_withDataFilesMismatch_shouldBeInvalid() throws Exception { Container container = TestDataBuilder.createContainerWithFile("testFiles/word_file.docx"); Signature signature = openAdESSignature(container); SignatureValidationResult result = signature.validateSignature(); assertFalse(result.isValid()); assertEquals("The reference data object(s) is not found!", result.getErrors().get(0).getMessage()); } @Test public void openXadesSignature_withoutXmlPreamble_shouldNotThrowException() throws Exception { Container container = TestDataBuilder.createContainerWithFile("testFiles/test.txt"); byte[] signatureBytes = FileUtils.readFileToByteArray(new File("testFiles/xades/bdoc-tm-jdigidoc-mobile-id.xml")); SignatureBuilder. aSignature(container). openAdESSignature(signatureBytes); } @Test public void openXadesSignature_andSavingContainer_shouldNotChangeSignature() throws Exception { String containerPath = testFolder.newFile("test.bdoc").getPath(); Container container = TestDataBuilder.createContainerWithFile("testFiles/word_file.docx"); Signature signature = openAdESSignature(container); container.addSignature(signature); container.saveAsFile(containerPath); container = ContainerOpener.open(containerPath); byte[] originalSignatureBytes = FileUtils.readFileToByteArray(new File("testFiles/xades/valid-bdoc-tm.xml")); byte[] signatureBytes = container.getSignatures().get(0).getAdESSignature(); assertArrayEquals(originalSignatureBytes, signatureBytes); } private Signature openSignatureFromExistingSignatureDocument(Container container) throws IOException { Signature signature = openAdESSignature(container); assertEquals("id-6a5d6671af7a9e0ab9a5e4d49d69800d", signature.getId()); return signature; } private Signature openAdESSignature(Container container) throws IOException { byte[] signatureBytes = FileUtils.readFileToByteArray(new File("testFiles/xades/valid-bdoc-tm.xml")); return SignatureBuilder. aSignature(container). openAdESSignature(signatureBytes); } @Test(expected = NotSupportedException.class) public void signUnknownContainerFormat_shouldThrowException() throws Exception { ContainerBuilder.setContainerImplementation("TEST-FORMAT", TestContainer.class); Container container = TestDataBuilder.createContainerWithFile(testFolder, "TEST-FORMAT"); TestDataBuilder.buildDataToSign(container); } @Test public void signCustomContainer() throws Exception { ContainerBuilder.setContainerImplementation("TEST-FORMAT", TestContainer.class); SignatureBuilder.setSignatureBuilderForContainerType("TEST-FORMAT", TestSignatureBuilder.class); Container container = TestDataBuilder.createContainerWithFile(testFolder, "TEST-FORMAT"); DataToSign dataToSign = TestDataBuilder.buildDataToSign(container); assertNotNull(dataToSign); byte[] signatureValue = TestSigningHelper.sign(dataToSign.getDigestToSign(), dataToSign.getDigestAlgorithm()); Signature signature = dataToSign.finalize(signatureValue); assertNotNull(signature); } @Test public void invokeSigningForCustomContainer() throws Exception { ContainerBuilder.setContainerImplementation("TEST-FORMAT", TestContainer.class); SignatureBuilder.setSignatureBuilderForContainerType("TEST-FORMAT", TestSignatureBuilder.class); Container container = TestDataBuilder.createContainerWithFile(testFolder, "TEST-FORMAT"); Signature signature = SignatureBuilder. aSignature(container). withSignatureToken(testSignatureToken). invokeSigning(); assertNotNull(signature); } @Test public void invokeSigning_whenOverridingBDocContainerFormat() throws Exception { TestContainer.type = BDOC_CONTAINER_TYPE; ContainerBuilder.setContainerImplementation(BDOC_CONTAINER_TYPE, TestContainer.class); SignatureBuilder.setSignatureBuilderForContainerType(BDOC_CONTAINER_TYPE, TestSignatureBuilder.class); Container container = TestDataBuilder.createContainerWithFile(testFolder, BDOC_CONTAINER_TYPE); Signature signature = SignatureBuilder. aSignature(container). withSignatureToken(testSignatureToken). invokeSigning(); assertNotNull(signature); TestContainer.resetType(); } private Signature createBDocSignatureWithProfile(SignatureProfile profile) throws IOException { Container container = TestDataBuilder.createContainerWithFile(testFolder); Signature signature = SignatureBuilder. aSignature(container). withSignatureToken(testSignatureToken). withSignatureProfile(profile). invokeSigning(); container.addSignature(signature); return signature; } private void assertSignatureIsValid(Signature signature) { assertNotNull(signature.getProducedAt()); assertEquals(SignatureProfile.LT_TM, signature.getProfile()); assertNotNull(signature.getClaimedSigningTime()); assertNotNull(signature.getAdESSignature()); assertTrue(signature.getAdESSignature().length > 1); assertTrue(signature.validateSignature().isValid()); } }