none
Problem with 3DES Encryption

    Question

  • Hi,
        I've come across a problem with the TripleDESCryptoServiceProvider in .Net. I'm using the following code to encrypt / decrypt passwords.

    Encryption

    byte[] key = { 0xd5, 0xdf, 0xf8, 0xcb, 0xb3, 0xf8, 0xbc, 0x0e, 0x4c, 0xfd, 0x80, 0xd9, 0x3b, 0xf8, 0x6e, 0x92, 0xc1, 0x3e, 0xce, 0x7c, 0x32, 0x2a, 0xb9, 0x13 };
                byte[] iv = { 0x00, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00, 0x00 };

                byte[] data = System.Text.Encoding.UTF8.GetBytes("123456");

                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
                ICryptoTransform encryptor = tdes.CreateEncryptor(key, iv);

                MemoryStream memStream = new MemoryStream();
                CryptoStream incStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write);

                incStream.Write(data, 0, data.Length);
                incStream.FlushFinalBlock();

                byte[] encrypted = memStream.ToArray();

                incStream.Close();
                String cipher = Convert.ToBase64String(encrypted, 0, (int)encrypted.Length);

    Decryption

    byte[] key = { 0xd5, 0xdf, 0xf8, 0xcb, 0xb3, 0xf8, 0xbc, 0x0e, 0x4c, 0xfd, 0x80, 0xd9, 0x3b, 0xf8, 0x6e, 0x92, 0xc1, 0x3e, 0xce, 0x7c, 0x32, 0x2a, 0xb9, 0x13 };
                byte[] iv = { 0x00, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00, 0x00 };

                byte[] data = Convert.FromBase64String(cipher); // Cipher is the encrypted string...
                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

                ICryptoTransform decryptor = tdes.CreateDecryptor(key, iv);

                MemoryStream memStream = new MemoryStream(data);
                CryptoStream decStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Read);

                byte[] fromEncrypt = new byte[data.Length];
                decStream.Read(fromEncrypt, 0, fromEncrypt.Length);
                String password = Encoding.UTF8.GetString(fromEncrypt );

    Now, the problem is, I get some unwanted characters (\0) in the decrypted string. For example, if I encrypt "123456" and then decrypt the encrypted string, I get "12345\0\0" and not "123456". Why does this happen? How can I avoid this? Could somebody help me?

    Many Thanks
    Dileep Krishnan

    • Changed type SamAgain Wednesday, February 24, 2010 8:40 AM
    Tuesday, February 16, 2010 12:47 PM

Answers

  • Like i said before its caused by padding something all blockcyphers have to deal with. Looked some more into your problem and by the looks of it the padding is actually getting taken care of properly using PCKS#7 padding which is good, so if its not padding that causing you grief, why you get the 2 extra bytes?

    still padding! :) You allocated a buffer with the length of the encrypted data (which has the two extra for padding) then you called read to fill that buffer, since the cryptostream knows of the padding only 6 bytes are written in your 8 byte buffer hence the last two places in that array are \0's :)

                byte[] fromEncrypt = new byte[data.Length];
                int len = decStream.Read(fromEncrypt, 0, fromEncrypt.Length);
                String password = Encoding.UTF8.GetString(fromEncrypt,0,len);

    That ought to fix it.


    • Marked as answer by SamAgain Wednesday, February 24, 2010 8:41 AM
    Wednesday, February 17, 2010 2:40 PM

All replies

  • DES and therefore 3DES is a 64 bit block cypher and will always operate on 64 bits blocks, if your data is shorter it will be padded with 0's till it has a full 64 bit block.  I'm not sure why you are getting "12345\0\0"  though, running your code i couldn't reproduce that the 6 never went missing during my tests.

    What is usually done is store the length of the unencrypted data with the encrypted data so you know how many padding bytes there are.

    Tuesday, February 16, 2010 6:20 PM
  • Hi Ray,
              Thank you for your response. I'm sorry, but the decrypted string is "123456\0\0" and not "12345\0\0" - that was a typo. Then, I've got no way of knowing the length of the actual password because the encryption happens at client side and decryption on server side. If I want to pass the size of the password, I'll have to change a lot of code as this is common code used at various places. BTW, could you reproduce the "\0"s in the decrypted string? Is there any way I can prevent it from appearing in the decrypted string?

    Many Thanks
    Dileep Krishnan
    Tuesday, February 16, 2010 6:35 PM
  • Wait you're encrypting all passwords on the server with the same 3des key? The same key that is shared with *ALL* clients? wow... don't you see a problem with that? (Hint: its someone downloading a copy of your database and decrypting all passwords in one go) may I suggest storing a sha1 hash of the password on the server and comparing that to the hash the client sends?

    If you still want to go this way and are sure \0 will never be valid data just strip the \0's off the final string like this :

    String password = Encoding.UTF8.GetString(fromEncrypt).Replace("\0","");
    Tuesday, February 16, 2010 6:50 PM
  • Hi Ray,
              Thanks again for your message. However, things are not that dirty as you think. It might be helpful if I elaborate our system setup a little.
    What we have is a middle ware component that provides a connectivity infrastructure to external clients of our banking software system. it looks like this

    External Client <->Our Connectivity Stuff <-> Banking System Core

    Now, this connectivity system is implemented as a windows service with a winform administration module. The communication between service and admin client is implemented using remoting.

    The banking software has a provision to create / delete users. So, our winform client just accepts username / password from admin users, encrypts it using 3DES and sends this to the windows service. The service will decrypt it and sends it to the banking software. And that uses hashing for passwords. As you can see, the password is not stored anywhere in encrypted form here.

    So my question here is not how to remove this \0 from the decrypted string - its just a child's play. I want to know how I can prevent the decryption system from attaching \0 to the decrypted string. I tried AES and it doesn't happen there. Is this a limitation of the 3DES implementation in .Net ?

    Many Thanks
    Dileep Krishnan

    Wednesday, February 17, 2010 10:36 AM
  • Like i said before its caused by padding something all blockcyphers have to deal with. Looked some more into your problem and by the looks of it the padding is actually getting taken care of properly using PCKS#7 padding which is good, so if its not padding that causing you grief, why you get the 2 extra bytes?

    still padding! :) You allocated a buffer with the length of the encrypted data (which has the two extra for padding) then you called read to fill that buffer, since the cryptostream knows of the padding only 6 bytes are written in your 8 byte buffer hence the last two places in that array are \0's :)

                byte[] fromEncrypt = new byte[data.Length];
                int len = decStream.Read(fromEncrypt, 0, fromEncrypt.Length);
                String password = Encoding.UTF8.GetString(fromEncrypt,0,len);

    That ought to fix it.


    • Marked as answer by SamAgain Wednesday, February 24, 2010 8:41 AM
    Wednesday, February 17, 2010 2:40 PM
  • Hi Ray,
                Finally, you have come up with what I was looking for - the real solution. Thanks a lot.


    Dileep Krishnan
    Wednesday, February 17, 2010 7:55 PM