Reputation: 2761
I've implemented a 3DES (TripleDES) decryption routine in my code and have noticed that it never produces the same cleartext when the supplied ciphertext is the same as the previous iteration. It seems non-deterministic and I'm sure it's something I'm doing wrong.
I've tried CBC (with a zeroed-out IV), ECB, multiple combinations of disposing and clearing. I cannot seem to get consistent output.
(for some reason the code formatting on the 'Code' and 'Output' below are not coming out right, sorry)
Code
using System;
using System.Security.Cryptography;
using System.Threading;
namespace Test
{
class Program
{
static void Main(string[] args)
{
while (true)
{
string key = "27F66D5244FF621EAA6F6120EDEB427F";
string cipher = "C25C1D1197D31CAA87285D59A892047426D9182EC11353C051ADD6D0F072A6CB3436560B3071FC1FD11D9F7E74886742D9BEE0CFD1EA1064C213BB55278B2F12";
Console.WriteLine("clear: " + byte_array_to_hex_string(
decrypt_3des(
hex_string_to_byte_array(cipher),
hex_string_to_byte_array(key)
), true, true
));
Console.WriteLine("");
Thread.Sleep(1000);
}
}
static byte[] decrypt_3des(byte[] cipher, byte[] key)
{
if (cipher == null) return null;
if (key == null) return null;
int num_chunks = (cipher.Length) / 8;
Console.WriteLine("Entering decrypt_3des");
Console.WriteLine(" - cipher: " + byte_array_to_hex_string(cipher, true, true));
Console.WriteLine(" - key: " + byte_array_to_hex_string(key, true, true));
Console.WriteLine(" - cipher length: " + cipher.Length);
Console.WriteLine(" - key length: " + key.Length);
if ((cipher.Length % 8) != 0)
{
Console.WriteLine("cipher length not divisble by eight");
return null;
}
if ((key.Length % 8) != 0)
{
Console.WriteLine("key length not divisible by eight");
return null;
}
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = key;
tdes.Mode = CipherMode.ECB;
tdes.IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
tdes.Padding = PaddingMode.Zeros;
tdes.KeySize = (key.Length * 8);
ICryptoTransform ct = tdes.CreateDecryptor();
Console.WriteLine(" - IV: " + byte_array_to_hex_string(tdes.IV, true, true));
byte[] clear = new byte[(cipher.Length)];
clear = ct.TransformFinalBlock(cipher, 0, cipher.Length);
Console.WriteLine(" - clear: " + byte_array_to_hex_string(clear, true, true));
tdes.Clear();
tdes.Dispose();
ct.Dispose();
return clear;
}
public static byte[] hex_string_to_byte_array(string hex)
{
int number_chars = hex.Length;
byte[] bytes = new byte[number_chars / 2];
for (int i = 0; i < number_chars; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
public static string byte_array_to_hex_string(byte[] ba, bool remove_dashes, bool include_spaces)
{
string hex = BitConverter.ToString(ba);
string hex_dashes = "";
string hex_spaces = "";
if (remove_dashes)
{
hex_dashes = hex.Replace("-", "");
}
else
{
return hex;
}
int hex_char_count = 0;
if (include_spaces)
{
for (int i = (hex_dashes.Length - 1); i >= 0; i--)
{
if (hex_char_count == 0)
{
hex_spaces += hex_dashes[i];
hex_char_count++;
continue;
}
if (hex_char_count % 16 == 0)
{
hex_spaces = (hex_dashes[i] + " " + hex_spaces);
hex_char_count++;
continue;
}
hex_spaces = hex_dashes[i] + hex_spaces;
hex_char_count++;
}
return hex_spaces;
}
else
{
return hex_dashes;
}
}
}
}
Output
Entering decrypt_3des - cipher: C25C1D1197D31CAA 87285D59A8920474 26D9182EC11353C0 51ADD6D0F072A6CB 3436560B3071FC1F D11D9F7E74886742 D9BEE0C FD1EA1064 C213BB55278B2F12 - key: 27F66D5244FF621E AA6F6120EDEB427F - cipher length: 64 - key length: 16 - IV: 10F99E2232BEC7F4 - clear: BF3CBF923E6C0E19 DBBB64E0FCBBFBB9 4CA3D68F117BE51F 0C1294CEC85470B6 6A799CC5D914F427 30D0C47BC9E6340C BD8E4C2E 6E5819ED 23125066DBA83477 clear: BF3CBF923E6C0E19 DBBB64E0FCBBFBB9 4CA3D68F117BE51F 0C1294CEC85470B6 6A799CC5D914F427 30D0C47BC9E6340C BD8E4C2E6E5 819ED 23125066DBA83477 Entering decrypt_3des - cipher: C25C1D1197D31CAA 87285D59A8920474 26D9182EC11353C0 51ADD6D0F072A6CB 3436560B3071FC1F D11D9F7E74886742 D9BEE0C FD1EA1064 C213BB55278B2F12 - key: 27F66D5244FF621E AA6F6120EDEB427F - cipher length: 64 - key length: 16 - IV: 32B9C802333CFBB4 - clear: A878F23C3AF5E724 2ACB3458F8D68E68 9BE4F6C007F24FD2 9AF4CE98239A4F2C 5710633D88266AFD 77EF88A7B09485D9 F07C33D7 08832E90 77382A7A51A532C1 clear: A878F23C3AF5E724 2ACB3458F8D68E68 9BE4F6C007F24FD2 9AF4CE98239A4F2C 5710633D88266AFD 77EF88A7B09485D9 F07C33D7088 32E90 77382A7A51A532C1 Entering decrypt_3des - cipher: C25C1D1197D31CAA 87285D59A8920474 26D9182EC11353C0 51ADD6D0F072A6CB 3436560B3071FC1F D11D9F7E74886742 D9BEE0C FD1EA1064 C213BB55278B2F12 - key: 27F66D5244FF621E AA6F6120EDEB427F - cipher length: 64 - key length: 16 - IV: 2FCB1A9F5B502E1B - clear: 80B7302520ACA111 223BF99421EFDA2C 60CABCB3C632A61C 3422552A07B582B7 E50E44E38DEDC300 DD9A6EB5CF3C0A63 E512FAC0 C04D3EC5 EEC551C3E845BF80 clear: 80B7302520ACA111 223BF99421EFDA2C 60CABCB3C632A61C 3422552A07B582B7 E50E44E38DEDC300 DD9A6EB5CF3C0A63 E512FAC0C04 D3EC5 EEC551C3E845BF80 Entering decrypt_3des - cipher: C25C1D1197D31CAA 87285D59A8920474 26D9182EC11353C0 51ADD6D0F072A6CB 3436560B3071FC1F D11D9F7E74886742 D9BEE0C FD1EA1064 C213BB55278B2F12 - key: 27F66D5244FF621E AA6F6120EDEB427F - cipher length: 64 - key length: 16 - IV: 355096FE3CA1E9A1 - clear: E4EDCDC436745A51 42FE9D0C71933080 6DFCAACE07FBCC22 0F5C9AF9082C8264 870362982FF90CA4 665E312FC1A8D264 DDBB27B7 3B30840E 6030D791CD849A3D clear: E4EDCDC436745A51 42FE9D0C71933080 6DFCAACE07FBCC22 0F5C9AF9082C8264 870362982FF90CA4 665E312FC1A8D264 DDBB27B73B3 0840E 6030D791CD849A3D
Upvotes: 2
Views: 562
Reputation: 1904
Setting the key size results in a new random key being created. If you do this before you set the key and the IV, you will get consistent results.
Upvotes: 5
Reputation: 2761
I can't explain why, but replacing it with the code below worked. I'll dive into it more and if I figure it out I'll comment.
static byte[] decrypt_3des(byte[] cipher, byte[] key)
{
byte[] clear;
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = key;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.None;
tdes.IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
try
{
ICryptoTransform ic = tdes.CreateDecryptor();
clear = ic.TransformFinalBlock(cipher, 0, cipher.Length);
}
finally
{
tdes.Clear();
}
return clear;
}
Upvotes: 0