I have the following scenario:
Algorithm: 3DES
Cipher Mode: CBC
Key Size: 128-bit
Block Size: 64 bit
IV: 0x0000000000000000 (an eight byte array of zeros)
The results I get using .NET with the following routine are:
1. EncryptUnicode: takes a unicode string in plain text and encrypts
it, returning a unicode string.
2. DecryptUnicode: takes an encrypted unicode string as input and
decrypts it, returning plain text in a unicode string.
3. EncryptBase64: takes a plain text in a unicode string and encrypts
it, returning a base64 string.
4. DecryptBase64: takes a base64 string and decrypts it, returns a
plain text unicode string.
The source code of the routines is given at the end of this post.
I am using C#. When I take a plain text such as "hello" and put it
through EncryptBase64, and then put the output of EncryptBase64 to
DecryptBase64, I get back the plain old string, for e.g "hello". So,
this part works ok.
However, when I do the same with the other set of methods, i.e
EncryptUnicode and DecryptUnicode, it doesn't work. The call to
EncryptUnicode gives me back some string. I assume that that is the
encrypted string. When I call DecryptUnicode with that string, it gives
me an exception saying, "Bad Data".
So, my first question is:
1. Does 3DES encryption in CBC mode work only in base64 strings?
You might say, "One set of your methods work, i.e the base64 ones. Why
are you being so pedantic. Just go ahead and use them. If it works,
it's good." And you'd be right in saying that. Only, my problem is that
some other machine on another platform, probably, is going to do the
encryption using 3DES in CBC (we'll share a IV and key), and I'll have
to decrypt them.
2. My second question pertains to the length of the output in
encryption process. I found that if I give a string of a length that is
less than a multiple of eight, the encrypted output is of the length
that is equal to the the nearest, higher multiple of eight. This is
true for the EncryptUnicode method. I understand that this might be
because 3DES works with a block size of 64-bits, i.e works with data in
chunks of 64-bits, and thus because of chaining the block (the
initialization vector), it might require some memory.
We'll get to the base64 methods later. So, my second question is:
Is it not possible to give 3DES in CBC mode a X length string, where X
is a multiple of 8, and get back exactly X bytes in the output as the
encrypted string? My finding says it is NOT possible. However, the
other end which, in my case, is going to do the encryption says that,
for example, a 16-byte plain text that they are encrypting will spit
out a 16-byte encrypted output.
Here're my findings (please ignore the base64 part for now). Look at
the plain text length and the encrypted text lengths in the case of
Unicode string encryption.
Enter plain text string [Press Q to quit]: a
______________________________________________
Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------
Unicode 1 8
Base64 1 12
--------------------------------------------------------------------------------
Enter plain text string [Press Q to quit]: ab
______________________________________________
Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------
Unicode 2 8
Base64 2 12
--------------------------------------------------------------------------------
Enter plain text string [Press Q to quit]: abcdefg
_____________________________________________
Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------
Unicode 7 8
Base64 7 12
--------------------------------------------------------------------------------
Enter plain text string [Press Q to quit]: abcdefgh
______________________________________________
Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------
Unicode 8 16
Base64 8 24
--------------------------------------------------------------------------------
Enter plain text string [Press Q to quit]: abcdefghijklmno
______________________________________________
Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------
Unicode 15 16
Base64 15 24
--------------------------------------------------------------------------------
Enter plain text string [Press Q to quit]: abcdefghijklmnop
______________________________________________
Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------
Unicode 16 24
Base64 16 32
--------------------------------------------------------------------------------
CODE FOR THE FOUR METHODS MENTIONED ABOVE
public static string EncryptUnicode(string s, byte[] bkey, byte[] bIV)
{
ASCIIEncoding asc = new ASCIIEncoding();
TripleDESCryptoServiceProvider p = new
TripleDESCryptoServiceProvider();
p.Mode = CipherMode.CBC;
p.Key = bkey;
p.IV = bIV;
ICryptoTransform c = p.CreateEncryptor();
byte[] bClear = asc.GetBytes(s);
byte[] bEncrypted = c.TransformFinalBlock(bClear, 0, bClear.Length);
return asc.GetString(bEncrypted);
}
public static string DecryptUnicode(string s, byte[] bkey, byte[]
bIV)
{
ASCIIEncoding asc = new ASCIIEncoding();
TripleDESCryptoServiceProvider p = new
TripleDESCryptoServiceProvider();
p.Mode = CipherMode.CBC;
p.Key = bkey;
p.IV = bIV;
ICryptoTransform c = p.CreateDecryptor();
byte[] bEncrypted = asc.GetBytes(s);
byte[] bClear = c.TransformFinalBlock(bEncrypted, 0,
bEncrypted.Length);
return asc.GetString(bClear);
}
public static string EncryptBase64(string s, byte[] bkey, byte[] bIV)
{
ASCIIEncoding asc = new ASCIIEncoding();
TripleDESCryptoServiceProvider p = new
TripleDESCryptoServiceProvider();
p.Mode = CipherMode.CBC;
p.Key = bkey;
p.IV = bIV;
ICryptoTransform c = p.CreateEncryptor();
byte[] bClear = asc.GetBytes(s);
byte[] bEncrypted = c.TransformFinalBlock(bClear, 0, bClear.Length);
return Convert.ToBase64String(bEncrypted);
}
public static string DecryptBase64(string s, byte[] bkey, byte[] bIV)
{
ASCIIEncoding asc = new ASCIIEncoding();
TripleDESCryptoServiceProvider p = new
TripleDESCryptoServiceProvider();
p.Mode = CipherMode.CBC;
p.Key = bkey;
p.IV = bIV;
ICryptoTransform c = p.CreateDecryptor();
byte[] bEncrypted = Convert.FromBase64String(s);
byte[] bClear = c.TransformFinalBlock(bEncrypted, 0,
bEncrypted.Length);
return asc.GetString(bClear);
}