Scenarij 8.4: Verifikacija potpisa

Kategorija: Digitalni potpisi
Složenost: ⭐⭐⭐ (Srednja)
Preduvjeti: Potpisani podaci, certifikat za potpis
Procijenjeno vrijeme: 10-15 minuta


Opis

Ovaj scenarij opisuje potpunu verifikaciju digitalnih potpisa. Ispravna verifikacija obuhvaća:


Tijek rada

flowchart TD SIG[Potpis] --> CRYPTO[Kripto-provjera] CRYPTO --> CERT[Provjera certifikata] CERT --> CHAIN[Izgradnja lanca] CHAIN --> REV[Provjera opoziva] REV --> TIME[Vremenska provjera] TIME --> TS{Vremenska oznaka?} TS -->|Da| TS_CHECK[Provjera vremenske oznake] TS -->|Ne| POLICY[Provjera politike] TS_CHECK --> POLICY POLICY --> RESULT{Sve OK?} RESULT -->|Da| VALID[Potpis VALJAN] RESULT -->|Ne| INVALID[Potpis NEVALJAN] style VALID fill:#e8f5e9 style INVALID fill:#ffebee


Primjer koda: Potpuna verifikacija

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
 
using var ctx = PqCryptoContext.Initialize();
 
public class SignatureVerifier
{
    public VerificationResult Verify(
        byte[] data,
        byte[] signature,
        X509Certificate2 signerCert,
        VerificationOptions options)
    {
        var result = new VerificationResult();
 
        try
        {
            // 1. Kriptografska provjera potpisa
            result.SignatureValid = ctx.VerifyData(
                data: data,
                signature: signature,
                certificate: signerCert,
                hashAlgorithm: options.HashAlgorithm,
                mode: options.CryptoMode
            );
 
            if (!result.SignatureValid)
            {
                result.Error = "Kriptografski potpis nije valjan";
                return result;
            }
 
            // 2. Validacija lanca certifikata
            var chain = new X509Chain();
            ConfigureChainPolicy(chain.ChainPolicy, options);
 
            result.CertificateValid = chain.Build(signerCert);
            result.ChainStatus = chain.ChainStatus
                .Select(s => s.Status.ToString())
                .ToList();
 
            if (!result.CertificateValid)
            {
                result.Error = $"Certifikat nije valjan: {string.Join(", ", result.ChainStatus)}";
                return result;
            }
 
            // 3. Provjera Extended Key Usage
            if (options.RequiredEku != null)
            {
                result.EkuValid = HasExtendedKeyUsage(signerCert, options.RequiredEku);
                if (!result.EkuValid)
                {
                    result.Error = $"Potrebna EKU ({options.RequiredEku}) nedostaje";
                    return result;
                }
            }
 
            // 4. Provjera vremena potpisa
            var signatureTime = options.SignatureTime ?? DateTimeOffset.UtcNow;
            result.TimeValid = signatureTime >= signerCert.NotBefore &&
                               signatureTime <= signerCert.NotAfter;
 
            if (!result.TimeValid)
            {
                result.Error = "Certifikat nije bio valjan u vrijeme potpisivanja";
                return result;
            }
 
            result.IsValid = true;
            result.SignerSubject = signerCert.Subject;
            result.SignatureTime = signatureTime;
        }
        catch (Exception ex)
        {
            result.IsValid = false;
            result.Error = ex.Message;
        }
 
        return result;
    }
}

Primjer koda: Verifikacija CMS/PKCS#7 potpisa

public VerificationResult VerifyCms(byte[] signedData, byte[]? originalContent = null)
{
    var result = new VerificationResult();
 
    try
    {
        // Parsiranje CMS-a
        var signedCms = new SignedCms();
 
        if (originalContent != null)
        {
            // Detached signature
            var contentInfo = new ContentInfo(originalContent);
            signedCms = new SignedCms(contentInfo, true);
        }
 
        signedCms.Decode(signedData);
 
        // Provjera svakog potpisa
        foreach (var signerInfo in signedCms.SignerInfos)
        {
            // Kriptografska provjera
            signerInfo.CheckSignature(verifySignatureOnly: false);
 
            // Certifikat potpisnika
            var cert = signerInfo.Certificate;
            result.SignerSubject = cert?.Subject;
 
            // Provjera vremenske oznake (ako postoji)
            var timestampAttr = signerInfo.UnsignedAttributes
                .Cast<CryptographicAttributeObject>()
                .FirstOrDefault(a => a.Oid.Value == "1.2.840.113549.1.9.16.2.14");
 
            if (timestampAttr != null)
            {
                var tsToken = Rfc3161TimestampToken.Decode(
                    timestampAttr.Values[0].RawData, out _);
 
                result.TimestampPresent = true;
                result.TimestampTime = tsToken.TokenInfo.Timestamp;
 
                // Validacija vremenske oznake
                var signatureBytes = signerInfo.GetSignature();
                var signatureHash = SHA256.HashData(signatureBytes);
 
                result.TimestampValid = tsToken.VerifySignatureForHash(
                    signatureHash,
                    HashAlgorithmName.SHA256,
                    out _,
                    extraCandidates: null
                );
            }
 
            // Signing Time atribut
            var signingTimeAttr = signerInfo.SignedAttributes
                .Cast<CryptographicAttributeObject>()
                .FirstOrDefault(a => a.Oid.Value == "1.2.840.113549.1.9.5");
 
            if (signingTimeAttr != null)
            {
                var pkcs9Time = new Pkcs9SigningTime(signingTimeAttr.Values[0].RawData);
                result.SignatureTime = pkcs9Time.SigningTime;
            }
        }
 
        result.IsValid = true;
        result.SignatureValid = true;
        result.CertificateValid = true;
    }
    catch (CryptographicException ex)
    {
        result.IsValid = false;
        result.Error = ex.Message;
    }
 
    return result;
}

Rezultat verifikacije

public class VerificationResult
{
    public bool IsValid { get; set; }
    public bool SignatureValid { get; set; }
    public bool CertificateValid { get; set; }
    public bool TimeValid { get; set; }
    public bool EkuValid { get; set; } = true;
    public bool TimestampPresent { get; set; }
    public bool TimestampValid { get; set; }
 
    public string? SignerSubject { get; set; }
    public DateTimeOffset? SignatureTime { get; set; }
    public DateTimeOffset? TimestampTime { get; set; }
    public List<string> ChainStatus { get; set; } = new();
    public string? Error { get; set; }
 
    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine($"Potpis valjan: {IsValid}");
        sb.AppendLine($"  Kripto-provjera: {SignatureValid}");
        sb.AppendLine($"  Certifikat: {CertificateValid}");
        sb.AppendLine($"  Vremenska provjera: {TimeValid}");
 
        if (SignerSubject != null)
            sb.AppendLine($"  Potpisnik: {SignerSubject}");
 
        if (SignatureTime.HasValue)
            sb.AppendLine($"  Vrijeme potpisa: {SignatureTime:yyyy-MM-dd HH:mm:ss}");
 
        if (TimestampPresent)
        {
            sb.AppendLine($"  Vremenska oznaka: {(TimestampValid ? "valjana" : "nevaljana")}");
            if (TimestampTime.HasValue)
                sb.AppendLine($"  Vrijeme oznake: {TimestampTime:yyyy-MM-dd HH:mm:ss}");
        }
 
        if (Error != null)
            sb.AppendLine($"  GREŠKA: {Error}");
 
        return sb.ToString();
    }
}

Grupna verifikacija

public class BatchVerifier
{
    public async Task<Dictionary<string, VerificationResult>> VerifyDirectory(
        string directory,
        X509Certificate2Collection trustedCerts)
    {
        var results = new Dictionary<string, VerificationResult>();
        var verifier = new SignatureVerifier();
 
        // Pronalazak svih .sig datoteka
        var sigFiles = Directory.GetFiles(directory, "*.sig", SearchOption.AllDirectories);
 
        foreach (var sigFile in sigFiles)
        {
            // Pronalazak originalne datoteke
            var originalFile = sigFile.Replace(".sig", "");
            if (!File.Exists(originalFile))
            {
                results[sigFile] = new VerificationResult
                {
                    IsValid = false,
                    Error = "Originalna datoteka nije pronađena"
                };
                continue;
            }
 
            // Pronalazak certifikata
            var certFile = sigFile.Replace(".sig", ".crt");
            X509Certificate2 signerCert;
 
            if (File.Exists(certFile))
            {
                signerCert = new X509Certificate2(certFile);
            }
            else
            {
                // Pretraživanje u Trusted Certs (pojednostavljeno)
                signerCert = trustedCerts[0];
            }
 
            // Verifikacija
            var data = await File.ReadAllBytesAsync(originalFile);
            var signature = await File.ReadAllBytesAsync(sigFile);
 
            results[sigFile] = verifier.Verify(
                data,
                signature,
                signerCert,
                new VerificationOptions
                {
                    HashAlgorithm = HashAlgorithmName.SHA256,
                    CryptoMode = CryptoMode.Hybrid
                }
            );
        }
 
        return results;
    }
}

Kodovi grešaka i rješenja

Greška Uzrok Rješenje
SignatureInvalid Podaci ili potpis manipulirani Provjerite originale
CertificateExpired Certifikat istekao Koristite vremensku oznaku
CertificateRevoked Certifikat opozvan Novi certifikat
UntrustedRoot Root nije pouzdan Provjerite Trust Store
ChainIncomplete Nedostaje Intermediate Dovršite lanac
TimestampInvalid Potpis vremenske oznake nije valjan Provjerite TSA certifikat

Povezani scenariji

Povezanost Scenarij Opis
Preduvjet 8.1 Potpisivanje dokumenata Kreiranje potpisa
Preduvjet 8.3 Vremenska oznaka Dodavanje vremenske oznake
Povezano 5.2 Validacija lanca Provjera certifikata

« ← 8.3 Vremenska oznaka | ↑ Pregled potpisa | → Svi scenariji »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional