2

I get the following exception when decrypting a string, but it only occurs when the original string (before encryption) is > 751 characters. It didn't have any problems encrypting. How can I avoid this problem?

System.Security.Cryptography.CryptographicException occurred
  Message="Length of the data to decrypt is invalid."
  Source="mscorlib"
  StackTrace:
       at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)    at System.Security.Cryptography.CryptoStream.Read(Byte[] buffer, Int32 offset, Int32 count)    at VHC.Reporting.View.QueryString.QueryStringObsuficator.Decrypt(String inputText) in E:\Kiln_Repository\Reports\DevExpress_New\VHC_Reporting\VHC.Reporting.View\Classes\QueryString\QueryStringObsuficator.vb:line 159
  InnerException: 

I have spent a while looking at this as there it seems to be a common question on stack overflow, however I don't think any of them cover my question, apologies if I have missed something.

There's a bug report here, but special characters don't seem to be the problem.

The methods I use are:

Public Shared Function Encrypt(ByVal inputText As String) As String
    Dim rijndaelCipher As New RijndaelManaged()
    Dim plainText As Byte() = Encoding.Unicode.GetBytes(inputText)
    Dim SecretKey As New Rfc2898DeriveBytes(EncryptionKey, SALT)

    Using encryptor As ICryptoTransform = rijndaelCipher.CreateEncryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16))
        Using memoryStream As New MemoryStream()
            Using cryptoStream As New CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)
                cryptoStream.Write(plainText, 0, plainText.Length)
                cryptoStream.FlushFinalBlock()
                Return String.Concat(QuestionMark, ParameterName, Convert.ToBase64String(memoryStream.ToArray()))
            End Using
        End Using
    End Using
End Function


Private Shared Function Decrypt(ByVal inputText As String) As String
    Dim rijndaelCipher As New RijndaelManaged()
    Dim encryptedData As Byte() = Convert.FromBase64String(inputText)
    Dim secretKey As New Rfc2898DeriveBytes(EncryptionKey, SALT)

    Using decryptor As ICryptoTransform = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16))
        Using memoryStream As New MemoryStream(encryptedData)
            Using cryptoStream As New CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)
                Dim plainText As Byte() = New Byte(encryptedData.Length - 1) {}
                Dim decryptedCount As Integer = cryptoStream.Read(plainText, 0, plainText.Length)
                Return Encoding.Unicode.GetString(plainText, 0, decryptedCount)
            End Using
        End Using
    End Using
End Function
Mr Shoubs
  • 14,629
  • 17
  • 68
  • 107

1 Answers1

2

I tested the code in your post and it seems to work correctly (I changed it to return the Base64 encoded data directly, rather than concatenating it to the QuestionMark and ParameterName), and Decrypt(Encrypt(New String("x"C, 800))) worked fine.

This suggests that something is going on between your encrypting the data and subsequently decrypting it. The fact that you're concatenating it to QuestionMark and ParameterName suggests that you are perhaps putting it in a URL? If so there is likely a maximum length on the parameter data which is resulting in truncation of the ciphertext causing the error you're seeing when attempting to decrypt.

After Base64 encryption (which results in a 4/3 expansion of the output) 751 bytes of plaintext puts you just under 1024 bytes of encrypted data. However 752 bytes results in an additional block (16 bytes) of output, which after Base64 encoding results in 1024 bytes. It seems no coincidence that you're having problems at exactly 1KB of output.

It's probably also worth mentioning that your method for generating both the key and IV are potentially insecure - it appears that you are using a fixed salt when deriving the key bytes from the password which, depending on how this is being used could reduce security. The second problem is that you are also deriving the IV from the password. The IV should be random data, unrelated to the key being used, and a different IV should be used for every encryption. This ensures that repeated encryption of the same message with the same key still results in distinct ciphertext. The random IV can be transmitted (unencrypted) along with the ciphertext and extracted for use in the decryption process.

Iridium
  • 23,323
  • 6
  • 52
  • 74
  • Thanks, you are correct, about it being potentially insecure. But, I don't know how to implement your suggestion - which part is the initialisation vector and how can I change the salt each time without transmitting it as well, or is it ok to transmit the IV and Salt along with the encrypted sting? – Mr Shoubs Dec 22 '13 at 23:07
  • 1
    @MrShoubs The IV is the 2nd parameter to `rijndaelCipher.CreateEncryptor/Decryptor()` (where you are currently using `secretKey.GetBytes(16)`). I would suggest using the parameterless versions of these methods, and specify `rijndaelCipher.Key = secretKey.GetBytes(32)` and `rijndaelCipher.GenerateIV()` to set the key/IV prior to calling `CreateEncryptor()`, you will need to transmit the value of `rijndaelCipher.IV` along with the ciphertext. For decryption set the key the same way as for encryption but set `rijndaelCipher.IV` to the transmitted IV value prior to calling `CreateDecryptor()`. – Iridium Dec 23 '13 at 12:46
  • Thanks, I've implemented that, but what about the salt? I didn't think I should transmit this? – Mr Shoubs Jan 02 '14 at 10:37
  • 1
    @MrShoubs There should be no problem in sending the salt unencrypted (indeed, this is specified in RFC2898 - See Appendix A.2 of: http://www.ietf.org/rfc/rfc2898.txt). – Iridium Jan 02 '14 at 13:03