Rijndael256 icon indicating copy to clipboard operation
Rijndael256 copied to clipboard

256 blocksize does not work in .net core

Open ByronAP opened this issue 8 years ago • 26 comments

In .net core AES is used which does not support 256 blocksize which is the exact reason why RijindaelManaged doesn't work. Unless I am missing something here, if I am please correct me as I can not get it to work.

Ref: https://github.com/dotnet/corefx/issues/12064

ByronAP avatar Nov 03 '17 03:11 ByronAP

What version of .NET Core are you using @ByronAP? Can you provide an example?

JasonPierce avatar Nov 04 '17 18:11 JasonPierce

.net core 2.0 and any example using a 256 block size will fail because you are trying to use AES which does not support that. This is block size not key size, a 256 key should work, a 256 block size will absolutely not work unless you write the actual rounds from scratch because the .net core team has emphatically refused to support Rijndael. You can try it by making a .net core console app and trying to decrypt anything that has been encrypted using Rijndael 256. It will fail as soon as you try and set that invalid block size. To further prove this remove the net45 section in the src, it should fail on all systems since it is AES (not tested but should).

ByronAP avatar Nov 04 '17 18:11 ByronAP

@ByronAP can you provide an example illustrating how you're using a 256 block size with Rijndael256?

Rijndael256 currently supports:

  • 128-128
  • 192-128
  • 256-128

N-256 isn't something I set out to support when I created this lib, but if a significant number of users would like to see it, I'm not opposed to investigating it's implementation. Are you certain you want to use 256 block size? It hasn't had nearly as much attention as 128 block size (which means it could have weaknesses that have yet to be discovered).

JasonPierce avatar Nov 04 '17 20:11 JasonPierce

I'm sure it is not all that common in .net however some libs in php use it along with some other functions so not having it in c# creates an interoperability impediment. I personally wouldn't bother because you have to write the whole thing yourself or rip some code from bouncy castle to make it work for what is essentially an edge case.

ByronAP avatar Nov 04 '17 23:11 ByronAP

Closing, since there doesn't appear to be a great interest in this feature. Will re-open if that changes.

JasonPierce avatar Nov 05 '17 19:11 JasonPierce

+1

niemyjski avatar Dec 28 '17 17:12 niemyjski

https://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp

I'm using this easy and nice implementation that use 256 block size, could you have a look at it? it's native (only system.security) and super fast to test

ciacco85 avatar Oct 12 '18 10:10 ciacco85

I'm using the same as @ciacco85 but doesn't work on .net core cause it's block size is 256 D:

Gabriel-Espinoza avatar May 23 '19 00:05 Gabriel-Espinoza

@Gabriel-Espinoza https://stackoverflow.com/a/51947250/773165

parabola949 avatar Jun 19 '19 16:06 parabola949

Reopening, since interest is spiking

JasonPierce avatar Jun 28 '19 05:06 JasonPierce

I'm very interested, too. I have a library based created on .Net Standard. Importing it on .Net Framwork all goes right but using it on .Net Core I have problems.

tedebus avatar Jan 10 '20 16:01 tedebus

I have a similar issue, as well. I have data stored that is encrypted using an initialization vector that requires a block size of 256. I'm trying to find a way to decrypt these values in .NET Core and cannot use 256.

darrinjolson avatar Feb 10 '20 17:02 darrinjolson

Unfortunately, as of .NET Core 3.1, RijndaelManaged still does not support block sizes greater than 128. Looking at the source code, you can see .NET Core throws an exception if you try to use 192 or 256: https://github.com/dotnet/corefx/blob/release/3.1/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RijndaelImplementation.cs#L37

cc: @ciacco85 , @Gabriel-Espinoza

JasonPierce avatar Feb 12 '20 03:02 JasonPierce

