If you're building anything that touches sensitive data — passwords, personal info, files, network traffic — encryption isn't optional.
Thankfully, C# makes this a lot less painful than it sounds. In this guide, we'll walk through what encryption actually means in a C# context and look at some real, working code so you can start using it right away.
So What Does Encryption in C# Actually Mean?
At its core, encryption just takes readable data and scrambles it into something nobody can make sense of — unless they have the right key to unscramble it. .NET gives you the tools to do this through the System.Security.Cryptography namespace, which includes support for algorithms like AES, RSA, and DES.
Think of it like mailing a locked briefcase. Anyone can carry it, look at it, even try to pry it open — but only the person holding the matching key can actually get inside. That's the whole idea behind encryption: it doesn't stop people from seeing your data, it just makes sure they can't read it.
(If you want to go deeper on the theory, it's worth reading up on the differences between symmetric and asymmetric encryption before diving into code.)
Symmetric vs. Asymmetric: Which One Do You Need?
There are two main flavors of encryption you'll run into, and picking the right one matters:
- Symmetric encryption uses one single key to both lock and unlock the data. It's quick and efficient, but the catch is you need a safe way to get that key to whoever needs to decrypt the data — if it leaks, so does everything encrypted with it.
- Asymmetric encryption uses a key pair instead: a public key that anyone can use to encrypt data, and a private key — kept secret — that's the only thing capable of decrypting it. It's heavier computationally, but it sidesteps the whole "how do I share the key safely" problem.
Most real-world systems actually use a mix of both — asymmetric encryption to exchange a symmetric key securely, then symmetric encryption for the bulk of the actual data (since it's much faster).
Let's Write Some Code
Here are a few practical examples you can adapt for your own projects.
1. Symmetric Encryption with AES
AES (Advanced Encryption Standard) is the go-to choice for symmetric encryption these days — it's fast, well-tested, and considered secure by pretty much every modern standard.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class SymmetricEncryptionExample
{
public static void Main()
{
string original = "Sensitive data";
using (Aes aes = Aes.Create())
{
byte[] encrypted = EncryptString(original, aes.Key, aes.IV);
string decrypted = DecryptString(encrypted, aes.Key, aes.IV);
Console.WriteLine($"Original: {original}");
Console.WriteLine($"Decrypted: {decrypted}");
}
}
static byte[] EncryptString(string plainText, byte[] key, byte[] iv)
{
using (Aes aes = Aes.Create())
using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv))
using (MemoryStream ms = new MemoryStream())
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
cs.Write(plainBytes, 0, plainBytes.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
static string DecryptString(byte[] cipherText, byte[] key, byte[] iv)
{
using (Aes aes = Aes.Create())
using (ICryptoTransform decryptor = aes.CreateDecryptor(key, iv))
using (MemoryStream ms = new MemoryStream(cipherText))
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
byte[] plainBytes = new byte[cipherText.Length];
int decryptedBytes = cs.Read(plainBytes, 0, plainBytes.Length);
return Encoding.UTF8.GetString(plainBytes, 0, decryptedBytes);
}
}
}
A couple things worth noting here: the key and IV (initialization vector) are what actually make the encryption secure — lose track of either one and you've effectively lost your data, even though it still technically exists. Run this and you'll see the original string come right back after the round trip through encryption and decryption.
2. Asymmetric Encryption with RSA
RSA is the classic example of public-key cryptography — you encrypt with one key, decrypt with a completely different one.
using System;
using System.Security.Cryptography;
class AsymmetricEncryptionExample
{
public static void Main()
{
string data = "Critical information";
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
string publicKey = rsa.ToXmlString(false);
string privateKey = rsa.ToXmlString(true);
byte[] encryptedData = EncryptData(data, publicKey);
string decryptedData = DecryptData(encryptedData, privateKey);
Console.WriteLine($"Original: {data}");
Console.WriteLine($"Decrypted: {decryptedData}");
}
}
static byte[] EncryptData(string data, string publicKey)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(publicKey);
return rsa.Encrypt(System.Text.Encoding.UTF8.GetBytes(data), false);
}
}
static string DecryptData(byte[] data, string privateKey)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(privateKey);
return System.Text.Encoding.UTF8.GetString(rsa.Decrypt(data, false));
}
}
}
Notice how the public key does the encrypting and the private key does the decrypting — that's the whole point. You can hand the public key out to anyone, but only whoever's holding the private key can actually read what gets sent to them.
3. Encrypting an Entire File
Sometimes you don't just want to encrypt a string in memory — you want to lock down a whole file. You can do this by streaming the file's contents through a CryptoStream:
using System;
using System.IO;
using System.Security.Cryptography;
class FileEncryptionExample
{
public static void Main()
{
string filePath = "data.txt";
string encryptedFilePath = "data_encrypted.aes";
using (Aes aes = Aes.Create())
{
EncryptFile(filePath, encryptedFilePath, aes.Key, aes.IV);
Console.WriteLine("File encrypted successfully.");
}
}
static void EncryptFile(string inputFile, string outputFile, byte[] key, byte[] iv)
{
using (FileStream fsInput = new FileStream(inputFile, FileMode.Open))
using (FileStream fsOutput = new FileStream(outputFile, FileMode.Create))
using (Aes aes = Aes.Create())
using (CryptoStream cryptoStream = new CryptoStream(fsOutput, aes.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
fsInput.CopyTo(cryptoStream);
}
}
}
This is handy for things like encrypting config files, backups, or any file you don't want sitting around in plain text on disk.
4. Hashing Passwords (Not the Same Thing as Encryption!)
Here's a distinction that trips a lot of people up: passwords should never be encrypted — they should be hashed. Encryption is reversible if you have the key. Hashing isn't reversible at all, which is exactly what you want for passwords — you're only ever checking whether an input matches, never "unlocking" the original.
using System;
using System.Security.Cryptography;
using System.Text;
class HashingExample
{
public static void Main()
{
string password = "SecurePa$$word";
string hashedPassword = HashPassword(password);
Console.WriteLine($"Hashed Password: {hashedPassword}");
}
static string HashPassword(string password)
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password));
return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
}
}
}
Worth flagging: for production password storage, plain SHA-256 on its own isn't ideal — it's fast, and fast is bad when someone's trying to brute-force guesses. In practice, you'd want something like PBKDF2, bcrypt, or Argon2, which are specifically designed to be slow and resistant to that kind of attack. This example is more about showing the mechanics of hashing than a drop-in production solution.