Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Android Chrome - Cryptographic Exception #6480

Closed
rynowak opened this issue Jul 2, 2017 · 17 comments
Closed

Android Chrome - Cryptographic Exception #6480

rynowak opened this issue Jul 2, 2017 · 17 comments
Assignees
Milestone

Comments

@rynowak
Copy link
Member

rynowak commented Jul 2, 2017

From @troncomputers on July 2, 2017 9:11

Hi!
Sometimes we are getting Cryptographic Exception on Android Chrome. Never happend on desktop.
I'm new to ASP, specialy Core.
Project is made with ASP .NET Core WebPages (Razor with seperate model class) with EntityFramework.
Microsoft.AspNetCore.All
2.0.0-preview1-final
ERROR:

An unhandled exception occurred while processing the request.

CryptographicException: The key {da62fb91-5285-48aa-84f0-f1360c5a2dd7} was not found in the key ring.

Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)

CryptographicException: The key {da62fb91-5285-48aa-84f0-f1360c5a2dd7} was not found in the key ring.
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.DangerousUnprotect(Byte[] protectedData, bool ignoreRevocationErrors, out bool requiresMigration, out bool wasRevoked)
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData)
Microsoft.AspNetCore.Mvc.ViewFeatures.CookieTempDataProvider.LoadTempData(HttpContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.TempDataDictionary.Load()
Microsoft.AspNetCore.Mvc.ViewFeatures.TempDataDictionary.get_Item(string key)
Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.SaveTempDataPropertyFilterBase.SetPropertyVaules(ITempDataDictionary tempData, object subject)
Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.PageSaveTempDataPropertyFilter.ApplyTempDataChanges(HttpContext httpContext)
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker+<ExecutePageAsync>d__13.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker+<InvokeInnerFilterAsync>d__10.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Core.Internal.ResourceInvoker+<InvokeNextResourceFilter>d__18.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Core.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Core.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Core.Internal.ResourceInvoker+<InvokeFilterPipelineAsync>d__13.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Core.Internal.ResourceInvoker+<InvokeAsync>d__11.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Builder.RouterMiddleware+<Invoke>d__4.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware+<Invoke>d__6.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__7.MoveNext()

I posted this error on different repo and someone explain me that this is a server side error and has nothing to do with Android. I'm getting this exception only on mobile.
Android puts allmost every app to 'background', maybe is cache problem or ASP session with old "session keys".

Please help!

Copied from original issue: dotnet/aspnetcore#2083

@rynowak
Copy link
Member Author

rynowak commented Jul 2, 2017

This error is definitely coming from our stack. This is the piece of code that tries to decrypt temporary data stored in a cookie. This should not be crashing like this even if the data can't be decrypted. But we will probably need more information to really understand the cause

@blowdart - ideas about when/how this could occur? Could this be due to ephemeral keys on linux?

@troncomputers - what's the server platform?

@rynowak rynowak added the bug label Jul 2, 2017
@rynowak
Copy link
Member Author

rynowak commented Jul 2, 2017

I'm moving this issue to MVC which is a more appropriate place for it.

@blowdart
Copy link
Member

blowdart commented Jul 2, 2017

Also is the user authenticated when this happens?

@troncomputers
Copy link

@rynowak
Platform:
Windows Server 2016
SQL Server 2016 Standard
IIS ver. 10.0.14393.0
@blowdart
I do not set "isPersistent" to true so should not be logged in.
It might be problem with compiled project? At the end of a day I do "publish" and copy only wwwroot directory, projectName.dll and projectName.PrecompiledViews.dll to server.

@rynowak
Copy link
Member Author

rynowak commented Jul 3, 2017

The relevant part of the stack is this:

CryptographicException: The key {da62fb91-5285-48aa-84f0-f1360c5a2dd7} was not found in the key ring.

Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)

CryptographicException: The key {da62fb91-5285-48aa-84f0-f1360c5a2dd7} was not found in the key ring.
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.DangerousUnprotect(Byte[] protectedData, bool ignoreRevocationErrors, out bool requiresMigration, out bool wasRevoked)
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData)
Microsoft.AspNetCore.Mvc.ViewFeatures.CookieTempDataProvider.LoadTempData(HttpContext context)

@blowdart - does the encrypted data get stamped with a key identifier? That's what this looks like to me.


To repeat, we should not be throwing an exception when something like this happens - temp data is optional/transient and all kinds of things can go wrong. However, I want to understand if something underlying is broken as well.

@troncomputers
Copy link

I can make an account for you on my project website to test it. There are still fake data so you can do whatever you want to fix this.

@blowdart
Copy link
Member

blowdart commented Jul 3, 2017

@rynowak Yes everything that goes through data protection keys a KID.

@troncomputers Can you turn on debug logging and watch what happens at app startup? There may be a data protection log in there I'd like to see. Did you run the deployment script for data protection and IIS? Or does the app pool load a profile?

@troncomputers
Copy link

troncomputers commented Jul 3, 2017

@blowdart Like I said, I am new to ASP .NET, I need some guidance if you want satisfying answer. Sorry about that but I'm trying to learn on that project.
Most of my configuration code I took from this sample project:
RazorPagesSample
My Startup.cs

public class Startup
    {
        public Startup(IConfiguration configuration, IHostingEnvironment env)
        {
            Configuration = configuration;
            HostingEnvironment = env;
        }

        public IConfiguration Configuration { get; }
        public IHostingEnvironment HostingEnvironment { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.AddMvc();

            return services.BuildServiceProvider(validateScopes: HostingEnvironment.IsDevelopment());
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app)
        {
            if (HostingEnvironment.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else 
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();
            app.UseAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

My Program.cs

 public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseKestrel()
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();
    }

And Login.cshtml.cs (Login model class)

public class LoginModel : PageModel
    {
        private readonly string _externalCookieScheme;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly ILogger<LoginModel> _logger;

        public LoginModel(SignInManager<ApplicationUser> signInManager, IOptions<IdentityCookieOptions> identityCookieOptions, ILogger<LoginModel> logger)
        {
            _signInManager = signInManager;
            _externalCookieScheme = identityCookieOptions.Value.ApplicationCookieAuthenticationScheme;
            _logger = logger;
        }

        [Required]
        [EmailAddress]
        [BindProperty]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [BindProperty]
        public string Password { get; set; }

        public string ReturnUrl { get; set; }

        [TempData]
        public string ErrorMessage { get; set; }

        public async Task OnGetAsync(string returnUrl = null)
        {
            if (!string.IsNullOrEmpty(ErrorMessage))
                ModelState.AddModelError(string.Empty, ErrorMessage);

            await HttpContext.SignOutAsync(_externalCookieScheme);

            ReturnUrl = returnUrl;
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            ReturnUrl = returnUrl;

            if(ModelState.IsValid)
            {
                var result = await _signInManager.PasswordSignInAsync(Email, Password, false, lockoutOnFailure: true);

                if(result.Succeeded)
                {
                    _logger.LogInformation(1, "Zalogowany");
                    return RedirectToPage("/");
                }
                else
                {
                    ModelState.AddModelError(string.Empty, "Niepoprawne dane do logowania.");
                    return Page();
                }
            }
            return Page();
        }
    }

@blowdart
Copy link
Member

blowdart commented Jul 3, 2017

Ok, if this is in a production environment, or under IIS rather than IIS express there's a step you need to go through first;

Near the bottom of the IIS docs is a section on configuring data protection, which gives you the following options;

To configure Data Protection under IIS you must use one of the following approaches:

  • Run a powershell script to create suitable registry entries (For example, .\Provision-AutoGenKeys.ps1 DefaultAppPool). This will store keys in the registry, protected using DPAPI with a machine wide key.
  • Configure the IIS Application Pool to load the user profile. This setting is in the Process Model section under the Advanced Settings for the application pool. Set Load User Profile to True. This will store keys under the user profile directory, and protected using DPAPI with a key specific to the user account used for the app pool.
  • Adjust your application code to use the file system as a key ring store. Use an X509 certificate to protect the key ring and ensure it is a trusted certificate. For example, if it is a self signed certificate you must place it in the Trusted Root store.

I'd try option 1 or two first. You need 3 if you're going to load balance over multiple machines.

@troncomputers
Copy link

@blowdart First of all after I did first 2 points website on desktop is just 1000% faster - faster loading, faster searching (getting data from database) etc.
On mobile everything is fine, no exceptions and also really fast.
Was that security "thing" not configured slowing down my website and throwing exceptions on mobile?

@blowdart
Copy link
Member

blowdart commented Jul 3, 2017

Aha. it works? Great.

So what happens in data protection is we generate our own encryption keys, and rotate them. But if you don't configure where they're stored it has to start again from scratch, as they are only retained in memory for as long as your app runs. Generating keys takes a little while, so you're incurring that cost every time you restart your app. So it would explain the initial load at least. But nothing else :)

@troncomputers
Copy link

You said that you should not throw exception when decripting cookies. Is it still a bug or just my no experience?

@blowdart
Copy link
Member

blowdart commented Jul 3, 2017

It should work fine now, well, for identity and csrf cookies. If it's still happening then something else is going on.

@troncomputers
Copy link

Thanx for help, you saved my life 👍

@rynowak
Copy link
Member Author

rynowak commented Jul 4, 2017

@Eilon - thoughts on making this not throw for 2.0.0?

@blowdart
Copy link
Member

blowdart commented Jul 5, 2017

It needs to error somehow I'm afraid.

@rynowak
Copy link
Member Author

rynowak commented Jul 5, 2017

Let's discuss this briefly tomorrow, I want to understand your concerns

@rynowak rynowak added this to the 2.0.0 milestone Jul 7, 2017
@rynowak rynowak self-assigned this Jul 7, 2017
rynowak added a commit that referenced this issue Jul 7, 2017
This change logs when we encounter and exception reading temp data from a
cookie and swallows the exception. Additionally, we clear the cookie so
that this won't happen on subsequent requests.

This will handle cases where data protection is misconfigured, or a
request just has general garbage in the cookies.
rynowak added a commit that referenced this issue Jul 7, 2017
This change logs when we encounter and exception reading temp data from a
cookie and swallows the exception. Additionally, we clear the cookie so
that this won't happen on subsequent requests.

This will handle cases where data protection is misconfigured, or a
request just has general garbage in the cookies.
@rynowak rynowak closed this as completed in f80f7ce Jul 7, 2017
@rynowak rynowak added 3 - Done and removed 1 - Ready labels Jul 7, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants