Skip to content

Generate ArchiveTimeStamp fails for some tsa #2379

Description

@jmeeder

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

  • bcpkix-jdk18on-1.81.jar

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);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions