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
HMACSHA256 doesn't pad key when the secret is under 64 bytes #80180
Comments
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones Issue DetailsDescriptionThe
However I'm not seeing that play out in practice and the key remains as it is. If I then try to use it it is unable to validate the JWT. Padding it myself fixes the issue and the token can be validated. Reproduction StepsI've created the following repro: using System.Security.Cryptography;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifQ.LRuMOc2ie7o3belYB9TMC44fEdvH2laZ_sIxZKRM8yg";
var secret = "secret_value";
var tokenHandler = new JwtSecurityTokenHandler();
var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hmacKey = hmac.Key;
Console.WriteLine(hmacKey.Length);
// Array.Resize(ref hmacKey, 64); <-- uncomment for fix
// Console.WriteLine(hmacKey.Length);
var validationParameters = new TokenValidationParameters
{
ValidateLifetime = false,
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(hmacKey)
};
var result = await tokenHandler.ValidateTokenAsync(token, validationParameters);
Console.WriteLine(result.IsValid); <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.0" />
</ItemGroup>
</Project> Expected behaviorOutput should show Actual behaviorOutput shows Regression?Tried on .NET 6 and .NET 7 and fails on both. Known WorkaroundsManually resizing the key to a 64 byte array appears to work: Array.Resize(ref hmacKey, 64); ConfigurationNo response Other informationI had a peek around the relevant code to see if I might be missing something. Looking at the runtime/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HMACCommon.cs Lines 55 to 62 in 6c0c96c
That line of code hasn't changed in 7 years though so perhaps I'm barking up the wrong tree.
|
I can take a look to dig in to the details here. |
So, as a first observation, even .NET Framework does not zero-pad the key. This prints out "00". using System;
using System.Security.Cryptography;
byte[] key = new byte[1];
HMACSHA256 hmac = new HMACSHA256(key);
Console.WriteLine(BitConverter.ToString(hmac.Key)); The "zero pad the key" is an internal implementation of how HMAC works, which is what I think the documentation is trying to convey. We don't literally zero-pad the key. What I was more curious about was why you have to zero pad your key with the JWT library. After all, with the way HMAC works, this produces the same result: using System;
using System.Security.Cryptography;
using HMACSHA256 hmac = new HMACSHA256();
hmac.Key = new byte[] { 1 };
Console.WriteLine(Convert.ToHexString(hmac.ComputeHash("yabba dabba doo"u8.ToArray())));
hmac.Key = new byte[] { 1, 0, 0, 0, 0, 0 };
Console.WriteLine(Convert.ToHexString(hmac.ComputeHash("yabba dabba doo"u8.ToArray()))); But it's not that it thinks the key is invalid. When you are supplying the short token, it is actually throwing an exception. We need to throw a But now with: IdentityModelEventSource.ShowPII = true;
var result = await tokenHandler.ValidateTokenAsync(token, validationParameters);
Console.WriteLine(result.Exception.Message); we get:
Ah. So the library is trying to stop you from using what it thinks is a too-weak HS256 key. It thinks your key is 96 bits, which is right, as your key The zero padding is just circumventing the "your key is too small" check. So in summary, there is no behavior regression. The documentation is a little confusing around this, but the behavior observed goes far back in to .NET Framework. The behavioral difference observed in the JWT library is because the library is trying to be helpful by telling you your key is too short. Manually padding with zeros acts as an escape hatch if you really want to use a short HS256 key. |
Just to prove the point about the JWT library, if you change your |
Interesting, that makes sense. Thanks for the swift response and adjusting the messaging, that should help a lot! Would it be useful to document this apparent minimum-length requirement from the security check? I'm integrating an existing system so I'll stick with my padding workaround for now but I'll aim to move to a stronger secret in the future. |
I don't think it would hurt to raise an issue. Would you be interested in writing one up in the Azure SDK repository? I think the right place for documentation issues is https://github.com/Azure/azure-sdk-for-net/issues. If it isn't, I'm sure someone will help ferry it to the right location. |
I opened a question around documentation over at Azure/azure-sdk-for-net#33300. Summarizing:
I don't think there is any remaining work to perform for this issue. I'm going to close this out, but please feel free to re-open if you believe there are still issues that need to be addressed. |
Description
The
HMACSHA256(Byte[])
constructor saysHowever I'm not seeing that play out in practice and the key remains as it is. If I then try to use it it is unable to validate the JWT. Padding it myself fixes the issue and the token can be validated.
Reproduction Steps
I've created the following repro:
https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifQ.LRuMOc2ie7o3belYB9TMC44fEdvH2laZ_sIxZKRM8yg
Expected behavior
Output should show
True
since the secret is validActual behavior
Output shows
False
Regression?
Tried on .NET 6 and .NET 7 and fails on both.
Known Workarounds
Manually resizing the key to a 64 byte array appears to work:
Configuration
No response
Other information
I had a peek around the relevant code to see if I might be missing something. Looking at the
ChangeKeyImpl
code it seems like we only modify the key if it is longer than the secret and not when it is shorter.runtime/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HMACCommon.cs
Lines 55 to 62 in 6c0c96c
That line of code hasn't changed in 7 years though so perhaps I'm barking up the wrong tree.
The text was updated successfully, but these errors were encountered: