AzureAD / azure-activedirectory-identitymodel-extensions-for-dotnet

IdentityModel extensions for .Net
MIT License
1.06k stars 401 forks source link

[Bug] CryptographicException 'The buffer supplied to a function was too small' with specific 2048 bit RSA key and certain library versions on Windows Server 2016 #2704

Open MattBussing opened 4 months ago

MattBussing commented 4 months ago

Hello. Thanks for taking the time to review this issue! I found a very odd bug. I get a CryptographicException "The buffer supplied to a function was too small" when the following condition are true:

  1. I use a specific 2048 bit RSA key. All other 2048 bit keys I've tested don't fail in this manner. This key is still valid as it works to encrypt and decrypt data.
  2. I run my code on Windows Server 2016. I don't get this error on Windows 11 or Windows Server 22.
  3. I call CreateEncodedJwt using RsaSecurityKey key new(rsa.ExportParameters(true)). I don't get this error using RsaSecurityKey key = new(rsa).
  4. I use version 7.6.0 or 7.5.2 of the following libraries. I'm sure it is on more versions. Those are the only ones I tested. I found this bug when upgrading from 5.6.0. It doesn't happen on 5.6.0.
    • Microsoft.IdentityModel.JsonWebTokens
    • Microsoft.IdentityModel.Logging
    • Microsoft.IdentityModel.Protocols.OpenIdConnect
    • Microsoft.IdentityModel.Tokens
    • System.IdentityModel.Tokens.Jwt

You can reproduce this error with https://github.com/MattBussing/IdentityModelRepro. Granted you don't have the RSA key to generate the issue and I can't share the key for security reasons. But, when you run the following commands it will demonstrate the issue.

Note this is running in .NET Framework 4.8.

fails

BufferRepro.exe --path "key.xml" --use-bad-version true

passes

BufferRepro.exe --path "key.xml" --use-bad-version false

Error

BufferRepro.exe : Unhandled exception: System.Reflection.TargetInvocationException: 
Exception has been thrown by the target of an invocation. ---> System.Security.Cryptography.CryptographicException: The buffer supplied to a function was too small.
At line:1 char:1
+ BufferRepro.exe --path "C:\ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (Unhandled excep... was too small.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
   at System.Security.Cryptography.NCryptNative.SignHash[T](SafeNCryptKeyHandle key, Byte[] hash, T& paddingInfo, AsymmetricPaddingMode paddingMode, NCryptHashSigner`1 signer)
   at System.Security.Cryptography.NCryptNative.SignHashPkcs1(SafeNCryptKeyHandle key, Byte[] hash, String hashAlgorithm)
   at System.Security.Cryptography.RSACng.SignHash(Byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider.Sign(Byte[] input)
   at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwtSecurityTokenPrivate(String issuer, String audience, ClaimsIdentity subject, Nullable`1 notBefore, Nullable`1 expires, Nullable`1 issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, IDictionary`2 claimCollection, String tokenType, IDictionary`2 additionalHeaderClaims, IDictionary`2 additionalInnerHeaderClaims)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateEncodedJwt(String issuer, String audience, ClaimsIdentity subject, Nullable`1 notBefore, Nullable`1 expires, Nullable`1 issuedAt, SigningCredentials signingCredentials)
   at BufferRepro.Program.TestKey(String fileContent, Boolean useBadVersion) in IdentityModelRepro\BufferRepro\Program.cs:line 41
   at BufferRepro.Program.Main(String path, Boolean useBadVersion) in IdentityModelRepro\BufferRepro\Program.cs:line 23
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.CommandLine.NamingConventionBinder.ModelBindingCommandHandler.<InvokeAsync>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass4_0.<<BuildInvocationChain>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass17_0.<<UseParseErrorReporting>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass15_0.<<AddMiddleware>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass12_0.<<UseHelp>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass22_0.<<UseVersionOption>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass19_0.<<UseTypoCorrections>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseSuggestDirective>b__18_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass16_0.<<UseParseDirective>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<RegisterWithDotnetSuggest>b__5_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass8_0.<<UseExceptionHandler>b__0>d.MoveNext()

Are there similar issues?

Kind of. These have the same error message "The buffers supplied to a function was too small", but they aren't using WinHTTP.

Is this caused by 0 padding?

https://stackoverflow.com/a/39217099/9728299

All the RSA params do not end or begin with a byte of zero.

Is the RSA key valid?

Yes. It was able to encrypt and decrypt data correctly.

Does it work if we use Microsoft.IdentityModel.JsonWebTokens instead of System.IdentityModel.Tokens.Jwt?

No. In fact they both take in RSACryptoServiceProvider, which uses Windows' native crypto libraries. So, it breaks on both.

MattBussing commented 3 months ago

Following up on this. Is there any updates?

MattBussing commented 3 months ago

Pinging @davidsh since you worked on https://github.com/dotnet/runtime/issues/17005#issuecomment-308746803

jennyf19 commented 2 months ago

Thanks @MattBussing for the repro and all the details. We will take a look.

MattBussing commented 2 months ago

@jennyf19 Thank you for taking a look!