Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
rsarendus committed Nov 29, 2021
2 parents 81c98e5 + b88614a commit 2c7d07b
Show file tree
Hide file tree
Showing 51 changed files with 1,311 additions and 381 deletions.
Binary file not shown.
2 changes: 1 addition & 1 deletion .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
20 changes: 20 additions & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
DigiDoc4J Java library release notes
------------------------------------

Release 4.3.0
------------------
Summary of the major changes since 4.2.2
------------------------------------------
* DSS version update to 5.8 (sd-dss.5.8.d4j.1), previously used DSS 5.7. Check changes in DSS here: https://github.com/esig/dss/releases
* CommonOCSPSource improvements of building certificate chain from OCSP certificate to trust anchor in TSL
* ContainerBuilder thread-safety improvements
* TSL TLS truststore update
* Dependencies update

Known issues
------------
* We have noticed a decrease in performance with the introduction of properly accessing AIA certificate resources
* Opening a container that contains signatures, triggers TSL loading (TSL lazy loading does not work as expected)
* While upgrading from versions older than 2.1.1 be sure that your integration :
- doesn't use Xalan or XercesImpl dependencies
- uses a patched Java version (JDK8 or higher)
Xalan and XercesImpl were used to patch XML vulnerabilities in older java versions. They should be discarded with higher versions because they override default Java XML security.
If it is not possible to remove Xalan, then you can set your system property to override TransformerFactory : System.setProperty("javax.xml.transform.TransformerFactory","com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");

Release 4.2.2
------------------
Summary of the major changes since 4.2.1
Expand Down
4 changes: 2 additions & 2 deletions ddoc4j/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>org.digidoc4j</groupId>
<artifactId>ddoc4j</artifactId>
<packaging>jar</packaging>
<version>4.2.2</version>
<version>4.3.0</version>

<name>DDoc4J</name>
<description>DDoc4J is Java Library for validating DDOC documents. It's not recommended to use it directly but rather through DigiDoc4J's API.</description>
Expand All @@ -15,7 +15,7 @@
<parent>
<artifactId>digidoc4j-parent</artifactId>
<groupId>org.digidoc4j</groupId>
<version>4.2.2</version>
<version>4.3.0</version>
</parent>

<dependencies>
Expand Down
20 changes: 10 additions & 10 deletions digidoc4j/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<groupId>org.digidoc4j</groupId>
<artifactId>digidoc4j</artifactId>
<packaging>jar</packaging>
<version>4.2.2</version>
<version>4.3.0</version>

<name>DigiDoc4j</name>
<description>DigiDoc4j is a Java library for digitally signing documents and creating digital signature containers
Expand All @@ -18,16 +18,16 @@
<parent>
<artifactId>digidoc4j-parent</artifactId>
<groupId>org.digidoc4j</groupId>
<version>4.2.2</version>
<version>4.3.0</version>
</parent>

<properties>
<hamcrest.version>2.2</hamcrest.version>
<logback-classic.version>1.2.3</logback-classic.version>
<jackson.version>2.12.4</jackson.version>
<logback-classic.version>1.2.7</logback-classic.version>
<jackson.version>2.13.0</jackson.version>
<junit.version>4.13.2</junit.version>
<dss.groupId>org.digidoc4j.dss</dss.groupId>
<dss.version>5.7.d4j.2</dss.version>
<dss.version>5.8.d4j.1</dss.version>
<dss.util.build>${project.build.directory}/build/util</dss.util.build>
<dss.util.lib>${project.build.directory}/library/util</dss.util.lib>
<dss.zip.lib>${project.build.directory}/library/zip</dss.zip.lib>
Expand All @@ -45,7 +45,7 @@
<dependency>
<artifactId>ddoc4j</artifactId>
<groupId>org.digidoc4j</groupId>
<version>4.2.2</version>
<version>4.3.0</version>
</dependency>

<dependency>
Expand All @@ -64,7 +64,7 @@
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.4</version>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
Expand Down Expand Up @@ -287,7 +287,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.1</version>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -322,8 +322,8 @@
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.27.2</version>
<artifactId>wiremock-jre8</artifactId>
<version>2.31.0</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
78 changes: 15 additions & 63 deletions digidoc4j/src/main/java/org/digidoc4j/ContainerBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,11 @@

