I have a webforms website that is calling into a new MVC6 website that we are working on. The user will login as they always have done on the webforms website using forms authentication and then get redirected to the new MVC6 website. I know in MVC6 that I should be using Cookie Authentication but cannot get it to decrypt the cookie. I suspect its down to changes around web.config and machinekey but am really stuck.
Here is what I have done.
I have set up cookie authentication as follows
app.UseCookieAuthentication(options =>
{
options.CookieName = "MyWebformsCookie";
options.AutomaticAuthenticate = true;
options.AuthenticationScheme = "Cookies";
options.TicketDataFormat = new MySecureDataFormat();
options.DataProtectionProvider = new MyDataProtectionProvider();
//options.CookieDomain = "localhost";
});
The class is as follows
public class MySecureDataFormat : ISecureDataFormat<AuthenticationTicket>
{
public string Protect(AuthenticationTicket data)
{
return string.Empty;
}
public string Protect(AuthenticationTicket data, string purpose)
{
return string.Empty;
}
public AuthenticationTicket Unprotect(string protectedText)
{
return null;
}
public AuthenticationTicket Unprotect(string protectedText, string purpose)
{
var ticket = FormsAuthentication.Decrypt(protectedText);
return null;
}
}
The cookie is being read, and the Unprotect method called, but then it errors on the FormsAuthentication.Decrypt method with error
An exception of type 'System.Web.HttpException' occurred in System.Web.dll but was not handled in user code
Additional information: Unable to validate data.
Stack = at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Boolean useValidationSymAlgo, Boolean useLegacyMode, IVType ivType, Boolean signData) at System.Web.Security.FormsAuthentication.Decrypt(String encryptedTicket) at WebApplication.Mvc.MySecureDataFormat.Unprotect(String protectedText, String purpose) in C:\SVNCode\GlobalConnectV2\WebApplication.Mvc\Startup.cs:line 153
at Microsoft.AspNet.Authentication.Cookies.CookieAuthenticationHandler.d__9.MoveNext()
So this leads me to believe that its not reading machine key. I have this in the web.config in wwwroot folder
<configuration>
<system.webServer>
...
</system.webServer>
<system.web>
<machineKey compatibilityMode="Framework20SP2" validation="SHA1" decryption="AES" validationKey="mykey" decryptionKey="dec" />
</system.web>
</configuration>
This works on earlier MVC apps but guessing something changed in MVC6. I have also tried the following but no luck
services.ConfigureDataProtection(configure =>
{
configure.UseCryptographicAlgorithms(new Microsoft.AspNet.DataProtection.AuthenticatedEncryption.AuthenticatedEncryptionOptions()
{
EncryptionAlgorithm = Microsoft.AspNet.DataProtection.AuthenticatedEncryption.EncryptionAlgorithm.AES_256_CBC,
ValidationAlgorithm = Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ValidationAlgorithm.HMACSHA256
});
});
Any advice?
Decrypt the cookie and check the digest: Decrypt de key of the cookie: do Base64 decoding, then decrypt it using your institution's private RSA key. Decrypt the data using the decrypted AES key. Check the digest using secutix public certificate. The following example in java will show you how to proceed.
The ASPXAUTH cookie is used to determine if a user is authenticated. As far as the location of the cookie, that depends on your browser. If you are using Firefox you can view the cookie by clicking on Tools -> Options -> Privacy. Then scroll down to the domain and expand it to see the cookie and its value.
ASPXAUTH in your browser. This is a cookie returned by Forms Authentication once the user is signed in. The value of the cookie contains an encrypted string that can be used to authenticate the user on subsequent requests.
The forms-authentication ticket supplies forms-authentication information to the next request made by the browser. With forms authentication, you can use the SetAuthCookie method when you want to authenticate a user but still retain control of the navigation with redirects.
I had no joy attempting to use FormsAuthentication.Decrypt()
in an ASP.NET 5 application.
In the end I wrote a decryption routine, based on the documentation available, and also looking at reference source code that Microsoft made available for system web.
The classes required to decrypt a forms authentication cookie that uses SHA1 for validation, and AES for encryption, can be found on my GIST here: https://gist.github.com/dazinator/0cdb8e1fbf81d3ed5d44
Once you have these, you can create a custom TicketFormat as before:
public class FormsAuthCookieTicketFormat : ISecureDataFormat<AuthenticationTicket>
{
private LegacyFormsAuthenticationTicketEncryptor _Encryptor;
private Sha1HashProvider _HashProvider;
public FormsAuthCookieTicketFormat(string decryptionKey, string validationKey)
{
_Encryptor = new LegacyFormsAuthenticationTicketEncryptor(decryptionKey);
_HashProvider = new Sha1HashProvider(validationKey);
}
public string Protect(AuthenticationTicket data)
{
throw new NotImplementedException();
}
public string Protect(AuthenticationTicket data, string purpose)
{
throw new NotImplementedException();
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
public AuthenticationTicket Unprotect(string protectedText, string purpose)
{
var ticket = _Encryptor.DecryptCookie(protectedText, _HashProvider);
var identity = new ClaimsIdentity("MyCookie");
identity.AddClaim(new Claim(ClaimTypes.Name, ticket.Name));
identity.AddClaim(new Claim(ClaimTypes.IsPersistent, ticket.IsPersistent.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Expired, ticket.Expired.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Expiration, ticket.Expiration.ToString()));
identity.AddClaim(new Claim(ClaimTypes.CookiePath, ticket.CookiePath));
identity.AddClaim(new Claim(ClaimTypes.Version, ticket.Version.ToString()));
// Add some additional properties to the authentication ticket.
var props = new AuthenticationProperties();
props.ExpiresUtc = ticket.Expiration.ToUniversalTime();
props.IsPersistent = ticket.IsPersistent;
var principal = new ClaimsPrincipal(identity);
var authTicket = new AuthenticationTicket(principal, props, CookieDetails.AuthenticationScheme);
return authTicket;
}
And wire it up like so:
var formsCookieFormat = new FormsAuthCookieTicketFormat(_DecryptionKeyText, _ValidationKeyText);
app.UseCookieAuthentication(options =>
{
// shortened for brevity...
options.TicketDataFormat = formsCookieFormat ;
options.CookieName = "MyCookie";
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With