bitfoundation / bitplatform

Build all of your apps using what you already know and love ❤️
https://bitplatform.dev
MIT License
1.07k stars 222 forks source link

Boilerplate on MacOS fails to log in user due to a crypto exception CSSMERR_DL_DATASTORE_DOESNOT_EXIST #6262

Closed garunski closed 9 months ago

garunski commented 10 months ago

Is there an existing issue for this?

Describe the bug

Creating a new project with Boilerplate template fails on signing a user in with a crypto exception.

Expected Behavior

Should be able to login.

Steps To Reproduce

create a new project with the template

Exceptions (if any)

Interop+AppleCrypto+AppleCFErrorCryptographicException: The operation couldn’t be completed. (OSStatus error -25294 - CSSM Exception: -2147413737 CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
         at Interop.AppleCrypto.NativeCreateSignature(SafeSecKeyRefHandle privateKey, ReadOnlySpan`1 dataHash, PAL_HashAlgorithm hashAlgorithm, PAL_SignatureAlgorithm signatureAlgorithm)
         at Interop.AppleCrypto.CreateSignature(SafeSecKeyRefHandle privateKey, ReadOnlySpan`1 dataHash, PAL_HashAlgorithm hashAlgorithm, PAL_SignatureAlgorithm signatureAlgorithm)
         at System.Security.Cryptography.RSAImplementation.RSASecurityTransforms.SignHash(Byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)

.NET Version

8.0.100

Anything else?

Essentially the issue is with the encryption of the bearer token.

https://github.com/bitfoundation/bitplatform/blob/develop/src/Templates/Boilerplate/Bit.Boilerplate/src/Boilerplate.Server/Extensions/IServiceCollectionExtensions.cs#L81

I fixed the problem by removing the using statement around the certificate.

var signingCert = new X509Certificate2(certificatePath,
    appSettings.IdentitySettings.IdentityCertificatePassword,
    OperatingSystem.IsWindows()
        ? X509KeyStorageFlags.EphemeralKeySet
        : X509KeyStorageFlags.DefaultKeySet);
var rsaPrivateKey = signingCert.GetRSAPrivateKey();

alternatively, using the constructor for X509SecurityKey also works:

var signingCert = new X509Certificate2(certificatePath,
  appSettings.IdentitySettings.IdentityCertificatePassword,
  OperatingSystem.IsWindows() ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet);
var securityKey = new X509SecurityKey(signingCert);

I think something gets lost in the key storage pointers once the certificate is destructed.

ysmoradi commented 10 months ago

Could you please check this code too?

using X509Certificate2 signingCert = new X509Certificate2(certificatePath, appSettings.IdentitySettings.IdentityCertificatePassword, OperatingSystem.IsWindows() ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet);
RSA rsaPrivateKey = signingCert.GetRSAPrivateKey()!;

image

With this new change, I'm basically keeping certificate alive until SecurityKey gets created.

ysmoradi commented 10 months ago

I'm also open to this change which is based on your hint. (The using block exists though)

using X509Certificate2 signingCert = new X509Certificate2(certificatePath, appSettings.IdentitySettings.IdentityCertificatePassword, OperatingSystem.IsWindows() ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet);
var validationParameters = new TokenValidationParameters
{
      ClockSkew = TimeSpan.Zero,
      RequireSignedTokens = true,
      ValidateIssuerSigningKey = true,
      IssuerSigningKey = new X509SecurityKey(signingCert),

image

garunski commented 10 months ago

Could you please check this code too?

using X509Certificate2 signingCert = new X509Certificate2(certificatePath, appSettings.IdentitySettings.IdentityCertificatePassword, OperatingSystem.IsWindows() ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet);
RSA rsaPrivateKey = signingCert.GetRSAPrivateKey()!;

image

With this new change, I'm basically keeping certificate alive until SecurityKey gets created.

Same error.

garunski commented 10 months ago

I'm also open to this change which is based on your hint. (The using block exists though)

using X509Certificate2 signingCert = new X509Certificate2(certificatePath, appSettings.IdentitySettings.IdentityCertificatePassword, OperatingSystem.IsWindows() ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet);
var validationParameters = new TokenValidationParameters
{
      ClockSkew = TimeSpan.Zero,
      RequireSignedTokens = true,
      ValidateIssuerSigningKey = true,
      IssuerSigningKey = new X509SecurityKey(signingCert),

New Error, same vein tho.

ail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.Security.Cryptography.CryptographicException: m_safeCertContext is an invalid handle.
         at System.Security.Cryptography.X509Certificates.X509Certificate.ThrowIfInvalid()
         at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PublicKey()
         at System.Security.Cryptography.X509Certificates.CertificateExtensionsCommon.GetPublicKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
         at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_PublicKey()
         at Microsoft.IdentityModel.Tokens.SupportedAlgorithms.IsSupportedAlgorithm(String algorithm, SecurityKey key)
         at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.IsSupportedAlgorithm(String algorithm, SecurityKey key)
         at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)
         at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm, Boolean cacheProvider)
         at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm)
         at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
         at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwtSecurityTokenPrivate(String issuer, String audience, ClaimsIdentity subject, Nullable`1 notBefore, Nullable`1 expires, Nullable`1 issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, IDictionary`2 claimCollection, String tokenType, IDictionary`2 additionalHeaderClaims, IDictionary`2 additionalInnerHeaderClaims)
         at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwtSecurityToken(SecurityTokenDescriptor tokenDescriptor)
         at bitBoilerplate1.Server.Services.AppSecureJwtDataFormat.Protect(AuthenticationTicket data, String purpose) in /Users/garun/code/blazoradmin/bitBoilerplate1/bitBoilerplate1/src/bitBoilerplate1.Server/Services/AppSecureJwtDataFormat.cs:line 46
         at bitBoilerplate1.Server.Services.AppSecureJwtDataFormat.Protect(AuthenticationTicket data) in /Users/garun/code/blazoradmin/bitBoilerplate1/bitBoilerplate1/src/bitBoilerplate1.Server/Services/AppSecureJwtDataFormat.cs:line 40
         at Microsoft.AspNetCore.Authentication.BearerToken.BearerTokenHandler.HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
         at Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext context, String scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
         at Microsoft.AspNetCore.Identity.SignInManager`1.SignInWithClaimsAsync(TUser user, AuthenticationProperties authenticationProperties, IEnumerable`1 additionalClaims)
         at Microsoft.AspNetCore.Identity.SignInManager`1.SignInOrTwoFactorAsync(TUser user, Boolean isPersistent, String loginProvider, Boolean bypassTwoFactor)
         at Microsoft.AspNetCore.Identity.SignInManager`1.PasswordSignInAsync(TUser user, String password, Boolean isPersistent, Boolean lockoutOnFailure)
         at Microsoft.AspNetCore.Identity.SignInManager`1.PasswordSignInAsync(String userName, String password, Boolean isPersistent, Boolean lockoutOnFailure)
         at bitBoilerplate1.Server.Controllers.Identity.IdentityController.SignIn(SignInRequestDto signInRequest) in /Users/garun/code/blazoradmin/bitBoilerplate1/bitBoilerplate1/src/bitBoilerplate1.Server/Controllers/Identity/IdentityController.cs:line 169
         at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
         at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
         at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
         at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)
ysmoradi commented 10 months ago

Ok, could you please send a PR with the following code? I can also handle this.

X509Certificate2 signingCert = new X509Certificate2(certificatePath, appSettings.IdentitySettings.IdentityCertificatePassword, OperatingSystem.IsWindows() ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet);
var validationParameters = new TokenValidationParameters
{
      ClockSkew = TimeSpan.Zero,
      RequireSignedTokens = true,
      ValidateIssuerSigningKey = true,
      IssuerSigningKey = new X509SecurityKey(signingCert),