442

I'm looking for very simple obfuscation (like encrypt and decrypt but not necessarily secure) functionality for some data. It's not mission critical. I need something to keep honest people honest, but something a little stronger than ROT13 or Base64.

I'd prefer something that is already included in the .NET framework 2.0, so I don't have to worry about any external dependencies.

I really don't want to have to mess around with public/private keys, etc. I don't know much about encryption, but I do know enough to know that anything I wrote would be less than worthless... In fact, I'd probably screw up the math and make it trivial to crack.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Matt Dawdy
  • 19,247
  • 18
  • 66
  • 91
  • 3
    Hi Mark -- no problem. I felt bad that I had to unaccept the answer from richdiet, as I did actually use his solution and it worked just fine. However, I kept coming back here to read the other answers, and yours really is better. No reason to tell people to use something that, while it works, is not really a great way to do something when there is a better answer available. – Matt Dawdy Feb 07 '10 at 03:53
  • 3
    Save yourself hours and use the HttpServerUtility.UrlTokenEn/Decode to convert back and forth from the byte arrays to a url friendly string. – Praesagus Feb 13 '10 at 00:25
  • 33
    +1 for not trying to roll your own clever design. You may not know much about encryption, but the fact that you know that puts you lightyears ahead of most developers I've met who don't know much about encryption but think they can create their own solution anyway. – Dinah Mar 11 '10 at 18:26
  • 6
    Attention: Many of the answers in this question are unauthenticated encryption only. This means that the **attacker can change the data without the app noticing**. It leads to other serious vulnerabilities as well (like decryption without key due to padding oracle). TL;DR: Don't use the code in the answers given if you are not OK with that, or don't understand what I just said. – usr Jan 26 '13 at 16:51
  • This question has two stipulations, "not mission critical security", and "no external dependencies" and for most that is going to hurt them security wise copying and pasting these answers. Ideally, you want to use a [high level open source library](http://jbtule.github.com/keyczar-dotnet/) for better security, *disclaimer: I ported this to c# so it would exist.*, If that isn't going to work, don't make concessions, authenticate the ciphertext, properly use the IV, such as in my [Modern Examples of Symmetric Authenticated Encryption of a string C#](http://stackoverflow.com/a/10366194/637783). – jbtule Feb 20 '13 at 13:54
  • 37
    **Not a single answer to this question describes secure encryption.** Use jbtule's answer at [Encrypt and decrypt a string](http://stackoverflow.com/a/10366194/445517) instead. – CodesInChaos May 20 '16 at 15:34
  • @paxdiablo This appears to genuinely be about C#, not C. Was your edit a mistake? – Kyle Strand May 10 '17 at 19:14
  • @CodesInChaos Why did you close & then reopen? – Kyle Strand May 10 '17 at 19:15
  • 2
    @KyleStrand I don't remember. But I guess I tried to close it as duplicate of https://stackoverflow.com/questions/202011/encrypt-and-decrypt-a-string instead of https://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp but couldn't vote to close twice. That certainly fits with me closing the latter as a duplicate of the former at that time. – CodesInChaos May 10 '17 at 19:23
  • 1
    [There is an ongoing meta discussion about this question](https://meta.stackoverflow.com/q/348946/497418). – zzzzBov May 11 '17 at 16:38

17 Answers17

474

Other answers here work fine, but AES is a more secure and up-to-date encryption algorithm. This is a class that I obtained a few years ago to perform AES encryption that I have modified over time to be more friendly for web applications (e,g. I've built Encrypt/Decrypt methods that work with URL-friendly string). It also has the methods that work with byte arrays.

NOTE: you should use different values in the Key (32 bytes) and Vector (16 bytes) arrays! You wouldn't want someone to figure out your keys by just assuming that you used this code as-is! All you have to do is change some of the numbers (must be <= 255) in the Key and Vector arrays (I left one invalid value in the Vector array to make sure you do this...). You can use https://www.random.org/bytes/ to generate a new set easily:

Using it is easy: just instantiate the class and then call (usually) EncryptToString(string StringToEncrypt) and DecryptString(string StringToDecrypt) as methods. It couldn't be any easier (or more secure) once you have this class in place.


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


public class SimpleAES
{
    // Change these keys
    private byte[] Key = __Replace_Me__({ 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 });

    // a hardcoded IV should not be used for production AES-CBC code
    // IVs should be unpredictable per ciphertext
    private byte[] Vector = __Replace_Me__({ 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 2521, 112, 79, 32, 114, 156 });


    private ICryptoTransform EncryptorTransform, DecryptorTransform;
    private System.Text.UTF8Encoding UTFEncoder;

    public SimpleAES()
    {
        //This is our encryption method
        RijndaelManaged rm = new RijndaelManaged();

        //Create an encryptor and a decryptor using our encryption method, key, and vector.
        EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
        DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);

        //Used to translate bytes to text and vice versa
        UTFEncoder = new System.Text.UTF8Encoding();
    }

    /// -------------- Two Utility Methods (not used but may be useful) -----------
    /// Generates an encryption key.
    static public byte[] GenerateEncryptionKey()
    {
        //Generate a Key.
        RijndaelManaged rm = new RijndaelManaged();
        rm.GenerateKey();
        return rm.Key;
    }

    /// Generates a unique encryption vector
    static public byte[] GenerateEncryptionVector()
    {
        //Generate a Vector
        RijndaelManaged rm = new RijndaelManaged();
        rm.GenerateIV();
        return rm.IV;
    }


    /// ----------- The commonly used methods ------------------------------    
    /// Encrypt some text and return a string suitable for passing in a URL.
    public string EncryptToString(string TextValue)
    {
        return ByteArrToString(Encrypt(TextValue));
    }

    /// Encrypt some text and return an encrypted byte array.
    public byte[] Encrypt(string TextValue)
    {
        //Translates our text value into a byte array.
        Byte[] bytes = UTFEncoder.GetBytes(TextValue);

        //Used to stream the data in and out of the CryptoStream.
        MemoryStream memoryStream = new MemoryStream();

        /*
         * We will have to write the unencrypted bytes to the stream,
         * then read the encrypted result back from the stream.
         */
        #region Write the decrypted value to the encryption stream
        CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write);
        cs.Write(bytes, 0, bytes.Length);
        cs.FlushFinalBlock();
        #endregion

        #region Read encrypted value back out of the stream
        memoryStream.Position = 0;
        byte[] encrypted = new byte[memoryStream.Length];
        memoryStream.Read(encrypted, 0, encrypted.Length);
        #endregion

        //Clean up.
        cs.Close();
        memoryStream.Close();

        return encrypted;
    }

    /// The other side: Decryption methods
    public string DecryptString(string EncryptedString)
    {
        return Decrypt(StrToByteArray(EncryptedString));
    }

    /// Decryption when working with byte arrays.    
    public string Decrypt(byte[] EncryptedValue)
    {
        #region Write the encrypted value to the decryption stream
        MemoryStream encryptedStream = new MemoryStream();
        CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write);
        decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
        decryptStream.FlushFinalBlock();
        #endregion

        #region Read the decrypted value from the stream.
        encryptedStream.Position = 0;
        Byte[] decryptedBytes = new Byte[encryptedStream.Length];
        encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
        encryptedStream.Close();
        #endregion
        return UTFEncoder.GetString(decryptedBytes);
    }

    /// Convert a string to a byte array.  NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so).
    //      System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
    //      return encoding.GetBytes(str);
    // However, this results in character values that cannot be passed in a URL.  So, instead, I just
    // lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100).
    public byte[] StrToByteArray(string str)
    {
        if (str.Length == 0)
            throw new Exception("Invalid string value in StrToByteArray");

        byte val;
        byte[] byteArr = new byte[str.Length / 3];
        int i = 0;
        int j = 0;
        do
        {
            val = byte.Parse(str.Substring(i, 3));
            byteArr[j++] = val;
            i += 3;
        }
        while (i < str.Length);
        return byteArr;
    }

    // Same comment as above.  Normally the conversion would use an ASCII encoding in the other direction:
    //      System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    //      return enc.GetString(byteArr);    
    public string ByteArrToString(byte[] byteArr)
    {
        byte val;
        string tempStr = "";
        for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
        {
            val = byteArr[i];
            if (val < (byte)10)
                tempStr += "00" + val.ToString();
            else if (val < (byte)100)
                tempStr += "0" + val.ToString();
            else
                tempStr += val.ToString();
        }
        return tempStr;
    }
}
jbtule
  • 31,383
  • 12
  • 95
  • 128
Mark Brittingham
  • 28,545
  • 12
  • 80
  • 110
  • Thanks - this is one of my favorite answers and I'm happy that folks are getting use out of it. Some folks think that you should salt the beginning of the encrypted string to avoid discovery of the Vector array but it really depends on just how paranoid you are about someone trying to decrypt your strings and whether they know what is being encrypted to begin with. – Mark Brittingham May 20 '09 at 21:37
  • 3
    The `using` police (that's not me) might be after you for some of the stream usage – Chris S Sep 23 '09 at 21:45
  • Would that be to ensure that the memoryStream is closed? I use "using" much more now than I did when I wrote this algorithm so I can see your point. Don't want to change this without testing it, however, so it might be a little while... – Mark Brittingham Sep 24 '09 at 10:03
  • @Chris, there's no benefit to Disposing of MemoryStreams -- they're not like normal streams that hold an OS resource -- they just hold a buffer object that will be collected as all other in-memory objects will be. – Frank Krueger Nov 12 '09 at 05:22
  • 54
    @AndyMcKenna - That's done on purpose so that you change the values in the arrays, as Mark notes in the second paragraph. – Pauk Dec 02 '09 at 12:27
  • 3
    Pauk's right. You REALLY shouldn't use the keys shown in the code. Change 'em to your own. With a "bad" key, you'll have no choice ;-) – Mark Brittingham Dec 02 '09 at 13:29
  • Hi Mark, For a given string, is there a way to shorten the length of the encrypted string/bytearray? – Uchitha Dec 03 '09 at 10:36
  • Uchitha - unfortunately no. It is just in the nature of most modern encryption algorithms that they expand the encrypted string in this way. You *might* try a zip-type algorithm on the result. However, I think that you'd find that the shrinkage would be minimal due to the nature of the encryption output. I have to admit though - I'm not sure of this as I've never tried it. I just seem to remember reading somewhere that there is little compression to be had. – Mark Brittingham Dec 06 '09 at 17:49
  • Let's say one wanted to add salting to this how would one go about it? :-p – Hungry Beast Mar 14 '10 at 20:16
  • 2
    Mark, you shouldn't use words like "above" in your answer since the answers move around in relation to each other. In fact, the only thin above this answer in vote order is the question. – paxdiablo Aug 13 '10 at 09:39
  • 2
    Nice - note that you could use Convert.ToBase64String and Convert.FromBase64String to get a more compact string representation in your EncryptString and DecryptString methods (rather than your hand-rolled StrToByteArray and ByteArrayToStr methods) – zcrar70 Sep 10 '10 at 21:46
  • Thank you for this! Is there any way to know what the length would be of the output byte-array, from Encrypt, based on a specific length of the input string? – Andreas Ågren Jan 13 '11 at 12:33
  • Andy, two things pop into my head right away. First, try 4 x Length. It is probably wrong but, well, it popped into my head as something I remember finding earlier. Second, try running a variety of strings and see if there is a constant relationship between input size and output size. You can NOT just try it once and assume that this will hold for all. Indeed, the relationship may well vary by content. – Mark Brittingham Jan 14 '11 at 14:54
  • Mark, thanks for replying. I did try what you suggested on 9 million randomly generated strings that were 10 - 16 characters long. 15 - 16 characters always returned 32 bytes. When going down to 11 chars 32 bytes was returned only a few times, and finally with 10 chars all 9m encryptions returned 16 bytes. Judging only by this your conclusion is correct, number of bytes vary by content. My strings will always be 10 chars so to be safe I assumed that the length might be 32 bytes anyway (this exercise was to decide the length of a varbinary database field). Once again thanks! – Andreas Ågren Jan 19 '11 at 12:27
  • 5
    Converting to base 64 then URL encoding would result in less code and more compact output. – Mud Mar 31 '11 at 23:07
  • This code is using RijndaelManaged. Is the code equivalent to 128 (or above) AES? – Anton Andreev Oct 04 '11 at 13:56
  • FYI: Small compiler error in line 11 where you used a number above 255 in the vector byte array. – DomenicDatti Oct 10 '11 at 18:39
  • 3
    @DomenicDatti - this is not an error. If you read the comments, you'll see that I did this purposely so that people don't just use the key values shown in the example. – Mark Brittingham Oct 15 '11 at 02:22
  • 1
    @AntonAndreev see http://blogs.msdn.com/b/shawnfa/archive/2006/10/09/the-differences-between-rijndael-and-aes.aspx for interoperability - however now that the Aes class is in .net 3.5+, why not just use that ie System.Security.Cryptography.AesManaged ? : ) – Adam Tuliper Jan 23 '12 at 21:39
  • 42
    You should not use the IV like this. For a given two messages, they should not have been encrypted with the same Key and same IV. The IV should be random for each message, prepended to the cryptostream, and read out before decryption. http://crypto.stackexchange.com/a/82/1934 – jbtule Apr 27 '12 at 13:39
  • 3
    @jbtule - That is why I included the "GenerateEncryptionVector" function - so you could do this in an environment that needed maximal security. In most commercial encryption settings, though, this is a bit of overkill. Of course, as time goes on, what tends to be more exotic in the way of security evolves to become standard due to a simple arms race between the developers and those that would exploit information systems. – Mark Brittingham Apr 27 '12 at 15:47
  • 30
    Using a random IV for each message is not exotic or new, just important and part of the design of the algorithm. Using a predictable IV for every message is a common crypto mistake that doesn't need to be perpetuated. – jbtule Apr 27 '12 at 16:08
  • Made the blind copy paste and happy too see it is working after facing hell of "Error occurred while decoding OAEP padding". – prabhakaran May 07 '12 at 10:37
  • Note that this code uses Cipher-Block Chaining (CBC) as its mode, which you should take into account. – Polynomial Jul 25 '12 at 14:54
  • 1
    The static functions GenerateEncryptionKey and GenerateEncryptionVector do not need to call GenerateKey/GenerateIV as this is done in the constructor of RijndaelManaged class. – Michel van Engelen Aug 21 '12 at 07:49
  • 14
    Note also that a consequence of using CBC as its mode is that you are likely to be vulnerable to [padding oracle attacks](http://www.skullsecurity.org/blog/2013/a-padding-oracle-example). Use authenticated encryption, and whenever possible, *do not implement cryptography yourself*. – Stephen Touset Jan 15 '13 at 23:37
  • Just wondering if there is a reason why you (and all the others who answered on this thread) go through all the contortions of using MemoryStream and CryptoStream instead of using the ICryptoTransform.TransformFinalBlock() method, as demonstrated in this program: http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/ ? If there is a reason to avoid doing it that way I'd certainly like to hear about it. – RenniePet Sep 05 '13 at 03:11
  • 5
    Do not use a constant IV. Please do not tell people to do this, you are breaking a security feature of the encryption algorithm. There is no reason why you can't generate a new IV for every encryption and append it to your cryptotext. – Tom Heard Dec 02 '13 at 23:55
  • 1
    "All you have to do is change some of the numbers (must be <= 255) in the Key and Vector arrays (I left one invalid value in the Vector array to make sure you do this...). You can use https://www.random.org/bytes/ to generate a new set easily." That's not good advice at all. Change *some* of the numbers? – Maarten Bodewes Apr 09 '15 at 22:49
  • 1
    Note that base 64 uses ~33% more characters than bytes. The current method uses 200% more characters. – Maarten Bodewes Apr 09 '15 at 23:00
  • I'm using this algorythm as is but over an heavy use I saw some problem. 100% of the strings I encrypt are correct but for no reason sometimes, if I decrypt and re-encrypt the data it fails silently. I get an encrypted value that isn't possible to decrypt. it happenned 10 times over 200000 iterations. Not catastrophic but this cause us problem. Anyone else experimenting this? – Yannick Richard Nov 20 '15 at 15:10
  • Yannick - I haven't run into any problems in my use of the algorithm but, then again, with a 10/200000 failure rate, I'm not sure I would have caught it. Since this is essentially just a package around methods offered by MS, I imagine that it will come down to something from their side. – Mark Brittingham Nov 25 '15 at 19:44
  • 2
    I found the problem. The encrypt and decrypt method aren't thread safe. I added code to make it thread safe and no more errors. For further references, here is the error I had : Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's – Yannick Richard Nov 26 '15 at 14:12
  • 3
    The IV should be different each time 1) so that if you encrypt the same message multiple times, it gives you different results (similar to salt) 2) because some algorithms fail catastrophically if you ever reuse the IV – Mark Sowul Dec 19 '16 at 19:10
  • 58
    **Security Warning: Do Not Use This Code** Despite being the accepted answer, there are severe security issues mention in the above comments that the author has continued to ignore for 8 years. – jbtule May 08 '17 at 21:52
  • 7
    Why so many comments here about the answer being insecure? Not every encryption needs to be very secure. The OP asked for a simple insecure encryption. Perhaps security is not important. Just the fact that the text is garbled and not Base64 encoded might be just sufficient. – Tony_Henrich Sep 21 '17 at 22:30
186

I cleaned up SimpleAES (above) for my use. Fixed convoluted encrypt/decrypt methods; separated methods for encoding byte buffers, strings, and URL-friendly strings; made use of existing libraries for URL encoding.

The code is small, simpler, faster and the output is more concise. For instance, johnsmith@gmail.com produces:

SimpleAES: "096114178117140150104121138042115022037019164188092040214235183167012211175176167001017163166152"
SimplerAES: "YHKydYyWaHmKKnMWJROkvFwo1uu3pwzTr7CnARGjppg%3d"

Code:

public class SimplerAES
{
    private static byte[] key = __Replace_Me__({ 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 });

    // a hardcoded IV should not be used for production AES-CBC code
    // IVs should be unpredictable per ciphertext
    private static byte[] vector = __Replace_Me_({ 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 221, 112, 79, 32, 114, 156 });

    private ICryptoTransform encryptor, decryptor;
    private UTF8Encoding encoder;

    public SimplerAES()
    {
        RijndaelManaged rm = new RijndaelManaged();
        encryptor = rm.CreateEncryptor(key, vector);
        decryptor = rm.CreateDecryptor(key, vector);
        encoder = new UTF8Encoding();
    }

    public string Encrypt(string unencrypted)
    {
        return Convert.ToBase64String(Encrypt(encoder.GetBytes(unencrypted)));
    }

    public string Decrypt(string encrypted)
    {
        return encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
    }

    public byte[] Encrypt(byte[] buffer)
    {
        return Transform(buffer, encryptor);
    }

    public byte[] Decrypt(byte[] buffer)
    {
        return Transform(buffer, decryptor);
    }

    protected byte[] Transform(byte[] buffer, ICryptoTransform transform)
    {
        MemoryStream stream = new MemoryStream();
        using (CryptoStream cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }
        return stream.ToArray();
    }
}
jbtule
  • 31,383
  • 12
  • 95
  • 128
Mud
  • 28,277
  • 11
  • 59
  • 92
  • 2
    When decoding, I had to replace space with + for it to work with QueryString in Chrome: (new SimplerAES()).Decrypt(Request.QueryString["myParam"].Replace(' ', '+')); – live-love Oct 30 '13 at 18:56
  • 21
    Do not ever use a constant Initialization Vector, see: http://crypto.stackexchange.com/questions/66/what-are-the-details-of-the-des-weakness-of-reusing-the-same-iv-in-cbc-mode-with/82#82 for more information about why. Instead, generate a new IV for each encryption and append it to the cryptotext, so much better and not to hard. – Tom Heard Dec 03 '13 at 02:17
  • 1
    `EncryptToUrl` and `DecryptFromUrl` don't have any necessary business in this class. Otherwise ; great answer. – Timothy Groote Jan 08 '14 at 09:00
  • 2
    Be aware that the output of the EncryptToUrl method in this solution (or any use of an UrlEncoded base 64 string in general) won't work by default under IIS 7 when used as part of an URL path (not query string), as in an ASP.NET MVC route, due to an IIS 7 security setting. For more, see: http://stackoverflow.com/a/2014121/12484 – Jon Schneider Feb 28 '14 at 20:18
  • Like 40-Love I too had to Replace characters in my cypher string. The string was coming from a browser QueryString, I'm not exactly sure what was wrong with it but the call to Replace(' ', '+')); before I Decrypt fixed it for me.# – learnerplates Apr 09 '15 at 12:42
  • 5
    @TomHeard How would one go about doing that, with the above code? – MKII Jul 27 '15 at 08:46
  • 27
    **Security Warning: Do Not Use This Code** See comment by @TomHeard – jbtule May 08 '17 at 21:54
35

Yes, add the System.Security assembly, import the System.Security.Cryptography namespace. Here's a simple example of a symmetric (DES) algorithm encryption:

DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.GenerateKey();
byte[] key = des.Key; // save this!

ICryptoTransform encryptor = des.CreateEncryptor();
// encrypt
byte[] enc = encryptor.TransformFinalBlock(new byte[] { 1, 2, 3, 4 }, 0, 4);

ICryptoTransform decryptor = des.CreateDecryptor();

// decrypt
byte[] originalAgain = decryptor.TransformFinalBlock(enc, 0, enc.Length);
Debug.Assert(originalAgain[0] == 1);
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
ZeroBugBounce
  • 3,652
  • 3
  • 31
  • 40
  • 5
    This is a nice, compact two-way encryption. The only caveat is that DES is no longer considered state-of-the-art security. That title now goes to the AES algorithm I discuss below. – Mark Brittingham Dec 12 '08 at 13:04
  • @richdiet. I'm sorry I unaccepted your answer. The other answer with 37+ votes because it is more current. Thank you for your answer, as it is still a good one. – Matt Dawdy Feb 06 '10 at 23:11
  • 14
    @MarkBrittingham: any block cipher without block chaining function, initialisation vector and proper padding is insecure. Using DES is the least important problem with this scheme. – Hubert Kario Aug 19 '12 at 01:00
  • 2
    So where is the key used? – Alex Oct 31 '13 at 13:19
  • 22
    **Security Warning: Do Not Use This Code** See comment by @HubertKario – jbtule May 08 '17 at 22:20
29

Just thought I'd add that I've improved Mud's SimplerAES by adding a random IV that's passed back inside the encrypted string. This improves the encryption as encrypting the same string will result in a different output each time.

public class StringEncryption
{
    private readonly Random random;
    private readonly byte[] key;
    private readonly RijndaelManaged rm;
    private readonly UTF8Encoding encoder;

    public StringEncryption()
    {
        this.random = new Random();
        this.rm = new RijndaelManaged();
        this.encoder = new UTF8Encoding();
        this.key = Convert.FromBase64String("Your+Secret+Static+Encryption+Key+Goes+Here=");
    }

    public string Encrypt(string unencrypted)
    {
        var vector = new byte[16];
        this.random.NextBytes(vector);
        var cryptogram = vector.Concat(this.Encrypt(this.encoder.GetBytes(unencrypted), vector));
        return Convert.ToBase64String(cryptogram.ToArray());
    }

    public string Decrypt(string encrypted)
    {
        var cryptogram = Convert.FromBase64String(encrypted);
        if (cryptogram.Length < 17)
        {
            throw new ArgumentException("Not a valid encrypted string", "encrypted");
        }

        var vector = cryptogram.Take(16).ToArray();
        var buffer = cryptogram.Skip(16).ToArray();
        return this.encoder.GetString(this.Decrypt(buffer, vector));
    }

    private byte[] Encrypt(byte[] buffer, byte[] vector)
    {
        var encryptor = this.rm.CreateEncryptor(this.key, vector);
        return this.Transform(buffer, encryptor);
    }

    private byte[] Decrypt(byte[] buffer, byte[] vector)
    {
        var decryptor = this.rm.CreateDecryptor(this.key, vector);
        return this.Transform(buffer, decryptor);
    }

    private byte[] Transform(byte[] buffer, ICryptoTransform transform)
    {
        var stream = new MemoryStream();
        using (var cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }

        return stream.ToArray();
    }
}

And bonus unit test

[Test]
public void EncryptDecrypt()
{
    // Arrange
    var subject = new StringEncryption();
    var originalString = "Testing123!£$";

    // Act
    var encryptedString1 = subject.Encrypt(originalString);
    var encryptedString2 = subject.Encrypt(originalString);
    var decryptedString1 = subject.Decrypt(encryptedString1);
    var decryptedString2 = subject.Decrypt(encryptedString2);

    // Assert
    Assert.AreEqual(originalString, decryptedString1, "Decrypted string should match original string");
    Assert.AreEqual(originalString, decryptedString2, "Decrypted string should match original string");
    Assert.AreNotEqual(originalString, encryptedString1, "Encrypted string should not match original string");
    Assert.AreNotEqual(encryptedString1, encryptedString2, "String should never be encrypted the same twice");
}
Andy C
  • 795
  • 1
  • 9
  • 10
  • 12
    1) Don't use `System.Random` as RNG. 2) This is utterly broken against chosen-ciphertext attacks (in particular padding-oracles) – CodesInChaos May 20 '16 at 15:24
  • 22
    **Security Warning: Do Not Use This Code** see above comment by @CodesInChaos – jbtule May 08 '17 at 21:55
  • 3
    @jbtule please don't misguide to every person who don't want complications just encrypt, and also who is no wary about the attack, -- Please don't order if you want give suggestion. – Virbhadrasinh Jun 23 '17 at 14:12
  • @Virbhadrasinh there's no misguiding on my part, in fact it's quite the opposite. If you are going to use AES, using it right is quite important, using it incorrectly and saying that's okay I'm not using it for something important, is misguided. – jbtule Jun 23 '17 at 14:35
  • 3
    @jbtule If this code is vulnerable to some attack - which it is - then perhaps instead of just yelling at us not to use it you could link to some code that does it right. Just saying. – Corey Aug 10 '17 at 23:44
  • 2
    @Corey Not yelling, and had followed the best practice for dealing with security issues in stack overflow answers. If you want a link, it was posted in the question comments. But i'll put it here for you as well https://stackoverflow.com/a/10366194/637783 – jbtule Aug 10 '17 at 23:59
12

A variant of Marks (excellent) answer

  • Add "using"s
  • Make the class IDisposable
  • Remove the URL encoding code to make the example simpler.
  • Add a simple test fixture to demonstrate usage

Hope this helps

[TestFixture]
public class RijndaelHelperTests
{
    [Test]
    public void UseCase()
    {
        //These two values should not be hard coded in your code.
        byte[] key = {251, 9, 67, 117, 237, 158, 138, 150, 255, 97, 103, 128, 183, 65, 76, 161, 7, 79, 244, 225, 146, 180, 51, 123, 118, 167, 45, 10, 184, 181, 202, 190};
        byte[] vector = {214, 11, 221, 108, 210, 71, 14, 15, 151, 57, 241, 174, 177, 142, 115, 137};

        using (var rijndaelHelper = new RijndaelHelper(key, vector))
        {
            var encrypt = rijndaelHelper.Encrypt("StringToEncrypt");
            var decrypt = rijndaelHelper.Decrypt(encrypt);
            Assert.AreEqual("StringToEncrypt", decrypt);
        }
    }
}

public class RijndaelHelper : IDisposable
{
    Rijndael rijndael;
    UTF8Encoding encoding;

    public RijndaelHelper(byte[] key, byte[] vector)
    {
        encoding = new UTF8Encoding();
        rijndael = Rijndael.Create();
        rijndael.Key = key;
        rijndael.IV = vector;
    }

    public byte[] Encrypt(string valueToEncrypt)
    {
        var bytes = encoding.GetBytes(valueToEncrypt);
        using (var encryptor = rijndael.CreateEncryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
        {
            crypto.Write(bytes, 0, bytes.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            var encrypted = new byte[stream.Length];
            stream.Read(encrypted, 0, encrypted.Length);
            return encrypted;
        }
    }

    public string Decrypt(byte[] encryptedValue)
    {
        using (var decryptor = rijndael.CreateDecryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
        {
            crypto.Write(encryptedValue, 0, encryptedValue.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            var decryptedBytes = new Byte[stream.Length];
            stream.Read(decryptedBytes, 0, decryptedBytes.Length);
            return encoding.GetString(decryptedBytes);
        }
    }

    public void Dispose()
    {
        if (rijndael != null)
        {
            rijndael.Dispose();
        }
    }
}
Simon
  • 33,714
  • 21
  • 133
  • 202
  • Good Answer. One thing in the Dispose method you will need to cast rijndael to IDisposable or you will get a protection level error by calling Dispose – John ClearZ Aug 25 '13 at 21:23
  • 8
    Do not ever use a constant Initialization Vector, see: crypto.stackexchange.com/questions/66/… for more information about why. Instead, generate a new IV for each encryption and append it to the cryptotext, so much better and not to hard. – Tom Heard Dec 03 '13 at 02:20
  • @TomHeard - how do you do that in C#? – Chalky Sep 18 '14 at 06:15
  • 5
    @Chalky On encrypt, you use the Rijndael class to generate a random IV for you (http://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.generateiv(v=vs.110).aspx), do your encrypt, and then grab the IV from the Rijndael instance using the IV property. You then prepend (or append, either works as long as your decrypt grabs it from the same side) it to your crypto text. On decrypt you then pull the IV from the data received (The size of the IV property is the same as the BlockSize property divided by 8), then pass it to your decrypt instance before decrypt. – Tom Heard Sep 18 '14 at 21:05
  • 2
    @Chalky Note that the IV doesn't need to be secret, it just needs to be unique for each message sent. – Tom Heard Sep 18 '14 at 21:07
  • 20
    **Security Warning: Do Not Use This Code** See above Comments by @TomHeard – jbtule May 08 '17 at 21:57
  • 1
    @jbtule The question literally says _"**Simple insecure** two-way data "obfuscation"?_ Why are you then commenting on every single answer **sEcURiTy WaRNinG: Do nOT usE ThiS cOde** -_- ? A bit redundant and kinda repetitive if not downright annoying. As per the question's title, it's not meant to be secure nor complicated... – matronator May 23 '22 at 01:28
11

I combined what I found the best from several answers and comments.

  • Random initialization vector prepended to crypto text (@jbtule)
  • Use TransformFinalBlock() instead of MemoryStream (@RenniePet)
  • No pre-filled keys to avoid anyone copy & pasting a disaster
  • Proper dispose and using patterns

Code:

/// <summary>
/// Simple encryption/decryption using a random initialization vector
/// and prepending it to the crypto text.
/// </summary>
/// <remarks>Based on multiple answers in http://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp </remarks>
public class SimpleAes : IDisposable
{
    /// <summary>
    ///     Initialization vector length in bytes.
    /// </summary>
    private const int IvBytes = 16;

    /// <summary>
    ///     Must be exactly 16, 24 or 32 bytes long.
    /// </summary>
    private static readonly byte[] Key = Convert.FromBase64String("FILL ME WITH 24 (2 pad chars), 32 OR 44 (1 pad char) RANDOM CHARS"); // Base64 has a blowup of four-thirds (33%)

    private readonly UTF8Encoding _encoder;
    private readonly ICryptoTransform _encryptor;
    private readonly RijndaelManaged _rijndael;

    public SimpleAes()
    {
        _rijndael = new RijndaelManaged {Key = Key};
        _rijndael.GenerateIV();
        _encryptor = _rijndael.CreateEncryptor();
        _encoder = new UTF8Encoding();
    }

    public string Decrypt(string encrypted)
    {
        return _encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
    }

    public void Dispose()
    {
        _rijndael.Dispose();
        _encryptor.Dispose();
    }

    public string Encrypt(string unencrypted)
    {
        return Convert.ToBase64String(Encrypt(_encoder.GetBytes(unencrypted)));
    }

    private byte[] Decrypt(byte[] buffer)
    {
        // IV is prepended to cryptotext
        byte[] iv = buffer.Take(IvBytes).ToArray();
        using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv))
        {
            return decryptor.TransformFinalBlock(buffer, IvBytes, buffer.Length - IvBytes);
        }
    }

    private byte[] Encrypt(byte[] buffer)
    {
        // Prepend cryptotext with IV
        byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); 
        return _rijndael.IV.Concat(inputBuffer).ToArray();
    }
}

Update 2015-07-18: Fixed mistake in private Encrypt() method by comments of @bpsilver and @Evereq. IV was accidentally encrypted, is now prepended in clear text as expected by Decrypt().

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
angularsen
  • 8,160
  • 1
  • 69
  • 83
  • You should encrypt the entire inputBuffer with IV prepended otherwise the first 16 characters of the string-to-encrypt are lost. So your code should read: `return _encryptor.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);` – bpsilver Nov 17 '14 at 18:04
  • The IV is prepended in clear text as recommended by @Tom Heard and others above. Those are the 16 bytes you refer to. If you encrypt the IV then Decrypt won't be able to extract the IV. – angularsen Nov 17 '14 at 18:51
  • 2
    In that case: `byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray();` – bpsilver Nov 17 '14 at 21:24
  • 1
    That would do the same thing as the current implementation, wouldn't it? – angularsen Nov 17 '14 at 23:13
  • 1
    "FILL ME WITH 16, 24 OR 32 CHARS" well, no, not *before* base 64 decoding. And a key should be random. Really random. – Maarten Bodewes Apr 09 '15 at 22:58
  • 1
    I notice that @bpsilver right, and provided code will not work without his fix: encrypt method returns encrypted data *without* IV (it first adds IV to inputbuffer, but next encrypt and return data without it). So if possible just update the answer with his code. (Note: I test only methods with byte[] parameters, not strings). Thanks! – Evereq Jul 18 '15 at 17:15
9

Using TripleDESCryptoServiceProvider in System.Security.Cryptography :

public static class CryptoHelper
{
    private const string Key = "MyHashString";
    private static TripleDESCryptoServiceProvider GetCryproProvider()
    {
        var md5 = new MD5CryptoServiceProvider();
        var key = md5.ComputeHash(Encoding.UTF8.GetBytes(Key));
        return new TripleDESCryptoServiceProvider() { Key = key, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
    }

    public static string Encrypt(string plainString)
    {
        var data = Encoding.UTF8.GetBytes(plainString);
        var tripleDes = GetCryproProvider();
        var transform = tripleDes.CreateEncryptor();
        var resultsByteArray = transform.TransformFinalBlock(data, 0, data.Length);
        return Convert.ToBase64String(resultsByteArray);
    }

    public static string Decrypt(string encryptedString)
    {
        var data = Convert.FromBase64String(encryptedString);
        var tripleDes = GetCryproProvider();
        var transform = tripleDes.CreateDecryptor();
        var resultsByteArray = transform.TransformFinalBlock(data, 0, data.Length);
        return Encoding.UTF8.GetString(resultsByteArray);
    }
}
Ashkan S
  • 10,464
  • 6
  • 51
  • 80
  • 2
    I love this solution while keeping the keys in the class as a property is not good. So I make a gist here for someone who cares about it. https://gist.github.com/shukebeta/780df627a3de55c848a800cfb242a276 – shukebeta Dec 09 '20 at 20:15
8

[EDIT] Years later, I've come back to say: don't do this! See What's wrong with XOR encryption? for details.

A very simple, easy two-way encrytpion is XOR encryption.

  1. Come up with a password. Let's have it be mypass.
  2. Convert the password into binary (according to ASCII). The password becomes 01101101 01111001 01110000 01100001 01110011 01110011.
  3. Take the message you want to encode. Convert that into binary, also.
  4. Look at the length of the message. If the message length is 400 bytes, turn the password into a 400 byte string by repeating it over and over again. It would become 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011... (or mypassmypassmypass...)
  5. XOR the message with the long password.
  6. Send the result.
  7. Another time, XOR the encrypted message with the same password (mypassmypassmypass...).
  8. There's your message!
Community
  • 1
  • 1
stalepretzel
  • 15,543
  • 22
  • 76
  • 91
  • 10
    @Ryan Not every situation requires cryptographically secure hashes or Rijndael ciphers. "Simple 2 way encryption" might actually mean *simple*, which suggests xor or even ROT13. –  Nov 12 '10 at 13:49
  • 1
    @Ryan: AES with static encryption key, no initialization vector and no block chaining function is just fancy name for XOR encryption, you're just using really fancy KDF... – Hubert Kario Aug 19 '12 at 01:02
  • 17
    **Security Warning: Do Not Use This Code** XOR Encryption with a repeating key is trivially cracked. – jbtule May 08 '17 at 22:07
  • 2
    @jbtule The question specifically asked for an `insecure` algorithm. XOR encryption is a perfect match because of its simplicity. I think this is a valid answer - just choose a long enough, random password. – Bip901 Jan 19 '21 at 12:44
5

If you just want simple encryption (i.e., possible for a determined cracker to break, but locking out most casual users), just pick two passphrases of equal length, say:

deoxyribonucleicacid
while (x>0) { x-- };

and xor your data with both of them (looping the passphrases if necessary)(a). For example:

1111-2222-3333-4444-5555-6666-7777
deoxyribonucleicaciddeoxyribonucle
while (x>0) { x-- };while (x>0) { 

Someone searching your binary may well think the DNA string is a key, but they're unlikely to think the C code is anything other than uninitialized memory saved with your binary.


(a) Keep in mind this is very simple encryption and, by some definitions, may not be considered encryption at all (since the intent of encryption is to prevent unauthorised access rather than just make it more difficult). Although, of course, even the strongest encryption is insecure when someone's standing over the key-holders with a steel pipe.

As stated in the first sentence, this is a means to make it difficult enough for the casual attacker that they'll move on. It's similar to preventing burglaries on your home - you don't need to make it impregnable, you just need to make it less pregnable than the house next door :-)

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 3
    Interesting idea. I'm not sure I'd "believe" source code in a binary - but how about adapting the idea to use an error message as the passphrase? – Jon Skeet Oct 03 '08 at 06:11
  • 1
    I prefer using an md5 hash of some cleartext string that already exists in the application (error message or so). – Treb Oct 03 '08 at 07:43
  • 2
    Why do they need to be of equal length? It actually seems better if they are different lengths. That way, the length of your effective XOR operand is LCM(length1, length2), instead of just length1 (=length2). Which of course becomes length1 * length2 if the lengths are relatively prime. – Fantius Oct 27 '11 at 19:42
  • 3
    @jbtule, if you'd read the question, you'd realise that more secure encryption was in no way required. Specifically the reference to 'simple encryption', 'not mission critical' and just 'keeping honest people honest'. You should also read my first paragraph which explicitly calls out the fact it won't lock out determined attackers. – paxdiablo May 09 '17 at 00:06
  • @paxdiablo The question also states concern about trivial to crack. Ultimately the question Is poorly asked, probably caused a lot of poor security answers, and yet people have been using the answers from this question otherwise by just reading the title. – jbtule May 09 '17 at 00:10
  • @jbtule, if someone reads something stating quite clearly its limitations, and then expects it to work outside those limitations, *they* are to blame. Case in point, I recently refused my 13yo son permission for using the chainsaw because he had the opinion that he didn't need to bother learning about kickback and other dangers - my main concern is having him and all his friends reach adulthood with all four limbs intact :-) In terms of this answer, I made it *extremely* clear the expected shortcomings in the first sentence. However, I'll update the answer to try make it clearer. – paxdiablo May 09 '17 at 00:46
  • 1
    @paxdiablo you may be interested in weighing in on this meta discussion: https://meta.stackoverflow.com/q/348946/637783 – jbtule May 09 '17 at 19:10
  • @paxdiablo Check the question upvotes and guess how many people came to find a solution for a similar problem. They use this insecure solution. Code gets copied. From people finding this answer and from people who copied from people finding this answer (and so on). – steffen May 11 '17 at 17:08
  • 1
    @steffan, I have no idea nor even the roughest estimate of a guess, nor does anyone else, I'd warrant. My comment still stands. If you read an answer detailing all its shortcomings and decide to use (or disseminate) it inappropriately, it's on you. It's no different from medicine bottles stating you can only take two tablets an hour. If you decide to overdose by swallowing the whole bottle, that's hardly Glaxo's fault. Or, closer to home, if I advise you to use C for writing an OS and you then decide you should use C for *everything,* that's also your fault rather than mine. – paxdiablo May 12 '17 at 00:34
5

Encryption is easy: as others have pointed out, there are classes in the System.Security.Cryptography namespace that do all the work for you. Use them rather than any home-grown solution.

But decryption is easy too. The issue you have is not the encryption algorithm, but protecting access to the key used for decryption.

I would use one of the following solutions:

  • DPAPI using the ProtectedData class with CurrentUser scope. This is easy as you don't need to worry about a key. Data can only be decrypted by the same user, so no good for sharing data between users or machines.

  • DPAPI using the ProtectedData class with LocalMachine scope. Good for e.g. protecting configuration data on a single secure server. But anyone who can log into the machine can encrypt it, so no good unless the server is secure.

  • Any symmetric algorithm. I typically use the static SymmetricAlgorithm.Create() method if I don't care what algorithm is used (in fact it's Rijndael by default). In this case you need to protect your key somehow. E.g. you can obfuscate it in some way and hide it in your code. But be aware that anyone who is smart enough to decompile your code will likely be able to find the key.

Joe
  • 122,218
  • 32
  • 205
  • 338
5

I wanted to post my solution since none of the above the solutions are as simple as mine. Let me know what you think:

 // This will return an encrypted string based on the unencrypted parameter
 public static string Encrypt(this string DecryptedValue)
 {
      HttpServerUtility.UrlTokenEncode(MachineKey.Protect(Encoding.UTF8.GetBytes(DecryptedValue.Trim())));
 }

 // This will return an unencrypted string based on the parameter
 public static string Decrypt(this string EncryptedValue)
 {
      Encoding.UTF8.GetString(MachineKey.Unprotect(HttpServerUtility.UrlTokenDecode(EncryptedValue)));
 }

Optional

This assumes that the MachineKey of the server used to encrypt the value is the same as the one used to decrypt the value. If desired, you can specify a static MachineKey in the Web.config so that your application can decrypt/encrypt data regardless of where it is run (e.g. development vs. production server). You can generate a static machine key following these instructions.

William
  • 3,335
  • 9
  • 42
  • 74
1

The namespace System.Security.Cryptography contains the TripleDESCryptoServiceProvider and RijndaelManaged classes

Don't forget to add a reference to the System.Security assembly.

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
1

Using the builtin .Net Cryptography library, this example shows how to use the Advanced Encryption Standard (AES).

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

namespace Aes_Example
{
    class AesExample
    {
        public static void Main()
        {
            try
            {

                string original = "Here is some data to encrypt!";

                // Create a new instance of the Aes
                // class.  This generates a new key and initialization 
                // vector (IV).
                using (Aes myAes = Aes.Create())
                {

                    // 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);
                }

            }
            catch (Exception e)
            {
                Console.WriteLine("Error: {0}", e.Message);
            }
        }
        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("Key");
            byte[] encrypted;
            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decrytor 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("Key");

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

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

                // Create a decrytor 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;

        }
    }
}
Matt
  • 33,328
  • 25
  • 83
  • 97
1

I changed this:

public string ByteArrToString(byte[] byteArr)
{
    byte val;
    string tempStr = "";
    for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
    {
        val = byteArr[i];
        if (val < (byte)10)
            tempStr += "00" + val.ToString();
        else if (val < (byte)100)
            tempStr += "0" + val.ToString();
        else
            tempStr += val.ToString();
    }
    return tempStr;
}

to this:

    public string ByteArrToString(byte[] byteArr)
    {
        string temp = "";
        foreach (byte b in byteArr)
            temp += b.ToString().PadLeft(3, '0');
        return temp;
    }
Community
  • 1
  • 1
Achilleterzo
  • 742
  • 6
  • 16
0

I know you said you don't care about how secure it is, but if you chose DES you might as well take AES it is the more up-to-date encryption method.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Harald Scheirich
  • 9,676
  • 29
  • 53
0

I've been using the accepted answer by Mark Brittingham and its has helped me a lot. Recently I had to send encrypted text to a different organization and that's where some issues came up. The OP does not require these options but since this is a popular question I'm posting my modification (Encrypt and Decrypt functions borrowed from here):

  1. Different IV for every message - Concatenates IV bytes to the cipher bytes before obtaining the hex. Of course this is a convention that needs to be conveyed to the parties receiving the cipher text.
  2. Allows two constructors - one for default RijndaelManaged values, and one where property values can be specified (based on mutual agreement between encrypting and decrypting parties)

Here is the class (test sample at the end):

/// <summary>
/// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
/// Uses UTF8 Encoding
///  http://security.stackexchange.com/a/90850
/// </summary>
public class AnotherAES : IDisposable
{
    private RijndaelManaged rijn;

    /// <summary>
    /// Initialize algo with key, block size, key size, padding mode and cipher mode to be known.
    /// </summary>
    /// <param name="key">ASCII key to be used for encryption or decryption</param>
    /// <param name="blockSize">block size to use for AES algorithm. 128, 192 or 256 bits</param>
    /// <param name="keySize">key length to use for AES algorithm. 128, 192, or 256 bits</param>
    /// <param name="paddingMode"></param>
    /// <param name="cipherMode"></param>
    public AnotherAES(string key, int blockSize, int keySize, PaddingMode paddingMode, CipherMode cipherMode)
    {
        rijn = new RijndaelManaged();
        rijn.Key = Encoding.UTF8.GetBytes(key);
        rijn.BlockSize = blockSize;
        rijn.KeySize = keySize;
        rijn.Padding = paddingMode;
        rijn.Mode = cipherMode;
    }

    /// <summary>
    /// Initialize algo just with key
    /// Defaults for RijndaelManaged class: 
    /// Block Size: 256 bits (32 bytes)
    /// Key Size: 128 bits (16 bytes)
    /// Padding Mode: PKCS7
    /// Cipher Mode: CBC
    /// </summary>
    /// <param name="key"></param>
    public AnotherAES(string key)
    {
        rijn = new RijndaelManaged();
        byte[] keyArray = Encoding.UTF8.GetBytes(key);
        rijn.Key = keyArray;
    }

    /// <summary>
    /// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
    /// Encrypt a string using RijndaelManaged encryptor.
    /// </summary>
    /// <param name="plainText">string to be encrypted</param>
    /// <param name="IV">initialization vector to be used by crypto algorithm</param>
    /// <returns></returns>
    public byte[] Encrypt(string plainText, byte[] IV)
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        // Check arguments.
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText cannot be null or empty");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV cannot be null or empty");
        byte[] encrypted;

        // Create a decrytor to perform the stream transform.
        using (ICryptoTransform encryptor = rijn.CreateEncryptor(rijn.Key, 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;
    }//end EncryptStringToBytes

    /// <summary>
    /// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
    /// </summary>
    /// <param name="cipherText">bytes to be decrypted back to plaintext</param>
    /// <param name="IV">initialization vector used to encrypt the bytes</param>
    /// <returns></returns>
    public string Decrypt(byte[] cipherText, byte[] IV)
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        // Check arguments.
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText cannot be null or empty");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV cannot be null or empty");

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

        // Create a decrytor to perform the stream transform.
        using (ICryptoTransform decryptor = rijn.CreateDecryptor(rijn.Key, 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;
    }//end DecryptStringFromBytes

    /// <summary>
    /// Generates a unique encryption vector using RijndaelManaged.GenerateIV() method
    /// </summary>
    /// <returns></returns>
    public byte[] GenerateEncryptionVector()
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        //Generate a Vector
        rijn.GenerateIV();
        return rijn.IV;
    }//end GenerateEncryptionVector


    /// <summary>
    /// Based on https://stackoverflow.com/a/1344255
    /// Generate a unique string given number of bytes required.
    /// This string can be used as IV. IV byte size should be equal to cipher-block byte size. 
    /// Allows seeing IV in plaintext so it can be passed along a url or some message.
    /// </summary>
    /// <param name="numBytes"></param>
    /// <returns></returns>
    public static string GetUniqueString(int numBytes)
    {
        char[] chars = new char[62];
        chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
        byte[] data = new byte[1];
        using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
        {
            data = new byte[numBytes];
            crypto.GetBytes(data);
        }
        StringBuilder result = new StringBuilder(numBytes);
        foreach (byte b in data)
        {
            result.Append(chars[b % (chars.Length)]);
        }
        return result.ToString();
    }//end GetUniqueKey()

    /// <summary>
    /// Converts a string to byte array. Useful when converting back hex string which was originally formed from bytes.
    /// </summary>
    /// <param name="hex"></param>
    /// <returns></returns>
    public static byte[] StringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }//end StringToByteArray

