~~NOTOC~~
====== Scenarij 8.3: Časovni žig (Timestamp) ======
**Kategorija:** [[.:start|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 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** | [[.:dokument_signieren|8.1 Podpisovanje dokumentov]] | Ustvarjanje podpisa |
| **Predpogoj** | [[.:code_signieren|8.2 Podpisovanje kode]] | Podpis kode |
| **Naslednji korak** | [[.:signatur_verifizieren|8.4 Verifikacija podpisa]] | Validacija |
----
<< [[.:code_signieren|← 8.2 Podpisovanje kode]] | [[.:start|↑ Pregled podpisov]] | [[.:signatur_verifizieren|8.4 Verifikacija podpisa →]] >>
{{tag>scenarij podpis časovni-žig tsa rfc3161 dolgoročno}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//