I've been a C# hobbyist for some time now and would consider having intermediate development skills, but little to no encryption knowledge. As part of a side project I need to decrypt files that were encrypted using MCrypt. It doesn't seem any special arguments were passed into the command. For example, this is quite common (key & filename changed) and the keys are of varying length, anywhere from 14-18 characters.
mcrypt -a rijndael-256 fileToEncrypt.tar.gz -k 0123456789abcdef1
So far, I have taken two approaches to this task. The first is to use mcrypt.exe and start the process using Process
. However, I feel that makes the code (and program flow) very clunky. The second is to try to directly decrypt the file from my within program and have zero external program dependencies; I'd like to go this route.
I'm a bit confused with the MCrypt format. I've reviewed the FORMAT doc in the source code (here to view online) and I believe I have the beginning portion of the header taken care of properly. I cannot, however, seem to decrypt the encrypted data within the file.
1) How large is the IV and how do I pass it into my decryptor?
2) How large is the checksum at the end of the file and do I need it?
3) Are the above static in length?
4) What is keymode (mcrypt-sha1) and how is it used?
5) I notice that when properly decrypting (using mcrypt.exe) that there is a 140 byte difference between the encrypted and decrypted file. What makes up these 140 bytes?
Code and the beginning of the encrypted file below; no doubt my code is wrong starting with the comment "Get the data" Any pointers in the right direction would be greatly appreciated.
/// <summary>
/// Decrypt an mcrypt file using rijndael-256
/// </summary>
/// <param name="inputFile">File to decrypt</param>
/// <param name="encryptionKey">Password</param>
/// <param name="purge"></param>
public static bool Decrypt (string inputFile, string encryptionKey)
{
var rv = false;
if (File.Exists(inputFile) == true)
{
using (FileStream stream = new FileStream(inputFile, FileMode.Open))
{
var buffer = new byte[1024];
// MCrypt header
stream.Read(buffer, 0, 3);
if (buffer[0] == 0x00 && buffer[1] == 0x6D && buffer[2] == 0x03)
{
// Flag
// Bit 7 - Salt Used
// Bit 8 - IV not used
var flag = (byte)stream.ReadByte();
byte[] saltVal = null;
var saltUsed = Utils.GetBit(flag, 6);
byte[] ivVal = new byte[16];
var ivUsed = (Utils.GetBit(flag, 7) == false);
var algorithmName = Utils.GetNullTerminatedString(stream);
stream.Read(buffer, 0, 2);
var keyLen = (buffer[1] << 8) + buffer[0];
var algorithModeName = Utils.GetNullTerminatedString(stream);
var keygenName = Utils.GetNullTerminatedString(stream);
if (saltUsed)
{
var saltFlag = (byte)stream.ReadByte();
if (Utils.GetBit(saltFlag, 0))
{
// After clearing the first bit the salt flag is now the length
Utils.ClearBit (ref saltFlag, 0);
saltVal = new byte[saltFlag];
stream.Read(saltVal, 0, saltFlag);
}
}
var algorithmModeName = Utils.GetNullTerminatedString(stream);
if (ivUsed)
{
stream.Read(ivVal, 0, ivVal.Length);
}
// Get the data - how much to get???
buffer = new byte[stream.Length - stream.Position + 1];
var bytesRead = stream.Read(buffer, 0, buffer.Length);
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged rijndael = new RijndaelManaged())
{
rijndael.KeySize = 256;
rijndael.BlockSize = 128;
var key = new Rfc2898DeriveBytes(System.Text.Encoding.ASCII.GetBytes(encryptionKey), saltVal, 1000);
rijndael.Key = key.GetBytes(rijndael.KeySize / 8);
//AES.Key = System.Text.Encoding.ASCII.GetBytes(encryptionKey);
//AES.IV = key.GetBytes(AES.BlockSize / 8);
rijndael.IV = ivVal;
rijndael.Mode = CipherMode.CBC;
rijndael.Padding = PaddingMode.None;
using (var cs = new CryptoStream(ms, rijndael.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
cs.Close();
using (FileStream fs = new FileStream(inputFile + Consts.FILE_EXT, FileMode.Create))
{
byte[] decryptedBytes = ms.ToArray();
fs.Write(decryptedBytes, 0, decryptedBytes.Length);
fs.Close();
rv = true;
}
}
}
}
}
}
}
return rv;
}
Edit
I receive the following when turning on its verbose mode and without specifying rijndael-256. When I do specify the algorithm it does reflect that in the verbose output; both decrypt the file properly. The plot thickens...
Algorithm: rijndael-128
Keysize: 32
Mode: cbc
Keyword mode: mcrypt-sha1
File format: mcrypt
Also, "passwords" used to encrypt in various parts of the software range from 12 to 28 characters.