MyHash1.1来啦

你有没有发现,现在下载东西,很多都会给你一个hash值,用来核对下载内容是否完整,但是如何快速核对呢?我做了一个小工具,非常方便,离线使用,免费分享代码,喜欢可以点赞收藏!

import hashlib
import bcrypt
import argon2
import getpass
import json
from pathlib import Path
from datetime import datetime
from enum import Enum, auto
import sys
import os
import platform

# 将HashAlgorithm移到顶部
class HashAlgorithm(Enum):
    MD5 = auto()
    SHA256 = auto()
    BCRYPT = auto()
    ARGON2 = auto()

# 改进的颜色类 - 增加Windows兼容性检测
class Color:
    if platform.system() == 'Windows':
        try:
            import colorama
            colorama.init()  # Windows下初始化colorama
            HEADER = colorama.Fore.MAGENTA
            OKBLUE = colorama.Fore.BLUE
            OKCYAN = colorama.Fore.CYAN
            OKGREEN = colorama.Fore.GREEN
            WARNING = colorama.Fore.YELLOW
            FAIL = colorama.Fore.RED
            ENDC = colorama.Style.RESET_ALL
            BOLD = colorama.Style.BRIGHT
            UNDERLINE = colorama.Style.NORMAL  # Windows终端通常不支持下划线
        except ImportError:
            # 如果没有安装colorama,则禁用颜色
            HEADER = OKBLUE = OKCYAN = OKGREEN = WARNING = FAIL = ENDC = BOLD = UNDERLINE = ''
    else:
        # Unix-like系统的ANSI颜色代码
        HEADER = '\033[95m'
        OKBLUE = '\033[94m'
        OKCYAN = '\033[96m'
        OKGREEN = '\033[92m'
        WARNING = '\033[93m'
        FAIL = '\033[91m'
        ENDC = '\033[0m'
        BOLD = '\033[1m'
        UNDERLINE = '\033[4m'

def clear_screen():
    """跨平台清屏函数"""
    if platform.system() == 'Windows':
        os.system('cls')
    else:
        print("\033c", end="")

def print_header(title):
    """改进的标题打印,自动调整宽度"""
    try:
        # 获取终端宽度
        width = os.get_terminal_size().columns - 2
    except:
        width = 50  # 默认值
    
    print(f"{Color.OKCYAN}{'='*width}")
    print(f"{title.center(width)}")
    print(f"{'='*width}{Color.ENDC}\n")

def print_menu(options):
    """改进的菜单打印,处理窄终端情况"""
    try:
        term_width = os.get_terminal_size().columns
    except:
        term_width = 80
        
    for i, option in enumerate(options, 1):
        # 如果终端太窄,换行显示
        if term_width < 40:
            print(f"{Color.OKBLUE}{i}.")
            print(f"  {option}{Color.ENDC}")
        else:
            print(f"{Color.OKBLUE}{i}. {option}{Color.ENDC}")

def get_choice(prompt, valid_choices=None):
    """改进的用户输入获取,增加输入超时处理"""
    while True:
        try:
            choice = input(f"{Color.BOLD}{prompt}{Color.ENDC} ").strip().lower()
            if valid_choices and choice not in [str(c).lower() for c in valid_choices]:
                raise ValueError
            return choice
        except ValueError:
            print(f"{Color.FAIL}无效输入,请重试!{Color.ENDC}")
        except (KeyboardInterrupt, EOFError):
            print(f"\n{Color.FAIL}输入被中断{Color.ENDC}")
            raise
def get_input(prompt: str, show: bool, is_salt=False) -> bytes:
    """通用输入函数,is_salt为True时不隐藏"""
    prompt = f"{Color.BOLD}{prompt}{Color.ENDC} "
    if show or is_salt:
        return input(prompt).encode()
    return getpass.getpass(prompt).encode()

def ask_show_prompt() -> bool:
    """统一使用'明文输入原始数据'提示"""
    choice = get_choice("明文输入原始数据?(y/n)[n]: ", ['y', 'n', ''])
    return choice == 'y'

def export_hash_result(result: dict):
    """导出哈希结果到文件"""
    print(f"\n{Color.OKBLUE}=== 导出选项 ==={Color.ENDC}")
    file_name = input(f"{Color.BOLD}输入导出文件名(默认:hash_result.json): {Color.ENDC}") or "hash_result.json"

    # 如果不包含盐值,则从结果中移除
    export_data = result.copy()
    if not result.get('include_salt', False) and 'salt_value' in export_data:
        del export_data['salt_value']

    try:
        with open(file_name, 'w') as f:
            json.dump(export_data, f, indent=2)
        print(f"{Color.OKGREEN}结果已导出到 {Path(file_name).absolute()}{Color.ENDC}")
    except Exception as e:
        print(f"{Color.FAIL}导出失败: {str(e)}{Color.ENDC}")

def select_algorithm():
    """选择哈希算法"""
    algorithms = {
        "1": ("MD5", HashAlgorithm.MD5),
        "2": ("SHA-256", HashAlgorithm.SHA256),
        "3": ("BCrypt", HashAlgorithm.BCRYPT),
        "4": ("Argon2", HashAlgorithm.ARGON2)
    }

    print(f"\n{Color.OKBLUE}=== 选择哈希算法 ==={Color.ENDC}")
    for key, (name, _) in algorithms.items():
        print(f"{Color.BOLD}{key}. {name}{Color.ENDC}")

    choice = get_choice("请选择算法(1-4): ", algorithms.keys())
    return algorithms[choice][1]

def calculate_file_hash(file_path: str, algorithm: HashAlgorithm) -> str:
    """计算文件的哈希值"""
    hash_obj = hashlib.md5() if algorithm == HashAlgorithm.MD5 else hashlib.sha256()
    
    with open(file_path, 'rb') as f:
        while chunk := f.read(8192):
            hash_obj.update(chunk)
    
    return hash_obj.hexdigest()

def generate_hash():
    """生成哈希模式"""
    clear_screen()
    print_header("哈希生成模式")

    print(f"{Color.BOLD}选择哈希类型:{Color.ENDC}")
    print(f"{Color.OKBLUE}1. 文本/字符串哈希{Color.ENDC}")
    print(f"{Color.OKBLUE}2. 文件哈希{Color.ENDC}")
    hash_type = get_choice("请选择(1-2): ", ['1', '2'])

    if hash_type == '1':
        algorithm = select_algorithm()
        show = ask_show_prompt()
        data = get_input("输入要哈希的值: ", show)

        # 生成哈希
        result = {
            "algorithm": algorithm.name.lower(),
            "input_length": len(data),
            "timestamp": datetime.now().isoformat()
        }

        try:
            if algorithm == HashAlgorithm.MD5:
                salt = get_choice("是否加盐?(y/n)[n]: ", ['y', 'n', '']) == 'y'
                if salt:
                    custom_salt = get_input("输入盐值: ", show=True, is_salt=True)
                    hash_value = hashlib.md5(custom_salt + data).hexdigest()
                    result.update({
                        "salt_used": True,
                        "salt_value": custom_salt.decode(),
                        "include_salt": False
                    })
                else:
                    hash_value = hashlib.md5(data).hexdigest()
                    result["salt_used"] = False

            elif algorithm == HashAlgorithm.SHA256:
                salt = get_choice("是否加盐?(y/n)[n]: ", ['y', 'n', '']) == 'y'
                if salt:
                    custom_salt = get_input("输入盐值: ", show=True, is_salt=True)
                    hash_value = hashlib.sha256(custom_salt + data).hexdigest()
                    result.update({
                        "salt_used": True,
                        "salt_value": custom_salt.decode(),
                        "include_salt": False
                    })
                else:
                    hash_value = hashlib.sha256(data).hexdigest()
                    result["salt_used"] = False

            elif algorithm == HashAlgorithm.BCRYPT:
                print(f"{Color.WARNING}BCrypt 正在生成哈希,可能需要一些时间...{Color.ENDC}")
                hash_value = bcrypt.hashpw(data, bcrypt.gensalt()).decode()
                result["salt_used"] = "auto"

            elif algorithm == HashAlgorithm.ARGON2:
                print(f"{Color.WARNING}Argon2 正在生成哈希,可能需要一些时间...{Color.ENDC}")
                hasher = argon2.PasswordHasher()
                hash_value = hasher.hash(data)
                result["salt_used"] = "auto"

            # 输出结果
            result["hash_value"] = hash_value
            clear_screen()
            print_header("哈希生成结果")
            print(f"{Color.BOLD}算法:{Color.ENDC} {algorithm.name}")
            if algorithm in [HashAlgorithm.MD5, HashAlgorithm.SHA256]:
                print(f"{Color.BOLD}加盐:{Color.ENDC} {'是' if result.get('salt_used') else '否'}")
            print(f"{Color.BOLD}哈希值:{Color.ENDC}\n{Color.OKGREEN}{hash_value}{Color.ENDC}")

            # 导出逻辑
            export = get_choice("\n是否导出哈希结果?(y/n)[n]: ", ['y', 'n', '']) == 'y'
            if export:
                if algorithm in [HashAlgorithm.MD5, HashAlgorithm.SHA256] and result.get("salt_used"):
                    result['include_salt'] = get_choice("是否导出盐值?(y/n)[n]: ", ['y', 'n', '']) == 'y'
                export_hash_result(result)

        except Exception as e:
            print(f"{Color.FAIL}生成哈希时出错: {str(e)}{Color.ENDC}")

    elif hash_type == '2':
        print(f"\n{Color.BOLD}=== 文件哈希生成 ==={Color.ENDC}")
        file_path = input(f"{Color.BOLD}输入文件路径: {Color.ENDC}").strip()
        
        if not Path(file_path).is_file():
            print(f"{Color.FAIL}错误: 文件不存在或不是有效文件!{Color.ENDC}")
            return
        
        print(f"\n{Color.BOLD}选择文件哈希算法:{Color.ENDC}")
        print(f"{Color.OKBLUE}1. MD5{Color.ENDC}")
        print(f"{Color.OKBLUE}2. SHA-256{Color.ENDC}")
        algorithm_choice = get_choice("请选择(1-2): ", ['1', '2'])
        algorithm = HashAlgorithm.MD5 if algorithm_choice == '1' else HashAlgorithm.SHA256
        
        try:
            print(f"{Color.WARNING}正在计算文件哈希,请稍候...{Color.ENDC}")
            hash_value = calculate_file_hash(file_path, algorithm)
            
            result = {
                "algorithm": algorithm.name.lower(),
                "file_path": file_path,
                "file_size": Path(file_path).stat().st_size,
                "timestamp": datetime.now().isoformat(),
                "hash_value": hash_value
            }
            
            clear_screen()
            print_header("文件哈希生成结果")
            print(f"{Color.BOLD}文件:{Color.ENDC} {file_path}")
            print(f"{Color.BOLD}大小:{Color.ENDC} {result['file_size']} 字节")
            print(f"{Color.BOLD}算法:{Color.ENDC} {algorithm.name}")
            print(f"{Color.BOLD}哈希值:{Color.ENDC}\n{Color.OKGREEN}{hash_value}{Color.ENDC}")
            
            export = get_choice("\n是否导出哈希结果?(y/n)[n]: ", ['y', 'n', '']) == 'y'
            if export:
                export_hash_result(result)
                
        except Exception as e:
            print(f"{Color.FAIL}计算文件哈希时出错: {str(e)}{Color.ENDC}")

def verify_hash():
    """验证哈希模式"""
    clear_screen()
    print_header("哈希验证模式")

    print(f"{Color.BOLD}选择验证类型:{Color.ENDC}")
    print(f"{Color.OKBLUE}1. 文本/字符串哈希验证{Color.ENDC}")
    print(f"{Color.OKBLUE}2. 文件哈希验证{Color.ENDC}")
    verify_type = get_choice("请选择(1-2): ", ['1', '2'])

    if verify_type == '1':
        stored_hash = input(f"{Color.BOLD}输入待验证的哈希值: {Color.ENDC}").strip()
        show = ask_show_prompt()
        original_data = get_input("输入原始数据: ", show)

        try:
            if stored_hash.startswith("$2b$"):
                # BCrypt 验证
                if bcrypt.checkpw(original_data, stored_hash.encode()):
                    print(f"\n{Color.OKGREEN}验证结果: ✅ 匹配 (BCrypt){Color.ENDC}")
                else:
                    print(f"\n{Color.FAIL}验证结果: ❌ 不匹配{Color.ENDC}")

            elif stored_hash.startswith("$argon2"):
                # Argon2 验证
                hasher = argon2.PasswordHasher()
                try:
                    hasher.verify(stored_hash, original_data)
                    print(f"\n{Color.OKGREEN}验证结果: ✅ 匹配 (Argon2){Color.ENDC}")
                except:
                    print(f"\n{Color.FAIL}验证结果: ❌ 不匹配{Color.ENDC}")

            else:
                # MD5 或 SHA256 验证
                if len(stored_hash) == 32:
                    algorithm = "MD5"
                    salt = get_choice("\n生成此哈希时是否加盐?(y/n)[n]: ", ['y', 'n', '']) == 'y'
                    if salt:
                        custom_salt = get_input("输入原始盐值: ", show=True, is_salt=True)
                        new_hash = hashlib.md5(custom_salt + original_data).hexdigest()
                    else:
                        new_hash = hashlib.md5(original_data).hexdigest()
                elif len(stored_hash) == 64:
                    algorithm = "SHA-256"
                    salt = get_choice("\n生成此哈希时是否加盐?(y/n)[n]: ", ['y', 'n', '']) == 'y'
                    if salt:
                        custom_salt = get_input("输入原始盐值: ", show=True, is_salt=True)
                        new_hash = hashlib.sha256(custom_salt + original_data).hexdigest()
                    else:
                        new_hash = hashlib.sha256(original_data).hexdigest()
                else:
                    print(f"\n{Color.FAIL}错误: 无法识别的哈希格式!{Color.ENDC}")
                    return

                if new_hash == stored_hash:
                    print(f"\n{Color.OKGREEN}验证结果: ✅ 匹配 ({algorithm}){Color.ENDC}")
                else:
                    print(f"\n{Color.FAIL}验证结果: ❌ 不匹配{Color.ENDC}")

        except Exception as e:
            print(f"\n{Color.FAIL}验证时出错: {str(e)}{Color.ENDC}")

    elif verify_type == '2':
        print(f"\n{Color.BOLD}=== 文件哈希验证 ==={Color.ENDC}")
        file_path = input(f"{Color.BOLD}输入要验证的文件路径: {Color.ENDC}").strip()
        
        if not Path(file_path).is_file():
            print(f"{Color.FAIL}错误: 文件不存在或不是有效文件!{Color.ENDC}")
            return
        
        stored_hash = input(f"{Color.BOLD}输入预期的哈希值: {Color.ENDC}").strip()
        
        print(f"\n{Color.BOLD}选择文件哈希算法:{Color.ENDC}")
        print(f"{Color.OKBLUE}1. MD5{Color.ENDC}")
        print(f"{Color.OKBLUE}2. SHA-256{Color.ENDC}")
        algorithm_choice = get_choice("请选择(1-2): ", ['1', '2'])
        algorithm = HashAlgorithm.MD5 if algorithm_choice == '1' else HashAlgorithm.SHA256
        
        try:
            print(f"{Color.WARNING}正在计算文件哈希,请稍候...{Color.ENDC}")
            calculated_hash = calculate_file_hash(file_path, algorithm)
            
            if calculated_hash.lower() == stored_hash.lower():
                print(f"\n{Color.OKGREEN}验证结果: ✅ 匹配 ({algorithm.name}){Color.ENDC}")
                print(f"{Color.BOLD}文件哈希值:{Color.ENDC} {calculated_hash}")
            else:
                print(f"\n{Color.FAIL}验证结果: ❌ 不匹配{Color.ENDC}")
                print(f"{Color.BOLD}预期哈希值:{Color.ENDC} {stored_hash}")
                print(f"{Color.BOLD}实际哈希值:{Color.ENDC} {calculated_hash}")
                
        except Exception as e:
            print(f"\n{Color.FAIL}验证文件哈希时出错: {str(e)}{Color.ENDC}")

def show_help():
    """显示帮助信息"""
    clear_screen()
    print_header("帮助信息")
    print(f"""
{Color.BOLD}功能说明:{Color.ENDC}
1. {Color.OKBLUE}生成哈希{Color.ENDC} - 创建各种安全算法的哈希值
   - 支持文本/字符串哈希和文件哈希
2. {Color.OKBLUE}验证哈希{Color.ENDC} - 验证已有哈希值是否匹配原始数据
   - 支持文本/字符串哈希验证和文件哈希验证
3. {Color.OKBLUE}帮助{Color.ENDC} - 显示本帮助信息
4. {Color.OKBLUE}退出{Color.ENDC} - 退出程序

{Color.BOLD}支持的算法:{Color.ENDC}
- {Color.OKGREEN}MD5{Color.ENDC}: 快速但不安全的哈希算法
- {Color.OKGREEN}SHA-256{Color.ENDC}: 安全哈希算法
- {Color.OKGREEN}BCrypt{Color.ENDC}: 专为密码设计的慢哈希算法
- {Color.OKGREEN}Argon2{Color.ENDC}: 密码哈希大赛获胜者,安全性高

{Color.BOLD}文件哈希功能:{Color.ENDC}
- 支持计算和验证文件的 MD5 和 SHA-256 哈希值
- 适用于验证文件完整性,检测文件是否被篡改

{Color.BOLD}安全提示:{Color.ENDC}
- 对于密码存储,推荐使用 {Color.WARNING}BCrypt 或 Argon2{Color.ENDC}
- MD5 和 SHA-256 需要手动加盐才适合密码存储
- 盐值输入会始终显示,其他敏感输入可以隐藏

{Color.BOLD}开发者信息:{Color.ENDC}
- 开发者: {Color.OKBLUE}五陵年少{Color.ENDC}
- 邮箱: {Color.OKBLUE}[email protected]{Color.ENDC}

{Color.BOLD}版权说明:{Color.ENDC}
{Color.WARNING}Copyright © 2025 五陵年少. 保留所有权利。{Color.ENDC}
本工具仅供学习和合法用途使用。
""")
    input(f"\n{Color.BOLD}按Enter键返回主菜单...{Color.ENDC}")

def main():
    try:
        while True:
            clear_screen()
            print_header("高级哈希工具")
            print_menu(["生成哈希", "验证哈希", "帮助", "退出"])

            choice = get_choice("请选择功能(1-4): ", ['1', '2', '3', '4'])

            if choice == '1':
                generate_hash()
            elif choice == '2':
                verify_hash()
            elif choice == '3':
                show_help()
            elif choice == '4':
                print(f"\n{Color.OKGREEN}感谢使用,再见!{Color.ENDC}")
                sys.exit(0)

            input(f"\n{Color.BOLD}按Enter键继续...{Color.ENDC}")
    except KeyboardInterrupt:
        print(f"\n{Color.FAIL}程序被用户中断{Color.ENDC}")
        sys.exit(1)
    except Exception as e:
        print(f"\n{Color.FAIL}发生错误: {str(e)}{Color.ENDC}")
        sys.exit(1)

if __name__ == "__main__":
    main()

你可能感兴趣的:(python)