TripleDES String Encrypt/Decrypt Problem

W

wkodie

I'm having trouble encrypting/decrypting a simple string using the
System.Security.Cryptography.TripleDESCryptoServiceProvider, etc...

The encryption works, but the decryption does not properly decrypt
several of the first few characters.

Here's the code:

class TMyCipher
{
public string Encipher(string s, string key)
{
byte[] bKey = ASCIIEncoding.ASCII.GetBytes(key);
TripleDESCryptoServiceProvider TripleDesProv = new
TripleDESCryptoServiceProvider();
ICryptoTransform ict = TripleDesProv.CreateEncryptor(bKey,null);
byte[] bInput = ASCIIEncoding.ASCII.GetBytes(s);
byte[] bOutput = ict.TransformFinalBlock(bInput,0,bInput.Length);
System.Console.WriteLine(System.Text.Encoding.ASCII.GetString(bOutput,0,bOutput.Length));
return Convert.ToBase64String(bOutput,0,bOutput.Length);
}

public string Decipher(string ciphertext, string key)
{
byte[] bKey = ASCIIEncoding.ASCII.GetBytes(key);
TripleDESCryptoServiceProvider TripleDesDec = new
TripleDESCryptoServiceProvider();
ICryptoTransform Decryptor =
TripleDesDec.CreateDecryptor(bKey,null);
System.Console.WriteLine(ciphertext);
byte[] eInput = Convert.FromBase64String(ciphertext);
System.Console.WriteLine(System.Text.Encoding.ASCII.GetString(eInput,0,eInput.Length));
byte[] eOutput =
Decryptor.TransformFinalBlock(eInput,0,eInput.Length);
return System.Text.Encoding.ASCII.GetString(eOutput,0,eOutput.Length);
}
}

string ClearText = "test-text-this-is-text";

string sKey = "mykey";
sKey = sKey.PadRight(16,' ');
byte[] bKey = ASCIIEncoding.ASCII.GetBytes(sKey);
TMyCipher ciph = new TMyCipher();
string ciphertext = ciph.Encipher(ClearText,sKey);
System.Console.WriteLine(ciphertext);
string sFinal = ciph.Decipher(ciphertext,sKey);
System.Console.WriteLine("Final: " + sFinal);

The result:

G)♠SK|YS
M♀H)←b ↨\
r2xpLw1HKQbTy/zZh9MKTYzIqZtiCRfc
r2xpLw1HKQbTy/zZh9MKTYzIqZtiCRfc
G)♠SK|YS
M♀H)←b ↨\
Final: I'|htT<#t-this-is-text
 
J

Jon Skeet [C# MVP]

I'm having trouble encrypting/decrypting a simple string using the
System.Security.Cryptography.TripleDESCryptoServiceProvider, etc...

The encryption works, but the decryption does not properly decrypt
several of the first few characters.

<snip>

The problem is that you're giving it more in one block than you should.
Rather than using the transform directly, I suggest you use the
CryptoStream API. That way you don't need to worry about block sizes
etc.
 
W

wkodie

Jon Skeet said:
The problem is that you're giving it more in one block than you should.
Rather than using the transform directly, I suggest you use the
CryptoStream API. That way you don't need to worry about block sizes
etc.

Thank you. I've tried to implement a simple solution using a
CryptoStream, but the results are almost identical:

My ClearText String
d!U#CP↕?¶XgN☼q%♀U∟o→
ZCHVo3eIQ1AShz+UWOdOj3GlDNUcbxoN <-- After Base64 Encoding.

d!U#CP↕?¶XgN☼q%♀U∟o→
My CleSRText String <-- After Decryption.
^^^^

Here's my code. Thanks again.

static void Main(string[] args)
{
string sKey = "MyKey";
sKey = sKey.PadRight(16,' ');
string sInit = "123456";
byte[] bInit = ASCIIEncoding.ASCII.GetBytes(sInit);
string ClearText = "My ClearText String";
byte[] bKey = ASCIIEncoding.ASCII.GetBytes(sKey);
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(ClearText);
byte[] rbuffer;

System.Console.WriteLine(ClearText);
TripleDESCryptoServiceProvider tdc = new
TripleDESCryptoServiceProvider();
ICryptoTransform icp = tdc.CreateEncryptor(bKey,bInit);
System.IO.MemoryStream ms = new System.IO.MemoryStream();
CryptoStream cs = new CryptoStream(ms,icp,CryptoStreamMode.Write);
cs.Write(buffer,0,buffer.Length);
cs.Close();
rbuffer = ms.ToArray();
System.Console.WriteLine(ASCIIEncoding.ASCII.GetString(rbuffer,0,rbuffer.Length));
string CipherText =
Convert.ToBase64String(rbuffer,0,rbuffer.Length);
System.Console.WriteLine(CipherText);
System.Console.ReadLine();
ms.Close();

System.IO.MemoryStream dms = new System.IO.MemoryStream();
byte[] eBuffer = Convert.FromBase64String(CipherText);
System.Console.WriteLine(ASCIIEncoding.ASCII.GetString(eBuffer,0,eBuffer.Length));
dms.Write(eBuffer,0,eBuffer.Length);
dms.Seek(0,System.IO.SeekOrigin.Begin);

TripleDESCryptoServiceProvider ddc = new
TripleDESCryptoServiceProvider();
ICryptoTransform dcp = ddc.CreateDecryptor(bKey,bInit);
CryptoStream dcs = new CryptoStream(dms,dcp,CryptoStreamMode.Read);
byte[] cBuffer = new byte[eBuffer.Length];
dcs.Read(cBuffer,0,eBuffer.Length);
dcs.Close();
System.Console.WriteLine(ASCIIEncoding.ASCII.GetString(cBuffer,0,cBuffer.Length));
dms.Close();
string cText = ASCIIEncoding.ASCII.GetString(cBuffer,0,cBuffer.Length);
System.Console.WriteLine(cText);
System.Console.ReadLine();
}
 
B

Bret Mulvey

You need to use CryptoStream.FlushFinalBlock before closing the crypto
stream after writing.

Jon Skeet [C# MVP] <[email protected]> wrote in message
The problem is that you're giving it more in one block than you should.
Rather than using the transform directly, I suggest you use the
CryptoStream API. That way you don't need to worry about block sizes
etc.

Thank you. I've tried to implement a simple solution using a
CryptoStream, but the results are almost identical:

My ClearText String
d!U#CP↕?¶XgN☼q%♀U∟o→
ZCHVo3eIQ1AShz+UWOdOj3GlDNUcbxoN <-- After Base64 Encoding.

d!U#CP↕?¶XgN☼q%♀U∟o→
My CleSRText String <-- After Decryption.
^^^^

Here's my code. Thanks again.

static void Main(string[] args)
{
string sKey = "MyKey";
sKey = sKey.PadRight(16,' ');
string sInit = "123456";
byte[] bInit = ASCIIEncoding.ASCII.GetBytes(sInit);
string ClearText = "My ClearText String";
byte[] bKey = ASCIIEncoding.ASCII.GetBytes(sKey);
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(ClearText);
byte[] rbuffer;

System.Console.WriteLine(ClearText);
TripleDESCryptoServiceProvider tdc = new
TripleDESCryptoServiceProvider();
ICryptoTransform icp = tdc.CreateEncryptor(bKey,bInit);
System.IO.MemoryStream ms = new System.IO.MemoryStream();
CryptoStream cs = new CryptoStream(ms,icp,CryptoStreamMode.Write);
cs.Write(buffer,0,buffer.Length);
cs.Close();
rbuffer = ms.ToArray();
System.Console.WriteLine(ASCIIEncoding.ASCII.GetString(rbuffer,0,rbuffer.Len
gth));
string CipherText =
Convert.ToBase64String(rbuffer,0,rbuffer.Length);
System.Console.WriteLine(CipherText);
System.Console.ReadLine();
ms.Close();

System.IO.MemoryStream dms = new System.IO.MemoryStream();
byte[] eBuffer = Convert.FromBase64String(CipherText);
System.Console.WriteLine(ASCIIEncoding.ASCII.GetString(eBuffer,0,eBuffer.Len
gth));
dms.Write(eBuffer,0,eBuffer.Length);
dms.Seek(0,System.IO.SeekOrigin.Begin);

TripleDESCryptoServiceProvider ddc = new
TripleDESCryptoServiceProvider();
ICryptoTransform dcp = ddc.CreateDecryptor(bKey,bInit);
CryptoStream dcs = new CryptoStream(dms,dcp,CryptoStreamMode.Read);
byte[] cBuffer = new byte[eBuffer.Length];
dcs.Read(cBuffer,0,eBuffer.Length);
dcs.Close();
System.Console.WriteLine(ASCIIEncoding.ASCII.GetString(cBuffer,0,cBuffer.Len
gth));
dms.Close();
string cText = ASCIIEncoding.ASCII.GetString(cBuffer,0,cBuffer.Length);
System.Console.WriteLine(cText);
System.Console.ReadLine();
}
 
J

Jon Skeet [C# MVP]

Thank you. I've tried to implement a simple solution using a
CryptoStream, but the results are almost identical:

Well, one of the problems which may well be everything in fact is that
you're using Stream.Read and assuming that it will completely fill the
buffer you've specified. You're also assuming that the encrypted length
is the same as the unencrypted length.

Rather than using CryptoStreamMode.Read, I find it easier to use
CryptoStreamMode.Write again, and write into a memory stream.

Here's some working code I wrote for someone else the other day - it
uses a straight DESCryptoServiceProvider, but I'm sure you can change
that part.

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

public class Working
{
DESCryptoServiceProvider provider;

Working()
{
provider = new DESCryptoServiceProvider();
provider.GenerateKey();
provider.GenerateIV();
}

byte[] Encrypt (string text)
{
byte[] encodedText = Encoding.UTF8.GetBytes (text);
ICryptoTransform transform = provider.CreateEncryptor();

using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream
(ms, transform, CryptoStreamMode.Write))
{
cs.Write (encodedText, 0, encodedText.Length);
cs.FlushFinalBlock();
}

return ms.ToArray();
}
}

string Decrypt (byte[] data)
{
ICryptoTransform transform = provider.CreateDecryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream (ms,
transform, CryptoStreamMode.Write))
{
cs.Write (data, 0, data.Length);
cs.FlushFinalBlock();
}
return Encoding.UTF8.GetString (ms.ToArray());
}
}

