Secure Data Handling C Sharp Cryptography API: Protect Sensitive Information with AES and RSA

We remember the first time we handled encrypted data. It felt like holding a locked box without the key. We had a string, a purpose, and a fear—fear that we’d mess something up and leave that data wide open. 

Working with C# cryptography APIs taught me to take that fear and turn it into a habit—one of caution, clarity, and clean coding. We don’t just use these tools—we learn to respect them.

Key Takeaway

  • Use AES and RSA algorithms properly to encrypt and decrypt data securely in C#.
  • Manage cryptographic keys carefully, avoiding hardcoding and using secure storage.
  • Protect sensitive data in memory by zeroing buffers and avoiding immutable strings.

Understanding Cryptography APIs in C#

C# gives us a set of cryptography APIs that handle both symmetric and asymmetric encryption, along with hashing and digital signatures. We’ve got a whole toolbox here. But the tricky part isn’t finding the tools—it’s knowing how and when to use ’em right.

These APIs let us encrypt data, secure communication, and verify integrity. They’re built into the .NET framework, and we can access them through classes like Aes, RSA, and SHA256. We’ve leaned on these libraries in more than one secure app, and we’ve seen what happens when they’re misused. (1)

Symmetric Encryption with AES

We usually turn to AES (Advanced Encryption Standard) when we need to encrypt large amounts of data efficiently. It uses a single key for both encryption and decryption. We like it because it’s fast and strong—but it’s also a little unforgiving. One small mistake with key handling or IV reuse, and suddenly the door’s wide open.

AES works with 128-bit blocks and supports key sizes of 128, 192, or 256 bits. Most of us stick with 256 for the extra padding of safety. That key and its partner—the Initialization Vector (IV)—have to be generated securely. And reused IVs? That’s asking for trouble.

We’ve learned to always:

  • Use a different IV for every encryption.
  • Generate keys using a secure random number generator.
  • Store both the key and IV in a secure location.

Asymmetric Encryption with RSA

RSA is heavier. Slower. But it’s crucial when we need to send encrypted data to someone without already having a shared secret key. With RSA, we encrypt using a public key and decrypt with a private one. That split is what makes it so useful—and what makes key protection so critical.

RSA is usually used for small chunks of data or encrypting AES keys themselves (before using AES for the rest). Key sizes range from 2048 to 4096 bits. We prefer 3072 or above these days, just to stay ahead of brute-force attacks.

Implementing AES Encryption and Decryption in C#

Our AES encryption process follows a pattern we’ve refined over time. It starts with key and IV generation, moves into transformation, and finishes with clean, controlled memory handling.

AES Class Structure

Encapsulating AES in its own class makes the process manageable and secure. We define it like this:

csharp

CopyEdit

public class AesEncryption

{

    private readonly byte[] key;

    private readonly byte[] iv;

    public AesEncryption(byte[] key, byte[] iv)

    {

        this.key = key;

        this.iv = iv;

    }

    public string Encrypt(string plainText)

    {

        using (Aes aesAlg = Aes.Create())

        {

            aesAlg.Key = key;

            aesAlg.IV = iv;

            using var encryptor = aesAlg.CreateEncryptor();

            using var msEncrypt = new MemoryStream();

            using var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

            using var swEncrypt = new StreamWriter(csEncrypt);

            swEncrypt.Write(plainText);

            swEncrypt.Flush();

            csEncrypt.FlushFinalBlock();

            return Convert.ToBase64String(msEncrypt.ToArray());

        }

    }

    public string Decrypt(string cipherText)

    {

        using (Aes aesAlg = Aes.Create())

        {

            aesAlg.Key = key;

            aesAlg.IV = iv;

            using var decryptor = aesAlg.CreateDecryptor();

            using var msDecrypt = new MemoryStream(Convert.FromBase64String(cipherText));

            using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

            using var srDecrypt = new StreamReader(csDecrypt);

            return srDecrypt.ReadToEnd();

        }

    }

}

We encrypt, we decrypt, and we isolate responsibility. That’s how we keep mistakes from spreading.

How We Generate Keys and IVs

We never hardcode keys. Not ever. Instead, we generate them like this:

csharp

CopyEdit

using (Aes aes = Aes.Create())

{

    aes.KeySize = 256;

    aes.GenerateKey();

    aes.GenerateIV();

    byte[] key = aes.Key;

    byte[] iv = aes.IV;

    // Store securely

}

This gives us confidence that nobody can guess or reverse-engineer the key. We store them in secure vaults or encrypted files.

Using Authenticated Encryption (GCM and CCM)

Credits: C# interview questions

AES by itself doesn’t prove the message wasn’t changed. That’s why we use modes like GCM (Galois/Counter Mode). These authenticated modes attach a tag to each message. If someone tampers with it—even a single byte—the decryption fails.

We’ve switched to AES-GCM in most of our apps. It saves us from gluing MACs (message authentication codes) onto every message manually.

Our AES Checklist

  1. Use AES-256
  2. Generate keys with secure RNG
  3. Use a unique IV every time
  4. Prefer AES-GCM over AES-CBC
  5. Never store keys in plain text
  6. Always clear key material after use

RSA Encryption and Key Management

RSA Encryption and Key Management

RSA isn’t about speed. It’s about security in transit. We use it to protect AES keys or sign messages. And when we do, we treat those keys like gold.

Basic RSA Implementation

csharp

CopyEdit

public class RsaEncryption

{

    private readonly RSA rsa;

    public RsaEncryption()

    {

        rsa = RSA.Create(2048);

    }

    public byte[] Encrypt(byte[] data)

    {

        return rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);

    }

    public byte[] Decrypt(byte[] encryptedData)

    {

        return rsa.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA256);

    }

    public byte[] ExportPublicKey()

    {

        return rsa.ExportSubjectPublicKeyInfo();

    }

}

We like OAEP padding. It’s more secure than older schemes like PKCS#1 v1.5. We always export and store public keys separately, away from their private counterparts.

Private Key Storage Tips

Our rules are simple:

  • Never store private keys unencrypted
  • Use DPAPI or secure vaults
  • Rotate keys every 90 days
  • Keep key sizes at 3072 bits or higher

Protecting Sensitive Data in Memory

We’ve seen how dangerous it is to let passwords sit in memory. C# strings are immutable—they can’t be changed, so we can’t zero them out. That’s why we use byte arrays. (2)

Why We Avoid Strings for Secrets

Strings stay in memory, can be captured by memory dumps, and don’t give us control. Byte arrays, though—they’re ours to wipe.

csharp

CopyEdit

byte[] secret = Encoding.UTF8.GetBytes(“mySecretPassword”);

CryptographicOperations.ZeroMemory(secret);

Once we’re done with the secret, we clear it. Always. It’s a habit now.

Handling Secrets in SecureString

Sometimes we also use SecureString, though .NET has phased it out a bit. It helps in certain UI-based apps but comes with tradeoffs. So we stick to byte arrays in server environments.

Safe Practices Checklist

  • Avoid immutable strings for sensitive data
  • Use byte arrays
  • Zero arrays immediately after use
  • Don’t write secrets to logs
  • Don’t pass secrets to third-party libraries

Best Practices for Cryptographic Key Management

We’ve made our share of mistakes here. Leaving keys in app settings. Forgetting to rotate. But we’ve learned—sometimes the hard way.

How We Store Keys Now

  • Encrypted configuration files
  • Hardware security modules (HSMs)
  • Environment variables (short term only)
  • Platform-native secure storage

Key rotation? We schedule it. We log it. And we automate it where we can.

Secure Key Exchange

When we need to exchange AES keys between systems, we wrap them with RSA. The recipient decrypts with their private key.

We verify authenticity by signing the message with our private RSA key. They check the signature with our public key. That way, we know who sent what.

Putting It All Together

Here’s how we handle secure data end-to-end:

  1. Generate AES key + IV
  2. Encrypt data with AES (GCM preferred)
  3. Encrypt AES key with RSA
  4. Send encrypted key + encrypted data
  5. On the other side:
    • Decrypt AES key with RSA
    • Decrypt data with AES
  6. Clear memory buffers

Every time we skip a step or rush through it, we increase risk. So we slow down, double-check, and keep our code clean.

FAQ

What is the C# cryptography API and why should I use it for secure data handling?

The C# cryptography API gives you built-in tools to protect sensitive information in your apps. It handles encryption, decryption, and password security without you having to build these features from scratch. This saves time and reduces the chance of making security mistakes that could expose your data.

How do I encrypt data using C# cryptography classes?

You can use the AES class to encrypt your data safely. Create an AES instance, generate a key and initialization vector, then use the CreateEncryptor method to transform your plain text into encrypted bytes. Always store your keys separately from the encrypted data to keep everything secure.

What’s the difference between symmetric and asymmetric encryption in C# secure data handling?

Symmetric encryption uses the same key to lock and unlock data, making it fast for large files. Asymmetric encryption uses two different keys – one public and one private. Symmetric works great for storing data, while asymmetric helps when you need to share encrypted information with others safely.

How do I securely store passwords using C# cryptography API features?

Never store passwords as plain text. Use the Rfc2898DeriveBytes class to create password hashes with salt. This class applies thousands of rounds of hashing, making it extremely hard for attackers to crack passwords even if they steal your database. Always use a unique salt for each password.

What are the most common mistakes people make with C# cryptography and secure data handling?

People often reuse the same encryption keys, skip input validation, or store keys alongside encrypted data. Another big mistake is using weak random number generators or hardcoding secrets in source code. These errors can make your carefully encrypted data vulnerable to attacks.

How do I generate secure random numbers and keys for C# cryptography operations?

Use the RandomNumberGenerator class instead of the regular Random class for anything security-related. This gives you cryptographically strong random values that attackers can’t predict. Generate new keys for each encryption session and make sure they’re long enough for your chosen algorithm.

What’s the best way to handle cryptographic keys in C# secure data handling applications?

Store keys in secure key management systems or encrypted configuration files, never in your source code. Use the Data Protection API for Windows applications or consider cloud key management services. Rotate keys regularly and have a plan for what happens if a key gets compromised.

How do I verify data integrity when implementing secure data handling with C# cryptography?

Use HMAC classes to create message authentication codes that detect tampering. Combine this with encryption for both privacy and integrity protection. The HMACSHA256 class works well for most applications. Always verify the HMAC before decrypting data to catch any modifications or corruption.

Conclusion

Encryption isn’t a feature—it’s a mindset. We don’t bolt it on. We build it in. We keep our keys safe, treat memory like it’s glass, and audit every cryptographic call with suspicion. If something feels off, we trust that instinct.

And we don’t stop there. We test. We pen-test. We build proof-of-concepts and break them on purpose. Because every time we do, we learn what we missed.

So, we encrypt. We sign. We store keys like secrets. And above all—we stay paranoid. That’s how we keep our data safe.

Want to sharpen your secure coding skills with real-world, hands-on training?
Join the Secure Coding Practices Bootcamp and learn to build safer software from day one.

Related Articles

References

  1. https://en.wikipedia.org/wiki/Bouncy_Castle_(cryptography)
  2. https://en.wikipedia.org/wiki/Data_security

Avatar photo
Leon I. Hicks

Hi, I'm Leon I. Hicks — an IT expert with a passion for secure software development. I've spent over a decade helping teams build safer, more reliable systems. Now, I share practical tips and real-world lessons on securecodingpractices.com to help developers write better, more secure code.