import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.digidoc4j.exceptions.DigiDoc4JException;
import org.digidoc4j.exceptions.InvalidDataFileException;
import org.digidoc4j.exceptions.NotSupportedException;
import org.digidoc4j.impl.CustomContainerBuilder;
import org.digidoc4j.impl.asic.AsicContainer;
import org.digidoc4j.impl.asic.AsicParseResult;
import org.digidoc4j.impl.asic.asice.AsicEContainer;
import org.digidoc4j.impl.asic.asice.AsicEContainerBuilder;
import org.digidoc4j.impl.asic.asice.bdoc.BDocContainer;
import org.digidoc4j.impl.asic.asice.bdoc.BDocContainerBuilder;
import org.digidoc4j.impl.asic.asics.AsicSContainer;
import org.digidoc4j.impl.asic.asics.AsicSContainerBuilder;
import org.digidoc4j.impl.ddoc.DDocContainerBuilder;
import org.digidoc4j.impl.pades.PadesContainerBuilder;
Expand All @@ -34,9 +28,9 @@
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Class for creating and opening containers.
Expand All @@ -62,12 +56,12 @@ public abstract class ContainerBuilder {

private static final Logger logger = LoggerFactory.getLogger(ContainerBuilder.class);

protected static Map<String, Class<? extends Container>> containerImplementations = new HashMap<>();
protected static final Map<String, Class<? extends Container>> containerImplementations = new ConcurrentHashMap<>();

protected Configuration configuration;
protected List<ContainerDataFile> dataFiles = new ArrayList<>();
protected String containerFilePath;
protected InputStream containerInputStream;
protected static String containerType;
private DataFile timeStampToken;

/**
Expand All @@ -87,7 +81,6 @@ public static ContainerBuilder aContainer() {
* @return builder for creating a container.
*/
public static ContainerBuilder aContainer(String type) {
ContainerBuilder.containerType = type;
if (ContainerBuilder.isCustomContainerType(type)) {
return new CustomContainerBuilder(type);
} else {
Expand All @@ -107,9 +100,8 @@ public static ContainerBuilder aContainer(String type) {
* @return builder for creating a container.
*/
public static ContainerBuilder aContainer(Container.DocumentType type) {
ContainerBuilder.containerType = type.name();
if (ContainerBuilder.isCustomContainerType(ContainerBuilder.containerType)) {
return new CustomContainerBuilder(ContainerBuilder.containerType);
if (ContainerBuilder.isCustomContainerType(type.name())) {
return new CustomContainerBuilder(type.name());
} else {
switch (type) {
case BDOC:
Expand All @@ -134,9 +126,9 @@ public static ContainerBuilder aContainer(Container.DocumentType type) {
*/
public Container build() {
if (shouldOpenContainerFromFile()) {
return overrideContainerIfNeeded(openContainerFromFile());
return openContainerFromFile();
} else if (shouldOpenContainerFromStream()) {
return overrideContainerIfNeeded(openContainerFromStream());
return openContainerFromStream();
}
Container container = createNewContainer();
addDataFilesToContainer(container);
Expand Down Expand Up @@ -166,10 +158,7 @@ public ContainerBuilder withConfiguration(Configuration configuration) {
* @throws InvalidDataFileException
*/
public ContainerBuilder withDataFile(String filePath, String mimeType) throws InvalidDataFileException {
if (Constant.ASICS_CONTAINER_TYPE.equals(ContainerBuilder.containerType)
&& !dataFiles.isEmpty()){
throw new DigiDoc4JException("Cannot add second file in case of ASICS container");
}
assertAddingDataFileIsSupported();
dataFiles.add(new ContainerDataFile(filePath, mimeType));
return this;
}
Expand All @@ -184,10 +173,7 @@ public ContainerBuilder withDataFile(String filePath, String mimeType) throws In
* @throws InvalidDataFileException
*/
public ContainerBuilder withDataFile(InputStream inputStream, String fileName, String mimeType) throws InvalidDataFileException {
if (Constant.ASICS_CONTAINER_TYPE.equals(ContainerBuilder.containerType)
&& !dataFiles.isEmpty()){
throw new DigiDoc4JException("Cannot add second file in case of ASICS container");
}
assertAddingDataFileIsSupported();
dataFiles.add(new ContainerDataFile(inputStream, fileName, mimeType));
return this;
}
Expand All @@ -201,10 +187,7 @@ public ContainerBuilder withDataFile(InputStream inputStream, String fileName, S
* @throws InvalidDataFileException
*/
public ContainerBuilder withDataFile(File file, String mimeType) throws InvalidDataFileException {
if (Constant.ASICS_CONTAINER_TYPE.equals(ContainerBuilder.containerType)
&& !dataFiles.isEmpty()){
throw new DigiDoc4JException("Cannot add second file in case of ASICS container");
}
assertAddingDataFileIsSupported();
dataFiles.add(new ContainerDataFile(file.getPath(), mimeType));
return this;
}
Expand All @@ -216,10 +199,7 @@ public ContainerBuilder withDataFile(File file, String mimeType) throws InvalidD
* @return builder for creating or opening a container.
*/
public ContainerBuilder withDataFile(DataFile dataFile) {
if (Constant.ASICS_CONTAINER_TYPE.equals(ContainerBuilder.containerType)
&& !dataFiles.isEmpty()){
throw new DigiDoc4JException("Cannot add second file in case of ASICS container");
}
assertAddingDataFileIsSupported();
dataFiles.add(new ContainerDataFile(dataFile));
return this;
}
Expand Down Expand Up @@ -297,6 +277,10 @@ protected Container openContainerFromStream() {
return ContainerOpener.open(containerInputStream, configuration);
}

protected void assertAddingDataFileIsSupported() {
// No assertions by default. Override in derived classes if needed.
}

protected void addDataFilesToContainer(Container container) {
for (ContainerDataFile file : dataFiles) {
if (file.isStream) {
Expand Down Expand Up @@ -376,39 +360,7 @@ private void validateFileName() {
+ Helper.SPECIAL_CHARACTERS);
}
}
}

/**
* DD4J-414 - hackish solution for building BDoc container from existing container with no signatures.
* ContainerOpener considers any Asic container without signatures that is not ASiCS, a ASiCE by default.
* In the future ContainerOpener should take container type as an input to force BDoc when needed.
* At the moment did not want to change ContainerOpener API, that will be done with major release with
* more API changes.
*
* TODO: Should be refactored away in task -
*/
private Container overrideContainerIfNeeded(Container container) {
if (container instanceof AsicContainer && container.getSignatures().isEmpty()) {
return overrideContainerIfDifferentType((AsicContainer) container);
} else {
return container;
}
}

private Container overrideContainerIfDifferentType(AsicContainer container) {
if (container instanceof AsicSContainer || containerType.equalsIgnoreCase(container.getType())) {
return container;
} else {
AsicParseResult containerParseResult = container.getContainerParseResult();
Configuration configuration = container.getConfiguration();

if (containerType.equals(Container.DocumentType.BDOC.name())) {
return new BDocContainer(containerParseResult, configuration);
} else if (containerType.equals(Container.DocumentType.ASICE.name())) {
return new AsicEContainer(containerParseResult, configuration);
} else {
return container;
}
}
}
}
63 changes: 45 additions & 18 deletions digidoc4j/src/main/java/org/digidoc4j/impl/CommonOCSPSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
package org.digidoc4j.impl;

import eu.europa.esig.dss.model.x509.CertificateToken;
import org.apache.commons.collections4.CollectionUtils;
import eu.europa.esig.dss.model.x509.X500PrincipalHelper;
import eu.europa.esig.dss.spi.x509.ListCertificateSource;
import eu.europa.esig.dss.spi.x509.revocation.ocsp.OCSPCertificateSource;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
Expand All @@ -34,9 +36,8 @@

import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Set;

/**
* Created by Janar Rahumeel (CGI Estonia)
Expand Down Expand Up @@ -98,24 +99,50 @@ public Extension createNonce(X509Certificate certificate) {
}

@Override
protected void verifyOcspResponderCertificate(CertificateToken token, Date producedAt) {
verifyValidityDate(token, producedAt);
TSLCertificateSource certificateSource = getConfiguration().getTSL();

if (!certificateSource.isTrusted(token)
&& CollectionUtils.isEmpty(certificateSource.getBySubject(token.getIssuer()))) {
throw CertificateValidationException.of(CertificateValidationException.CertificateValidationStatus.UNTRUSTED,
String.format("OCSP response certificate <%s> match is not found in TSL", token.getDSSIdAsString()));
protected void verifyOcspResponderCertificate(CertificateToken token, BasicOCSPResp ocspResponse) {
TSLCertificateSource tslCertificateSource = getConfiguration().getTSL();

if (tslCertificateSource.isTrusted(token)) {
return;
}
try {
if (!token.getCertificate().getExtendedKeyUsage().contains(OID_OCSP_SIGNING)) {
throw CertificateValidationException.of(CertificateValidationException.CertificateValidationStatus.TECHNICAL,
String.format("OCSP response certificate <%s> does not have 'OCSPSigning' extended key usage", token.getDSSIdAsString()));

OCSPCertificateSource ocspCertificateSource = new OCSPCertificateSource(ocspResponse);
ListCertificateSource allCertificateSources = new ListCertificateSource();
allCertificateSources.add(ocspCertificateSource);
allCertificateSources.add(tslCertificateSource);

CertificateToken currentToken = token;

do {
CertificateToken issuerToken = findIssuerFromCertificateSources(currentToken, allCertificateSources);
if (issuerToken != null && tslCertificateSource.isTrusted(issuerToken)) {
return;
} else if (issuerToken == currentToken) {
break;
}
currentToken = issuerToken;
} while (currentToken != null);

throw CertificateValidationException.of(CertificateValidationException.CertificateValidationStatus.UNTRUSTED,
String.format("OCSP response certificate <%s> match is not found in TSL", token.getDSSIdAsString()));
}

private static CertificateToken findIssuerFromCertificateSources(CertificateToken token, ListCertificateSource certificateSources) {
Set<CertificateToken> candidateIssuers;

if (token.getIssuerX500Principal() != null) {
candidateIssuers = certificateSources.getBySubject(new X500PrincipalHelper(token.getIssuerX500Principal()));
} else {
return null;
}

for (CertificateToken candidateIssuer : candidateIssuers) {
if (token.isSignedBy(candidateIssuer) && candidateIssuer.isValidOn(token.getCreationDate())) {
return candidateIssuer;
}
} catch (CertificateParsingException e) {
throw CertificateValidationException.of(CertificateValidationException.CertificateValidationStatus.TECHNICAL,
String.format("Error on verifying 'OCSPSigning' extended key usage for OCSP response certificate <%s>", token.getDSSIdAsString()), e);
}

return null;
}

@Override
Expand Down
Loading

0 comments on commit 2c7d07b

Please sign in to comment.