Description
Hi guys,
our application creates Evidence Records (RFC4998) for documents at some point for long-term storage.
We use the BC implementation of ERSArchiveTimeStampGenerator
Today we decided to change to a different TSA for signing the root hash of the hash-tree.
We noticed, that some TSAs (e.g. DigiCert http://timestamp.digicert.com) return the AlgorithmIdentifier in their TimeStampResponse with an explicit DERNull parameter, while the DigestCalculator on ERSArchiveTimeStampGenerator uses absent (null) parameters if initalised with the constructor
ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);
This will trigger an ERSException("time stamp imprint for wrong algorithm") from ERSArchiveTimeStampGenerator because it compares the AlgorithmIdentifier objects with equals(), which would fail in this case.
This should be covered by BC implementation, or am I misunderstanding something?
Any help is appreciated :)
Environment
Relevant code in ERSArchiveTimeStampGenerator didn't change compared to latest version, so the behaviour should be reproducable.
Test
import com.fsastech.thesira.apps.mimir.krypto.application.components.OnlineTSPSourceExt;
import com.fsastech.thesira.apps.mimir.shared.utils.HashAlgorithmOIDs;
import com.fsastech.thesira.apps.mimir.shared.utils.HexSerializer;
import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.tsp.MessageImprint;
import org.bouncycastle.asn1.tsp.TSTInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.ers.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.jupiter.api.Assertions.*;
@Test
public void generateArchiveTimeStamp_fails_for_some_tsa() throws TSPException, IOException, OperatorCreationException, ERSException {
// TSA URL
String url = "http://timestamp.digicert.com";
// Some random data to sign
String someRandomStringData = "Sample data for timestamping";
// Prepare the Data for the hash-tree
ERSData req = new ERSByteData(someRandomStringData.getBytes());
ERSDataGroup ersDataGroup = new ERSDataGroup(req);
// init the ERSArchiveTimeStampGenerator
ASN1ObjectIdentifier hashAlg = HashAlgorithmOIDs.getNistObjectIdentifierByOID("2.16.840.1.101.3.4.2.1"); //<=SHA256
DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(hashAlg));
ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);
// At this point ERSArchiveTimeStampGenerator.digiCalc.algorithm.parameters is null
// Add the ers-data to the generator
ersGen.addData(ersDataGroup);
// Prepare the TimeStampRequest
OnlineTSPSourceExt tspSource = new OnlineTSPSourceExt(url);
TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();
TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);
// Get the root hash
MessageImprint rootHash = tspReq.getMessageImprint();
// trigger the actual request to the TSA
tspSource.getTimeStampResponse(DigestAlgorithm.SHA256, rootHash.getHashedMessage());
// Get the TimeStampResponse
TimeStampResponse timeStampResponse = tspSource.getTimeStampResponse();
// try to build the ArchiveTimestampStamps to be used later in the Evidence Records
AtomicReference<List<ERSArchiveTimeStamp>> atss = new AtomicReference<>(new ArrayList<>());
// Expect the ERSException to be thrown due to time stamp imprint for wrong algorithm
assertThrows(ERSException.class, () -> atss.set(ersGen.generateArchiveTimeStamps(timeStampResponse)), "time stamp imprint for wrong algorithm");
assertTrue(atss.get().isEmpty());
// Expect the ERSException was thrown because getParameters is not null
TSTInfo tstInfo = timeStampResponse.getTimeStampToken().getTimeStampInfo().toASN1Structure();
assertTrue(tstInfo.getMessageImprint().getHashAlgorithm().getParameters() != null);
}
Description
Hi guys,
our application creates Evidence Records (RFC4998) for documents at some point for long-term storage.
We use the BC implementation of ERSArchiveTimeStampGenerator
Today we decided to change to a different TSA for signing the root hash of the hash-tree.
We noticed, that some TSAs (e.g. DigiCert http://timestamp.digicert.com) return the AlgorithmIdentifier in their TimeStampResponse with an explicit DERNull parameter, while the DigestCalculator on ERSArchiveTimeStampGenerator uses absent (null) parameters if initalised with the constructor
ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);This will trigger an ERSException("time stamp imprint for wrong algorithm") from ERSArchiveTimeStampGenerator because it compares the AlgorithmIdentifier objects with equals(), which would fail in this case.
This should be covered by BC implementation, or am I misunderstanding something?
Any help is appreciated :)
Environment
Relevant code in ERSArchiveTimeStampGenerator didn't change compared to latest version, so the behaviour should be reproducable.
Test