Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<ItemGroup Label="Package versions for .NET Framework 4.8"
Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' And $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '4.8.0')) ">
<PackageVersion Include="EntityFramework" Version="6.5.2" />
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.2" />
<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" />
Expand All @@ -32,9 +33,9 @@
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.WebEncoders" Version="10.0.8" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.18.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="8.18.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.18.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.19.1" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="8.19.1" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.19.1" />
<PackageVersion Include="Microsoft.Owin.Security" Version="4.2.3" />
<PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.17763.1000" />
<PackageVersion Include="MongoDB.Bson" Version="3.8.1" />
Expand All @@ -59,6 +60,7 @@
<PackageVersion Include="Microsoft.Owin.Testing" Version="4.2.3" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="System.Linq.Async" Version="7.0.1" />
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.61" />

<!--
Note: the following references are exclusively used in the samples:
Expand Down Expand Up @@ -110,9 +112,9 @@
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.18.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="8.18.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.18.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.19.1" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="8.19.1" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.19.1" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="10.0.8" />
Expand All @@ -132,6 +134,7 @@
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.8" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.61" />

<!--
Note: the following references are exclusively used in the samples:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ static string GenerateBuilderMethods(XDocument document)
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
Expand Down Expand Up @@ -659,6 +660,7 @@ public sealed partial class {{ provider.name }}
{{~ if setting.obsolete ~}}
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
{{~ end ~}}
[UnsupportedOSPlatform(""linux"")]
public {{ provider.name }} Set{{ setting.property_name }}(string thumbprint)
{
ArgumentException.ThrowIfNullOrEmpty(thumbprint);
Expand Down
6 changes: 6 additions & 0 deletions sandbox/OpenIddict.Sandbox.AspNet.Client/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,12 @@
<bindingRedirect oldVersion="0.0.0.0-9.1.0.0" newVersion="9.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.Cryptography" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.8" newVersion="10.0.0.8" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.codedom>
<compilers>
Expand Down
6 changes: 6 additions & 0 deletions sandbox/OpenIddict.Sandbox.AspNet.Server/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@
<bindingRedirect oldVersion="0.0.0.0-9.1.0.0" newVersion="9.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.Cryptography" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.8" newVersion="10.0.0.8" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<entityFramework>
<providers>
Expand Down
8 changes: 4 additions & 4 deletions src/OpenIddict.Abstractions/OpenIddictResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,6 @@ Consider using 'options.AddEncryptionCredentials(EncryptingCredentials)' instead
<value>A signature algorithm cannot be automatically inferred from the signing key.
Consider using 'options.AddSigningCredentials(SigningCredentials)' instead.</value>
</data>
<data name="ID0069" xml:space="preserve">
<value>ECDSA signing keys are not supported on this platform.</value>
</data>
<data name="ID0070" xml:space="preserve">
<value>The specified certificate is not a signing certificate.</value>
</data>
Expand Down Expand Up @@ -2721,7 +2718,7 @@ The principal used to create the token contained the following claims: {Claims}.
<value>A JSON Web Key was excluded from the key set because it didn't contain the mandatory '{Parameter}' parameter.</value>
</data>
<data name="ID6071" xml:space="preserve">
<value>An unsupported signing key of type '{Type}' was ignored and excluded from the key set. Only RSA and ECDSA asymmetric security keys can be exposed via the JSON Web Key Set endpoint.</value>
<value>An unsupported signing key of type '{Type}' was ignored and excluded from the key set. Only RSA, ECDSA and ML-DSA asymmetric security keys can be exposed via the JSON Web Key Set endpoint.</value>
</data>
<data name="ID6072" xml:space="preserve">
<value>An unsupported signing key of type '{Type}' was ignored and excluded from the key set. Only RSA asymmetric security keys can be exposed via the JSON Web Key Set endpoint.</value>
Expand Down Expand Up @@ -3348,6 +3345,9 @@ This may indicate that the hashed entry is corrupted or malformed.</value>
<data name="ID6295" xml:space="preserve">
<value>The client secret attached to the client application {ClientId} couldn't be automatically re-hashed due to a concurency exception.</value>
</data>
<data name="ID6296" xml:space="preserve">
<value>A signing key of type '{Type}' was ignored because its ML-DSA public key couldn't be extracted.</value>
</data>
<data name="ID8000" xml:space="preserve">
<value>https://documentation.openiddict.com/errors/{0}</value>
</data>
Expand Down
1 change: 1 addition & 0 deletions src/OpenIddict.Client/OpenIddict.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ To use the client feature on ASP.NET Core or OWIN/Katana, reference the OpenIddi
</ItemGroup>

<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">
<PackageReference Include="BouncyCastle.Cryptography" />
<PackageReference Include="Microsoft.Bcl.Cryptography" />
</ItemGroup>

Expand Down
80 changes: 55 additions & 25 deletions src/OpenIddict.Client/OpenIddictClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.Versioning;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
Expand Down Expand Up @@ -154,7 +155,7 @@ public OpenIddictClientBuilder AddEncryptionKey(SecurityKey key)

if (key.IsSupportedAlgorithm(SecurityAlgorithms.Aes256KW))
{
if (key.KeySize != 256)
if (key.KeySize is not 256)
{
throw new InvalidOperationException(SR.FormatID0283(256, key.KeySize));
}
Expand Down Expand Up @@ -414,6 +415,7 @@ public OpenIddictClientBuilder AddEncryptionCertificate(Stream stream, string? p
/// </summary>
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <returns>The <see cref="OpenIddictClientBuilder"/> instance.</returns>
[UnsupportedOSPlatform("linux")]
public OpenIddictClientBuilder AddEncryptionCertificate(string thumbprint)
{
ArgumentException.ThrowIfNullOrEmpty(thumbprint);
Expand Down Expand Up @@ -493,30 +495,45 @@ public OpenIddictClientBuilder AddSigningKey(SecurityKey key)
throw new InvalidOperationException(SR.GetResourceString(SR.ID0067));
}

if (key.IsSupportedAlgorithm(SecurityAlgorithms.RsaSha256))
// Note: ECDSA algorithms are bound to specific curves and must be treated separately.
if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha256))
{
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.RsaSha256));
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256));
}

if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha384))
{
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha384));
}

if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha512))
{
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha512));
}

if (key.IsSupportedAlgorithm(SecurityAlgorithms.HmacSha256))
{
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
}

// Note: ECDSA algorithms are bound to specific curves and must be treated separately.
if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha256))
if (key.IsSupportedAlgorithm(SecurityAlgorithms.MlDsa44))
{
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256));
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.MlDsa44));
}

if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha384))
if (key.IsSupportedAlgorithm(SecurityAlgorithms.MlDsa65))
{
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha384));
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.MlDsa65));
}

if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha512))
if (key.IsSupportedAlgorithm(SecurityAlgorithms.MlDsa87))
{
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha512));
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.MlDsa87));
}

if (key.IsSupportedAlgorithm(SecurityAlgorithms.RsaSha256))
{
return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.RsaSha256));
}

throw new InvalidOperationException(SR.GetResourceString(SR.ID0068));
Expand Down Expand Up @@ -644,21 +661,6 @@ public OpenIddictClientBuilder AddEphemeralSigningKey(string algorithm)

return algorithm switch
{
SecurityAlgorithms.RsaSha256 or
SecurityAlgorithms.RsaSha384 or
SecurityAlgorithms.RsaSha512 or
SecurityAlgorithms.RsaSha256Signature or
SecurityAlgorithms.RsaSha384Signature or
SecurityAlgorithms.RsaSha512Signature or
SecurityAlgorithms.RsaSsaPssSha256 or
SecurityAlgorithms.RsaSsaPssSha384 or
SecurityAlgorithms.RsaSsaPssSha512 or
SecurityAlgorithms.RsaSsaPssSha256Signature or
SecurityAlgorithms.RsaSsaPssSha384Signature or
SecurityAlgorithms.RsaSsaPssSha512Signature
=> AddSigningCredentials(new SigningCredentials(new RsaSecurityKey(
RSA.Create(keySizeInBits: 4096)), algorithm)),

SecurityAlgorithms.EcdsaSha256 or
SecurityAlgorithms.EcdsaSha256Signature
=> AddSigningCredentials(new SigningCredentials(new ECDsaSecurityKey(
Expand All @@ -674,6 +676,33 @@ SecurityAlgorithms.EcdsaSha512 or
=> AddSigningCredentials(new SigningCredentials(new ECDsaSecurityKey(
ECDsa.Create(ECCurve.NamedCurves.nistP521)), algorithm)),

SecurityAlgorithms.MlDsa44
=> AddSigningCredentials(new SigningCredentials(new MlDsaSecurityKey(
MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa44)), algorithm)),

SecurityAlgorithms.MlDsa65
=> AddSigningCredentials(new SigningCredentials(new MlDsaSecurityKey(
MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65)), algorithm)),

SecurityAlgorithms.MlDsa87
=> AddSigningCredentials(new SigningCredentials(new MlDsaSecurityKey(
MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa87)), algorithm)),

SecurityAlgorithms.RsaSha256 or
SecurityAlgorithms.RsaSha384 or
SecurityAlgorithms.RsaSha512 or
SecurityAlgorithms.RsaSha256Signature or
SecurityAlgorithms.RsaSha384Signature or
SecurityAlgorithms.RsaSha512Signature or
SecurityAlgorithms.RsaSsaPssSha256 or
SecurityAlgorithms.RsaSsaPssSha384 or
SecurityAlgorithms.RsaSsaPssSha512 or
SecurityAlgorithms.RsaSsaPssSha256Signature or
SecurityAlgorithms.RsaSsaPssSha384Signature or
SecurityAlgorithms.RsaSsaPssSha512Signature
=> AddSigningCredentials(new SigningCredentials(new RsaSecurityKey(
RSA.Create(keySizeInBits: 4096)), algorithm)),

_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0058))
};
}
Expand Down Expand Up @@ -782,6 +811,7 @@ public OpenIddictClientBuilder AddSigningCertificate(Stream stream, string? pass
/// </summary>
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <returns>The <see cref="OpenIddictClientBuilder"/> instance.</returns>
[UnsupportedOSPlatform("linux")]
public OpenIddictClientBuilder AddSigningCertificate(string thumbprint)
{
ArgumentException.ThrowIfNullOrEmpty(thumbprint);
Expand Down
50 changes: 30 additions & 20 deletions src/OpenIddict.Client/OpenIddictClientConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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;
Expand Down Expand Up @@ -160,41 +161,50 @@ from credentials in options.EncryptionCredentials
// inferred from the hexadecimal representation of the certificate thumbprint (SHA-1)
// when the key is bound to a X.509 certificate or from the public part of the signing key.

if (key is X509SecurityKey x509SecurityKey)
return key switch
{
return x509SecurityKey.Certificate.Thumbprint;
X509SecurityKey value => value.Certificate.Thumbprint,
RsaSecurityKey value => GetRsaSecurityKeyIdentifier(value),
ECDsaSecurityKey value => GetEcdsaSecurityKeyIdentifier(value),
MlDsaSecurityKey value => GetMLDsaSecurityKeyIdentifier(value),

_ => null
};

static string GetEcdsaSecurityKeyIdentifier(ECDsaSecurityKey key)
{
var parameters = key.ECDsa.ExportParameters(includePrivateParameters: false);

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);
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()));
return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant();
}

