Scenarij 8.3: Časovni žig (Timestamp)
Kategorija: Digitalni podpisi
Kompleksnost: ⭐⭐⭐ (Srednja)
Predpogoji: Podpis na voljo, dostop do TSA
Predviden čas: 10-15 minut
Opis
Ta scenarij opisuje dodajanje časovnih žigov digitalnim podpisom (RFC 3161). Časovni žigi dokazujejo, da je podpis obstajal v določenem trenutku in omogočajo:
- Dolgoročna validacija - Podpis ostane veljaven po poteku certifikata
- Dokazljivost - Natančen čas podpisa je dokazan
- Skladnost - Za kvalificirane podpise eIDAS obvezno
Koncept:
- Zgoščena vrednost podpisa se pošlje organu za časovne žige (TSA)
- TSA podpiše zgoščeno vrednost s časovno oznako
- Žeton časovnega žiga se pripne podpisu
Potek dela
flowchart LR
SIG[Podpis] --> HASH[Zgoščena vrednost podpisa]
HASH --> REQ[TSA zahteva]
REQ -->|HTTP POST| TSA[Organ za časovne žige]
TSA --> TOKEN[Žeton časovnega žiga]
TOKEN --> ATTACH[Pripenjanje k podpisu]
ATTACH --> OUTPUT[Podpis s časovnim žigom]
style TSA fill:#e8f5e9
style OUTPUT fill:#e3f2fd
Primer kode: RFC 3161 zahteva za časovni žig
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; using var ctx = PqCryptoContext.Initialize(); // Nalaganje podpisa var signature = File.ReadAllBytes("document.sig"); // Izračun zgoščene vrednosti podpisa var signatureHash = SHA256.HashData(signature); // Ustvarjanje zahteve za časovni žig (RFC 3161) var tsRequest = new Rfc3161TimestampRequest( messageHash: signatureHash, hashAlgorithm: HashAlgorithmName.SHA256, requestedPolicyId: null, // Privzeta politika nonce: GenerateNonce(), requestSignerCertificates: true ); // Pošiljanje zahteve 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(); // Razčlenjevanje odgovora var tsResponse = Rfc3161TimestampToken.Decode(tsResponseBytes, out int bytesConsumed); // Validacija if (!tsResponse.VerifySignatureForHash(signatureHash, HashAlgorithmName.SHA256, out var signerCert, null)) { throw new CryptographicException("Podpis časovnega žiga neveljaven"); } // Shranjevanje žetona File.WriteAllBytes("document.sig.tst", tsResponse.AsSignedCms().Encode()); Console.WriteLine("Časovni žig prejet:"); Console.WriteLine($" Čas: {tsResponse.TokenInfo.Timestamp}"); Console.WriteLine($" TSA: {signerCert.Subject}"); Console.WriteLine($" Politika: {tsResponse.TokenInfo.PolicyId}");
Primer kode: Dodajanje časovnega žiga CMS podpisu
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<SignedCms> AddTimestamp(SignedCms signedCms) { foreach (var signerInfo in signedCms.SignerInfos) { // Zgoščena vrednost podpisa za časovni žig var signatureHash = SHA256.HashData(signerInfo.GetSignature()); // Zahteva za časovni žig var tsRequest = new Rfc3161TimestampRequest( signatureHash, HashAlgorithmName.SHA256, requestedPolicyId: null, nonce: GenerateNonce(), requestSignerCertificates: true ); // Pošiljanje 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 _); // Dodajanje kot nepodpisan atribut 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; } }
Validacija žetona časovnega žiga
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; // Preverjanje zgoščene vrednosti var expectedHash = SHA256.HashData(originalSignature); if (!tsToken.TokenInfo.GetMessageHash().ReadOnlySpan.SequenceEqual(expectedHash)) { result.IsValid = false; result.Error = "Zgoščena vrednost se ne ujema"; return result; } // Preverjanje podpisa if (!tsToken.VerifySignatureForHash(expectedHash, HashAlgorithmName.SHA256, out var tsaCert, trustedTsaCerts)) { result.IsValid = false; result.Error = "TSA podpis neveljaven"; return result; } result.TsaCertificate = tsaCert; // Preverjanje verige certifikatov 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 = "Veriga certifikatov TSA neveljavna"; } } 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; } }
Organi za časovne žige (TSA)
| Ponudnik | URL | Kvalificiran (eIDAS) |
|---|---|---|
| DigiCert | http://timestamp.digicert.com | Ne |
| Sectigo | http://timestamp.sectigo.com | Ne |
| GlobalSign | http://timestamp.globalsign.com | Ne |
| D-TRUST | http://zeitstempel.2.1.3.bundesdruckerei.de | Da |
| SwissSign | http://tsa.swisscom.com/tsa | Da |
| Bundesdruckerei | http://ts.2.bundesdruckerei.de | Da |
eIDAS: Za kvalificirane elektronske podpise je potreben kvalificiran TSA.
Dolgoročno arhiviranje (LTA)
public class LongTermArchive { public async Task ExtendValidation( SignedCms signedCms, string tsaUrl, X509Certificate2Collection validationData) { // 1. Vgraditev validacijskih podatkov (certifikati, CRL-ji, OCSP) AddValidationData(signedCms, validationData); // 2. Dodajanje arhivskega časovnega žiga var timestampService = new TimestampService(tsaUrl); await timestampService.AddArchiveTimestamp(signedCms); // Ta proces se lahko periodično ponavlja, // da podpis ostane dolgoročno preverljiv } }
Panožne zahteve
| Uporaba | Časovni žig obvezen? | Kvalificiran? |
|---|---|---|
| Podpisovanje kode | Obvezno | Ne |
| eIDAS QES | Obvezno | Da |
| PDF/A arhiviranje | Priporočeno | Odvisno od zahtev |
| S/MIME | Opcijsko | Ne |
| Zdravstvo | Obvezno | Odvisno od države |
Povezani scenariji
| Povezava | Scenarij | Opis |
|---|---|---|
| Predpogoj | 8.1 Podpisovanje dokumentov | Ustvarjanje podpisa |
| Predpogoj | 8.2 Podpisovanje kode | Podpis kode |
| Naslednji korak | 8.4 Verifikacija podpisa | Validacija |
« ← 8.2 Podpisovanje kode | ↑ Pregled podpisov | 8.4 Verifikacija podpisa → »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional
Zuletzt geändert: dne 30.01.2026 ob 06:42