dazinator / AspNetCore.LegacyAuthCookieCompat

Provides classes to encrypt / decrypt asp.net 2 / 3.5 / 4 and 4.5 FormsAuthenticationTickets (cookies) without relying on system.web
MIT License
74 stars 18 forks source link

Error decrypting cookie #7

Closed jacobslusser closed 7 years ago

jacobslusser commented 7 years ago

I'm trying to get an older ASP.NET 4.x cookie decrypted in my ASP.NET Core 2.0 application but I get a System.Exception when I try and decrypt.

My older ASP.NET 4.x application is configured as:

<machineKey
  validationKey=""
  decryptionKey=""
  validation="SHA1"
  decryption="AES" />
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(new FormsAuthenticationTicket
    (
        1,
        user.Username,
        DateTime.Now,
        DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),
        false,
        JsonConvert.SerializeObject(userContext, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore })
    )));

My ASP.NET Core 2.0 code looks like this:

string cookie;
if (!Context.Request.Cookies.TryGetValue(Options.Name, out cookie))
{
    // No cookie was provided
    return Task.FromResult(AuthenticateResult.NoResult());
}

var decryptionKey = HexUtils.HexToBinary(Options.DecryptionKey);
var validationKey = HexUtils.HexToBinary(Options.ValidationKey);
var legacyFormsAuthenticationTicketEncryptor = new LegacyFormsAuthenticationTicketEncryptor(decryptionKey, validationKey);
var ticket = legacyFormsAuthenticationTicketEncryptor.DecryptCookie(cookie);

I've made sure the validation and decryption keys match and I've also tried changing my legacy application to use compatibilityMode="Framework20SP2" but I get an exception every time I try to decrypt. The value I'm passing to DecryptCookie is the plain cookie value, no prefix or suffix.

What am I doing wrong?

dazinator commented 7 years ago

Not sure! Perhaps you could add a failing test case to the tests?

jacobslusser commented 7 years ago

After some debugging it looks like the hash check is failing. Specifically, the Sha1HashProvider.CheckHash method is returning false to the CheckHashAndRemove method which returns a null array to the LegacyFormsAuthenticationTicketEncryptor.DecryptCookie method which throws a generic System.Exception.

Not knowing much about the binary cookie format myself... any guesses as to why the hash check would be failing?

dazinator commented 7 years ago

This looks to be basically the same issue reported in #5. The solution there seems to have been to use compatibilityMode="Framework20SP2" which you have already tried. Can you just double check that - by creating a brand new cookie with a brand new machine key, and compatibilityMode="Framework20SP2" has the same problem - If you have not double checked already that is.

To be honest I spent days putting this library together, by reverse engineering the .NET 3.5 stack in conjunction with reading the documented source code, to work out what was going on. IT WAS PRETTY PAINFUL and I don't want to have to wear that hat again. I did notice whilst doing this that the .NET 4, 4.5 frameworks had variations with how they handled the cookie decryption / encryption, but it wasn't my primary concern to support all possible code paths at the time, I was just focusing on my own code path. So I am not surprised that there may be some issues here. Unfortunately I won't have time to in the foreseeable future to investigate it though - however if you do work out what is going on, I will gladly accept a pull request.

Perhaps one of the commenters on #5 might be able to help you?

jacobslusser commented 7 years ago

Dude, you're a lifesaver! I was starting to look at having to roll my own and you are right... it is PAINFUL! Fortunately I had the wherewithal to circle back to see if I had done something wrong. It looks like I was either using compatibilityMode="Framework20SP1" instead of compatibilityMode="Framework20SP2" or I had made that change on my website Web.config instead of my API Web.config (the API in my site is hosted in a different application).

Once I got that straightened out, it worked perfectly and I was able to round-trip a cookie from old to new site and back again. You literally saved me days.

Thanks!

dazinator commented 7 years ago

Haha no problem, glad it helped!

nlptr commented 6 years ago

@jacobslusser : I am exactly in the same scenario as yours. Do you mind providing me the working code so i can take a look on the decryption ?

jacobslusser commented 6 years ago

The way I implemented this was to write a custom authentication handler based on the official ASP.NET Core cookies implementation here: https://github.com/aspnet/Security/blob/rel/2.0.0/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs

The part using the LegacyAuthCookieCompat library is basically this:

string cookie;
if (!Context.Request.Cookies.TryGetValue(Options.Name, out cookie) || string.IsNullOrEmpty(cookie))
{
    // No cookie was provided
    logger.LogInformation("Cookie was not found or was empty.");
    return AuthenticateResult.NoResult();
}

FormsAuthenticationTicket legacyTicket;
UserContext userContext;
try
{
    var encryptor = new LegacyFormsAuthenticationTicketEncryptor(Options.DecryptionKey, Options.ValidationKey);
    legacyTicket = encryptor.DecryptCookie(cookie);
    userContext = JsonConvert.DeserializeObject<UserContext>(legacyTicket.UserData);
}
catch (Exception ex)
{
    // Any exception here would just be garbage (e.g. System.Exception, CryptographicException, etc.)
    logger.LogWarning(ex, "Cookie authentication failed during decryption. Message: {Message}", ex.Message);
    return AuthenticateResult.Fail("Cookie was not in a valid format.");
}

The UserContext object is our model for user data.

omarhimada commented 4 years ago

Unfortunately my cookie is not evenly divisible by 2 and thus fails at the 'hex to binary' conversion step. I am not sure why my cookie is not in hexadecimal format

dazinator commented 4 years ago

@omarhimada

Unfortunately my cookie is not evenly divisible by 2 and thus fails at the 'hex to binary' conversion step. I am not sure why my cookie is not in hexadecimal format

What cookie. Any details on how its produced? Are you perhaps looking at the wrong authentication cookie?