news 2026/6/17 3:18:11

Python软件授权验证完整方案(免费卡密系统搭建)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python软件授权验证完整方案(免费卡密系统搭建)

本文将带你完整实现一个带卡密验证的Python软件授权系统,包含:卡密验证、MD5签名防篡改、时间戳防重放、心跳保活、强制版本更新、公告推送等功能。无需自建服务器,对接即用。

一、为什么需要软件授权验证?

作为独立开发者,你辛辛苦苦写出来的软件,最怕的就是被人随意复制、破解、传播。一套完善的授权验证系统,可以帮你:

  • 控制软件使用期限(天卡/周卡/月卡/年卡/终身)

  • 绑定设备防止账号共享

  • 远程控制软件状态(禁用/启用)

  • 获取软件使用统计

  • 推送更新公告

市面上有不少付费的授权平台,但如果你只是想快速实现功能、又不想花冤枉钱,卡密通这个平台可以免费使用,我已经用了很久,稳定可靠。

二、完整代码

python

# ============================================================ # ceshi.py — 带卡密验证的测试软件 # 打包命令:pyinstaller --onefile --noconsole ceshi.py # ============================================================ # ---------- 卡密系统配置 ---------- SIGN_KEY = "keyt@2026" # 签名密钥(后台获取) API_BASE_URL = "https://www.keyt.cn/kami/你的用户名/check.php" APP_NAME = "a" # 应用名称 TIMESTAMP_MAX_DIFF = 120 # 时间戳容差(秒) HEARTBEAT_INTERVAL = 55 # 心跳间隔(秒) MAX_HEARTBEAT_FAILS = 5 # 最大连续心跳失败次数 APP_VERSION = "1.0.0" # 当前版本号 ENABLE_VERSION_CHECK = True # 版本检查开关 ENABLE_CARD_CACHE = True # 卡密缓存开关 ENABLE_HEARTBEAT = True # 心跳检测开关 import tkinter as tk from tkinter import ttk, messagebox import hashlib import time import uuid import threading import platform import subprocess import ssl import urllib.request import urllib.parse import os import sys import webbrowser import re # ================== 获取机器码 ================== def get_machine_code(): """获取本机机器码(MAC地址)""" try: mac = uuid.getnode() mac_str = ':'.join(('%012X' % mac)[i:i+2] for i in range(0, 12, 2)) return mac_str.replace(':', '') + "MAC" except: try: if platform.system() == "Windows": result = subprocess.run(['getmac', '/v', '/fo', 'csv'], capture_output=True, text=True, encoding='gbk') lines = result.stdout.split('\n') for line in lines[1:]: if 'Wi-Fi' in line or '以太网' in line or 'Ethernet' in line or 'WLAN' in line: parts = line.split(',') if len(parts) >= 2: mac = parts[0].strip('"').replace('-', '') if mac and mac != '00-00-00-00-00-00' and len(mac) >= 12: return mac + "MAC" except: pass try: result = subprocess.run(['ipconfig', '/all'], capture_output=True, text=True, encoding='gbk') mac_pattern = r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})' matches = re.findall(mac_pattern, result.stdout) if matches: mac = ''.join(matches[0]).replace(':', '').replace('-', '') if mac and len(mac) >= 12: return mac[:12] + "MAC" except: pass return "获取失败" # ================== MD5加密 ================== def md5_encrypt(text): return hashlib.md5(text.encode('utf-8')).hexdigest().lower() # ================== HTTP请求 ================== def http_get(url): try: ssl_context = ssl.create_default_context() ssl_context.check_hostname = False ssl_context.verify_mode = ssl.CERT_NONE headers = { 'Cache-Control': 'no-cache', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } req = urllib.request.Request(url, headers=headers) response = urllib.request.urlopen(req, timeout=10, context=ssl_context) headers_dict = dict(response.getheaders()) xsign2 = headers_dict.get('X-Sign2', '') content = response.read().decode('utf-8') return { 'status': response.status, 'text': content, 'xsign2': xsign2 } except Exception as e: return { 'status': 0, 'text': 'error|网络错误', 'xsign2': '' } # ================== 签名校验(核心防篡改)================== def verify_response(raw_response, xsign2): pos = raw_response.find("|sign=") if pos == -1: return "" body = raw_response[:pos] sign = raw_response[pos + 6:] local_sign = md5_encrypt(body + SIGN_KEY) if xsign2 and local_sign == xsign2.lower(): pass elif local_sign == sign.lower(): pass else: return "" last_pipe = body.rfind('|') if last_pipe == -1: return "" ts_str = body[last_pipe + 1:] if not ts_str.isdigit(): return "" ts = int(ts_str) now_ts = int(time.time()) diff = abs(now_ts - ts) if diff > TIMESTAMP_MAX_DIFF: return "" return body[:last_pipe] # ================== 获取开关状态 ================== def get_card_switch(): for retry in range(3): try: t = time.strftime("%Y%m%d%H%M%S") url = f"{API_BASE_URL}?act=get_switch&app={APP_NAME}&t={t}" result = http_get(url) if result['status'] == 200: biz = verify_response(result['text'], result['xsign2']) if biz: return "CARD_ON" if "CARD_ON" in biz else "CARD_OFF" except: pass if retry < 2: time.sleep(0.5) return "CARD_ON" # ================== 获取公告 ================== def fetch_notice(): try: url = f"{API_BASE_URL}?act=get_notice&app={APP_NAME}&t={int(time.time())}" result = http_get(url) if result['status'] == 200: notice = result['text'].strip() return notice if notice else "暂无公告" except: pass return "暂无公告" # ================== 获取最新版本号 ================== def fetch_latest_version(): try: url = f"{API_BASE_URL}?act=get_version&app={APP_NAME}&t={int(time.time())}" result = http_get(url) if result['status'] == 200: latest_version = result['text'].strip() return latest_version if latest_version else None except: pass return None # ================== 解析剩余时间 ================== def parse_time_str(resp): if not resp or "|" not in resp: return None parts = resp.split("|") if "bypass" in resp: return "验证已关闭" if len(parts) >= 2 and parts[1] == "permanent": return "永久有效" if len(parts) >= 4 and parts[3].isdigit(): total_mins = int(parts[3]) days = total_mins // 1440 hours = (total_mins % 1440) // 60 mins = total_mins % 60 if days > 0: return f"剩余 {days} 天 {hours} 小时" elif hours > 0: return f"剩余 {hours} 小时 {mins} 分钟" else: return f"剩余 {mins} 分钟" return None # ================== 错误码转换 ================== def trans_msg(code): messages = { "already_online": "该卡密已在线(多设备上限可能已满)", "online_limit_reached": "在线设备数已满", "activate": "激活成功", "valid": "验证通过", "permanent": "终身有效", "heartbeat": "心跳正常", "bypass": "验证已关闭", "unbind_ok": "解绑成功", "invalid_card": "卡密无效", "expired": "卡密已过期", "banned": "卡密已被禁用", "device_mismatch": "设备不匹配", "missing_params": "参数不完整" } return messages.get(code, code) # ================== 验证卡密 ================== def verify_card(card, mac): global g_last_xsign2 try: t = time.strftime("%Y%m%d%H%M%S") url = f"{API_BASE_URL}?card={card}&mac={mac}&app={APP_NAME}&heart=1&t={t}" result = http_get(url) if result['status'] == 200: g_last_xsign2 = result['xsign2'] return result['text'] else: return "error|网络错误" except Exception as e: return "error|网络错误" g_last_xsign2 = "" heartbeat_thread = None main_thread = None stop_heartbeat = False def get_last_xsign2(): return g_last_xsign2 # ================== 心跳线程 ================== def heartbeat_loop(card, mac, callback=None): global stop_heartbeat consecutive_fails = 0 while not stop_heartbeat: try: t = time.strftime("%Y%m%d%H%M%S") url = f"{API_BASE_URL}?card={card}&mac={mac}&app={APP_NAME}&heart=1&t={t}" result = http_get(url) if result['status'] == 200: biz = verify_response(result['text'], result['xsign2']) if biz and biz.startswith("ok|"): consecutive_fails = 0 else: consecutive_fails += 1 if consecutive_fails >= MAX_HEARTBEAT_FAILS: if callback: callback() break else: consecutive_fails += 1 if consecutive_fails >= MAX_HEARTBEAT_FAILS: if callback: callback() break except: consecutive_fails += 1 if consecutive_fails >= MAX_HEARTBEAT_FAILS: if callback: callback() break for _ in range(HEARTBEAT_INTERVAL): if stop_heartbeat: break time.sleep(1) # ================== 主程序线程 ================== def main_program_loop(): """用户主程序 - 请在这里写你的软件代码""" while not stop_heartbeat: # ========== ↓↓↓ 你的软件代码写在这里 ↓↓↓ ========== time.sleep(1) # ========== ↑↑↑ 你的软件代码写在这里 ↑↑↑ ========== # ================== 主程序界面 ================== class App: def __init__(self, root): self.root = root self.heartbeat_thread = None self.main_thread = None self.stop_threads = False self.switch_off = False self.cached_card = None self.remaining_time = None self.notice_text = "加载中..." root.title("软件验证") root.geometry("420x460") root.resizable(False, False) root.configure(bg="#ffffff") ws = root.winfo_screenwidth() hs = root.winfo_screenheight() root.geometry(f"420x460+{(ws-420)//2}+{(hs-460)//2}") root.attributes('-topmost', True) root.lift() root.focus_force() self._load_cached_data() self._show_loading() threading.Thread(target=self._init_all, daemon=True).start() def _load_cached_data(self): self.cached_card = read_saved_card() if self.cached_card: mac = get_machine_code() if mac != "获取失败": ret = verify_card(self.cached_card, mac) xsign2 = get_last_xsign2() biz = verify_response(ret, xsign2) if biz and biz.startswith("ok|"): self.remaining_time = parse_time_str(biz) def _show_loading(self): for w in self.root.winfo_children(): w.destroy() self.loading_label = tk.Label( self.root, text="正在加载,请稍候...", font=("微软雅黑", 14), fg="#999", bg="#ffffff" ) self.loading_label.pack(expand=True) self._loading_start = time.time() self._update_loading_timer() def _update_loading_timer(self): if not hasattr(self, 'loading_label') or not self.loading_label: return if not self.loading_label.winfo_exists(): return elapsed = int(time.time() - self._loading_start) self.loading_label.config(text=f"正在加载,请稍候... {elapsed} 秒") self.root.after(1000, self._update_loading_timer) def _init_all(self): self.notice_text = fetch_notice() if ENABLE_VERSION_CHECK: latest_version = fetch_latest_version() if latest_version and latest_version != APP_VERSION: self.root.after(0, lambda: force_update(latest_version)) return time.sleep(1) try: self.switch_off = not get_card_switch() except: self.switch_off = False self.root.after(0, self._show_main_ui) def _show_main_ui(self): self.loading_label = None for w in self.root.winfo_children(): w.destroy() tk.Label( self.root, text=f"🔐 软件验证", font=("微软雅黑", 18, "bold"), fg="#1967d2", bg="#ffffff" ).pack(pady=(25, 5)) notice_frame = tk.Frame(self.root, bg="#f0f7ff", bd=0, highlightthickness=0) notice_frame.pack(fill="x", padx=30, pady=(5, 5)) tk.Label( notice_frame, text="📢 公告", font=("微软雅黑", 10, "bold"), fg="#333", bg="#f0f7ff" ).pack(anchor="w", padx=10, pady=(8, 0)) self.notice_label = tk.Label( notice_frame, text=self.notice_text, font=("微软雅黑", 9), fg="#555", bg="#f0f7ff", wraplength=340, justify="left" ) self.notice_label.pack(anchor="w", padx=10, pady=(2, 8)) ver_text = f"📌 当前版本:{APP_VERSION}" tk.Label( self.root, text=ver_text, font=("微软雅黑", 9), fg="#888", bg="#ffffff" ).pack(pady=(0, 5)) self.time_label = tk.Label( self.root, text="", font=("微软雅黑", 10, "bold"), fg="#0d904f", bg="#ffffff" ) self.time_label.pack(pady=(0, 5)) if self.remaining_time: self.time_label.config(text=f"⏱️ {self.remaining_time}") ttk.Separator(self.root, orient="horizontal").pack(fill="x", padx=30, pady=5) tk.Label( self.root, text="请输入卡密", font=("微软雅黑", 12), fg="#333", bg="#ffffff" ).pack(pady=(15, 5)) self.card_entry = tk.Entry( self.root, width=28, font=("微软雅黑", 13), justify="center", bd=1, relief="solid" ) self.card_entry.pack(pady=(0, 5)) self.card_entry.focus() self.remember_var = tk.BooleanVar(value=False) if self.cached_card: self.card_entry.insert(0, self.cached_card) self.remember_var.set(True) self.card_entry.select_range(0, tk.END) if ENABLE_CARD_CACHE: tk.Checkbutton( self.root, text="记住卡密,下次自动填写", variable=self.remember_var, font=("微软雅黑", 9), fg="#666", bg="#ffffff", activebackground="#ffffff" ).pack(pady=(2, 10)) btn_text = "直接使用" if self.switch_off else "验证卡密" btn_cmd = self._run_original if self.switch_off else self._do_verify self.verify_btn = tk.Button( self.root, text=btn_text, command=btn_cmd, bg="#1967d2", fg="white", font=("微软雅黑", 12, "bold"), width=18, height=2, bd=0, cursor="hand2" ) self.verify_btn.pack(pady=(5, 5)) if self.switch_off: tip, fg = "卡密验证已关闭,可直接使用", "green" elif self.remaining_time: tip, fg = f"点击验证后直接启动", "#0d904f" else: tip, fg = "请输入卡密后点击验证", "#999" self.status_label = tk.Label( self.root, text=tip, font=("微软雅黑", 9), fg=fg, bg="#ffffff" ) self.status_label.pack(pady=(5, 0)) self.root.bind("<Return>", lambda e: self._do_verify() if not self.switch_off else self._run_original()) def _do_verify(self): card = self.card_entry.get().strip() if not card: self.status_label.config(text="请输入卡密", fg="red") self.card_entry.focus() return if self.remaining_time and card == self.cached_card: if self.remember_var.get(): save_card(card) self._run_original(skip_popup=True) return self.verify_btn.config(state="disabled", text="验证中...") self.status_label.config(text="正在验证,请稍候...", fg="blue") self.root.update() def do(): mac = get_machine_code() if mac == "获取失败": self.root.after(0, lambda: self._on_verify_result(card, None, "获取机器码失败")) return ret = verify_card(card, mac) xsign2 = get_last_xsign2() biz = verify_response(ret, xsign2) if not biz: self.root.after(0, lambda: self._on_verify_result(card, None, "签名校验失败")) elif biz.startswith("ok|"): time_str = parse_time_str(biz) self.root.after(0, lambda: self._on_verify_result(card, time_str, None)) elif biz.startswith("error|"): parts = biz.split("|") err_msg = trans_msg(parts[1]) if len(parts) >= 2 else "验证失败" self.root.after(0, lambda: self._on_verify_result(card, None, err_msg)) else: self.root.after(0, lambda: self._on_verify_result(card, None, "验证失败")) threading.Thread(target=do, daemon=True).start() def _on_verify_result(self, card, time_str, error_msg): self.verify_btn.config(state="normal", text="验证卡密") if error_msg: self.status_label.config(text=error_msg, fg="red") self.card_entry.focus() return if self.remember_var.get(): save_card(card) else: clear_cache() self.cached_card = card self.remaining_time = time_str top = tk.Tk() top.withdraw() top.attributes('-topmost', True) top.lift() top.focus_force() if time_str: messagebox.showinfo("验证成功", time_str, parent=top) else: messagebox.showinfo("验证成功", "卡密验证通过", parent=top) top.destroy() self._run_original(skip_popup=True) def _run_original(self, skip_popup=False): if not skip_popup and not self.switch_off: top = tk.Tk() top.withdraw() top.attributes('-topmost', True) top.lift() top.focus_force() if self.remaining_time: messagebox.showinfo("验证成功", self.remaining_time, parent=top) else: messagebox.showinfo("验证成功", "卡密验证通过", parent=top) top.destroy() self.root.withdraw() self.stop_threads = False def heartbeat_callback(): self.root.after(0, self._on_heartbeat_fail) card = self.cached_card or "bypass" mac = get_machine_code() if ENABLE_HEARTBEAT: self.heartbeat_thread = threading.Thread( target=heartbeat_loop, args=(card, mac, heartbeat_callback), daemon=True ) self.heartbeat_thread.start() self.main_thread = threading.Thread(target=main_program_loop, daemon=True) self.main_thread.start() self._show_main_interface() def _on_heartbeat_fail(self): self.stop_threads = True messagebox.showerror( "卡密已失效", f"连续 {MAX_HEARTBEAT_FAILS} 次心跳验证失败\n\n" f"可能原因:\n" f"• 卡密已过期\n" f"• 卡密被禁用\n" f"• 设备不匹配\n" f"• 网络异常\n\n" f"程序即将关闭" ) self.root.after(500, self._on_closing) def _show_main_interface(self): for widget in self.root.winfo_children(): widget.destroy() self.root.title(f"我的软件 - 运行中") self.root.geometry("400x300") self.root.configure(bg="#ffffff") ws = self.root.winfo_screenwidth() hs = self.root.winfo_screenheight() self.root.geometry(f"400x300+{(ws-400)//2}+{(hs-300)//2}") self.root.deiconify() tk.Label( self.root, text=f"✅ 我的软件运行中", font=("微软雅黑", 18, "bold"), fg="#1967d2", bg="#ffffff" ).pack(expand=True) if self.switch_off: status_text = f"⚠️ 验证已关闭\n程序正常运行中" status_color = "#ff9800" else: status_text = f"✅ 卡密验证成功\n心跳检测正常运行中" status_color = "#4caf50" self.status_info = tk.Label( self.root, text=status_text, font=("微软雅黑", 10), fg=status_color, bg="#ffffff", justify=tk.CENTER ) self.status_info.pack(pady=10) tk.Button( self.root, text="退出程序", command=self._on_closing, bg="#dc3545", fg="white", font=("微软雅黑", 12), width=12, bd=0, cursor="hand2" ).pack(pady=20) if not self.switch_off and ENABLE_HEARTBEAT: self._update_heartbeat_status() def _update_heartbeat_status(self): if not self.stop_threads and self.heartbeat_thread and self.heartbeat_thread.is_alive(): current_time = time.strftime("%H:%M:%S") self.status_info.config( text=f"✅ 卡密验证成功\n心跳检测正常运行中\n最后心跳时间: {current_time}" ) self.root.after(5000, self._update_heartbeat_status) elif not self.stop_threads: self.status_info.config(text=f"⚠️ 心跳线程异常,请重新验证") def _on_closing(self): global stop_heartbeat stop_heartbeat = True self.stop_threads = True self.root.destroy() os._exit(0) def main(): root = tk.Tk() app = App(root) root.mainloop() if __name__ == "__main__": main()

三、核心安全机制说明

1. MD5签名校验(防篡改)

服务器返回数据时带签名,客户端验证签名一致性:

python

local_sign = md5_encrypt(body + SIGN_KEY) if local_sign != sign: return "" # 签名不匹配,拒绝

2. 时间戳防重放

验证时间戳与本地时间差不超过120秒:

python

diff = abs(now_ts - ts) if diff > TIMESTAMP_MAX_DIFF: return "" # 时间戳过期

3. 心跳保活

验证成功后,后台线程每55秒验证一次卡密状态:

python

while not stop_heartbeat: biz = verify_response(result['text'], result['xsign2']) if biz and biz.startswith("ok|"): consecutive_fails = 0 else: consecutive_fails += 1 if consecutive_fails >= 5: callback() # 连续失败5次,退出程序

四、配置说明

配置项说明示例
SIGN_KEY签名密钥(平台后台获取)keyt@2026
API_BASE_URL验证接口地址https://www.keyt.cn/kami/用户名/check.php
APP_NAME应用名称a
APP_VERSION软件版本号1.0.0
HEARTBEAT_INTERVAL心跳间隔(秒)55

五、打包发布

bash

# 安装依赖 pip install pycryptodome psutil # 打包成单文件EXE pyinstaller --onefile --noconsole ceshi.py

