Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AES256GCM JWE encryption in .NET standard 2.0 using Bouncy Castle #207

Open
shaiArn opened this issue Nov 29, 2022 · 6 comments
Open

AES256GCM JWE encryption in .NET standard 2.0 using Bouncy Castle #207

shaiArn opened this issue Nov 29, 2022 · 6 comments

Comments

@shaiArn
Copy link

shaiArn commented Nov 29, 2022

Hey @dvsekhvalnov,

I'm trying to encode a JWT with JWE encryption using AES 256 GCM.
On Windows, it works great:
JWT.Encode(payload, jwk, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM, extraHeaders: header)
The tricky part is that I need it to run on iOS, and the AES 256 GCM algorithm used is using the bcrypt dll that does not exist on iOS.

Is it possible to use Bouncy Castle's implementation instead of the .NET core one?

@shaiArn shaiArn changed the title AES256GCM JWE encryption in .NET standard 2.0 using Bouncy Castle AES256GCM JWE encryption in .NET standard 2.0 using Bouncy Castle Nov 29, 2022
@dvsekhvalnov
Copy link
Owner

dvsekhvalnov commented Nov 29, 2022

Hi @shaiArn,

library is using managed AES-GCM implementation on NETSTANDARD 2.1 runtime. Should work just fine on .NetCore 3.0+

Please check out yourself: https://github.com/dvsekhvalnov/jose-jwt/blob/master/jose-jwt/crypto/AesGcmNetCore.cs

What exact error are you facing?

Answering your questions about Bouncy Castle. It's against library design goal to be zero-dependency. There are different reasons behind it.

On the other hand you can easily implement it yourself and register your own encryption implementation with:

   JWT.DefaultSettings.RegisterJwe(JweEncryption.A256GCM, YourBCImplementation);

If i ever have time, probably can publish alternate set of implementations as separate library too.

@shaiArn
Copy link
Author

shaiArn commented Nov 30, 2022

Hi @dvsekhvalnov, thank you for your answer.

Here is the full error:

DllNotFoundException: bcrypt.dll assembly:<unknown assembly> type:<unknown type> member:(null)
Jose.AesGcm.OpenAlgorithmProvider (System.String alg, System.String provider, System.String chainingMode) (at <e53f5c14391a4dae86321317d2821418>:0)
Jose.AesGcm.Encrypt (System.Byte[] key, System.Byte[] iv, System.Byte[] aad, System.Byte[] plainText) (at <e53f5c14391a4dae86321317d2821418>:0)
Jose.AesGcmEncryption.Encrypt (System.Byte[] aad, System.Byte[] plainText, System.Byte[] cek) (at <e53f5c14391a4dae86321317d2821418>:0)
Jose.JWE.EncryptBytes (System.Byte[] plaintext, System.Collections.Generic.IEnumerable`1[T] recipients, Jose.JweEncryption enc, System.Byte[] aad, Jose.SerializationMode mode, System.Nullable`1[T] compression, System.Collections.Generic.IDictionary`2[TKey,TValue] extraProtectedHeaders, System.Collections.Generic.IDictionary`2[TKey,TValue] unprotectedHeaders, Jose.JwtSettings settings) (at <e53f5c14391a4dae86321317d2821418>:0)
Jose.JWT.EncodeBytes (System.Byte[] payload, System.Object key, Jose.JweAlgorithm alg, Jose.JweEncryption enc, System.Nullable`1[T] compression, System.Collections.Generic.IDictionary`2[TKey,TValue] extraHeaders, Jose.JwtSettings settings) (at <e53f5c14391a4dae86321317d2821418>:0)
Jose.JWT.Encode (System.String payload, System.Object key, Jose.JweAlgorithm alg, Jose.JweEncryption enc, System.Nullable`1[T] compression, System.Collections.Generic.IDictionary`2[TKey,TValue] extraHeaders, Jose.JwtSettings settings) (at <e53f5c14391a4dae86321317d2821418>:0)
Jose.JWT.Encode (System.Object payload, System.Object key, Jose.JweAlgorithm alg, Jose.JweEncryption enc, System.Nullable`1[T] compression, System.Collections.Generic.IDictionary`2[TKey,TValue] extraHeaders, Jose.JwtSettings settings) (at <e53f5c14391a4dae86321317d2821418>:0)

Some more context and information

  1. The code is running as part of a Unity app that is meant to run as an iOS app
  2. When developing on a PC (Windows) - The code runs well.
  3. When running on a Mac/iOS device, we get the error above.
  4. When using A128CBC_HS256(or any other CBC algo) instead of A256GCM (or any other GCM algo), the error is not raised.

Do you think we there's a chance to use A256GCM without using a 3rd party dependency?

@dvsekhvalnov
Copy link
Owner

dvsekhvalnov commented Nov 30, 2022

Yeah, it looks it doesn't use .Netstandard 2.1 according to stack trace.

I'm not an expert on Unity itself, but quick googling gave me https://forum.unity.com/threads/unity-future-net-development-status.1092205/

Probably you can check if Unity support .Netstandard 2.1 runtime or any plans.

@shaiArn
Copy link
Author

shaiArn commented Dec 4, 2022

@dvsekhvalnov, thank you for your previous suggestions and for offering to help with this issue.

  1. I examined Unity's support for .Netstandard 2.1. and I found out that that the package manager, by default, imports the 1.4 version of this NuGet package, which I guess explains the original issue.

However, when I force it to use the 2.1. version I get the following error: TypeLoadException: Could not load type of field 'Jose.JsonMapper:SerializeOptions' (0) due to: Could not load file or assembly 'System.Text.Json.... I am now looking into it and trying to understand what might be causing this error.

  1. I am also working on registering my own encryption implementation using Bouncy Castle, but I am having some difficulty getting it to work properly. I do get a valid JWT, but it seems like the payload is not being encrypted well. I think it might have to do with handling the protected header part. But i'm not sure.

Here is the encrypt method of my implementation:

 public byte[][] Encrypt(byte[] aad, byte[] plainText, byte[] cek)
        {
            const int nonceLength = 12; // in bytes
            const int tagLenth = 16; // in bytes

            var nonce = new byte[nonceLength];
            RandomNumberGenerator.Fill(nonce);

            var plaintextBytes = plainText;
            var bcCiphertext = new byte[plaintextBytes.Length + tagLenth];

            var cipher = new GcmBlockCipher(new AesEngine());
            var parameters = new AeadParameters(new KeyParameter(cek), tagLenth * 8, nonce);
            cipher.Init(true, parameters);

            var offset = cipher.ProcessBytes(plaintextBytes, 0, plaintextBytes.Length, bcCiphertext, 0);
            cipher.DoFinal(bcCiphertext, offset);

            // Bouncy Castle includes the authentication tag in the ciphertext
            var ciphertext = new byte[plaintextBytes.Length];
            var tag = new byte[tagLenth];
            Buffer.BlockCopy(bcCiphertext, 0, ciphertext, 0, plaintextBytes.Length);
            Buffer.BlockCopy(bcCiphertext, plaintextBytes.Length, tag, 0, tagLenth);


            return new byte[][] { nonce, ciphertext, tag };
        }

(I used Scott Brady's blog post as a reference)

Is there anything that you see that I might have missed in my code? Any guidance would be greatly appreciated.

@dvsekhvalnov
Copy link
Owner

Hi @shaiArn ,

  1. Probably just install https://www.nuget.org/packages/System.Text.Json from nuget, if it can't be found (it should but..) ?

  2. I'll give you link to java version that using BouncyCastle for AESGCM encryption, hopefully it will be more useful than my comments without trying to run your code: (just in case it is under tag v3.4)

https://bitbucket.org/connect2id/nimbus-jose-jwt/src/3.4/src/main/java/com/nimbusds/jose/crypto/AESGCM.java

@shaiArn
Copy link
Author

shaiArn commented Dec 5, 2022

@dvsekhvalnov, thank you!

As for 1 - It seems like mono does not support AES GCM. So unless there's a workaround to it, I guess the Bouncy Castle lead is the one to follow.

Thank you for the Java version. If I figure it out, I'll share my solution here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants