237 lines
8 KiB
C#
237 lines
8 KiB
C#
// Copyright (c) 2015, Outercurve Foundation.
|
||
// All rights reserved.
|
||
//
|
||
// Redistribution and use in source and binary forms, with or without modification,
|
||
// are permitted provided that the following conditions are met:
|
||
//
|
||
// - Redistributions of source code must retain the above copyright notice, this
|
||
// list of conditions and the following disclaimer.
|
||
//
|
||
// - Redistributions in binary form must reproduce the above copyright notice,
|
||
// this list of conditions and the following disclaimer in the documentation
|
||
// and/or other materials provided with the distribution.
|
||
//
|
||
// - Neither the name of the Outercurve Foundation nor the names of its
|
||
// contributors may be used to endorse or promote products derived from this
|
||
// software without specific prior written permission.
|
||
//
|
||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
||
using System;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
|
||
namespace WebsitePanel.Providers.Common
|
||
{
|
||
public class PasswdHelper
|
||
{
|
||
protected static readonly string MD5_MAGIC_PREFIX = "$apr1$";
|
||
protected const int MD5_DIGESTSIZE = 16;
|
||
protected static readonly string SHA_MAGIC_PREFIX = "{SHA}";
|
||
|
||
private static readonly string itoa64 = /* 0 ... 63 => ASCII - 64 */
|
||
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||
|
||
private static Random _random;
|
||
|
||
static PasswdHelper()
|
||
{
|
||
_random = new Random();
|
||
|
||
}
|
||
|
||
public static string to64(ulong v, int n)
|
||
{
|
||
StringBuilder sb = new StringBuilder();
|
||
while (--n >= 0)
|
||
{
|
||
sb.Append(itoa64[(int)v & 0x3f]);
|
||
v >>= 6;
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
|
||
|
||
public static string ByteArrayToHexString(byte[] ba)
|
||
{
|
||
StringBuilder sb = new StringBuilder();
|
||
|
||
foreach (byte b in ba)
|
||
{
|
||
sb.Append(b.ToString("x2"));
|
||
}
|
||
|
||
return sb.ToString();
|
||
}
|
||
|
||
|
||
public static byte[] getMD5HashHex(string s)
|
||
{
|
||
MD5 md5 = MD5.Create();
|
||
return md5.ComputeHash(Encoding.ASCII.GetBytes(s));
|
||
}
|
||
|
||
|
||
public static string MD5Encode(string pw, string salt)
|
||
{
|
||
// <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>:
|
||
// $apr1$Vs5.....$iSQlpTkND9RjL7iAMTjDt.
|
||
|
||
// <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> - <20><><EFBFBD><EFBFBD>
|
||
|
||
string password;
|
||
byte[] final;
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD>
|
||
// 1. <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> $apr1$
|
||
if (salt.StartsWith(MD5_MAGIC_PREFIX))
|
||
{
|
||
salt = salt.Substring(MD5_MAGIC_PREFIX.Length);
|
||
}
|
||
|
||
// 2. <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> '$' <20><><EFBFBD> 8 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
int sp = salt.IndexOf('$');
|
||
if (sp < 0 || sp > 8) sp = 8;
|
||
|
||
salt = salt.Substring(0, sp);
|
||
//Debug.WriteLine(string.Format("salt [{0}]", salt));
|
||
|
||
ByteVector s = new ByteVector();
|
||
ByteVector s1 = new ByteVector();
|
||
|
||
s.Add(pw);
|
||
s.Add(MD5_MAGIC_PREFIX);
|
||
s.Add(salt);
|
||
|
||
s1.Add(pw);
|
||
s1.Add(salt);
|
||
s1.Add(pw);
|
||
|
||
final = s1.GetMD5Hash();
|
||
|
||
for (int i = pw.Length; i > 0; i -= MD5_DIGESTSIZE)
|
||
{
|
||
s.Add(final, 0, (i > MD5_DIGESTSIZE) ? MD5_DIGESTSIZE : i);
|
||
}
|
||
|
||
for (int i = 0; i < final.Length; i++)
|
||
final[i] = 0;
|
||
|
||
for (int i = pw.Length; i != 0; i >>= 1)
|
||
{
|
||
// (i & 1) <20> <20><><EFBFBD><EFBFBD><EFBFBD>
|
||
if ((i & 0x01) == 1)
|
||
{
|
||
s.Add(final, 0, 1);
|
||
}
|
||
else
|
||
{
|
||
s.Add(pw.Substring(0, 1));
|
||
}
|
||
}
|
||
|
||
final = s.GetMD5Hash();
|
||
|
||
for (int i = 0; i < 1000; i++)
|
||
{
|
||
s1.Clear();
|
||
if ((i & 1) != 0)
|
||
{
|
||
s1.Add(pw);
|
||
}
|
||
else
|
||
{
|
||
s1.Add(final);
|
||
}
|
||
if ((i % 3) != 0)
|
||
{
|
||
s1.Add(salt);
|
||
}
|
||
|
||
if ((i % 7) != 0)
|
||
{
|
||
s1.Add(pw);
|
||
}
|
||
|
||
if ((i & 1) != 0)
|
||
{
|
||
s1.Add(final);
|
||
}
|
||
else
|
||
{
|
||
s1.Add(pw);
|
||
}
|
||
final = s1.GetMD5Hash();
|
||
}
|
||
|
||
password = "";
|
||
ulong l;
|
||
|
||
l = ((ulong)final[0] << 16) | ((ulong)final[6] << 8) | ((ulong)final[12]);
|
||
password += PasswdHelper.to64(l, 4);
|
||
l = ((ulong)final[1] << 16) | ((ulong)final[7] << 8) | ((ulong)final[13]);
|
||
password += PasswdHelper.to64(l, 4);
|
||
l = ((ulong)final[2] << 16) | ((ulong)final[8] << 8) | ((ulong)final[14]);
|
||
password += PasswdHelper.to64(l, 4);
|
||
l = ((ulong)final[3] << 16) | ((ulong)final[9] << 8) | ((ulong)final[15]);
|
||
password += PasswdHelper.to64(l, 4);
|
||
l = ((ulong)final[4] << 16) | ((ulong)final[10] << 8) | ((ulong)final[5]);
|
||
password += PasswdHelper.to64(l, 4);
|
||
l = ((ulong)final[11]);
|
||
password += PasswdHelper.to64(l, 2);
|
||
|
||
password = string.Format("{0}{1}${2}", MD5_MAGIC_PREFIX, salt, password);
|
||
|
||
return password;
|
||
}
|
||
|
||
|
||
public static string SHA1Encode(string clear)
|
||
{
|
||
if (clear.StartsWith(SHA_MAGIC_PREFIX))
|
||
{
|
||
clear = clear.Substring(SHA_MAGIC_PREFIX.Length);
|
||
}
|
||
|
||
SHA1 sha = new SHA1CryptoServiceProvider();
|
||
|
||
string cr = Convert.ToBase64String(
|
||
sha.ComputeHash(Encoding.Default.GetBytes(clear))
|
||
);
|
||
return SHA_MAGIC_PREFIX + cr;
|
||
}
|
||
|
||
|
||
public static string GetRandomSalt()
|
||
{
|
||
return to64((ulong)_random.Next(), 8);
|
||
}
|
||
|
||
|
||
public static string DigestEncode(string username, string realm, string passwd)
|
||
{
|
||
MD5 md5 = MD5.Create();
|
||
|
||
byte[] b = md5.ComputeHash(Encoding.ASCII.GetBytes(
|
||
string.Format("{0}:{1}:{2}", username, realm, passwd)
|
||
));
|
||
|
||
StringBuilder sb = new StringBuilder(b.Length*2);
|
||
for (int i = 0; i < b.Length; ++i)
|
||
{
|
||
sb.Append( String.Format("{0:x2}", b[i]) );
|
||
}
|
||
|
||
return sb.ToString();
|
||
}
|
||
}
|
||
}
|