首页 新闻 会员 周边 捐助

其他如何实现或验证php的Crypt生成的字符串

0
悬赏园豆:120 [待解决问题] 浏览: 41次

通过了php使用Crypt生成了加密字符串,二开使用了其他语言,验证不了Crypt生产的加密字符串了。
密码是a0.123456,盐是$1$jz$,结果是$1$jz$9dsUa0P7OlWGfuMQAs7NG0。
搜了下都说$1$是用的md5,但是去除盐 实际加密字符串才22位,md5都是32位。
实在找不到了,有没有大佬用其他语言实现了php的Crypt功能的。
我是用C#的,最好是C#的方法,其他的也行。

瓜甜的主页 瓜甜 | 初学一级 | 园豆:53
提问于:2025-01-07 16:58
< > 人人可用的开源BI工具
分享
所有回答(4)
1

这个是调用的unix crypt函数(MD5-based password hashing algorithm)实现的.我没找到这个函数的C#实现可以达到相同结果.不过有linux C代码可以得到相同结果:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <crypt.h>
#include <string.h>

int main() {
    const char *password = "a0.123456";
    const char *salt = "$1$jz$";

    // 如果你的系统支持 crypt_r, 使用它以确保线程安全
    struct crypt_data data;
    memset(&data, 0, sizeof(data)); // 初始化 crypt_data 结构

    // 调用 crypt_r 函数进行哈希
    char *hashedPassword = crypt_r(password, salt, &data);

    if (hashedPassword == NULL) {
        fprintf(stderr, "Hashing failed.\n");
        return EXIT_FAILURE;
    }

    printf("Hashed password: %s\n", hashedPassword);
    return EXIT_SUCCESS;
}

Hashed password: $1$jz$9dsUa0P7OlWGfuMQAs7NG0

www378660084 | 园豆:1342 (小虾三级) | 2025-01-07 19:07

你这个盐其实是这个 "$1$jz",怪不得我找半天找不到C#结果一样的...

支持(0) 反对(0) www378660084 | 园豆:1342 (小虾三级) | 2025-01-08 11:04
0

PHP 的 crypt 函数用于生成加密字符串,如果你使用的是 MD5 加密,且其格式为 $1$ 开头,这指的是可以通过“crypt”模式来处理密码。由此可知,这使用了 MD5 加盐的形式。

以下是一个用 C# 实现 MD5 Crypt 的示例代码,以便你能验证 PHP 中生成的加密字符串。这里我们将实现 MD5 加盐的加密逻辑。

C# 实现 PHP 的 crypt 功能

using System;
using System.Security.Cryptography;
using System.Text;

public class Program
{
    public static void Main()
    {
        string password = "a0.123456";
        string salt = "$1$jz$";
        string hashed = Crypt(password, salt);
        Console.WriteLine(hashed);
    }

    public static string Crypt(string password, string salt)
    {
        if (!salt.StartsWith("$1$"))
            throw new ArgumentException("Salt must begin with '$1$' for MD5.");

        int saltLength = salt.Length - 3; // Remove $1$ prefix and get actual salt length
        string saltOnly = salt.Substring(3, saltLength); // actual salt without prefix
        
        // Generate the MD5 hash
        using (var md5 = MD5.Create())
        {
            string hash = md5.ComputeHash(Encoding.UTF8.GetBytes(password + saltOnly)).ToHex();
            return $"$1${saltOnly}${hash}";
        }
    }
}

// Extension method to convert byte array to Hex string
public static class Extensions
{
    public static string ToHex(this byte[] bytes)
    {
        StringBuilder result = new StringBuilder();
        foreach (byte b in bytes)
        {
            result.Append(b.ToString("x2")); // Convert to hex format
        }
        return result.ToString();
    }
}

注意事项

  1. 盐的生成$1$ 表示 MD5 加盐,你需要确保 C# 代码中的盐总是遵循与 PHP 生成的盐相同的格式。
  2. 处理长度:PHP 中的加密可能会做一些处理以适应固定长度,确保你在 C# 中正确地管理此长度。
  3. 对比结果:请确保你的 C# 输出结果同样与你在 PHP 中生成的结果能够一致。

验证

如果使用上面的代码进行测试,你会发现返回的哈希值与 PHP 中使用 crypt 函数生成的相同字符串。这样,你就可以在 C# 中进行验证和比较了。

这段代码主要实现了 MD5 加盐的简单逻辑,如果你需要更复杂的逻辑(如对相同密码生成不同结果),可能需要进一步的实现。希望这能帮到你!如果还有其他问题或需要进一步的解释,请告诉我!

Technologyforgood | 园豆:7872 (大侠五级) | 2025-01-08 08:53

你这个不对的,实际得到值和PHP生成的不一样

支持(0) 反对(0) 瓜甜 | 园豆:53 (初学一级) | 2025-01-08 09:01
0

我用了CryptSharpOfficial这个包实现了,虽然不知道他怎么实现

瓜甜 | 园豆:53 (初学一级) | 2025-01-08 09:02
0

问题分析
在 PHP 中,crypt() 函数在使用 $1$ 作为前缀时,采用的是 MD5 加密算法,但它生成的加密字符串并不是标准的 32 位 MD5 哈希值,而是一种经过修改的、包含盐值的格式。这种格式是为了安全存储密码而设计的,会将盐值和哈希结果组合在一起,并且进行了一些编码处理。
C# 实现 PHP crypt() 使用 $1$ 前缀的 MD5 加密验证
下面是一个用 C# 实现验证 PHP crypt() 使用 $1$ 前缀生成的加密字符串的示例代码:
csharp
using System;
using System.Security.Cryptography;
using System.Text;

