为你的python程序上锁:软件序列号生成器

devtools/2025/3/14 7:24:33/

序列号

很多同学可能开发了非常多的程序了,并且进行了 exe 的打包,可是由于没有使用序列号,程序被无限复制,导致收益下降。

接下来我们来自己实现序列号的生成及使用,通过本文的学习,希望能够帮助到你!

本文适合 windows 系统,linux 系统原理相通,但代码有所不同。

安装库

pip install wmi
pip install pycryptodome

结构流程图

在这里插入图片描述

我们首先要通过 硬件信息、UUID和时间戳 来生成一个 协议文件,再通过非对称加密 RSA 生成公钥和私钥。

1. 当我们给客户程序的时候,会附带一个 私钥

2. 程序启动时会判断是否存在 协议文件 ,如果没有 协议文件 将会生成一个 序列号,客户需要把序列号发送给管理员

3. 管理员获取 序列号,使用 公钥 进行加密生成 协议文件,将其发送给客户

4. 客户将 协议文件 放在相应位置,程序使用 私钥 进行解密,与相应的 序列号 进行对比

5. 协议文件 解密成功,通过 协议文件序列号 中的时间戳信息判断是否过期

6. 当时间戳未过期,则运行程序,反之无法启动,提示 序列号过期

结构代码

1. 生成RSA公钥与私钥文件

python">from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import os
import datetime
import base64CURRENT_FOLDER_PATH = os.path.dirname(os.path.abspath(__file__))def make_rsa_key(length=1024):"""生成公钥和私钥:return:"""# 伪随机数生成器random_gen = Random.new().read# 生成秘钥对实例对象:1024是秘钥的长度rsa = RSA.generate(length, random_gen)private_pem = rsa.exportKey()public_pem = rsa.publickey().exportKey()return private_pem, public_pemdef rsa_encrypt(pub_key, content, length=128):"""rsa数据加密,单次加密串的长度最大为 (key_size/8)-111024bit的证书用100, 2048bit的证书用 200:param pub_key::param content::param length::return:"""pub_key = RSA.importKey(pub_key.decode())cipher = PKCS1_v1_5.new(pub_key)content = content.encode()res = []for i in range(0, len(content), length):res.append(cipher.encrypt(content[i: i + length]))return base64.b64encode(base64.b64encode(b''.join(res)))def rsa_decrypt(pri_key, encrypt_txt, length=128):"""rsa信息解密1024bit的证书用128,2048bit证书用256位:param pri_key::param encrypt_txt::param length::return:"""try:encrypt_txt = base64.b64decode(encrypt_txt)encrypt_txt = base64.b64decode(encrypt_txt.decode())pri_obj = RSA.importKey(pri_key)pri_obj = PKCS1_v1_5.new(pri_obj)res = []for i in range(0, len(encrypt_txt), length):res.append(pri_obj.decrypt(encrypt_txt[i:i + length], ''))return b''.join(res)except Exception as e:return Nonedef rsa_file_generator(pri_path, pub_path, length=1024):"""生成公私钥文件:param pri_path: 私钥文件地址:param pub_path: 公钥文件地址:return:"""pri_key, pub_key = make_rsa_key(length)with open(pri_path, 'wb') as f:f.write(pri_key)with open(pub_path, 'wb') as f:f.write(pub_key)return pri_key, pub_keydef get_or_make_rsa(refresh=False):"""生成或获取公私钥内容:return:"""folder_path = os.path.join(CURRENT_FOLDER_PATH, 'pem')if not os.path.exists(folder_path):os.makedirs(folder_path)file_list = os.listdir(folder_path)now = datetime.datetime.now()now_str = now.strftime('%Y%m%d%H%M%S')new_pri_path = os.path.join(folder_path, f'{now_str}_private.pem')new_pub_path = os.path.join(folder_path, f'{now_str}_public.pem')# 公钥和私钥文件不存在if len(file_list) == 0:pri_key, pub_key = rsa_file_generator(new_pri_path,new_pub_path,1024)else:pri_path = ''pub_path = ''for file in file_list:if file.endswith('private.pem'):pri_path = os.path.join(folder_path, file)elif file.endswith('public.pem'):pub_path = os.path.join(folder_path, file)if not pri_path or not pub_path:pri_key, pub_key = rsa_file_generator(new_pri_path,new_pub_path,1024)else:# 手动更新私钥if refresh:pri_key, pub_key = rsa_file_generator(new_pri_path,new_pub_path,1024)os.remove(pri_path)os.remove(pub_path)else:with open(pri_path, 'rb') as f:pri_key = f.read()with open(pub_path, 'rb') as f:pub_key = f.read()return pri_key, pub_key

