win10密鑰生成器(為你的python程序上鎖軟件序列號(hào)生成器)
很多同學(xué)可能開發(fā)了非常多的程序了,并且進(jìn)行了 exe 的打包,可是由于沒(méi)有使用序列號(hào),程序被無(wú)限復(fù)制,導(dǎo)致收益下降。
接下來(lái)我們來(lái)自己實(shí)現(xiàn)序列號(hào)的生成及使用,通過(guò)本文的學(xué)習(xí),希望能夠幫助到你!
本文適合 windows 系統(tǒng),linux 系統(tǒng)原理相通,但代碼有所不同。
安裝庫(kù)pip install wmipip install pycryptodome結(jié)構(gòu)流程圖
結(jié)構(gòu)圖
我們首先要通過(guò) 硬件信息、UUID和時(shí)間戳 來(lái)生成一個(gè) 協(xié)議文件,再通過(guò)非對(duì)稱加密 RSA 生成公鑰和私鑰。
當(dāng)我們給客戶程序的時(shí)候,會(huì)附帶一個(gè) 私鑰程序啟動(dòng)時(shí)會(huì)判斷是否存在 協(xié)議文件 ,如果沒(méi)有 協(xié)議文件 將會(huì)生成一個(gè) 序列號(hào),客戶需要把序列號(hào)發(fā)送給管理員管理員獲取 序列號(hào),使用 公鑰 進(jìn)行加密生成 協(xié)議文件,將其發(fā)送給客戶客戶將 協(xié)議文件 放在相應(yīng)位置,程序使用 私鑰 進(jìn)行解密,與相應(yīng)的 序列號(hào) 進(jìn)行對(duì)比協(xié)議文件 解密成功,通過(guò) 協(xié)議文件序列號(hào) 中的時(shí)間戳信息判斷是否過(guò)期當(dāng)時(shí)間戳未過(guò)期,則運(yùn)行程序,反之無(wú)法啟動(dòng),提示 序列號(hào)過(guò)期結(jié)構(gòu)代碼1. 生成RSA公鑰與私鑰文件from Crypto import Randomfrom Crypto.PublicKey import RSAfrom Crypto.Cipher import PKCS1_v1_5import osimport datetimeimport base64CURRENT_FOLDER_PATH = os.path.dirname(os.path.abspath(__file__))def make_rsa_key(length=1024): """ 生成公鑰和私鑰 :return: """ # 偽隨機(jī)數(shù)生成器 random_gen = Random.new().read # 生成秘鑰對(duì)實(shí)例對(duì)象:1024是秘鑰的長(zhǎng)度 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數(shù)據(jù)加密,單次加密串的長(zhǎng)度最大為 (key_size/8)-11 1024bit的證書用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): """ 生成或獲取公私鑰內(nèi)容 :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: # 手動(dòng)更新私鑰 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() 方法即可獲取公鑰與私鑰,程序會(huì)生成一個(gè) pem 文件夾保存相應(yīng)的公鑰和私鑰。
如果需要更新公鑰與私鑰,只要調(diào)用 get_or_make_rsa() 方法時(shí),傳入 refresh 的值為 True 即可。
CURRENT_FOLDER_PATH 全局變量保存的是當(dāng)前程序的絕對(duì)路徑,就算是打包后的 exe 也可以正確獲取。
2. 序列號(hào)生成import uuidimport wmiimport hashlibfrom itertools import zip_longestdef get_license_txt(): c = wmi.WMI() # 獲取第一個(gè)硬盤的序列號(hào) disk_number = '' for physical_disk in c.Win32_DiskDrive(): disk_number = physical_disk.SerialNumber.strip() break # 獲取第一個(gè)CPU的序列號(hào) cpu_number = '' for cpu in c.Win32_Processor(): cpu_number = cpu.ProcessorId.strip() break # 獲取第一個(gè)BIOS的序列號(hào) bios_number = '' for bios in c.Win32_BIOS(): bios_number = bios.SerialNumber.strip() break uid = 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}'我們使用了一個(gè)相對(duì)復(fù)雜的方式將 硬盤、cpu、BIOS 的序列號(hào)與一個(gè) UUID 進(jìn)行組合,竟將其進(jìn)行 md5 加密,最后將這個(gè) 加密結(jié)果 與 UUID 明文組合在一起作為一個(gè) 完整的軟件序列號(hào)。
現(xiàn)在,客戶將在程序啟動(dòng)時(shí)拿到這個(gè) 軟件序列號(hào) ,再將這個(gè)軟件序列號(hào)發(fā)送給管理員進(jìn)行加密即可。
加密方法將使用之前的 rsa_encrypt() 方法。
3. 加密軟件序列號(hào)def make_license_key_file(license_txt='', days=365): ''' 創(chuàng)建協(xié)議文件 :param license_txt: 軟件序列號(hào) :param days: 有效天數(shù) :return: ''' pri_key, pub_key = get_or_make_rsa() s_list = license_txt.split('==') if len(s_list) != 2: return None uid = s_list[1] timestamp = int(datetime.datetime.now().timestamp()) + days * 86400 new_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以當(dāng)前時(shí)間戳為基準(zhǔn)添加有效天數(shù),然后將客戶發(fā)送過(guò)來(lái)的序列號(hào)再進(jìn)行一次組合,最后通過(guò) 公鑰 進(jìn)行加密,生成一個(gè) 協(xié)議文件 發(fā)送給客戶。
4. 驗(yàn)證協(xié)議文件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() break cpu_number = '' for cpu in c.Win32_Processor(): cpu_number = cpu.ProcessorId.strip() break bios_number = '' for bios in c.Win32_BIOS(): bios_number = bios.SerialNumber.strip() break # 協(xié)議文件不存在,無(wú)法通過(guò) if not os.path.exists(key_file_path): return False # 私鑰文件不存在,無(wú)法通過(guò) if not os.path.exists(pem_file_path): return False # 讀取協(xié)議文件內(nèi)容 with open(key_file_path, 'rb') as f: key_res = f.read().decode() # 讀取私鑰內(nèi)容 with open(pem_file_path, 'rb') as f: pem_res = f.read().decode() res = rsa_decrypt(pem_res, key_res).decode() # 解密失敗,無(wú)法通過(guò) if not res: return False s_list = res.split('==') # 沒(méi)有兩個(gè)等號(hào)的內(nèi)容,無(wú)法通過(guò) if len(s_list) != 3: return False uid = 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() # 序列號(hào)不一致,無(wú)法通過(guò) if content != s_list[0]: return False try: timestamp = int(s_list[2]) except Exception as e: # 無(wú)法變?yōu)闀r(shí)間戳,無(wú)法通過(guò) return False now_timestamp = int(datetime.datetime.now().timestamp()) # 在有效時(shí)間內(nèi),通過(guò) if now_timestamp <= timestamp: return True return False這個(gè)驗(yàn)證過(guò)程其實(shí)就是再次進(jìn)行一次 序列號(hào) 組合,來(lái)判斷是否與 協(xié)議文件 一致,其后還需判斷是否在有效期內(nèi)。
結(jié)尾我相信如果認(rèn)真看完文章的朋友已經(jīng)可以實(shí)現(xiàn)自己的序列號(hào)生成器了,不過(guò)在此之前我需要申明這個(gè)文章的代碼還不是完整版,這里提幾個(gè)優(yōu)化點(diǎn):
定時(shí)驗(yàn)證:定時(shí)判斷是否超過(guò)有效期,防止客戶程序未關(guān)閉,就算超過(guò)有效期還能繼續(xù)使用 打包為加密模塊:當(dāng)前代碼為明文代碼,由于 python 為解釋器語(yǔ)言,如果不加密打包,很容易被破解規(guī)則 添加可視化窗口:為了方便使用,可以將程序設(shè)計(jì)為可視化窗口模式,最簡(jiǎn)單的方式使用 TK 創(chuàng)建當(dāng)然,如果你不想自己寫,推薦可以直接使用 pyarmor ,這也是國(guó)人開發(fā)的一個(gè)加密庫(kù),包含加密、有效期、許可證。
如果這篇文章對(duì)你有幫助,點(diǎn)個(gè)贊讓我知道哦!
轉(zhuǎn)載請(qǐng)注明來(lái)自夕逆IT,本文標(biāo)題:《win10密鑰生成器(為你的python程序上鎖軟件序列號(hào)生成器)》

還沒(méi)有評(píng)論,來(lái)說(shuō)兩句吧...