Reputation: 43
Hello I am trying to encrypt / decrypt a string via Rijaendal. I simply can't figure out why the decryption blows up. I always end up with an incorrect padding error. One thing that throws me off is the result of my encryption which I return as HEX array. It has a length of 14 bytes. In my decryption function, the same byte array ends up having 16 bytes upon conversion from HEX.
Any help would be appreciated:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace rjandal
{
class Program
{
static void Main(string[] args)
{
string DataForEncrypting = "this is a test";
string key = string.Empty;
string iv = string.Empty;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
rmt.GenerateKey();
rmt.GenerateIV();
key = Convert.ToBase64String(rmt.Key);
iv = Convert.ToBase64String(rmt.IV);
}
string encryptedData = _encrypt(DataForEncrypting, key, iv);
string unencryptedData = _decrypt(key, iv, HexString2Ascii(encryptedData));
Console.WriteLine(unencryptedData);
Console.WriteLine(encryptedData);
Console.ReadKey();
}
private static string _encrypt(string value, string key, string initVector)
{
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(value);
byte[] encBuffer;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
encBuffer = rmt.CreateEncryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector)).TransformFinalBlock(buffer, 0, buffer.Length);
}
string encryptValue = ConvertToHex(ASCIIEncoding.ASCII.GetString(encBuffer));
return encryptValue;
}
private static string _decrypt(string key, string initVector, string value)
{
byte[] hexBuffer = ASCIIEncoding.ASCII.GetBytes(value);
byte[] decBuffer;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
decBuffer = rmt.CreateDecryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector)).TransformFinalBlock(hexBuffer, 0, hexBuffer.Length);
}
return System.Text.ASCIIEncoding.ASCII.GetString(decBuffer);
}
private static string ConvertToHex(string asciiString)
{
string hex = "";
foreach (char c in asciiString)
{
int tmp = c;
hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
}
return hex;
}
private static string HexString2Ascii(string hexString)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i <= hexString.Length - 2; i += 2)
{
sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hexString.Substring(i, 2), System.Globalization.NumberStyles.HexNumber))));
}
return sb.ToString();
}
}
}
Upvotes: 4
Views: 4812
Reputation: 21
You may avoid the issues with Decypting/Encrypting and usign System.Text.Encoding and avoid using Base64 encoding work around, by adding a few methods that completely bypass microsoft's mismatched conversions in the System.Text.Encoding, by allowing you to encrypt the real bytes in memory without any translations.
Since using these I have avoided padding errors caused by System.Text.Encoding methods, without using the Base64 conversions either.
private static Byte[] GetBytes(String SomeString)
{
Char[] SomeChars = SomeString.ToCharArray();
Int32 Size = SomeChars.Length * 2;
List<Byte> TempList = new List<Byte>(Size);
foreach (Char Character in SomeChars)
{
TempList.AddRange(BitConverter.GetBytes(Character));
}
return TempList.ToArray();
}
private static String GetString(Byte[] ByteArray)
{
Int32 Size = ByteArray.Length / 2;
List<Char> TempList = new List<Char>(Size);
for (Int32 i = 0; i < ByteArray.Length; i += 2)
{
TempList.Add(BitConverter.ToChar(ByteArray, i));
}
return new String(TempList.ToArray());
}
And how they are used with encryption
private static String Encrypt(String Test1, Byte[] Key, Byte[] IV)
{
Byte[] Encrypted;
using (AesCryptoServiceProvider AesMan = new AesCryptoServiceProvider())
{
AesMan.Mode = CipherMode.CBC;
AesMan.Padding = PaddingMode.ISO10126;
ICryptoTransform EncThis = AesMan.CreateEncryptor(Key, IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, EncThis, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(Test1);
}
Encrypted = msEncrypt.ToArray();
}
}
};
return GetString(Encrypted);
}
private static String Decrypt(String Data, Byte[] Key, Byte[] IV)
{
String Decrypted;
using (AesCryptoServiceProvider AesMan = new AesCryptoServiceProvider())
{
AesMan.Mode = CipherMode.CBC;
AesMan.Padding = PaddingMode.ISO10126;
ICryptoTransform EncThis = AesMan.CreateDecryptor(Key, IV);
using (MemoryStream msDecrypt = new MemoryStream(GetBytes(Data)))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, EncThis, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
Decrypted = srDecrypt.ReadToEnd();
}
}
}
}
return Decrypted;
}
Upvotes: 2
Reputation: 185643
You shouldn't be using ASCII character encoding as an intermediate step; you should change your functions that go from hex to ASCII (and back again) to go from a byte[]
to hex (and back again) instead.
private static string ConvertToHex(byte[] data)
{
string hex = "";
foreach (byte b in data)
{
hex += b.ToString("X2");
}
return hex;
}
private static byte[] HexString2ByteArray(string hexString)
{
byte[] output = new byte[hexString.Length / 2];
for (int i = 0; i <= hexString.Length - 2; i += 2)
{
output[i/2] = Convert.ToByte(hexString.Substring(i, 2), 16);
}
return output;
}
As a side note, is there a reason that you're looking for a hex representation of the array versus something more compact like Base64? You're using Base64 in your example to transfer the key and IV, so I'm just curious about what makes you want to return the encrypted data as hex here.
In any case, here's something that should work for you:
private static string _encrypt(string value, string key, string initVector)
{
byte[] buffer = Encoding.Unicode.GetBytes(value);
byte[] encBuffer;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
encBuffer = rmt.CreateEncryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector)).TransformFinalBlock(buffer, 0, buffer.Length);
}
string encryptValue = ConvertToHex(encBuffer);
return encryptValue;
}
private static string _decrypt(string key, string initVector, string value)
{
byte[] hexBuffer = HexString2ByteArray(value);
byte[] decBuffer;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
decBuffer = rmt.CreateDecryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector)).TransformFinalBlock(hexBuffer, 0, hexBuffer.Length);
}
return Encoding.Unicode.GetString(decBuffer);
}
Upvotes: 2
Reputation: 1500514
You're doing way too much conversion between text and data, basically. Look at this, for example:
string encryptValue = ConvertToHex(ASCIIEncoding.ASCII.GetString(encBuffer));
Once you've got an ASCII string, why would you need to convert that into hex? It's already text! But by then you'll already have lost the data. Unless you really need it in hex (in which case follow Adam's suggestion and change your HexToAscii method to take a byte[] instead of a string) you should just use Convert.ToBase64String:
string encryptValue = Convert.ToBase64String(encBuffer);
Use Convert.FromBase64String
at the other end when decrypting. You can then get rid of your hex methods completely.
Oh, and in general I wouldn't use Encoding.ASCII
to start with... I'd almost always use Encoding.UTF8
instead. Currently you'll fail to encrypt (correctly) any strings containing non-ASCII characters such as accents.
Here's a rejigged version of your test program, with a few of those changes made. Note that the names "cipher text" and "plain text" are in terms of encryption... they're still binary data rather than text!
using System;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main(string[] args)
{
string DataForEncrypting = "this is a test";
string key = string.Empty;
string iv = string.Empty;
using (RijndaelManaged rmt = new RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = CipherMode.CBC;
rmt.Padding = PaddingMode.ISO10126;
rmt.GenerateKey();
rmt.GenerateIV();
key = Convert.ToBase64String(rmt.Key);
iv = Convert.ToBase64String(rmt.IV);
}
string encryptedData = _encrypt(DataForEncrypting, key, iv);
string unencryptedData = _decrypt(key, iv, encryptedData);
Console.WriteLine(unencryptedData);
Console.WriteLine(encryptedData);
Console.ReadKey();
}
private static string _encrypt(string value, string key, string initVector)
{
using (RijndaelManaged rmt = new RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = CipherMode.CBC;
rmt.Padding = PaddingMode.ISO10126;
byte[] plainText = Encoding.UTF8.GetBytes(value);
byte[] cipherText = rmt.CreateEncryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector))
.TransformFinalBlock(plainText, 0, plainText.Length);
return Convert.ToBase64String(cipherText);
}
}
private static string _decrypt(string key, string initVector, string value)
{
using (RijndaelManaged rmt = new RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = CipherMode.CBC;
rmt.Padding = PaddingMode.ISO10126;
byte[] cipherText = Convert.FromBase64String(value);
byte[] plainText = rmt.CreateDecryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector))
.TransformFinalBlock(cipherText, 0, cipherText.Length);
return Encoding.UTF8.GetString(plainText);
}
}
}
Upvotes: 5