Skip to content

Instantly share code, notes, and snippets.

@sandorfr
Last active March 3, 2021 12:56
Show Gist options
  • Save sandorfr/4039d540b6b552154522 to your computer and use it in GitHub Desktop.
Save sandorfr/4039d540b6b552154522 to your computer and use it in GitHub Desktop.
Jwt HmacSha256 aps.net 5 beta7 workaround
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace System.IdentityModel.Tokens
{
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
/// <summary>
/// Creates <see cref="SignatureProvider"/>s by specifying a <see cref="SecurityKey"/> and algorithm.
/// <para>Supports both <see cref="AsymmetricSecurityKey"/> and <see cref="SymmetricSecurityKey"/>.</para>
/// </summary>
public class MacSignatureProviderFactory: SignatureProviderFactory
{
private static Int32 minimumAsymmetricKeySizeInBitsForSigning = AbsoluteMinimumAsymmetricKeySizeInBitsForSigning;
private static Int32 minimumAsymmetricKeySizeInBitsForVerifying = AbsoluteMinimumAsymmetricKeySizeInBitsForVerifying;
private static Int32 minimumSymmetricKeySizeInBits = AbsoluteMinimumSymmetricKeySizeInBits;
static MacSignatureProviderFactory()
{
Default = new MacSignatureProviderFactory();
}
/// <summary>
/// Creates a <see cref="SignatureProvider"/> that supports the <see cref="SecurityKey"/> and algorithm.
/// </summary>
/// <param name="key">
/// The <see cref="SecurityKey"/> to use for signing.
/// </param>
/// <param name="algorithm">
/// The algorithm to use for signing.
/// </param>
/// <exception cref="ArgumentNullException">
/// 'key' is null.
/// </exception>
/// <exception cref="ArgumentNullException">
/// 'algorithm' is null.
/// </exception>
/// <exception cref="ArgumentException">
/// 'algorithm' contains only whitespace.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// '<see cref="AsymmetricSecurityKey"/>' is smaller than <see cref="MinimumAsymmetricKeySizeInBitsForSigning"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// '<see cref="SymmetricSecurityKey"/>' is smaller than <see cref="MinimumSymmetricKeySizeInBits"/>.
/// </exception>
/// <exception cref="ArgumentException">
/// '<see cref="SecurityKey"/>' is not a <see cref="AsymmetricSecurityKey"/> or a <see cref="SymmetricSecurityKey"/>.
/// </exception>
/// <remarks>
/// AsymmetricSignatureProviders require access to a PrivateKey for Signing.
/// </remarks>
/// <returns>
/// The <see cref="SignatureProvider"/>.
/// </returns>
public override SignatureProvider CreateForSigning(SecurityKey key, string algorithm)
{
return CreateProvider(key, algorithm, true);
}
/// <summary>
/// Returns a <see cref="SignatureProvider"/> instance supports the <see cref="SecurityKey"/> and algorithm.
/// </summary>
/// <param name="key">
/// The <see cref="SecurityKey"/> to use for signing.
/// </param>
/// <param name="algorithm">
/// The algorithm to use for signing.
/// </param>
/// <exception cref="ArgumentNullException">
/// 'key' is null.
/// </exception>
/// <exception cref="ArgumentNullException">
/// 'algorithm' is null.
/// </exception>
/// <exception cref="ArgumentException">
/// 'algorithm' contains only whitespace.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// '<see cref="AsymmetricSecurityKey"/>' is smaller than <see cref="MinimumAsymmetricKeySizeInBitsForVerifying"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// '<see cref="SymmetricSecurityKey"/>' is smaller than <see cref="MinimumSymmetricKeySizeInBits"/>.
/// </exception>
/// <exception cref="ArgumentException">
/// '<see cref="SecurityKey"/>' is not a <see cref="AsymmetricSecurityKey"/> or a <see cref="SymmetricSecurityKey"/>.
/// </exception>
/// <returns>
/// The <see cref="SignatureProvider"/>.
/// </returns>
public override SignatureProvider CreateForVerifying(SecurityKey key, string algorithm)
{
return CreateProvider(key, algorithm, false);
}
/// <summary>
/// When finished with a <see cref="SignatureProvider"/> call this method for cleanup. The default behavior is to call <see cref="SignatureProvider.Dispose(bool)"/>
/// </summary>
/// <param name="signatureProvider"><see cref="SignatureProvider"/> to be released.</param>
public override void ReleaseProvider(SignatureProvider signatureProvider)
{
if (signatureProvider != null)
{
signatureProvider.Dispose();
}
}
private static SignatureProvider CreateProvider(SecurityKey key, string algorithm, bool willCreateSignatures)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (algorithm == null)
{
throw new ArgumentNullException("algorithm");
}
if (string.IsNullOrWhiteSpace(algorithm))
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10002, "algorithm "));
}
AsymmetricSecurityKey asymmetricKey = key as AsymmetricSecurityKey;
if (asymmetricKey != null)
{
if (willCreateSignatures)
{
if (asymmetricKey.KeySize < MinimumAsymmetricKeySizeInBitsForSigning)
{
throw new ArgumentOutOfRangeException("key.KeySize", asymmetricKey.KeySize, string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10630, key.GetType(), MinimumAsymmetricKeySizeInBitsForSigning));
}
}
if (asymmetricKey.KeySize < MinimumAsymmetricKeySizeInBitsForVerifying)
{
throw new ArgumentOutOfRangeException("key.KeySize", asymmetricKey.KeySize, string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10631, key.GetType(), MinimumAsymmetricKeySizeInBitsForVerifying));
}
return new AsymmetricSignatureProvider(asymmetricKey, algorithm, willCreateSignatures);
}
SymmetricSecurityKey symmetricKey = key as SymmetricSecurityKey;
if (symmetricKey != null)
{
if (symmetricKey.KeySize < MinimumSymmetricKeySizeInBits)
{
throw new ArgumentOutOfRangeException("key.KeySize", key.KeySize, string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10603, key.GetType(), MinimumSymmetricKeySizeInBits));
}
return new MacSymmetricSignatureProvider(symmetricKey, algorithm);
}
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10600, typeof(SignatureProvider).ToString(), typeof(SecurityKey), typeof(AsymmetricSecurityKey), typeof(SymmetricSecurityKey), key.GetType()));
}
}
}
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Threading.Tasks;
namespace System.IdentityModel.Tokens
{
public class MacSymmetricSecurityKey : SymmetricSecurityKey
{
public MacSymmetricSecurityKey(byte[] key) : base(key) { }
public override SignatureProvider GetSignatureProvider(string algorithm, bool forSigning)
{
var factory = this.SignatureProviderFactory ?? SignatureProviderFactory.Default;
if (forSigning)
{
return factory.CreateForSigning(this, algorithm);
}
else
{
return factory.CreateForVerifying(this, algorithm);
}
}
}
}
namespace System.IdentityModel.Tokens
{
using Microsoft.IdentityModel.Logging;
using System;
using System.Diagnostics.Tracing;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
/// <summary>
/// Provides signing and verifying operations using a <see cref="SymmetricSecurityKey"/> and specifying an algorithm.
/// </summary>
public class MacSymmetricSignatureProvider : SignatureProvider
{
private static byte[] bytesA = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
private static byte[] bytesB = new byte[] { 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
private bool disposed;
private KeyedHashAlgorithm keyedHash;
/// <summary>
/// Initializes a new instance of the <see cref="SymmetricSignatureProvider"/> class that uses an <see cref="SymmetricSecurityKey"/> to create and / or verify signatures over a array of bytes.
/// </summary>
/// <param name="key">The <see cref="SymmetricSecurityKey"/> used for signing.</param>
/// <param name="algorithm">The signature algorithm to use.</param>
/// <exception cref="ArgumentNullException">'key' is null.</exception>
/// <exception cref="ArgumentNullException">'algorithm' is null.</exception>
/// <exception cref="ArgumentException">'algorithm' contains only whitespace.</exception>
/// <exception cref="ArgumentOutOfRangeException">'<see cref="SymmetricSecurityKey"/>.KeySize' is smaller than <see cref="SignatureProviderFactory.MinimumSymmetricKeySizeInBits"/>.</exception>
/// <exception cref="InvalidOperationException"><see cref="SymmetricSecurityKey.GetKeyedHashAlgorithm"/> throws.</exception>
/// <exception cref="InvalidOperationException"><see cref="SymmetricSecurityKey.GetKeyedHashAlgorithm"/> returns null.</exception>
/// <exception cref="InvalidOperationException"><see cref="SymmetricSecurityKey.GetSymmetricKey"/> throws.</exception>
public MacSymmetricSignatureProvider(SymmetricSecurityKey key, string algorithm)
{
if (key == null)
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10000, GetType() + ": key"), typeof(ArgumentNullException), EventLevel.Verbose);
}
if (!IsSupportedAlgorithm(algorithm))
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10640, algorithm ?? "null"), typeof(InvalidOperationException), EventLevel.Error);
}
if (key.KeySize < SignatureProviderFactory.MinimumSymmetricKeySizeInBits)
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10603, key.GetType(), SignatureProviderFactory.MinimumSymmetricKeySizeInBits + ", KeySize: " + key.KeySize), typeof(ArgumentOutOfRangeException), EventLevel.Error);
}
this.keyedHash = GetKeyedHashAlgorithm(algorithm);
try
{
this.keyedHash.Key = key.Key;
}
catch (Exception ex)
{
if (DiagnosticUtility.IsFatal(ex))
{
throw;
}
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10634, algorithm, key, ex), typeof(InvalidOperationException), EventLevel.Error);
}
}
public override bool IsSupportedAlgorithm(string algorithm)
{
if (string.IsNullOrWhiteSpace(algorithm))
return false;
switch (algorithm)
{
case SecurityAlgorithms.HmacSha1Signature:
case SecurityAlgorithms.HmacSha256Signature:
return true;
default:
return false;
}
}
protected virtual KeyedHashAlgorithm GetKeyedHashAlgorithm(string algorithm)
{
if (string.IsNullOrWhiteSpace(algorithm))
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10000, GetType() + ": algorithm"), typeof(ArgumentNullException), EventLevel.Verbose);
}
switch (algorithm)
{
case SecurityAlgorithms.HmacSha1Signature:
return new HMACSHA1();
case SecurityAlgorithms.HmacSha256Signature:
return new HMACSHA256();
default:
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10640, algorithm), typeof(ArgumentOutOfRangeException), EventLevel.Error);
return null;
}
}
}
/// <summary>
/// Produces a signature over the 'input' using the <see cref="SymmetricSecurityKey"/> and 'algorithm' passed to <see cref="SymmetricSignatureProvider( SymmetricSecurityKey, string )"/>.
/// </summary>
/// <param name="input">bytes to sign.</param>
/// <returns>signed bytes</returns>
/// <exception cref="ArgumentNullException">'input' is null. </exception>
/// <exception cref="ArgumentException">'input.Length' == 0. </exception>
/// <exception cref="ObjectDisposedException"><see cref="Dispose(bool)"/> has been called.</exception>
/// <exception cref="InvalidOperationException"><see cref="KeyedHashAlgorithm"/> is null. This can occur if a derived type deletes it or does not create it.</exception>
public override byte[] Sign(byte[] input)
{
if (input == null)
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10000, GetType() + ": input"), typeof(ArgumentNullException), EventLevel.Verbose);
}
if (input.Length == 0)
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10624), typeof(ArgumentException), EventLevel.Error);
}
if (this.disposed)
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, typeof(SymmetricSignatureProvider).ToString()), typeof(ObjectDisposedException), EventLevel.Error);
}
if (this.keyedHash == null)
{
LogHelper.Throw(ErrorMessages.IDX10623, typeof(InvalidOperationException), EventLevel.Error);
}
IdentityModelEventSource.Logger.WriteInformation("Creating signature using the input");
return this.keyedHash.ComputeHash(input);
}
/// <summary>
/// Verifies that a signature created over the 'input' matches the signature. Using <see cref="SymmetricSecurityKey"/> and 'algorithm' passed to <see cref="SymmetricSignatureProvider( SymmetricSecurityKey, string )"/>.
/// </summary>
/// <param name="input">bytes to verify.</param>
/// <param name="signature">signature to compare against.</param>
/// <returns>true if computed signature matches the signature parameter, false otherwise.</returns>
/// <exception cref="ArgumentNullException">'input' is null.</exception>
/// <exception cref="ArgumentNullException">'signature' is null.</exception>
/// <exception cref="ArgumentException">'input.Length' == 0.</exception>
/// <exception cref="ArgumentException">'signature.Length' == 0. </exception>
/// <exception cref="ObjectDisposedException"><see cref="Dispose(bool)"/> has been called.</exception>
/// <exception cref="InvalidOperationException">if the internal <see cref="KeyedHashAlgorithm"/> is null. This can occur if a derived type deletes it or does not create it.</exception>
public override bool Verify(byte[] input, byte[] signature)
{
if (input == null)
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10000, GetType() + ": input"), typeof(ArgumentNullException), EventLevel.Verbose);
}
if (signature == null)
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10000, GetType() + ": signature"), typeof(ArgumentNullException), EventLevel.Verbose);
}
if (input.Length == 0)
{
LogHelper.Throw(ErrorMessages.IDX10625, typeof(ArgumentException), EventLevel.Error);
}
if (signature.Length == 0)
{
LogHelper.Throw(ErrorMessages.IDX10626, typeof(ArgumentException), EventLevel.Error);
}
if (this.disposed)
{
LogHelper.Throw(string.Format(CultureInfo.InvariantCulture, typeof(SymmetricSignatureProvider).ToString()), typeof(ObjectDisposedException), EventLevel.Error);
}
if (this.keyedHash == null)
{
LogHelper.Throw(ErrorMessages.IDX10623, typeof(InvalidOperationException), EventLevel.Error);
}
IdentityModelEventSource.Logger.WriteInformation("Comparing the signature created over the input with the token signature");
return AreEqual(signature, this.keyedHash.ComputeHash(input));
}
#region IDisposable Members
/// <summary>
/// Disposes of internal components.
/// </summary>
/// <param name="disposing">true, if called from Dispose(), false, if invoked inside a finalizer.</param>
protected override void Dispose(bool disposing)
{
if (!this.disposed)
{
this.disposed = true;
if (disposing)
{
if (this.keyedHash != null)
{
this.keyedHash.Dispose();
this.keyedHash = null;
}
}
}
}
#endregion
/// <summary>
/// Compares two byte arrays for equality. Hash size is fixed normally it is 32 bytes.
/// The attempt here is to take the same time if an attacker shortens the signature OR changes some of the signed contents.
/// </summary>
/// <param name="a">
/// One set of bytes to compare.
/// </param>
/// <param name="b">
/// The other set of bytes to compare with.
/// </param>
/// <returns>
/// true if the bytes are equal, false otherwise.
/// </returns>
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
private static bool AreEqual(byte[] a, byte[] b)
{
int result = 0;
byte[] a1, a2;
if (((null == a) || (null == b))
|| (a.Length != b.Length))
{
a1 = bytesA;
a2 = bytesB;
}
else
{
a1 = a;
a2 = b;
}
for (int i = 0; i < a1.Length; i++)
{
result |= a1[i] ^ a2[i];
}
return result == 0;
}
}
}
public partial class Startup
{
public void ConfigureServices(IServiceCollection services) {
System.IdentityModel.Tokens.SecurityKey securityKey;
System.IdentityModel.Tokens.SigningCredentials signingCredentials;
System.IdentityModel.Tokens.SignatureProviderFactory factory;
factory = new System.IdentityModel.Tokens.MacSignatureProviderFactory();
securityKey = new System.IdentityModel.Tokens.MacSymmetricSecurityKey(Convert.FromBase64String(Configuration["Authentication:Jwt:Key"]))
{
SignatureProviderFactory = factory
};
signingCredentials = new System.IdentityModel.Tokens.SigningCredentials(securityKey,
System.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature,
System.IdentityModel.Tokens.SecurityAlgorithms.Sha256Digest);
System.IdentityModel.Tokens.SignatureProviderFactory.Default = factory;
services.Configure<OAuthBearerAuthenticationOptions>(bearer =>
{
bearer.TokenValidationParameters.ValidAudience = "xxxxxx";
bearer.TokenValidationParameters.ValidIssuer = "xxxxxxxx";
bearer.TokenValidationParameters.IssuerSigningKey = securityKey;
bearer.AutomaticAuthentication = true;
bearer.SecurityTokenValidators = new List<ISecurityTokenValidator> { new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler() { SignatureProviderFactory = factory } };
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment