C # için sorun taşıma PHP crypt () fonksiyonu

5 Cevap

Im, yeni bir ASP.Net çözüm için bazı eski ALP kullanıcı hesaplarını taşıma çalışıyorum, ve kullanıcıların eski şifreleri kullanmak edebilmek için isterim.

Ancak, bu çalışması için için, ben yeni yazdığınız şifre dayalı bir yeni hesaplanan birine eski sağlamalarının karşılaştırmak gerekiyor.

Etrafa arandı, ve this crypt() PHP ile adlandırılan uygulama olarak bulundu:

char *
crypt_md5(const char *pw, const char *salt)
{
    MD5_CTX	ctx,ctx1;
    unsigned long l;
    int sl, pl;
    u_int i;
    u_char final[MD5_SIZE];
    static const char *sp, *ep;
    static char passwd[120], *p;
    static const char *magic = "$1$";

    /* Refine the Salt first */
    sp = salt;

    /* If it starts with the magic string, then skip that */
    if(!strncmp(sp, magic, strlen(magic)))
    	sp += strlen(magic);

    /* It stops at the first '$', max 8 chars */
    for(ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
    	continue;

    /* get the length of the true salt */
    sl = ep - sp;

    MD5Init(&ctx);

    /* The password first, since that is what is most unknown */
    MD5Update(&ctx, (const u_char *)pw, strlen(pw));

    /* Then our magic string */
    MD5Update(&ctx, (const u_char *)magic, strlen(magic));

    /* Then the raw salt */
    MD5Update(&ctx, (const u_char *)sp, (u_int)sl);

    /* Then just as many characters of the MD5(pw,salt,pw) */
    MD5Init(&ctx1);
    MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
    MD5Update(&ctx1, (const u_char *)sp, (u_int)sl);
    MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
    MD5Final(final, &ctx1);
    for(pl = (int)strlen(pw); pl > 0; pl -= MD5_SIZE)
    	MD5Update(&ctx, (const u_char *)final,
    	    (u_int)(pl > MD5_SIZE ? MD5_SIZE : pl));

    /* Don't leave anything around in vm they could use. */
    memset(final, 0, sizeof(final));

    /* Then something really weird... */
    for (i = strlen(pw); i; i >>= 1)
    	if(i & 1)
    	    MD5Update(&ctx, (const u_char *)final, 1);
    	else
    	    MD5Update(&ctx, (const u_char *)pw, 1);

    /* Now make the output string */
    strcpy(passwd, magic);
    strncat(passwd, sp, (u_int)sl);
    strcat(passwd, "$");

    MD5Final(final, &ctx);

    /*
     * and now, just to make sure things don't run too fast
     * On a 60 Mhz Pentium this takes 34 msec, so you would
     * need 30 seconds to build a 1000 entry dictionary...
     */
    for(i = 0; i < 1000; i++) {
    	MD5Init(&ctx1);
    	if(i & 1)
    		MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
    	else
    		MD5Update(&ctx1, (const u_char *)final, MD5_SIZE);

    	if(i % 3)
    		MD5Update(&ctx1, (const u_char *)sp, (u_int)sl);

    	if(i % 7)
    		MD5Update(&ctx1, (const u_char *)pw, strlen(pw));

    	if(i & 1)
    		MD5Update(&ctx1, (const u_char *)final, MD5_SIZE);
    	else
    		MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
    	MD5Final(final, &ctx1);
    }

    p = passwd + strlen(passwd);

    l = (final[ 0]<<16) | (final[ 6]<<8) | final[12];
    _crypt_to64(p, l, 4); p += 4;
    l = (final[ 1]<<16) | (final[ 7]<<8) | final[13];
    _crypt_to64(p, l, 4); p += 4;
    l = (final[ 2]<<16) | (final[ 8]<<8) | final[14];
    _crypt_to64(p, l, 4); p += 4;
    l = (final[ 3]<<16) | (final[ 9]<<8) | final[15];
    _crypt_to64(p, l, 4); p += 4;
    l = (final[ 4]<<16) | (final[10]<<8) | final[ 5];
    _crypt_to64(p, l, 4); p += 4;
    l = final[11];
    _crypt_to64(p, l, 2); p += 2;
    *p = '\0';

    /* Don't leave anything around in vm they could use. */
    memset(final, 0, sizeof(final));

    return (passwd);
}

Ve, burada beklenen maçı ile birlikte, C # benim sürümüdür.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Security.Cryptography;
using System.IO;
using System.Management;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] salt = Encoding.ASCII.GetBytes("$1$ls3xPLpO$Wu/FQ.PtP2XBCqrM.w847/");
            Console.WriteLine("Hash:  " + Encoding.ASCII.GetString(salt));

            byte[] passkey = Encoding.ASCII.GetBytes("suckit");

            byte[] newhash = md5_crypt(passkey, salt);
            Console.WriteLine("Hash2: " + Encoding.ASCII.GetString(newhash));

            byte[] newhash2 = md5_crypt(passkey, newhash);
            Console.WriteLine("Hash3: " + Encoding.ASCII.GetString(newhash2));


            Console.ReadKey(true);
        }

        public static byte[] md5_crypt(byte[] pw, byte[] salt)
        {
            MemoryStream ctx, ctx1;
            ulong l;
            int sl, pl;
            int i;
            byte[] final;
            int sp, ep; //** changed pointers to array indices
            MemoryStream passwd = new MemoryStream();
            byte[] magic = Encoding.ASCII.GetBytes("$1$");

            // Refine the salt first
            sp = 0;  //** Changed to an array index, rather than a pointer.

            // If it starts with the magic string, then skip that
            if (salt[0] == magic[0] &&
                salt[1] == magic[1] &&
                salt[2] == magic[2])
            {
                sp += magic.Length;
            }

            // It stops at the first '$', max 8 chars
            for (ep = sp;
                (ep + sp < salt.Length) &&  //** Converted to array indices, and rather than check for null termination, check for the end of the array.
                salt[ep] != (byte)'$' &&
                ep < (sp + 8);
                ep++)
                continue;

            // Get the length of the true salt
            sl = ep - sp;

            ctx = MD5Init();

            // The password first, since that is what is most unknown
            MD5Update(ctx, pw, pw.Length);

            // Then our magic string
            MD5Update(ctx, magic, magic.Length);

            // Then the raw salt
            MD5Update(ctx, salt, sp, sl);

            // Then just as many characters of the MD5(pw,salt,pw)
            ctx1 = MD5Init();
            MD5Update(ctx1, pw, pw.Length);
            MD5Update(ctx1, salt, sp, sl);
            MD5Update(ctx1, pw, pw.Length);
            final = MD5Final(ctx1);
            for(pl = pw.Length; pl > 0; pl -= final.Length)
                MD5Update(ctx, final, 
                    (pl > final.Length ? final.Length : pl));

            // Don't leave anything around in vm they could use.
            for (i = 0; i < final.Length; i++) final[i] = 0;

            // Then something really weird...
            for (i = pw.Length; i != 0; i >>= 1)
    	        if((i & 1) != 0)
    	            MD5Update(ctx, final, 1);
    	        else
    	            MD5Update(ctx, pw, 1);


            // Now make the output string
            passwd.Write(magic, 0, magic.Length);
            passwd.Write(salt, sp, sl);
            passwd.WriteByte((byte)'$');

            final = MD5Final(ctx);

            // and now, just to make sure things don't run too fast
            // On a 60 Mhz Pentium this takes 34 msec, so you would
            // need 30 seconds to build a 1000 entry dictionary...
            for(i = 0; i < 1000; i++)
            {
    	        ctx1 = MD5Init();
    	        if((i & 1) != 0)
    		        MD5Update(ctx1, pw, pw.Length);
    	        else
    		        MD5Update(ctx1, final, final.Length);

    	        if((i % 3) != 0)
    		        MD5Update(ctx1, salt, sp, sl);

    	        if((i % 7) != 0)
    		        MD5Update(ctx1, pw, pw.Length);

    	        if((i & 1) != 0)
    		        MD5Update(ctx1, final, final.Length);
    	        else
    		        MD5Update(ctx1, pw, pw.Length);

                final = MD5Final(ctx1);
            }

            //** Section changed to use a memory stream, rather than a byte array.
            l = (((ulong)final[0]) << 16) | (((ulong)final[6]) << 8) | ((ulong)final[12]);
            _crypt_to64(passwd, l, 4);
            l = (((ulong)final[1]) << 16) | (((ulong)final[7]) << 8) | ((ulong)final[13]);
            _crypt_to64(passwd, l, 4);
            l = (((ulong)final[2]) << 16) | (((ulong)final[8]) << 8) | ((ulong)final[14]);
            _crypt_to64(passwd, l, 4);
            l = (((ulong)final[3]) << 16) | (((ulong)final[9]) << 8) | ((ulong)final[15]);
            _crypt_to64(passwd, l, 4);
            l = (((ulong)final[4]) << 16) | (((ulong)final[10]) << 8) | ((ulong)final[5]);
            _crypt_to64(passwd, l, 4);
            l = final[11];
            _crypt_to64(passwd, l, 2);

            byte[] buffer = new byte[passwd.Length];
            passwd.Seek(0, SeekOrigin.Begin);
            passwd.Read(buffer, 0, buffer.Length);
            return buffer;
        }

        public static MemoryStream MD5Init()
        {
            return new MemoryStream();
        }

        public static void MD5Update(MemoryStream context, byte[] source, int length)
        {
            context.Write(source, 0, length);
        }

        public static void MD5Update(MemoryStream context, byte[] source, int offset, int length)
        {
            context.Write(source, offset, length);
        }

        public static byte[] MD5Final(MemoryStream context)
        {
            long location = context.Position;
            byte[] buffer = new byte[context.Length];
            context.Seek(0, SeekOrigin.Begin);
            context.Read(buffer, 0, (int)context.Length);
            context.Seek(location, SeekOrigin.Begin);
            return MD5.Create().ComputeHash(buffer);
        }

        // Changed to use a memory stream rather than a character array.
        public static void _crypt_to64(MemoryStream s, ulong v, int n)
        {
            char[] _crypt_a64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray(); 

            while (--n >= 0)
            {
    	        s.WriteByte((byte)_crypt_a64[v & 0x3f]);
    	        v >>= 6;
            }
        }


    }
}

Neyi yanlış yapıyorum? Ben FreeBSD sürümünde MD5xxxx fonksiyonların işleyişi hakkında bazı büyük varsayımlar yapıyorum, ama o iş gibi görünüyor.

Bu PHP tarafından kullanılan gerçek sürüm değil mi? Herkes herhangi bir fikir var mı?

EDIT:

PHP'nin kaynak kodunun bir kopyasını indirdim ve glibc kütüphanesini kullanır bulundu. Yani, ben, glibc'mizin kaynak kodunun bir kopyasını karşıdan __ md5_crypt_r işlevi bulundu, bu FreeBSD sürümü ÖZDEŞ aynı karmaları ile geri geldi işlevselliği, karınca çoğaltılamaz.

Şimdi, hemen hemen şaşkına duyuyorum. PHP 4 5 daha farklı bir yöntem kullandınız mı? Neler oluyor?

5 Cevap

Tamam, işte cevabı:

PHP crypt fonksiyonunun glibc uygulamasını kullanır. (Ekli: C # uygulaması)

Linux standart olmayan bir karma algoritma vardı oturdu (GoDaddy tarafından barındırılan) benim eski web kutu çünkü benim eski parolaları karma eşleşen değil nedenidir. (Muhtemelen algoritması yapılan WEIRD bazı şeyler düzeltmek için.)

Ancak, glibc'mizin ünite testleri karşı ve pencere karşı aşağıdaki uygulama PHP kurulumu test ettik. Her iki testte de% 100 kabul edildi.

EDIT
Here is the link: (moved to a Github Gist)

https://gist.github.com/1092558

PHP crypt() function temel işletim sistemi verileri şifreleyerek sağlar olursa olsun karma algoritması kullanır - belgelerinde bakabilirsiniz. Yani ilk adım (kullanılan ne karma algoritması) veriler şifrelenmiş nasıl öğrenmek olmalıdır. Bunu öğrendikten sonra, C # için aynı algoritmayı bulmak için önemsiz olmalıdır.

Her zaman sistemi () sizin için crypt yapan bir php komut satırı komut dosyasını dışarı (veya ne olursa olsun C # statik işlevi denir) olabilir.

Ben Başarılı bir girişten sonra olsa da bir parola değişikliğini zorlama öneriyoruz. Sonra kullanıcı değişti olmadığını gösteren bir bayrak olabilir. Herkes değişti kez php çağrı dökümü olabilir.

Sadece php uygulamasını yeniden ... emin php crypt kütüphaneler sistem ortam yolunda emin olun ...

Siz dize dizme / charset ... sonra özgün karma algoritması, doğru kullanabilirsiniz emin olmak için birlikte çalışma yöntemi güncellemeniz gerekebilir.

[DllImport("crypt.dll", CharSet=CharSet.ASCII)]
private static extern string crypt(string password, string salt);

public bool ValidLogin(string username, string password)
{
    string hash = crypt(password, null);
    ...
}

Bu önemsiz görünmüyor.

UPDATE: Originally yazdı: açıklamalarda belirttiği gibi "The PHP Crypt function does not look like a standard hash. Why not? Who knows.", PHP crypt () passwd crypt için BSD kullanılan aynıdır. Bir dejure standart ise bilmiyorum, ama defacto standarttır. Yani.

Ben önemsiz olmak görünmüyor benim konumda stand by.

Aksine kodu taşıma yerine, eski PHP çalıştıran tutmak düşünebilirsiniz, eski ve şifreleri şifre doğrulaması için kesinlikle kullanabilirsiniz. Kullanıcıların kendi şifrelerini değiştirmek gibi, yeni bir karma algoritması, biraz daha "açık" bir şey kullanın. Her kullanıcı için karma, hem de "karma lezzet" saklamak gerekir.