Last active
July 1, 2020 01:52
-
-
Save royashbrook/e708a7969084345069df4a43b3229567 to your computer and use it in GitHub Desktop.
cli for password hashing using dotnetcore identity with no user
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# this is actually just output from running in powershell, | |
# but I'll just add my comments in here along with the output | |
## First of all, I am going to set an alias to the compiled exe | |
└[C:\git\h]> set-alias h "C:\git\h\bin\Release\netcoreapp3.1\win-x64\h.exe" | |
## Generate a users file, I'm using a tab here because if you | |
## select this from a database query window, it'll be tab seperated | |
## `t is a tab character in powershell | |
## This first line puts a header row in there | |
└[C:\git\h]> "Username`tPassword" > users.txt | |
## Add a value for users 0-5 | |
└[C:\git\h]> 0..5 | %{"User$_`tPassword$_">>users.txt} | |
## show the file | |
└[C:\git\h]> type users.txt | |
Username Password | |
User0 Password0 | |
User1 Password1 | |
User2 Password2 | |
User3 Password3 | |
User4 Password4 | |
User5 Password5 | |
## Import the file, noting the delimiter is a `t, by default it's , | |
└[C:\git\h]> $users = ipcsv users.txt -Delimiter "`t" | |
## Now generate hashes for each password, note the call to the 'h.exe' here | |
└[C:\git\h]> $hashes = $users | select Username, @{n="Hash";e={h $_.Password}} | |
## Show them, note these will be different each time due to how the hasher | |
## works, but they will still match, also due to how the hasher works | |
└[C:\git\h]> $hashes | |
Username Hash | |
-------- ---- | |
User0 AQAAAAEAACcQAAAAEMEKg+0ZBbN5yrU61QwLjLmGtfqz9rxeI/JE+yh50b8MUSQ5ZP9jTnBo03Rgqjxinw== | |
User1 AQAAAAEAACcQAAAAEOITU5VLDv09PxDS0Srlx3fBfN/ESW8kcCDLvZWMA7MoiI+FaJxAEjTgowP0v8bkHw== | |
User2 AQAAAAEAACcQAAAAEDtt5KDzMHns6HAxu+BRVBC9fx4p+sbp7C865vBQ55FkEzwis4r0KZEVXF1tf68vkA== | |
User3 AQAAAAEAACcQAAAAEGl8rdnq7w32QMxprJvyhQm5c+uPStmvz+xWqC1h0KsufB9I2tfwjQB66BnoRmWm2A== | |
User4 AQAAAAEAACcQAAAAEICrchmblaQio2cGyDtuNKVoosNZBDYOpSlu/otk5ZlTICVGeIpGdY0hMxRZgGcnbA== | |
User5 AQAAAAEAACcQAAAAELZbMaqVlXex8t40fXlpyXpaW4PClcVrxqjTuIMP4rp7v6OCJy6CC0wnNQlmB4tGgw== | |
## Now we will generate some sql that holds the users and hashes. | |
## This will be wrapped with the actual statements needed to insert | |
## You'll see the output in the following lines, but just to provide | |
## some info on what is happening, I'm taking the hashes and then | |
## creating a new object with a property called sql that has a | |
## select statement for each value, then i use the join command | |
## for each of those sql values to put a new line and a union all | |
## sql command between them. What this basically will do is make | |
## a big select statement that will merge all of these values | |
└[C:\git\h]> $UsersAndHashes = ($hashes | select @{n="sql";e={"select '{0}','{1}'" -f $_.UserName,$_.Hash}} ).sql -join "`nunion all " | |
## This will actually generate the sql to run. You can see that the | |
## sql generated above is just placed into the middle of the value below | |
## Note: just in case you are not a powershell person, note that the >> at | |
## the front of the line is part of the way the shell shows commands that | |
## span a line, if you were running this at the command line, you would not | |
## have the >> characters at the front. This is what it looks like | |
## typed or pasted into the command line. | |
└[C:\git\h]> $SqlToRun = @" | |
>> ;with a(UserName,Hash) as ( | |
>> $UsersAndHashes | |
>> ) | |
>> insert into [dbo].[AspNetUsers] ([Id],[SecurityStamp],UserName,[NormalizedUserName],[EmailConfirmed],[PasswordHash],[TwoFactorEnabled],[LockoutEnabled],AccessFailedCount,PhoneNumberConfirmed) | |
>> select newid(),newid(),UserName, UPPER(UserName),1,[Hash],0,0,0,0 from a | |
>> "@ | |
## And here is the Sql you'll actually paste into a sql window with the | |
## aspnet identity tables in it. when i was working on this, i wrapped it | |
## in a tran so i could check it before and after. you should probably | |
## do the same just to be safe =). | |
## In case this sql is not clear, what is happening is we are using a CTE | |
## to basically serve as a temp table for the values we generated for | |
## users and passwordhashes, that is then getting used along with some | |
## default values with an insert statement to put the users into the | |
## required table. You have to add a value for the security stamp and | |
## the id, but these are just guids so newid works fine. you also have | |
## to add the 0 and 1 values as those are non null fields. Obviously, | |
## if you are wanting to set some different values, do so. This is just | |
## what I used for testing. I normalized to uppercase as that seemed | |
## to be what was in there when I created some users the normal way, | |
## although the 'normal' way had email addresses in there. This worked | |
## just fine with usernames though. | |
└[C:\git\h]> $SqlToRun | |
;with a(UserName,Hash) as ( | |
select 'User0','AQAAAAEAACcQAAAAEMEKg+0ZBbN5yrU61QwLjLmGtfqz9rxeI/JE+yh50b8MUSQ5ZP9jTnBo03Rgqjxinw==' | |
union all select 'User1','AQAAAAEAACcQAAAAEOITU5VLDv09PxDS0Srlx3fBfN/ESW8kcCDLvZWMA7MoiI+FaJxAEjTgowP0v8bkHw==' | |
union all select 'User2','AQAAAAEAACcQAAAAEDtt5KDzMHns6HAxu+BRVBC9fx4p+sbp7C865vBQ55FkEzwis4r0KZEVXF1tf68vkA==' | |
union all select 'User3','AQAAAAEAACcQAAAAEGl8rdnq7w32QMxprJvyhQm5c+uPStmvz+xWqC1h0KsufB9I2tfwjQB66BnoRmWm2A==' | |
union all select 'User4','AQAAAAEAACcQAAAAEICrchmblaQio2cGyDtuNKVoosNZBDYOpSlu/otk5ZlTICVGeIpGdY0hMxRZgGcnbA==' | |
union all select 'User5','AQAAAAEAACcQAAAAELZbMaqVlXex8t40fXlpyXpaW4PClcVrxqjTuIMP4rp7v6OCJy6CC0wnNQlmB4tGgw==' | |
) | |
insert into [dbo].[AspNetUsers] ([Id],[SecurityStamp],UserName,[NormalizedUserName],[EmailConfirmed],[PasswordHash],[TwoFactorEnabled],[LockoutEnabled],AccessFailedCount,PhoneNumberConfirmed) | |
select newid(),newid(),UserName, UPPER(UserName),1,[Hash],0,0,0,0 from a |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# make the base project | |
dotnet new console -o h | |
# switch to project dir | |
cd h | |
# add a couple of packages we'll need | |
dotnet add package Microsoft.AspNetCore.Identity | |
dotnet add package Microsoft.AspNetCore.Cryptography.KeyDerivation | |
# get the source file we'll tweak from github | |
$uri = "https://raw.githubusercontent.com/aspnet/Identity/master/src/Core/" | |
"PasswordHasher.cs" | %{iwr $uri$_ -o .\$_} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[C:\git\h]> dotnet build | |
Microsoft (R) Build Engine version 16.6.0+5ff7b0c9e for .NET Core | |
Copyright (C) Microsoft Corporation. All rights reserved. | |
Determining projects to restore... | |
All projects are up-to-date for restore. | |
h -> C:\git\h\bin\Debug\netcoreapp3.1\h.dll | |
Build succeeded. | |
0 Warning(s) | |
0 Error(s) | |
Time Elapsed 00:00:01.51 | |
└[C:\git\h]> $passwordHash = dotnet run password | |
└[C:\git\h]> $password1Hash = dotnet run password1 | |
└[C:\git\h]> $passwordHash | |
AQAAAAEAACcQAAAAEKBC6Tb5klZXeE51B8GwBssJWHVyqAy4O3BWPEVkaNzYx6XttTBXNuUYlI6Bu4sGFQ== | |
└[C:\git\h]> $password1Hash | |
AQAAAAEAACcQAAAAEEO6oT/nnLhprMbx2TwFhaM3WLdylwAS5bNVwAeWI9t0jD1BBLgL0+gcYmFS5y+7og== | |
└[C:\git\h]> dotnet run password $passwordHash | |
True | |
└[C:\git\h]> dotnet run password $password1Hash | |
False | |
└[C:\git\h]> dotnet run password1 $password1Hash | |
True | |
└[C:\git\h]> dotnet run password1 $passwordHash | |
False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//change this: | |
public class PasswordHasher<TUser> : IPasswordHasher<TUser> where TUser : class | |
//to this, or just remove everything after the comment below | |
public class PasswordHasher//<TUser> : IPasswordHasher<TUser> where TUser : class | |
//next, simply add some default values for these private vars | |
//these... | |
private readonly PasswordHasherCompatibilityMode _compatibilityMode; | |
private readonly int _iterCount; | |
private readonly RandomNumberGenerator _rng; | |
//become these... | |
//Note these are the default values from the PasswordHasherOptions.cs file | |
// you can see that here if you want: | |
//https://github.com/aspnet/Identity/blob/master/src/Core/PasswordHasherOptions.cs | |
private readonly PasswordHasherCompatibilityMode _compatibilityMode = PasswordHasherCompatibilityMode.IdentityV3; | |
private readonly int _iterCount = 10000; | |
private readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); | |
//next we are going to, basically, wipe out the constructor | |
// as we don't need to pass in any information | |
//replace this... | |
public PasswordHasher(IOptions<PasswordHasherOptions> optionsAccessor = null) | |
{ | |
var options = optionsAccessor?.Value ?? new PasswordHasherOptions(); | |
_compatibilityMode = options.CompatibilityMode; | |
switch (_compatibilityMode) | |
{ | |
case PasswordHasherCompatibilityMode.IdentityV2: | |
// nothing else to do | |
break; | |
case PasswordHasherCompatibilityMode.IdentityV3: | |
_iterCount = options.IterationCount; | |
if (_iterCount < 1) | |
{ | |
throw new InvalidOperationException(Resources.InvalidPasswordHasherIterationCount); | |
} | |
break; | |
default: | |
throw new InvalidOperationException(Resources.InvalidPasswordHasherCompatibilityMode); | |
} | |
_rng = options.Rng; | |
} | |
// with just this one line: | |
public PasswordHasher(){} | |
// and finally, we need to remove the user var from these two function calls | |
// so these lines... | |
public virtual string HashPassword(TUser user, string password) | |
public virtual PasswordVerificationResult VerifyHashedPassword(TUser user, string hashedPassword, string providedPassword) | |
//become these lines... | |
public virtual string HashPassword(string password) | |
public virtual PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace h | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
string result = "Syntax: hash <password> or hash <password> <hash>"; | |
if (args.Length==1 || args.Length==2){ | |
var passwordHasher = new Microsoft.AspNetCore.Identity.PasswordHasher(); | |
if(args.Length == 1) | |
result = passwordHasher.HashPassword(args[0]); | |
if(args.Length == 2) | |
result = (passwordHasher.VerifyHashedPassword(args[1],args[0]).ToString() == "Success").ToString(); | |
} | |
Console.WriteLine(result); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment