.NET Documentation

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:

  1. The sender hashes the message.
  2. The sender encrypts the hash with their private key (this is the digital signature).
  3. The sender sends the original message and the digital signature.
  4. The recipient receives the message and signature.
  5. The recipient hashes the received message.
  6. The recipient decrypts the digital signature with the sender's public key to get the original hash.
  7. 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