SignatureExtensions

Namespace: WvdS.System.Security.Cryptography.Signatures

Drop-in replacement extensions for digital signatures with Post-Quantum support. Extends RSA, ECDsa and X509Certificate2 with PQ-hybrid signing functions.

Overview

This class provides three types of signatures:

Mode Classical ML-DSA Usage
Classic - Standard .NET behavior
Hybrid Maximum security
PostQuantum - Pure post-quantum

RSA SignData/VerifyData

using var rsa = RSA.Create(2048);
byte[] data = Encoding.UTF8.GetBytes("Important data");
 
// Sign with explicit mode
byte[] signature = rsa.SignData(
    data,
    HashAlgorithmName.SHA256,
    RSASignaturePadding.Pkcs1,
    CryptoMode.Hybrid);
 
// Verify
bool isValid = rsa.VerifyData(
    data,
    signature,
    HashAlgorithmName.SHA256,
    RSASignaturePadding.Pkcs1,
    CryptoMode.Hybrid);

Stream Signature

using var fileStream = File.OpenRead("document.pdf");
 
byte[] signature = rsa.SignData(
    fileStream,
    HashAlgorithmName.SHA256,
    RSASignaturePadding.Pkcs1,
    CryptoMode.Hybrid);

ECDsa SignData/VerifyData

using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
byte[] data = GetDataToSign();
 
// Sign
byte[] signature = ecdsa.SignData(
    data,
    HashAlgorithmName.SHA256,
    CryptoMode.Hybrid);
 
// Verify
bool isValid = ecdsa.VerifyData(
    data,
    signature,
    HashAlgorithmName.SHA256,
    CryptoMode.Hybrid);

Certificate-based Signatures

var certificate = GetSigningCertificate();
byte[] data = GetDataToSign();
 
// Sign (automatically uses RSA or ECDSA)
byte[] signature = certificate.SignData(
    data,
    HashAlgorithmName.SHA256,
    CryptoMode.Hybrid);
 
// Verify
bool isValid = certificate.VerifyData(
    data,
    signature,
    HashAlgorithmName.SHA256,
    CryptoMode.Hybrid);

The key type is automatically detected:

  • RSA certificate → RSA.SignData + ML-DSA
  • ECDSA certificate → ECDsa.SignData + ML-DSA

Standalone ML-DSA

Direct ML-DSA signatures without classical key:

// Generate key pair
var (publicKey, privateKey) = SignatureExtensions.GenerateMlDsaKeyPair();
 
// Sign
byte[] data = GetDataToSign();
byte[] signature = SignatureExtensions.SignMlDsa(data, privateKey);
 
// Verify
bool isValid = SignatureExtensions.VerifyMlDsa(data, signature, publicKey);

Hybrid Signature Format

+---------------------------------------------+
| [4 Bytes] Classical signature length        |
| [n Bytes] Classical signature (RSA/ECDSA)   |
| [m Bytes] PQ signature (ML-DSA-65)          |
+---------------------------------------------+
Component Typical Size
Length field 4 bytes
RSA-2048 signature 256 bytes
ECDSA P-256 signature ~70 bytes
ML-DSA-65 signature 3,293 bytes
Hybrid RSA ~3,553 bytes
Hybrid ECDSA ~3,367 bytes

Transient PQ Keys

For standalone signatures (without certificate), thread-local transient keys are used:

// Set own keys
var (pubKey, privKey) = SignatureExtensions.GenerateMlDsaKeyPair();
SignatureExtensions.SetTransientPqKey(pubKey, privKey);
 
// Now SignData/VerifyData can be used without certificate
byte[] sig = rsa.SignData(data, HashAlgorithmName.SHA256,
    RSASignaturePadding.Pkcs1, CryptoMode.Hybrid);
 
// Only public key for verification
SignatureExtensions.SetTransientPqPublicKey(pubKey);
bool valid = rsa.VerifyData(data, sig, HashAlgorithmName.SHA256,
    RSASignaturePadding.Pkcs1, CryptoMode.Hybrid);
 
// Cleanup (overwrites private key with zeros)
SignatureExtensions.ClearTransientPqKey();

Methods Overview

RSA Extensions

Method Parameters Return
SignData(data, hash, padding, mode) byte[], HashAlgorithmName, RSASignaturePadding, CryptoMode? byte[]
SignData(stream, hash, padding, mode) Stream, HashAlgorithmName, RSASignaturePadding, CryptoMode? byte[]
VerifyData(data, sig, hash, padding, mode) byte[], byte[], HashAlgorithmName, RSASignaturePadding, CryptoMode? bool

ECDsa Extensions

Method Parameters Return
SignData(data, hash, mode) byte[], HashAlgorithmName, CryptoMode? byte[]
SignData(stream, hash, mode) Stream, HashAlgorithmName, CryptoMode? byte[]
VerifyData(data, sig, hash, mode) byte[], byte[], HashAlgorithmName, CryptoMode? bool

X509Certificate2 Extensions

Method Parameters Return
SignData(data, hash, mode) byte[], HashAlgorithmName, CryptoMode? byte[]
VerifyData(data, sig, hash, mode) byte[], byte[], HashAlgorithmName, CryptoMode? bool

Standalone ML-DSA

Method Parameters Return
GenerateMlDsaKeyPair() - (byte[] PublicKey, byte[] PrivateKey)
SignMlDsa(data, privateKey) byte[], byte[] byte[]
VerifyMlDsa(data, sig, publicKey) byte[], byte[], byte[] bool

Transient Key Management

Method Description
SetTransientPqKey(pub, priv) Sets key pair for thread
SetTransientPqPublicKey(pub) Sets only public key
ClearTransientPqKey() Securely deletes keys

Example: Complete Workflow

// 1. Configure CryptoMode
CryptoConfig.DefaultMode = CryptoMode.Hybrid;
 
// 2. Load certificate (with PQ keys)
var cert = X509Certificate2ExportExtensions.ImportPfx(
    "signing-cert.pfx",
    "password");
 
// 3. Sign document
byte[] documentData = File.ReadAllBytes("contract.pdf");
byte[] signature = cert.SignData(documentData, HashAlgorithmName.SHA256);
 
// 4. Save signature
File.WriteAllBytes("contract.sig", signature);
 
// 5. Verify later
var verifyCert = new X509Certificate2("signing-cert.cer");
bool isValid = verifyCert.VerifyData(
    File.ReadAllBytes("contract.pdf"),
    File.ReadAllBytes("contract.sig"),
    HashAlgorithmName.SHA256);
 
Console.WriteLine($"Signature valid: {isValid}");

Security Notes

  • In PostQuantum mode, no classical signature is created - not backward compatible
  • Transient keys are stored thread-locally - do not share between threads
  • Call ClearTransientPqKey() when keys are no longer needed

See Also


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

Zuletzt geändert: on 2026/01/30 at 12:14 AM