|
Introduction
There are many, many examples of how to do encryption and decryption in .NET. So why one more? Well, for one thing, I find that the majority of good code examples of any type are issued in C#. This isn't surprising, since those from a C++ or Java background will have an easier transition to C# than a similarly experienced Visual Basic developer moving to VB.NET. It will take time for the VB.NET community to develop into what the C++ and Java community already has at its disposal. So, I thought that a solid, simple VB.NET example would do well.
Overview
This code example does four things that I think are missing from the examples that I have seen so far:
- It's written in Visual Basic .NET. Not that it is in any way superior or inferior to C#, but most of the other examples (and all of the good ones) were done in C#.
- It uses the UTF-8 encoder to ensure that the strings which are encrypted or decrypted are in an 8-bit format. Many examples that I see use the ASCII encoding, which is a 7-bit format. When you combine this with TripleDES (which uses 3, 8-bit blocks for the key), you can get yourself into situations where you cannot decrypt something you encrypted.
- It uses the
Convert object's Base-64 methods to make sure that the encrypted text is output in such a way that it can be easily stored in text files and/or database fields without the risk of your encrypted content being inadvertently modified by implicit conversions.
- It centralizes the actual encryption and decryption functionality into a single method, thus eliminating what would otherwise be 99% redundant code.
The Code
The complete cTripleDES class in VB.NET: Imports System.IO
Imports System.Text
Imports System.Security.Cryptography
Friend Class cTripleDES
Private m_des As New TripleDESCryptoServiceProvider
Private m_utf8 As New UTF8Encoding
Private m_key() As Byte
Private m_iv() As Byte
Public Sub New(ByVal key() As Byte, ByVal iv() As Byte)
Me.m_key = key
Me.m_iv = iv
End Sub
Public Function Encrypt(ByVal input() As Byte) As Byte()
Return Transform(input, m_des.CreateEncryptor(m_key, m_iv))
End Function
Public Function Decrypt(ByVal input() As Byte) As Byte()
Return Transform(input, m_des.CreateDecryptor(m_key, m_iv))
End Function
Public Function Encrypt(ByVal text As String) As String
Dim input() As Byte = m_utf8.GetBytes(text)
Dim output() As Byte = Transform(input, _
m_des.CreateEncryptor(m_key, m_iv))
Return Convert.ToBase64String(output)
End Function
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
Private Function Transform(ByVal input() As Byte, _
ByVal CryptoTransform As ICryptoTransform) As Byte()
Dim memStream As MemoryStream = New MemoryStream
Dim cryptStream As CryptoStream = New _
CryptoStream(memStream, CryptoTransform, _
CryptoStreamMode.Write)
cryptStream.Write(input, 0, input.Length)
cryptStream.FlushFinalBlock()
memStream.Position = 0
Dim result(CType(memStream.Length - 1, System.Int32)) As Byte
memStream.Read(result, 0, CType(result.Length, System.Int32))
memStream.Close()
cryptStream.Close()
Return result
End Function
End Class
The complete cTripleDES class in C#: using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
namespace Utilities.Crypto
{
class cTripleDES
{
private TripleDESCryptoServiceProvider m_des =
new TripleDESCryptoServiceProvider();
private UTF8Encoding m_utf8 = new UTF8Encoding();
private byte[] m_key;
private byte[] m_iv;
public cTripleDES(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 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);
}
private byte[] Transform(byte[] input,
ICryptoTransform CryptoTransform)
{
MemoryStream memStream = new MemoryStream();
CryptoStream cryptStream = new CryptoStream(memStream,
CryptoTransform, CryptoStreamMode.Write);
cryptStream.Write(input, 0, input.Length);
cryptStream.FlushFinalBlock();
memStream.Position = 0;
byte[] result = memStream.ToArray();
memStream.Close();
cryptStream.Close();
return result;
}
}
}
Using the cTripleDES class in VB.NET:
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 iv() As Byte = {8, 7, 6, 5, 4, 3, 2, 1}
Private des As New cTripleDES(key, iv)
Private ReadOnly encryptedData As String = "++XIiGymvbg="
Private decryptedData As String = des.Decrypt(encryptedData)
Private newEncryptedData As String = des.Encrypt(decryptedData)
Using the cTripleDES class in C#:
byte[] key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
byte[] iv = {8, 7, 6, 5, 4, 3, 2, 1};
cTripleDES des = new cTripleDES(key, iv);
string encryptedData = "++XIiGymvbg=";
string decryptedData = des.Decrypt(encryptedData);
string newEncryptedData = des.Encrypt(decryptedData);
Conclusion
Hopefully, this simple, straight-forward example shows how to effectively and efficiently wrap the TripleDES (or any other encryption) functionality in Visual Basic .NET syntax. Of course, the same principles can just as easily be applied to a C#, J#, or managed C++ implementation.
Update (March 17, 2005)
It's been nearly two years since I first published this article and it's gotten a fair number of views, votes, and thanks. I'd like to thank you all for your kindness and, in return, I have updated the article with the much asked-for C# version of the class. Happy coding!
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 80 (Total in Forum: 80) (Refresh) | FirstPrevNext |
|
 |
