Simple insecure two-way data "obfuscation"?
Asked Answered
D

17

443

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.

Deneb answered 3/10, 2008 at 5:41 Comment(10)
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.Deneb
Save yourself hours and use the HttpServerUtility.UrlTokenEn/Decode to convert back and forth from the byte arrays to a url friendly string.Albric
+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.Babineaux
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.Tague
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 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#.Sage
Not a single answer to this question describes secure encryption. Use jbtule's answer at Encrypt and decrypt a string instead.Intrados
@paxdiablo This appears to genuinely be about C#, not C. Was your edit a mistake?Darrick
@Intrados Why did you close & then reopen?Darrick
@KyleStrand I don't remember. But I guess I tried to close it as duplicate of https://mcmap.net/q/27038/-encrypt-and-decrypt-a-string-in-c-closed instead of #10168740 but couldn't vote to close twice. That certainly fits with me closing the latter as a duplicate of the former at that time.Intrados
There is an ongoing meta discussion about this question.Bornie
S
475

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;
    }
}
Stakhanovism answered 17/10, 2008 at 15:45 Comment(36)
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.Stakhanovism
The using police (that's not me) might be after you for some of the stream usageCarpospore
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...Stakhanovism
@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.Lumberman
@AndyMcKenna - That's done on purpose so that you change the values in the arrays, as Mark notes in the second paragraph.Talanta
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 ;-)Stakhanovism
Hi Mark, For a given string, is there a way to shorten the length of the encrypted string/bytearray?Herb
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.Stakhanovism
Let's say one wanted to add salting to this how would one go about it? :-pRori
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.Wiliness
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)Fortnightly
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?Eventful
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.Stakhanovism
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!Eventful
Converting to base 64 then URL encoding would result in less code and more compact output.Micromho
This code is using RijndaelManaged. Is the code equivalent to 128 (or above) AES?Snappish
FYI: Small compiler error in line 11 where you used a number above 255 in the vector byte array.Biforked
@Biforked - 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.Stakhanovism
@AntonAndreev see blogs.msdn.com/b/shawnfa/archive/2006/10/09/… for interoperability - however now that the Aes class is in .net 3.5+, why not just use that ie System.Security.Cryptography.AesManaged ? : )Marketa
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. crypto.stackexchange.com/a/82/1934Sage
@Sage - 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.Stakhanovism
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.Sage
Made the blind copy paste and happy too see it is working after facing hell of "Error occurred while decoding OAEP padding".Kultur
Note that this code uses Cipher-Block Chaining (CBC) as its mode, which you should take into account.Babbette
The static functions GenerateEncryptionKey and GenerateEncryptionVector do not need to call GenerateKey/GenerateIV as this is done in the constructor of RijndaelManaged class.Wendell
Note also that a consequence of using CBC as its mode is that you are likely to be vulnerable to padding oracle attacks. Use authenticated encryption, and whenever possible, do not implement cryptography yourself.Subedit
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: zenu.wordpress.com/2011/09/21/… ? If there is a reason to avoid doing it that way I'd certainly like to hear about it.Kingsley
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.Giamo
"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 random.org/bytes to generate a new set easily." That's not good advice at all. Change some of the numbers?Inhesion
Note that base 64 uses ~33% more characters than bytes. The current method uses 200% more characters.Inhesion
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?Calycine
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.Stakhanovism
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'sCalycine
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 IVAra
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.Sage
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.Insufficiency
M
190

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, [email protected] 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();
    }
}
Micromho answered 1/4, 2011 at 19:44 Comment(7)
When decoding, I had to replace space with + for it to work with QueryString in Chrome: (new SimplerAES()).Decrypt(Request.QueryString["myParam"].Replace(' ', '+'));Sedillo
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.Giamo
EncryptToUrl and DecryptFromUrl don't have any necessary business in this class. Otherwise ; great answer.Mesomorph
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: https://mcmap.net/q/27040/-can-i-put-a-sign-in-a-folder-with-iisNorthumbrian
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.#Tourer
@TomHeard How would one go about doing that, with the above code?Rozella
Security Warning: Do Not Use This Code See comment by @TomHeardSage
N
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);
Newness answered 3/10, 2008 at 5:59 Comment(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.Stakhanovism
@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.Deneb
@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.Moneymaker
So where is the key used?Blalock
Security Warning: Do Not Use This Code See comment by @HubertKarioSage
W
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");
}
Weltschmerz answered 3/10, 2014 at 10:6 Comment(6)
1) Don't use System.Random as RNG. 2) This is utterly broken against chosen-ciphertext attacks (in particular padding-oracles)Intrados
Security Warning: Do Not Use This Code see above comment by @IntradosSage
@Sage 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.Kennel
@Kennel 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.Sage
@Sage 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.Sugary
@Sugary 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://mcmap.net/q/27038/-encrypt-and-decrypt-a-string-in-c-closedSage
A
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();
        }
    }
}
Acetanilide answered 6/9, 2011 at 2:38 Comment(7)
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 DisposeHowitzer
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.Giamo
@TomHeard - how do you do that in C#?Significancy
@Significancy On encrypt, you use the Rijndael class to generate a random IV for you (msdn.microsoft.com/en-us/library/…), 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.Giamo
@Significancy Note that the IV doesn't need to be secret, it just needs to be unique for each message sent.Giamo
Security Warning: Do Not Use This Code See above Comments by @TomHeardSage
@Sage 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...Charles
B
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 https://mcmap.net/q/27023/-simple-insecure-two-way-data-quot-obfuscation-quot </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().

Breathed answered 22/10, 2014 at 22:54 Comment(6)
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);Marjoriemarjory
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.Breathed
In that case: byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray();Marjoriemarjory
That would do the same thing as the current implementation, wouldn't it?Breathed
"FILL ME WITH 16, 24 OR 32 CHARS" well, no, not before base 64 decoding. And a key should be random. Really random.Inhesion
I notice that @Marjoriemarjory 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!Southeasterly
T
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);
    }
}
Typescript answered 12/10, 2017 at 13:46 Comment(1)
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. gist.github.com/shukebeta/780df627a3de55c848a800cfb242a276Adaurd
B
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!
Bridie answered 17/10, 2008 at 15:53 Comment(4)
@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.Maroon
@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...Moneymaker
Security Warning: Do Not Use This Code XOR Encryption with a repeating key is trivially cracked.Sage
@Sage 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.Mancunian
W
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 :-)

Wiliness answered 3/10, 2008 at 6:8 Comment(9)
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?Lareelareena
I prefer using an md5 hash of some cleartext string that already exists in the application (error message or so).Bender
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.Unrighteous
@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.Wiliness
@Wiliness 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.Sage
@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.Wiliness
@Wiliness you may be interested in weighing in on this meta discussion: meta.stackoverflow.com/q/348946/637783Sage
@Wiliness 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).Posse
@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.Wiliness
A
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.

Atrice answered 3/10, 2008 at 11:42 Comment(0)
M
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.

Mortgagor answered 17/11, 2015 at 14:42 Comment(1)
note this approach can only be used for ASP.NET app.Hull
A
1

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

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

Annice answered 3/10, 2008 at 5:49 Comment(1)
Not that I downvoted, but why should the age of a question matter when voting?Reconstruct
M
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;
    }
Microsecond answered 22/2, 2011 at 17:8 Comment(0)
G
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;

        }
    }
}
Gherardi answered 11/2, 2016 at 16:40 Comment(0)
L
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.

Loadstar answered 3/10, 2008 at 10:10 Comment(0)
E
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

Eph answered 22/2, 2017 at 17:55 Comment(0)
H
-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
Himalayas answered 10/1, 2015 at 14:55 Comment(1)
ROT... 1? Really? OP even called out ROT13 as an example of what he didn't want to do.Parlando

© 2022 - 2024 — McMap. All rights reserved.