From 4dc0e91dea8da665c7016737c972f8c065ff63b4 Mon Sep 17 00:00:00 2001 From: zhji Date: Sun, 30 Nov 2025 15:55:22 +0800 Subject: [PATCH] [feat] add licence for hfc --- .gitignore | 2 + python/hfc/cali.py | 132 +++++++++++++++++++++++- python/hfc/hfc.py | 139 ++++++++++++++++++++++++- python/hfc/license_generator.py | 175 ++++++++++++++++++++++++++++++++ python/hfc/packet.cmd | 4 + 5 files changed, 447 insertions(+), 5 deletions(-) create mode 100644 python/hfc/license_generator.py diff --git a/.gitignore b/.gitignore index b9deb68..8645cfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ python/*/build/* python/*/dist/* python/*/*.spec +**/__pycache__/* python/hfc/测试报告_*.jpg +python/hfc/license diff --git a/python/hfc/cali.py b/python/hfc/cali.py index e2934ac..b22d61c 100644 --- a/python/hfc/cali.py +++ b/python/hfc/cali.py @@ -8,6 +8,13 @@ from PyQt5.QtGui import QPixmap, QFont, QIcon from pymodbus.client.sync import ModbusSerialClient as ModbusRtuClient from pymodbus.exceptions import ModbusException +import hashlib +import base64 +import uuid +import subprocess +import platform +import logging + class ModbusWorker(QThread): """Modbus通信工作线程""" concentration_updated = pyqtSignal(int) # 改为int类型 @@ -210,6 +217,9 @@ class MainWindow(QMainWindow): } """) self.calibrate_button.clicked.connect(self.start_calibration) + if license_check() == False: + self.calibrate_button.setEnabled(False) + self.calibrate_button.setText("许可证无效,请联系管理员") layout.addWidget(self.calibrate_button) # 状态标签 @@ -245,7 +255,10 @@ class MainWindow(QMainWindow): def update_concentration(self, concentration): """更新浓度显示""" # 直接显示整数,不加小数位 - self.concentration_label.setText(f"{concentration} ppm") + if license_check(): + self.concentration_label.setText(f"{concentration} ppm") + else: + self.concentration_label.setText("许可证无效") # 根据浓度值改变颜色 if concentration < 100: @@ -290,6 +303,123 @@ class MainWindow(QMainWindow): self.modbus_worker.stop() event.accept() +def get_windows_serial_number(): + """ + 获取Windows系统的唯一序列号 + """ + try: + if platform.system() != "Windows": + raise Exception("此功能仅支持Windows系统") + + # 使用WMIC获取BIOS序列号 + result = subprocess.check_output( + 'wmic bios get serialnumber', + shell=True, + stderr=subprocess.STDOUT, + text=True + ) + + # 解析输出结果 + lines = result.strip().split('\n') + for line in lines: + if line.strip() and "SerialNumber" not in line: + serial = line.strip() + if serial and serial != "System Serial Number" and serial != "To be filled by O.E.M.": + return serial + + # 如果无法获取BIOS序列号,尝试获取磁盘序列号 + result = subprocess.check_output( + 'wmic diskdrive get serialnumber', + shell=True, + stderr=subprocess.STDOUT, + text=True + ) + + lines = result.strip().split('\n') + for line in lines: + if line.strip() and "SerialNumber" not in line: + serial = line.strip() + if serial: + return serial + + raise Exception("无法获取系统序列号") + + except Exception as e: + logging.warning(f"获取Windows序列号失败: {e}") + # 返回一个备用标识符 + return str(uuid.getnode()) + +def get_mac_address(): + """ + 获取MAC地址 + """ + try: + # 获取本机的MAC地址 + mac = uuid.getnode() + mac_str = ':'.join(("%012X" % mac)[i:i+2] for i in range(0, 12, 2)) + return mac_str + except Exception as e: + logging.error(f"获取MAC地址失败: {e}") + return "00:00:00:00:00:00" + +def triple_hash_sha384(data): + """ + 对数据进行三次连续的SHA384哈希计算 + """ + # 第一次SHA384 + hash1 = hashlib.sha384(data.encode('utf-8')).hexdigest() + # 第二次SHA384 + hash2 = hashlib.sha384(hash1.encode('utf-8')).hexdigest() + # 第三次SHA384 + hash3 = hashlib.sha384(hash2.encode('utf-8')).hexdigest() + + return hash3 + +def triple_hash_sha256(data): + """ + 对数据进行三次连续的SHA256哈希计算 + """ + # 第一次SHA256 + hash1 = hashlib.sha256(data.encode('utf-8')).hexdigest() + # 第二次SHA256 + hash2 = hashlib.sha256(hash1.encode('utf-8')).hexdigest() + # 第三次SHA256 + hash3 = hashlib.sha256(hash2.encode('utf-8')).hexdigest() + + return hash3 + +def license_check(): + """ + j检查证书是否合法 + """ + + try: + # 0. 读取证书文件 + if not os.path.exists('license'): + return False + with open('license', 'r', encoding='utf-8') as f: + existing_license = f.read().strip() + # 1. 获取Windows序列号和MAC地址 + serial_number = get_windows_serial_number() + mac_address = get_mac_address() + # 2. 对序列号进行三次SHA384计算 + serial_hash = triple_hash_sha384(serial_number) + # 3. 对MAC地址进行三次SHA256计算 + mac_hash = triple_hash_sha256(mac_address) + # 4. 拼接两个哈希值并进行Base64编码 + combined_hash = serial_hash + mac_hash + base64_encoded = base64.b64encode(combined_hash.encode('utf-8')).decode('utf-8') + # 5. 对Base64结果进行三次SHA384计算 + final_hash = triple_hash_sha384(base64_encoded) + # 6. 对最终哈希值进行Base64编码 + final_base64 = base64.b64encode(final_hash.encode('utf-8')).decode('utf-8') + # 7. 校验证书合法性 + return existing_license == final_base64 + + except Exception as e: + print(f"\n许可证获取失败: {e}") + return False + if __name__ == "__main__": app = QApplication(sys.argv) diff --git a/python/hfc/hfc.py b/python/hfc/hfc.py index 7e7280e..e1f3021 100644 --- a/python/hfc/hfc.py +++ b/python/hfc/hfc.py @@ -10,6 +10,13 @@ import struct from PIL import Image, ImageTk, ImageDraw, ImageFont import os +import hashlib +import base64 +import uuid +import subprocess +import platform +import logging + class ModbusGasAnalyzer: def __init__(self, root): self.root = root @@ -129,10 +136,17 @@ class ModbusGasAnalyzer: title_label.pack(pady=40) # 开始测试按钮 - self.start_button = tk.Button(right_frame, text="开始测试", font=('Arial', 16, 'bold'), - bg='green', fg='white', width=15, height=2, - command=self.start_test) - self.start_button.pack(pady=40) + if license_check(): + self.start_button = tk.Button(right_frame, text="开始测试", font=('Arial', 16, 'bold'), + bg='green', fg='white', width=15, height=2, + command=self.start_test) + self.start_button.pack(pady=40) + else: + self.start_button = tk.Button(right_frame, text="许可证无效\n请联系管理员", font=('Arial', 16, 'bold'), + bg='green', fg='white', width=15, height=2, + command=self.start_test) + self.start_button.config(state="disable") + self.start_button.pack(pady=40) def create_page2(self): """创建第二个页面""" @@ -595,6 +609,123 @@ class ModbusGasAnalyzer: return False +def get_windows_serial_number(): + """ + 获取Windows系统的唯一序列号 + """ + try: + if platform.system() != "Windows": + raise Exception("此功能仅支持Windows系统") + + # 使用WMIC获取BIOS序列号 + result = subprocess.check_output( + 'wmic bios get serialnumber', + shell=True, + stderr=subprocess.STDOUT, + text=True + ) + + # 解析输出结果 + lines = result.strip().split('\n') + for line in lines: + if line.strip() and "SerialNumber" not in line: + serial = line.strip() + if serial and serial != "System Serial Number" and serial != "To be filled by O.E.M.": + return serial + + # 如果无法获取BIOS序列号,尝试获取磁盘序列号 + result = subprocess.check_output( + 'wmic diskdrive get serialnumber', + shell=True, + stderr=subprocess.STDOUT, + text=True + ) + + lines = result.strip().split('\n') + for line in lines: + if line.strip() and "SerialNumber" not in line: + serial = line.strip() + if serial: + return serial + + raise Exception("无法获取系统序列号") + + except Exception as e: + logging.warning(f"获取Windows序列号失败: {e}") + # 返回一个备用标识符 + return str(uuid.getnode()) + +def get_mac_address(): + """ + 获取MAC地址 + """ + try: + # 获取本机的MAC地址 + mac = uuid.getnode() + mac_str = ':'.join(("%012X" % mac)[i:i+2] for i in range(0, 12, 2)) + return mac_str + except Exception as e: + logging.error(f"获取MAC地址失败: {e}") + return "00:00:00:00:00:00" + +def triple_hash_sha384(data): + """ + 对数据进行三次连续的SHA384哈希计算 + """ + # 第一次SHA384 + hash1 = hashlib.sha384(data.encode('utf-8')).hexdigest() + # 第二次SHA384 + hash2 = hashlib.sha384(hash1.encode('utf-8')).hexdigest() + # 第三次SHA384 + hash3 = hashlib.sha384(hash2.encode('utf-8')).hexdigest() + + return hash3 + +def triple_hash_sha256(data): + """ + 对数据进行三次连续的SHA256哈希计算 + """ + # 第一次SHA256 + hash1 = hashlib.sha256(data.encode('utf-8')).hexdigest() + # 第二次SHA256 + hash2 = hashlib.sha256(hash1.encode('utf-8')).hexdigest() + # 第三次SHA256 + hash3 = hashlib.sha256(hash2.encode('utf-8')).hexdigest() + + return hash3 + +def license_check(): + """ + j检查证书是否合法 + """ + + try: + # 0. 读取证书文件 + if not os.path.exists('license'): + return False + with open('license', 'r', encoding='utf-8') as f: + existing_license = f.read().strip() + # 1. 获取Windows序列号和MAC地址 + serial_number = get_windows_serial_number() + mac_address = get_mac_address() + # 2. 对序列号进行三次SHA384计算 + serial_hash = triple_hash_sha384(serial_number) + # 3. 对MAC地址进行三次SHA256计算 + mac_hash = triple_hash_sha256(mac_address) + # 4. 拼接两个哈希值并进行Base64编码 + combined_hash = serial_hash + mac_hash + base64_encoded = base64.b64encode(combined_hash.encode('utf-8')).decode('utf-8') + # 5. 对Base64结果进行三次SHA384计算 + final_hash = triple_hash_sha384(base64_encoded) + # 6. 对最终哈希值进行Base64编码 + final_base64 = base64.b64encode(final_hash.encode('utf-8')).decode('utf-8') + # 7. 校验证书合法性 + return existing_license == final_base64 + + except Exception as e: + print(f"\n许可证获取失败: {e}") + return False + def main(): root = tk.Tk() app = ModbusGasAnalyzer(root) diff --git a/python/hfc/license_generator.py b/python/hfc/license_generator.py new file mode 100644 index 0000000..c3504fa --- /dev/null +++ b/python/hfc/license_generator.py @@ -0,0 +1,175 @@ +import hashlib +import base64 +import uuid +import subprocess +import platform +import logging + +def get_windows_serial_number(): + """ + 获取Windows系统的唯一序列号 + """ + try: + if platform.system() != "Windows": + raise Exception("此功能仅支持Windows系统") + + # 使用WMIC获取BIOS序列号 + result = subprocess.check_output( + 'wmic bios get serialnumber', + shell=True, + stderr=subprocess.STDOUT, + text=True + ) + + # 解析输出结果 + lines = result.strip().split('\n') + for line in lines: + if line.strip() and "SerialNumber" not in line: + serial = line.strip() + if serial and serial != "System Serial Number" and serial != "To be filled by O.E.M.": + return serial + + # 如果无法获取BIOS序列号,尝试获取磁盘序列号 + result = subprocess.check_output( + 'wmic diskdrive get serialnumber', + shell=True, + stderr=subprocess.STDOUT, + text=True + ) + + lines = result.strip().split('\n') + for line in lines: + if line.strip() and "SerialNumber" not in line: + serial = line.strip() + if serial: + return serial + + raise Exception("无法获取系统序列号") + + except Exception as e: + logging.warning(f"获取Windows序列号失败: {e}") + # 返回一个备用标识符 + return str(uuid.getnode()) + +def get_mac_address(): + """ + 获取MAC地址 + """ + try: + # 获取本机的MAC地址 + mac = uuid.getnode() + mac_str = ':'.join(("%012X" % mac)[i:i+2] for i in range(0, 12, 2)) + return mac_str + except Exception as e: + logging.error(f"获取MAC地址失败: {e}") + return "00:00:00:00:00:00" + +def triple_hash_sha384(data): + """ + 对数据进行三次连续的SHA384哈希计算 + """ + print(f"原始数据: {data}") + + # 第一次SHA384 + hash1 = hashlib.sha384(data.encode('utf-8')).hexdigest() + print(f"第一次SHA384: {hash1}") + + # 第二次SHA384 + hash2 = hashlib.sha384(hash1.encode('utf-8')).hexdigest() + print(f"第二次SHA384: {hash2}") + + # 第三次SHA384 + hash3 = hashlib.sha384(hash2.encode('utf-8')).hexdigest() + print(f"第三次SHA384: {hash3}") + + return hash3 + +def triple_hash_sha256(data): + """ + 对数据进行三次连续的SHA256哈希计算 + """ + print(f"原始数据: {data}") + + # 第一次SHA256 + hash1 = hashlib.sha256(data.encode('utf-8')).hexdigest() + print(f"第一次SHA256: {hash1}") + + # 第二次SHA256 + hash2 = hashlib.sha256(hash1.encode('utf-8')).hexdigest() + print(f"第二次SHA256: {hash2}") + + # 第三次SHA256 + hash3 = hashlib.sha256(hash2.encode('utf-8')).hexdigest() + print(f"第三次SHA256: {hash3}") + + return hash3 + +def main(): + """ + 主函数 + """ + print("=" * 50) + print("开始生成许可证文件") + print("=" * 50) + + try: + # 1. 获取Windows序列号和MAC地址 + print("\n步骤1: 获取系统信息") + serial_number = get_windows_serial_number() + mac_address = get_mac_address() + + print(f"系统序列号: {serial_number}") + print(f"MAC地址: {mac_address}") + + # 2. 对序列号进行三次SHA384计算 + print("\n步骤2: 对序列号进行三次SHA384计算") + serial_hash = triple_hash_sha384(serial_number) + + # 3. 对MAC地址进行三次SHA256计算 + print("\n步骤3: 对MAC地址进行三次SHA256计算") + mac_hash = triple_hash_sha256(mac_address) + + # 4. 拼接两个哈希值并进行Base64编码 + print("\n步骤4: 拼接哈希值并进行Base64编码") + combined_hash = serial_hash + mac_hash + print(f"拼接后的哈希值: {combined_hash}") + + base64_encoded = base64.b64encode(combined_hash.encode('utf-8')).decode('utf-8') + print(f"Base64编码结果: {base64_encoded}") + + # 5. 对Base64结果进行三次SHA384计算 + print("\n步骤5: 对Base64结果进行三次SHA384计算") + final_hash = triple_hash_sha384(base64_encoded) + + # 6. 对最终哈希值进行Base64编码 + print("\n步骤6: 对最终哈希值进行Base64编码") + final_base64 = base64.b64encode(final_hash.encode('utf-8')).decode('utf-8') + print(f"最终Base64序列: {final_base64}") + + # 7. 保存到lic文件 + print("\n步骤7: 保存到文件") + with open('license', 'w', encoding='utf-8') as f: + f.write(final_base64) + + print(f"许可证文件已保存到: {os.path.abspath('lic')}") + + # 8. 成功日志 + print("\n" + "=" * 50) + print("许可证生成成功!") + print("=" * 50) + + # 显示文件内容预览 + print(f"生成的许可证内容 (前50字符): {final_base64[:50]}...") + + except Exception as e: + print(f"\n错误: {e}") + logging.error(f"许可证生成失败: {e}") + +if __name__ == "__main__": + # 配置日志 + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + + # 导入os模块(在需要时导入) + import os + + main() \ No newline at end of file diff --git a/python/hfc/packet.cmd b/python/hfc/packet.cmd index 61dcf4d..1fa9ef6 100644 --- a/python/hfc/packet.cmd +++ b/python/hfc/packet.cmd @@ -1 +1,5 @@ python.exe -m PyInstaller --onefile --windowed --name HFC .\hfc.py + +python.exe -m PyInstaller --onefile --windowed --name CALI .\cali.py + +python.exe -m PyInstaller --onefile --console --name license_generator .\license_generator.py