Browse Source

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

pull/2490/head
Kévin Chalet 1 month ago
parent
commit
d626bdf4d8
  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.Bcl.Cryptography" Version="10.0.8" />
<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.DependencyInjection.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.Text;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
@ -18,7 +19,6 @@ using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
using Properties = OpenIddict.Client.AspNetCore.OpenIddictClientAspNetCoreConstants.Properties;
@ -421,7 +421,7 @@ public static partial class OpenIddictClientAspNetCoreHandlers
try
{
// 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)
{
context.Reject(
@ -818,7 +818,7 @@ public static partial class OpenIddictClientAspNetCoreHandlers
Debug.Assert(written == count, SR.FormatID4016(written, count));
// 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;
}
@ -1041,7 +1041,7 @@ public static partial class OpenIddictClientAspNetCoreHandlers
Debug.Assert(written == count, SR.FormatID4016(written, count));
// 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;
}

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

@ -4,12 +4,12 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Security.Claims;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Client.DataProtection.OpenIddictClientDataProtectionConstants.Purposes;
using static OpenIddict.Client.OpenIddictClientHandlers.Protection;
using Schemes = OpenIddict.Client.DataProtection.OpenIddictClientDataProtectionConstants.Purposes.Schemes;
@ -134,7 +134,7 @@ public static partial class OpenIddictClientDataProtectionHandlers
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);
// 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);
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.Token, context.Principal.Claims);

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

@ -5,6 +5,7 @@
*/
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
@ -15,7 +16,6 @@ using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens;
using Owin;
using static OpenIddict.Client.Owin.OpenIddictClientOwinConstants;
using Properties = OpenIddict.Client.Owin.OpenIddictClientOwinConstants.Properties;
@ -432,7 +432,7 @@ public static partial class OpenIddictClientOwinHandlers
try
{
// 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)
{
context.Reject(
@ -846,7 +846,7 @@ public static partial class OpenIddictClientOwinHandlers
_options.CurrentValue.CookieOptions);
// 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,
HttpOnly = options.HttpOnly,
@ -1090,7 +1090,7 @@ public static partial class OpenIddictClientOwinHandlers
_options.CurrentValue.CookieOptions);
// 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,
HttpOnly = options.HttpOnly,

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

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.ComponentModel;
using System.IO.Pipes;
using System.Net;
@ -14,7 +15,6 @@ using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Client.SystemIntegration.OpenIddictClientSystemIntegrationAuthenticationMode;
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,
// 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.
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 (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.

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

@ -4,11 +4,11 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.ComponentModel;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Client.SystemNetHttp;
@ -61,6 +61,6 @@ public sealed class OpenIddictClientSystemNetHttpContext
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' ">
<PackageReference Include="BouncyCastle.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Memory" />
</ItemGroup>
<ItemGroup>

10
src/OpenIddict.Client/OpenIddictClientConfiguration.cs

@ -4,11 +4,11 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
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));
// 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();
}
static string GetMLDsaSecurityKeyIdentifier(MlDsaSecurityKey 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();
}
@ -202,7 +202,7 @@ public sealed class OpenIddictClientConfiguration : IPostConfigureOptions<OpenId
}
// 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();
}
}
@ -222,7 +222,7 @@ public sealed class OpenIddictClientConfiguration : IPostConfigureOptions<OpenId
algorithm.TransformFinalBlock([], 0, 0);
return Base64UrlEncoder.Encode(algorithm.Hash);
return Base64Url.EncodeToString(algorithm.Hash);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
@ -1183,7 +1184,7 @@ public static partial class OpenIddictClientHandlers
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);

23
src/OpenIddict.Client/OpenIddictClientHandlers.cs

