0

I'm aware of the other thread on this issue (AES decryption error " The input data is not a complete block." Error vb.net), but I'm either not implementing the solutions offered there correctly, or something about my particular variant of this issue isn't covered by those solutions. In any event I'm getting the incomplete block error from the following code

Private GD As System.Security.Cryptography.Aes = System.Security.Cryptography.Aes.Create
Private PDB As New System.Security.Cryptography.Rfc2898DeriveBytes(EK, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D, &H65, &H64, &H76, &H65, &H64, &H65, &H76})

Public Function Decrypt(ByVal val As String) As String
    Dim ret As String = Nothing
    Dim TTB As New System.Text.UTF8Encoding

    Try
        Dim input() As Byte = TTB.GetBytes(val)

        Using ms As New System.IO.MemoryStream(input)
            Using cs As New System.Security.Cryptography.CryptoStream(ms, GD.CreateDecryptor(PDB.GetBytes(32), PDB.GetBytes(16)), Security.Cryptography.CryptoStreamMode.Read)
                Using sr As New System.IO.StreamReader(cs)
                    ret = sr.ReadToEnd()
                End Using
            End Using
        End Using

        input = nothing
    Catch ex As Exception
        EL.AddErr("Encountered an error while decrypting the provided text for " & FName & ". Error Details: " & ex.Message, path)
    End Try

    Return ret
End Function

EK is my key, which I'll not be including. It's just a String though, nothing special.

I've tried several other methods to decrypt based on guidance on the MSDN site, DreamInCode, etc. None worked, but they all had different issues (typically returning a blank string). Seeing as this version of code closely mirrors my encryption code, I'd like to stick with it (or at least as close as I can while still having functional code).

Community
  • 1
  • 1
