214 lines
6.1 KiB
Python
214 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
激活码生成工具
|
||
用于根据客户设备UUID生成激活码
|
||
|
||
使用方法:
|
||
python generate_activation_code.py <设备UUID> <客户密码>
|
||
|
||
示例:
|
||
python generate_activation_code.py "12345678-1234-1234-1234-123456789ABC" "888888"
|
||
|
||
参数说明:
|
||
设备UUID: 客户设备的主板UUID
|
||
客户密码: 与程序中 customerPassword 一致的密码
|
||
"""
|
||
|
||
import sys
|
||
import base64
|
||
import hashlib
|
||
|
||
# ============ 配置参数 ============
|
||
ENCRYPT_KEY = b"GetonAgain2026SecretKey!@#$%" # 加密密钥(必须与程序一致)
|
||
# ==================================
|
||
|
||
def encrypt(plain_text: str) -> str:
|
||
"""
|
||
加密函数 - 与 C++ LicenseManager::encrypt 完全对应
|
||
"""
|
||
data = plain_text.encode('utf-8')
|
||
|
||
# 第一步:计算SHA256哈希
|
||
hash_obj = hashlib.sha256(data)
|
||
hash_bytes = hash_obj.digest()
|
||
|
||
# 第二步:构造加密数据 = 哈希前8字节 + 原始数据
|
||
to_encrypt = hash_bytes[:8] + data
|
||
|
||
# 第三步:多层加密
|
||
encrypted = bytearray()
|
||
for i, byte in enumerate(to_encrypt):
|
||
c = byte
|
||
|
||
# 第一层:与主密钥XOR
|
||
c = c ^ ENCRYPT_KEY[i % len(ENCRYPT_KEY)]
|
||
|
||
# 第二层:字节置换(非线性变换)
|
||
c = (c * 7 + 13) % 256
|
||
|
||
# 第三层:与位置相关的混淆
|
||
c = c ^ ((i * 31 + 17) % 256)
|
||
|
||
encrypted.append(c)
|
||
|
||
# 第四步:Base64编码
|
||
return base64.b64encode(bytes(encrypted)).decode('latin-1')
|
||
|
||
|
||
def decrypt(encrypted_text: str) -> str:
|
||
"""
|
||
解密函数 - 与 C++ LicenseManager::decrypt 完全对应
|
||
"""
|
||
# Base64解码
|
||
encrypted = bytearray(base64.b64decode(encrypted_text.encode('latin-1')))
|
||
|
||
if len(encrypted) < 9: # 至少8字节校验头 + 1字节数据
|
||
return ""
|
||
|
||
# 逆向解密
|
||
decrypted = bytearray()
|
||
for i, byte in enumerate(encrypted):
|
||
c = byte
|
||
|
||
# 逆向第三层:位置混淆
|
||
c = c ^ ((i * 31 + 17) % 256)
|
||
|
||
# 逆向第二层:字节置换的逆运算
|
||
# 7 的模逆元是 183 (因为 7 * 183 = 1281 = 5 * 256 + 1)
|
||
c = ((c + 256 - 13) * 183) % 256
|
||
|
||
# 逆向第一层:与主密钥XOR
|
||
c = c ^ ENCRYPT_KEY[i % len(ENCRYPT_KEY)]
|
||
|
||
decrypted.append(c)
|
||
|
||
# 提取校验头和原始数据
|
||
stored_hash = bytes(decrypted[:8])
|
||
original_data = bytes(decrypted[8:])
|
||
|
||
# 验证哈希
|
||
computed_hash = hashlib.sha256(original_data).digest()[:8]
|
||
if computed_hash != stored_hash:
|
||
print("警告: 哈希校验失败,数据可能已损坏")
|
||
return ""
|
||
|
||
return original_data.decode('utf-8')
|
||
|
||
|
||
def generate_activation_code(uuid: str, password: str) -> str:
|
||
"""
|
||
生成激活码
|
||
格式: encrypt(UUID:password)
|
||
"""
|
||
plain_text = f"{uuid}:{password}"
|
||
return encrypt(plain_text)
|
||
|
||
|
||
def verify_activation_code(activation_code: str, expected_uuid: str, expected_password: str) -> bool:
|
||
"""验证激活码是否有效"""
|
||
try:
|
||
decrypted = decrypt(activation_code)
|
||
|
||
if not decrypted or ':' not in decrypted:
|
||
return False
|
||
|
||
# 使用 rsplit 从右侧分割,只分割一次
|
||
parts = decrypted.rsplit(':', 1)
|
||
uuid = parts[0]
|
||
password = parts[1]
|
||
|
||
return uuid == expected_uuid and password == expected_password
|
||
|
||
except Exception as e:
|
||
print(f"验证错误: {e}")
|
||
return False
|
||
|
||
|
||
def save_to_csv(uuid: str, activation_code: str, customer_name: str = ""):
|
||
"""
|
||
将激活记录保存到CSV文件
|
||
"""
|
||
import csv
|
||
import os
|
||
from datetime import datetime
|
||
|
||
csv_file = "activation_records.csv"
|
||
file_exists = os.path.exists(csv_file)
|
||
|
||
# 获取当前时间
|
||
activation_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
|
||
try:
|
||
with open(csv_file, 'a', newline='', encoding='utf-8-sig') as f:
|
||
writer = csv.writer(f)
|
||
|
||
# 如果文件不存在,先写入表头
|
||
if not file_exists:
|
||
writer.writerow(['序号', '设备UUID', '激活码', '激活时间', '客户名称', '备注'])
|
||
|
||
# 统计当前记录数作为序号
|
||
record_num = 1
|
||
if file_exists:
|
||
with open(csv_file, 'r', encoding='utf-8-sig') as rf:
|
||
record_num = sum(1 for _ in rf) # 包含表头的行数就是新记录的序号
|
||
|
||
# 写入新记录
|
||
writer.writerow([record_num, uuid, activation_code, activation_time, customer_name, ''])
|
||
|
||
print(f"激活记录已保存到: {csv_file}")
|
||
|
||
except Exception as e:
|
||
print(f"保存CSV记录失败: {e}")
|
||
|
||
|
||
def main():
|
||
# 检查参数
|
||
if len(sys.argv) < 3:
|
||
print("用法: python generate_activation_code.py <设备UUID> <客户密码>")
|
||
print("示例: python generate_activation_code.py \"12345678-1234-1234-1234-123456789ABC\" \"888888\"")
|
||
return 1
|
||
|
||
uuid = sys.argv[1].strip()
|
||
password = sys.argv[2].strip()
|
||
|
||
if not uuid:
|
||
print("错误: UUID 不能为空!")
|
||
return 1
|
||
|
||
if not password:
|
||
print("错误: 密码不能为空!")
|
||
return 1
|
||
|
||
# 生成激活码
|
||
activation_code = generate_activation_code(uuid, password)
|
||
|
||
# 验证
|
||
if not verify_activation_code(activation_code, uuid, password):
|
||
print("错误: 激活码验证失败!")
|
||
return 1
|
||
|
||
# 保存激活码文件
|
||
filename = f"license_{uuid[:8]}.gal"
|
||
try:
|
||
with open(filename, 'w', encoding='utf-8') as f:
|
||
f.write(activation_code)
|
||
except Exception as e:
|
||
print(f"保存文件失败: {e}")
|
||
return 1
|
||
|
||
# 记录到CSV
|
||
save_to_csv(uuid, activation_code)
|
||
|
||
# 输出结果
|
||
print(f"UUID: {uuid}")
|
||
print(f"密码: {password}")
|
||
print(f"激活码: {activation_code}")
|
||
print(f"文件: {filename}")
|
||
print("完成")
|
||
|
||
return 0
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|