I have a set of encrypted documents encoded with TripleDES coming from a remote system. I need to decode the data in C# and I have no control over the key or encoding algorithm. All I have is the key and the mode (CBC) and the data located in a file.
The TripleDESCryptoServiceProvider is easy enough to use, but I can't figure out how to use the Decryptor without an Initialization Vector.
We have a have 24 byte (192bit) key to decrypt with, but nothing else.
string key = "1468697320656E6372797174696F6E206973737265206933";
byte[] keyData = ParseHex(key); // key is OK at 24 bytes
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
des.Mode = CipherMode.CBC;
des.GenerateIV();
var decryptor = des.CreateDecryptor(keyData,null); // des.IV
var encoded = File.ReadAllBytes(@"..\..\..\..\test.tdes");
byte[] output = decryptor.TransformFinalBlock(encoded, 0, encoded.Length);
This fails outright with Bad data. If I switch to TransformBlock the code at least runs but produces just gibberish:
byte[] output = new byte[10000];
var count = decryptor.TransformBlock(encoded, 0, encoded.Length, output, 0);
So the questions are:
- If I only have a key is the InitializationVector required?
- If not is null the right thing to pass?
- What else would I possibly need to set beyond the key and mode?
- Why does TransformBlock at least work and TransformFinalBlock just fails?
Update - found the problem
It turns out the decoding problem was caused, not by the missing Initialization Vector, but by incorrect information from the provider of the encrypted data. The updated working code looks like this:
// Read the test data
byte[] encoded = File.ReadAllBytes(@"..\..\..\..\test.tdes");
// Get the key into a byte array
string key = "1468697320656E6372797174696F6E206973737265206933";
byte[] keyData = ParseHex(key);
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
des.Mode = CipherMode.ECB; // Make sure this is correct!!!
des.Padding = PaddingMode.Zeros; // Make sure this is correct!!!
des.Key = keyData;
var decryptor = des.CreateDecryptor();
byte[] output = decryptor.TransformFinalBlock(encoded, 0, encoded.Length);
string dataString = Encoding.Default.GetString(encoded);
Console.WriteLine(dataString);
Console.WriteLine("\r\n\r\nDecoded:");
string result = Encoding.Default.GetString(output);
Console.WriteLine(result);
Console.Read();
The key in our case was using the proper CipherMode and Padding. Fixing the padding made TransformFinalBlock() work without Bad Data errors. Fixing the CipherMode made properly unencrypted the data.
Moral of the story: In CipherMode.ECB mode at least an Initialization Vector you don't need to provide an initialization vector. If no IV is provided the provider will auto-generate one, but the decryption still works (at least with ECB).
In the end it's CRUCIAL to make sure you have all the information from the provider that encrypted the data.
Trying to answer each point:
- The Initialization Vector is required in CBC mode. It is not required to be a secret (unlike the key) so it should be sent from the remote system.
- Since you need the IV, null is not the right thing to pass.
- Padding mode. You need to know which padding mode is used.
- TransformFinalBlock probably fails because the Padding mode is wrong.
Edit
The difference between ECB (Electronic Code Book) and CBC (Cipher Block Chaining) is illustrated below:
As you can see no IV is used in ECB mode. So even if you provide one it will be ignored.
来源:https://stackoverflow.com/questions/12522191/c-sharp-tripledes-provider-without-an-initialization-vector