Beweisfeste Langzeitarchivierung

03.01.2026

Wie bereits angekündigt werde ich in den nächsten Wochen einige Aspekte asymmetrischer Kryptographie beschreiben. Der vorliegende Artikel erläutert nochmals beweisfeste Langzeitarchivierung und demonstriert die Implementierung in Java.

In meinem 2022 erschienenen Artikel zu diesem Thema habe ich bereits die Notwendigkeit dargelegt, die Authentizität gewisser Informationen auch über Jahrzehnte fortlaufend robust nachweisen zu können. Auch zu den diversen Standards habe ich damals schon berichtet.

Mein Projekt eines konformen RFC3161 Timestamping-Servers ist bei entsprechender Konfiguration in der Lage, entsprechende Zeitstempel zu erzeugen.

In diesen Zeitstempeln sind dann bereits sämtliche Zertifikate der kompletten Kette bis zum Root-Zertifikat für die zur Erstellung des Zeitstempels benutzte Digitale Identität ebenso enthalten, wie alle zugehörigen CRLs bis hinauf zum Root-Zertifikat.

Die Erstellung eines darauf aufbauenden Evidence Record ist zum Beispiel mit folgendem Code möglich:

public static ERSEvidenceRecord create(java.io.File f, java.lang.String serverAddress) throws IOException, TSPException, CMSException, ERSException, OperatorCreationException, NoSuchAlgorithmException, CertificateException
{
    byte[] timestampQuery = TsaClientHelper.makeQuery(f.toURI().toURL(), true, "0.4.0.2023.1.1");
    byte[] timestampResponse= de.elbosso.util.security.TsaClientHelper.getResponse(timestampQuery,serverAddress);
    CLASS_LOGGER.debug("size of response: "+(timestampResponse.length)+" bytes");
    java.io.InputStream inputStream = new java.io.ByteArrayInputStream(timestampResponse);
    org.bouncycastle.tsp.TimeStampResponse timeStampResponse = new org.bouncycastle.tsp.TimeStampResponse(inputStream);
    inputStream.close();
    TimeStampToken tst=timeStampResponse.getTimeStampToken();
    ContentInfo contentInfo=new ContentInfo(new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1.4"), ASN1Primitive.fromByteArray(timeStampResponse.getTimeStampToken().toCMSSignedData().getEncoded("DER")));
    ByteArrayInputStream ioEx=new ByteArrayInputStream(timeStampResponse.getTimeStampToken().toCMSSignedData().getEncoded("DER"));
    ASN1InputStream aIn = new ASN1InputStream(ioEx);
    contentInfo = ContentInfo.getInstance(aIn.readObject());
    CMSSignedData cmsSignedData=new CMSSignedData(contentInfo);
    AlgorithmIdentifier digestAlgorithm = null;
    for(AlgorithmIdentifier aid:cmsSignedData.getDigestAlgorithmIDs())
    {
        digestAlgorithm=aid;
        break;
    }
    ArchiveTimeStamp archiveTimeStamp=new ArchiveTimeStamp(digestAlgorithm,new PartialHashtree[0],contentInfo);
    ArchiveTimeStampChain archiveTimeStampChain=new ArchiveTimeStampChain(archiveTimeStamp);
    ArchiveTimeStampSequence archiveTimeStampSequence=new ArchiveTimeStampSequence(archiveTimeStampChain);
    EvidenceRecord evidenceRecord=new EvidenceRecord(new AlgorithmIdentifier[]{digestAlgorithm},null,null,archiveTimeStampSequence);
    DigestCalculatorProvider digProvider = new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build();
    ERSEvidenceRecord ersEvidenceRecord=new ERSEvidenceRecord(evidenceRecord.getEncoded(),digProvider);
    return ersEvidenceRecord;
}

Die Verifikation ist ebenso einfach und schnell erledigt:

public static void validate(java.io.File f,ERSEvidenceRecord ersEvidenceRecord) throws IOException, TSPException, CMSException, ERSException, OperatorCreationException, NoSuchAlgorithmException, CertificateException
{
    SignerId signerID =  ersEvidenceRecord.getLastArchiveTimeStamp().getTimeStampToken().getSID();
    Store allCertificates = ersEvidenceRecord.getLastArchiveTimeStamp().getTimeStampToken().getCertificates();
    Collection signerCertificates = allCertificates.getMatches(signerID);
    if (signerCertificates.isEmpty()) {
        // Response without certificate
        Utilities.SYSTEMOUT_LOGGER.error("No signer certificate in response. If you are"
                + " using openssl for request generation try -cert.");
        System.exit(3);
    }
    X509CertificateHolder certHolder = null;
    for (Object match : signerCertificates) {
        certHolder = (X509CertificateHolder) match;
        break;
    }
    assert certHolder != null : "certHolder can't be null because the "
            + "collection is not empty.";
    X509Certificate tsaCert = new JcaX509CertificateConverter()
            .setProvider(BC).getCertificate(certHolder);
    SignerInformationVerifier siv = new JcaSimpleSignerInfoVerifierBuilder()
            .setProvider(BC).build(tsaCert);
    ersEvidenceRecord.getLastArchiveTimeStamp().getTimeStampToken().validate(siv);
    CLASS_LOGGER.info("trying to validate");
    ersEvidenceRecord.validate(siv);
    byte[] tokenFingerPrint=ersEvidenceRecord.getLastArchiveTimeStamp().getTimeStampToken().getTimeStampInfo().getMessageImprintDigest();
    java.io.InputStream is = new java.io.FileInputStream(f);;
    java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
    de.elbosso.util.io.Utilities.copyBetweenStreams(is, baos, true);
    java.security.MessageDigest digest = java.security.MessageDigest.getInstance(TSPAlgorithms.SHA512.toString());
    byte[] in = baos.toByteArray();
    byte[] out = digest.digest(in);
    is.close();
    if(java.util.Arrays.equals(tokenFingerPrint,out))
        CLASS_LOGGER.info("validation successfull!");
    else
        CLASS_LOGGER.info("validation NOT successfull!");
}

Artikel, die hierher verlinken

Asymmetrische Kryptographie

28.03.2026

Ich habe mich mit der Idee schon länger getragen: Nochmal einen Rundumschlag zu asymmetrischer Kryptographie zu machen. Dabei werde ich mich auf Demonstrationen der einzelnen Konzepte und Operationen mit Beispielcode konzentrieren und zu jedem der vorgestellten Konzepte mehr oder weniger ausführlich bezüglich der Einsatzszenarien und Vor- und Nachteile Stellung beziehen

Code Signing

22.03.2026

Wie bereits angekündigt werde ich in den nächsten Wochen einige Aspekte asymmetrischer Kryptographie beschreiben. Der vorliegende Artikel erläutert einige Kernaspekte des Code Signing unter anderem mit Blick auf die Verifikation dieser Signaturen in unterschiedlichen Betriebssystemen und Laufzeitumgebungen.

Alle Artikel rss Wochenübersicht Monatsübersicht Codeberg Repositories Mastodon Über mich home xmpp


Vor 5 Jahren hier im Blog

  • xBrowserSync in Docker

    29.03.2021

    Nachdem ich schon längere Zeit nicht mehr über neue Dienste in meinem Docker-Zoo berichtet habe, habe ich in der vergangenen Woche wieder einmal einen Neuzugang begrüßen dürfen...

    Weiterlesen

Neueste Artikel

  • Asymmetrische Kryptographie

    Ich habe mich mit der Idee schon länger getragen: Nochmal einen Rundumschlag zu asymmetrischer Kryptographie zu machen. Dabei werde ich mich auf Demonstrationen der einzelnen Konzepte und Operationen mit Beispielcode konzentrieren und zu jedem der vorgestellten Konzepte mehr oder weniger ausführlich bezüglich der Einsatzszenarien und Vor- und Nachteile Stellung beziehen

    Weiterlesen
  • Windows? Nur noch gegen Bezahlung!

    Ich habe mich nun völlig von Windows - der armseligen Ausrede für ein Computerbetriebssystem aus Redmond - abgenabelt

    Weiterlesen
  • Vergleich Analoger und Digitaler Identitäten

    Eine Präsentation zum besseren Verständnis der Konzepte hinter digitalen Identitäten

    Aktualisierung vom 16. März 2025

    Aktualisierung der Präsentation mit einem Beispiel aus einem Film der 1980er Jahre und Betonung des Fakts, dass das Subjekt überhaupt nicht bemerken muss, dass eine Identität erstellt wird...

    Aktualisierung vom 17. August 2025

    Ein weiteres Beispiel wurde hinzugefügt.

    Aktualisierung vom 30. März 2026

    Aktualisierung der Präsentation: Erläuterung der Möglichkeit, mehr als ein Zertifikat für dasselbe Schlüsselpaar auszustellen und Exkurs zu Transport Layer Security als Beispiel der Forderung des Vorweisens bestimmter Arten digitaler Identitäten.
    Weiterlesen

Manche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.

Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.

Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...

PS: Meine öffentlichen Codeberg-Repositories findet man hier.