Other Posts in Encryption

  1. AES Encryption in C#

AES Encryption in C#

5/1/2008

With a couple of projects that I've been given recently, I needed to add some encryption. Luckily I had a utility class laying around that implemented AES (otherwise known as Rijndael). Advanced Encryption Standard is an encryption standard adopted by the US Govt that is approved by the NSA. The great thing about it though is that it's incredibly easy to implement (about 24 lines of code to encrypt, 23 to decrypt). And since you're here I'm fairly sure that you want to know how to do just that:

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.IO;
  25: using System.Security.Cryptography;
  26: using System.Text;
  27: #endregion
  28:  
  29: namespace Utilities.Encryption
  30: {
  31:     /// <summary>
  32:     /// Utility class that handles encryption
  33:     /// </summary>
  34:     public static class AESEncryption
  35:     {
  36:         #region Static Functions
  37:  
  38:         /// <summary>
  39:         /// Encrypts a string
  40:         /// </summary>
  41:         /// <param name="PlainText">Text to be encrypted</param>
  42:         /// <param name="Password">Password to encrypt with</param>
  43:         /// <param name="Salt">Salt to encrypt with</param>
  44:         /// <param name="HashAlgorithm">Can be either SHA1 or MD5</param>
  45:         /// <param name="PasswordIterations">Number of iterations to do</param>
  46:         /// <param name="InitialVector">Needs to be 16 ASCII characters long</param>
  47:         /// <param name="KeySize">Can be 128, 192, or 256</param>
  48:         /// <returns>An encrypted string</returns>
  49:         public static string Encrypt(string PlainText, string Password,
  50:             string Salt = "Kosher", string HashAlgorithm = "SHA1",
  51:             int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",
  52:             int KeySize = 256)
  53:         {
  54:             if (string.IsNullOrEmpty(PlainText))
  55:                 return "";
  56:             byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
  57:             byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
  58:             byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
  59:             PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
  60:             byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
  61:             RijndaelManaged SymmetricKey = new RijndaelManaged();
  62:             SymmetricKey.Mode = CipherMode.CBC;
  63:             byte[] CipherTextBytes = null;
  64:             using (ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes))
  65:             {
  66:                 using (MemoryStream MemStream = new MemoryStream())
  67:                 {
  68:                     using (CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))
  69:                     {
  70:                         CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
  71:                         CryptoStream.FlushFinalBlock();
  72:                         CipherTextBytes = MemStream.ToArray();
  73:                         MemStream.Close();
  74:                         CryptoStream.Close();
  75:                     }
  76:                 }
  77:             }
  78:             SymmetricKey.Clear();
  79:             return Convert.ToBase64String(CipherTextBytes);
  80:         }
  81:  
  82:         /// <summary>
  83:         /// Decrypts a string
  84:         /// </summary>
  85:         /// <param name="CipherText">Text to be decrypted</param>
  86:         /// <param name="Password">Password to decrypt with</param>
  87:         /// <param name="Salt">Salt to decrypt with</param>
  88:         /// <param name="HashAlgorithm">Can be either SHA1 or MD5</param>
  89:         /// <param name="PasswordIterations">Number of iterations to do</param>
  90:         /// <param name="InitialVector">Needs to be 16 ASCII characters long</param>
  91:         /// <param name="KeySize">Can be 128, 192, or 256</param>
  92:         /// <returns>A decrypted string</returns>
  93:         public static string Decrypt(string CipherText, string Password,
  94:             string Salt = "Kosher", string HashAlgorithm = "SHA1",
  95:             int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",
  96:             int KeySize = 256)
  97:         {
  98:             if (string.IsNullOrEmpty(CipherText))
  99:                 return "";
 100:             byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
 101:             byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
 102:             byte[] CipherTextBytes = Convert.FromBase64String(CipherText);
 103:             PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
 104:             byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
 105:             RijndaelManaged SymmetricKey = new RijndaelManaged();
 106:             SymmetricKey.Mode = CipherMode.CBC;
 107:             byte[] PlainTextBytes = new byte[CipherTextBytes.Length];
 108:             int ByteCount = 0;
 109:             using (ICryptoTransform Decryptor = SymmetricKey.CreateDecryptor(KeyBytes, InitialVectorBytes))
 110:             {
 111:                 using (MemoryStream MemStream = new MemoryStream(CipherTextBytes))
 112:                 {
 113:                     using (CryptoStream CryptoStream = new CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read))
 114:                     {
 115:  
 116:                         ByteCount = CryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);
 117:                         MemStream.Close();
 118:                         CryptoStream.Close();
 119:                     }
 120:                 }
 121:             }
 122:             SymmetricKey.Clear();
 123:             return Encoding.UTF8.GetString(PlainTextBytes, 0, ByteCount);
 124:         }
 125:  
 126:         #endregion
 127:     }
 128: }

It's pretty straight forward, but if you want to use them it would probably help to know the following:

  • HashAlgorithm can be SHA1 or MD5.
  • InitialVector should be a string of 16 ASCII characters.
  • KeySize can be 128, 192, or 256.
  • The Salt string really acts as a second password.
  • PasswordIterations is the number of times the algorithm is run on the text.

Other than that, go crazy with the inputs. In other words, when using the code it would look something like this:

   1: string FinalValue=AESEncryption.Encrypt("My Text","My Password","Salt or Password2","SHA1",2,"16CHARSLONG12345",256);

Anyway, use the code, leave feedback, and happy coding.



Comments

James Craig
December 09, 2011 7:53 PM

First, it's NSA (National Security Agency) and not NASA. Although I'm pretty sure that NASA would also approve of the algorithm. :) Secondly, there's no real way to do that and still have a secure system. Even with systems like OAuth or encryption algorithms like RSA you're still keeping a key which is separate from the encrypted data (private key is kept by you where as everyone else knows the public key). If you store it with the encrypted data and the person knows the algorithm, it's an open pass to the data and thus not very secure. Or at least I can't think of an algorithm that does this and is secure.

James
November 30, 2011 11:54 PM

I appreciate that we have a "NASA-approved" encryption standard that public can use. However, I would like to have a similar but alternate system where I don't need to supply the password (key) each time I want to decrypt the cipher-text. So, that would be something like storing the key within the cipher-text itself. Is there any known way of doing it?

James Craig
November 20, 2011 7:21 PM

Column selection has been in Visual Studio for a bit now. Just use that to get rid of the line numbers (Press alt while selecting).

YY
November 17, 2011 4:51 PM
Emm... Nice code but not copy and paste friendly...

Nick
May 09, 2011 7:12 AM

James - Thanks man! Works as advertised.Nick

James Craig
August 30, 2010 7:09 AM

Depends on your setup. If you're on a web farm, I'd ensure your machine keys are the same on each site. Second potential issue is that your not sending in the entire encrypted string. Another potential issue is setting the padding mode incorrectly (the default shouldn't give you an issue, but there are cases where it does). So setting the Padding property on the RijndaelManaged object to PaddingMode.ISO10126, PaddingMode.PKCS7, PaddingMode.ANSIX923, etc. might help. On top of that, you may have just screwed up one of the steps in your code (not converting the string to byte array properly, etc.), which happens more often than you'd think.

C# coder
August 29, 2010 10:18 PM

To be more specific, the error occurs here:using (CryptoStream CryptoStream = new CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read)){ByteCount = CryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);MemStream.Close();CryptoStream.Close();}When it gets to the ByteCount = CryptoStream........ it throws an exception, but the error occurs in the using(Crypto blah blah.

C# coder
August 29, 2010 9:45 PM

Hello...I used the code above and slightly modified it to fit my logic, but basically what changed is how the parameter values are assigned. I got this error "Padding is invalid and cannot be removed." when it got to the ByteCount = CryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length); part of the Decrypt method.Any idea what Im doing wrong?

James Craig
August 27, 2010 2:23 PM

Assuming you encrypted it using the Encrypt function, you would just pass in the same parameters that you sent the Encrypt function (except the ciper text instead of the clear text version).If you're decrypting from something else, then you need a couple of things. The first is the initialization vector (InitialVector). Basically it ensures that the stream is independent, etc. Which basically means it's used to encrypt a block of text such that it doesn't go to the same cipher text twice even if you use the same password in two separate instances. You need this along with the password and cipher text to decrypt.Another thing to keep in mind is that in my implementation I use two passwords (in your instance they would be MADRIDLONDONROME and Salt). You could actually rewrite the code so that it only uses one (strip out the Salt parameter and the DerivedPassword part of the code and instead get the bytes directly from the password, but you'd still need to know if it was SHA1 hash

Alan Ostrowski
August 27, 2010 10:32 AM

I am trying to use your code to decrypt this string.Cannot get it to work?What is the InitialVector?I am not certain what I am missing?string FinalValue = AESEncryption.Decrypt("fddfc286032a97e087ac082a03153d106c0d1602b4e3606732670d8b53b32cb548df75a96e5a3a7b2580cb72c520eb5c2fa93dca3a617f53d35ecc41dfe8380f01bc9d651c261f292f50d447d26ef7ca", "MADRIDLONDONROME", "Salt", "SHA1", 1, "16CHARSLONG12345", 128);

James Craig
May 25, 2010 9:32 AM

Ok, I was looking at the wrong class. The RijndaelManaged class... Yeah I should add that. Thanks, I learn something new all the time.

Ryu Zaki
May 25, 2010 1:40 AM

Thanks for explaining the using statement. I'm still not all that proficient in C#.http://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.clear(VS.90).aspx explains the overwriting. I thought it would be applicable here. Of course, I could be wrong.

James Craig
May 24, 2010 4:08 PM

The code sets the objects within using blocks. The using statement automatically calls Dispose as soon as the object leaves the scope of the using block. According to MSDN, Clear just calls Dispose as well (http://msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream.clear%28v=VS.100%29.aspx). If it overwrites the data prior to Dispose, I've never seen it documented. Then again I've never tried it to see.

Ryu Zaki
May 24, 2010 2:58 AM

Hi,Thanks for the code.Btw, I do not see the calls to Dispose in the current version.You might want to call Clear before Dispose. This would overwrite any sensitive data before Dispose makes the memory available for re-allocation.

Noemí
April 09, 2010 3:27 AM

Great code !!!!! Works perfect, thanks so much.

Nirman Doshi
March 13, 2010 2:39 AM

Awesome code.. I've found tons of blogs on this, but I was looking for similar kind of thing.. No complexity whatsoever...Nirman DoshiVadodara

James Craig
March 01, 2010 4:14 PM

Well the HashAlgorithm string gets passed to the PasswordDeriveBytes class, which in turn uses CryptoConfig for the names of the algorithms (or something you setup in the machine.config if you write your own but that's a bit more than what most people need). So you can use SHA256, SHA384, SHA512, etc. without any code change.

July
March 01, 2010 5:54 AM

Hi,Thnks for the codeIn the Hash algorithm can we use sha256 directly instead of SHA1 or MD5 by passing Hash algo name as SHA256 or there will be some code change for tht?

Sinan Bozdemir
February 24, 2010 2:13 PM

Thanks a lot...

James Craig
January 29, 2010 4:40 PM

Actually it was updated this morning when I posted my last comment. The usings within the function call Dispose at the correct times.

Hugh
January 29, 2010 4:15 PM

[quote]Dispose needs to be called[/quote]Can you please actually update the example to show where Dispose should be called?[quote]there were other issues with what was shown[/quote]What were the other issues? Can you please update the example for correctness?

James Craig
January 29, 2010 9:37 AM

Thanks for the catch. And actually there were other issues with what was shown. Dispose needs to be called, which the original code wasn't doing. Should be updated now though.

BinaryBrother
January 28, 2010 8:15 PM

You need to let other users know that it requires the following libraries...using System.Security.Cryptography;using System.IO;and you used "cryptoSteam" instead of "CryptoStream" once in the "Encrypt" function... :)Happy coding.

James Craig
December 14, 2009 2:03 PM

You could use SHA-1, MD5, etc. to create a HMAC fairly easily (for example, my SHA-1 hashing code is 4 lines total so it's not difficult). Although I'd avoid MD5 due to it's rather insecure nature. And depending on your setup, the hash isn't going to be a perfect solution either (if they can change the ciphertext, they may have access to the hash as well and the ability to change what you have stored there).

Richard
December 14, 2009 9:45 AM

Good stuff - you might also want to add an HMAC to the output as well, which would have the benefit of helping to ensure that the ciphertext hasn't been tampered with prior to decryption.

Mariusz
December 11, 2009 11:37 AM

It is working like a charm.Thank you.Mariusz