你有没有发现,现在下载东西,很多都会给你一个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()