4

I am trying to decrypt (using the DES algorithm) data that comes from a third party in C# code. There are plenty of examples here and elsewhere that have helped me out. The problem is that I don't know what to use for the 'initialization vector'.

The third party supplied a tool for use on the command line (DES.EXE, which I believe is an out-of-the-box build of the libdes library v4.01) which only requires that you supply an encryption key. So, I can decrypt fine with the tool. However, I would rather not spawn a process to run the tool from my code.

My question is how do I generate/find that initialization vector. I am 99.9% sure it can be done from looking at other posts but I can't figure it out. I talked to the third party and they said they do not support that approach. Any help would be greatly appreciated.

Mark M
  • 1,807
  • 2
  • 21
  • 40
  • In response to the answer below, the initialization vector is required. I believe the third party uses CBC mode. – Mark M Oct 24 '09 at 13:37
  • Well as I said IF they are using an IV they have to give it to you, because you can't guess it. – svens Oct 24 '09 at 21:26
  • Somehow it is not required for the command line tool that they provide des.exe. You just enter the key. That's why I believe it is derivable. Thanks for the input. – Mark M Oct 24 '09 at 23:24
  • Did you solve this? I have the exact same problem. – Hallis Nov 07 '12 at 13:52
  • I have the solution in Java and C#. If someone need it send a message here and I will post an answer. – Dmitry Kazakov Nov 28 '16 at 12:30

2 Answers2

2

I spent some time (~2 days) to sort out the same issue. "Rewriting" des.exe into C#. In the end, I got the libdes sources and reverse-engineered the logic.

The initialization vector is all (8) zeros. I.e. new byte[8] should do.

However, the trick may be how to convert a string password into 8 bytes long key. If you're using simple DES (not Triple DES), this code may do the trick for you:

public class LibDesPasswordConvertor
{
    public byte[] PasswordToKey(string password)
    {
        if (password == null)
            throw new ArgumentNullException("password");
        if (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();

        var des = DES.Create();
        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 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};
}

(Some pieces of libdes code reused, though I had to figure out the DES "checksum" part myself).

The last pitfall is, that libdes uses non-standard padding mechanism. It's almost like the ISO one, but the last byte is not the number of added bytes but 8 - this number. I set the Padding property to None and handled the padding myself.

Elephantik
  • 1,998
  • 1
  • 17
  • 23
0

See http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation for a description of the different modes of operation used in block ciphers. You don't need an initialization vector (IV) if you're using ECB mode which is just encrypting the message block by block. Maybe you have to check your DES library for setting ECB mode / disabling others.

If they aren't using ECB mode you need the IV, you can't "guess" it. The IV in most cases doesn't has to be secret, so it's not a problem to move it around.

svens
  • 11,438
  • 6
  • 36
  • 55