使用.NET或MS SQL模拟MySql的password()加密

6

我正在将一个旧的ASP / MySql webapp更新为ASP.NET / MS SQL。

我们希望在新应用程序中保留来自旧网站的登录功能。

不幸的是,密码是使用MySql的password()函数存储在MySql DB中的。

是否可以在.NET或MS SQL中模拟MySql的password()函数?

感谢任何帮助/链接。


1
你正在使用哪个版本的MySQL?如果你知道你正在使用的版本,这似乎是可以实现的。MySQL使用双重SHA1哈希算法来编码密码。 - Scott Arrington
我会在获得mysql数据库访问权限后进行检查。感谢您发布的解决方案! - Thomas Stock
7个回答

14

据MySQL文档介绍,该算法是双重SHA1哈希。当检查MySQL源代码时,在libmysql/password.c中找到一个名为make_scrambled_password()的函数。该函数定义如下:

/*
    MySQL 4.1.1 password hashing: SHA conversion (see RFC 2289, 3174) twice
    applied to the password string, and then produced octet sequence is
    converted to hex string.
    The result of this function is used as return value from PASSWORD() and
    is stored in the database.
  SYNOPSIS
    make_scrambled_password()
    buf       OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string
    password  IN  NULL-terminated password string
*/

void
make_scrambled_password(char *to, const char *password)
{
  SHA1_CONTEXT sha1_context;
  uint8 hash_stage2[SHA1_HASH_SIZE];

  mysql_sha1_reset(&sha1_context);
  /* stage 1: hash password */
  mysql_sha1_input(&sha1_context, (uint8 *) password, (uint) strlen(password));
  mysql_sha1_result(&sha1_context, (uint8 *) to);
  /* stage 2: hash stage1 output */
  mysql_sha1_reset(&sha1_context);
  mysql_sha1_input(&sha1_context, (uint8 *) to, SHA1_HASH_SIZE);
  /* separate buffer is used to pass 'to' in octet2hex */
  mysql_sha1_result(&sha1_context, hash_stage2);
  /* convert hash_stage2 to hex string */
  *to++= PVERSION41_CHAR;
  octet2hex(to, (const char*) hash_stage2, SHA1_HASH_SIZE);
}

通过这个方法,你可以创建一个.NET版本的相同功能的方法。以下是我开发的代码。当我在我的本地MySQL上运行SELECT PASSWORD('test');语句时,返回的值为:

*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29

根据源代码(password.c中),开头的星号表示这是加密密码的MySQL 4.1后的方法。例如,在VB.Net中模拟这个功能时,我得到了这个结果:

Public Function GenerateMySQLHash(ByVal strKey As String) As String
    Dim keyArray As Byte() = Encoding.UTF8.GetBytes(strKey)
    Dim enc = New SHA1Managed()
    Dim encodedKey = enc.ComputeHash(enc.ComputeHash(keyArray))
    Dim myBuilder As New StringBuilder(encodedKey.Length)

    For Each b As Byte In encodedKey
        myBuilder.Append(b.ToString("X2"))
    Next

    Return "*" & myBuilder.ToString()
End Function

请注意,SHA1Managed()方法位于System.Security.Cryptography命名空间中。该方法返回与MySQL中的PASSWORD()调用相同的输出。希望这可以帮助你。

编辑:以下是C#版本的相同代码:

public string GenerateMySQLHash(string key)
{
    byte[] keyArray = Encoding.UTF8.GetBytes(key);
    SHA1Managed enc = new SHA1Managed();
    byte[] encodedKey = enc.ComputeHash(enc.ComputeHash(keyArray));
    StringBuilder myBuilder = new StringBuilder(encodedKey.Length);

    foreach (byte b in encodedKey)
        myBuilder.Append(b.ToString("X2"));

    return "*" + myBuilder.ToString();
}

太棒了,谢谢!我会尝试看看它是否适用于我的 MySQL 版本(目前无法检查),并让您知道。非常感谢您的研究和出色的解决方案。 - Thomas Stock
如果您使用的是4.1.1之前的版本,则有一种较旧的加密方法。我也可以帮助您编写该方法。让我知道,我很乐意提供帮助。祝你好运! - Scott Arrington
我实际上也找到了一种使用T-SQL的方法。虽然它看起来很丑,但它能用!SELECT '*' + SUBSTRING(master.dbo.fn_varbintohexstr(HASHBYTES('SHA1',HASHBYTES('SHA1','VALUE-TO-ENCRYPT-GOES-HERE'))),1,42) - Scott Arrington
谢谢!mysql 4.1.1 密码函数的 Python 版本在这里:http://csl.sublevel3.org/mysql-password-function/。如果您能够同时包含 4.1 版本之前的版本,我会非常感激,因为仍有许多密码采用旧格式。 - nealmcb

1

C#版本已修改以解决填充问题。
[测试] public void GenerateMySQLHashX2() { string password = "20iva05";

        byte[] keyArray = Encoding.UTF8.GetBytes(password);
        SHA1Managed enc = new SHA1Managed();
        byte[] encodedKey = enc.ComputeHash(enc.ComputeHash(keyArray));
        StringBuilder myBuilder = new StringBuilder(encodedKey.Length);

        foreach (byte b in encodedKey)
        {
            log.Debug(b.ToString() + "    ->   " +  b.ToString("X2"));
            myBuilder.Append(b.ToString("X2"));
        }

        Assert.AreEqual("*8C2029A96507478FD1720F6B713ADD57C66EED49", "*" + myBuilder.ToString());
    } 

1
"他们不会披露他们使用的算法。"
嗯...有人应该提醒你 MySQL 是 开源 的...他们不需要“披露”什么,因为每个人都可以阅读数据库的结构-只需下载源代码,搜索加密函数(我猜他们正在使用C++)并开始调查... SHA1现在似乎相当不安全-最近一些科学家已经证明,可以选择多达 20% 没有限制的数据并生成相同的散列,因此建议使用AES或SHA2(因此不使用MySQL密码功能)"

1

在.Net中,您可以使用MD5或SHA1加密字符串,但MySQL实际使用的算法可能与这两种方法不同。我怀疑它基于某种基于服务器实例的“盐”,但我不确定。

理论上,由于我相信MySQL是开源的,您可以调查源代码并确定如何完成此操作。

http://dev.mysql.com/doc/refman/5.1/en/encryption-functions.html#function_password

编辑1:我相信所使用的算法是双重SHA1加上其他“调整”(根据this博客文章)。


我知道,我知道... 能够研究那些信息,但它并没有真正回答我的问题 :-) 不管怎样,谢谢。 - Thomas Stock
我查看了源代码,我认为它基于SHA1,但使用了各种盐和随机化技术。我知道这并不是很有帮助,所以我认为答案在其他地方 :) - samjudson

0

mysql密码函数并不特别安全(例如没有盐或其他保护方法来防止查找匹配哈希值),我能想到的唯一原因是: 1:您有一个包含mysql密码()的用户列表/表格 2:您想要从用户表中的哈希值破解mysql密码。

否则,请使用不同的东西,例如结合用户的名称(和/或某些其他行不变量)和他们的密码的哈希。这样,如果两个用户选择相同的密码,它就不会很明显。如果表格暴露了,安全性不会以同样的速度降低(例如搜索446a12100c856ce9,这是非常流行的密码的password()哈希)


0
对于VB net,此函数会在左侧填充0,以便当十六进制代码为[例如]'E'时,该函数将正确地将'0E'附加到结果字符串中。因此,在最后,包括预置的'*',生成的字符串将是41个字符长,这是MySQL所要求的。
Public Function GenerateMySQLHash(ByVal strKey As String) As String

    Dim keyArray As Byte() = System.Text.UTF8Encoding.UTF8.GetBytes(strKey)
    Dim enc = New Security.Cryptography.SHA1Managed()
    Dim encodedKey = enc.ComputeHash(enc.ComputeHash(keyArray))
    Dim myBuilder As New System.Text.StringBuilder(encodedKey.ToString.Length)

    For Each b As Byte In encodedKey
        myBuilder.Append(Strings.Right("0" & b.ToString("X"), 2))
    Next

    Return "*" & myBuilder.ToString()
End Function

-3

你将无法模拟MySql的password()函数,至少不会轻松实现。由于MySql使用自己的算法对字符串进行单向加密,他们不会公开使用的算法;这意味着你必须进行试错、比较结果字符串等操作。

解决你的问题:

我建议你复制数据库但保持与MySql的连接。使用标志来识别第一次登录。要求用户使用当前密码,并使用MySql password()函数验证密码。如果密码有效,则要求用户重置密码,可以输入相同的密码或新密码;此时使用你喜欢的加密算法并将结果存储在MS Sql数据库中。

我知道这听起来很麻烦,但这是除了试图入侵解密MySql加密算法之外最好的方法。

祝你好运!


谢谢提供的信息。我将部分使用您的解决方案: 第一步,将用户导入到MS SQL中,并将“HasMysqlPassword”位设置为“1”。 第二步,在登录时,我检索MS SQL用户详细信息并检查HasMysqlPassword = 1。如果不是,则检查MS SQL中的“Password”字段。如果是,则检查mysql中的密码。如果匹配,则使用SHA1加密输入的密码,将其存储在MS SQL中,并将HasMysqlPassword设置为0。过一段时间后,我应该能够分离mysql数据库并通知剩余的用户他们的新密码是自动生成的 - Thomas Stock
这种方法的好处是用户不会察觉到任何问题。 - Thomas Stock
这是不正确的。我已经编写了一个在.NET中转换为有效密码哈希的方法...稍后会发布。 - Scott Arrington
你懂了。我认为这是一个不错的干净解决方案。 - Victor
1
他们不打算透露他们使用的算法;MySQL是开源的,对吧? - Cheeso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接