if (key is RsaSecurityKey rsaSecurityKey)
static string GetRsaSecurityKeyIdentifier(RsaSecurityKey key)
{
// Note: if the RSA parameters are not attached to the signing key,
// extract them by calling ExportParameters on the RSA instance.
var parameters = rsaSecurityKey.Parameters;
var parameters = key.Parameters;
if (parameters.Modulus is null)
{
parameters = rsaSecurityKey.Rsa.ExportParameters(includePrivateParameters: false);
parameters = key.Rsa.ExportParameters(includePrivateParameters: false);

Debug.Assert(parameters.Modulus is not null, SR.GetResourceString(SR.ID4003));
}

// Only use the 40 first chars of the base64url-encoded modulus.
var identifier = Base64UrlEncoder.Encode(parameters.Modulus);
return identifier[..Math.Min(identifier.Length, 40)].ToUpperInvariant();
}

if (key is ECDsaSecurityKey ecsdaSecurityKey)
{
// Extract the ECDSA parameters from the signing credentials.
var parameters = ecsdaSecurityKey.ECDsa.ExportParameters(includePrivateParameters: false);

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);
return identifier[..Math.Min(identifier.Length, 40)].ToUpperInvariant();
return identifier[.. Math.Min(identifier.Length, 40)].ToUpperInvariant();
}

return null;
}

static string ComputeDefaultRegistrationId(OpenIddictClientRegistration registration)
Expand Down
Loading