title: Decrypting an mcrypt file in .Net (C#)
body: <p>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 &amp; filename changed) and the keys are of varying length, anywhere from 14-18 characters.
  <br><code>mcrypt -a rijndael-256 fileToEncrypt.tar.gz -k 0123456789abcdef1</code></p>
  
  <p>So far, I have taken two approaches to this task. The first is to use mcrypt.exe and start the process using <code>Process</code>. 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.</p>
  
  <p>I'm a bit confused with the MCrypt format. I've reviewed the FORMAT doc in the source code (<a href="https://github.com/Distrotech/mcrypt/blob/master/doc/FORMAT" rel="nofollow noreferrer">here</a> 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.</p>
  
  <p>1) How large is the IV and how do I pass it into my decryptor?<br>
  2) How large is the checksum at the end of the file and do I need it?<br>
  3) Are the above static in length?<br>
  4) What is keymode (mcrypt-sha1) and how is it used?<br>
  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?<br></p>
  
  <p>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.</p>
  
  <p><a href="https://i.stack.imgur.com/gVC57.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/gVC57.png" alt="Sample mcrypt Rijndael-256 file"></a>
  <p></p>
  
  <pre><code>/// &lt;summary&gt;
  /// Decrypt an mcrypt file using rijndael-256
  /// &lt;/summary&gt;
  /// &lt;param name="inputFile"&gt;File to decrypt&lt;/param&gt;
  /// &lt;param name="encryptionKey"&gt;Password&lt;/param&gt;
  /// &lt;param name="purge"&gt;&lt;/param&gt;
  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 &amp;&amp; buffer[1] == 0x6D &amp;&amp; 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] &lt;&lt; 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;
  }
  </code></pre>
  
  <p><strong>Edit</strong><br>
  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...
  <p>
  Algorithm: rijndael-128<br>
  Keysize: 32<br>
  Mode: cbc<br>
  Keyword mode: mcrypt-sha1<br>
  File format: mcrypt
  <p>
  Also, "passwords" used to encrypt in various parts of the software range from 12 to 28 characters.</p>