~~NOTOC~~
====== Scenario 8.4: Verify Signature ======
**Category:** [[.:start|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()
.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()
.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 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> VerifyDirectory(
string directory,
X509Certificate2Collection trustedCerts)
{
var results = new Dictionary();
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 |
----
===== Related Scenarios =====
^ Relationship ^ Scenario ^ Description ^
| **Prerequisite** | [[.:dokument_signieren|8.1 Sign Document]] | Create signature |
| **Prerequisite** | [[.:timestamp|8.3 Timestamp]] | Add timestamp |
| **Related** | [[en:int:pqcrypt:szenarien:validierung:chain_validation|5.2 Chain Validation]] | Certificate verification |
----
<< [[.:timestamp|← 8.3 Timestamp]] | [[.:start|↑ Signatures Overview]] | [[en:int:pqcrypt:szenarien:start|→ All Scenarios]] >>
{{tag>scenario signature verification validation cms}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//