Overview
This implementation verifies that data has not been tampered with (integrity) and that it was sent by the correct sender (authenticity) by using the received data, the digital signature, and the sender’s public key. In modern .NET implementations, you use the VerifyData method of the RSA class for a simple and secure process instead of the older RSAPKCS1SignatureDeformatter class.
Specifications (Input/Output)
- Input: – Text data to be verified (UTF-8).
- Digital signature (Base64 string).
- Sender’s RSA public key (XML string).
- Output: Verification result (
bool:trueif valid,falseif tampered with or inconsistent). - Prerequisites: Use the same hash algorithm (e.g., SHA256) and padding mode (e.g., PKCS#1) as when the signature was created.
Basic Usage
Call the validation method as follows:
using System.Security.Cryptography;
using System.Text;
// Call the signature verification method
bool isValid = ValidateSignature(originalData, signatureBase64, publicKeyXml);
if (isValid)
{
Console.WriteLine("Signature is valid. The data is trusted.");
}
else
{
Console.WriteLine("Warning: The signature is invalid or the data has been tampered with.");
}
Full Code Example
The following is a console application that includes the signature verification logic and test code.
using System;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main()
{
// 1. Prepare test data
// In reality, you use data and a signature received via a network, etc.
string receivedMessage = "OrderConfirmed: ID-998877";
// For testing, we generate a temporary key pair and signature (normally not needed)
GenerateTestEnvironment(receivedMessage, out string publicKeyXml, out string validSignature);
Console.WriteLine($"[Received Message] {receivedMessage}");
Console.WriteLine($"[Public Key] Found XML key info.");
Console.WriteLine($"[Signature] {validSignature.Substring(0, 20)}...");
// 2. Verify a valid signature
Console.WriteLine("\n--- Test 1: Valid Signature ---");
bool result1 = VerifyDigitalSignature(receivedMessage, validSignature, publicKeyXml);
Console.WriteLine($"Verification Result: {(result1 ? "OK (Trusted)" : "NG (Tampered)")}");
// 3. Verify tampered data
Console.WriteLine("\n--- Test 2: Tampered Data ---");
string tamperedMessage = "OrderConfirmed: ID-000000"; // Modifying the ID
bool result2 = VerifyDigitalSignature(tamperedMessage, validSignature, publicKeyXml);
Console.WriteLine($"Verification Result: {(result2 ? "OK (Trusted)" : "NG (Tampered)")}");
}
/// <summary>
/// Receives a message, signature, and public key to verify validity.
/// </summary>
/// <param name="data">The original data to verify.</param>
/// <param name="signatureBase64">Base64 formatted signature.</param>
/// <param name="publicKeyXml">RSA public key in XML format.</param>
/// <returns>True if the signature is valid.</returns>
static bool VerifyDigitalSignature(string data, string signatureBase64, string publicKeyXml)
{
try
{
// Convert data to byte array
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
// Convert signature to byte array
byte[] signatureBytes = Convert.FromBase64String(signatureBase64);
// Create RSA instance
using var rsa = RSA.Create();
// Load public key
// Use ImportFromPem for non-XML formats like PEM
rsa.FromXmlString(publicKeyXml);
// Execute verification
// Important: Use the same "hash algorithm" and "padding" as the signing process
return rsa.VerifyData(
dataBytes,
signatureBytes,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
}
catch (FormatException)
{
Console.WriteLine("Error: Invalid Base64 signature format.");
return false;
}
catch (CryptographicException ex)
{
Console.WriteLine($"Error: {ex.Message}");
return false;
}
}
// Helper to generate signature for testing (signing logic)
static void GenerateTestEnvironment(string msg, out string pubKey, out string sig)
{
using var rsa = RSA.Create(2048);
pubKey = rsa.ToXmlString(false);
byte[] data = Encoding.UTF8.GetBytes(msg);
byte[] sigBytes = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
sig = Convert.ToBase64String(sigBytes);
}
}
Customization Points
- Hash Algorithm Consistency: The
HashAlgorithmName.SHA256must perfectly match the one used by the signer. If the signer usedSHA1orSHA512, you must change this setting. - Padding Mode: While
RSASignaturePadding.Pkcs1is common, newer systems may use the more robustPss. Check your technical specifications. - Public Key Format: This example uses
FromXmlString. If you are using keys generated by OpenSSL, switch the implementation to useImportFromPemorImportSubjectPublicKeyInfo.
Important Notes
- Reasons for Verification Failure: If
VerifyDatareturnsfalse, it may not just mean the data was tampered with. It could also be due to a mismatched public key, incorrect hash algorithm, or different text encoding (UTF-8 vs. Shift-JIS). - Avoiding Old APIs: Using the
RSAPKCS1SignatureDeformatterclass is redundant in modern .NET environments and is not recommended. Methods integrated into theRSAclass provide cleaner, cross-platform code. - Exception Handling: Exceptions will occur if the Base64 string is corrupted or the XML key format is invalid. You should catch these within your verification method to prevent application crashes.
Advanced Usage
Verifying Hash Values (Digests) Directly
Use this implementation if you are verifying a pre-calculated hash value instead of the entire data.
static bool VerifyHashDirectly(byte[] hashDigest, byte[] signature, string publicKeyXml)
{
using var rsa = RSA.Create();
rsa.FromXmlString(publicKeyXml);
// Use VerifyHash instead of VerifyData
return rsa.VerifyHash(
hashDigest,
signature,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
}
Conclusion
Digital signature verification can be handled entirely by the RSA.VerifyData method. The most important requirement is to match the “hash algorithm” and “padding method” exactly with the ones used during signature creation. If these do not match, verification will always fail even if the data is correct. Since XML-formatted keys are specific to .NET, designing your system to support PEM format will increase flexibility when integrating with external systems.
