DuendeSoftware/IdentityServer

CryptographicException while calling /.well-known/openid-configuration (.NET 8)

Tobias-08 opened this issue ยท 14 comments

Which version of Duende IdentityServer are you using?

7.0.1

Which version of .NET are you using?

.NET 8

Describe the bug

Calling /.well-known/openid-configuration leads to CryptographicException: The system cannot find the file specified since we migrated from .NET 6/IdentityServer 6.x to .NET 8/IdentityServer 7.x.

To Reproduce

  • Setup an IdentityServer project in .NET 8 and serve it via IIS with Load user profile: false.
  • Call /.well-known/openid-configuration.

Expected behavior

It should work in .NET 8 even if Load user profile is set to false.

Log output/exception with stacktrace

CryptographicException: The system cannot find the file specified.
System.Security.Cryptography.X509Certificates.CertificatePal.FilterPFXStore(ReadOnlySpan<byte> rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
System.Security.Cryptography.X509Certificates.CertificatePal.FromBlobOrFile(ReadOnlySpan<byte> rawData, string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
System.Security.Cryptography.X509Certificates.X509Certificate..ctor(ReadOnlySpan<byte> data)
System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(byte[] rawData)
Duende.IdentityServer.Services.KeyManagement.X509KeyContainer.ToSecurityKey() in X509KeyContainer.cs
Duende.IdentityServer.Services.KeyManagement.AutomaticKeyManagerKeyStore+<>c.<GetValidationKeysAsync>b__5_0(KeyContainer x) in AutomaticKeyManagerKeyStore.cs
System.Linq.Enumerable+SelectArrayIterator<TSource, TResult>.Fill(ReadOnlySpan<TSource> source, Span<TResult> destination, Func<TSource, TResult> func)
System.Linq.Enumerable+SelectArrayIterator<TSource, TResult>.ToArray()
Duende.IdentityServer.Services.KeyManagement.AutomaticKeyManagerKeyStore.GetValidationKeysAsync() in AutomaticKeyManagerKeyStore.cs
Duende.IdentityServer.Services.DefaultKeyMaterialService.GetValidationKeysAsync() in DefaultKeyMaterialService.cs
Duende.IdentityServer.ResponseHandling.DiscoveryResponseGenerator.CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri) in DiscoveryResponseGenerator.cs
Duende.IdentityServer.Endpoints.DiscoveryEndpoint.ProcessAsync(HttpContext context) in DiscoveryEndpoint.cs
Duende.IdentityServer.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IdentityServerOptions options, IEndpointRouter router, IUserSession userSession, IEventService events, IIssuerNameService issuerNameService, ISessionCoordinationService sessionCoordinationService) in IdentityServerMiddleware.cs
Duende.IdentityServer.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IdentityServerOptions options, IEndpointRouter router, IUserSession userSession, IEventService events, IIssuerNameService issuerNameService, ISessionCoordinationService sessionCoordinationService) in IdentityServerMiddleware.cs
Duende.IdentityServer.Hosting.MutualTlsEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes) in MutualTlsEndpointMiddleware.cs
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Duende.IdentityServer.Hosting.DynamicProviders.DynamicSchemeAuthenticationMiddleware.Invoke(HttpContext context) in DynamicSchemeAuthenticationMiddleware.cs
Duende.IdentityServer.Hosting.BaseUrlMiddleware.Invoke(HttpContext context) in BaseUrlMiddleware.cs
Serilog.AspNetCore.RequestLoggingMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

Additional context

Background:

  • We are running IdentityServer in IIS with an application pool configured with Load user profile: false.
  • This worked in .NET 6 but does not work in .NET 8.
  • It works in .NET 8 if we set Load user profile to true.

Theory:

  • The behaviour of X509Certificate2-constructor might have changed from .NET 6 to .NET 8 so that the workaround (type check) in IdentityServer's X509KeyContainer-implementation for the Load user profile: false-case might not work anymore.

Thanks for the detailed info @Tobias-08. A couple of questions:

  • Are you using automatic key management with the UseX509Certificate enabled, or are you supplying an x509 cert as a manual signing key?
  • Do you need to avoid loading the user profile/is this preventing you from upgrading while we investigate?

@josephdecock Thanks for your response. Regarding your questions:

  • We are using automatic key management with UseX509Certificate enabled (because we are using RSK's WS-Federation plugin which requires this).
  • We are not in production yet so we are not blocked for the moment.

This has been asked about before in another support channel. I think that you are right @Tobias-08 that there is a change in behaviour of the constructor. It looks like the exception type thrown has changed in .NET 8 so we might need to update our code to detect the new exception type.

It looks like this is due to a changed behaviour from .NET 7 and up: dotnet/runtime@43c4405

Moving to IdentityServer repo to fix it.

@brockallen @josephdecock The exception type was changed in .NET 7. So technically we should patch IdSrv 6.x. But end of life for .NET 7 is less than two months away. I think a fix for IdSrv 7.x is enough. What do you think?

@Tobias-08 Do you think you could help me to get a value out of the exception thrown? I would need the value of CryptographicException.HResult. I assume that it should be 0x80070002 (= -2147024894), but I would like to verify before creating the patch.

@AndersAbel: Yes, you are right. CryptographicException.HResult is -2147024894.

@Tobias-08 Thank you!

@Tobias-08 If we merge this and put out a preview on Nuget, would you be able to help us test it?

@brockallen Could we merge this and put out another preview on 7.0.2 to get it tested in a proper environment? It's obviously not testable on a local development setup because the user profile is always available then.

@AndersAbel Yes, of course.

We're working on putting out a 7.0.3-preview.1 to test.

We've pushed a 7.0.3-preview.1 to NuGet with the proposed fix. Please have a look and let us know how it works. Thanks.

@brockallen @AndersAbel 7.0.3-preview.1 works as expected in my setup, i. e. it works with .NET 8 and Load user profile: false.

Thanks!

7.0.3 Has been pushed to NuGet. Thanks!