Cryptography
This section provides comprehensive guidance on using cryptography features within the .NET ecosystem. Learn how to implement secure encryption, hashing, digital signatures, and certificate management for your applications.
Introduction to Cryptography in .NET
.NET provides a robust set of classes and algorithms for implementing cryptographic operations. These are primarily found within the System.Security.Cryptography
namespace. Key concepts include:
- Symmetric Encryption: Uses a single key for both encryption and decryption. Fast and suitable for encrypting large amounts of data.
- Asymmetric Encryption: Uses a pair of keys (public and private). Slower but essential for key exchange and digital signatures.
- Hashing: Creates a fixed-size digest of data, ensuring data integrity and authenticity.
- Digital Signatures: Uses asymmetric cryptography to verify the origin and integrity of a message.
- Certificates: Public key infrastructure (PKI) for identity verification and secure communication.
Symmetric Encryption
Symmetric encryption algorithms like AES (Advanced Encryption Standard) are commonly used for their speed and efficiency. The System.Security.Cryptography
namespace provides classes such as Aes
for this purpose.
Key Concepts:
- Key: A secret value used in the encryption and decryption process.
- Initialization Vector (IV): A block of data used to initialize the encryption process, ensuring that even identical plaintexts encrypt to different ciphertexts.
- Padding: Used to ensure that the plaintext is a multiple of the block size of the algorithm.
Example: AES Encryption and Decryption
using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;
public class AesExample
{
public static void Main(string[] args)
{
string original = "This is my secret text.";
using (Aes aes = Aes.Create())
{
aes.KeySize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
// Generate a random key and IV
aes.GenerateKey();
aes.GenerateIV();
// Encrypt
byte[] encrypted = EncryptStringToBytes_Aes(original, aes.Key, aes.IV);
Console.WriteLine($"Encrypted data: {Convert.ToBase64String(encrypted)}");
// Decrypt
string decrypted = DecryptBytesToString_Aes(encrypted, aes.Key, aes.IV);
Console.WriteLine($"Decrypted data: {decrypted}");
}
}
public static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
byte[] encrypted;
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
public static string DecryptBytesToString_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
Asymmetric Encryption
Asymmetric encryption, typically using RSA, is vital for secure key exchange and creating digital signatures. The RSA
class in System.Security.Cryptography
is the primary tool.
Key Concepts:
- Public Key: Can be shared freely. Used for encrypting data or verifying signatures.
- Private Key: Must be kept secret. Used for decrypting data encrypted with the public key or creating digital signatures.
Example: RSA Encryption and Decryption
using System;
using System.Security.Cryptography;
using System.Text;
public class RsaExample
{
public static void Main(string[] args)
{
string message = "This message will be sent securely.";
// Generate RSA keys
using (var rsa = RSA.Create())
{
// Export public and private keys (for demonstration)
string publicKey = rsa.ToXmlString(false); // false for public key only
string privateKey = rsa.ToXmlString(true); // true for private key
Console.WriteLine("--- Public Key ---");
Console.WriteLine(publicKey);
Console.WriteLine("\n--- Private Key ---");
Console.WriteLine(privateKey);
// Encrypt with public key
byte[] encryptedData = EncryptWithPublicKey(message, publicKey);
Console.WriteLine($"\nEncrypted: {Convert.ToBase64String(encryptedData)}");
// Decrypt with private key
string decryptedMessage = DecryptWithPrivateKey(encryptedData, privateKey);
Console.WriteLine($"Decrypted: {decryptedMessage}");
}
}
public static byte[] EncryptWithPublicKey(string message, string publicKey)
{
using (var rsa = RSA.Create())
{
rsa.FromXmlString(publicKey);
return rsa.Encrypt(Encoding.UTF8.GetBytes(message), RSAEncryptionPadding.OaepSHA256);
}
}
public static string DecryptWithPrivateKey(byte[] encryptedData, string privateKey)
{
using (var rsa = RSA.Create())
{
rsa.FromXmlString(privateKey);
byte[] decryptedBytes = rsa.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA256);
return Encoding.UTF8.GetString(decryptedBytes);
}
}
}
Hashing
Hashing algorithms, such as SHA256, generate a unique, fixed-size "fingerprint" of data. They are used to verify data integrity and are a fundamental component of secure password storage and digital signatures.
Key Concepts:
- One-way Function: It's computationally infeasible to reverse the hashing process.
- Collision Resistance: It's computationally infeasible to find two different inputs that produce the same hash output.
Example: SHA256 Hashing
using System;
using System.Security.Cryptography;
using System.Text;
public class HashExample
{
public static void Main(string[] args)
{
string inputString = "This is the data to hash.";
string hash = CalculateSHA256(inputString);
Console.WriteLine($"Input: {inputString}");
Console.WriteLine($"SHA256 Hash: {hash}");
string anotherInput = "This is the data to hash.";
string anotherHash = CalculateSHA256(anotherInput);
Console.WriteLine($"\nInput: {anotherInput}");
Console.WriteLine($"SHA256 Hash: {anotherHash}"); // Will be the same
string differentInput = "This is different data.";
string differentHash = CalculateSHA256(differentInput);
Console.WriteLine($"\nInput: {differentInput}");
Console.WriteLine($"SHA256 Hash: {differentHash}"); // Will be different
}
public static string CalculateSHA256(string input)
{
using (SHA256 sha256Hash = SHA256.Create())
{
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
byte[] hashBytes = sha256Hash.ComputeHash(inputBytes);
// Convert the byte array to a hexadecimal string
StringBuilder builder = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
builder.Append(hashBytes[i].ToString("x2"));
}
return builder.ToString();
}
}
}
Digital Signatures
Digital signatures provide authentication, integrity, and non-repudiation. They involve signing a message's hash with the sender's private key, and verifying the signature using the sender's public key.
Process:
- The sender hashes the message.
- The sender encrypts the hash with their private key (this is the digital signature).
- The sender sends the original message and the digital signature.
- The recipient receives the message and signature.
- The recipient hashes the received message.
- The recipient decrypts the digital signature with the sender's public key to get the original hash.
- The recipient compares the hash of the received message with the decrypted hash from the signature. If they match, the signature is valid.
Example: RSA Digital Signature
using System;
using System.Security.Cryptography;
using System.Text;
public class DigitalSignatureExample
{
public static void Main(string[] args)
{
string message = "This is a document to be signed.";
// Generate RSA keys for signing
using (var rsaSigner = RSA.Create())
{
string publicKey = rsaSigner.ToXmlString(false);
string privateKey = rsaSigner.ToXmlString(true);
// Sign the message
byte[] signature = SignData(message, privateKey);
Console.WriteLine($"Message: {message}");
Console.WriteLine($"Signature: {Convert.ToBase64String(signature)}");
// Verify the signature
bool isValid = VerifyData(message, signature, publicKey);
Console.WriteLine($"\nSignature is valid: {isValid}");
// Tamper with the message and verify again
string tamperedMessage = message + " (tampered)";
bool isTamperedValid = VerifyData(tamperedMessage, signature, publicKey);
Console.WriteLine($"Signature valid for tampered message: {isTamperedValid}"); // Should be false
}
}
public static byte[] SignData(string message, string privateKey)
{
using (var rsa = RSA.Create())
{
rsa.FromXmlString(privateKey);
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
return rsa.SignData(messageBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
public static bool VerifyData(string message, byte[] signature, string publicKey)
{
using (var rsa = RSA.Create())
{
rsa.FromXmlString(publicKey);
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
return rsa.VerifyData(messageBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
}
Certificates
X.509 certificates are fundamental to Public Key Infrastructure (PKI) and secure communication (like SSL/TLS). .NET provides classes in System.Security.Cryptography.X509Certificates
to manage and use certificates.
Key Concepts:
- Certificate Store: A repository for certificates.
- Public Key: Embedded in the certificate.
- Private Key: May or may not be associated with the certificate (e.g., for signing).
- Certificate Chain: A hierarchy of certificates from a root authority down to the end-entity certificate.
Common Operations:
- Finding certificates in the user or machine store.
- Creating self-signed certificates.
- Verifying certificate chains.
- Using certificates for encryption and decryption.
Example: Finding a Certificate
using System;
using System.Security.Cryptography.X509Certificates;
public class CertificateExample
{
public static void Main(string[] args)
{
// Access the Current User's Certificate Store
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
// Find certificates issued by a specific authority (e.g., "CN=MyTestRootCA")
// Replace "Your Certificate Subject Name" with an actual subject name or use other criteria.
// For demonstration, let's try to find any certificate with a subject.
X509Certificate2Collection certificates = store.Certificates;
Console.WriteLine($"Found {certificates.Count} certificates in the CurrentUser/My store:");
foreach (X509Certificate2 cert in certificates)
{
Console.WriteLine($"\nSubject: {cert.SubjectName.Name}");
Console.WriteLine($"Issuer: {cert.IssuerName.Name}");
Console.WriteLine($"Expires: {cert.NotAfter}");
Console.WriteLine($"Thumbprint: {cert.Thumbprint}");
// Example: Use the public key for encryption (if available)
if (cert.PublicKey != null)
{
// You can access the public key blob or use it directly
// For actual encryption, you'd typically use RSA.Create() and load the public key.
}
}
}
catch (CryptographicException ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
finally
{
if (store != null) store.Close();
}
}
}
Commonly Used APIs
-
System.Security.Cryptography.Aes
Implements the Advanced Encryption Standard (AES) algorithm.
-
System.Security.Cryptography.RSA
Implements the RSA encryption algorithm.
-
System.Security.Cryptography.SHA256
Computes the SHA256 hash of the input data.
-
System.Security.Cryptography.RSACryptoServiceProvider
Provides an implementation of the RSA algorithm (older, but still widely used).
-
System.Security.Cryptography.X509Certificates.X509Certificate2
Represents an X.509 certificate.
-
System.Security.Cryptography.HMACSHA256
Computes the Hash-based Message Authentication Code (HMAC) using SHA256.
-
System.Security.Cryptography.AesManaged
Provides a managed implementation of the AES algorithm.