AKushWarrior / steel_crypt

A collection of high-level API's exposing PointyCastle to perform hashing and encrypting in popular/secure algorithms.
https://pub.dev/packages/steel_crypt
Mozilla Public License 2.0
40 stars 10 forks source link

ASP.NET Core Identity PBKDF2 #9

Closed Lidgett closed 4 years ago

Lidgett commented 4 years ago

Hello.

I was wondering if you could help me. If you already have a hashed password done via ASP.NET Core Identity (PasswordHasher), is it possible to use this library to check the password hash against a plain text password?

I have tried to use PassCrypt with SHA-256 HMAC ('SHA-256/HMAC/PBKDF2') using the checkPassKey method but it does not work. Obviously it's fine if I generate the hash with this library and check it, however I need to authenticate already registered hashes.

The back-end uses this below: ASP.NET Core Identity Version 3: PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations

My knowledge of this isn't great hence why I'm asking for some help as to why it doesn't work. Any help would be much appreciated.

Thanks

Lidgett commented 4 years ago

Ok, so I did some digging and a lot of reading to better understand ASP.NET Core Identity and the way it works. Hopefully what I've discovered will help others in the future.

The end goal for me was to check a password against an already hashed password that has been retrieved from a database, using Dart of course!

Basically the hash generated by ASP.NET Identity is encoded to base64. It'll look something like this -> AQAAAAEAACcQAAAAEJSPbbFM1aeXB8fGRV7RRamlPjzktGG8FjwDWtFx35wrd4AxN6vm4zaD9EApc7WAsQ==

The idea is to decode this back into a List of Bytes and work out the salt and hash and run it against the hash algorithm.

For anyone interested, once decoded there are some "standards" to the bytes and each set of bytes tells us some important information.
For example, the first byte indicates whether the hash has been generated from Identity v2 or Identity v3. The 2nd to 5th byte indicates the KeyDerviation (so HMACSHA256 for example). I won't go into all of them, but you get details about the iteration count, the salt size, the actual salt and the hash.
The salt used to hash the password is the missing ingredient here, and this data lies within the 14th to 29th byte (it's always 16 bytes).
From 30th to 61st is the actual password hash.

So to solve my problem all I needed to do was extract the salt and chuck it back into the steel_crypts checkPassKey() method as this is the salt needed, not a newly generated one.

Now the PassCrypt class within this library didn't exactly fill my needs properly as the salt it expects is a String and any converts I did to it failed the unit tests, so I copied the class and changed the requirement from a String to a Uint8List (which is the list of bytes for the salt). Ironically deep within the library it actually uses Uint8List anyway, so the developer could easily adapt this class as I have.

The method I use is:

/// @password - the users entered password /// @hash - the hashed password value from the api bool check(String password, String hash, {int length = 32, int iterationCount}) { // the hash is base64 encoded, so decode it final List decoded = base64.decode(hash); // grab the salt final Uint8List extractedSalt = Uint8List.fromList(decoded.getRange(13, 29).toList()); // grab the hashedPassword final Uint8List hashedPassword = Uint8List.fromList(decoded.getRange(29, decoded.length).toList()); // hash the password with the extracted salt and check it return _checkPassKey(extractedSalt, password, base64Encode(hashedPassword), length: length, iterationCount: iterationCount); }

The result is so simple in the end after a bit of research!

The _checkPassKey() method is identical to the libraries one, as mentioned above, the only difference is the salt type (oh and iterationCount which I use to either speed up the check or make it more secure depending on mobile hardware).

I wonder if the owner could add something similar into this library so others can use it. A way to check a plain text password from an existing generated hash. All the relevant details can then also be extracted from this hash, which algorithm for example.

AKushWarrior commented 4 years ago

So there is reasoning behind this. You are correct in that Uint8Lists are more versatile and commonly used than Strings. However, I made the entire library deal in Strings because Strings are more friendly to the average programmer than Uint8Lists. The reason your hash didn't work is because it is encoded using base64, whereas I encode using utf-8. I'm not really willing to sacrifice user-friendliness for some edge cases revolving around encoding issues. Thus, I can't really address this in the main lib. If you think that a similar lib with Uint8Lists would be more valuable, feel free to branch this and do your own thing. For now, this is closed.