they have made it quite clear over the years that it will never be supported since it is not an official implementation :-(

ByronAP avatar Mar 03 '20 02:03 ByronAP

+1 @JasonPierce are you still working on this?

wmmihaa avatar Mar 17 '20 22:03 wmmihaa

@wmmihaa I'm keeping on eye on it. Unfortunately, as of .NET Core 3, it still does not support this. We can hope that they do add support in the new .NET 5 that is currently in Preview 1. Perhaps we'll get support for it at that time, since it is currently supported in .NET Framework 🤞

JasonPierce avatar Mar 18 '20 22:03 JasonPierce

I believe it is necessary to provide 256 blocksize encryption and decryption. We have an old windows application that used 256 blocksize to encrypt passwords in the database. Now we are developing a web application version. the problem is we can not use that data in the web application which is developing with asp.net core 3

sediinhesari avatar Apr 01 '20 15:04 sediinhesari

Hey,

If .NET Core 3.1 still does not support 256 initialization vector why documentation does not reflect that?

Look at this:

//RijndaelManaged // Legal min key size = 128 // Legal max key size = 256 // Legal min block size = 128 // Legal max block size = 256

Anybody can't get the same result! Why does the documentation is not refleting the truth?

https://docs.microsoft.com/pt-br/dotnet/api/system.security.cryptography.symmetricalgorithm.legalkeysizes?view=netcore-3.1

dericferreira avatar May 28 '20 12:05 dericferreira

Just discovered this, and it creates a huge block. I have to support decryption in .net core of values encrypted with .net framework using 256 bit. Arrrrgggg!!!

davidathompson avatar Jun 02 '20 01:06 davidathompson

@davidathompson ...hope this can be of help

Helper class

public static class CipherHelper
    {
        // This constant is used to determine the keysize of the encryption algorithm in bits.
        // We divide this by 8 within the code below to get the equivalent number of bytes.
        private const int Keysize = 256;

        // This constant determines the number of iterations for the password bytes generation function.
        private const int DerivationIterations = 1000;

        public static string Encrypt(string plainText, string passPhrase)
        {
            // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
            // so that the same Salt and IV values can be used when decrypting.  
            var saltStringBytes = Generate256BitsOfRandomEntropy();
            var ivStringBytes = Generate256BitsOfRandomEntropy();
            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                var engine = new RijndaelEngine(256);
                var blockCipher = new CbcBlockCipher(engine);
                var cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
                var keyParam = new KeyParameter(keyBytes);
                var keyParamWithIV = new ParametersWithIV(keyParam, ivStringBytes, 0, 32);

                cipher.Init(true, keyParamWithIV);
                var comparisonBytes = new byte[cipher.GetOutputSize(plainTextBytes.Length)];
                var length = cipher.ProcessBytes(plainTextBytes, comparisonBytes, 0);

                cipher.DoFinal(comparisonBytes, length);
                //                return Convert.ToBase64String(comparisonBytes);
                return Convert.ToBase64String(saltStringBytes.Concat(ivStringBytes).Concat(comparisonBytes).ToArray());
            }
        }

        public static string Decrypt(string cipherText, string passPhrase)
        {
            // Get the complete stream of bytes that represent:
            // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
            var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
            // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
            var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
            // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
            var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
            // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
            var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();

            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                var engine = new RijndaelEngine(256);
                var blockCipher = new CbcBlockCipher(engine);
                var cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
                var keyParam = new KeyParameter(keyBytes);
                var keyParamWithIV = new ParametersWithIV(keyParam, ivStringBytes, 0, 32);

                cipher.Init(false, keyParamWithIV);
                var comparisonBytes = new byte[cipher.GetOutputSize(cipherTextBytes.Length)];
                var length = cipher.ProcessBytes(cipherTextBytes, comparisonBytes, 0);

                cipher.DoFinal(comparisonBytes, length);
                //return Convert.ToBase64String(saltStringBytes.Concat(ivStringBytes).Concat(comparisonBytes).ToArray());

                var nullIndex = comparisonBytes.Length - 1;
                while (comparisonBytes[nullIndex] == (byte)0)
                    nullIndex--;
                comparisonBytes = comparisonBytes.Take(nullIndex + 1).ToArray();


                var result = Encoding.UTF8.GetString(comparisonBytes, 0, comparisonBytes.Length);

                return result;
            }
        }

        private static byte[] Generate256BitsOfRandomEntropy()
        {
            var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
            using (var rngCsp = new RNGCryptoServiceProvider())
            {
                // Fill the array with cryptographically secure random bytes.
                rngCsp.GetBytes(randomBytes);
            }
            return randomBytes;
        }
    }

Unit test

[Test]
        public async Task CipherHelper_ShouldWork()
        {
            try
            {
                var value = "Secret to encrypt";
                var passPhrase = "your pass phrase";
                var encryptedValue = CipherHelper.Encrypt(value, passPhrase);
                var decryptedValue = CipherHelper.Decrypt(encryptedValue, passPhrase);
                
                Assert.IsTrue(value == decryptedValue);
            }
            catch (Exception ex)
            {
                Assert.Fail(ex.Message);
            }
        }

wmmihaa avatar Jun 02 '20 18:06 wmmihaa

@wmmihaa Thank you! That is exactly what I needed. You saved me a ton of time.

zutroy97 avatar Jun 05 '20 15:06 zutroy97

@wmmihaa Thanks for this - seems to work flawlessly. For anyone wondering - it requires BouncyCastle.NetCore :)

suj87 avatar Jul 30 '20 17:07 suj87

Thanks @wmmihaa and @suj87 , totally works. We were dead in the water before!

jbaumbach avatar Apr 21 '21 22:04 jbaumbach

We have a desktop software using WPF and written a crypto library using RijndaelManaged using .NET FX 4.0 ~2010-2011 timeframe. Now we are moving that code base to .NET 6, and discover this issue. We are also Microsoft Partner and this is leaving us high and dry.

If MS supported RijndaelManaged with 256 bit blocksize, then it should have a upgrade route. We can definitely use the newer and ratified AES also, but what do we do with encrypted data at rest in files. We do have to support data/ config files from our older products and can not abandon that. Right now the only working solution is to use BouncyCastle.NetCore. When MS has 1000s of developers, how hard is it to support 256 bit blocksize giving us a native .NET solution instead of relying on a 3rd party library. We plan to use it for "Reading old", and generate new files using new AES. But do not like the concept no straightforward upgrade path.

I strongly vote for this to be added to .NET 6.

RajeshAKumar avatar Oct 10 '21 03:10 RajeshAKumar

@wmmihaa, thank you so much! Saved us a lot of time. Cheers! ❤️

viniciusvillas avatar Aug 14 '23 16:08 viniciusvillas

@wmmihaa , thanks for code!

Just need to process corner-case of decrypting empty string.

So, instead of

                while (comparisonBytes[nullIndex] == (byte)0)
                    nullIndex--;

in Decrypt method we should check array boundaries:

                while (comparisonBytes[nullIndex] == (byte)0)
                {
                    nullIndex--;
                    if (nullIndex < 0)
                        break;
                }

Works like a charm!

And it seems like Bouncycastle.NetCore is unofficial package, so I've used official BouncyCastle.Cryptography

halex2005 avatar Jun 27 '24 07:06 halex2005