I am new to encryption and have run into an issue with a small encryption/decryption class I created (which is a blend of a few different pieces I came across on the internet).
I have found that while most strings work with the four symmetric algorithms I have enabled for the class (DES, RC2, Rijndael, and TripleDES), sometimes I receive a cryptographicexception Length of the data to decrypt is invalid. My impression is that it has something to do with the block sizes required by each algorithm, but am unclear why it allows me to encrypt a string using a particular algorithm, but then fails on the decryption. To that end, I have no idea as to what needs to be done to fix it so that I get 100% encryption/decryption based on a string and supplied key.
I have built a small webform to test the class, which allows me to enter the string, key, and select the desired algorithm.
Anything that can be explained to shed light on this issue would be greatly appreciated.
Thanks in advance.
Code is below (class, html for webform, codebehind for webform)
class CryptoSymmetric
/******************************************************************/
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
namespace CSCodeSamples
{
///
/// Summary description for SymmCrypto2.
///
public class CryptoSymmetric
{
public enum EncryptionAlgorithm
{
DES, RC2, Rijndael, TripleDES
}
private enum CryptoMethod
{
Encrypt, Decrypt
}
private SymmetricAlgorithm objCryptoService;
private EncryptionAlgorithm algorithmID;
private byte[] bytesKey;
private byte[] bytesIV;
public byte[] EncryptionKey
{
get
{
return bytesKey;
}
set
{
bytesKey = value;
}
}
public byte[] EncryptionIV
{
get
{
return bytesIV;
}
set
{
bytesIV = value;
}
}
///
/// Constructor for using an intrinsic .Net SymmetricAlgorithm class.
///
public CryptoSymmetric(EncryptionAlgorithm CryptoSelected)
{
switch (CryptoSelected)
{
case EncryptionAlgorithm.DES:
{
objCryptoService = new DESCryptoServiceProvider();
algorithmID = EncryptionAlgorithm.DES;
break;
}
case EncryptionAlgorithm.RC2:
{
objCryptoService = new RC2CryptoServiceProvider();
algorithmID = EncryptionAlgorithm.RC2;
break;
}
case EncryptionAlgorithm.Rijndael:
{
objCryptoService = new RijndaelManaged();
algorithmID = EncryptionAlgorithm.Rijndael;
break;
}
case EncryptionAlgorithm.TripleDES:
{
objCryptoService = new TripleDESCryptoServiceProvider();
algorithmID = EncryptionAlgorithm.TripleDES;
break;
}
}
}
private ICryptoTransform CreateServiceProvider(byte[] bKey, CryptoMethod method)
{
// Pick the provider.
switch (algorithmID)
{
case EncryptionAlgorithm.DES:
{
objCryptoService = new DESCryptoServiceProvider();
objCryptoService.Mode = CipherMode.CBC;
break;
}
case EncryptionAlgorithm.TripleDES:
{
objCryptoService = new TripleDESCryptoServiceProvider();
objCryptoService.Mode = CipherMode.CBC;
break;
}
case EncryptionAlgorithm.RC2:
{
objCryptoService = new RC2CryptoServiceProvider();
objCryptoService.Mode = CipherMode.CBC;
break;
}
case EncryptionAlgorithm.Rijndael:
{
objCryptoService = new RijndaelManaged();
objCryptoService.Mode = CipherMode.CBC;
break;
}
default:
{
throw new CryptographicException("Algorithm ID '" +
algorithmID +
"' not supported.");
}
}
// set up the KEY and IV
bytesKey = bKey;
if (bytesIV == null)
{
bytesIV = bKey;
}
// now determine whether to send back the encryptor or decryptor
switch(method)
{
case CryptoMethod.Encrypt:
return objCryptoService.CreateEncryptor(bytesKey, bytesIV);
case CryptoMethod.Decrypt:
return objCryptoService.CreateDecryptor(bytesKey, bytesIV);
default:
{
throw new CryptographicException("Method '" +
method +
"' not supported.");
}
}
}
private byte[] GetLegalKey(string Key)
{
string sTemp;
if (objCryptoService.LegalKeySizes.Length > 0)
{
int lessSize = 0, moreSize = objCryptoService.LegalKeySizes[0].MinSize;
// key sizes are in bits
while (Key.Length * 8 > moreSize)
{
lessSize = moreSize;
moreSize += objCryptoService.LegalKeySizes[0].SkipSize;
}
sTemp = Key.PadRight(moreSize / 8, ' ');
}
else
sTemp = Key;
// convert the secret key to byte array
return ASCIIEncoding.ASCII.GetBytes(sTemp);
}
public string Encrypt(string Source, string Key)
{
byte[] bInput = System.Text.ASCIIEncoding.ASCII.GetBytes(Source);
// create a MemoryStream so that the process can be done without I/O files
System.IO.MemoryStream ms = new System.IO.MemoryStream();
byte[] bKey = GetLegalKey(Key);
// create an Encryptor
ICryptoTransform encrypto = CreateServiceProvider(bKey, CryptoMethod.Encrypt);
// create Crypto Stream that transforms a stream using the encryption
CryptoStream cs = new CryptoStream(ms, encrypto, CryptoStreamMode.Write);
// write out encrypted content into MemoryStream
cs.Write(bInput, 0, bInput.Length);
cs.FlushFinalBlock();
// get the output and trim the '\0' bytes
byte[] bOutput = ms.GetBuffer();
int i = 0;
for (i = 0; i < bOutput.Length; i++)
if (bOutput[i] == 0)
break;
// convert into Base64 so that the result can be used in xml
return Convert.ToBase64String(bOutput, 0, i);
}
public string Decrypt(string Source, string Key)
{
// convert from Base64 to binary
byte[] bInput = System.Convert.FromBase64String(Source);
// create a MemoryStream with the input
System.IO.MemoryStream ms = new System.IO.MemoryStream(bInput, 0, bInput.Length);
byte[] bKey = GetLegalKey(Key);
// create a Decryptor
ICryptoTransform decrypto = CreateServiceProvider(bKey, CryptoMethod.Decrypt);
// create Crypto Stream that transforms a stream using the decryption
CryptoStream cs = new CryptoStream(ms, decrypto, CryptoStreamMode.Read);
// read out the result from the Crypto Stream
System.IO.StreamReader sr = new System.IO.StreamReader(cs);
return sr.ReadToEnd();
}
}
}
html for webform CryptoForm
/******************************************************************/
<%@ Page language="c#" Codebehind="CryptoForm.aspx.cs" AutoEventWireup="false" Inherits="CSCodeSamples.CryptoForm" %>
CryptoForm
codebehind class CryptoForm
/******************************************************************/
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Security;
using System.Security.Cryptography;
namespace CSCodeSamples
{
///
/// Summary description for CryptoForm.
///
public class CryptoForm : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Label Label1;
protected System.Web.UI.WebControls.Label Label2;
protected System.Web.UI.WebControls.Label Label3;
protected System.Web.UI.WebControls.Button Button2;
protected System.Web.UI.WebControls.HyperLink HyperLink1;
protected System.Web.UI.WebControls.Label Label4;
protected System.Web.UI.WebControls.Label Label5;
protected System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1;
protected System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator2;
protected System.Web.UI.WebControls.TextBox txtSample;
protected System.Web.UI.WebControls.TextBox txtKey;
protected System.Web.UI.WebControls.DropDownList drpType;
protected System.Web.UI.WebControls.TextBox txtDecrypt;
protected System.Web.UI.WebControls.TextBox txtEncrypt;
protected System.Web.UI.WebControls.Label lblTextLength;
protected System.Web.UI.WebControls.Label lblKeyLength;
protected System.Web.UI.WebControls.Label lblEncryptLength;
protected System.Web.UI.WebControls.Label lblDecryptLength;
protected System.Web.UI.WebControls.Button Button1;
private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.Button1.Click += new System.EventHandler(this.Button1_Click);
this.Button2.Click += new System.EventHandler(this.Button2_Click);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
private void Button1_Click(object sender, System.EventArgs e)
{
// default to DES crypto algorithm
CryptoSymmetric objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.DES);
switch(drpType.SelectedIndex)
{
case 0:
objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.DES);
break;
case 1:
objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.RC2);
break;
case 2:
objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.Rijndael);
break;
case 3:
objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.TripleDES);
break;
}
txtEncrypt.Text = objCrypto.Encrypt(txtSample.Text, txtKey.Text);
DisplayLengths();
}
private void Button2_Click(object sender, System.EventArgs e)
{
// default to DES crypto algorithm
CryptoSymmetric objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.DES);
switch(drpType.SelectedIndex)
{
case 0:
objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.DES);
break;
case 1:
objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.RC2);
break;
case 2:
objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.Rijndael);
break;
case 3:
objCrypto = new CryptoSymmetric(CryptoSymmetric.EncryptionAlgorithm.TripleDES);
break;
}
txtDecrypt.Text = objCrypto.Decrypt(txtEncrypt.Text, txtKey.Text);
DisplayLengths();
}
private void DisplayLengths()
{
lblTextLength.Text = txtSample.Text.Length.ToString();
lblKeyLength.Text = txtKey.Text.Length.ToString();
lblEncryptLength.Text = txtEncrypt.Text.Length.ToString();
lblDecryptLength.Text = txtDecrypt.Text.Length.ToString();
}
}
}