欢迎关注我的微信公众号:MatlabGUI QtCPP等学习记录
前言
昨天老师找我,让我给软件整一个加密的功能,不能让别人随便拷贝都能用。
开始时我搜了一下加密狗,感觉这个也不方便一个软件就要一个加密狗。用加密狗的话,需要你的软件去读取这个加密狗的信息,感觉这个也不好整。
所以还是找一找通过软件来加密的办法。如果要联网验证的话,我还得整一个服务器给老师,然后还要写一个服务端的程序,感觉不划算,还是离线吧。
离线验证,有一个安全的方法是:使用公钥和私钥。实际上就相当于根据客户的计算的硬件特征,单独发送一个许可证给客户。只有我给用户发了这个许可证之后,他才能使用。
公钥和私钥,是RSA加密算法方面的东西,我没找到现成的C++库。我看一些例子是调用openssl这个三方库,但是这个库我下载不下来:)。有的大佬甚至自己写,然而我只是一条机械专业的菜狗,不懂密码学,只想着找轮子。折腾好久,感觉用C++我是整不出来了,然后就想着用Python来做这项工作吧,反正老师让做的这软件也要用到Python,正好发现Python有一个加密算法库:pycryptodome
。
公钥和私钥可以用于加密和解密:
-
公钥加密
-
私钥解密
私钥和公钥还可以用于数字签名及验证:
-
私钥产生数字签名
-
公钥用于验证私钥产生的数字签名
对软件进行加密采用的是:私钥和公钥的数字签名及验证
我不懂密码学,理论原理方面我不好表述,万一说错了还误导大家。我在B站上看到一个国外的小姐姐讲的挺好的,大家可以去看看:https://www.bilibili.com/video/BV1AW41197yV?from=search&seid=15899869232401830710
我这篇推送主要是总结整理对软件进行加密的步骤,理论方面目前不是重点(我不会)。
使用公钥私钥来加密软件的主要步骤
1. 在我自己的电脑上:
-
生成 公钥和私钥 对(生成后就不要再动它了)
-
把公钥编译到exe程序中
-
私钥自己偷偷保留好
-
把exe程序发送给客户
2. 在客户的电脑上运行程序时:
-
获取硬件信息生成硬件指纹
-
让客户把硬件指纹发送给我,我通过私钥来生成客户硬件指纹的签名文件
-
客户把指纹签名文件复制到程序的目录中
-
客户点击验证后,程序内部会根据公钥来对这个指纹签名进行验证
需要安装的两个库
-
.\pip.exe install pycryptodome
-
.\pip.exe install wmi
我这边要完成的工作
生成 公私钥 对
-
必须成对生成。
-
私钥自己保留藏好,后面用于生成数字签名。
-
公钥则跟软件一块发送给客户,客户端用公钥来验证数字签名。数字签名是在客户发给我指纹后,我利用私钥来对指纹生成数字签名。
-
最好把公钥放到资源文件里,最后编译到exe中。不然碰到懂行的大哥,他可能直接拿自己的公钥来替换掉你的公钥。要是编译到exe中的话,别人就无法替换公钥了,只能用你的私钥生成的数字签名来验证。
-
私钥文件最好也编译到生成签名的exe软件中(自己做一个注册机)。
-
生成公私钥的这个代码运行一次就可以了,主要就是为了生成公私钥文件的。
GenerateKeyFile.py
from Crypto.PublicKey import RSA
key = RSA.generate(2048)
def prepare_key_file():
r"""生成公钥文件和私钥文件
"""
private_key = key.export_key() # 私钥
public_key = key.publickey().export_key() # 公钥
# 把公私钥保存到文件(.pem)
with open("private_key.pem", "wb") as private_file, \
open("public_key.pem", "wb") as public_file:
private_file.write(private_key)
public_file.write(public_key)
if __name__ == '__main__':
prepare_key_file()
生成签名文件
根据客户发过来的硬件指纹,来生成签名文件,然后再把签名文件发送给客户。所以这部分工作不是一开始就能做的,需要把软件给客户后,我才可能做这项工作。
GenerateSignature.py
"""
在客户端使用,公钥提前发给客户了;
私钥自己留着,等客户发来摘要后,对摘要进行签名;
把签名发给客户,验证程序根据公钥对签名进行验证
"""
from Crypto.Signature import pkcs1_15
from Crypto.Hash import MD5
from Crypto.PublicKey import RSA
def get_digest(fingerprint):
"""
获取硬件指纹的摘要
:param fingerprint: 硬件指纹
:return: 返回硬件指纹的摘要
"""
return MD5.new(fingerprint.encode('utf-8'))
def generate_signature(private_key, digest):
"""定义签名函数,能够使用指定的私钥对数据文件进行签名,并将签名结果输出到文件返回
:param private_key: 私有密钥
:param digest: 用户发过来的摘要(十六进制值)
:return: 无,签名结果直接写入文件了
"""
# 使用私钥对HASH值进行签名
signature = pkcs1_15.new(private_key).sign(digest)
# 将签名结果写入文件
sig_results = open("sig_results.txt", "wb")
sig_results.write(signature)
sig_results.close()
if __name__ == "__main__":
# 导入私有密钥
with open('private_key.pem') as private_file:
private_key_ = RSA.import_key(private_file.read())
# 输入用户发来的指纹特征
fingerprint_ = "239af1404f79b42e11b5aea5a8aa85ec"
# 获取指纹摘要
digest_ = get_digest(fingerprint_)
# 用自己的私有密钥对摘要进行签名(签名结果在sig_results.txt中)
generate_signature(private_key_, digest_)
客户程序中要完成的工作
需要这两个py文件,这两个文件里都写成函数的形式,不要写成类,方便以后用C++来调用他们。
获取硬件指纹
我取的硬件信息为:
-
cpuid +
-
systemName +
-
主板的SerialNumber +
-
所有硬盘的SerialNumber +
-
所有memory的SerialNumber
这些字符拼接起来作为计算机的硬件信息,然后对这个硬件信息进行MD5加密获取硬件信息的摘要,即生成硬件指纹。
客户在获取硬件指纹后,需要把硬件指纹复制发给我用来生成签名!
GetHardwareCharacteristic.py
import wmi
from Crypto.Hash import MD5
c = wmi.WMI()
def get_hardware_characteristic():
"""
获取硬件指纹特征:
cpuid + systemName + 主板的SerialNumber + 所有硬盘的SerialNumber + 所有memory的SerialNumber
:return: 返回硬件指纹特征
"""
# 初始化指纹特征为空字符串
fingerprint = ""
# 获取所有cpu的id和systemName
for cpu in c.Win32_Processor():
fingerprint = fingerprint + cpu.ProcessorId.strip()
fingerprint = fingerprint + cpu.SystemName.strip()
# 获取主板的序列号
for board_id in c.Win32_BaseBoard():
fingerprint = fingerprint + board_id.SerialNumber.strip()
# 获取所有硬盘的的序列号
for disk in c.Win32_DiskDrive():
fingerprint = fingerprint + disk.SerialNumber.strip()
# 获取所有内存的的序列号
for mem in c.Win32_PhysicalMemory():
fingerprint = fingerprint + mem.SerialNumber.strip()
# 返回指纹
return MD5.new(fingerprint.encode('utf-8')).hexdigest()
# if __name__ == '__main__':
# print(get_hardware_characteristic())
你可以少取一点硬件信息,或者取某一个硬件的信息。
客户端验证签名
ClientVerification.py
"""
在客户端使用,公钥提前发给客户了;
私钥自己留着,等客户发来指纹后,对指纹摘要进行签名;
把签名发给客户,验证程序根据公钥对签名进行验证
公钥不能放在文件中,要一块编译到exe里面。不然别人拿自己的公钥来替换就完蛋了
"""
from Crypto.Signature import pkcs1_15
from Crypto.Hash import MD5
from Crypto.PublicKey import RSA
import GetHardwareCharacteristic
def get_digest(fingerprint):
"""
获取硬件指纹的摘要
:param fingerprint: 硬件指纹
:return: 返回硬件指纹的摘要
"""
return MD5.new(fingerprint.encode('utf-8'))
def verifier(public_key, digest, signature):
"""
利用公钥,验证签名
:param public_key: 公钥
:param digest: 硬件指纹的摘要
:param signature: 签名
:return:
"""
try:
pkcs1_15.new(public_key).verify(digest, signature)
print("验证通过!!!")
return True
except ValueError:
print("签名无效,请联系作者购买签名文件!!!")
return False
if __name__ == "__main__":
# 打开签名文件(在后面生成exe时,需要把公钥编译到exe中,不能放文件里)
with open('public_key.pem') as public_file, open('sig_results.txt', 'rb') as sig_file:
public_key_ = RSA.import_key(public_file.read()) # 导入公钥
signature_ = sig_file.read() # 读取公钥
# 获取硬件指纹
fingerprint_ = GetHardwareCharacteristic.get_hardware_characteristic()
print(fingerprint_)
# 指纹摘要(用于验证)
digest_ = get_digest(fingerprint_)
# 验证签名
verifier(public_key_, digest_, signature_)
Note: 在生成exe时,这个公钥必须一块编译到exe程序文件中。不然懂行的大哥就可能就拿他自己的公钥来替换我的公钥了,然后他就能拿他自己的私钥来生成签名。这样就失去了加密软件的目的,所以要把公钥一块编译到exe程序里。
现在这个软件也没做好,所以这里为了测试效果就放在__main__
模块中来运行下看看,我也懒得再写个界面了。
我用PPT大概画了一个验证的界面!
测试加密功能
我在我的本地机器上生成签名文件给本地的验证程序试一下;
然后再用这个本地的签名文件拿到Win10虚拟机中试一下;
本地测试,验证通过
执行:GenerateKeyFile.py 生成公钥和私钥:
把公钥拿走,放到验证程序的文件夹中:
执行:GetHardwareCharacteristic.py获取硬件指纹:
把签名文件复制到验证程序中,执行验证程序:
拿相同的签名文件,换一台机器试试
这个是在Win10虚拟机上测试的!
可见,硬件指纹不一样,拿着其他电脑的签名肯定是验证不通过的,如果你想通过更改计算机硬件信息来改变硬件指纹是不现实的,所以你只能联系我购买数字签名了!
参考价值最大的两篇博文
-
获取计算机硬件信息:https://blog.csdn.net/fengmm521/article/details/79468677
-
pycryptodome库简介:https://blog.csdn.net/weixin_42323041/article/details/105832325