views:

2072

answers:

7

Hi guys,

I'm updating an old ASP/MySql webapp to ASP.NET/MS SQL.

We would like to keep the logins from the old website working in the new app.

Unfortunately the passwords were stored in the MySql DB using MySql's password() function.

Is it possible to simulate MySql's password() function in either .NET or MS SQL?

Any help/links are appreciated.

+1  A: 

You can encrypt strings using MD5 or SHA1 in .Net, but the actual algorithm used by MySQL is probably different to these two methods. I suspect it is based on some kind of 'salt' based on the instance of the server, but I don't know.

In theory, since I believe MySQL is open source you could investigate the source and determine how this is done.

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

Edit 1: I believe the algorithm used is a double SHA1 with other 'tweaks' (according to this blog post).

samjudson
I know, I know...Was able to research that information too but it doesn't really answer my question :-)Thanks anyway.
Thomas Stock
I looked at the source, and I think its based on SHA1, but with various salt and randomization technologies. Not very helpful I know, so I think the answer is to look elsewhere :(
samjudson
thanks. +1 for checking the source :-)
Thomas Stock
A: 

You will not be able to simulate the MySql password() function. at least not without a lot of work and luck. Being that MySql uses their own algorithm to one-way encrypt strings, they are not going to disclose what algorithm they use; which means you would have to do trial and error, compare resulting strings, etc.

To fix your issue:

I'd replicate the DB but maintain a connection with MySql. Use a flag to identify a first time login. Ask them to use their current password; verify password with MySql password() function. If password is valid; ask them to reset it, either by entering the same password again, or a new one; at this point use your favorite encryption algorithm and store the result in your MS Sql DB.

I know it sounds like a pain, but it is the best you can do short of trying to hack your way into deciphering MySql encryption algorithm.

Good luck!

Victor
Thanks for the information. I'll partially use your solution:First step, import the users into MS SQL and set a "HasMysqlPassword" bit to "1".Second step, upon login I retrieve the MS SQL user details and check if HasMysqlPassword=1. If it's not, I check the "Password" field in MS SQL. If it is, I'll check the password in mysql. If it matches, I encrypt the entered password using SHA1, store it in MS SQL while setting HasMysqlPassword to 0. After a while I should be able to detach the mysql database and notify the remaining users that their new password is *autogenerated*
Thomas Stock
What's nice about that approach is that the user won't notice a thing.
Thomas Stock
This is incorrect. I've written a method to convert to valid password hashes in .NET... post to follow
Scott Anderson
you got it. i think it is a nice clean solution.
Victor
"they are not going to disclose what algorithm they use; " -- MySql is open source, no?
Cheeso
+5  A: 

According to MySQL documentation, the algorithm is a double SHA1 hash. When examining the MySQL source code, you find a function called make_scrambled_password() in libmysql/password.c. The function is defined as follows:

/*
    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);
}

Given this method, you can create a .NET counterpart that basically does the same thing. Here's what I've come up with. When I run SELECT PASSWORD('test'); against my local copy of MySQL, the value returned is:

*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29

According to the source code (again in password.c), the beginning asterisk indicates that this is the post-MySQL 4.1 method of encrypting the password. When I emulate the functionality in VB.Net for example, this is what I come up with:

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("X"))
    Next

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

Keep in mind that SHA1Managed() is in the System.Security.Cryptography namespace. This method returns the same output as the PASSWORD() call in MySQL. I hope this helps for you.

Edit: Here's the same code in 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("X"));

    return "*" + myBuilder.ToString();
}
Scott Anderson
Awesome, thanks!Will try if it works for my version of mysql (can't check it at the moment) and let you know. Many thanks for the research and great solution.
Thomas Stock
If you're using a version previous to 4.1.1, there's an older method for encryption. I may be able to help you write out that method, too. Let me know and I'll be happy to assist. Good luck!
Scott Anderson
I actually found a way to do this with T-SQL too. It's pretty ugly-looking, but it works!SELECT '*' + SUBSTRING(master.dbo.fn_varbintohexstr(HASHBYTES('SHA1',HASHBYTES('SHA1','VALUE-TO-ENCRYPT-GOES-HERE'))),1,42)
Scott Anderson
A: 

For VB net, this pads with 0's to the left so that when the hex code is [for example] 'E', this function will correctly append '0E' to the result string. So that in the end, including the pre-pended '*' the resulting string will be 41 character long, as is required in 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
+1  A: 

"they are not going to disclose what algorithm they use"

erm ... someone should wake and tell you that MySQL is OPEN SOURCE ... they dont need to "disclose" something because everybody can read the very structure of the database - just download the sources, search for the encryption-functions (i guess they're using C++) and start investigating ... SHA1 seems pretty insecure now - just recently a few scientists have proven that it is possible to choose up to 20% of the data without limitations and produce the same hash so one would be advised to use AES or SHA2 (therefore not the MySQL-PASSWORD-func)

A: 

the mysql password functions is not particularly secure (eg there's no salt or other method of protection against looking for matching hashes) the only reasons I can see for wanting to duplicate the algorithm are 1: you have a list/table of users with mysql password()s 2: you want to crack mysql passwords from the hashes in the user table.

Else use something different, eg a hash that combines both the user's name (and/or some other row invariant) and their password. thay way if two users pick the same password it won't be obvious. and if the table gets exposed the security does not decrease at the same rate (eg google for 446a12100c856ce9 which is the password() hash of a very popular password)

Jasen
A: 

C# version modified to correct padding issue. Thanks Scott for all the initial hard work :D

    [Test]
    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());
    } 
Martin Hymers