|
|
I have one encrypted password added into database using java application. Now i am developing .net application and want to manipulate the existing encrypted password but this class giving me different encrypted string for sa password.
Any help?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I love this class...it is (almost)exactly what I'm looking for!
I do have one question for you however. I am parsing a multi-part mime request, and only one of the "fields" is encrypted. I already know: (1)it is using triple DES in CBC mode, and (2)the key that was used to encrypt it.
What changes would I need to make to be able to decrypt the value?
Thanks for making this so easy to understand.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Three things:
First, you need to add a line of code in the constructor that will tell the TDES instance variable to use CBC. Someone else previously had thhis need and used this method successfully.
Second, if your encryption process used a vector (IV), you'll need to know & set that prior to decrypting.
At this point, you can simply calll one of the Decrypt methods as long as your "field" has been stored as a string variable that is Base64 encoded or a byte array. If not, then your third step is to create a third set of Encryppt/Decrypt overloads similar to the ones that are there now, but able to accept & return the data format that you have.
HTH
Tony
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Tony, I love the simplicity and reliability of this code. I am using it in a database program to encrypt passwords. (And I am including your name in the code documentation.) I have a question about the encryption key byte arrays. Are the values just random in those arrays? Could I put any values into them when initializing the class? Thanks for sharing your knowledge, Scott
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
Thanks for the feedback! I am glad it's proven useful for you.
The key and vector (IV) byte arrays are values that you must pre-fill and pass in as part of the constructor of the class. The class itself does not pre-fill them with any sort of random data.
Perhaps you are refering to the example useage code above, where I pre-fill the arrays with sequential values before passing them into the class constructor? Again, that's for example ony. You can and should pick your own key and vector values prior to using the class in your own code.
Maybe I don't quite follow your question?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
That's exactly what I was asking. So, if I understand, I could populate those arrays with any values (within byte limits) I want, and that then becomes the encryption key for that instance of the class. Then, only if I have the same encryption key, could I decrypt those values. Is that accurate? Scott
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
That is it exactly. I would keep the same array lengths, but definitly fill them with your own values. You could collect a password string and then hash the string and use the hash as your key. Or you could use the password string directly by using one of the string encoding classes to convert it to a byte array, as long as you limited the maximum length of the password and padded anything shorter with a known character. I know one team that used a hash and a fixed prefix, so that even if you knew the hash type and password, you had to have the "secret key" prefix to decrypt the data. Whatever works for you!
Tony
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
OK, I've got this working after a few tweaks for my particular needs....
BUT, in ASP.NET (VB, not C#) I've got a Key and an IV
When I swap over to my Oracle PL/SQL Package that I'm passing the encrypted data to, I also have the tripledes object, but it only accepts one Key.... What do I do with the IV?
dbms_obfuscation_toolkit.des3decrypt ( input => l_datar, --input data to be decrypted key => utl_raw.cast_to_raw(p_key), -encryption key passed in which => dbms_obfuscation_toolkit.ThreeKeyMode, --use des3 encrypted_data => l_retval --output );
In short, how in the world do you use PL/SQL to decrypt the value sent from the web page?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The class is declared with these values:
IV=new byte[8] {47, 27, 31, 135, 234, 223, 2, 141}; Key=new byte[24] {11, 217, 31, 191, 87, 44, 107, 241, 5, 221, 226, 153, 53, 15, 48, 92, 214, 122, 103, 3, 66, 166, 71, 171}; des=new cTripleDES(Key,IV);
I have a declared jagged byte array: byte[][] a=new byte[12][];
This function returns a byte[] from a varbinary column in a SQL table: ComplogData.GetEncryptedDataByDomainID(Convert.ToInt16(sDomainID));
I assign a[x] to the return value: a[x]=ComplogData.GetEncryptedDataByDomainID(Convert.ToInt16(sDomainID));
It so happens that I set twelve rows in the SQL table to the same encrypted data.
So with the following I am iterating through the jagged array forward, then backward decrypting the data each time:
for (x=0;x<=11;x++) { Console.Write("x="+x.ToString()+": "); foreach (byte y in a[x]) { Console.Write(y.ToString()+":"); } c=des.Decrypt(a[x]); Console.WriteLine(this.ByteToString(c)); } Console.WriteLine("------------------------------------------------------"); for (x=11;x>=0;x--) { Console.Write("x="+x.ToString()+": "); foreach (byte y in a[x]) { Console.Write(y.ToString()+":"); } c=des.Decrypt(a[x]); Console.WriteLine(this.ByteToString(c)); }
Can anyone explain why I would get the following results:
x=0: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:]}iå«7¨ x=1: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:]}iå«7¨ x=2: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:]}iå«7¨ x=3: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=4: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=5: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=6: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=7: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=8: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=9: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=10: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=11: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% ------------------------------------------------------ x=11: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=10: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=9: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=8: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=7: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=6: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=5: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=4: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=3: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=2: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=1: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=0: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5%
And here is a reversal I tried by switching the loops:
x=11: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:]}iå«7¨ x=10: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:]}iå«7¨ x=9: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=8: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=7: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=6: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=5: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=4: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=3: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=2: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=1: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=0: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% ------------------------------------------------------ x=0: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=1: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=2: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=3: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=4: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=5: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=6: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=7: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=8: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=9: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=10: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5% x=11: 11:206:145:108:71:110:143:129:219:144:152:197:181:168:124:213:rfvbgt5%
"rfvbgt5%" is in fact the correct decryption. All of the byte[] values are identical and no error is being thrown from anywhere. Why would the decryption not be consistent?
I've run this dozens of times and it comes out like this every time. It looks to me like the CryptoAPI for tripleDES at least, is broken in .NET 1.0, but did it ever work? When it is wrong, it is consistently wrong and it almost seems like there is a timing error involved as it is the first few iterations either way that are incorrect. This boggles my mind trying to understand what is going on.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The very first time I did crypto in .NET it was Fx 1.0. At that time, I was running into some very wierd problems that looked almost like memory leaks. In that case, as I recall, anytime I called the XmlDocument.Load() function, the IV property of my 3DES class would have its content overwritten with zeros. After days of fighting with it and talking to Microsoft, I gave up and used a C++ library with P/Invoke calls.
The next time I did this work with the Fx 1.1 framework and that worked flawlessly.
So, in short, I know that 1.0 had problems, but I don't know how bad or widespread they were.
Tony
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
This class works great if you supply the correct key during decryption, but if a bad key is supplied it is generating a "Bad Data" error:
An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
Additional information: Bad Data.
Here:
cryptStream.FlushFinalBlock(); // Read the memory stream and // convert it back into byte array memStream.Position=0;
Is this normal and it's simply up to me to build on this class by catching the error and returning some sort of code back to the caller?
I'm writing code which allows a user to input all or part of a key like a password, basically I'm using something similar to:
byte[] key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
as padding and replacing 1 or more positions with user input.
If the user enters the wrong password, so that the resulting key is incorrect, I assumed the decryption would still occur with the wrong key; but the decrypted data would of course be bad data. I didn't expect the unhandled exception.
Should I catch the unhandled exception and assume "Bad Data" mean the key is bad and handle it accordingly, or is there something else I'm not understanding?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I ended up using:
cryptStream.Write(input, 0, input.Length); try { cryptStream.FlushFinalBlock(); } catch { Console.WriteLine("Bad Data"); } // Read the memory stream and // convert it back into byte array memStream.Position=0; byte[] result = memStream.ToArray(); // close and release the streams memStream.Close(); try { cryptStream.Close(); } catch { Console.WriteLine("Bad Data"); }
It ends up returning what appears to be partially decrypted data. I'm guessing that the DES algorithm runs into trouble with bad keys such as divide by 0 or something similar and can't continue. Returning bad data on a bad key is all I wanted, so this small modification works for me.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The function does not attempt to trap and "eat" or re-throw errors. It was designed as a "helper class" and trapping errors is left to the caller/consumer. I don't like to handle errors at this low a level, since that would require me to somehow predict if/when/how any potential consumer would want such error handling to happen. It is much more flexible to leave the error trapping & handling to the consumer of such a library function.
I am not certain off the top of my head under what conditions the 3DES decryption would fail versus "succeed" with bad data as the result. You would have to research or experiment with that on your own.
On an unrelated note, might I suggest you try the updated version of this code, which can be found in another article titled Generic SymmetricAlgorithm Helper[^]. This version of the code is only available in C#, but it has additional functionality as well as the ability to handle all the .NET symmetric encryption providers.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Unfortunately I'm using .NET 1.1, so I guess your newer class isn't going to work for me. I'll see if I can figure out what is going wrong with the way I'm using this one.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I needed encryption on the compact.net framework for a personal project I am working on. I found this article and since 3des is supposed to be fully working in the compact.net cryptography side, I tried it out. It only needed a very very minor tweak to get working. I changed which constructor is used for the final call in the decrypt method. The example is below.
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, 0, output.Length) End Function
The only change being which constructor the utf8 object uses for GetString. It turns out that the constructor used in the original code sample is NOT ported to Compact.NET but this one is and it really does do the same thing with the additional parameters supplied. I have tested this on a compact.net emulator but the project is not ready for a full deployment so I have not actually tested this on a device yet.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
The abstract base for TripleDESCryptoServiceProvider is SymmetricAlgorithm and that only exposes the Key and Initialization Vector (IV) properties, which is all that is used for encryption. I don't know the "down and dirty" similarities or differences between the Salt and IV, but they appear to be functionally analogous here.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I was wondering if you can assist me. I will be passing a string to be decrypted. Where is I set the password, IV and how in what order do I call your code so that I get back a decrypted answer
Freddy
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Two things:
First, there are examples of exactly how to use the class in the article above. Take a look above and follow those. YOu shouldn't need anything else.
Second, I would reccommend using the updated version of this code, which you can find here:
Generic SymmetricAlgorithm Helper[^]
Tony
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
I did look at this article and I liked the idea of having built-in validation. When it comes to my data, I don't mind wearing a belt and suspenders. 
I had left you links to my related articles, only because I thought that readers interested in that topic would also be interested in combining your unique functionality with a more simple provider model that would work with any string or byte array.
Thanks for the post!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I published a new Code Project article about a new and very improved version of this class. It is only available in C#, but it allows for using any .NET Framework crypto provider and it includes built-in support for handling Stream objects directly (MemoryStream, FileStream, etc.).
Generic SymmetricAlgorithm Helper[^]
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
i want to encrypt xml data (DES AND RSA) using device pocket pc in vb.net.. can u help me???cause i need it so much.. many components in regular form cannot be used in pocket pc form, such as tripledesprovider and etc.. please help me..thanks
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
hello, I'm having a similar problem as andreas nolte (the previous messager)
An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
Additional information: Bad Data.
any answers?
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
| | |