#!/usr/bin/env python3 """ 最终版本:标准APR1算法实现的crypt.crypt()替代函数 基于 https://github.com/Tblue/pyapr1/blob/master/apr1.py """ import hashlib import os def to64(data, n_out): """APR1标准的base64编码""" chars = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" out = "" for i in range(n_out): out += chars[data & 0x3f] data >>= 6 return out def mkint(data, *indexes): """从字节数组的指定索引创建整数""" r = 0 for i, idx in enumerate(indexes): r |= data[idx] << 8 * (len(indexes) - i - 1) return r def hash_apr1(salt, password): """标准APR1哈希算法""" sb = bytes(salt, "ascii") pb = bytes(password, "iso-8859-1") ph = hashlib.md5() ph.update(pb) ph.update(b"$apr1$") ph.update(sb) sandwich = hashlib.md5(pb + sb + pb).digest() ndig, nrem = divmod(len(pb), ph.digest_size) for n in ndig * [ph.digest_size] + [nrem]: ph.update(sandwich[:n]) i = len(pb) while i: if i & 1: ph.update(b'\x00') else: ph.update(pb[:1]) i >>= 1 final = ph.digest() for i in range(1000): maelstrom = hashlib.md5() if i & 1: maelstrom.update(pb) else: maelstrom.update(final) if i % 3: maelstrom.update(sb) if i % 7: maelstrom.update(pb) if i & 1: maelstrom.update(final) else: maelstrom.update(pb) final = maelstrom.digest() pw_ascii = (to64(mkint(final, 0, 6, 12), 4) + to64(mkint(final, 1, 7, 13), 4) + to64(mkint(final, 2, 8, 14), 4) + to64(mkint(final, 3, 9, 15), 4) + to64(mkint(final, 4, 10, 5), 4) + to64(mkint(final, 11), 2)) return f"$apr1${salt}${pw_ascii}" def generate_salt(): """生成随机盐值""" random_bytes = os.urandom(6) return to64(mkint(random_bytes, *range(6)), 8) def crypt(password, salt=None): """ 直接替代 crypt.crypt(password, salt) 生成nginx完全兼容的APR1密码哈希 Args: password (str): 要加密的密码 salt (str, optional): 盐值,如果为None则自动生成 Returns: str: APR1格式的密码哈希,如 $apr1$salt$hash 用法: 原来: import crypt; hashed = crypt.crypt(password, salt) 替换: from final_crypt import crypt; hashed = crypt(password, salt) """ if salt is None: salt = generate_salt() elif salt.startswith('$apr1$'): parts = salt.split('$') if len(parts) >= 3: salt = parts[2] else: salt = generate_salt() return hash_apr1(salt, password)