Error occurred during a cryptographic operation in debug

added stack trace

You didn't. Pretty important to understand why you get such a useless exception message. It is intentional. System.Web hides the real reason that the cryptographic code failed. You get a bland error message ("it did not work") and no stack trace of the actual code that failed.

Important because not doing so is dangerous, it allows an attacker to probe your web app with intentionally malformed data and gain knowledge from the exceptions to find a way to crack your secure code.

You need to get a better stack trace and exception message to find the real reason. That requires you to tell the debugger to stop when the exception is thrown. The real one, not the bland one. In VS2015, use Debug > Windows > Exception Settings. Click the "Common Language Runtime Exceptions" checkbox so it turns from a rectangle to a check-mark. Also: Tools > Options > Debugging > General > untick the "Enable Just My Code" checkbox.


The reason for the CryptographicException is explained by the documentation for the MachineKey.Unprotect method, which is also available in VS' Intellisense:

"Possible causes include the following: The application is deployed to more than one server and is using auto-generated encryption keys." (emphasis added)

This error occurs when you have an existing session cookie with the same session ID as a previous session, but the server is using a different key for crypto operations. ADALTokenCache is encrypting / decrypting the session using symmetric encryption, which requires the same key in both directions.

The session data is retrieved from the UserTokenCaches table as follows:

  1. Get the userID (e.g. from claims)
  2. Retrieve cacheBits where webUserUniqueID = userID
  3. Decrypt cacheBits using MachineKey as the decryption key

When MachineKey has changed since the value was first encrypted, the decryption fails and throws a CryptographicException. You can create a similar situation by copying these two symmetric encryption examples: Encrypting Data and Decrypting Data, but changing either of the keys.

For some reason, when you updated Visual Studio it re-generated your MachineKey. Since your web browser still had the same session cookie, ASP.NET retrieved your earlier session from the DB, but couldn't decrypt it anymore. All the other answers solve the problem for local debugging by preventing AdalTokenCache from being able to retrieve the previous session, but don't really address the underlying reason (and certainly aren't viable for production systems!)

  • Clearing your cookies works, as ASP.NET has to create a new session instead of retrieving your previous one. This would be difficult to communicate to the users of a production system.
  • Deleting all rows from the UserTokenCaches database table works for the same reason - it can't retrieve the previous session and has to make a new one instead. This would end all user sessions across all production instances of your application, and you won't get a maintenance window to do it in, because some of your users will be getting exceptions.

A better solution is to ensure that production deployments are all using the same encryption key (e.g. set MachineKey in web.config), or by securing access to session data in a different way. Some people work around this by storing the encryption key in the same database as the encrypted data, but I don't really see the point of doing this...


Ran into the same issue. After 'jacking with it' for more than a hour, I went into the member database (often auto-created by Visual Studio) and removed all the rows from the UserTokenCaches table. Ran the application, got past the crytographic error message. A new cache token record was created and inserted into the table.


Found a solution to this.

The section of code the error was coming from is the AdalToken.Cache.cs file.

userId = signedInUserId;
this.AfterAccess = AfterAccessNotification;
this.BeforeAccess = BeforeAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
// look up the entry in the database
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
// place the entry in memory
this.Deserialize((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits,"ADALCache"));

Specifically the last line.

Also relevant is the context for the db.UserTokenCacheList which is:

{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }

        public DbSet<UserTokenCache> UserTokenCacheList { get; set; }
    }

    public class UserTokenCache
    {
        [Key]
        public int UserTokenCacheId { get; set; }
        public string webUserUniqueId { get; set; }
        public byte[] cacheBits { get; set; }
        public DateTime LastWrite { get; set; }
    }
}

All of this was generated by visual studio when I went through the wizard for setting up Azure authentication when I started this new project.

Regarding the base("DefaultConnection") in the ApplicationDbContext.

There was no entry for this in my web.config, however up until recently this has always worked.

In the web.config, within the < connectionStrings > I added a line for DefaultConnection to point at my database and now it all works, at least for now.

Hope this may be of help to anyone who gets the same error.

Tags:

C#