Previous Versioning in REST APIs Distributed Transactions in Microservices Next

Encryption and Decryption in .NET

Encrypting and decrypting sensitive information like usernames and passwords in .NET Core involves using cryptographic methods to protect the data.

Hashing Passwords (Recommended for Storage)

  • For storing passwords in a database, hashing is the recommended approach. Hashing is a one-way process, meaning the original password cannot be retrieved from the hash.
  • ASP.NET Core Identity: provides built-in mechanisms for securely hashing and salting passwords using PasswordHasher.
  • Salting: involves adding a unique, random value (the salt) to the password before hashing.
  • Pepper: can be added as an additional secret key known only to the application.

Example using PasswordHasher

using Microsoft.AspNetCore.Identity;

public class UserService
{
    private readonly IPasswordHasher<ApplicationUser> _passwordHasher;

    public UserService(IPasswordHasher<ApplicationUser> passwordHasher)
    {
        _passwordHasher = passwordHasher;
    }

    public string HashPassword(ApplicationUser user, string password)
    {
        return _passwordHasher.HashPassword(user, password);
    }

    public PasswordVerificationResult VerifyPassword(ApplicationUser user, string hashedPassword, string providedPassword)
    {
        return _passwordHasher.VerifyHashedPassword(user, hashedPassword, providedPassword);
    }
}

Symmetric Encryption (for Usernames or Data requiring Decryption)

  • Use symmetric encryption like AES for data that must be retrievable in its original form.
  • Same key is used for both encryption and decryption.
  • System.Security.Cryptography namespace provides cryptographic classes.
  • Aes.Create() creates an AES instance.
  • Rfc2898DeriveBytes derives a strong key and IV from a password.

Symmetric Encryption with AES

  • Generate or supply a 256-bit key and a 128-bit IV.
  • Create an Aes instance.
  • Wrap streams in CryptoStream.
  • Dispose objects to zero out sensitive data.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public class AesEncryption
{
    public static (byte[] Cipher, byte[] Key, byte[] IV) Encrypt(string plainText)
    {
        using var aes = Aes.Create();
        aes.GenerateKey();
        aes.GenerateIV();

        var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        using var ms = new MemoryStream();
        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        using (var writer = new StreamWriter(cs, Encoding.UTF8))
        {
            writer.Write(plainText);
        }

        return (ms.ToArray(), aes.Key, aes.IV);
    }

    public static string Decrypt(byte[] cipher, byte[] key, byte[] iv)
    {
        using var aes = Aes.Create();
        aes.Key = key;
        aes.IV = iv;

        var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
        using var ms = new MemoryStream(cipher);
        using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
        using var reader = new StreamReader(cs, Encoding.UTF8);
        return reader.ReadToEnd();
    }
}

Asymmetric Encryption with RSA

  • Uses a public/private key pair.
  • Encrypt with the public key, decrypt with the private key.
  • Use RSA.Create() or RSACryptoServiceProvider.
  • Use OAEP padding for secure encryption.
using System;
using System.Security.Cryptography;
using System.Text;

public class RsaEncryption
{
    public static (string PublicKey, string PrivateKey) GenerateKeys()
    {
        using var rsa = RSA.Create(2048);
        return (rsa.ToXmlString(false), rsa.ToXmlString(true));
    }

    public static byte[] Encrypt(string plainText, string publicKeyXml)
    {
        using var rsa = RSA.Create();
        rsa.FromXmlString(publicKeyXml);
        return rsa.Encrypt(Encoding.UTF8.GetBytes(plainText), RSAEncryptionPadding.OaepSHA256);
    }

    public static string Decrypt(byte[] cipher, string privateKeyXml)
    {
        using var rsa = RSA.Create();
        rsa.FromXmlString(privateKeyXml);
        var bytes = rsa.Decrypt(cipher, RSAEncryptionPadding.OaepSHA256);
        return Encoding.UTF8.GetString(bytes);
    }
}

Another Example of AES Encryption/Decryption

using System.Security.Cryptography;
using System.Text;

public class DataEncryptor
{
    private const string EncryptionKey = "YourStrongEncryptionKeyHere";

    public static string Encrypt(string plainText)
    {
        byte[] clearBytes = Encoding.Unicode.GetBytes(plainText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
                0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64,
                0x76, 0x65, 0x64, 0x65, 0x76
            });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                plainText = Convert.ToBase64String(ms.ToArray());
            }
        }
        return plainText;
    }

    public static string Decrypt(string cipherText)
    {
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
                0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64,
                0x76, 0x65, 0x64, 0x65, 0x76
            });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }
                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }
        return cipherText;
    }
}

Important Considerations

  • Never store encryption keys directly in code or config files.
  • Hashing is for passwords; encryption is for retrievable data.
  • Regularly review and update security practices.

Best Practices

  • Use RandomNumberGenerator.Create() for secure randomness.
  • Never reuse IVs with the same key.
  • Protect and rotate keys using secure vaults.
  • Prefer OAEP padding over PKCS#1 v1.5 for RSA.
  • Dispose cryptographic objects to zero out secrets.

Clarifying Your Scenario

  • Are you encrypting strings in memory, files on disk, or network streams?
  • Do you need symmetric, asymmetric, or both?
  • Will key management live in code, config, or an external vault?

Beyond Core APIs

  • Microsoft.AspNetCore.DataProtection simplifies encryption for ASP.NET Core apps.
  • Use Sodium.Core for high-performance encryption.
  • Integrate with Azure Key Vault or AWS KMS for managed key storage.
  • Use X509Certificate2 for enterprise scenarios.
  • Check FIPS-compliant algorithms for government-grade security.
Back to Index
Previous Versioning in REST APIs Distributed Transactions in Microservices Next
*