Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@PalmerEk
Last active January 26, 2023 13:24
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save PalmerEk/1191651 to your computer and use it in GitHub Desktop.
Save PalmerEk/1191651 to your computer and use it in GitHub Desktop.
ASP.NET Membership password hash for Node.js
See updated reply by OpenSpacesAndPlaces
https://gist.github.com/PalmerEk/1191651?permalink_comment_id=4449026#gistcomment-4449026
/////////////////////////////////////////////////////////////
// ORIGINAL/OLD for posterity
/////////////////////////////////////////////////////////////
var crypto = require('crypto');
console.log(dotnet_membership_password_hash("welcome1", "qkI2wRPSW7Y4ArqWkfHm5g==")); // W25/z6MAywBax7DITuKgSmsXua4=
function dotnet_membership_password_hash(pass, salt)
{
var bytes = new Buffer(pass || '', 'ucs2');
var src = new Buffer(salt || '', 'base64');
var dst = new Buffer(src.length + bytes.length);
src.copy(dst, 0, 0, src.length);
bytes.copy(dst, src.length, 0, bytes.length);
return crypto.createHash('sha1').update(dst).digest('base64');
}
@chrisumbel
Copy link

if this works out well it warrants a blog post, brudda. this little function could solve one of the major stumbling blocks for people porting apps from ASP.Net to node.

@FrancoIbarra
Copy link

thank you!!

@brbeaird
Copy link

brbeaird commented May 5, 2022

I cannot express how useful this was. Thanks for sharing!

@OpenSpacesAndPlaces
Copy link

OpenSpacesAndPlaces commented Jan 25, 2023

@PalmerEk

The C# I'm trying to mirror has a longer salt:

   public static String GenerateSalt()
        {
            byte[] buf = new byte[64];
            (new RNGCryptoServiceProvider()).GetBytes(buf);
            return Convert.ToBase64String(buf);
        }

And the base alg is set as:
<machineKey compatibilityMode="Framework45" validationKey="**512ValdiationKey**" decryptionKey="**256Key**" validation="HMACSHA512" decryption="AES"/>

I've tried like:
crypto.createHmac('sha512', **512ValdiationKey**).update(dst).digest('base64');

Is there a different way this needs to be approached.

@OpenSpacesAndPlaces
Copy link

Should anyone else run into this - see:
https://referencesource.microsoft.com/#System.Web/Security/SQLMembershipProvider.cs,f37d42faca2b921e

private string [EncodePassword](https://referencesource.microsoft.com/System.Web/R/f37d42faca2b921e.html)(string pass, int passwordFormat, string salt)
        {
            if (passwordFormat == 0) // MembershipPasswordFormat.Clear
                return pass;
 
            byte[] bIn = [Encoding](https://referencesource.microsoft.com/mscorlib/A.html#3b6090c501893c25).[Unicode](https://referencesource.microsoft.com/mscorlib/A.html#3ab75dace56ae6d2).[GetBytes](https://referencesource.microsoft.com/mscorlib/A.html#83f2d2c6f22db1a9)(pass);
            byte[] bSalt = [Convert](https://referencesource.microsoft.com/mscorlib/A.html#fc990bd1275d43d6).[FromBase64String](https://referencesource.microsoft.com/mscorlib/A.html#08c34f52087ba624)(salt);
            byte[] bRet = null;
 
            if (passwordFormat == 1)
            { // MembershipPasswordFormat.Hashed
                [HashAlgorithm](https://referencesource.microsoft.com/mscorlib/A.html#e7c6be1ed86f474f) hm = [GetHashAlgorithm](https://referencesource.microsoft.com/System.Web/Security/SQLMembershipProvider.cs.html#0f16b82251e5f3d9)();
                if (hm is [KeyedHashAlgorithm](https://referencesource.microsoft.com/mscorlib/A.html#c68ffe2089d6c464)) {
                    [KeyedHashAlgorithm](https://referencesource.microsoft.com/mscorlib/A.html#c68ffe2089d6c464) kha = ([KeyedHashAlgorithm](https://referencesource.microsoft.com/mscorlib/A.html#c68ffe2089d6c464)) hm;
                    if (kha.[Key](https://referencesource.microsoft.com/mscorlib/A.html#922271ab9c7d178d).[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94) == bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94)) {
                        kha.[Key](https://referencesource.microsoft.com/mscorlib/A.html#922271ab9c7d178d) = bSalt;
                    } else if (kha.[Key](https://referencesource.microsoft.com/mscorlib/A.html#922271ab9c7d178d).[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94) < bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94)) {
                        byte[] bKey = new byte[kha.[Key](https://referencesource.microsoft.com/mscorlib/A.html#922271ab9c7d178d).[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94)];
                        [Buffer](https://referencesource.microsoft.com/mscorlib/A.html#570e88af5685d024).[BlockCopy](https://referencesource.microsoft.com/mscorlib/A.html#1fbec67d6ee92203)(bSalt, 0, bKey, 0, bKey.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94));
                        kha.[Key](https://referencesource.microsoft.com/mscorlib/A.html#922271ab9c7d178d) = bKey;
                    } else {
                        byte[] bKey = new byte[kha.[Key](https://referencesource.microsoft.com/mscorlib/A.html#922271ab9c7d178d).[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94)];
                        for (int iter = 0; iter < bKey.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94); ) {
                            int len = [Math](https://referencesource.microsoft.com/mscorlib/A.html#a4407e67b9a5afad).[Min](https://referencesource.microsoft.com/mscorlib/A.html#f441d55c88d635ad)(bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94), bKey.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94) - iter);
                            [Buffer](https://referencesource.microsoft.com/mscorlib/A.html#570e88af5685d024).[BlockCopy](https://referencesource.microsoft.com/mscorlib/A.html#1fbec67d6ee92203)(bSalt, 0, bKey, iter, len);
                            iter += len;
                        }
                        kha.[Key](https://referencesource.microsoft.com/mscorlib/A.html#922271ab9c7d178d) = bKey;
                    }
                    bRet = kha.[ComputeHash](https://referencesource.microsoft.com/mscorlib/A.html#a71a3cba2c656040)(bIn);
                }
                else {
                    byte[] bAll = new byte[bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94) + bIn.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94)];
                    [Buffer](https://referencesource.microsoft.com/mscorlib/A.html#570e88af5685d024).[BlockCopy](https://referencesource.microsoft.com/mscorlib/A.html#1fbec67d6ee92203)(bSalt, 0, bAll, 0, bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94));
                    [Buffer](https://referencesource.microsoft.com/mscorlib/A.html#570e88af5685d024).[BlockCopy](https://referencesource.microsoft.com/mscorlib/A.html#1fbec67d6ee92203)(bIn, 0, bAll, bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94), bIn.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94));
                    bRet = hm.[ComputeHash](https://referencesource.microsoft.com/mscorlib/A.html#a71a3cba2c656040)(bAll);
                }
            } else {
                byte[] bAll = new byte[bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94) + bIn.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94)];
                [Buffer](https://referencesource.microsoft.com/mscorlib/A.html#570e88af5685d024).[BlockCopy](https://referencesource.microsoft.com/mscorlib/A.html#1fbec67d6ee92203)(bSalt, 0, bAll, 0, bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94));
                [Buffer](https://referencesource.microsoft.com/mscorlib/A.html#570e88af5685d024).[BlockCopy](https://referencesource.microsoft.com/mscorlib/A.html#1fbec67d6ee92203)(bIn, 0, bAll, bSalt.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94), bIn.[Length](https://referencesource.microsoft.com/mscorlib/A.html#42e9b7616956cf94));
                bRet = [EncryptPassword](https://referencesource.microsoft.com/System.Web.ApplicationServices/A.html#a76e2e1b0e95333c)(bAll, [_LegacyPasswordCompatibilityMode](https://referencesource.microsoft.com/System.Web/Security/SQLMembershipProvider.cs.html#8d4dbd5be6f5073e));
            }
 
            return [Convert](https://referencesource.microsoft.com/mscorlib/A.html#fc990bd1275d43d6).[ToBase64String](https://referencesource.microsoft.com/mscorlib/A.html#f9e5bd7b69c5f334)(bRet);
        }
const dotnet_membership_password_hmachash = function (pass, salt, VALIDATION_KEY) {
    var key = null;
    var bIn = Buffer.from(pass, 'utf16le');
    var bSalt = Buffer.from(salt, 'base64');
    if (VALIDATION_KEY.length == bSalt.length) {
        key = bSalt;
    } else if (VALIDATION_KEY.length < bSalt.length) {

        var bKey = new Buffer(VALIDATION_KEY.length);

        bSalt.copy(bKey, 0, 0, bKey.Length);
        key = bKey;

    } else {
        var bKey = new Buffer(VALIDATION_KEY.length);

        for (var iter = 0; iter < bKey.length; )
        {
            var len = Math.min(bSalt.length, bKey.length - iter);
            bSalt.copy(bKey, iter, 0, len);
            iter += len;
        }

        key = bKey;
    }

    return crypto.createHmac('sha512', key).update(bIn).digest('base64');
};

@PalmerEk
Copy link
Author

Great job! So glad you were able to find the solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment