19

I am after a symmetric encryption/decryption routine in C#. I know there have been a few questions on this topic before, but most of the answers seem to be about the philosophy of encryption rather than giving actual code.

Update: I'd really like to see some code, rather than just links. Many thanks!

Craig Schwarze
  • 11,367
  • 15
  • 60
  • 80
  • 1
    I'd recommend using Rijndael, then :) – Ian P Jan 27 '10 at 22:25
  • 12
    Never use encryption technology for something it was not intended for. Encryption was not designed to protect a secret from a fully-trusted user, and cannot be safely used for that. If your user can run a program that decrypts the data then the user can decrypt the data. What you're trying to do is a very, very bad idea and I strongly encourage you to not do so. – Eric Lippert Jan 27 '10 at 22:39
  • What would you suggest instead, Eric? – Craig Schwarze Jan 27 '10 at 22:41
  • 1
    Make the server robust in the face of hostile clients. – Eric Lippert Jan 27 '10 at 22:47
  • 5
    Sure there is. The user isn't executing trades on their home computer, and they are not reporting audit logs to their home computer. There are at least two servers involved: the broker's server and the audit server. Those are the ones that you can trust, so your security system should be relying on them, not on the presumed-hostile user. (If the brokers are hostile to you and the customers and brokers are conspiring to rip you off, then you have an even bigger problem.) – Eric Lippert Jan 27 '10 at 23:14
  • plz send teh codz thx – JoelFan Jul 01 '17 at 20:04
  • Have a look at this [book : Practical Cryptography for Developers](https://cryptobook.nakov.com/symmetric-key-ciphers) – Ognyan Dimitrov Jun 28 '19 at 08:17

8 Answers8

20

Look at the example code at the bottom of this page.

Copy-pasting it here:

int Rfc2898KeygenIterations= 100;
int AesKeySizeInBits = 128;
String Password = "VerySecret!";
byte[] Salt = new byte[16];
System.Random rnd = new System.Random(); 
rnd.NextBytes(Salt);
byte[] rawPlaintext = System.Text.Encoding.Unicode.GetBytes("This is all clear now!");
byte[] cipherText= null;
byte[] plainText= null;
using (Aes aes = new AesManaged())
{
    aes.Padding = PaddingMode.PKCS7;
    aes.KeySize = AesKeySizeInBits;
    int KeyStrengthInBytes= aes.KeySize/8;
    System.Security.Cryptography.Rfc2898DeriveBytes rfc2898 =
        new System.Security.Cryptography.Rfc2898DeriveBytes(Password, Salt, Rfc2898KeygenIterations);
    aes.Key = rfc2898.GetBytes(KeyStrengthInBytes);
    aes.IV = rfc2898.GetBytes(KeyStrengthInBytes);
    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
        {
            cs.Write(rawPlaintext, 0, rawPlaintext.Length);
        }
        cipherText= ms.ToArray();
    }

    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
        {
            cs.Write(cipherText, 0, cipherText.Length);
        }
        plainText = ms.ToArray();
    }
}
string s = System.Text.Encoding.Unicode.GetString(plainText);
Console.WriteLine(s);
orip
  • 73,323
  • 21
  • 116
  • 148
  • 6
    Ewww, using the non-secure Random to create the salt? Bad bad idea. – blowdart Jan 27 '10 at 22:53
  • 15
    @blowdart - Nope. Salt and IV need to be as unique as possible, but generating them securely means nothing. Secure random means random that's hard to predict, and you don't care if your IV or salt are predicted - they're passed/stored in plaintext anyway. – orip Jan 27 '10 at 23:20
  • @blowdart - I concede your point about IV in CBC mode. It doesn't hold for for salt in PBKDF2 though. – orip Jan 27 '10 at 23:23
  • Sorry to res this, but how are you supposed to decrypt the cipherText without the original aes object? It looks like you've lost the IV. – Benjamin Oct 14 '15 at 21:07
  • @ginkner The ciphertext contains the IV – orip Oct 15 '15 at 08:10
  • You need the resulting key to be highly unpredictable, which can be accomplished with either a crypto-random salt or a crypto-random IV. By convention we rotate the IV every message and keep salt constant, but could just as easily do the reverse as long as one or the other is changed for every message (the one that is changed has to be crypto-random). While you don't *need* both IV and salt to be crypto-random, there's no real reason not to. – Matthew Jan 04 '16 at 17:04
  • @Matthew but don't both sides have to know the key in order for the payload to be successfully decrypted? If you randomize both the key and IV, how would the recipient be able to decrypt everything? – Tom Lint Aug 25 '16 at 14:30
  • I don't understand the benefit of the entire encryption thing, if you end up holding a plain-text passphrase at the end. What do you do with it? Encrypt it again and then encrypt again the passphrase for your passphrase... and so on? lol – hyankov Sep 21 '16 at 13:19
9

Here's a simple solution that I found on a VB.NET forum and converted to C#. It certainly helped me understand the topic better.

// Shamelessly lifted from http://discuss.itacumens.com/index.php?topic=62872.0, 
// then converted to C# (http://www.developerfusion.com/tools/convert/vb-to-csharp/) and
// changed where necessary.
public class Encryptor
{
    private static SymmetricAlgorithm _cryptoService = new TripleDESCryptoServiceProvider(); 
    // maybe use AesCryptoServiceProvider instead?

    // vector and key have to match between encryption and decryption
    public static string Encrypt(string text, byte[] key, byte[] vector)
    {
        return Transform(text, _cryptoService.CreateEncryptor(key, vector));
    }

    // vector and key have to match between encryption and decryption
    public static string Decrypt(string text, byte[] key, byte[] vector)
    {
        return Transform(text, _cryptoService.CreateDecryptor(key, vector));
    }

    private static string Transform(string text, ICryptoTransform cryptoTransform)
    {
        MemoryStream stream = new MemoryStream();
        CryptoStream cryptoStream = new CryptoStream(stream, cryptoTransform, CryptoStreamMode.Write);

        byte[] input = Encoding.Default.GetBytes(text);

        cryptoStream.Write(input, 0, input.Length);
        cryptoStream.FlushFinalBlock();

        return Encoding.Default.GetString(stream.ToArray());
    }
}
Jan
  • 683
  • 10
  • 18
  • This could occasionally fail if the system's default encoding is one that disallows certain characters or if the system's default is changed between encryption and description. To fix this, replace `Encoding.Default` with something like `Encoding.GetEncoding(437)`. – Edward Brey Jun 16 '21 at 14:48
  • Also, it's recommended to use the `Create` method to create the crypto provider, as in `Aes.Create`. – Edward Brey Jun 16 '21 at 14:56
8

Well for starters keys are not strings, keys are binary blobs. PlainText is the same, it's not actually text, again it's a binary blob.

Now of course you can convert strings to byte arrays using Encoding.UTF8.GetBytes(message), however when converting keys back and forth it's a little more complicated, you usually use Convert.ToBase64String and Convert.FromBase64String.

Don't forget that block ciphers also need one more thing, the Initialization Vector, so really your method signatures should be

byte[] Encrypt(byte[] plainText, byte[] key, byte[] iv)

byte[] Decrypt(byte[] cipherText, byte[] key, byte[] iv)

The key and IVs must be cryptographically secure random numbers, don't just type them and don't use C#'s Random function. The size of the key and the IV depend on the cipher algorithm used, and can be accessed by the properties on the classes.

To generate a CSRPNG you do something like

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] key = new byte[algorithm.KeySizeValue / 8];
rng.GetBytes(key);
byte[] iv = new byte[algorithm.BlockSizeValue / 8];
rng.GetBytes(iv);

You can also use the Rfc2898DeriveBytes class to derive a key and IV from a password and a salt, but again the salt should be a cryptographically secure random number. You should also note when you create a symmetric algorithm a secure key and IV is generated for you.

This way you can then choose the correct encoding for your text, be it UTF8, ASCII or whatever. The links have enough samples so cutting and pasting in here is rather pointless.

AD.Net
  • 13,352
  • 2
  • 28
  • 47
blowdart
  • 55,577
  • 12
  • 114
  • 149
3

what u want are cryptoserviceproviders in the class library

like this one for AES

http://msdn.microsoft.com/en-us/library/system.security.cryptography.aescryptoserviceprovider_members.aspx

pm100
  • 48,078
  • 23
  • 82
  • 145
3

Convert your text, key and initialization vector to bytes first using the encoding of your choice. Then use the triple DES provider, as demonstrated here:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledes.aspx

Or the one for AES, if you think triple DES is too old-school, or whatever.

Out of curiosity, how are you planning on communicating the secret key?

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
1

This approach uses strings for an encryption password and the encrypted text. It's useful for interoperating with non-.NET code that requires strings.

It uses the OEM United States encoding for converting between binary data and strings so that the only transformation is to add or remove an extra 0 byte in each unicode characters (versus UTF8 encoding, which interprets characters' bytes dynamically).

public class AesEncryptor {
    readonly Encoding encoding = Encoding.GetEncoding(437); // Using the OEM United States encoding since it provides symmetric transform for 0..255.
    readonly AesManaged aes;

    /// <param name="password">Should be at least 32 characters.</param>
    public AesEncryptor(string password) {
        const int keySize = 256;
        const int saltSize = 16;
        var derivedBytes = new Rfc2898DeriveBytes(password, saltSize);
        aes = new AesManaged { Padding = PaddingMode.PKCS7, KeySize = keySize, Key = derivedBytes.GetBytes(keySize / 8), IV = derivedBytes.Salt };
    }

    public string Encrypt(string text) => CryptoTransform(text, aes.CreateEncryptor());
    public string Decrypt(string text) => CryptoTransform(text, aes.CreateDecryptor());

    string CryptoTransform(string text, ICryptoTransform cryptoTransform) {
        byte[] input = encoding.GetBytes(text);
        var stream = new MemoryStream();
        using (var cryptoStream = new CryptoStream(stream, cryptoTransform, CryptoStreamMode.Write))
            cryptoStream.Write(input, 0, input.Length);
        return encoding.GetString(stream.ToArray());
    }
}
Edward Brey
  • 40,302
  • 20
  • 199
  • 253
1

Posting from MSDN documetation for AES symmetric algorithm:

using System;
using System.IO;
using System.Security.Cryptography;

namespace Aes_Example
{
    class AesExample
    {
        public static void Main()
        {
            string original = "Here is some data to encrypt!";

            // Create a new instance of the AesCryptoServiceProvider
            // class.  This generates a new key and initialization
            // vector (IV).
            using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider())
            {
                // Encrypt the string to an array of bytes.
                byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);

                // Decrypt the bytes to a string.
                string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);

                //Display the original data and the decrypted data.
                Console.WriteLine("Original:   {0}", original);
                Console.WriteLine("Round Trip: {0}", roundtrip);
            }
        }
        static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");
            byte[] encrypted;

            // Create an AesCryptoServiceProvider object
            // with the specified key and IV.
            using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;
        }

        static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an AesCryptoServiceProvider object
            // with the specified key and IV.
            using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }

            return plaintext;
        }
    }
}
RBT
  • 24,161
  • 21
  • 159
  • 240