    /// <summary>
    /// Dispose RijndaelManaged object initialized in the constructor
    /// </summary>
    public void Dispose()
    {
        if (rijn != null)
            rijn.Dispose();
    }//end Dispose()
}//end class

and..

Here is the test sample:

class Program
{
    string key;
    static void Main(string[] args)
    {
        Program p = new Program();

        //get 16 byte key (just demo - typically you will have a predetermined key)
        p.key = AnotherAES.GetUniqueString(16);

        string plainText = "Hello World!";

        //encrypt
        string hex = p.Encrypt(plainText);

        //decrypt
        string roundTrip = p.Decrypt(hex);

        Console.WriteLine("Round Trip: {0}", roundTrip);
    }

    string Encrypt(string plainText)
    {
        Console.WriteLine("\nSending (encrypt side)...");
        Console.WriteLine("Plain Text: {0}", plainText);
        Console.WriteLine("Key: {0}", key);
        string hex = string.Empty;
        string ivString = AnotherAES.GetUniqueString(16);
        Console.WriteLine("IV: {0}", ivString);
        using (AnotherAES aes = new AnotherAES(key))
        {
            //encrypting side
            byte[] IV = Encoding.UTF8.GetBytes(ivString);

            //get encrypted bytes (IV bytes prepended to cipher bytes)
            byte[] encryptedBytes = aes.Encrypt(plainText, IV);
            byte[] encryptedBytesWithIV = IV.Concat(encryptedBytes).ToArray();

            //get hex string to send with url
            //this hex has both IV and ciphertext
            hex = BitConverter.ToString(encryptedBytesWithIV).Replace("-", "");
            Console.WriteLine("sending hex: {0}", hex);
        }

        return hex;
    }

