How can I decrypt a file in C# which has been encrypted by des.exe?

妖精的绣舞 提交于 2020-02-20 09:29:09

问题


I have a file which has been encrypted by des.exe.

A file can be encrypted and decrypted using the following commands:

des -E -k "foo" sample.txt sample.txt.enc
des -D -k "foo" sample.txt.enc sample.txt.dec

I have attempted to decrypt using the following:

public byte[] Decrypt(FileInfo file, string key)
{
  byte[] keyAsBytes = LibDesPasswordConvertor.PasswordToKey(key);
  byte[] initializationVector = keyAsBytes;

  var cryptoProvider = new DESCryptoServiceProvider();  
  cryptoProvider.Mode = CipherMode.CBC;
  cryptoProvider.Padding = PaddingMode.None;  

  using (FileStream fs = file.OpenRead())
  using (var memStream = new MemoryStream())
  using (var decryptor = cryptoProvider.CreateDecryptor(keyAsBytes, initializationVector))
  using (var cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Write))
  {
    fs.CopyTo(cryptoStream);
    fs.Flush();
    cryptoStream.FlushFinalBlock();

    return memStream.ToArray();
  }
}

public static class LibDesPasswordConvertor
{
  public static byte[] PasswordToKey(string password)
  {
    if (string.IsNullOrWhiteSpace(password)) 
    { 
      throw new ArgumentException("password"); 
    }

    var key = new byte[8];

    for (int i = 0; i < password.Length; i++)
    {
      var c = (int)password[i];
      if ((i % 16) < 8)
      {
        key[i % 8] ^= (byte)(c << 1);
      }
      else
      {
        // reverse bits e.g. 11010010 -> 01001011
        c = (((c << 4) & 0xf0) | ((c >> 4) & 0x0f));
        c = (((c << 2) & 0xcc) | ((c >> 2) & 0x33));
        c = (((c << 1) & 0xaa) | ((c >> 1) & 0x55));
        key[7 - (i % 8)] ^= (byte)c;
      }
    }

    AddOddParity(key);

    var target = new byte[8];
    var passwordBuffer = Encoding.ASCII.GetBytes(password).Concat(new byte[8]).Take(password.Length + (8 - (password.Length % 8)) % 8).ToArray();

    using(var des = DES.Create())
    using(var encryptor = des.CreateEncryptor(key, key))
    {
      for (int x = 0; x < passwordBuffer.Length / 8; ++x)
      {
        encryptor.TransformBlock(passwordBuffer, 8 * x, 8, target, 0);
      }
    }

    AddOddParity(target);

    return target;
  }


  private static void AddOddParity(byte[] buffer)
  {
    for (int i = 0; i < buffer.Length; ++i)
    {
      buffer[i] = _oddParityTable[buffer[i]];
    }
  }

  private static byte[] _oddParityTable = {
          1,  1,  2,  2,  4,  4,  7,  7,  8,  8, 11, 11, 13, 13, 14, 14,
         16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
         32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
         49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
         64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
         81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
         97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
        112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
        128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
        145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
        161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
        176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
        193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
        208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
        224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
        241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254};
}

But when I execute:

const string KEY = "foo";  
var utf8Bytes = Decrypt(new FileInfo(@"PATH-TO\sample.txt.enc"), KEY);

I get:

�1D���z+�a Sample.y���0F�01

Original text:

This is a Sample.

Encrypted:

ñGYjl¦ûg†¼64©‹Bø
é¯Kœ|

回答1:


To my surprise you've already derived the key correctly. That was the meat of the problem, so Kudos for solving that part already. That the key is correct becomes clear when you see that part of the plaintext is present in the decryption - it wouldn't if the key was wrong.


Looking into the source and some docs from times past, I found a likely IV of all zeros instead of reusing the key bytes (both are very wrong, in cryptographic terms).

Furthermore, as always for SSLeay, the ECB and CBC modes use PKCS#7 compatible padding, rather than no padding.


Finally, FlushFinalBlock will be automatically called if you close the stream, e.g. by exiting the try-with-resources. So if you get the array afterwards then you should get the right values - after you unpad correctly, of course. If you call Flush then FlushFinalBlock will already be called, and calling it twice will make a mess out of things.

Simply removing the flush calls and retrieving the array after the CryptoStream is closed is the way to go.


Both DES and the key derivation (des_string_to_key and des_string_to_2keys) that Young copied from MIT are completely insecure. Using an all zero IV is wrong.

If you use this as transport mode than padding oracles will apply, and decryption is not even necessary for an attacker. The ciphertext is not integrity protected.

If you use the above routines to keep anything confidential or secure you're fooling yourself. This is 80s technology, and I think that real cryptographers wouldn't find it secure back then either.

Basically if your attacker is over 8 years old, you're in trouble.



来源:https://stackoverflow.com/questions/59770092/how-can-i-decrypt-a-file-in-c-sharp-which-has-been-encrypted-by-des-exe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!