@ -19,6 +19,7 @@ using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Abstractions.OpenIddictExceptions;
using System.Runtime.CompilerServices;
using System.Buffers.Text;
#if !NET
using Org.BouncyCastle.Crypto.Digests;
@ -1942,7 +1943,7 @@ public static partial class OpenIddictClientHandlers
case { FrontchannelIdentityTokenNonce: string left, StateTokenNonce: string right } when
!CryptographicOperations.FixedTimeEquals(
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())):
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.
// 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)]
@ -3678,7 +3679,7 @@ public static partial class OpenIddictClientHandlers
case { BackchannelIdentityTokenNonce: string left, StateTokenNonce: string right } when
!CryptographicOperations.FixedTimeEquals(
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())):
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.
// 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)]
@ -5481,7 +5482,7 @@ public static partial class OpenIddictClientHandlers
// Generate a new crypto-secure random identifier that will
// be used as the non-guessable part of the state token.
context.RequestForgeryProtection = Base64UrlEncoder.Encode(
context.RequestForgeryProtection = Base64Url.EncodeToString(
RandomNumberGenerator.GetBytes(count: 256 / 8));
return ValueTask.CompletedTask;
@ -5519,7 +5520,7 @@ public static partial class OpenIddictClientHandlers
// attached to the authorization request so that the identity provider can bind
// the issued identity tokens to the generated value, which helps detect token
// 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;
}
@ -5598,7 +5599,7 @@ public static partial class OpenIddictClientHandlers
}
// 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
{
@ -5609,7 +5610,7 @@ public static partial class OpenIddictClientHandlers
//
// 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.
CodeChallengeMethods.Sha256 => Base64UrlEncoder.Encode(
CodeChallengeMethods.Sha256 => Base64Url.EncodeToString(
SHA256.HashData(Encoding.ASCII.GetBytes(context.CodeVerifier))),
_ => 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 &&
(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)));
}
@ -9180,7 +9181,7 @@ public static partial class OpenIddictClientHandlers
// Generate a new crypto-secure random identifier that will
// be used as the non-guessable part of the state token.
context.RequestForgeryProtection = Base64UrlEncoder.Encode(
context.RequestForgeryProtection = Base64Url.EncodeToString(
RandomNumberGenerator.GetBytes(count: 256 / 8));
return ValueTask.CompletedTask;
@ -9207,7 +9208,7 @@ public static partial class OpenIddictClientHandlers
ArgumentNullException.ThrowIfNull(context);
// 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;
}

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

@ -4,12 +4,12 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Security.Claims;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Server.DataProtection.OpenIddictServerDataProtectionConstants.Purposes;
using static OpenIddict.Server.OpenIddictServerHandlers.Protection;
using Schemes = OpenIddict.Server.DataProtection.OpenIddictServerDataProtectionConstants.Purposes.Schemes;
@ -229,7 +229,7 @@ public static partial class OpenIddictServerDataProtectionHandlers
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);
// 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);
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.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' ">
<PackageReference Include="BouncyCastle.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Memory" />
</ItemGroup>
<ItemGroup>

7
src/OpenIddict.Server/OpenIddictServerConfiguration.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.ComponentModel;
using System.Diagnostics;
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));
// 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();
}
static string GetMLDsaSecurityKeyIdentifier(MlDsaSecurityKey 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();
}
@ -165,7 +166,7 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions<OpenId
}
// 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();
}
}

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

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
@ -1290,8 +1291,8 @@ public static partial class OpenIddictServerHandlers
// Note: both X and Y must be base64url-encoded.
// See https://tools.ietf.org/html/rfc7518#section-6.2.1.2.
key.X = Base64UrlEncoder.Encode(parameters.Q.X);
key.Y = Base64UrlEncoder.Encode(parameters.Q.Y);
key.X = Base64Url.EncodeToString(parameters.Q.X);
key.Y = Base64Url.EncodeToString(parameters.Q.Y);
}
// 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.Pub = Base64UrlEncoder.Encode(blob);
key.Pub = Base64Url.EncodeToString(blob);
}
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.
// See https://tools.ietf.org/html/rfc7518#section-6.3.1.1.
key.E = Base64UrlEncoder.Encode(parameters.Exponent);
key.N = Base64UrlEncoder.Encode(parameters.Modulus);
key.E = Base64Url.EncodeToString(parameters.Exponent);
key.N = Base64Url.EncodeToString(parameters.Modulus);
}
// 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.
// 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.
// 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
// 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.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.InteropServices;
@ -13,7 +14,6 @@ using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
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.
CodeChallengeMethods.Plain => context.Request.CodeVerifier,
CodeChallengeMethods.Sha256 => Base64UrlEncoder.Encode(
CodeChallengeMethods.Sha256 => Base64Url.EncodeToString(
SHA256.HashData(Encoding.ASCII.GetBytes(context.Request.CodeVerifier))),
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.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
@ -1179,7 +1180,7 @@ public static partial class OpenIddictServerHandlers
// If the thumbprint of the certificate doesn't match the hash
// 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(
left : MemoryMarshal.AsBytes<char>(hash),
right: MemoryMarshal.AsBytes<char>(thumbprint)))
@ -1844,7 +1845,7 @@ public static partial class OpenIddictServerHandlers
else
{
// 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.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
@ -3667,7 +3668,7 @@ public static partial class OpenIddictServerHandlers
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
{
[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
{
[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.
// 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))
@ -5325,7 +5326,7 @@ public static partial class OpenIddictServerHandlers
// Note: only the left-most half of the hash is used.
// 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;

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

@ -4,12 +4,12 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Security.Claims;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Validation.DataProtection.OpenIddictValidationDataProtectionConstants.Purposes;
using static OpenIddict.Validation.OpenIddictValidationHandlers.Protection;
using Schemes = OpenIddict.Validation.DataProtection.OpenIddictValidationDataProtectionConstants.Purposes.Schemes;
@ -127,7 +127,7 @@ public static partial class OpenIddictValidationDataProtectionHandlers
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);
// 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.
*/
using System.Buffers.Text;
using System.ComponentModel;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Validation.SystemNetHttp;
@ -52,6 +52,6 @@ public sealed class OpenIddictValidationSystemNetHttpContext
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' ">
<PackageReference Include="Microsoft.Bcl.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Memory" />
</ItemGroup>
<ItemGroup>

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

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
@ -866,7 +867,7 @@ public static partial class OpenIddictValidationHandlers
// If the thumbprint of the certificate doesn't match the hash
// 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(
left : MemoryMarshal.AsBytes<char>(hash),
right: MemoryMarshal.AsBytes<char>(thumbprint)))

3
src/OpenIddict.Validation/OpenIddictValidationHandlers.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
@ -921,7 +922,7 @@ public static partial class OpenIddictValidationHandlers
// If the thumbprint of the certificate doesn't match the hash
// 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(
left : MemoryMarshal.AsBytes<char>(hash),
right: MemoryMarshal.AsBytes<char>(thumbprint)))

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

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

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

@ -5,7 +5,6 @@
*/
using System.Buffers.Binary;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
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.
*/
using System.Buffers.Text;
using System.Collections.Immutable;
using System.Net.Http;
using System.Security.Cryptography;
@ -1486,8 +1487,8 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(key?[JsonWebKeyParameterNames.P]);
Assert.Null(key?[JsonWebKeyParameterNames.Q]);
Assert.Equal(parameters.Exponent, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.E]));
Assert.Equal(parameters.Modulus, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.N]));
Assert.Equal(parameters.Exponent, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.E]));
Assert.Equal(parameters.Modulus, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.N]));
}
[Theory]
@ -1537,8 +1538,8 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert
Assert.Null(key?[JsonWebKeyParameterNames.D]);
Assert.Equal(parameters.Q.X, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.X]));
Assert.Equal(parameters.Q.Y, Base64UrlEncoder.DecodeBytes((string?) key?[JsonWebKeyParameterNames.Y]));
Assert.Equal(parameters.Q.X, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.X]));
Assert.Equal(parameters.Q.Y, Base64Url.DecodeFromChars((string?) key?[JsonWebKeyParameterNames.Y]));
}
[SkippableFact(typeof(PlatformNotSupportedException))]
@ -1563,7 +1564,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert
Assert.Equal(JsonWebAlgorithmsKeyTypes.Akp, (string?) key?[JsonWebKeyParameterNames.Kty]);
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]

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

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

Loading…
Cancel
Save