    string Decrypt(string hex)
    {
        Console.WriteLine("\nReceiving (decrypt side)...");
        Console.WriteLine("received hex: {0}", hex);
        string roundTrip = string.Empty;
        Console.WriteLine("Key " + key);
        using (AnotherAES aes = new AnotherAES(key))
        {
            //get bytes from url
            byte[] encryptedBytesWithIV = AnotherAES.StringToByteArray(hex);

            byte[] IV = encryptedBytesWithIV.Take(16).ToArray();

            Console.WriteLine("IV: {0}", System.Text.Encoding.Default.GetString(IV));

            byte[] cipher = encryptedBytesWithIV.Skip(16).ToArray();

            roundTrip = aes.Decrypt(cipher, IV);
        }
        return roundTrip;
    }
}

enter image description here

Community
  • 1
  • 1
joym8
  • 4,014
  • 3
  • 50
  • 93
-2

I think this is the worlds simplest one !

string encrypted = "Text".Aggregate("", (c, a) => c + (char) (a + 2));

Test

 Console.WriteLine(("Hello").Aggregate("", (c, a) => c + (char) (a + 1)));
            //Output is Ifmmp
 Console.WriteLine(("Ifmmp").Aggregate("", (c, a) => c + (char)(a - 1)));
            //Output is Hello
Thunder
  • 10,366
  • 25
  • 84
  • 114