Library tutorials & articles
Using Encryption in .NET
Choosing a Cipher
Choosing a cipher
The first step toward your goal of encrypting data in C# is to choose a cipher. There are 4 ciphers provided by the FCL: DES, RC2, Rijndael and TripleDES. I'll offer a brief explanation of these and offer some advice as to why you would use one over another.
DES – DES stands for Data Encryption Standard. This is an old algorithm that has been shown to be weak by today's standards. The only weakness I'll discuss here is that it uses a short key and block size of 64 bits. We discussed the problem with short keys above, but short blocks are also weak, for reasons that are beyond the scope of this article. If you would like to learn more about why short keys and blocks are bad, search the web for "brute force key attack", "birthday paradox", "birthday attack" and "meet-in-the-middle attack", or read Practical Cryptography, by Schneier & Ferguson. Schneier & Ferguson recommend a key and block length of 256 bits. Another problem with DES is that it was purposely designed to be efficient in hardware and inefficient in software. This makes DES a relatively slow algorithm in any software implementation.
Triple DES – Triple DES is simply a strengthened version of DES. To strengthen the cipher, Triple DES runs the DES algorithm over the plain text three times, hence the "triple" in the name. It also offers stronger key lengths of 128 and 192 bits. However, it still uses a relatively weak block size of 64 bits. Another disadvantage of Triple DES is that, since it must perform the DES algorithm three times, it is a relatively slow cipher.
RC2 – RC2 is generally accepted as a good cipher and it has been around since the mid 1990s. It is also more than twice as fast as DES when implemented in software. However, it still uses a 64 bit block size and a key length (in the FCL implementation) of 40 to 128 bits in 8-bit increments.
Rijndael (a.k.a. AES) – Rijndael is the U.S. government's Federal Information Processing Standard (FIPS) Advanced Encryption Standard (AES) cipher algorithm. The names Rijndael and AES are used interchangeably in cryptography literature. Rijndael offers key lengths and block sizes of 128, 192 and 256 bits. One drawback of Rijndael is it's relative newness. Although it is highly recommended and considered strong by most cryptographers, a new cipher is one that has not withstood the same scrutiny and test of time as an older, more seasoned cipher. Newness not withstanding, the available 256-bit key and block lengths and it's acceptance as a government standard lead me to favor this cipher and I would recommend this cipher to you if you aren't constrained by compatibility or political concerns.
Key Management and Authentication
Key management and authentication are not discussed in detail in this article, but our discussion would be incomplete without mentioning them.
Key Management – To borrow a phase from Keith Brown, in essence, cryptography simply compresses large secrets into small secrets. That is, a large, secret message is rendered unreadable and no longer secret without the use of a much smaller secret, the key. Although your message, if properly encrypted, is no longer secret, you must still maintain the secrecy of the key. If an attacker gains access to the key, your messages are obviously no longer secret. Therefore, it is absolutely essential that you implement a good key management strategy to maintain the security of your keys. Another precaution you must take is to change keys frequently. Keys with a long lifetime not only allow an attacker plenty of time to break the key, but they also increase the amount of damage an attacker can do should he succeed in recovering your key.
Authentication – Even if you properly encrypt messages in your system, you are still left with two problems on the receiving end. First, do you really know who sent the data? Is the data really from the party with whom you think you're communicating, or has an attacker sent the message? Secondly, is the data received really the same as the data that was sent by the other party, or has an attacker modified the contents of the message? These problems are addressed by Message Authentication Codes (MACs) and digital signatures, and you absolutely must implement an authentication strategy in your application so that you can detect bogus messages.
Before you read on, I would highly recommend further study of cryptographic principles if you are new to the subject. An excellent place to start would be with the book I mentioned above, Practical Cryptography, by Schneier and Ferguson.
Related articles
Related discussion
-
Binary Studio | software development outsourcing Ukraine
by shane124 (4 replies)
-
Research topic in software
by reachsangeethamathew (0 replies)
-
career improvement advice
by hnasr82 (0 replies)
-
Advice on studying and preparing for interviews
by caryatid (0 replies)
-
Chart insertation in a windows form...
by pdhanik (1 replies)
Related podcasts
-
More jQuery in ASP.NET
In this episode Chris Brandsma, Rick Strahl, Dave Ward, Bertrand Le Roy, and Scott Koon conclude their discussion of Microsoft's jQuery in ASP.NET announcement1.This episode of the Alt.NET Podcast is brought to you by LLBLGen Pro, the most mature O/R mapper and code generator out there.Are ...
Events coming up
-
Nov
18
15 Minutes of Fame
Dresher, United States
This is a yearly tradition. We select 10 of the favorite speakers from monthly meetings, code camps, and hands on labs. Each one does a 15 minute talk on their favorite .NET technology. This is our 10th anniversary so we plan a gala event with special prizes and refreshments.
I totally agree that varbinary could have been used. My only problem with that solution is that it seems difficult to work with binary data in ADO.NET and SQL Server. I may be wrong. I haven't tried it, but I seem to remember seeing some nasty code dealing with storing images and other binary data in SQL Server. The conversion to and from Base64 strings is 1 line of code in each case and then I can deal with regular char and varchar fields.
Couldn't you also store this in a Varbinary field instead of trying to convert it to a string for a varchar field?
Dear Steve,
I was wondering if you had some insight as to how to best to file encrypting with the rijndael encryption. Most examples I've found only give info. about how to encrypt messages. I know you said that the CryptoStream object was beyond your scope, but that's where I'm needing to go with this and I like to use a FileStream instead of a MemoryStream. I would also like to save the encrypted file in place of the original (which I may just have to rename them differently and then delete the original). I also know you said that you don't have to write the encryption byte for byte, but how do you do this with a FileStream? And if you do have to do it byte for byte with a FileStream, then how many bytes do I/should I set it to? Also, all examples I've seen only demonstrate how to Encrypt a file and not Decrypt it again. On the decryption, though, I only need to display it and not keep or save it anywhere. Maybe loading it into a MemoryStream might be better? Keep in mind that this files could be big (most are images). Is there any advice you could give or maybe another discussion you have?
Thanks!
Exactly. If you don't use the same IV for encryption and decryption, the message can't be properly decoded.
Problem seems to be fixed now - I'd uncommented the initialisation vector code and it looks like it's mandetory.
Also - replaced the hex encoding with base64 strings (many thanks to Ben Mills on the other thread about storing values in SQL server for pointing me in this direction).
Many thanks Ben - I've got a working solution now!
I think the problem was the fact I'd commented out the initialisation vector code.. I didn't think this was essential.
Anyhow - I've also removed my conversion routines to and from hex as the conversion to base64 strings is a lot more elegant (and the strings generated shorter).
Thanks again.
I actually ended up cracking the problem in a different way. Here's what I do to encode a .NET string:
1. Turn the string into a byte array using the ASCII encoder.
2. Run the byte array through the Rijndael encryptor (some bytes cannot now be represented as ASCII chars).
3. Convert the resulting array of bytes to a Base64 string using Convert.ToBase64String().
4. The result is an ASCII string which can be strored in regular (non-unicode) database columns.
To decrypt the string I do the reverse:
1. Convert the string from the database into a byte array using Convert.FromBase64String().
2. Run the byte array through the Rijndael decryptor.
3. Convert the resulting array back into a string using the ASCII encoder.
The trick with all of this is that the an 8 bit unicode string can be stored as a 7 bit ASCII string by converting it into a Base64 string. Base 64 strings use 4 bytes to represent every block of 3 byte unicode characters. See http://email.about.com/cs/standards/a/base64_encoding.htm for a good intro on base64 strings. The one thing you have to think about is how long to make your database columns. Just remember that every block of 3 unencrypted characters results in 4 encrypted characters in the database.
I hope this helps,
Ben Mills
Hi all,
Excellent article - made a lot of sense.
I've created a little test app which converts the byte arrays to hex strings (for storing in DB) - and then does the reverse. This all seems to work, but for some strange reason the first 32 characters of the decrypted string are garbled (everything after this point is fine!!).
I'm guessing it's something to do with padding, but not sure - maybe I'm missing something really obvious!
Any help would be hugely appreciated. I've posted the code from my test form below (hope you're okay about this and hope it benefits others)..
cheers.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Security.Cryptography;
using System.Text;
using System.IO;
using System.Globalization; // for NumberStyles
namespace EncryptionTool
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox txtToEncrypt;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button btnDecrypt;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox txtKey;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button btnEncrypt;
private System.Windows.Forms.TextBox txtEncrypted;
private System.Windows.Forms.TextBox txtDecrypted;
private System.Windows.Forms.Label label4;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.txtToEncrypt = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.txtEncrypted = new System.Windows.Forms.TextBox();
this.btnDecrypt = new System.Windows.Forms.Button();
this.label2 = new System.Windows.Forms.Label();
this.txtKey = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.btnEncrypt = new System.Windows.Forms.Button();
this.txtDecrypted = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// txtToEncrypt
//
this.txtToEncrypt.Location = new System.Drawing.Point(8, 72);
this.txtToEncrypt.Multiline = true;
this.txtToEncrypt.Name = "txtToEncrypt";
this.txtToEncrypt.Size = new System.Drawing.Size(448, 80);
this.txtToEncrypt.TabIndex = 1;
this.txtToEncrypt.Text = "";
//
// label1
//
this.label1.Location = new System.Drawing.Point(8, 56);
this.label1.Name = "label1";
this.label1.TabIndex = 2;
this.label1.Text = "Text to encrypt";
//
// txtEncrypted
//
this.txtEncrypted.Location = new System.Drawing.Point(8, 176);
this.txtEncrypted.Multiline = true;
this.txtEncrypted.Name = "txtEncrypted";
this.txtEncrypted.Size = new System.Drawing.Size(448, 80);
this.txtEncrypted.TabIndex = 3;
this.txtEncrypted.Text = "";
//
// btnDecrypt
//
this.btnDecrypt.Location = new System.Drawing.Point(464, 184);
this.btnDecrypt.Name = "btnDecrypt";
this.btnDecrypt.TabIndex = 4;
this.btnDecrypt.Text = "&Decrypt";
this.btnDecrypt.Click += new System.EventHandler(this.btnDecrypt_Click);
// <
Hi,
I'm attempting to do the same thing. I've gone down the route of converting the cipher's byte array to a Hex string using the following: -
BitConverter.ToString(bytEncryptedMessage);
This creates a hyphon delimitted string (e.g. 0A-10-FA etc...) which can be stored in the database.
When I want to decrypt I'm first calling the following function: -
// Assumes the hex string values are "-" delimitted. e.g. 0A-FA-BD
public byte[] HexStringToBytes(string sHexString)
{
string[] sStringArray;
byte[] bytByteArray;
int iCount = 0;
sStringArray = sHexString.Split('-');
bytByteArray = new byte[sStringArray.Length];
foreach (string sHexValue in sStringArray)
{
bytByteArray[iCount] = byte.Parse(sHexValue, NumberStyles.HexNumber);
iCount ++;
}
return bytByteArray;
}
which re-creates the byte array.
I thought I'd cracked it, but something really weird is happening!! When decrypted - the first 32 characters are garbage - everything after is fine!!!
I'm guessing this is something to do with the padding, but not sure.
Hope this points you in the right direction and would also really appreciate any assistance with the problem I'm having!
First of all, I'd like to say that this was a fantastic article. I was struggling through the MSDN info and other online articles and your article finally made everything clear.
I want to use this technique to encrypt credit card data before I store it in SQL Server. I take the ASCII credit card number, convert it to an array of bytes using the ASCII encoder and then encrypt the bytes. The trouble is that the bytes are now out of the ASCII range (0 to 127), so I can't convert the bytes back to an ASCII string to store in a VarChar column of a SQL Server table.
It seems like a solution is to convert the encrypted bytes to a unicode string using the unicode encoder and store that value in a NVarChar column. The only problem with this is that I usually avoid unicode SQL Server columns. Is this the only solution or do you have any other ideas?
Regarding my comment that "there is "no way" to reverse this process without the key."
I want to be clear that I mean that there is no trivial way to do this. What I'm trying to say is that the process is as secure as the key and the underlying cipher.
There is, of course, no such thing as "no way" in cryptography. We just try to use the best technology we can to make it as difficult and expensive as possible for an attacker to break the system.
Steve Johnson
Using CBC as an example, encryption proceeds as follows:
1 - XOR Plain Text Block #1 with IV
2 - Encrypt the block resulting from step 1 with the key
3 - The Cipher Text block resulting from step 3 is used as Cipher Text block #1
4 - XOR Plain Text block #2 with Cipher Text block #1
5 - Encrypt the block resulting from step 4 with the key
6 - The Cipher Text block resulting from step 5 is used as Cipher Text block #2
7 - etc, etc...
There's no way to reverse this process without the key. The IV is not being used to seed a PRNG; it's simply used as random data, with which to mix the first block of plain text on encryption and to retrieve the initial plain text block on decryption. As you can see, there is no intermediate "non-randomized" state that can be determined using the cipher text and the IV.
Steve Johnson
Wouldn't this mean that for eavesdroppers using the same random generator (which can not be that big a problem) and the transmitted seed it is possible to retrieve the 'un-randomized' (if thats a word) still encrypted content?
Wilfred Kuijpers
dbHost
You transmit the IV in the clear (unencrypted). Just add the IV to your transmitted message so the receiving computer can decrypt the message. There's no need to encrypt the IV because it is used simply to randomize the cipher text to prevent repetition.
Steve Johnson
Hi,
Great article,
Just one question though, how do you transmit your IV's? I mean, decryction usually happens at another computer and for encryption and decryption you suggest to use the iv?
regards,
Wilfred Kuijpers
dbhost
This thread is for discussions of Using Encryption in .NET.