|
|
I have examining your wonderful piece of code and I wonder how it is possible to decrypt data when key is different from the one which encrypts data?
Private ReadOnly key() As Byte = _
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, _
15, 16, 17, 18, 19, 20, 21, 22, 23, 24}
Private ReadOnly key2() As Byte = _
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, _
15, 16, 17, 18, 19, 21, 21, 22, 23, 24}
x = New clsEncryption(key, iv)
y = New clsEncryption(key2, iv2)
strEncyrpted = x.Encrypt("1")
strDecrypted = y.Decrypt(strEncyrpted)
If strDecrypted = "1" Then
MessageBox.Show("False")
Else
MessageBox.Show("True")
End If
If key is way too different it fails to decrypt but when it is slightly different then it decrypts perfectly. In the above example key(19) is different from key2(19).
PS: I am using
Return CryptoTransform.TransformFinalBlock(input, 0, input.Length)
as suggested on the Transform() function.
Türker TUNALI
modified 25-Jan-13 4:05am.
|
|
|
|
|
Nice article. It is old but still worked great. Thanks
|
|
|
|
|
Hey Tony,
A big if belated thank you -- I employed this article to get a leg up on TripleDES encryption some years ago.
Now, due to ever more demanding industry standards, I am being asked to validate or document as authoritatively as possible that this algorithm does indeed do TripleDES encryption.
Aside from pointing to MSDN documentation of what the referenced framework classes and methods do, would you have any suggestions for me?
If not, I understand but if so...
TIA
|
|
|
|
|
As you mentioned, you are going to have to start with Microsoft's documentation on the 3DES cryptographic service provider they published. You can find it here:
TripleDESCryptoServiceProvider Class[^]
Make sure that you select the proper version of the .NET Framework from the selector at the top of the page if you are going to reference the online documentation in your own.
Beyond that, I don't really have any suggestions other than to search online or perhaps to use reflector to decompile the framework assembly and see what it's doing under the hood.
Sorry I wasn't more help.
Tony
|
|
|
|
|
I really appreciate your reply
Thanks again,
Tom
|
|
|
|
|
I believe the following single line of VB.NET 2.0 code is the equivalent of your rather longer Transform() function:
Return CryptoTransform.TransformFinalBlock(input, 0, input.Length)
The above certainly works for me in testing and shorter code always seems more elegant!
|
|
|
|
|
You are correct, the ICryptoTransform.TransformFinalBlock function will do the one pass transformation and take care of all the stream "crap" internally for you.
I went this other route at first, because I had a plan to make this method more memory efficient for large transformations by transforming the entire block one chunk at a time (e.g. processing a 10MB array in 100KB chunks). However, I decided not to do this later because I had no immediate need (as you say, keep it simple) and I never went back and re-factored this method.
So, you are totally right.
Tony
|
|
|
|
|
|
Do you need to setup IDisposable (Dispose Pattern) in your class? and how?
|
|
|
|
|
I normally only implement IDisposable in two cases:
(A) I have non-CLR/native resources to manage.
(B) I have explicit things I need to do/cleanup that can't be left to the CLRs trash collection.
Since neither of those cases are in evidence in this class, there is no IDisposable implimentation.
If you want to know *how* to impliment IDisposable, I would reccommend reading the documentation:
MSDN IDisposable Interface
[^]
|
|
|
|
|
I was under the impression that the earlier you Dispose everything the better.
In addition that I should be using the "using" clause as much as possible on every 'Disposable' object/class. (So I added some to your code).
|
|
|
|
|
Private ReadOnly m_key_Enc() As Byte = _
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, _
15, 16, 17, 18, 19, 20, 21, 22, 23, 24}
Private ReadOnly m_key_Dec() As Byte = _
{0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, _
14, 17, 16, 19, 18, 21, 20, 23, 22, 25}
Encrypt Data with m_key_Enc and then Decrypt with m_key_Dec gives original Data return.
|
|
|
|
|
You don't specify what you used for your vector (m_iv), but based on your findings, I have to assume that you used the same vector for both of your keys. I don't know the specifics of how/why the following is true, but with the TripleDESCryptoServiceProvider, if you do not provide both a unique key and vector, you can run into the situation you found. If you use different vector values, you'ff find that it works correctly.
MSDN SymmetricAlgorithm.IV Property[^]
Personally, I haven't used 3DES in years. I prefer some of the other CryptoServiceProviders that are more secure, usually Microsoft AES Cryptographic Provider
[^]
modified on Wednesday, September 21, 2011 7:42 AM
|
|
|
|
|
I added a more secure class to the above for two options:
public class cRijndael
{
private RijndaelManaged m_des = new RijndaelManaged();
private UTF8Encoding m_utf8 = new UTF8Encoding();
private byte[] m_key;
private byte[] m_iv;
public cRijndael(byte[] key, byte[] iv)
{
this.m_key = key;
this.m_iv = iv;
}
public byte[] Encrypt(byte[] input)
{
return Transform(input, m_des.CreateEncryptor(m_key, m_iv));
}
public byte[] Decrypt(byte[] input )
{
return Transform(input, m_des.CreateDecryptor(m_key, m_iv));
}
public byte[] Decrypt(byte[] input, CipherMode cm, PaddingMode pm, int KeySize, int BlockSize)
{
m_des.Mode = cm;
m_des.Padding = pm;
m_des.KeySize = KeySize;
m_des.BlockSize = BlockSize;
m_des.FeedbackSize = BlockSize;
return Transform(input, m_des.CreateDecryptor(m_key, m_iv));
}
public string Encrypt(string text)
{
byte[] input = m_utf8.GetBytes(text);
byte[] output = Transform(input, m_des.CreateEncryptor(m_key, m_iv));
return Convert.ToBase64String(output);
}
public string Decrypt(string text)
{
byte[] input = Convert.FromBase64String(text);
byte[] output = Transform(input, m_des.CreateDecryptor(m_key, m_iv));
return m_utf8.GetString(output);
}
public string Decrypt(string text, CipherMode cm, PaddingMode pm, int KeySize, int BlockSize)
{
m_des.Mode = cm;
m_des.Padding = pm;
m_des.KeySize = KeySize;
m_des.BlockSize = BlockSize;
m_des.FeedbackSize = BlockSize;
byte[] input = Convert.FromBase64String(text);
byte[] output = Transform(input, m_des.CreateDecryptor(m_key, m_iv));
return m_utf8.GetString(output);
}
public bool Encrypt(string sourceFile, string destFile)
{
try
{
byte[] input = File.ReadAllBytes(sourceFile);
byte[] output = Transform(input, m_des.CreateEncryptor(m_key, m_iv));
File.WriteAllBytes(destFile, output);
return true;
}
catch (Exception)
{
return false;
}
}
public bool Encrypt(string sourceFile, string destFile, CipherMode cm, PaddingMode pm, int KeySize, int BlockSize)
{
m_des.Mode = cm;
m_des.Padding = pm;
m_des.KeySize = KeySize;
m_des.BlockSize = BlockSize;
m_des.FeedbackSize = BlockSize;
try
{
byte[] input = File.ReadAllBytes(sourceFile);
byte[] output = Transform(input, m_des.CreateEncryptor(m_key, m_iv));
File.WriteAllBytes(destFile, output);
return true;
}
catch (Exception)
{
return false;
}
}
public bool Decrypt(string sourceFile, string destFile)
{
try
{
byte[] input = File.ReadAllBytes(sourceFile);
byte[] output = Transform(input, m_des.CreateDecryptor(m_key, m_iv));
File.WriteAllBytes(destFile, output);
return true;
}
catch (Exception)
{
return false;
}
}
public bool Decrypt(string sourceFile, string destFile, CipherMode cm, PaddingMode pm, int KeySize, int BlockSize)
{
m_des.Mode = cm;
m_des.Padding = pm;
m_des.KeySize = KeySize;
m_des.BlockSize = BlockSize;
m_des.FeedbackSize = BlockSize;
try
{
byte[] input = File.ReadAllBytes(sourceFile);
byte[] output = Transform(input, m_des.CreateDecryptor(m_key, m_iv));
File.WriteAllBytes(destFile, output);
return true;
}
catch (Exception)
{
return false;
}
}
private byte[] Transform(byte[] input, ICryptoTransform CryptoTransform)
{
using (MemoryStream memStream = new MemoryStream())
{
using (CryptoStream cryptStream = new CryptoStream(memStream, CryptoTransform, CryptoStreamMode.Write))
{
cryptStream.Write(input, 0, input.Length);
cryptStream.FlushFinalBlock();
memStream.Position = 0;
byte[] result = memStream.ToArray();
return result;
}
}
}
}
|
|
|
|
|
Interesting, but to be honest, if you wanted to do it a lot cleaner and easier, you should just use the newer version of this class, found here:
Generic SymmetricAlgorithm Helper[^]
This is a generic version of the above class that supports all of the SymmetricAlgorithm-based classes.
|
|
|
|
|
Thanks I will try it out soon.
|
|
|
|
|
Tony, thanks for posting this. It is elegant and I think it's exactly what I need.
Perhaps you can take it one more step: How do you recommend storing the key? Would you encrypt the key itself in the config file? If so, could you outline the steps required so a newbie can understand them?
If not, how?
|
|
|
|
|
Well, encrypting the key is sort of a non-solution, since then you'd need a key to decrypt the key and where would you store that?
In the end, I have stored these keys in three general places, depending on my environment.
If I "own" the web server (assuming this is a web app), I store the key and vector in the registry in some location that is read-only to the IIS worker-process account.
If I am in a shared web server environment, I will store it in the web.config, but then I will encrypt the web.config. Not a bad idea for the purposes of protecting your database connection string also, if you have one.
Encrypting and Decrypting Configuration Sections[^]
As a third option, particularly when not doing a browser/web application, I have been known to store it in the DB and then just load it into memory in the client on load. This is secure enough in that it's not stored on the client machines, but it is stored in memory, so I guess that if you started playing games with tools able to read in-memory data, you could lose it - but in 20 years of building line-of-business applications, I haven't had one client consider this a risk worth worrying about.
|
|
|
|
|
I believe I have read that once you have encrypted a section of the web.config file all you need to do to get the unencrypted source was to read it via a get.settings type of code; the decryption occurs behind-the-scenes. And was rejected if called for via other methods. However, encrypting the config file setting in the first place may not be the best solution for us as each client has their own copy of the web site and their own config file.
But I am going to follow up on your store-in-db suggestion and evaluate that in our environment.
|
|
|
|
|
Tony, Thank you very much for the great example. Do I need to worry about an encrypted string including a single quote such that I will have problems writing the encrypted string to a database from a vb.net object that calls a stored procedure ? Thank you again. Jim
|
|
|
|
|
that's not a problem, as the strings are base-64 encoded.
|
|
|
|
|
I am trying to adapt your code to be used on a windows mobile device and have encountered a problem. In the decrypt funtion of your code,
Public Function Decrypt(ByVal text As String) As String
Dim input() As Byte = Convert.FromBase64String(text)
Dim output() As Byte = Transform(input, _
m_des.CreateDecryptor(m_key, m_iv))
Return m_utf8.GetString(output)
End Function
I am getting an error for the line reading Return m_utf8.GetString(output). It seems to want an index and a count as integers. Any suggestions? I am not sure what index or count it is refering to. Do you know if this code should work using the compact framework? Thanks for your help.
P.S. The code seems to work just fine for a desktop application, I only receive the error when trying to use the code in an application with a target platform of windows mobile.
modified on Thursday, November 19, 2009 12:45 PM
|
|
|
|
|
First, the disclaimer: I have never used this on the compact framework, so all bets are off.
Now, that being said, there isn't much "exciting" going on here, so I can't think of anything that would not work. Bear in mind, however, that not all objects, interfaces, signatures, etc. exist in the CF (as you have just found).
Having not looked at this at all, my guess is that the overload that my code is using for the GetString() function is not supported by the CF. If it is asking for an index and a count, then it probably wants to know where to start readying the array and how many bytes to read from the array. In the existing overload, the index defaults to 0 and the count defaults to the length of the array, so if you were to do something along the lines of:
Return m_utf8.GetString(output, 0, output.Length)
I would think that should work. You'll have to try it to be sure, though.
Good luck!
|
|
|
|
|
I'll give it a try and let you know, thanks for the quick reply!
|
|
|
|
|