Question

I am facing with problem when decrypting data with usage of TripleDESCryptoServiceProvider. The problem is that decrypted value contains beside of original value some additional, strange characters at the end

Per instance if I provide "rastko" to be encrypted, I will get later with decryption something like this "rastko⥊㮶". For other values it could be different number of 'dummy' characters or in some cases I will get exact value.

Then, I saw that for all encrypted data byte array size is divisible by 8. It looks like any provided data is rounded on value that is divisible by 8. Only in case when original encoded value is divisible by 8, decryption will retrieve appropriate value.

Here are methods that I am using :

        public static byte[] EncryptPassword(string password, out byte[] cryptoKey, out byte[] cryptoIV)
    {
        try
        {
            UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
            byte[] unicodePassword = unicodeEncoding.GetBytes(password);
            byte[] encryptedPassword;

            using (TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider())
            {
                tripleDes.Key = GetCryptoKey();
                tripleDes.Mode = CipherMode.CBC;
                tripleDes.Padding = PaddingMode.PKCS7;
                cryptoKey = tripleDes.Key;
                cryptoIV = tripleDes.IV;

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    ICryptoTransform cryptoTransform = tripleDes.CreateEncryptor();

                    using (
                        CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(unicodePassword, 0, unicodePassword.Length);
                        ////cryptoStream.FlushFinalBlock();
                    }

                    encryptedPassword = memoryStream.ToArray();
                }
            }

            return encryptedPassword;
        }
        catch (Exception ex)
        {
            throw new Exception("Password encryption failed !", ex);
        }
    }

    public static string DecryptPassword(byte[] encryptedPassword, byte[] cryptoKey, byte[] cryptoIV)
    {
        try
        {
            UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
            string readablePassword;

            using (TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider())
            {
                tripleDes.Key = cryptoKey;
                tripleDes.IV = cryptoIV;
                tripleDes.Mode = CipherMode.CBC;
                tripleDes.Padding = PaddingMode.PKCS7;

                // Create a new MemoryStream using the passed 
                // array of encrypted data.
                using (MemoryStream memoryStream = new MemoryStream(encryptedPassword))
                {
                    // Create crypto transform that defines the basic operations of cryptographic transformations.
                    ICryptoTransform cryptoTransform = tripleDes.CreateDecryptor();

                    // Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
                    using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
                    {
                        decryptoStream.Write(encryptedPassword, 0, encryptedPassword.Length);
                        ///decryptoStream.FlushFinalBlock();
                    }

                    byte[] decryptedPassword = memoryStream.ToArray();

                    //Convert the buffer into a string and return it.
                    readablePassword = unicodeEncoding.GetString(decryptedPassword, 0, decryptedPassword.Length);
                }
            }

            return readablePassword;
        }
        catch (Exception ex)
        {
            throw new Exception("Password decryption failed !", ex);
        }
    }

    private static byte[] GetCryptoKey()
    {
        UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
        string plainKey = "rastkoisajev2310982josipasenera153";
        byte[] encodedKey = unicodeEncoding.GetBytes(plainKey);

        // Prepares 192 bit key
        byte[] preparedKey = new byte[24];
        Array.Copy(encodedKey, preparedKey, 24);

        return preparedKey;
    }

Here is sample test invocation :

       private static void CryptoTest()
    {
        string password = "rastko";

        byte[] cryptoKey;
        byte[] cryptoIV;

        byte[] encryptedPassword = Crypto.EncryptPassword(password, out cryptoKey, out cryptoIV);

        string decryptedPAssword = Crypto.DecryptPassword(encryptedPassword, cryptoKey, cryptoIV);
    }

I have not good experience with security. What I see is that IV vector is 8byte size and as I found it is related to BlockSize, that is 8times greater then IV size. TripleDESCryptoServiceProvider for IV vector is using 8byte value. I can not change this.

Could you please tell me what I have to do or did I wrote something wrongly ?

Was it helpful?

Solution 3

After detail investigation I have found the solution for my problem. I have changed a little bit decryption logic.

Instead of this part in DecryptPassword method :

 // Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
                using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
                {
                    decryptoStream.Write(encryptedPassword, 0, encryptedPassword.Length);
                    ///decryptoStream.FlushFinalBlock();
                }

                byte[] decryptedPassword = memoryStream.ToArray();

                //Convert the buffer into a string and return it.
                readablePassword = unicodeEncoding.GetString(decryptedPassword, 0, decryptedPassword.Length);
            }

I am now using the Read logic from CryptoStream and then I am just removing nullable characters. It is like this now :

                        // Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
                    using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read))
                    {
                        // Create buffer to hold the decrypted data.
                        byte[] fromEncrypt = new byte[encryptedPassword.Length];

                        decryptoStream.Read(fromEncrypt, 0, fromEncrypt.Length);

                        //Convert the buffer into a string and return it.
                        readablePassword = unicodeEncoding.GetString(fromEncrypt);
                        readablePassword = readablePassword.Replace("\0", string.Empty);
                    }

This works perfectly for me ! Thank you all for your time.

OTHER TIPS

DES is a 64 bit block cypher. Any text that does not divide cleanly into 64 bit (=8 byte) blocks needs to be padded to make up a whole number of blocks. You need to set padding for encryption and decryption. If you have control of both ends then use PKCS#5 padding to encrypt and decrypt. If you only have control over the decryption end, then ask the encrypting end what padding they are using and expect that.

Note that encrypting a password is normally not the way to go. Use PBKDF2 instead. Don't confuse passwords and keys!

Try to make sure that your CryptoStreams get closed or flushed:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream.flushfinalblock.aspx

If you don't then the padding/unpadding will likely not be performed, and you get trash instead.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top