JMichael
  • 569
  • 11
  • 28
  • If your `val` contains a hex-encoded result of encryption, then I would start by replacing `TTB.GetBytes(val)` with a proper `StringToByteArray()` function (check [this answer](http://stackoverflow.com/a/311179/1879042) for an example). If that alone does not help, please, change your *cryptic* `GD` or `PDB` to some more verbose identifiers, because it is quite difficult to read your code and thus to diagnose it. – Anton Samsonov Jun 15 '15 at 18:16
  • val is the raw string to be encrypted. The definitions for GD & PDB are included prior to the function itself. I can move them in-line if it will help, but there'd be no new information. – JMichael Jun 15 '15 at 18:17
  • You mean “to be decrypted”? Anyway, I don't get it: both `val` and `ret` are text strings, yet you treat them as if they were raw bytes — you first take UTF-8 representation of `val` and finally push the output of CryptoStream (which is purely binary) to the StreamReader and then to `ret`. This just makes no sense. Particularly, your CryptoStream expects its input to be properly padded (PKCS7 by default), for which `input` likely doesn't satisfy. – Anton Samsonov Jun 15 '15 at 18:25
  • 'val' is treated like a string, thus the conversion to its bytes via `TTB.GetBytes(val)` (believe that came from CodeProject). If `StringToByteArray()` is the better way to go, I'll switch. `ret` should also be getting treated like a string. If `ret = sr.ReadToEnd()` is using `ret` as a byte(), that wasn't the intent (that's from MSDN https://msdn.microsoft.com/en-us/library/system.security.cryptography.aes(v=vs.110).aspx). The whole architecture of the function is carbon copy from that article. At this point could you provide a good example for me to start from, as I have no good references? – JMichael Jun 15 '15 at 20:41
  • The problem is that it's very difficult to understand what exactly are you trying to accomplish: what is the input for `Encrypt()` function (omitted in your example) and what is its output; what is the input for `Decrypt()` function (should be the same as output of Encrypt, but who knows?) and what is expected to be returned by it? What I mean is that you should provide the exact data you test with, i. e. the value of `val` and the value you expect `ret` should have at the end. – Anton Samsonov Jun 15 '15 at 20:56
  • The intent is that I provide the string to be encrypted to `Encrypt` and am returned the AES encrypted version of the string. Similarly, `Decrypt` is fed an encrypted string and should return the decrypted version of that string. Such that if I wrote `Dim s as string = Decrypt(Encrypt("testing")) MsgBox(s, vbokonly, "Testing")`, the message box should show "testing". – JMichael Jun 15 '15 at 21:05
  • Expanding, the original purpose of the functions is to encrypt mildly sensitive data for storage, and then to be able to decrypt that data for later use when needed. – JMichael Jun 15 '15 at 21:06
  • So you should change output type of `Encrypt()` to an array of bytes, and accept this array as input for `Decrypt()`. If you need to store the encrypted data as text, then encode it as hex string explicitly by any proper `ByteArrayToString()`, and then decode by `StringToByteArray()` respectively before decryption. – Anton Samsonov Jun 15 '15 at 21:52

1 Answers1

1

Despite all comments, I still lack understanding of your intentions. Therefore, the sample code below may not provide what you exactly want, but at least should give an idea how to employ cryptographic functions. Particularly, the most notable difference from your approach is that the encryption key and initialization vector are computed once and for all messages, rather than reevaluated on each occasion, because the latter is prone to synchronization errors — such as when you reuse single crypto object to communicate with multiple parties, or when some messages get lost in transmission.

Public Shared Sub Test()

    ' Note: You should not actually hard-code any sensitive information in your source files, ever!
    Dim sKeyPreimage As String = "MySuperPassword"
    Dim oMyCrypto As New MyCrypto(sKeyPreimage)

    Dim sPlaintext As String = "My super secret data"
    Dim sEncrypted As String = oMyCrypto.EncryptText(sPlaintext)
    Dim sDecrypted As String = oMyCrypto.DecryptText(sEncrypted)

    Console.Out.WriteLine("Plaintext: {0}", sPlaintext) ' "My super secret data"
    Console.Out.WriteLine("Encrypted: {0}", sEncrypted) ' "72062997872DC4B4D1BCBF48D5D30DF0D498B20630CAFA28D584CCC3030FC5F1"
    Console.Out.WriteLine("Decrypted: {0}", sDecrypted) ' "My super secret data"

End Sub

Public Class MyCrypto

    Private Shared TextEncoding As Text.Encoding = Text.Encoding.UTF8

    Private CipherEngine As System.Security.Cryptography.SymmetricAlgorithm

    ' Note: Unlike in the question, same key and IV are reused for all messages.
    Private CipherKey() As Byte
    Private CipherIV() As Byte

    Public Sub New(ByVal sKeyPreimage As String)

        Dim abKeyPreimage() As Byte = TextEncoding.GetBytes(sKeyPreimage)
        Dim abKeySalt() As Byte = TextEncoding.GetBytes("Ivan Medvedev")

        Const KeyDerivationRounds As Integer = 1 << 12
        Dim oKeyDerivationEngine As New System.Security.Cryptography.Rfc2898DeriveBytes(abKeyPreimage, abKeySalt, KeyDerivationRounds)

        Me.CipherEngine = System.Security.Cryptography.Aes.Create()
        Me.CipherEngine.Padding = Security.Cryptography.PaddingMode.PKCS7
        Me.CipherKey = oKeyDerivationEngine.GetBytes(Me.CipherEngine.KeySize >> 3)
        Me.CipherIV = oKeyDerivationEngine.GetBytes(Me.CipherEngine.BlockSize >> 3)

    End Sub

    Public Function Encrypt(ByVal abPlaintext() As Byte) As Byte()

        Dim abCiphertext() As Byte

        Using hStreamSource As New System.IO.MemoryStream(abPlaintext),
                hStreamCipher As New System.Security.Cryptography.CryptoStream(
                    hStreamSource,
                    Me.CipherEngine.CreateEncryptor(Me.CipherKey, Me.CipherIV),
                    Security.Cryptography.CryptoStreamMode.Read),
                hStreamTarget As New System.IO.MemoryStream

            hStreamCipher.CopyTo(hStreamTarget)
            abCiphertext = hStreamTarget.ToArray()

        End Using

        Return abCiphertext
    End Function

    Public Function Decrypt(ByVal abCiphertext() As Byte) As Byte()

        Dim abPlaintext() As Byte

        Using hStreamSource As New System.IO.MemoryStream(abCiphertext),
                hStreamCipher As New System.Security.Cryptography.CryptoStream(
                    hStreamSource,
                    Me.CipherEngine.CreateDecryptor(Me.CipherKey, Me.CipherIV),
                    Security.Cryptography.CryptoStreamMode.Read),
                hStreamTarget As New System.IO.MemoryStream

            hStreamCipher.CopyTo(hStreamTarget)
            abPlaintext = hStreamTarget.ToArray()

        End Using

        Return abPlaintext
    End Function

    Public Function EncryptText(ByVal sPlaintext As String) As String
        Dim abPlaintext() As Byte = TextEncoding.GetBytes(sPlaintext)
        Dim abCiphertext() As Byte = Me.Encrypt(abPlaintext)
        Dim sCiphertext As String = Hex.Format(abCiphertext)
        Return sCiphertext
    End Function

    Public Function DecryptText(ByVal sCiphertext As String) As String
        Dim abCiphertext() As Byte = Hex.Parse(sCiphertext)
        Dim abPlaintext() As Byte = Me.Decrypt(abCiphertext)
        Dim sPlaintext As String = TextEncoding.GetChars(abPlaintext)
        Return sPlaintext
    End Function

End Class

Public Class Hex

    Public Shared Function Format(ByVal abValue() As Byte) As String
        Dim asChars(0 To abValue.Length * 2 - 1) As Char
        Dim ndxChar As Integer = 0
        For ndxByte As Integer = 0 To abValue.Length - 1
            Dim bNibbleHi As Byte = abValue(ndxByte) >> 4, bNibbleLo As Byte = CByte(abValue(ndxByte) And &HFUS)
            asChars(ndxChar) = Convert.ToChar(If(bNibbleHi <= 9, &H30US + bNibbleHi, &H37US + bNibbleHi)) : ndxChar += 1
            asChars(ndxChar) = Convert.ToChar(If(bNibbleLo <= 9, &H30US + bNibbleLo, &H37US + bNibbleLo)) : ndxChar += 1
        Next
        Return New String(asChars)
    End Function

    Public Shared Function Parse(ByVal sValue As String) As Byte()

        If String.IsNullOrEmpty(sValue) Then Return New Byte() {}
        If (sValue.Length Mod 2) > 0 Then Return Nothing

        Dim ndxText As Integer = 0
        Dim ndxByteMax As Integer = (sValue.Length \ 2) - 1
        Dim abValue(0 To ndxByteMax) As Byte

        Try
            For ndxByte As Integer = 0 To ndxByteMax
                abValue(ndxByte) = Convert.ToByte(sValue.Substring(ndxText, 2), 16)
                ndxText += 2
            Next
        Catch ex As Exception
            Return Nothing
        End Try

        Return abValue
    End Function

End Class

Again, please, note that this is just an example. I am not endorsing any kind of protection techniques shown here, especially because your task remains unknown. The code above simply illustrates the syntax and semantics — not how to do it right.

Anton Samsonov
  • 1,380
  • 17
  • 34