3

I have the following problem that I've been dealing for a couple of hours by now and it's driving nuts.

Context

I have a legacy database that stored passwords using the following algorithm. The legacy code used a Python library.

  • PBKDF2 with SHA256
  • 1000 iterations
  • Salt has a length of 8
  • Password is stored like this $salt$hashedPassword

I'm switching login flow for the new system and I need to migrate that old algorithm to a new one. New system uses .netcore

Question

What I'm trying to do is even possible?. How can I achieve it?

What my logic dictates is that I can take the salt and recreate the hashing algorithm using .netcore Crypto library but its not working and the function returns always false.

Legacy Code

from werkzeug.security import generate_password_hash, check_password_hash

def setPassword(self, password):
    self.password = generate_password_hash(password, method='pbkdf2:sha256')

Where generate_password_hash comes from the library, this is the code

SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

def generate_password_hash(password, method="pbkdf2:sha256", salt_length=8):
    """Hash a password with the given method and salt with a string of
    the given length. The format of the string returned includes the     method
    that was used so that :func:`check_password_hash` can check the hash.
    The format for the hashed string looks like this::
        method$salt$hash
    This method can **not** generate unsalted passwords but it is possible
    to set param method='plain' in order to enforce plaintext passwords.
    If a salt is used, hmac is used internally to salt the password.
    If PBKDF2 is wanted it can be enabled by setting the method to
    ``pbkdf2:method:iterations`` where iterations is optional::
        pbkdf2:sha256:80000$salt$hash
        pbkdf2:sha256$salt$hash
    :param password: the password to hash.
    :param method: the hash method to use (one that hashlib supports).     Can
               optionally be in the format ``pbkdf2:<method>[:iterations]``
               to enable PBKDF2.
    :param salt_length: the length of the salt in letters.
    """
salt = gen_salt(salt_length) if method != "plain" else ""
h, actual_method = _hash_internal(method, salt, password)
return "%s$%s$%s" % (actual_method, salt, h)

def gen_salt(length):
    """Generate a random string of SALT_CHARS with specified     ``length``."""
    if length <= 0:
        raise ValueError("Salt length must be positive")
    return "".join(_sys_rng.choice(SALT_CHARS) for _ in range_type(length))

Code

using System;
using System.Security.Cryptography;
using System.Text;

namespace test_pwd
{
    class Program
    {
        static void Main(string[] args)
        {

            var res = SameHash("Qwerty12", "84e8c8a5dbdafaf23523ffa5dfecf29d53522a35ca4c76fa877c5fcf9eb4b654", "laSgSC6R");
            Console.WriteLine(res);
        }

        public static bool SameHash(string userpwd, string storedHash, string storedSalt)
        {
            var saltByte = Encoding.UTF8.GetBytes(storedSalt);
            var rfc = new Rfc2898DeriveBytes(userpwd, saltByte, 1000);
            var baseString = Convert.ToBase64String(rfc.GetBytes(64));
            return baseString == storedHash;
        }
    }
}

Base string is converted into

k6vhCweBNz8ymMeEdhi+1czrea+oTTYLrW1OuwdinA78AFyEXKitpKUGLCt1ZdyS1Vka8Cptzd5u5Uzdbi4MbA==

Which is not the same as the stored password hash I'm sending. What I'm doing wrong or this idea is even feasible?.

Rajesh
  • 31
  • 2
  • 2
    Do you have access to the Python source code? – John Wu Nov 09 '19 at 07:29
  • Hello John, I updated my question an added reference links. Thanks for the help @JohnWu – Rajesh Nov 09 '19 at 08:04
  • In the past when we've had to do this rather than try and recreate algo X in c# we've used a different algo for the c#, and we've simply taken whatever the user typed, passed it to a python script implementing the existing hash and asked it if the password is correct. If it is, we re-hash the password given in the new algo and store it – Caius Jard Nov 09 '19 at 08:09
  • I would suggest you either step debug or log the result of each individual operation. It will be a lot easier to troubleshoot step by step with known good inputs and outputs. – John Wu Nov 09 '19 at 08:13
  • Perhaps [this](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing?view=aspnetcore-3.0) page on the MS Docs site can give you some hints. – fredrik Nov 09 '19 at 08:33
  • Try using the [Rfc2898DeriveBytes constructor where you can specify the hash algorithm](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes.-ctor?view=netframework-4.8#System_Security_Cryptography_Rfc2898DeriveBytes__ctor_System_Byte___System_Byte___System_Int32_System_Security_Cryptography_HashAlgorithmName_) and specify `HashAlgorithmName.SHA256` as the default implementation (which you used) uses SHA-1. – ckuri Nov 09 '19 at 23:34
  • any success on that. I have a similar problem to resolve, but since the python app still using the same DB we can not replace it. – mzain Nov 05 '21 at 15:18

0 Answers0