Hello,
I'm migrating our newly updated CMS10/Commerce website from using the old .net Membership references to Episerver's aspnetidentity.
I followed the directions on this page, and all seemed to work well:
http://world.episerver.com/documentation/Items/Developers-Guide/Episerver-CMS/9/Security/episerver-aspnetidentity/
Next, I successfully ran a script that imported all our users from the old membership tables to the new aspnet identity tables.
Whenever I try to log on using an old login I get the following error:
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. ]
System.Convert.FromBase64_ComputeResultLength(Char* inputPtr, Int32 inputLength) +12271472
System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength) +71
System.Convert.FromBase64String(String s) +42
Microsoft.AspNet.Identity.Crypto.VerifyHashedPassword(String hashedPassword, String password) +53
Microsoft.AspNet.Identity.PasswordHasher.VerifyHashedPassword(String hashedPassword, String providedPassword) +11
Microsoft.AspNet.Identity.d__3e.MoveNext() +292
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.AspNet.Identity.d__17.MoveNext() +257
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.AspNet.Identity.d__12.MoveNext() +448
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.AspNet.Identity.AsyncHelper.RunSync(Func`1 func) +281
Microsoft.AspNet.Identity.UserManagerExtensions.Find(UserManager`2 manager, String userName, String password) +167
EPiServer.Cms.UI.AspNetIdentity.ApplicationSignInManager`1.SignIn(String userName, String password, String returnUrl) +117
EPiServer.Cms.UI.AspNetIdentity.ApplicationUISignInManager`1.SignIn(String providerName, String userName, String password) +44
EPiServer.UI.WebControls.Login.OnAuthenticate(AuthenticateEventArgs e) +77
System.Web.UI.WebControls.Login.AttemptLogin() +119
System.Web.UI.WebControls.Login.OnBubbleEvent(Object source, EventArgs e) +75
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +37
System.Web.UI.WebControls.Button.OnCommand(CommandEventArgs e) +114
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +260
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +12
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +15
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +35
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1639
After some searching, I tried implementing the following fix: http://sveinaandahl.blogspot.se/2016/03/how-to-validate-old-passwords-when.html
I added a UserManager Class:
using CMS.Models.ViewModels.Register;
using EPiServer.Cms.UI.AspNetIdentity;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
namespace CMS.Models
{
public class CustomUserManager : ApplicationUserManager
{
public CustomUserManager() : base(new UserStore(new ApplicationDbContext()))
{
this.PasswordHasher = new SQLPasswordHasher();
}
}
public class SQLPasswordHasher : PasswordHasher
{
public override string HashPassword(string password)
{
return base.HashPassword(password);
}
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
string[] passwordProperties = hashedPassword.Split('|');
if (passwordProperties.Length != 3)
{
return base.VerifyHashedPassword(hashedPassword, providedPassword);
}
else
{
string passwordHash = passwordProperties[0];
int passwordformat = 1;
string salt = passwordProperties[2];
if (String.Equals(EncryptPassword(providedPassword, passwordformat, salt), passwordHash, StringComparison.CurrentCultureIgnoreCase))
{
return PasswordVerificationResult.SuccessRehashNeeded;
}
else
{
return PasswordVerificationResult.Failed;
}
}
}
//This is copied from the existing SQL providers and is provided only for back-compat.
private string EncryptPassword(string pass, int passwordFormat, string salt)
{
if (passwordFormat == 0) // MembershipPasswordFormat.Clear
return pass;
byte[] bIn = Encoding.Unicode.GetBytes(pass);
byte[] bSalt = Convert.FromBase64String(salt);
byte[] bRet = null;
if (passwordFormat == 1)
{ // MembershipPasswordFormat.Hashed
HashAlgorithm hm = HashAlgorithm.Create("HMACSHA512");
if (hm is KeyedHashAlgorithm)
{
KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
if (kha.Key.Length == bSalt.Length)
{
kha.Key = bSalt;
}
else if (kha.Key.Length < bSalt.Length)
{
byte[] bKey = new byte[kha.Key.Length];
Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
kha.Key = bKey;
}
else
{
byte[] bKey = new byte[kha.Key.Length];
for (int iter = 0; iter < bKey.Length;)
{
int len = Math.Min(bSalt.Length, bKey.Length - iter);
Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
iter += len;
}
kha.Key = bKey;
}
bRet = kha.ComputeHash(bIn);
}
else
{
byte[] bAll = new byte[bSalt.Length + bIn.Length];
Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
bRet = hm.ComputeHash(bAll);
}
}
return Convert.ToBase64String(bRet);
}
}
}
And then I modified my StartUp.cs:
using CMS.Models;
using EPiServer.Cms.UI.AspNetIdentity;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
using System;
[assembly: OwinStartup("customStartup", typeof(CMS.Startup))]
namespace CMS
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Add CMS integration for ASP.NET Identity
app.AddCmsAspNetIdentity(new ApplicationOptions() { ConnectionStringName = "EcfSqlConnection" });
// Use cookie authentication
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/util/login.aspx"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity =
SecurityStampValidator.OnValidateIdentity<CustomUserManager, SiteUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user))
}
});
}
}
}
But it appears to have no effect, I still receive the same error every time. Has anyone dealt with this or fixed this that can help?
Thanks!
John