Eastrall / EntityFrameworkCore.DataEncryption

A plugin for Microsoft.EntityFrameworkCore to add support of encrypted fields using built-in or custom encryption providers.
MIT License
326 stars 54 forks source link

Intermittent error on Decrypting fields related to Padding ("Padding is invalid and cannot be removed") #48

Closed AngeloPacione closed 1 year ago

AngeloPacione commented 1 year ago

Hello,

I am using version 4.0 of your package and have an intermittent error that appears when attempting to decrypt data via EF calls. The error goes like this: ---ERROR--- System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed.    at System.Security.Cryptography.SymmetricPadding.GetPaddingLength(ReadOnlySpan1 block, PaddingMode paddingMode, Int32 blockSize)    at System.Security.Cryptography.UniversalCryptoDecryptor.UncheckedTransformFinalBlock(ReadOnlySpan1 inputBuffer, Span1 outputBuffer)    at System.Security.Cryptography.UniversalCryptoDecryptor.UncheckedTransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)    at System.Security.Cryptography.UniversalCryptoTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)    at System.Security.Cryptography.CryptoStream.ReadAsyncCore(Memory1 buffer, CancellationToken cancellationToken, Boolean useAsync)    at System.Security.Cryptography.CryptoStream.Read(Byte[] buffer, Int32 offset, Int32 count)    at System.Security.Cryptography.CryptoStream.CopyTo(Stream destination, Int32 bufferSize)    at Microsoft.EntityFrameworkCore.DataEncryption.Providers.AesProvider.StreamToBytes(Stream stream)    at Microsoft.EntityFrameworkCore.DataEncryption.Providers.AesProvider.Decrypt(Byte[] input)    at Microsoft.EntityFrameworkCore.DataEncryption.Internal.EncryptionConverter2.Decrypt[TInput,TOupout](TProvider input, IEncryptionProvider encryptionProvider, StorageFormat storageFormat)    at lambda_method909(Closure, QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator)    at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.MoveNextAsync()    at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable1 asyncEnumerable, CancellationToken cancellationToken)    at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable1 asyncEnumerable, CancellationToken cancellationToken) ---ERROR---

I am using Attribute-based code to declare the fields as Encrypted. It is definitely intermittent so far. I am using a 256-key. I have also explicitly set the PaddingMode just to be sure and it does not seem to help the issue. The code below is how I declare the encryption setup in the EF DataContext Constructor and OnModelCreating method:

---DATACONTEXT CONSTUCTOR CODE--- using Aes aesCrypto = Aes.Create(); aesCrypto.Key = Convert.FromBase64String("MyKeyHere"); // Expecting 256-bit Base64 encoded string aesCrypto.GenerateIV(); // Generating a new Initialization Vector each time _encryptionProvider = new AesProvider(aesCrypto.Key, aesCrypto.IV, padding: PaddingMode.PKCS7); ---DATACONTEXT CONSTUCTOR CODE---

---ONMODELCREATING CODE--- modelBuilder.UseEncryption(_encryptionProvider); ---ONMODELCREATING CODE---

There is a lot of discussion out there on this error and most of it appears older with differing solutions. I am wondering if you have seen this before in your testing, or possibly have a best practice on the way the constructor code is written that may prevent this intermittent issue. To throw a wrench in this a bit more, I test this locally and it seems fine. But when deployed as a service in a development environment, this is when the intermittent error happens. This post I found in particular seems to talk about what may be happening here - https://stackoverflow.com/questions/66482089/aes-padding-is-invalid-and-cannot-be-removed

Any help on the matter is appreciated in advance. Thank you.

Eastrall commented 1 year ago

Hi, by reading your code:

using Aes aesCrypto = Aes.Create();
aesCrypto.Key = Convert.FromBase64String("MyKeyHere"); // Expecting 256-bit Base64 encoded string
aesCrypto.GenerateIV(); // Generating a new Initialization Vector each time
_encryptionProvider = new AesProvider(aesCrypto.Key, aesCrypto.IV, padding: PaddingMode.PKCS7);

It looks like that your are generating an IV at every run of your program. If you do this you will not be able to decrypt the data your have encrypted with the (key, IV) pair. So you need to store the IV somewhere.

As this answer suggests: https://security.stackexchange.com/a/35211 You can genearte an IV at each encryption and add it to the begining of your encrypted data. EntityFrameworkCore.DataEncryption had once (in V3.X and prior) a support for dynamic IV on each encryption. If you want to use it, I'd suggest you to checkout this comment: https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/issues/46#issuecomment-1401939203

Note : when using a dynamic IV, all built-in LINQ-to-SQL statements will not work as intended. This is related to the ValueConverter (and thus the EncryptionProvider) being called each time you interact with the database.

AngeloPacione commented 1 year ago

Excellent thank you. I ended up figuring it out. I guess I read incorrectly somewhere about using a generated key every time .

Thanks for the reply and the package availability. -Angelo

On Fri, Feb 24, 2023, 3:28 a.m. Filipe @.***> wrote:

Hi, by reading your code:

using Aes aesCrypto = Aes.Create();aesCrypto.Key = Convert.FromBase64String("MyKeyHere"); // Expecting 256-bit Base64 encoded stringaesCrypto.GenerateIV(); // Generating a new Initialization Vector each time_encryptionProvider = new AesProvider(aesCrypto.Key, aesCrypto.IV, padding: PaddingMode.PKCS7);

It looks like that your are generating an IV at every run of your program. If you do this you will not be able to decrypt the data your have encrypted with the (key, IV) pair. So you need to store the IV somewhere.

As this answer suggests: https://security.stackexchange.com/a/35211 You can genearte an IV at each encryption and add it to the begining of your encrypted data. EntityFrameworkCore.DataEncryption had once (in V3.X and prior) a support for dynamic IV on each encryption. If you want to use it, I'd suggest you to checkout this comment: #46 (comment) https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/issues/46#issuecomment-1401939203

Note : when using a dynamic IV, all built-in LINQ-to-SQL statements will not work as intended. This is related to the ValueConverter (and thus the EncryptionProvider) being called each time you interact with the database.

— Reply to this email directly, view it on GitHub https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/issues/48#issuecomment-1443118485, or unsubscribe https://github.com/notifications/unsubscribe-auth/AM5EKMJHXS4FNZTUVSW2AKTWZBWK5ANCNFSM6AAAAAAVEZCFAU . You are receiving this because you authored the thread.Message ID: @.*** com>

Eastrall commented 1 year ago

Great news! I'm closing this issue since it has been solved.