0

I implemented methods for symmetric encryption and decryption of a string in .net core 3.1. Now I'm migrating a project from .net core 3.1 to .net 7. Encryption works fine, but when I decrypt the encrypted value (JSON-string) in my .net 7 implementation it shortens the result string. The .net core 3.1 implementation instead returns the full value.

My implementation:

 #region Settings

 private int _iterations = 2;
 private static int _keySize = 256;
 private string _hash = "SHA1";
 private string _salt = "xxx"; // Random
 private string _vector = "xxx"; // Random

 #endregion

 public string Run(string data, string key)
 {
     var jsonstring = Decrypt(data, key);
     return jsonstring;
 }


 private string Decrypt(string value, string password)
 {
     return Decrypt<AesManaged>(value, password);
 }

 private string Decrypt<T>(string value, string password) where T : SymmetricAlgorithm, new()
 {
     byte[] vectorBytes = Encoding.UTF8.GetBytes(_vector);
     byte[] saltBytes = Encoding.UTF8.GetBytes(_salt);
     byte[] valueBytes = Convert.FromBase64String(value);

     byte[] decrypted;
     int decryptedByteCount = 0;

     using (T cipher = new T())
     {
         PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
         byte[] keyBytes = _passwordBytes.GetBytes(_keySize / 8);

         cipher.Mode = CipherMode.CBC;

         try
         {
             using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes))
             {
                 using (MemoryStream from = new MemoryStream(valueBytes))
                 {
                     using (CryptoStream reader = new CryptoStream(from, decryptor, CryptoStreamMode.Read))
                     {
                         decrypted = new byte[valueBytes.Length];
                         decryptedByteCount = reader.Read(decrypted, 0, decrypted.Length);
                     }
                 }
             }
         }
         catch (Exception ex)
         {
             return String.Empty;
         }

         cipher.Clear();
     }

     var decrypt = Encoding.UTF8.GetString(decrypted, 0, decryptedByteCount);
     return decrypt;
 }

I made tests in .net core 3.1 and .net7 and encrypted and decrypted my example string. The encryption works fine and I'm getting the same encrypted value. The decryption in .net core 3.1 returns the full initial value. The decryption in .net 7 returns a shorter version of the initial value (tested with different strings). The last 6 to 8 characters are missing now.

President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
Stefan
  • 11
  • 2
  • 1
    [This breaking change](https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams) identified [in this post](https://stackoverflow.com/q/69911084) is the usual suspect for this kind of problem. – Topaco Aug 24 '23 at 14:24
  • @Topaco thank you that was the problem. I had to resize the buffer at the end... Showing my new implementation in the answer – Stefan Aug 25 '23 at 09:23

1 Answers1

1

Because of Partial and zero-byte reads in DeflateStream, GZipStream, and CryptoStream I had to resize the buffer at the end. Here is my new implementation:

    private string Decrypt<T>(string value, string password) where T : SymmetricAlgorithm, new()
{
    byte[] vectorBytes = Encoding.UTF8.GetBytes(_vector);
    byte[] saltBytes = Encoding.UTF8.GetBytes(_salt);
    byte[] valueBytes = Convert.FromBase64String(value);

    string result = string.Empty;

    using (T cipher = new T())
    {
        PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
        byte[] keyBytes = _passwordBytes.GetBytes(_keySize / 8);

        cipher.Mode = CipherMode.CBC;

        try
        {
            using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes))
            using (MemoryStream memoryStream = new MemoryStream(valueBytes))
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            {
                byte[] decryptedData = new byte[valueBytes.Length];
                int bytesRead;
                int offset = 0;

                while ((bytesRead = cryptoStream.Read(decryptedData, offset, decryptedData.Length - offset)) > 0)
                {
                    offset += bytesRead;

                    // If the offset reaches the end of the buffer, resize the buffer to accommodate more data
                    if (offset == decryptedData.Length)
                    {
                        Array.Resize(ref decryptedData, decryptedData.Length * 2);
                    }
                }

                Array.Resize(ref decryptedData, offset);

               result = Encoding.UTF8.GetString(decryptedData);
            }

        }
        catch (Exception ex)
        {
            return String.Empty;
        }

        cipher.Clear();
    }

    return result;
}
Stefan
  • 11
  • 2