static void Main()
{
Working w = new Working();

Console.WriteLine (w.Decrypt(w.Encrypt
("Some text to be encrypted and then decrypted")));
}
}
 
J

Jon Skeet [C# MVP]

Bret Mulvey said:
You need to use CryptoStream.FlushFinalBlock before closing the crypto
stream after writing.

Actually, if you close a CryptoStream without flushing the final block,
it'll flush it for you. Not a bad idea to make it clear though.
 
B

Bret Mulvey

Jon Skeet said:
Actually, if you close a CryptoStream without flushing the final block,
it'll flush it for you. Not a bad idea to make it clear though.

That's what I thought, but I tried his example and reproduced problem. Using
Close without FlushFinalBlock corrupted the data, and with FlushFinalBlock
before Close it resolved the problem. I was able to see this in his code as
well as a separate example I created.

In a decompilation of CryptoStream.Close I can clearly see where it calls
FlushFinalBlock, but only conditionally. Perhaps the TripleDES encryptor
doesn't correctly set this flag.
 
B

Bret Mulvey

Bret Mulvey said:
That's what I thought, but I tried his example and reproduced problem. Using
Close without FlushFinalBlock corrupted the data, and with FlushFinalBlock
before Close it resolved the problem. I was able to see this in his code as
well as a separate example I created.

In a decompilation of CryptoStream.Close I can clearly see where it calls
FlushFinalBlock, but only conditionally. Perhaps the TripleDES encryptor
doesn't correctly set this flag.

Never mind, I can't back this up. I just re-ran my test that illustrated
this and it's not behaving the same so I must have made some other change.
It does look like the problem is on the reading side.
 
S

sasha bond

your problem is that you do not provide "good" IV in

TripleDesProv.CreateEncryptor(bKey, null);

if you put the same array for encr and decr, it works fine:


private byte[] bIV;
...
bIV = str2bytes("tXesterX");

// and use

ict = tProvider.CreateEncryptor(bKey, bIV);

// and

ict = tProvider.CreateDecryptor(bKey, bIV);
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top