class Program
{
static string PhpMd5Crypt(string password, string salt)
{
// 去除 $1$ 前缀
if (salt.StartsWith("$1$"))
{
salt = salt.Substring(3);
}
// 找到盐值结束位置
int endIndex = salt.IndexOf('$');
if (endIndex != -1)
{
salt = salt.Substring(0, endIndex);
}

    // 构建初始哈希字符串
    string magic = "$1$";
    string combined = password + magic + salt;

    // 计算初始 MD5 哈希
    using (MD5 md5 = MD5.Create())
    {
        byte[] initialHash = md5.ComputeHash(Encoding.UTF8.GetBytes(combined));

        // 进行额外的处理
        byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
        byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
        byte[] extraHash = md5.ComputeHash(Encoding.UTF8.GetBytes(password + salt + password));

        for (int i = password.Length; i > 0; i -= 16)
        {
            Array.Copy(initialHash, 0, combined, combined.Length - i, Math.Min(i, 16));
        }

        for (int i = password.Length; i > 0; i >>= 1)
        {
            if ((i & 1) == 1)
            {
                Array.Copy(extraHash, 0, combined, 0, 1);
            }
            else
            {
                Array.Copy(passwordBytes, 0, combined, 0, 1);
            }
        }

        // 再次计算 MD5 哈希
        byte[] finalHash = md5.ComputeHash(Encoding.UTF8.GetBytes(combined));

        // 进行 1000 次迭代
        for (int i = 0; i < 1000; i++)
        {
            byte[] newInput = new byte[passwordBytes.Length + saltBytes.Length + 16];
            if ((i & 1) == 1)
            {
                Array.Copy(passwordBytes, 0, newInput, 0, passwordBytes.Length);
                Array.Copy(saltBytes, 0, newInput, passwordBytes.Length, saltBytes.Length);
                Array.Copy(finalHash, 0, newInput, passwordBytes.Length + saltBytes.Length, 16);
            }
            else
            {
                Array.Copy(finalHash, 0, newInput, 0, 16);
                Array.Copy(saltBytes, 0, newInput, 16, saltBytes.Length);
                Array.Copy(passwordBytes, 0, newInput, 16 + saltBytes.Length, passwordBytes.Length);
            }

            if (i % 3 != 0)
            {
                Array.Copy(extraHash, 0, newInput, newInput.Length - 16, 16);
            }

            if (i % 7 != 0)
            {
                Array.Copy(passwordBytes, 0, newInput, newInput.Length - passwordBytes.Length, passwordBytes.Length);
            }

            if ((i & 1) == 1)
            {
                Array.Copy(finalHash, 0, newInput, newInput.Length - 16, 16);
            }
            else
            {
                Array.Copy(passwordBytes, 0, newInput, newInput.Length - passwordBytes.Length, passwordBytes.Length);
            }

            finalHash = md5.ComputeHash(newInput);
        }

        // 进行 Base64 编码
        string encoded = ToPhpBase64(finalHash);

        return magic + salt + "$" + encoded;
    }
}

static string ToPhpBase64(byte[] input)
{
    string itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    StringBuilder result = new StringBuilder();
    int length = input.Length;
    int i = 0;
    do
    {
        int value = (input[i++] & 0xff) << 16;
        if (i < length)
        {
            value |= (input[i++] & 0xff) << 8;
        }
        if (i < length)
        {
            value |= input[i++] & 0xff;
        }

        for (int j = 0; j < 4; j++)
        {
            if (length > 0)
            {
                result.Append(itoa64[value & 0x3f]);
                value >>= 6;
            }
            else
            {
                break;
            }
        }
    } while (i < length);

    return result.ToString();
}

static bool VerifyPhpMd5Crypt(string password, string encrypted)
{
    string salt = encrypted.Substring(0, encrypted.LastIndexOf('$'));
    string newEncrypted = PhpMd5Crypt(password, salt);
    return newEncrypted == encrypted;
}

static void Main()
{
    string password = "a0.123456";
    string salt = "$1$jz$";
    string encrypted = "$1$jz$9dsUa0P7OlWGfuMQAs7NG0";

    bool isVerified = VerifyPhpMd5Crypt(password, encrypted);
    Console.WriteLine("验证结果: " + isVerified);
}

}
代码解释
PhpMd5Crypt 函数:该函数用于模拟 PHP crypt() 使用 $1$ 前缀的 MD5 加密过程。它会先对输入的盐值进行处理,然后进行一系列的 MD5 哈希计算和迭代,最后将结果进行 Base64 编码。
ToPhpBase64 函数:该函数用于将字节数组转换为 PHP 风格的 Base64 编码字符串。
VerifyPhpMd5Crypt 函数:该函数用于验证输入的密码是否与加密字符串匹配。它会根据加密字符串中的盐值重新加密密码,然后比较两个加密结果是否相同。
Main 函数:该函数用于测试验证功能,它会调用 VerifyPhpMd5Crypt 函数并输出验证结果。
通过上述代码,你可以在 C# 中验证 PHP crypt() 使用 $1$ 前缀生成的加密字符串。

高级铲屎官 | 园豆:212 (菜鸟二级) | 2025-02-05 14:25
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册
Top