~~NOTOC~~ ====== Scenario 8.3: Timestamp ====== **Categoria:** [[.:start|Firme digitali]] \\ **Complessità:** Media \\ **Prerequisiti:** Firma presente, accesso TSA \\ **Tempo stimato:** 10-15 minuti ---- ===== Descrizione ===== Questo scenario descrive l'**aggiunta di timestamp** alle firme digitali (RFC 3161). I timestamp dimostrano che una firma esisteva in un determinato momento e consentono: * **Validazione a lungo termine** - La firma rimane valida dopo la scadenza del certificato * **Dimostrabilità** - Momento esatto della firma documentato * **Conformità** - Richiesto per firme qualificate eIDAS **Concetto:** * L'hash della firma viene inviato alla Timestamp Authority (TSA) * La TSA firma l'hash con indicazione temporale * Il Timestamp-Token viene allegato alla firma ---- ===== Workflow ===== flowchart LR SIG[Firma] --> HASH[Hash della firma] HASH --> REQ[Richiesta TSA] REQ -->|HTTP POST| TSA[Timestamp Authority] TSA --> TOKEN[Timestamp Token] TOKEN --> ATTACH[Allegare alla firma] ATTACH --> OUTPUT[Firma con timestamp] style TSA fill:#e8f5e9 style OUTPUT fill:#e3f2fd ---- ===== Esempio codice: Richiesta Timestamp RFC 3161 ===== using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; using var ctx = PqCryptoContext.Initialize(); // Caricare firma var signature = File.ReadAllBytes("document.sig"); // Calcolare hash della firma var signatureHash = SHA256.HashData(signature); // Creare richiesta Timestamp (RFC 3161) var tsRequest = new Rfc3161TimestampRequest( messageHash: signatureHash, hashAlgorithm: HashAlgorithmName.SHA256, requestedPolicyId: null, // Policy predefinita nonce: GenerateNonce(), requestSignerCertificates: true ); // Inviare richiesta alla TSA using var http = new HttpClient(); var content = new ByteArrayContent(tsRequest.Encode()); content.Headers.ContentType = new MediaTypeHeaderValue("application/timestamp-query"); var response = await http.PostAsync("http://timestamp.digicert.com", content); var tsResponseBytes = await response.Content.ReadAsByteArrayAsync(); // Analizzare risposta var tsResponse = Rfc3161TimestampToken.Decode(tsResponseBytes, out int bytesConsumed); // Validare if (!tsResponse.VerifySignatureForHash(signatureHash, HashAlgorithmName.SHA256, out var signerCert, null)) { throw new CryptographicException("Firma timestamp non valida"); } // Salvare token File.WriteAllBytes("document.sig.tst", tsResponse.AsSignedCms().Encode()); Console.WriteLine("Timestamp ricevuto:"); Console.WriteLine($" Ora: {tsResponse.TokenInfo.Timestamp}"); Console.WriteLine($" TSA: {signerCert.Subject}"); Console.WriteLine($" Policy: {tsResponse.TokenInfo.PolicyId}"); ---- ===== Esempio codice: Aggiungere timestamp a firma CMS ===== public class TimestampService { private readonly string _tsaUrl; private readonly HttpClient _http; public TimestampService(string tsaUrl) { _tsaUrl = tsaUrl; _http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) }; } public async Task AddTimestamp(SignedCms signedCms) { foreach (var signerInfo in signedCms.SignerInfos) { // Hash firma per timestamp var signatureHash = SHA256.HashData(signerInfo.GetSignature()); // Richiesta timestamp var tsRequest = new Rfc3161TimestampRequest( signatureHash, HashAlgorithmName.SHA256, requestedPolicyId: null, nonce: GenerateNonce(), requestSignerCertificates: true ); // Inviare alla TSA var content = new ByteArrayContent(tsRequest.Encode()); content.Headers.ContentType = new MediaTypeHeaderValue("application/timestamp-query"); var response = await _http.PostAsync(_tsaUrl, content); response.EnsureSuccessStatusCode(); var tsResponseBytes = await response.Content.ReadAsByteArrayAsync(); var tsToken = Rfc3161TimestampToken.Decode(tsResponseBytes, out _); // Aggiungere come Unsigned Attribute var timestampAttr = new AsnEncodedData( new Oid("1.2.840.113549.1.9.16.2.14"), // id-aa-timeStampToken tsToken.AsSignedCms().Encode() ); signerInfo.AddUnsignedAttribute(timestampAttr); } return signedCms; } private byte[] GenerateNonce() { var nonce = new byte[8]; RandomNumberGenerator.Fill(nonce); return nonce; } } ---- ===== Validare Timestamp-Token ===== public class TimestampValidator { public TimestampValidationResult Validate( byte[] timestampToken, byte[] originalSignature, X509Certificate2Collection? trustedTsaCerts = null) { var result = new TimestampValidationResult(); try { var tsToken = Rfc3161TimestampToken.Decode(timestampToken, out _); result.Timestamp = tsToken.TokenInfo.Timestamp; result.PolicyId = tsToken.TokenInfo.PolicyId?.Value; // Verificare hash var expectedHash = SHA256.HashData(originalSignature); if (!tsToken.TokenInfo.GetMessageHash().ReadOnlySpan.SequenceEqual(expectedHash)) { result.IsValid = false; result.Error = "Hash non corrisponde"; return result; } // Verificare firma if (!tsToken.VerifySignatureForHash(expectedHash, HashAlgorithmName.SHA256, out var tsaCert, trustedTsaCerts)) { result.IsValid = false; result.Error = "Firma TSA non valida"; return result; } result.TsaCertificate = tsaCert; // Verificare catena certificati TSA var chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; if (trustedTsaCerts != null) { chain.ChainPolicy.CustomTrustStore.AddRange(trustedTsaCerts); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; } result.IsValid = chain.Build(tsaCert); if (!result.IsValid) { result.Error = "Catena certificati TSA non valida"; } } catch (Exception ex) { result.IsValid = false; result.Error = ex.Message; } return result; } } public class TimestampValidationResult { public bool IsValid { get; set; } public DateTimeOffset? Timestamp { get; set; } public string? PolicyId { get; set; } public X509Certificate2? TsaCertificate { get; set; } public string? Error { get; set; } } ---- ===== Timestamp Authorities (TSA) ===== ^ Fornitore ^ URL ^ Qualificato (eIDAS) ^ | DigiCert | http://timestamp.digicert.com | No | | Sectigo | http://timestamp.sectigo.com | No | | GlobalSign | http://timestamp.globalsign.com | No | | D-TRUST | http://zeitstempel.2.1.3.bundesdruckerei.de | **Si** | | SwissSign | http://tsa.swisscom.com/tsa | **Si** | | Bundesdruckerei | http://ts.2.bundesdruckerei.de | **Si** | **eIDAS:** Per firme elettroniche qualificate è richiesta una TSA qualificata. ---- ===== Archiviazione a lungo termine (LTA) ===== public class LongTermArchive { public async Task ExtendValidation( SignedCms signedCms, string tsaUrl, X509Certificate2Collection validationData) { // 1. Incorporare dati di validazione (certificati, CRL, OCSP) AddValidationData(signedCms, validationData); // 2. Aggiungere Archive Timestamp var timestampService = new TimestampService(tsaUrl); await timestampService.AddArchiveTimestamp(signedCms); // Questo processo può essere ripetuto periodicamente // per mantenere la firma validabile a lungo termine } } ---- ===== Requisiti specifici per settore ===== ^ Applicazione ^ Timestamp richiesto? ^ Qualificato? ^ | **Code Signing** | Obbligatorio | No | | **eIDAS QES** | Obbligatorio | Si | | **Archiviazione PDF/A** | Raccomandato | Secondo requisiti | | **S/MIME** | Opzionale | No | | **Sanità** | Obbligatorio | Secondo paese | ---- ===== Scenari correlati ===== ^ Relazione ^ Scenario ^ Descrizione ^ | **Prerequisito** | [[.:dokument_signieren|8.1 Firmare documento]] | Creare firma | | **Prerequisito** | [[.:code_signieren|8.2 Firmare codice]] | Firma codice | | **Passo successivo** | [[.:signatur_verifizieren|8.4 Verificare firma]] | Validazione | ---- << [[.:code_signieren|← 8.2 Firmare codice]] | [[.:start|↑ Panoramica firme]] | [[.:signatur_verifizieren|8.4 Verificare firma →]] >> {{tag>scenario firma timestamp tsa rfc3161 lungo-termine}} ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//