Scenario 8.4: Verificare firma

Categoria: Firme digitali
Complessità: Media
Prerequisiti: Dati firmati, certificato firmatario
Tempo stimato: 10-15 minuti


Descrizione

Questo scenario descrive la verifica completa delle firme digitali. Una verifica corretta comprende:


Workflow

flowchart TD SIG[Firma] --> CRYPTO[Verifica crittografica] CRYPTO --> CERT[Verificare certificato] CERT --> CHAIN[Chain Building] CHAIN --> REV[Controllo revoca] REV --> TIME[Verifica temporale] TIME --> TS{Timestamp?} TS -->|Si| TS_CHECK[Verificare timestamp] TS -->|No| POLICY[Verificare policy] TS_CHECK --> POLICY POLICY --> RESULT{Tutto OK?} RESULT -->|Si| VALID[Firma VALIDA] RESULT -->|No| INVALID[Firma NON VALIDA] style VALID fill:#e8f5e9 style INVALID fill:#ffebee


Esempio codice: Verifica completa

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. Verifica crittografica della firma
            result.SignatureValid = ctx.VerifyData(
                data: data,
                signature: signature,
                certificate: signerCert,
                hashAlgorithm: options.HashAlgorithm,
                mode: options.CryptoMode
            );
 
            if (!result.SignatureValid)
            {
                result.Error = "Firma crittografica non valida";
                return result;
            }
 
            // 2. Validare catena certificati
            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 = $"Certificato non valido: {string.Join(", ", result.ChainStatus)}";
                return result;
            }
 
            // 3. Verificare Extended Key Usage
            if (options.RequiredEku != null)
            {
                result.EkuValid = HasExtendedKeyUsage(signerCert, options.RequiredEku);
                if (!result.EkuValid)
                {
                    result.Error = $"EKU richiesto ({options.RequiredEku}) mancante";
                    return result;
                }
            }
 
            // 4. Verificare momento della firma
            var signatureTime = options.SignatureTime ?? DateTimeOffset.UtcNow;
            result.TimeValid = signatureTime >= signerCert.NotBefore &&
                               signatureTime <= signerCert.NotAfter;
 
            if (!result.TimeValid)
            {
                result.Error = "Certificato non valido al momento della firma";
                return result;
            }
 
            result.IsValid = true;
            result.SignerSubject = signerCert.Subject;
            result.SignatureTime = signatureTime;
        }
        catch (Exception ex)
        {
            result.IsValid = false;
            result.Error = ex.Message;
        }
 
        return result;
    }
}

Esempio codice: Verificare firma CMS/PKCS#7

public VerificationResult VerifyCms(byte[] signedData, byte[]? originalContent = null)
{
    var result = new VerificationResult();
 
    try
    {
        // Analizzare CMS
        var signedCms = new SignedCms();
 
        if (originalContent != null)
        {
            // Firma detached
            var contentInfo = new ContentInfo(originalContent);
            signedCms = new SignedCms(contentInfo, true);
        }
 
        signedCms.Decode(signedData);
 
        // Verificare ogni firma
        foreach (var signerInfo in signedCms.SignerInfos)
        {
            // Verifica crittografica
            signerInfo.CheckSignature(verifySignatureOnly: false);
 
            // Certificato firmatario
            var cert = signerInfo.Certificate;
            result.SignerSubject = cert?.Subject;
 
            // Verificare timestamp (se presente)
            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;
 
                // Validare timestamp
                var signatureBytes = signerInfo.GetSignature();
                var signatureHash = SHA256.HashData(signatureBytes);
 
                result.TimestampValid = tsToken.VerifySignatureForHash(
                    signatureHash,
                    HashAlgorithmName.SHA256,
                    out _,
                    extraCandidates: null
                );
            }
 
            // Attributo Signing Time
            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;
}

Risultato verifica

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($"Firma valida: {IsValid}");
        sb.AppendLine($"  Verifica crittografica: {SignatureValid}");
        sb.AppendLine($"  Certificato: {CertificateValid}");
        sb.AppendLine($"  Verifica temporale: {TimeValid}");
 
        if (SignerSubject != null)
            sb.AppendLine($"  Firmatario: {SignerSubject}");
 
        if (SignatureTime.HasValue)
            sb.AppendLine($"  Ora firma: {SignatureTime:yyyy-MM-dd HH:mm:ss}");
 
        if (TimestampPresent)
        {
            sb.AppendLine($"  Timestamp: {(TimestampValid ? "valido" : "non valido")}");
            if (TimestampTime.HasValue)
                sb.AppendLine($"  Ora timestamp: {TimestampTime:yyyy-MM-dd HH:mm:ss}");
        }
 
        if (Error != null)
            sb.AppendLine($"  ERRORE: {Error}");
 
        return sb.ToString();
    }
}

Verifica batch

public class BatchVerifier
{
    public async Task<Dictionary<string, VerificationResult>> VerifyDirectory(
        string directory,
        X509Certificate2Collection trustedCerts)
    {
        var results = new Dictionary<string, VerificationResult>();
        var verifier = new SignatureVerifier();
 
        // Trovare tutti i file .sig
        var sigFiles = Directory.GetFiles(directory, "*.sig", SearchOption.AllDirectories);
 
        foreach (var sigFile in sigFiles)
        {
            // Trovare file originale
            var originalFile = sigFile.Replace(".sig", "");
            if (!File.Exists(originalFile))
            {
                results[sigFile] = new VerificationResult
                {
                    IsValid = false,
                    Error = "File originale non trovato"
                };
                continue;
            }
 
            // Trovare certificato
            var certFile = sigFile.Replace(".sig", ".crt");
            X509Certificate2 signerCert;
 
            if (File.Exists(certFile))
            {
                signerCert = new X509Certificate2(certFile);
            }
            else
            {
                // Cercare nei certificati trusted (semplificato)
                signerCert = trustedCerts[0];
            }
 
            // Verificare
            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;
    }
}

Codici di errore e soluzioni

Errore Causa Soluzione
SignatureInvalid Dati o firma manipolati Verificare originali
CertificateExpired Certificato scaduto Usare timestamp
CertificateRevoked Certificato revocato Nuovo certificato
UntrustedRoot Root non affidabile Verificare Trust Store
ChainIncomplete Intermediate mancante Completare chain
TimestampInvalid Firma timestamp non valida Verificare certificato TSA

Scenari correlati

Relazione Scenario Descrizione
Prerequisito 8.1 Firmare documento Creare firma
Prerequisito 8.3 Timestamp Aggiungere timestamp
Correlato 5.2 Validazione Chain Verifica certificati

« ← 8.3 Timestamp | ↑ Panoramica firme | → Tutti gli scenari »


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