使用Python对ASP.NET角色/成员身份创建的数据库中的原始用户名、哈希、salt进行身份验证

使用Python对ASP.NET角色/成员身份创建的数据库中的原始用户名、哈希、salt进行身份验证,asp.net,python,hash,passwords,Asp.net,Python,Hash,Passwords,我们有一个当前应用程序,其中用户登录凭据存储在SQL Server数据库中。基本上,它们存储为纯文本用户名、密码散列和该散列的相关salt 这些都是由ASP.NET的成员/角色系统中的内置函数创建的。这是一行,用户名为“joe”,密码为“password”: joe,kDP0Py2QwEdJYtUX9cJABg==,OJF6H4KDXFGLU+oTDNFodCEfMA= 我已将此内容转储到CSV文件中,并尝试将其转换为Django的可用格式,该格式将密码存储为以下格式: [算法]$[盐]$[散列

我们有一个当前应用程序,其中用户登录凭据存储在SQL Server数据库中。基本上,它们存储为纯文本用户名、密码散列和该散列的相关salt

这些都是由ASP.NET的成员/角色系统中的内置函数创建的。这是一行,用户名为“joe”,密码为“password”:

joe,kDP0Py2QwEdJYtUX9cJABg==,OJF6H4KDXFGLU+oTDNFodCEfMA=

我已将此内容转储到CSV文件中,并尝试将其转换为Django的可用格式,该格式将密码存储为以下格式:

[算法]$[盐]$[散列]

其中salt是一个普通字符串,hash是SHA1哈希的十六进制摘要

到目前为止,我已经能够确定ASP正在以base64格式存储这些散列和盐。上面这些值被解码成二进制字符串

我们使用reflector收集了ASP如何根据这些值进行身份验证:

internal string EncodePassword(string pass, int passwordFormat, string salt)
{
    if (passwordFormat == 0)
    {
        return pass;
    }
    byte[] bytes = Encoding.Unicode.GetBytes(pass);
    byte[] src = Convert.FromBase64String(salt);
    byte[] dst = new byte[src.Length + bytes.Length];
    byte[] inArray = null;
    Buffer.BlockCopy(src, 0, dst, 0, src.Length);
    Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
    if (passwordFormat == 1)
    {
        HashAlgorithm algorithm = HashAlgorithm.Create(Membership.HashAlgorithmType);
        if ((algorithm == null) && Membership.IsHashAlgorithmFromMembershipConfig)
        {
            RuntimeConfig.GetAppConfig().Membership.ThrowHashAlgorithmException();
        }
        inArray = algorithm.ComputeHash(dst);
    }
    else
    {
        inArray = this.EncryptPassword(dst);
    }
    return Convert.ToBase64String(inArray);
}
因此,从DB中提取salt,b64将其解码为二进制表示。它对原始密码执行“GetBytes”操作,然后将其包含在内,首先是salt

然后对这个新字符串运行SHA1算法,base64对其进行编码,并将其与存储在数据库中的值进行比较

我曾试图编写一些代码,尝试在Python中重现这些哈希,但失败了。我将无法在Django中使用它们,直到我能够弄清楚这是如何翻译过来的。以下是我的测试方法:

import hashlib
from base64 import b64decode, b64encode

b64salt = "kDP0Py2QwEdJYtUX9cJABg=="
b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA="
binsalt = b64decode(b64salt)
password_string = 'password'

m1 = hashlib.sha1()
# Pass in salt
m1.update(binsalt)
# Pass in password
m1.update(password_string)
# B64 encode the binary digest
if b64encode(m1.digest()) == b64hash:
    print "Logged in!"
else:
    print "Didn't match"
    print b64hash
    print b64encode(m1.digest())

我想知道是否有人能看到我的方法中的任何缺陷,或者能建议一种替代方法。也许您可以使用上面的算法和上面的已知密码和salt,在您的系统上生成哈希值?

关于可能出现的问题,有两种想法

首先,来自反射的代码有三条路径:

  • 如果passwordFormat为0,则按原样返回密码
  • 如果passwordFormat为1,它将像python代码一样创建哈希
  • 如果passwordFormat不是0或1,它将调用此。EncryptPassword()
您如何知道您正在对密码进行哈希运算,而不是使用此.EncryptPassword()加密密码?您可能需要反转EncryptPassword()成员函数并复制它。这是除非你有一些信息,确保你是散列密码,而不是加密它

第二,如果确实是对密码进行哈希运算,您可能希望看到Encoding.Unicode.GetBytes()函数为字符串“password”返回的内容,因为您可能会得到如下结果:

0x00 0x70 0x00 0x61 0x00 0x73 0x00 0x73 0x00 0x77 0x00 0x6F 0x00 0x72 0x00 0x64
而不是:

0x70 0x61 0x73 0x73 0x77 0x6F 0x72 0x64

我希望这会有所帮助。

当您将UTF16字符串转换为二进制时,python似乎正在插入字节顺序标记。NET字节数组不包含BOM,因此我使用了一些犹太区python,将UTF16转换为十六进制,删除前4个字符,然后将其解码为二进制

也许有更好的方法来删除BOM表,但这对我来说很有用

这里有一个经过:

import hashlib
from base64 import b64decode, b64encode

def utf16tobin(s):
  return s.encode('hex')[4:].decode('hex')

b64salt = "kDP0Py2QwEdJYtUX9cJABg=="
b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA="
binsalt = b64decode(b64salt)
password_string = 'password'.encode("utf16")
password_string = utf16tobin(password_string)

m1 = hashlib.sha1()
# Pass in salt
m1.update(binsalt + password_string)
# Pass in password
# B64 encode the binary digest
if b64encode(m1.digest()) == b64hash:
    print "Logged in!"
else:
    print "Didn't match"
    print b64hash
    print b64encode(m1.digest())

我相信您需要的编码被称为“utf-16le”,因此您不必手动杀死BOM@BryanRehbein您是对的,使用
password\u string='password'.encode(“utf-16-le”)
您可以完全省略utf16tobin解决方案。