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

📅 2026/6/17 3:18:14
Python软件授权验证完整方案(免费卡密系统搭建)
本文将带你完整实现一个带卡密验证的Python软件授权系统包含卡密验证、MD5签名防篡改、时间戳防重放、心跳保活、强制版本更新、公告推送等功能。无需自建服务器对接即用。一、为什么需要软件授权验证作为独立开发者你辛辛苦苦写出来的软件最怕的就是被人随意复制、破解、传播。一套完善的授权验证系统可以帮你控制软件使用期限天卡/周卡/月卡/年卡/终身绑定设备防止账号共享远程控制软件状态禁用/启用获取软件使用统计推送更新公告市面上有不少付费的授权平台但如果你只是想快速实现功能、又不想花冤枉钱卡密通这个平台可以免费使用我已经用了很久稳定可靠。二、完整代码python# # ceshi.py — 带卡密验证的测试软件 # 打包命令pyinstaller --onefile --noconsole ceshi.py # # ---------- 卡密系统配置 ---------- SIGN_KEY keyt2026 # 签名密钥后台获取 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:i2] 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_outputTrue, textTrue, encodinggbk) 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_outputTrue, textTrue, encodinggbk) 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, headersheaders) response urllib.request.urlopen(req, timeout10, contextssl_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}?actget_switchapp{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}?actget_noticeapp{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}?actget_versionapp{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}heart1t{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, callbackNone): 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}heart1t{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(f420x460{(ws-420)//2}{(hs-460)//2}) root.attributes(-topmost, True) root.lift() root.focus_force() self._load_cached_data() self._show_loading() threading.Thread(targetself._init_all, daemonTrue).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(expandTrue) 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(textf正在加载请稍候... {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, textf 软件验证, font(微软雅黑, 18, bold), fg#1967d2, bg#ffffff ).pack(pady(25, 5)) notice_frame tk.Frame(self.root, bg#f0f7ff, bd0, highlightthickness0) notice_frame.pack(fillx, padx30, pady(5, 5)) tk.Label( notice_frame, text 公告, font(微软雅黑, 10, bold), fg#333, bg#f0f7ff ).pack(anchorw, padx10, pady(8, 0)) self.notice_label tk.Label( notice_frame, textself.notice_text, font(微软雅黑, 9), fg#555, bg#f0f7ff, wraplength340, justifyleft ) self.notice_label.pack(anchorw, padx10, pady(2, 8)) ver_text f 当前版本{APP_VERSION} tk.Label( self.root, textver_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(textf⏱️ {self.remaining_time}) ttk.Separator(self.root, orienthorizontal).pack(fillx, padx30, pady5) tk.Label( self.root, text请输入卡密, font(微软雅黑, 12), fg#333, bg#ffffff ).pack(pady(15, 5)) self.card_entry tk.Entry( self.root, width28, font(微软雅黑, 13), justifycenter, bd1, reliefsolid ) self.card_entry.pack(pady(0, 5)) self.card_entry.focus() self.remember_var tk.BooleanVar(valueFalse) 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记住卡密下次自动填写, variableself.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, textbtn_text, commandbtn_cmd, bg#1967d2, fgwhite, font(微软雅黑, 12, bold), width18, height2, bd0, cursorhand2 ) 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, texttip, font(微软雅黑, 9), fgfg, 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请输入卡密, fgred) 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_popupTrue) return self.verify_btn.config(statedisabled, text验证中...) self.status_label.config(text正在验证请稍候..., fgblue) 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(targetdo, daemonTrue).start() def _on_verify_result(self, card, time_str, error_msg): self.verify_btn.config(statenormal, text验证卡密) if error_msg: self.status_label.config(texterror_msg, fgred) 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, parenttop) else: messagebox.showinfo(验证成功, 卡密验证通过, parenttop) top.destroy() self._run_original(skip_popupTrue) def _run_original(self, skip_popupFalse): 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, parenttop) else: messagebox.showinfo(验证成功, 卡密验证通过, parenttop) 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( targetheartbeat_loop, args(card, mac, heartbeat_callback), daemonTrue ) self.heartbeat_thread.start() self.main_thread threading.Thread(targetmain_program_loop, daemonTrue) 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(f400x300{(ws-400)//2}{(hs-300)//2}) self.root.deiconify() tk.Label( self.root, textf✅ 我的软件运行中, font(微软雅黑, 18, bold), fg#1967d2, bg#ffffff ).pack(expandTrue) 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, textstatus_text, font(微软雅黑, 10), fgstatus_color, bg#ffffff, justifytk.CENTER ) self.status_info.pack(pady10) tk.Button( self.root, text退出程序, commandself._on_closing, bg#dc3545, fgwhite, font(微软雅黑, 12), width12, bd0, cursorhand2 ).pack(pady20) 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( textf✅ 卡密验证成功\n心跳检测正常运行中\n最后心跳时间: {current_time} ) self.root.after(5000, self._update_heartbeat_status) elif not self.stop_threads: self.status_info.config(textf⚠️ 心跳线程异常请重新验证) 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签名校验防篡改服务器返回数据时带签名客户端验证签名一致性pythonlocal_sign md5_encrypt(body SIGN_KEY) if local_sign ! sign: return # 签名不匹配拒绝2. 时间戳防重放验证时间戳与本地时间差不超过120秒pythondiff abs(now_ts - ts) if diff TIMESTAMP_MAX_DIFF: return # 时间戳过期3. 心跳保活验证成功后后台线程每55秒验证一次卡密状态pythonwhile 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签名密钥平台后台获取keyt2026API_BASE_URL验证接口地址https://www.keyt.cn/kami/用户名/check.phpAPP_NAME应用名称aAPP_VERSION软件版本号1.0.0HEARTBEAT_INTERVAL心跳间隔秒55五、打包发布bash# 安装依赖 pip install pycryptodome psutil # 打包成单文件EXE pyinstaller --onefile --noconsole ceshi.py六、适用场景✅ 商业软件授权管理✅ 会员制软件/工具✅ 企业内部工具权限管控✅ 任何需要防破解、控时长、管设备的场景七、总结本文实现了一个完整的Python软件授权验证系统核心功能包括✅ 卡密验证MD5签名时间戳防重放✅ 心跳保活连续失败自动退出✅ 版本检查强制更新✅ 公告推送✅ 卡密缓存这套方案我已经在多个项目中实际使用稳定运行超过一年。整个系统无需自建服务器免费、稳定、安全非常适合独立开发者使用。如果你也想给自己的软件加上授权验证可以试试这个方案。整套系统的后台管理、卡密生成、多应用支持等功能都是现成的直接注册就能用。本文方案基于卡密通平台实现如需完整文档和更多语言对接示例易语言、按键精灵、C#、Java等可以搜索「卡密通」或访问官方网站了解。