Last active
March 3, 2021 12:56
-
-
Save sandorfr/4039d540b6b552154522 to your computer and use it in GitHub Desktop.
Jwt HmacSha256 aps.net 5 beta7 workaround
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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())); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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