Browse Source

Reference Microsoft.Bcl.Memory on .NET Framework and use Base64Url instead of Base64UrlEncoder

pull/2491/head
Kévin Chalet 3 months ago
parent
commit
d41aeb28fc
  1. 1
      Directory.Packages.props
  2. 8
      src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs
  3. 6
      src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlers.Protection.cs
  4. 8
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs
  5. 6
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs
  6. 4
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpContext.cs
  7. 1
      src/OpenIddict.Client/OpenIddict.Client.csproj
  8. 10
      src/OpenIddict.Client/OpenIddictClientConfiguration.cs
  9. 3
      src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs
  10. 23
      src/OpenIddict.Client/OpenIddictClientHandlers.cs
  11. 6
      src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs
  12. 1
      src/OpenIddict.Server/OpenIddict.Server.csproj
  13. 7
      src/OpenIddict.Server/OpenIddictServerConfiguration.cs
  14. 15
      src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs
  15. 4
      src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
  16. 5
      src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs
  17. 11
      src/OpenIddict.Server/OpenIddictServerHandlers.cs
  18. 4
      src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionHandlers.Protection.cs
  19. 4
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpContext.cs
  20. 1
      src/OpenIddict.Validation/OpenIddict.Validation.csproj
  21. 3
      src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs
  22. 3
      src/OpenIddict.Validation/OpenIddictValidationHandlers.cs
  23. 1
      test/OpenIddict.Client.Tests/OpenIddictClientConfigurationTests.cs
  24. 1
      test/OpenIddict.Core.Tests/Managers/OpenIddictApplicationManagerTests.cs
  25. 11
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs
  26. 1
      test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs

1
Directory.Packages.props

@ -25,6 +25,7 @@
<PackageVersion Include="Microsoft.AspNetCore.DataProtection" Version="10.0.8" /> <PackageVersion Include="Microsoft.AspNetCore.DataProtection" Version="10.0.8" />
<PackageVersion Include="Microsoft.Bcl.Cryptography" Version="10.0.8" /> <PackageVersion Include="Microsoft.Bcl.Cryptography" Version="10.0.8" />
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" /> <PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
<PackageVersion Include="Microsoft.Bcl.Memory" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.8" />

8
src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs

