Overview
This is an implementation to create a digital signature for arbitrary data using an RSA private key. By attaching a digital signature, you can guarantee the authenticity (proving the sender’s identity) and integrity (confirming the data has not been tampered with). In modern .NET implementations, it is common to use the SignData method of the RSA class to handle both hashing and signature generation in one step, rather than using the older AsymmetricSignatureFormatter.
Specifications (Input/Output)
- Input: Text data to be signed and an RSA Private Key.
- Output: The generated digital signature as a Base64 string.
- Process: The target data is hashed with SHA256, and that hash value is then encrypted with the RSA private key to create the signature.
Basic Usage
using System.Security.Cryptography;
using System.Text;
var data = Encoding.UTF8.GetBytes("Important Contract Data");
using var rsa = RSA.Create();
rsa.ImportFromPem(privateKeyPem); // Loading the private key
// Execute hash calculation and signing in one step
// Choose PKCS1 for high compatibility or PSS for better security
byte[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Full Code Example
The following code is a console application that demonstrates the entire flow: generating a key pair, creating a signature, and verifying it.
using System;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main()
{
// 1. Message to be signed
string contractText = "Author: John Doe, Date: 2025-01-01, Content: Approved";
Console.WriteLine($"[Document] {contractText}");
// 2. Prepare RSA keys (In a real scenario, you would load stored keys)
using var rsa = RSA.Create(2048);
// Private Key (Used for creating the signature)
// Store this in a secure location like Azure Key Vault
string privateKeyPem = rsa.ExportRSAPrivateKeyPem();
// Public Key (Used for verification)
// This key is distributed to the recipient
string publicKeyPem = rsa.ExportSubjectPublicKeyPem();
// 3. Generate the Digital Signature
try
{
string signatureBase64 = GenerateDigitalSignature(contractText, privateKeyPem);
Console.WriteLine("\n[Signature Generated]");
Console.WriteLine(signatureBase64);
// 4. Verification (Reference)
// Confirm validity using data, signature, and public key
bool isValid = VerifySignature(contractText, signatureBase64, publicKeyPem);
Console.WriteLine($"\n[Verification Result] {isValid}");
}
catch (CryptographicException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
/// <summary>
/// Receives a message and a private key, and returns a digital signature (Base64).
/// </summary>
static string GenerateDigitalSignature(string message, string privateKeyPem)
{
byte[] dataBytes = Encoding.UTF8.GetBytes(message);
using var rsa = RSA.Create();
rsa.ImportFromPem(privateKeyPem);
// SignData: Internally calculates the hash and signs it
// Parameter 2: Hash Algorithm (SHA256 recommended)
// Parameter 3: Padding Mode (Pkcs1 or Pss)
byte[] signatureBytes = rsa.SignData(
dataBytes,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signatureBytes);
}
/// <summary>
/// Helper method for verification
/// </summary>
static bool VerifySignature(string message, string signatureBase64, string publicKeyPem)
{
byte[] dataBytes = Encoding.UTF8.GetBytes(message);
byte[] signatureBytes = Convert.FromBase64String(signatureBase64);
using var rsa = RSA.Create();
rsa.ImportFromPem(publicKeyPem);
return rsa.VerifyData(
dataBytes,
signatureBytes,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
}
}
Customization Points
- Padding Mode: This example uses
RSASignaturePadding.Pkcs1for compatibility. If you are building a new system and do not need to worry about legacy support, consider using the more robustRSASignaturePadding.Pss. - Hash Algorithm:
HashAlgorithmName.SHA256is the current standard, but you can change it toSHA384orSHA512if higher security levels are required. Ensure the signing and verification sides use the same algorithm.
Important Notes
- Private Key Leakage: Only the owner of the private key can create a signature. If the private key is leaked, a third party can impersonate the owner, so strict management is required.
- Matching Settings: When verifying a signature, the original data, hash algorithm (e.g., SHA256), and padding (e.g., PKCS1) must all match the settings used during generation.
- Avoid Legacy Implementations:
RSAPKCS1SignatureFormatterandSHA256Managedare older APIs and are not recommended for cross-platform support or performance. Use the methods provided by theRSAclass.
Advanced Usage
Signing Directly from a Hash Value (SignHash)
This implementation is used when you already have the hash value (digest) of the data. This is useful when you have pre-calculated a hash for a large file using a stream.
static byte[] SignHashDirectly(byte[] hashDigest, string privateKeyPem)
{
using var rsa = RSA.Create();
rsa.ImportFromPem(privateKeyPem);
// Use SignHash instead of SignData
// Specify the hash algorithm used to generate the provided hashDigest
return rsa.SignHash(hashDigest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
Conclusion
Creating digital signatures in .NET using the RSA.SignData method allows for a safe and concise implementation of both hashing and signing. Choose your signature method (padding and hash algorithm) based on your system’s security and compatibility requirements, and ensure that the private key—the core of the signature’s trust—is managed securely.