六、适用场景

  • ✅ 商业软件授权管理

  • ✅ 会员制软件/工具

  • ✅ 企业内部工具权限管控

  • ✅ 任何需要防破解、控时长、管设备的场景

七、总结

本文实现了一个完整的Python软件授权验证系统,核心功能包括:

  • ✅ 卡密验证(MD5签名+时间戳防重放)

  • ✅ 心跳保活(连续失败自动退出)

  • ✅ 版本检查(强制更新)

  • ✅ 公告推送

  • ✅ 卡密缓存

这套方案我已经在多个项目中实际使用,稳定运行超过一年。整个系统无需自建服务器,免费、稳定、安全,非常适合独立开发者使用。

如果你也想给自己的软件加上授权验证,可以试试这个方案。整套系统的后台管理、卡密生成、多应用支持等功能都是现成的,直接注册就能用。

本文方案基于卡密通平台实现,如需完整文档和更多语言对接示例(易语言、按键精灵、C#、Java等),可以搜索「卡密通」或访问官方网站了解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 3:18:01

如何在10分钟内为Honey Select 2安装高效完整的汉化与优化补丁

如何在10分钟内为Honey Select 2安装高效完整的汉化与优化补丁 【免费下载链接】HS2-HF_Patch Automatically translate, uncensor and update HoneySelect2! 项目地址: https://gitcode.com/gh_mirrors/hs/HS2-HF_Patch HS2-HF_Patch是一个专为Honey Select 2游戏设计的…

作者头像 李华
网站建设 2026/6/17 3:11:49

2026年,这些目前知名的水涡流测功机供应商,你都了解吗?

在电机测试领域&#xff0c;水涡流测功机是非常重要的设备&#xff0c;它在多个行业都有着广泛的应用。随着科技的发展和市场需求的变化&#xff0c;2026年的市场格局也在不断发展。下面为大家介绍目前知名的几家水涡流测功机供应商。杭州索川科技有限公司杭州索川科技有限公司…

作者头像 李华
网站建设 2026/6/17 3:03:21

讯维全域管控平台|一体化音视频架构,适配政企全场景数字化管控需求

数字化转型这些年&#xff0c;指挥调度、政务会议、智慧教学、文旅展示这些场景的音视频系统越建越多。但我跑过不少项目后发现&#xff0c;传统搭建模式有个老毛病&#xff1a;设备堆砌、系统割裂。矩阵、拼接处理器、KVM坐席、音频处理器、录播主机、中控——六台独立设备塞满…

作者头像 李华
网站建设 2026/6/17 2:57:00

【信息科学与工程学】【物理/化学和工程技术】第八十六篇 飞行力学01

字段 内容 编号​ 001 类型​ 纵向配平计算(稳态直线飞行) 领域​ 飞行力学 问题​ 某型飞机在海拔5000 m、真空速200 m/s下作定常直线水平飞行。已知飞机重量W = 60000 N,机翼面积S = 30 m,平均气动弦长c̄ = 3.5 m,重心位于机翼前缘之后0.25c̄处。机翼‑机身组…

作者头像 李华
网站建设 2026/6/17 2:49:41

2026永康入户门源头厂家直购攻略

一、领域核心挑战&#xff1a;用户端的真实困境永康作为“中国门都”&#xff0c;年产各类门产品逾亿樘&#xff0c;但产业繁荣背后&#xff0c;入户门领域长期存在几类被行业反复讨论的共识性难题。第一是材料与结构的不透明。 行业内普遍使用回收杂铝、薄钢皮、纸蜂窝填充等低…

作者头像 李华