You need to create a new Identity Server plugin.
I used Sitecore.Plugin.Authentication.SameSite
as an example. Duplicate the folder sitecore\Sitecore.Plugin.Authentication.SameSite
and give it a new name such as Sitecore.Plugin.IdentityServer.Custom
.
Create a new project in VS using netstandard2.0 as the target framework:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.14">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.14">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="IdentityServer4.Contrib.Membership">
<HintPath>IdentityServer4.Contrib.Membership.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<None Update="IdentityServer4.Contrib.Membership.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Reference the OOTB IdentityServer4.Contrib.Membership.dll as shown above.
Create a new class ConfigurableMembershipPasswordHasher
public class ConfigurableMembershipPasswordHasher : IMembershipPasswordHasher
{
private readonly string _hashAlgorithm;
public ConfigurableMembershipPasswordHasher(string hashAlgorithm)
{
_hashAlgorithm = ((!string.IsNullOrEmpty(hashAlgorithm)) ? hashAlgorithm : "SHA256");
}
/// <summary>Encrypts a password using the given format and salt</summary>
/// <param name="password">The password to encrypt / hash</param>
/// <param name="passwordFormat">The format to use 0 = clear, 1 = encrypt</param>
/// <param name="salt">The salt to apply to the password</param>
/// <returns>The encrypted / hashed password</returns>
/// <remarks>Taken from the original Membership code</remarks>
public string EncryptPassword(string password, int passwordFormat, string salt)
{
switch (passwordFormat)
{
case 0:
return password;
case 1:
return GetPasswordHash(password, salt);
default:
throw new InvalidOperationException($"Unsupported Password Format '{passwordFormat}'");
}
}
private string GetPasswordHash(string password, string salt)
{
byte[] bytes = Encoding.Unicode.GetBytes(password);
byte[] array = Convert.FromBase64String(salt);
HashAlgorithm hashAlgorithm = GetHashAlgorithm();
byte[] inArray;
if (hashAlgorithm is KeyedHashAlgorithm keyedHashAlgorithm)
{
if (keyedHashAlgorithm.Key.Length == array.Length)
{
keyedHashAlgorithm.Key = array;
}
else if (keyedHashAlgorithm.Key.Length < array.Length)
{
byte[] array2 = new byte[keyedHashAlgorithm.Key.Length];
Buffer.BlockCopy(array, 0, array2, 0, array2.Length);
keyedHashAlgorithm.Key = array2;
}
else
{
byte[] array3 = new byte[keyedHashAlgorithm.Key.Length];
int num;
for (int i = 0; i < array3.Length; i += num)
{
num = Math.Min(array.Length, array3.Length - i);
Buffer.BlockCopy(array, 0, array3, i, num);
}
keyedHashAlgorithm.Key = array3;
}
inArray = keyedHashAlgorithm.ComputeHash(bytes);
}
else
{
byte[] array4 = new byte[array.Length + bytes.Length];
Buffer.BlockCopy(array, 0, array4, 0, array.Length);
Buffer.BlockCopy(bytes, 0, array4, array.Length, bytes.Length);
inArray = hashAlgorithm.ComputeHash(array4);
}
return Convert.ToBase64String(inArray);
}
private HashAlgorithm GetHashAlgorithm()
{
string[] pBKDF2Hash = new string[] { "PBKDF2Hash" };
string[] source = new string[3] { "SHA", "SHA1", "SHA1Cng" };
string[] source2 = new string[3] { "SHA256", "SHA-256", "SHA256Cng" };
string[] source3 = new string[3] { "SHA384", "SHA-384", "SHA384Cng" };
string[] source4 = new string[3] { "SHA512", "SHA-512", "SHA512Cng" };
string[] source5 = new string[4] { "HMACSHA1", "HMACSHA256", "HMACSHA384", "HMACSHA512" };
if (pBKDF2Hash.Contains<string>(_hashAlgorithm, StringComparer.OrdinalIgnoreCase))
{
return PBKDF2Hash.Create();
}
if (source.Contains<string>(_hashAlgorithm, StringComparer.OrdinalIgnoreCase))
{
return SHA1.Create();
}
if (source2.Contains<string>(_hashAlgorithm, StringComparer.OrdinalIgnoreCase))
{
return SHA256.Create();
}
if (source3.Contains<string>(_hashAlgorithm, StringComparer.OrdinalIgnoreCase))
{
return SHA384.Create();
}
if (source4.Contains<string>(_hashAlgorithm, StringComparer.OrdinalIgnoreCase))
{
return SHA512.Create();
}
if (source5.Contains<string>(_hashAlgorithm, StringComparer.OrdinalIgnoreCase))
{
return HMAC.Create(_hashAlgorithm);
}
throw new InvalidOperationException("Unsupported Password Hash Algorithm '" + _hashAlgorithm + "'");
}
}
Create a new class
public sealed class ConfigureSitecore
{
public void ConfigureServices(IServiceCollection services)
{
services.Replace(new ServiceDescriptor(typeof(IMembershipPasswordHasher), (IServiceProvider p) => new ConfigurableMembershipPasswordHasher("PBKDF2Hash"), ServiceLifetime.Transient));
}
}
Build and copy the DLL's and config to identity server, then restart the server. It should be using the new hashing algorithm.