Scenario 8.4: Verify Signature

Category: Digital Signatures
Complexity: ⭐⭐⭐ (Medium)
Prerequisites: Signed data, signature certificate
Estimated Time: 10-15 Minutes


Description

This scenario describes the complete verification of digital signatures. Proper verification includes:

  • Signature verification - Cryptographic validation
  • Certificate verification - Chain, revocation, validity
  • Timestamp verification - If present
  • Policy verification - Intended usage

Workflow

flowchart TD SIG[Signature] --> CRYPTO[Crypto Verification] CRYPTO --> CERT[Verify Certificate] CERT --> CHAIN[Chain Building] CHAIN --> REV[Revocation Check] REV --> TIME[Time Verification] TIME --> TS{Timestamp?} TS -->|Yes| TS_CHECK[Verify Timestamp] TS -->|No| POLICY[Verify Policy] TS_CHECK --> POLICY POLICY --> RESULT{All OK?} RESULT -->|Yes| VALID[Signature VALID] RESULT -->|No| INVALID[Signature INVALID] style VALID fill:#e8f5e9 style INVALID fill:#ffebee


Code Example: Complete Verification

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. Cryptographic signature verification
            result.SignatureValid = ctx.VerifyData(
                data: data,
                signature: signature,
                certificate: signerCert,
                hashAlgorithm: options.HashAlgorithm,
                mode: options.CryptoMode
            );
 
            if (!result.SignatureValid)
            {
                result.Error = "Cryptographic signature invalid";
                return result;
            }
 
            // 2. Validate certificate chain
            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 = $"Certificate invalid: {string.Join(", ", result.ChainStatus)}";
                return result;
            }
 
            // 3. Verify Extended Key Usage
            if (options.RequiredEku != null)
            {
                result.EkuValid = HasExtendedKeyUsage(signerCert, options.RequiredEku);
                if (!result.EkuValid)
                {
                    result.Error = $"Required EKU ({options.RequiredEku}) missing";
                    return result;
                }
            }
 
            // 4. Verify signature time
            var signatureTime = options.SignatureTime ?? DateTimeOffset.UtcNow;
            result.TimeValid = signatureTime >= signerCert.NotBefore &&
                               signatureTime <= signerCert.NotAfter;
 
            if (!result.TimeValid)
            {
                result.Error = "Certificate was not valid at signature time";
                return result;
            }
 
            result.IsValid = true;
            result.SignerSubject = signerCert.Subject;
            result.SignatureTime = signatureTime;
        }
        catch (Exception ex)
        {
            result.IsValid = false;
            result.Error = ex.Message;
        }
 
        return result;
    }
}

Code Example: Verify CMS/PKCS#7 Signature

public VerificationResult VerifyCms(byte[] signedData, byte[]? originalContent = null)
{
    var result = new VerificationResult();
 
    try
    {
        // Parse CMS
        var signedCms = new SignedCms();
 
        if (originalContent != null)
        {
            // Detached signature
            var contentInfo = new ContentInfo(originalContent);
            signedCms = new SignedCms(contentInfo, true);
        }
 
        signedCms.Decode(signedData);
 
        // Verify each signature
        foreach (var signerInfo in signedCms.SignerInfos)
        {
            // Cryptographic verification
            signerInfo.CheckSignature(verifySignatureOnly: false);
 
            // Signer certificate
            var cert = signerInfo.Certificate;
            result.SignerSubject = cert?.Subject;
 
            // Verify timestamp (if present)
            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;
 
                // Validate timestamp
                var signatureBytes = signerInfo.GetSignature();
                var signatureHash = SHA256.HashData(signatureBytes);
 
                result.TimestampValid = tsToken.VerifySignatureForHash(
                    signatureHash,
                    HashAlgorithmName.SHA256,
                    out _,
                    extraCandidates: null
                );
            }
 
            // Signing Time attribute
            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;
}

Verification Result

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($"Signature valid: {IsValid}");
        sb.AppendLine($"  Crypto verification: {SignatureValid}");
        sb.AppendLine($"  Certificate: {CertificateValid}");
        sb.AppendLine($"  Time verification: {TimeValid}");
 
        if (SignerSubject != null)
            sb.AppendLine($"  Signer: {SignerSubject}");
 
        if (SignatureTime.HasValue)
            sb.AppendLine($"  Signature time: {SignatureTime:yyyy-MM-dd HH:mm:ss}");
 
        if (TimestampPresent)
        {
            sb.AppendLine($"  Timestamp: {(TimestampValid ? "valid" : "invalid")}");
            if (TimestampTime.HasValue)
                sb.AppendLine($"  Timestamp time: {TimestampTime:yyyy-MM-dd HH:mm:ss}");
        }
 
        if (Error != null)
            sb.AppendLine($"  ERROR: {Error}");
 
        return sb.ToString();
    }
}

Batch Verification

public class BatchVerifier
{
    public async Task<Dictionary<string, VerificationResult>> VerifyDirectory(
        string directory,
        X509Certificate2Collection trustedCerts)
    {
        var results = new Dictionary<string, VerificationResult>();
        var verifier = new SignatureVerifier();
 
        // Find all .sig files
        var sigFiles = Directory.GetFiles(directory, "*.sig", SearchOption.AllDirectories);
 
        foreach (var sigFile in sigFiles)
        {
            // Find original file
            var originalFile = sigFile.Replace(".sig", "");
            if (!File.Exists(originalFile))
            {
                results[sigFile] = new VerificationResult
                {
                    IsValid = false,
                    Error = "Original file not found"
                };
                continue;
            }
 
            // Find certificate
            var certFile = sigFile.Replace(".sig", ".crt");
            X509Certificate2 signerCert;
 
            if (File.Exists(certFile))
            {
                signerCert = new X509Certificate2(certFile);
            }
            else
            {
                // Search in trusted certs (simplified)
                signerCert = trustedCerts[0];
            }
 
            // Verify
            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;
    }
}

Error Codes and Resolution

Error Cause Solution
SignatureInvalid Data or signature tampered Check originals
CertificateExpired Certificate expired Use timestamp
CertificateRevoked Certificate revoked New certificate
UntrustedRoot Root not trusted Check trust store
ChainIncomplete Intermediate missing Complete chain
TimestampInvalid Timestamp signature invalid Check TSA certificate

Relationship Scenario Description
Prerequisite 8.1 Sign Document Create signature
Prerequisite 8.3 Timestamp Add timestamp
Related 5.2 Chain Validation Certificate verification

« ← 8.3 Timestamp | ↑ Signatures Overview | → All Scenarios »


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

Zuletzt geändert: on 2026/01/30 at 06:48 AM