@ -5,6 +5,7 @@
*/ */
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
@ -18,7 +19,6 @@ using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Properties = OpenIddict.Client.AspNetCore.OpenIddictClientAspNetCoreConstants.Properties; using Properties = OpenIddict.Client.AspNetCore.OpenIddictClientAspNetCoreConstants.Properties;
@ -421,7 +421,7 @@ public static partial class OpenIddictClientAspNetCoreHandlers
try try
{ {
// Extract the payload and validate the version marker. // Extract the payload and validate the version marker.
var payload = Base64UrlEncoder.DecodeBytes(value); var payload = Base64Url.DecodeFromChars(value);
if (payload.Length < (1 + sizeof(uint)) || payload[0] is not 0x01) if (payload.Length < (1 + sizeof(uint)) || payload[0] is not 0x01)
{ {
context.Reject( context.Reject(
@ -818,7 +818,7 @@ public static partial class OpenIddictClientAspNetCoreHandlers
Debug.Assert(written == count, SR.FormatID4016(written, count)); Debug.Assert(written == count, SR.FormatID4016(written, count));
// Add the correlation cookie to the response headers. // Add the correlation cookie to the response headers.
response.Cookies.Append(name, Base64UrlEncoder.Encode(payload), options); response.Cookies.Append(name, Base64Url.EncodeToString(payload), options);
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
@ -1041,7 +1041,7 @@ public static partial class OpenIddictClientAspNetCoreHandlers
Debug.Assert(written == count, SR.FormatID4016(written, count)); Debug.Assert(written == count, SR.FormatID4016(written, count));
// Add the correlation cookie to the response headers. // Add the correlation cookie to the response headers.
response.Cookies.Append(name, Base64UrlEncoder.Encode(payload), options); response.Cookies.Append(name, Base64Url.EncodeToString(payload), options);
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }

6
src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlers.Protection.cs

@ -4,12 +4,12 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Client.DataProtection.OpenIddictClientDataProtectionConstants.Purposes; using static OpenIddict.Client.DataProtection.OpenIddictClientDataProtectionConstants.Purposes;
using static OpenIddict.Client.OpenIddictClientHandlers.Protection; using static OpenIddict.Client.OpenIddictClientHandlers.Protection;
using Schemes = OpenIddict.Client.DataProtection.OpenIddictClientDataProtectionConstants.Purposes.Schemes; using Schemes = OpenIddict.Client.DataProtection.OpenIddictClientDataProtectionConstants.Purposes.Schemes;
@ -134,7 +134,7 @@ public static partial class OpenIddictClientDataProtectionHandlers
try try
{ {
using var buffer = new MemoryStream(protector.Unprotect(Base64UrlEncoder.DecodeBytes(context.Token))); using var buffer = new MemoryStream(protector.Unprotect(Base64Url.DecodeFromChars(context.Token)));
using var reader = new BinaryReader(buffer); using var reader = new BinaryReader(buffer);
// Note: since the data format relies on a data protector using different "purposes" strings // Note: since the data format relies on a data protector using different "purposes" strings
@ -247,7 +247,7 @@ public static partial class OpenIddictClientDataProtectionHandlers
_options.CurrentValue.Formatter.WriteToken(writer, context.Principal); _options.CurrentValue.Formatter.WriteToken(writer, context.Principal);
context.Token = Base64UrlEncoder.Encode(protector.Protect(buffer.ToArray())); context.Token = Base64Url.EncodeToString(protector.Protect(buffer.ToArray()));
context.Logger.LogTrace(6016, SR.GetResourceString(SR.ID6016), context.TokenType, context.Logger.LogTrace(6016, SR.GetResourceString(SR.ID6016), context.TokenType,
context.Token, context.Principal.Claims); context.Token, context.Principal.Claims);

8
src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs

@ -5,6 +5,7 @@
*/ */
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
@ -15,7 +16,6 @@ using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens;
using Owin; using Owin;
using static OpenIddict.Client.Owin.OpenIddictClientOwinConstants; using static OpenIddict.Client.Owin.OpenIddictClientOwinConstants;
using Properties = OpenIddict.Client.Owin.OpenIddictClientOwinConstants.Properties; using Properties = OpenIddict.Client.Owin.OpenIddictClientOwinConstants.Properties;
@ -432,7 +432,7 @@ public static partial class OpenIddictClientOwinHandlers
try try
{ {
// Extract the payload and validate the version marker. // Extract the payload and validate the version marker.
var payload = Base64UrlEncoder.DecodeBytes(value); var payload = Base64Url.DecodeFromChars(value);
if (payload.Length < (1 + sizeof(uint)) || payload[0] is not 0x01) if (payload.Length < (1 + sizeof(uint)) || payload[0] is not 0x01)
{ {
context.Reject( context.Reject(
@ -846,7 +846,7 @@ public static partial class OpenIddictClientOwinHandlers
_options.CurrentValue.CookieOptions); _options.CurrentValue.CookieOptions);
// Add the correlation cookie to the response headers. // Add the correlation cookie to the response headers.
manager.AppendResponseCookie(response.Context, name, Base64UrlEncoder.Encode(payload), new CookieOptions manager.AppendResponseCookie(response.Context, name, Base64Url.EncodeToString(payload), new CookieOptions
{ {
Domain = options.Domain, Domain = options.Domain,
HttpOnly = options.HttpOnly, HttpOnly = options.HttpOnly,
@ -1090,7 +1090,7 @@ public static partial class OpenIddictClientOwinHandlers
_options.CurrentValue.CookieOptions); _options.CurrentValue.CookieOptions);
// Add the correlation cookie to the response headers. // Add the correlation cookie to the response headers.
manager.AppendResponseCookie(response.Context, name, Base64UrlEncoder.Encode(payload), new CookieOptions manager.AppendResponseCookie(response.Context, name, Base64Url.EncodeToString(payload), new CookieOptions
{ {
Domain = options.Domain, Domain = options.Domain,
HttpOnly = options.HttpOnly, HttpOnly = options.HttpOnly,

6
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.ComponentModel; using System.ComponentModel;
using System.IO.Pipes; using System.IO.Pipes;
using System.Net; using System.Net;
@ -14,7 +15,6 @@ using System.Text;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Client.SystemIntegration.OpenIddictClientSystemIntegrationAuthenticationMode; using static OpenIddict.Client.SystemIntegration.OpenIddictClientSystemIntegrationAuthenticationMode;
namespace OpenIddict.Client.SystemIntegration; namespace OpenIddict.Client.SystemIntegration;
@ -96,13 +96,13 @@ public sealed class OpenIddictClientSystemIntegrationConfiguration : IConfigureO
// Note: only the left-most half of the hash is used to limit the length of the resulting discriminator, // Note: only the left-most half of the hash is used to limit the length of the resulting discriminator,
// which is required on platforms like macOS, where the name of pipes is always prefixed with a static part // which is required on platforms like macOS, where the name of pipes is always prefixed with a static part
// (e.g /var/folders/5j/jjxtct5j1gvg35z6sdh2fz0w0000gn/T/CoreFxPipe_) and must not exceed 104 characters. // (e.g /var/folders/5j/jjxtct5j1gvg35z6sdh2fz0w0000gn/T/CoreFxPipe_) and must not exceed 104 characters.
options.ApplicationDiscriminator = Base64UrlEncoder.Encode(digest, 0, digest.Length / 2); options.ApplicationDiscriminator = Base64Url.EncodeToString(digest.AsSpan(0, digest.Length / 2));
} }
// If no explicit instance identifier was specified, use a 96-bit random identifier. // If no explicit instance identifier was specified, use a 96-bit random identifier.
if (string.IsNullOrEmpty(options.InstanceIdentifier)) if (string.IsNullOrEmpty(options.InstanceIdentifier))
{ {
options.InstanceIdentifier = Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(count: 96 / 8)); options.InstanceIdentifier = Base64Url.EncodeToString(RandomNumberGenerator.GetBytes(count: 96 / 8));
} }
// If no explicit pipe name was specified, build one using the application discriminator. // If no explicit pipe name was specified, build one using the application discriminator.

4
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpContext.cs

@ -4,11 +4,11 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.ComponentModel; using System.ComponentModel;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Client.SystemNetHttp; namespace OpenIddict.Client.SystemNetHttp;
@ -61,6 +61,6 @@ public sealed class OpenIddictClientSystemNetHttpContext
algorithm.TransformFinalBlock([], 0, 0); algorithm.TransformFinalBlock([], 0, 0);
return Base64UrlEncoder.Encode(algorithm.Hash); return Base64Url.EncodeToString(algorithm.Hash);
} }
} }

1
src/OpenIddict.Client/OpenIddict.Client.csproj

@ -26,6 +26,7 @@ To use the client feature on ASP.NET Core or OWIN/Katana, reference the OpenIddi
<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' "> <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">
<PackageReference Include="BouncyCastle.Cryptography" /> <PackageReference Include="BouncyCastle.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Cryptography" /> <PackageReference Include="Microsoft.Bcl.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Memory" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

10
src/OpenIddict.Client/OpenIddictClientConfiguration.cs

@ -4,11 +4,11 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -178,14 +178,14 @@ public sealed class OpenIddictClientConfiguration : IPostConfigureOptions<OpenId
Debug.Assert(parameters.Q.X is not null, SR.GetResourceString(SR.ID4004)); Debug.Assert(parameters.Q.X is not null, SR.GetResourceString(SR.ID4004));
// Only use the 40 first chars of the base64url-encoded X coordinate. // Only use the 40 first chars of the base64url-encoded X coordinate.
var identifier = Base64UrlEncoder.Encode(parameters.Q.X); var identifier = Base64Url.EncodeToString(parameters.Q.X);
return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant(); return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant();
} }
static string GetMLDsaSecurityKeyIdentifier(MlDsaSecurityKey key) static string GetMLDsaSecurityKeyIdentifier(MlDsaSecurityKey key)
{ {
// Only use the 40 first chars of the base64url-encoded SHA256 of the ML-DSA public key. // Only use the 40 first chars of the base64url-encoded SHA256 of the ML-DSA public key.
var identifier = Base64UrlEncoder.Encode(SHA256.HashData(key.MLDsa.ExportMLDsaPublicKey())); var identifier = Base64Url.EncodeToString(SHA256.HashData(key.MLDsa.ExportMLDsaPublicKey()));
return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant(); return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant();
} }
@ -202,7 +202,7 @@ public sealed class OpenIddictClientConfiguration : IPostConfigureOptions<OpenId
} }
// Only use the 40 first chars of the base64url-encoded modulus. // Only use the 40 first chars of the base64url-encoded modulus.
var identifier = Base64UrlEncoder.Encode(parameters.Modulus); var identifier = Base64Url.EncodeToString(parameters.Modulus);
return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant(); return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant();
} }
} }
@ -222,7 +222,7 @@ public sealed class OpenIddictClientConfiguration : IPostConfigureOptions<OpenId
algorithm.TransformFinalBlock([], 0, 0); algorithm.TransformFinalBlock([], 0, 0);
return Base64UrlEncoder.Encode(algorithm.Hash); return Base64Url.EncodeToString(algorithm.Hash);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
static void TransformBlock(HashAlgorithm algorithm, string input) static void TransformBlock(HashAlgorithm algorithm, string input)

3
src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
@ -1183,7 +1184,7 @@ public static partial class OpenIddictClientHandlers
if (context.IsReferenceToken) if (context.IsReferenceToken)
{ {
descriptor.ReferenceId = Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(count: 256 / 8)); descriptor.ReferenceId = Base64Url.EncodeToString(RandomNumberGenerator.GetBytes(count: 256 / 8));
} }
await _tokenManager.UpdateAsync(token, descriptor); await _tokenManager.UpdateAsync(token, descriptor);

23
src/OpenIddict.Client/OpenIddictClientHandlers.cs

@ -19,6 +19,7 @@ using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Abstractions.OpenIddictExceptions; using static OpenIddict.Abstractions.OpenIddictExceptions;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Buffers.Text;
#if !NET #if !NET
using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Digests;
@ -1942,7 +1943,7 @@ public static partial class OpenIddictClientHandlers
case { FrontchannelIdentityTokenNonce: string left, StateTokenNonce: string right } when case { FrontchannelIdentityTokenNonce: string left, StateTokenNonce: string right } when
!CryptographicOperations.FixedTimeEquals( !CryptographicOperations.FixedTimeEquals(
left: MemoryMarshal.AsBytes(left.AsSpan()), // The nonce in the identity token is already hashed. left: MemoryMarshal.AsBytes(left.AsSpan()), // The nonce in the identity token is already hashed.
right: MemoryMarshal.AsBytes(Base64UrlEncoder.Encode( right: MemoryMarshal.AsBytes(Base64Url.EncodeToString(
SHA256.HashData(Encoding.UTF8.GetBytes(right))).AsSpan())): SHA256.HashData(Encoding.UTF8.GetBytes(right))).AsSpan())):
context.Logger.LogWarning(6210, SR.GetResourceString(SR.ID6210)); context.Logger.LogWarning(6210, SR.GetResourceString(SR.ID6210));
@ -2072,7 +2073,7 @@ public static partial class OpenIddictClientHandlers
// Warning: only the left-most half of the access token and authorization code digest is used. // Warning: only the left-most half of the access token and authorization code digest is used.
// See http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken for more information. // See http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken for more information.
return Base64UrlEncoder.Encode(hash, 0, hash.Length / 2).AsSpan(); return Base64Url.EncodeToString(hash.AsSpan(0, hash.Length / 2)).AsSpan();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -3678,7 +3679,7 @@ public static partial class OpenIddictClientHandlers
case { BackchannelIdentityTokenNonce: string left, StateTokenNonce: string right } when case { BackchannelIdentityTokenNonce: string left, StateTokenNonce: string right } when
!CryptographicOperations.FixedTimeEquals( !CryptographicOperations.FixedTimeEquals(
left: MemoryMarshal.AsBytes(left.AsSpan()), // The nonce in the identity token is already hashed. left: MemoryMarshal.AsBytes(left.AsSpan()), // The nonce in the identity token is already hashed.
right: MemoryMarshal.AsBytes(Base64UrlEncoder.Encode( right: MemoryMarshal.AsBytes(Base64Url.EncodeToString(
SHA256.HashData(Encoding.UTF8.GetBytes(right))).AsSpan())): SHA256.HashData(Encoding.UTF8.GetBytes(right))).AsSpan())):
context.Logger.LogWarning(6211, SR.GetResourceString(SR.ID6211)); context.Logger.LogWarning(6211, SR.GetResourceString(SR.ID6211));
@ -3772,7 +3773,7 @@ public static partial class OpenIddictClientHandlers
// Warning: only the left-most half of the access token and authorization code digest is used. // Warning: only the left-most half of the access token and authorization code digest is used.
// See http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken for more information. // See http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken for more information.
return Base64UrlEncoder.Encode(hash, 0, hash.Length / 2).AsSpan(); return Base64Url.EncodeToString(hash.AsSpan(0, hash.Length / 2)).AsSpan();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -5481,7 +5482,7 @@ public static partial class OpenIddictClientHandlers
// Generate a new crypto-secure random identifier that will // Generate a new crypto-secure random identifier that will
// be used as the non-guessable part of the state token. // be used as the non-guessable part of the state token.
context.RequestForgeryProtection = Base64UrlEncoder.Encode( context.RequestForgeryProtection = Base64Url.EncodeToString(
RandomNumberGenerator.GetBytes(count: 256 / 8)); RandomNumberGenerator.GetBytes(count: 256 / 8));
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
@ -5519,7 +5520,7 @@ public static partial class OpenIddictClientHandlers
// attached to the authorization request so that the identity provider can bind // attached to the authorization request so that the identity provider can bind
// the issued identity tokens to the generated value, which helps detect token // the issued identity tokens to the generated value, which helps detect token
// replays (and authorization code injection attacks when PKCE is not available). // replays (and authorization code injection attacks when PKCE is not available).
context.Nonce = Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(count: 256 / 8)); context.Nonce = Base64Url.EncodeToString(RandomNumberGenerator.GetBytes(count: 256 / 8));
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
@ -5598,7 +5599,7 @@ public static partial class OpenIddictClientHandlers
} }
// Generate a new crypto-secure random identifier that will be used as the code challenge. // Generate a new crypto-secure random identifier that will be used as the code challenge.
context.CodeVerifier = Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(count: 256 / 8)); context.CodeVerifier = Base64Url.EncodeToString(RandomNumberGenerator.GetBytes(count: 256 / 8));
context.CodeChallenge = context.CodeChallengeMethod switch context.CodeChallenge = context.CodeChallengeMethod switch
{ {
@ -5609,7 +5610,7 @@ public static partial class OpenIddictClientHandlers
// //
// Note: ASCII is deliberately used here, as it's the encoding required by the specification. // Note: ASCII is deliberately used here, as it's the encoding required by the specification.
// For more information, see https://datatracker.ietf.org/doc/html/rfc7636#section-4.2. // For more information, see https://datatracker.ietf.org/doc/html/rfc7636#section-4.2.
CodeChallengeMethods.Sha256 => Base64UrlEncoder.Encode( CodeChallengeMethods.Sha256 => Base64Url.EncodeToString(
SHA256.HashData(Encoding.ASCII.GetBytes(context.CodeVerifier))), SHA256.HashData(Encoding.ASCII.GetBytes(context.CodeVerifier))),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0045)) _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0045))
@ -5921,7 +5922,7 @@ public static partial class OpenIddictClientHandlers
context.ResponseType?.Split(Separators.Space) is IList<string> types && context.ResponseType?.Split(Separators.Space) is IList<string> types &&
(types.Contains(ResponseTypes.Code) || types.Contains(ResponseTypes.IdToken))) (types.Contains(ResponseTypes.Code) || types.Contains(ResponseTypes.IdToken)))
{ {
context.Request.Nonce = Base64UrlEncoder.Encode( context.Request.Nonce = Base64Url.EncodeToString(
SHA256.HashData(Encoding.UTF8.GetBytes(context.Nonce))); SHA256.HashData(Encoding.UTF8.GetBytes(context.Nonce)));
} }
@ -9180,7 +9181,7 @@ public static partial class OpenIddictClientHandlers
// Generate a new crypto-secure random identifier that will // Generate a new crypto-secure random identifier that will
// be used as the non-guessable part of the state token. // be used as the non-guessable part of the state token.
context.RequestForgeryProtection = Base64UrlEncoder.Encode( context.RequestForgeryProtection = Base64Url.EncodeToString(
RandomNumberGenerator.GetBytes(count: 256 / 8)); RandomNumberGenerator.GetBytes(count: 256 / 8));
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
@ -9207,7 +9208,7 @@ public static partial class OpenIddictClientHandlers
ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(context);
// Generate a new crypto-secure random identifier that will be used as the nonce. // Generate a new crypto-secure random identifier that will be used as the nonce.
context.Nonce = Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(count: 256 / 8)); context.Nonce = Base64Url.EncodeToString(RandomNumberGenerator.GetBytes(count: 256 / 8));
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }

6
src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs

@ -4,12 +4,12 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Server.DataProtection.OpenIddictServerDataProtectionConstants.Purposes; using static OpenIddict.Server.DataProtection.OpenIddictServerDataProtectionConstants.Purposes;
using static OpenIddict.Server.OpenIddictServerHandlers.Protection; using static OpenIddict.Server.OpenIddictServerHandlers.Protection;
using Schemes = OpenIddict.Server.DataProtection.OpenIddictServerDataProtectionConstants.Purposes.Schemes; using Schemes = OpenIddict.Server.DataProtection.OpenIddictServerDataProtectionConstants.Purposes.Schemes;
@ -229,7 +229,7 @@ public static partial class OpenIddictServerDataProtectionHandlers
try try
{ {
using var buffer = new MemoryStream(protector.Unprotect(Base64UrlEncoder.DecodeBytes(context.Token))); using var buffer = new MemoryStream(protector.Unprotect(Base64Url.DecodeFromChars(context.Token)));
using var reader = new BinaryReader(buffer); using var reader = new BinaryReader(buffer);
// Note: since the data format relies on a data protector using different "purposes" strings // Note: since the data format relies on a data protector using different "purposes" strings
@ -382,7 +382,7 @@ public static partial class OpenIddictServerDataProtectionHandlers
_options.CurrentValue.Formatter.WriteToken(writer, context.Principal); _options.CurrentValue.Formatter.WriteToken(writer, context.Principal);
context.Token = Base64UrlEncoder.Encode(protector.Protect(buffer.ToArray())); context.Token = Base64Url.EncodeToString(protector.Protect(buffer.ToArray()));
context.Logger.LogTrace(6016, SR.GetResourceString(SR.ID6016), context.TokenType, context.Logger.LogTrace(6016, SR.GetResourceString(SR.ID6016), context.TokenType,
context.Token, context.Principal.Claims); context.Token, context.Principal.Claims);

1
src/OpenIddict.Server/OpenIddict.Server.csproj

@ -24,6 +24,7 @@ To use the server feature on ASP.NET Core or OWIN/Katana, reference the OpenIddi
<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' "> <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">
<PackageReference Include="BouncyCastle.Cryptography" /> <PackageReference Include="BouncyCastle.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Cryptography" /> <PackageReference Include="Microsoft.Bcl.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Memory" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

7
src/OpenIddict.Server/OpenIddictServerConfiguration.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
@ -141,14 +142,14 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions<OpenId
Debug.Assert(parameters.Q.X is not null, SR.GetResourceString(SR.ID4004)); Debug.Assert(parameters.Q.X is not null, SR.GetResourceString(SR.ID4004));
// Only use the 40 first chars of the base64url-encoded X coordinate. // Only use the 40 first chars of the base64url-encoded X coordinate.
var identifier = Base64UrlEncoder.Encode(parameters.Q.X); var identifier = Base64Url.EncodeToString(parameters.Q.X);
return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant(); return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant();
} }
static string GetMLDsaSecurityKeyIdentifier(MlDsaSecurityKey key) static string GetMLDsaSecurityKeyIdentifier(MlDsaSecurityKey key)
{ {
// Only use the 40 first chars of the base64url-encoded SHA256 of the ML-DSA public key. // Only use the 40 first chars of the base64url-encoded SHA256 of the ML-DSA public key.
var identifier = Base64UrlEncoder.Encode(SHA256.HashData(key.MLDsa.ExportMLDsaPublicKey())); var identifier = Base64Url.EncodeToString(SHA256.HashData(key.MLDsa.ExportMLDsaPublicKey()));
return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant(); return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant();
} }
@ -165,7 +166,7 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions<OpenId
} }
// Only use the 40 first chars of the base64url-encoded modulus. // Only use the 40 first chars of the base64url-encoded modulus.
var identifier = Base64UrlEncoder.Encode(parameters.Modulus); var identifier = Base64Url.EncodeToString(parameters.Modulus);
return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant(); return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant();
} }
} }

