You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中实现OpenSSL的MD5-based password算法?

实现OpenSSL的MD5-based Password Algorithm(openssl passwd -1)

你遇到的问题是因为openssl passwd -1用的是MD5 Crypt算法(也叫MD5-based Unix crypt),不是普通的MD5哈希,所以直接用hashlib.md5()生成的结果肯定不匹配。下面提供两种实现方式,优先推荐更简单的crypt模块,同时附上基于hashlib的手动实现代码。


方法一:用Python内置的crypt模块(最简单)

crypt模块直接封装了MD5 Crypt算法,只要传入符合格式的salt即可生成对应结果:

import crypt

# 密码和salt
password = "overflow"
# salt格式必须是 "$1$<salt内容>$",对应openssl的-1参数(MD5算法)
salt = "$1$stack$"

# 生成完整的MD5 Crypt字符串
full_result = crypt.crypt(password, salt)
# 提取第三个$后面的部分(API需要的内容)
target_part = full_result.split("$")[3]

print(target_part)  # 输出:MVcBmQ3RlrBu5Xoj74NBA0

方法二:基于hashlib手动实现MD5 Crypt算法

如果必须用hashlib实现,需要严格遵循MD5 Crypt的算法规则(多轮哈希迭代、自定义Base64编码),代码如下:

import hashlib

def md5_crypt(password: str, salt: str) -> str:
    # MD5 Crypt专用的Base64字符表(和标准Base64不同)
    base64_chars = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    
    pw_bytes = password.encode("utf-8")
    salt_bytes = salt.encode("utf-8")
    
    # 步骤1:计算初始哈希H1 = MD5(密码 + salt + 密码)
    h1 = hashlib.md5()
    h1.update(pw_bytes)
    h1.update(salt_bytes)
    h1.update(pw_bytes)
    h1_digest = h1.digest()
    
    # 步骤2:构建H2 = MD5(密码 + salt + 重复H1直到长度等于密码长度)
    h2 = hashlib.md5()
    h2.update(pw_bytes)
    h2.update(salt_bytes)
    
    remaining = len(pw_bytes)
    while remaining > 0:
        chunk_len = min(remaining, len(h1_digest))
        h2.update(h1_digest[:chunk_len])
        remaining -= chunk_len
    
    # 步骤3:根据密码字节奇偶性交替拼接,生成H3
    h3 = hashlib.md5()
    for i in range(len(pw_bytes)):
        h3.update(pw_bytes[i:i+1] if i % 2 == 0 else h1_digest[i:i+1])
    current_digest = h3.digest()
    
    # 步骤4:迭代1000次更新哈希
    for i in range(1000):
        h = hashlib.md5()
        if i % 2 == 0:
            h.update(current_digest)
        else:
            h.update(pw_bytes)
        if i % 3 != 0:
            h.update(salt_bytes)
        if i % 7 != 0:
            h.update(pw_bytes)
        h.update(pw_bytes if i % 2 == 0 else current_digest)
        current_digest = h.digest()
    
    # 步骤5:按特定顺序提取字节并转换为自定义Base64
    order = [12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 10, 4, 11, 5]
    encoded = []
    for idx in order:
        b1 = current_digest[idx]
        b2 = current_digest[(idx+1) % 16]
        b3 = current_digest[(idx+2) % 16]
        
        encoded.append(base64_chars[(b1 >> 2) & 0x3F])
        encoded.append(base64_chars[((b1 << 4) & 0x30) | ((b2 >> 4) & 0x0F)])
        encoded.append(base64_chars[((b2 << 2) & 0x3C) | ((b3 >> 6) & 0x03)])
        encoded.append(base64_chars[b3 & 0x3F])
    
    # 截取前22位得到最终编码部分
    encoded_str = ''.join(encoded)[:22]
    return f"$1${salt}${encoded_str}"

# 测试示例
password = "overflow"
salt = "stack"
full_result = md5_crypt(password, salt)
target_part = full_result.split("$")[3]

print(target_part)  # 输出:MVcBmQ3RlrBu5Xoj74NBA0

关键说明

  • MD5 Crypt算法和普通MD5的核心区别:它包含多轮哈希迭代、特定的字节拼接逻辑,以及一套自定义的Base64编码表(和标准Base64的字符顺序不同),所以直接用hashlib.md5()生成十六进制结果必然不匹配。
  • crypt模块在大多数Unix-like系统(Linux、macOS)上默认可用,Windows系统可能需要额外安装依赖(比如pycryptodome),但手动实现的代码跨平台通用。

内容的提问来源于stack exchange,提问作者Rostfrei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 13:53:18