使用 get_or_make_rsa() 方法即可获取公钥与私钥,程序会生成一个 pem 文件夹保存相应的公钥和私钥。

如果需要更新公钥与私钥,只要调用 get_or_make_rsa() 方法时,传入 refresh 的值为 True 即可。

CURRENT_FOLDER_PATH 全局变量保存的是当前程序的绝对路径,就算是打包后的 exe 也可以正确获取。

2. 序列号生成

python">import uuid
import wmi
import hashlib
from itertools import zip_longestdef get_license_txt():c = wmi.WMI()# 获取第一个硬盘的序列号disk_number = ''for physical_disk in c.Win32_DiskDrive():disk_number = physical_disk.SerialNumber.strip()break# 获取第一个CPU的序列号cpu_number = ''for cpu in c.Win32_Processor():cpu_number = cpu.ProcessorId.strip()break# 获取第一个BIOS的序列号bios_number = ''for bios in c.Win32_BIOS():bios_number = bios.SerialNumber.strip()breakuid = get_a_guid()paired = zip_longest(disk_number, cpu_number, bios_number, uid, fillvalue='_')result = '|'.join([''.join(pair) for pair in paired])content = hashlib.md5(result.encode()).hexdigest().upper()return f'{content}=={uid}'

我们使用了一个相对复杂的方式将 硬盘、cpu、BIOS 的序列号与一个 UUID 进行组合,竟将其进行 md5 加密,最后将这个 加密结果UUID 明文组合在一起作为一个 完整的软件序列号

现在,客户将在程序启动时拿到这个 软件序列号 ,再将这个软件序列号发送给管理员进行加密即可。

加密方法将使用之前的 rsa_encrypt() 方法。

3. 加密软件序列号

python">def make_license_key_file(license_txt='', days=365):'''创建协议文件:param license_txt: 软件序列号:param days: 有效天数:return: '''pri_key, pub_key = get_or_make_rsa()s_list = license_txt.split('==')if len(s_list) != 2:return Noneuid = s_list[1]timestamp = int(datetime.datetime.now().timestamp()) + days * 86400new_license_text = f'{license_txt}=={timestamp}'res = rsa_encrypt(pub_key, new_license_text).decode()folder_path = os.path.join(CURRENT_FOLDER_PATH, 'key')if not os.path.exists(folder_path):os.makedirs(folder_path)file_path = os.path.join(folder_path, f'{uid}.key')with open(file_path, 'wb') as f:f.write(res.encode())return file_path

以当前时间戳为基准添加有效天数,然后将客户发送过来的序列号再进行一次组合,最后通过 公钥 进行加密,生成一个 协议文件 发送给客户。

4. 验证协议文件

python">def auth_license(key_file_path='', pem_file_path=''):c = wmi.WMI()disk_number = ''for physical_disk in c.Win32_DiskDrive():disk_number = physical_disk.SerialNumber.strip()breakcpu_number = ''for cpu in c.Win32_Processor():cpu_number = cpu.ProcessorId.strip()breakbios_number = ''for bios in c.Win32_BIOS():bios_number = bios.SerialNumber.strip()break# 协议文件不存在,无法通过if not os.path.exists(key_file_path):return False# 私钥文件不存在,无法通过if not os.path.exists(pem_file_path):return False# 读取协议文件内容with open(key_file_path, 'rb') as f:key_res = f.read().decode()# 读取私钥内容with open(pem_file_path, 'rb') as f:pem_res = f.read().decode()res = rsa_decrypt(pem_res, key_res).decode()# 解密失败,无法通过if not res:return Falses_list = res.split('==')# 没有两个等号的内容,无法通过if len(s_list) != 3:return Falseuid = s_list[1]paired = zip_longest(disk_number, cpu_number, bios_number, uid, fillvalue='_')result = '|'.join([''.join(pair) for pair in paired])content = hashlib.md5(result.encode()).hexdigest().upper()# 序列号不一致,无法通过if content != s_list[0]:return Falsetry:timestamp = int(s_list[2])except Exception as e:# 无法变为时间戳,无法通过return Falsenow_timestamp = int(datetime.datetime.now().timestamp())# 在有效时间内,通过if now_timestamp <= timestamp:return Truereturn False

这个验证过程其实就是再次进行一次 序列号 组合,来判断是否与 协议文件 一致,其后还需判断是否在有效期内。

结尾

我相信如果认真看完文章的朋友已经可以实现自己的序列号生成器了,不过在此之前我需要申明这个文章的代码还不是完整版,这里提几个优化点:

  1. 定时验证:定时判断是否超过有效期,防止客户程序未关闭,就算超过有效期还能继续使用

  2. 打包为加密模块:当前代码为明文代码,由于 python 为解释器语言,如果不加密打包,很容易被破解规则

  3. 添加可视化窗口:为了方便使用,可以将程序设计为可视化窗口模式,最简单的方式使用 TK 创建

当然,如果你不想自己写,推荐可以直接使用 pyarmor ,这也是国人开发的一个加密库,包含加密、有效期、许可证。

如果这篇文章对你有帮助,点个赞让我知道哦!

干货分享

  • 软考系统架构设计师三科备考经验附学习资料
  • 信息安全管理体系(ISMS)制度模板分享
  • 免费文档翻译工具(支持word、pdf、ppt、excel)
  • PuTTY中文版安装包
  • MobaXterm中文版安装包
  • pinginfoview网络诊断工具中文版

http://www.ppmy.cn/devtools/166690.html

相关文章

【Hadoop】详解HDFS

Hadoop 分布式文件系统(HDFS)被设计成适合运行在通用硬件上的分布式文件系统&#xff0c;它是一个高度容错性的系统&#xff0c;适合部署在廉价的机器上&#xff0c;能够提供高吞吐量的数据访问&#xff0c;非常适合大规模数据集上的应用。为了做到可靠性&#xff0c;HDFS创建了…

Pandas数据清洗实战之清洗猫眼电影

本次案例所需要用到的模块 pandas(文件读取保存 操作表格的模块) 将上次Scrapy爬取下来的文件 做个数据清洗 变成我们想要的数据 确定目的&#xff1a;将此文件中的duration字段中的分钟 和publisher_time上映去掉 只保留纯数值 数据清洗题目如下: 修复 publish_time列中的错…

刚刚!微调 DeepSeek 满血版正式开源。。。

近期&#xff0c;由中国科学院自动化研究所与中科闻歌联合推出的 DeepSeek-V3/R1 671B 全参数微调开源方案正式发布&#xff01;该项目完整公开了从模型训练到推理的全流程代码与脚本&#xff0c;并附带了实际训练中的经验总结与优化建议&#xff0c;为大模型开发者提供了一套可…

MyBatis的级联查询(一对一、一对多、多对多)

MyBatis的级联查询 级联的优点是获取关联数据十分便捷。但是级联过多会增加系统的复杂度&#xff0c;同时降低系统的性能&#xff0c;此增彼减。所以记录超过 3 层时&#xff0c;就不要考虑使用级联了&#xff0c;因为这样会造成多个对象的关联&#xff0c;导致系统的耦合、负…

给AI编程泼一盆冷水

AI确实扩大了普通人的能力边界&#xff0c;但是如果你连自己想要什么都描述不清楚&#xff0c;更不知道AI干了什么&#xff0c;你最好停下来认真的学习一下。 AI并没有消除认知差距&#xff0c;而是让人与人的认知差距急剧拉大了。 一、效率提升与隐性成本的博弈 AI编程工具如…

什么是 kafka

Kafka 是一个由 Apache 软件基金会开发的开源流处理平台&#xff0c;具有高吞吐量、低延迟和可扩展性等特点。 Kafka 的基本原理 ● 生产者-消费者模型: 生产者将消息发布到主题&#xff0c;消费者订阅主题并消费消息。生产者通过 push 操作将数据发送到 broker&#xff0c;…

在资源有限中逆势突围:从抗战智谋到寒门高考的破局智慧

目录 引言 一、历史中的非对称作战&#xff1a;从李牧到八路军的智谋传承 李牧戍边&#xff1a;古代军事博弈中的资源重构 八路军的游击战&#xff1a;现代战争中的智慧延续 二、创业界的逆袭之道&#xff1a;小米与拼多多的资源重构 从MVP到杠杆解 社交裂变与资源错配 …

Python基于Django的医用耗材网上申领系统【附源码、文档说明】

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…