15
src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@ -1290,8 +1291,8 @@ public static partial class OpenIddictServerHandlers
// Note: both X and Y must be base64url-encoded. // Note: both X and Y must be base64url-encoded.
// See https://tools.ietf.org/html/rfc7518#section-6.2.1.2. // See https://tools.ietf.org/html/rfc7518#section-6.2.1.2.
key.X = Base64UrlEncoder.Encode(parameters.Q.X); key.X = Base64Url.EncodeToString(parameters.Q.X);
key.Y = Base64UrlEncoder.Encode(parameters.Q.Y); key.Y = Base64Url.EncodeToString(parameters.Q.Y);
} }
// Note: while ML-DSA is supported on .NET Framework via the Microsoft.Bcl.Cryptography package, SHAKE256 - used // Note: while ML-DSA is supported on .NET Framework via the Microsoft.Bcl.Cryptography package, SHAKE256 - used
@ -1310,7 +1311,7 @@ public static partial class OpenIddictServerHandlers
} }
key.Kty = JsonWebAlgorithmsKeyTypes.Akp; key.Kty = JsonWebAlgorithmsKeyTypes.Akp;
key.Pub = Base64UrlEncoder.Encode(blob); key.Pub = Base64Url.EncodeToString(blob);
} }
else if (credentials.Key.IsSupportedAlgorithm(SecurityAlgorithms.RsaSha256) || else if (credentials.Key.IsSupportedAlgorithm(SecurityAlgorithms.RsaSha256) ||
@ -1329,8 +1330,8 @@ public static partial class OpenIddictServerHandlers
// Note: both E and N must be base64url-encoded. // Note: both E and N must be base64url-encoded.
// See https://tools.ietf.org/html/rfc7518#section-6.3.1.1. // See https://tools.ietf.org/html/rfc7518#section-6.3.1.1.
key.E = Base64UrlEncoder.Encode(parameters.Exponent); key.E = Base64Url.EncodeToString(parameters.Exponent);
key.N = Base64UrlEncoder.Encode(parameters.Modulus); key.N = Base64Url.EncodeToString(parameters.Modulus);
} }
// If the signing key is embedded in a X.509 certificate, set // If the signing key is embedded in a X.509 certificate, set
@ -1339,11 +1340,11 @@ public static partial class OpenIddictServerHandlers
{ {
// x5t must be base64url-encoded. // x5t must be base64url-encoded.
// See https://tools.ietf.org/html/rfc7517#section-4.8. // See https://tools.ietf.org/html/rfc7517#section-4.8.
key.X5t = Base64UrlEncoder.Encode(certificate.GetCertHash()); key.X5t = Base64Url.EncodeToString(certificate.GetCertHash());
// x5t#S256 must be base64url-encoded. // x5t#S256 must be base64url-encoded.
// See https://tools.ietf.org/html/rfc7517#section-4.9. // See https://tools.ietf.org/html/rfc7517#section-4.9.
key.X5tS256 = Base64UrlEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256)); key.X5tS256 = Base64Url.EncodeToString(certificate.GetCertHash(HashAlgorithmName.SHA256));
// Unlike E or N, the certificates contained in x5c // Unlike E or N, the certificates contained in x5c
// must be base64-encoded and not base64url-encoded. // must be base64-encoded and not base64url-encoded.

4
src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -13,7 +14,6 @@ using System.Text;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Server; namespace OpenIddict.Server;
@ -1993,7 +1993,7 @@ public static partial class OpenIddictServerHandlers
// In this case, the raw bytes of the verifier are directly compared to the challenge. // In this case, the raw bytes of the verifier are directly compared to the challenge.
CodeChallengeMethods.Plain => context.Request.CodeVerifier, CodeChallengeMethods.Plain => context.Request.CodeVerifier,
CodeChallengeMethods.Sha256 => Base64UrlEncoder.Encode( CodeChallengeMethods.Sha256 => Base64Url.EncodeToString(
SHA256.HashData(Encoding.ASCII.GetBytes(context.Request.CodeVerifier))), SHA256.HashData(Encoding.ASCII.GetBytes(context.Request.CodeVerifier))),
null or { Length: 0 } => throw new InvalidOperationException(SR.GetResourceString(SR.ID0268)), null or { Length: 0 } => throw new InvalidOperationException(SR.GetResourceString(SR.ID0268)),

5
src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
@ -1179,7 +1180,7 @@ public static partial class OpenIddictServerHandlers
// If the thumbprint of the certificate doesn't match the hash // If the thumbprint of the certificate doesn't match the hash
// resolved from the confirmation claim, return an error. // resolved from the confirmation claim, return an error.
var hash = Base64UrlEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256)); var hash = Base64Url.EncodeToString(certificate.GetCertHash(HashAlgorithmName.SHA256));
if (!CryptographicOperations.FixedTimeEquals( if (!CryptographicOperations.FixedTimeEquals(
left : MemoryMarshal.AsBytes<char>(hash), left : MemoryMarshal.AsBytes<char>(hash),
right: MemoryMarshal.AsBytes<char>(thumbprint))) right: MemoryMarshal.AsBytes<char>(thumbprint)))
@ -1844,7 +1845,7 @@ public static partial class OpenIddictServerHandlers
else else
{ {
// For other tokens, generate a base64url-encoded 256-bit random identifier. // For other tokens, generate a base64url-encoded 256-bit random identifier.
descriptor.ReferenceId = Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(count: 256 / 8)); descriptor.ReferenceId = Base64Url.EncodeToString(RandomNumberGenerator.GetBytes(count: 256 / 8));
} }
} }

11
src/OpenIddict.Server/OpenIddictServerHandlers.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
@ -3667,7 +3668,7 @@ public static partial class OpenIddictServerHandlers
static JsonNode CreateConfirmationClaim(X509Certificate2 certificate) => new JsonObject static JsonNode CreateConfirmationClaim(X509Certificate2 certificate) => new JsonObject
{ {
[JsonWebKeyParameterNames.X5tS256] = Base64UrlEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256)) [JsonWebKeyParameterNames.X5tS256] = Base64Url.EncodeToString(certificate.GetCertHash(HashAlgorithmName.SHA256))
}; };
} }
} }
@ -4196,7 +4197,7 @@ public static partial class OpenIddictServerHandlers
static JsonNode CreateConfirmationClaim(X509Certificate2 certificate) => new JsonObject static JsonNode CreateConfirmationClaim(X509Certificate2 certificate) => new JsonObject
{ {
[JsonWebKeyParameterNames.X5tS256] = Base64UrlEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256)) [JsonWebKeyParameterNames.X5tS256] = Base64Url.EncodeToString(certificate.GetCertHash(HashAlgorithmName.SHA256))
}; };
} }
} }
@ -4489,7 +4490,7 @@ public static partial class OpenIddictServerHandlers
static JsonNode CreateConfirmationClaim(X509Certificate2 certificate) => new JsonObject static JsonNode CreateConfirmationClaim(X509Certificate2 certificate) => new JsonObject
{ {
[JsonWebKeyParameterNames.X5tS256] = Base64UrlEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256)) [JsonWebKeyParameterNames.X5tS256] = Base64Url.EncodeToString(certificate.GetCertHash(HashAlgorithmName.SHA256))
}; };
} }
} }
@ -5316,7 +5317,7 @@ public static partial class OpenIddictServerHandlers
// Note: only the left-most half of the hash is used. // Note: only the left-most half of the hash is used.
// See http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken // See http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
context.IdentityTokenPrincipal.SetClaim(Claims.AccessTokenHash, Base64UrlEncoder.Encode(digest, 0, digest.Length / 2)); context.IdentityTokenPrincipal.SetClaim(Claims.AccessTokenHash, Base64Url.EncodeToString(digest.AsSpan(0, digest.Length / 2)));
} }
if (!string.IsNullOrEmpty(context.AuthorizationCode)) if (!string.IsNullOrEmpty(context.AuthorizationCode))
@ -5325,7 +5326,7 @@ public static partial class OpenIddictServerHandlers
// Note: only the left-most half of the hash is used. // Note: only the left-most half of the hash is used.
// See http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken // See http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken
context.IdentityTokenPrincipal.SetClaim(Claims.CodeHash, Base64UrlEncoder.Encode(digest, 0, digest.Length / 2)); context.IdentityTokenPrincipal.SetClaim(Claims.CodeHash, Base64Url.EncodeToString(digest.AsSpan(0, digest.Length / 2)));
} }
return ValueTask.CompletedTask; return ValueTask.CompletedTask;

4
src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionHandlers.Protection.cs

@ -4,12 +4,12 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Validation.DataProtection.OpenIddictValidationDataProtectionConstants.Purposes; using static OpenIddict.Validation.DataProtection.OpenIddictValidationDataProtectionConstants.Purposes;
using static OpenIddict.Validation.OpenIddictValidationHandlers.Protection; using static OpenIddict.Validation.OpenIddictValidationHandlers.Protection;
using Schemes = OpenIddict.Validation.DataProtection.OpenIddictValidationDataProtectionConstants.Purposes.Schemes; using Schemes = OpenIddict.Validation.DataProtection.OpenIddictValidationDataProtectionConstants.Purposes.Schemes;
@ -127,7 +127,7 @@ public static partial class OpenIddictValidationDataProtectionHandlers
try try
{ {
using var buffer = new MemoryStream(protector.Unprotect(Base64UrlEncoder.DecodeBytes(context.Token))); using var buffer = new MemoryStream(protector.Unprotect(Base64Url.DecodeFromChars(context.Token)));
using var reader = new BinaryReader(buffer); using var reader = new BinaryReader(buffer);
// Note: since the data format relies on a data protector using different "purposes" strings // Note: since the data format relies on a data protector using different "purposes" strings

4
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpContext.cs

@ -4,10 +4,10 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.ComponentModel; using System.ComponentModel;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Validation.SystemNetHttp; namespace OpenIddict.Validation.SystemNetHttp;
@ -52,6 +52,6 @@ public sealed class OpenIddictValidationSystemNetHttpContext
algorithm.TransformFinalBlock([], 0, 0); algorithm.TransformFinalBlock([], 0, 0);
return Base64UrlEncoder.Encode(algorithm.Hash); return Base64Url.EncodeToString(algorithm.Hash);
} }
} }

1
src/OpenIddict.Validation/OpenIddict.Validation.csproj

@ -24,6 +24,7 @@ To use the validation feature on ASP.NET Core or OWIN/Katana, reference the Open
<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' "> <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">
<PackageReference Include="Microsoft.Bcl.Cryptography" /> <PackageReference Include="Microsoft.Bcl.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Memory" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

3
src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
@ -866,7 +867,7 @@ public static partial class OpenIddictValidationHandlers
// If the thumbprint of the certificate doesn't match the hash // If the thumbprint of the certificate doesn't match the hash
// resolved from the confirmation claim, return an error. // resolved from the confirmation claim, return an error.
var hash = Base64UrlEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256)); var hash = Base64Url.EncodeToString(certificate.GetCertHash(HashAlgorithmName.SHA256));
if (!CryptographicOperations.FixedTimeEquals( if (!CryptographicOperations.FixedTimeEquals(
left : MemoryMarshal.AsBytes<char>(hash), left : MemoryMarshal.AsBytes<char>(hash),
right: MemoryMarshal.AsBytes<char>(thumbprint))) right: MemoryMarshal.AsBytes<char>(thumbprint)))

3
src/OpenIddict.Validation/OpenIddictValidationHandlers.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
@ -921,7 +922,7 @@ public static partial class OpenIddictValidationHandlers
// If the thumbprint of the certificate doesn't match the hash // If the thumbprint of the certificate doesn't match the hash
// resolved from the confirmation claim, return an error. // resolved from the confirmation claim, return an error.
var hash = Base64UrlEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256)); var hash = Base64Url.EncodeToString(certificate.GetCertHash(HashAlgorithmName.SHA256));
if (!CryptographicOperations.FixedTimeEquals( if (!CryptographicOperations.FixedTimeEquals(
left : MemoryMarshal.AsBytes<char>(hash), left : MemoryMarshal.AsBytes<char>(hash),
right: MemoryMarshal.AsBytes<char>(thumbprint))) right: MemoryMarshal.AsBytes<char>(thumbprint)))

1
test/OpenIddict.Client.Tests/OpenIddictClientConfigurationTests.cs

@ -1,6 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Tokens;
using Moq; using Moq;
using Xunit; using Xunit;

1
test/OpenIddict.Core.Tests/Managers/OpenIddictApplicationManagerTests.cs

@ -5,7 +5,6 @@
*/ */
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Globalization; using System.Globalization;
using System.Security.Cryptography; using System.Security.Cryptography;

11
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Buffers.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Net.Http; using System.Net.Http;
using System.Security.Cryptography; using System.Security.Cryptography;
@ -1486,8 +1487,8 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(key?[JsonWebKeyParameterNames.P]); Assert.Null(key?[JsonWebKeyParameterNames.P]);
Assert.Null(key?[JsonWebKeyParameterNames.Q]); Assert.Null(key?[JsonWebKeyParameterNames.Q]);
Assert.Equal(parameters.Exponent, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.E])); Assert.Equal(parameters.Exponent, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.E]));
Assert.Equal(parameters.Modulus, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.N])); Assert.Equal(parameters.Modulus, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.N]));
} }
[Theory] [Theory]
@ -1537,8 +1538,8 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Null(key?[JsonWebKeyParameterNames.D]); Assert.Null(key?[JsonWebKeyParameterNames.D]);
Assert.Equal(parameters.Q.X, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.X])); Assert.Equal(parameters.Q.X, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.X]));
Assert.Equal(parameters.Q.Y, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.Y])); Assert.Equal(parameters.Q.Y, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.Y]));
} }
[SkippableFact(typeof(PlatformNotSupportedException))] [SkippableFact(typeof(PlatformNotSupportedException))]
@ -1563,7 +1564,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal(JsonWebAlgorithmsKeyTypes.Akp, (string?) key?[JsonWebKeyParameterNames.Kty]); Assert.Equal(JsonWebAlgorithmsKeyTypes.Akp, (string?) key?[JsonWebKeyParameterNames.Kty]);
Assert.Equal(SecurityAlgorithms.MlDsa44, (string?) key?[JsonWebKeyParameterNames.Alg]); Assert.Equal(SecurityAlgorithms.MlDsa44, (string?) key?[JsonWebKeyParameterNames.Alg]);
Assert.Equal(blob, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.Pub])); Assert.Equal(blob, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.Pub]));
} }
[Fact] [Fact]

1
test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs

@ -6,7 +6,6 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Moq; using Moq;
using OpenIddict.Abstractions;
using Xunit; using Xunit;
using static OpenIddict.Validation.OpenIddictValidationEvents; using static OpenIddict.Validation.OpenIddictValidationEvents;

Loading…
Cancel
Save