Skip to content

Commit

Permalink
Fix for ECDSA+LT_TM signature finalization, needs additional analyse
Browse files Browse the repository at this point in the history
Signed-off-by: Indrek Jentson <[email protected]>
  • Loading branch information
Indrek Jentson committed Nov 20, 2017
1 parent 84b9f7f commit 3dff975
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 56 deletions.
9 changes: 9 additions & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
DigiDoc4J Java library release notes
------------------------------------
Release 1.0.7.1
------------------
Summary of the major changes since 1.0.7
----------------------------------------
* Fixed signing problem when encryption algorithm is ECDSA and signature profile LT_TM.
* Updated the pre-calculated policy digest value.

------------------------------------
Release 1.0.7
------------------
Summary of the major changes since 1.0.7 RC.2
----------------------------------------
* Performance test fix

------------------------------------
Release 1.0.7 RC.2
------------------
Expand Down
105 changes: 87 additions & 18 deletions src/org/digidoc4j/impl/bdoc/BDocSignatureBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@
import static eu.europa.esig.dss.SignatureLevel.XAdES_BASELINE_B;
import static eu.europa.esig.dss.SignatureLevel.XAdES_BASELINE_LT;
import static eu.europa.esig.dss.SignatureLevel.XAdES_BASELINE_LTA;
import static java.lang.Math.min;
import static org.apache.commons.codec.binary.Base64.decodeBase64;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.digidoc4j.impl.bdoc.ocsp.OcspSourceBuilder.anOcspSource;

import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.digidoc4j.Configuration;
import org.digidoc4j.DataFile;
Expand All @@ -37,6 +41,7 @@
import org.digidoc4j.exceptions.InvalidSignatureException;
import org.digidoc4j.exceptions.OCSPRequestFailedException;
import org.digidoc4j.exceptions.SignerCertificateRequiredException;
import org.digidoc4j.exceptions.TechnicalException;
import org.digidoc4j.impl.SignatureFinalizer;
import org.digidoc4j.impl.bdoc.asic.DetachedContentCreator;
import org.digidoc4j.impl.bdoc.ocsp.SKOnlineOCSPSource;
Expand All @@ -52,20 +57,95 @@
import eu.europa.esig.dss.Policy;
import eu.europa.esig.dss.SignerLocation;
import eu.europa.esig.dss.client.tsp.OnlineTSPSource;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.xades.signature.DSSSignatureUtils;

/**
* Class for building and finalizing BDOC signatures.
*/
public class BDocSignatureBuilder extends SignatureBuilder implements SignatureFinalizer {

private final static Logger logger = LoggerFactory.getLogger(BDocSignatureBuilder.class);
private static final Logger logger = LoggerFactory.getLogger(BDocSignatureBuilder.class);
private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
private static final int hexMaxlen = 10;
private transient XadesSigningDssFacade facade;
private Date signingDate;
private static final int maxTryCount = 5;

/**
* Prepare signature policy data for BDOC signature.
*
* @return Policy
*/
public static Policy createBDocSignaturePolicy() {
if (policyDefinedByUser != null && isDefinedAllPolicyValues()) {
return policyDefinedByUser;
}
Policy signaturePolicy = new Policy();
signaturePolicy.setId("urn:oid:" + XadesSignatureValidator.TM_POLICY);
signaturePolicy.setDigestValue(decodeBase64("0xRLPsW1UIpxtermnTGE+5+5620UsWi5bYJY76Di3o0="));
signaturePolicy.setQualifier("OIDAsURN");
signaturePolicy.setDigestAlgorithm(SHA256);
signaturePolicy.setSpuri("https://www.sk.ee/repository/bdoc-spec21.pdf");
return signaturePolicy;
}

/**
* Checks if the signature is ASN.1 encoded.
*
* @param signatureValue signature value to check.
* @return if the signature is ASN.1 encoded.
*/
private static boolean isAsn1Encoded(byte[] signatureValue) {
ASN1InputStream is = null;
try {
is = new ASN1InputStream(signatureValue);
ASN1Primitive obj = is.readObject();
return obj != null;
} catch (IOException e) {
return false;
} finally {
Utils.closeQuietly(is);
}
}

private static String bytesToHex(byte[] bytes, int maxLen) {
char[] hexChars = new char[min(bytes.length, maxLen) * 2];
for (int j = 0; j < min(bytes.length, maxLen); j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}

@Override
protected Signature invokeSigningProcess() {
logger.info("Signing BDoc container");
signatureParameters.setSigningCertificate(signatureToken.getCertificate());
byte[] dataToSign = getDataToBeSigned();
byte[] signatureValue = signatureToken.sign(signatureParameters.getDigestAlgorithm(), dataToSign);
return finalizeSignature(signatureValue);
Signature result = null;
byte[] signatureValue = null;
int count = 0;
boolean finalized = false;
while (!finalized && count < maxTryCount) {
try {
// TODO: Investigate instability (of BouncyCastle?)
// Sometimes sign returns value what causes error in finalizeSignature
signatureValue = signatureToken.sign(signatureParameters.getDigestAlgorithm(), dataToSign);
if (signatureParameters.getEncryptionAlgorithm() == EncryptionAlgorithm.ECDSA
&& isAsn1Encoded(signatureValue)) {
signatureValue = DSSSignatureUtils.convertToXmlDSig(eu.europa.esig.dss.EncryptionAlgorithm.ECDSA, signatureValue);
}
result = finalizeSignature(signatureValue);
finalized = true;
} catch (TechnicalException e) {
logger.warn("PROBLEM with signing [" + String.valueOf(count) + "]:" +
bytesToHex(dataToSign, hexMaxlen) + " -> " + bytesToHex(signatureValue, hexMaxlen));
count++;
}
}
return result;
}

@Override
Expand All @@ -87,7 +167,7 @@ public Signature openAdESSignature(byte[] signatureDocument) {

@Override
public Signature finalizeSignature(byte[] signatureValueBytes) {
logger.info("Finalizing BDoc signature");
logger.info("Finalizing BDoc signature: " + bytesToHex(signatureValueBytes, hexMaxlen));
populateParametersForFinalizingSignature(signatureValueBytes);
Collection<DataFile> dataFilesToSign = getDataFiles();
validateDataFilesToSign(dataFilesToSign);
Expand Down Expand Up @@ -174,7 +254,9 @@ private void validateOcspResponse(XadesSignature xadesSignature) {
}

private boolean isBaselineSignatureProfile() {
return signatureParameters.getSignatureProfile() != null && (SignatureProfile.B_BES == signatureParameters.getSignatureProfile() || SignatureProfile.B_EPES == signatureParameters.getSignatureProfile());
return signatureParameters.getSignatureProfile() != null
&& (SignatureProfile.B_BES == signatureParameters.getSignatureProfile()
|| SignatureProfile.B_EPES == signatureParameters.getSignatureProfile());
}

private void setOcspSource(byte[] signatureValueBytes) {
Expand Down Expand Up @@ -249,19 +331,6 @@ private void setSignaturePolicy() {
}
}

public static Policy createBDocSignaturePolicy() {
if (policyDefinedByUser != null && isDefinedAllPolicyValues()) {
return policyDefinedByUser;
}
Policy signaturePolicy = new Policy();
signaturePolicy.setId("urn:oid:" + XadesSignatureValidator.TM_POLICY);
signaturePolicy.setDigestValue(decodeBase64("3Tl1oILSvOAWomdI9VeWV6IA/32eSXRUri9kPEz1IVs="));
signaturePolicy.setQualifier("OIDAsURN");
signaturePolicy.setDigestAlgorithm(SHA256);
signaturePolicy.setSpuri("https://www.sk.ee/repository/bdoc-spec21.pdf");
return signaturePolicy;
}

private void setSignatureId() {
if (StringUtils.isNotBlank(signatureParameters.getSignatureId())) {
facade.setSignatureId(signatureParameters.getSignatureId());
Expand Down
23 changes: 18 additions & 5 deletions src/org/digidoc4j/impl/bdoc/OcspNonceValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.slf4j.Logger;
Expand All @@ -30,18 +31,29 @@
import eu.europa.esig.dss.DigestAlgorithm;
import eu.europa.esig.dss.xades.validation.XAdESSignature;

/**
* Validator of OCSP response NONCE extension
*/
public class OcspNonceValidator implements Serializable {

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

private XAdESSignature signature;
private BasicOCSPResp ocspResponse;

/**
* Constructor of the validator
* @param signature Xades signature object
*/
public OcspNonceValidator(XAdESSignature signature) {
this.signature = signature;
ocspResponse = getLatestOcspResponse(signature.getOCSPSource().getContainedOCSPResponses());
}

/**
* Method for asking if OCSP response is valid or not.
* @return True if OCSP response is valid, false otherwise.
*/
public boolean isValid() {
if (signature.getPolicyId() == null) {
return true;
Expand All @@ -62,17 +74,18 @@ private BasicOCSPResp getLatestOcspResponse(List<BasicOCSPResp> ocspResponses) {
Date latestDate = basicOCSPResp.getProducedAt();

for (int i = 1; i < ocspResponses.size(); i++) {
BasicOCSPResp ocspResponse = ocspResponses.get(i);
if (ocspResponse.getProducedAt().after(latestDate)) {
latestDate = ocspResponse.getProducedAt();
basicOCSPResp = ocspResponse;
BasicOCSPResp ocspResp = ocspResponses.get(i);
if (ocspResp.getProducedAt().after(latestDate)) {
latestDate = ocspResp.getProducedAt();
basicOCSPResp = ocspResp;
}
}
return basicOCSPResp;
}

private boolean isOcspResponseValid(BasicOCSPResp latestOcspResponse) {
Extension extension = latestOcspResponse.getExtension(new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.2"));
Extension extension = latestOcspResponse.getExtension(
new ASN1ObjectIdentifier(OCSPObjectIdentifiers.id_pkix_ocsp_nonce.getId()));
if (extension == null) {
logger.error("No valid OCSP extension found in signature: " + signature.getId());
return false;
Expand Down
9 changes: 9 additions & 0 deletions src/org/digidoc4j/impl/bdoc/ocsp/BDocTMOcspSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,20 @@

import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.DigestAlgorithm;
import eu.europa.esig.dss.xades.signature.DSSSignatureUtils;

/**
* BDocTMOcspSource is class for creating BDoc TM specific NONCE.
*/
public class BDocTMOcspSource extends SKOnlineOCSPSource {
private static final Logger logger = LoggerFactory.getLogger(SKOnlineOCSPSource.class);
private final byte[] signature;

/**
* Constructor.
* @param configuration Configuration.
* @param signature Signature value without DER prefixes.
*/
public BDocTMOcspSource(Configuration configuration, byte[] signature) {
super(configuration);
this.signature = signature;
Expand Down
5 changes: 5 additions & 0 deletions src/org/digidoc4j/impl/bdoc/ocsp/SKOnlineOCSPSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.digidoc4j.Configuration;
import org.digidoc4j.exceptions.ConfigurationException;
import org.digidoc4j.exceptions.DigiDoc4JException;
import org.digidoc4j.impl.bdoc.OcspNonceValidator;
import org.digidoc4j.impl.bdoc.SkDataLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -204,6 +205,10 @@ SkDataLoader getDataLoader() {
return dataLoader;
}

/**
* Define data loader.
* @param dataLoader Data loader object to be used.
*/
public void setDataLoader(SkDataLoader dataLoader) {
this.dataLoader = dataLoader;
}
Expand Down
33 changes: 20 additions & 13 deletions src/org/digidoc4j/impl/bdoc/xades/XadesSigningDssFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.List;

import org.digidoc4j.DataFile;
import org.digidoc4j.exceptions.TechnicalException;
import org.digidoc4j.impl.bdoc.SKCommonCertificateVerifier;
import org.digidoc4j.impl.bdoc.asic.DetachedContentCreator;
import org.slf4j.Logger;
Expand All @@ -26,6 +27,7 @@

import eu.europa.esig.dss.BLevelParameters;
import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DigestAlgorithm;
import eu.europa.esig.dss.DomUtils;
import eu.europa.esig.dss.EncryptionAlgorithm;
Expand All @@ -36,18 +38,19 @@
import eu.europa.esig.dss.SignatureValue;
import eu.europa.esig.dss.SignerLocation;
import eu.europa.esig.dss.ToBeSigned;
import eu.europa.esig.dss.signature.DocumentSignatureService;
import eu.europa.esig.dss.asic.ASiCNamespace;
import eu.europa.esig.dss.validation.CertificateVerifier;
import eu.europa.esig.dss.x509.CertificateSource;
import eu.europa.esig.dss.x509.CertificateToken;
import eu.europa.esig.dss.x509.ocsp.OCSPSource;
import eu.europa.esig.dss.x509.tsp.TSPSource;
import eu.europa.esig.dss.asic.ASiCNamespace;

import eu.europa.esig.dss.xades.DSSXMLUtils;
import eu.europa.esig.dss.xades.XAdESSignatureParameters;
import eu.europa.esig.dss.xades.signature.XAdESService;

/**
* Facade class for DSS Xades signing functionality.
*/
public class XadesSigningDssFacade {

private static final Logger logger = LoggerFactory.getLogger(XadesSigningDssFacade.class);
Expand All @@ -74,14 +77,18 @@ public byte[] getDataToSign(Collection<DataFile> dataFiles) {

public DSSDocument signDocument(byte[] signatureValue, Collection<DataFile> dataFiles) {
logger.debug("Signing document with DSS");
SignatureValue dssSignatureValue = new SignatureValue(xAdESSignatureParameters.getSignatureAlgorithm(), signatureValue);
DetachedContentCreator detachedContentCreator = new DetachedContentCreator().populate(dataFiles);
DSSDocument dssDocument = detachedContentCreator.getFirstDetachedContent();
List<DSSDocument> detachedContentList = detachedContentCreator.getDetachedContentList();
logger.debug("Signature parameters: " + xAdESSignatureParameters.toString());
xAdESSignatureParameters.setDetachedContents(detachedContentCreator.getDetachedContentList());
DSSDocument signedDocument = xAdESService.signDocument(detachedContentList, xAdESSignatureParameters, dssSignatureValue);
logger.debug("Finished signing document with DSS");
SignatureValue dssSignatureValue = new SignatureValue(xAdESSignatureParameters.getSignatureAlgorithm(), signatureValue);
DSSDocument signedDocument = null;
try {
signedDocument = xAdESService.signDocument(detachedContentList, xAdESSignatureParameters, dssSignatureValue);
} catch (DSSException e) {
logger.warn("Signing document with DSS failed:" + e.getMessage());
throw new TechnicalException("Got error in signing process");
}
DSSDocument correctedSignedDocument = surroundWithXadesXmlTag(signedDocument);
return correctedSignedDocument;
}
Expand Down Expand Up @@ -143,25 +150,25 @@ public void setSignatureLevel(SignatureLevel signatureLevel) {
xAdESSignatureParameters.setSignatureLevel(signatureLevel);
}

public String getSignatureId() {
return xAdESSignatureParameters.getDeterministicId();
}

public void setSignatureId(String signatureId) {
logger.debug("Setting deterministic id: " + signatureId);
//TODO find solution for method setDeterministicId(...)
xAdESSignatureParameters.setDeterministicId(signatureId);
}

public String getSignatureId() {
return xAdESSignatureParameters.getDeterministicId();
}

public void setSigningDate(Date signingDate) {
xAdESSignatureParameters.getBLevelParams().setSigningDate(signingDate);
}

public void setEn319132(boolean isSigningCertificateV2){
public void setEn319132(boolean isSigningCertificateV2) {
xAdESSignatureParameters.setEn319132(isSigningCertificateV2);
}

public void getEn319132(){
public void getEn319132() {
xAdESSignatureParameters.isEn319132();
}

Expand Down
Loading

0 comments on commit 3dff975

Please sign in to comment.