2

I have a password that I'm encrypting using openssl AES-256-CBC in php that I'm trying to decrypt in VB. I'm having problems with an IV generated by the openssl_random_pseudo_bytes() php function. If I just use a random IV string instead of calling openssl_random_pseudo_bytes() which returns a string of bytes in php, my VB code returns the correct decrypted password. Any suggestions would be greatly appreciated.

I have the following php function that encrypts a password:

function f_infr_encrypt_value($password, $key) {
   $iv  = openssl_random_pseudo_bytes(openssl_cipher_iv_length('AES-256-CBC'));
   return trim(
     base64_encode(
       openssl_encrypt($value, "AES-256-CBC", $key, 0, $iv) . '::' . $iv));
}

I have the following VB code that attempts to decrypt the password created by the php function above

Private Function DecryptPassword(encryptedPassword As String, key As String) As String
   Try
      'Decode password from base 64
      Dim base64Decoded As String
      Dim data() As Byte
      data = System.Convert.FromBase64String(encryptedPassword)
      base64Decoded = System.Text.ASCIIEncoding.ASCII.GetString(data)
    
      'Separating the password from the IV. Delimeter is "::"
      Dim ivct = base64Decoded.Split({"::"}, StringSplitOptions.None)
    
      'baseEncodedPassword is the password that is encrypted using AES-256-CBC
      Dim baseEncodedPassword As String
      baseEncodedPassword = ivct(0)
    
      Dim iv As String = ivct(1)
      Dim sEncryptedString As String = baseEncodedPassword
      Dim myRijndael As New RijndaelManaged
      myRijndael.Padding = PaddingMode.Zeros
      myRijndael.Mode = CipherMode.CBC
      myRijndael.KeySize = 256
      myRijndael.BlockSize = 128
    
      Dim keyByte() As Byte
      Dim IVByte() As Byte
    
      keyByte = System.Text.Encoding.ASCII.GetBytes(key)
      IVByte = System.Text.Encoding.ASCII.GetBytes(iv)
    
      Dim decryptor As ICryptoTransform = myRijndael.CreateDecryptor(keyByte, IVByte)
      Dim sEncrypted As Byte() = Convert.FromBase64String(sEncryptedString)
      Dim fromEncrypt() As Byte = New Byte(sEncrypted.Length) {}
      Dim msDecrypt As New MemoryStream(sEncrypted)
      Dim csDecrypt As New CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)
    
      csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length)
    
      Dim returnvalue = (System.Text.Encoding.ASCII.GetString(fromEncrypt))
    
      Return returnvalue
    
   Catch ex As Exception
      Return ex.Message
   End Try
End Function
Rudu
  • 15,682
  • 4
  • 47
  • 63
TonyK
  • 23
  • 3
  • why are you encrypting and not hashing the password? – Morgosus Sep 02 '20 at 00:55
  • You seem to be double Base64 decoding in VB? (Once at the start to generate `data`/`base64Decoded` and again on `sEncryptedString` which is actually just `baseEncodedPassword` to generate `sEncrypted`). At the composition of `$encrypted.'::'.$iv` because both `$encrypted` and `$iv` are random byte sequences, there's the tiniest chance that either may container double `0x3A` (the character *:*) – Rudu Sep 02 '20 at 03:01
  • You could convert the IV in PHP to Base64 before concatenating it with the ciphertext. On VB.net side decode the iv (after splitting) directly to IVByte, that should work. – Michael Fehr Sep 02 '20 at 06:19

1 Answers1

3

There are a few issues in the VB code:

  • In the current PHP code the ciphertext is implicitly Base64 encoded by openssl_encrypt and the binary IV is concatenated with the separator :: in the order ciphertext | IV. The result is then Base64 encoded again. In the VB code the data is converted into a string after Base64 decoding. This corrupts the binary IV (see also). The ciphertext is not corrupted because of its Base64 decoding. To prevent this, the conversion into a string must not be performed.

    Note: The concatenation in the PHP code is unnecessarily complicated. Usually IV and ciphertext are concatenated in binary form without a separator (typically in the order IV | ciphertext) and the result is Base64 encoded. The second variant would be to concatenate both parts, each in Base64 encoded form, using a separator (s. Michael Fehr's comment).

  • openssl_encrypt implicitly uses PKCS7 padding, in the VB code zero padding is applied (see also). Here PKCS7 padding must be used as well.

  • In the VB Code the plaintext is stored in a Byte array, which has the length of the ciphertext. Because the ciphertext is longer than the plaintext due to padding, the array is too large. This problem is solved when e.g. using a StreamReader.

  • Also, Using statements should be applied to release possible system resources.

A possible implementation is:

Dim ciphertextSepIv() As Byte
ciphertextSepIv = System.Convert.FromBase64String(encryptedPassword)

Dim iv(16 - 1) As Byte                                                 'arbitrary binary data, length: 16 bytes
Dim ciphertext(ciphertextSepIv.Length - 1 - 2 - 16) As Char            'ASCII data (since Base64 encoded), length: ciphertext.Length - 2 - 16 bytes, 2 considers the delimiter ::
Array.Copy(ciphertextSepIv, 0, ciphertext, 0, ciphertext.Length)
Array.Copy(ciphertextSepIv, ciphertext.Length + 2, iv, 0, iv.Length)

Dim rijndael As New RijndaelManaged
rijndael.Padding = PaddingMode.PKCS7                                   'use PKCS7-Padding
rijndael.Mode = CipherMode.CBC
rijndael.KeySize = 256
rijndael.BlockSize = 128

Dim keyByte() As Byte
keyByte = System.Text.Encoding.ASCII.GetBytes(key)

Dim decryptor As ICryptoTransform = rijndael.CreateDecryptor(keyByte, iv)
Dim encrypted As Byte() = Convert.FromBase64CharArray(ciphertext, 0, ciphertext.Length)
Dim decryptedData As String = Nothing
Using msDecrypt As New MemoryStream(encrypted)
    Using csDecrypt As New CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)
        Using srDecrypt As New StreamReader(csDecrypt)                 'return only the plain text (without whitespaces etc.)
            decryptedData = srDecrypt.ReadToEnd()
        End Using
    End Using
End Using

Return decryptedData

With this VB code a ciphertext encrypted with the posted PHP Code can be decrypted.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thank you very much. This implementation solved my issue. I wasn't quite sure how to handle the IV in binary form. Thank you! – TonyK Sep 02 '20 at 10:27