芯片AES加密密钥生成工具
前言:嵌入式单片机开发,为了防止别人将芯片内的代码读取出来通过反编译手段拿到源码,常用的手段是对芯片和烧录文件进行加密。大部分的芯片厂商都会提供一个加密烧录和配置文件的工具。这个工具一般需要你填写一定长度的密钥,如果密钥填写得太有规律比如:123456,或项目名+版本号等,这样的密钥对公司来说极度不安全,如果被对手知道别人很可能直接破解出你的源码,然后抄一下你的板就和你一模一样出货了,别人省去了开发成本何乐不为呢?所以本文介绍一种通过AES加密算法将明文转换为密文的工具制作。AES加密算法目前没有破解办法,据我所知微信小程序就用到了这种加密算法。
模块构成
制作这样的工具只需要三个模块即可完成,AES加密算法模块、芯片加密模块、GUI模块。本文使用Pathon3语言开发。
AES模块
AES加密是一种常见的对称加密算法,具有较高的安全性。之所以选择用Python3来做这个工具,就是因为其丰富的现成模块支持,没必要重复造轮子,Windows下我们直接导入pycryptodome模块。由于是对称加密其基本形式是明文+密钥=密文
,密文+密钥=明文
虽然AES算法不用我们实现,但是其原理和参数需要有一定的了解。实现一次AES运算需要输入密钥、明文、模式、偏移量、字符集类型、数据块、填充内容。输出即是密文。
- 输入参数:
- 密钥:因为是对称加密算法,所以加密和解密都需要使用这个密钥参与运算。
- 明文:需要被加密的字符串,建议限制用ASCII字符。
- 模式:AES有很多种加密模式,最常用的是ECB和CBC,如果是ECB模式则不需要设置偏移量,如果是CBC模式则需要设置偏移量。
- 偏移量:偏移量会被带入到加密运算过程中影响加密结果,具体看下算法理论部分就明白了。
- 字符集类型:输入输出都是文本,需要指明文本类型。
- 数据块:算法运算原理要求输入的明文和密钥需要是128、192、256Bits的整数倍,也即16、24、32字节的整数倍。
- 填充内容:上面提到了数据块要求明文和密钥是三种字节的整数倍,如果不是则需要填充一些字符使其成为整数倍这样才能进行AES算法的运算。所以这个参数即是定义填充的字符。
- 模块内函数:
- init(self, Key, Model, IV, Encode, DataBlockLen, SupplementChar) 构造函数,输入AES的参数。
- DataBlock(self, OriginalKey, DataBlockLen, SupplementChar) 对数据(指明文和密钥)进行块化,填充使其为16、24、32字节的整数倍。
- AESEncrypt(self, OriginalText) 加密函数,将明文转换为密文。
- AESDecrypt(self, DecryptedText) 解密函数,将密文转换为明文。我们的工具用于加密,所以这个函数实际没被调用,但我还是将其写出了。
'''
@file AESCrypt.py
@brief AES加密算法类
@details 创建该对象进行AES加密
@author Calm
@data 2020-03-13
@version 1.0.0
@copyright Calm
'''
from Crypto.Cipher import AES # Window下需要安装pycryptodome模块
import base64
class AESCrypt:
'''
@fn __init__(self, Key, Model, IV, Encode, DataBlockLen, SupplementChar)
@brief 带参构造函数,输入AES加密的参数。
@details ECB加密模式无偏移量IV,CBC需要设置IV。需要确保密钥进行了块化。
@param[in] Key 加密密钥。
@param[in] Model 加密模式(ECB、CBC等)。
@param[in] IV 加密偏移量。
@param[in] Encode 字符集类型(gb2312、gbk等)。
@param[in] DataBlockLen 数据块(单位是字节)(密钥和明文长度需是128、192、256Bit的倍数。
@param[in] SupplementChar 对数据块进行填充,使得数据为128、192、或256Bit的倍数。
@return None
@note 无。
@attention 无
'''
def __init__(self, Key, Model, IV, Encode, DataBlockLen, SupplementChar):
self.AESEncode = Encode
self.SupplementChar = SupplementChar
self.AESModel = {'ECB': AES.MODE_ECB, 'CBC': AES.MODE_CBC}[Model]
self.AESDataBlockLen = DataBlockLen
self.AESSupplementKey = self.DataBlock(Key, self.AESDataBlockLen, self.SupplementChar)
if Model == 'ECB':
self.AES = AES.new(self.AESSupplementKey, self.AESModel)
elif Model == 'CBC':
self.AES = AES.new(self.AESSupplementKey, self.AESModel, IV)
return
'''
@fn DataBlock(self, OriginalKey, DataBlockLen, SupplementChar)
@brief 对数据进行块化。
@details 数据块必须是128、192、256Bit的倍数。
@param[in] OriginalKey 原始未块化数据。
@param[in] DataBlockLen 块化后数据的长度。
@param[in] SupplementChar 用于填充的字符。
@return 块化后的数据。
@note 无。
@attention 无
'''
def DataBlock(self, OriginalKey, DataBlockLen, SupplementChar):
SupplementData = OriginalKey.encode(self.AESEncode)
#如果数据不是块长度的整数倍,则对数据进行填充
while len(SupplementData) % DataBlockLen != 0:
SupplementData += SupplementChar.encode('utf-8')
return SupplementData
'''
@fn AESEncrypt(self, OriginalText)
@brief AES加密,将明文转化为密文。
@details 需要确保加密的是块化后的数据。
@param[in] OriginalText 原始未块化明文。
@return 密文
@note 无。
@attention 无
'''
def AESEncrypt(self, OriginalText):
SupplementText = self.DataBlock(OriginalText, self.AESDataBlockLen, self.SupplementChar)
EncryptedText = self.AES.encrypt(SupplementText)
return base64.encodebytes(EncryptedText).decode().strip()
'''
@fn AESDecrypt(self, DecryptedText)
@brief AES解密,将密文转化为明文。
@details 无。
@param[in] DecryptedText 密文。
@return 明文
@note 无。
@attention 无
'''
def AESDecrypt(self, DecryptedText):
OriginalText = base64.decodebytes(DecryptedText.encode(self.AESEncode))
OriginalText = self.AES.decrypt(OriginalText)
return OriginalText .decode(self.AESEncode).strip('\0')
HDSCCrypt模块
对特定芯片和固件进行加密时设定AES参数。
- 输入参数:
- 明文:需要被加密的字符串。
- 其他参数:其他参数就是上面提到的AES加密需要的参数。
- 模块内函数:
- init(self) 构造函数,输入AES加密相关的参数。
- HDSCEncrypt(self, Plaintext) 针对特定厂商芯片的加密函数,将明文转换为密文。
- CheckPlaintext(self, Plaintext) 对明问进行格式检查,当含有非ASCII字符或空白时抛出错误码。
'''
@file HDSCCrypt.py
@brief 固件加密类
@details 创建该对象进行固件加密
@author Calm
@data 2020-03-13
@version 1.0.0
@copyright Calm
'''
from AESCrypt import *
import re
class HDSCCrypt:
'''
@fn __init__(self)
@brief 无参构造函数。
@details 固件使用AES加密的参数在构造函数里设定。
@return None
@note 无。
@attention 无
'''
def __init__(self):
self.HDSCCryptKey = "123456789"
self.HDSCCryptModel = "ECB"
self.HDSCCryptIV = ""
self.HDSCCryptEncode = "gbk"
self.HDSCCryptDataBlockLen = 16
self.AESCrypt = AESCrypt(self.HDSCCryptKey, self.HDSCCryptModel, self.HDSCCryptIV, self.HDSCCryptEncode, self.HDSCCryptDataBlockLen, "\x00")
return
'''
@fn HDSCEncrypt(self, Plaintext)
@brief 计算烧录固件和芯片加密的密钥。
@details 无。
@param[in] Plaintext 明文。
@return 密文
@note 无。
@attention 无
'''
def HDSCEncrypt(self, Plaintext):
return self.AESCrypt.AESEncrypt(Plaintext)
'''
@fn CheckPlaintext(self, Plaintext)
@brief 对明文输入进行格式检查。
@details 限制为不含空白字符的ASCII码。
@param[in] Plaintext 明文。
@return 检查结果
- 0 正确
- 1 含有空白字符
- 2 含有非ASCII码
- 3 空明文
@note 无。
@attention 无
'''
def CheckPlaintext(self, Plaintext):
Ret = 0
if re.match('.*\s.*', Plaintext) is not None: #包含空白字符
Ret = 1
elif len(Plaintext) != len(Plaintext.encode('utf-8')): #含有非ASCII码
Ret = 2
elif Plaintext == "": #明文为空
Ret = 3
else: #正确明文
Ret = 0
return Ret
CryptGUI模块
在Windows下构造GUI。这个GUI界面非常简洁,两个编辑框和两个按键,相应的有两个按键的回调函数。
'''
@file CryptGUI.py
@brief 固件加密软件GUI类
@details 创建该对象进行固件加密GUI生成
@author Calm
@data 2020-03-13
@version 1.0.0
@copyright Calm
'''
from tkinter import *
import tkinter.messagebox
from HDSCCrypt import *
from IconResourse import Icon
import os
class CryptGUI:
'''
@fn __init__(self, StaticText1, StaticText2, ButtonText1, ButtonText2)
@brief 带参构造函数,参数可设置静态文本和按钮文本。
@details 无。
@param[in] StaticText1 静态文本1。
@param[in] StaticText2 静态文本2。
@param[in] ButtonText1 按钮文本1。
@param[in] ButtonText2 按钮文本2。
@return None
@note 无。
@attention 无
'''
def __init__(self, StaticText1, StaticText2, ButtonText1, ButtonText2):
self.Window = Tk()
self.Str1 = StringVar()
self.Str2 = StringVar()
self.HDSCFirmwareKey = HDSCCrypt()
# 对Label进行表格式布局
Label(self.Window, text=StaticText1).grid(row=0, column=0)
Label(self.Window, text=StaticText2).grid(row=1, column=0)
# 对编辑框进行表格式布局
Edit1 = Entry(self.Window, textvariable=self.Str1, width=32)
Edit2 = Entry(self.Window, textvariable=self.Str2, state='readonly', width=32)
Edit1.grid(row=0, column=1, padx=10, pady=5)
Edit2.grid(row=1, column=1, padx=10, pady=5)
Button(self.Window, text=ButtonText1, width=10, command=self.CallBackButton1).grid(row=3, column=0, sticky=W,
padx=10,
pady=5)
Button(self.Window, text=ButtonText2, width=10, command=self.CallBackButton2).grid(row=3, column=1, sticky=E,
padx=10,
pady=5)
ScreenWidth, ScreenHeigh = self.Window.maxsize()
CurWidth = self.Window.winfo_reqwidth()
CurHeight = self.Window.winfo_reqheight()
self.Window.geometry("+{}+{}".format(ScreenWidth // 2 - CurWidth, ScreenHeigh // 2 - CurHeight))
#设置窗口是否可变长、宽,True:可变,False:不可变
self.Window.resizable(width=False, height=False)
#图标资源保存在IconResourse.py里,目的是保证生成的exe不依赖其他文件
tmp = open("tmp.icon", "wb+")
tmp.write(base64.b64decode(Icon)) #临时图标文件
tmp.close()
#修改图标
self.Window.iconbitmap("tmp.icon")
os.remove("tmp.icon") #删除临时图标文件
#修改窗口标题
self.Window.title("HDSCCrypt V1.0.0")
return
'''
@fn CallBackButton1(self)
@brief 按钮1回调函数。
@details 读取编辑框1内容进行加密后显示在编辑框2。会对明文格式进行检查。
@return None
@note 无。
@attention 无
'''
def CallBackButton1(self):
Plaintext = self.Str1.get()
self.Str2.set("")
CheckResult = self.HDSCFirmwareKey.CheckPlaintext(Plaintext )
if CheckResult == 1:
tkinter.messagebox.showinfo('Error', '明文不符合规范\n\n\n错误原因:含有空白字符。')
elif CheckResult == 2:
tkinter.messagebox.showinfo('Error', '明文不符合规范\n\n\n错误原因:限制只能使用ASCII字符。')
elif CheckResult == 3:
pass
else:
Ciphertext = self.HDSCFirmwareKey.HDSCEncrypt(Plaintext)
if len(Ciphertext) > 16:
Ciphertext = Ciphertext[0:16]
self.Str2.set(Ciphertext)
return
'''
@fn CallBackButton2(self)
@brief 按钮2回调函数。
@details 弹出关于对话框,显示描述信息。
@return None
@note 无。
@attention 无
'''
def CallBackButton2(self):
tkinter.messagebox.showinfo("关于", "使用说明:该软件用于单片机进行芯片和烧录文件加密时,将明文信息转换为密文。如果发现Bug请联系作者。\n\n作者:Calm\n发布日期:2020-03-13\n发布版本:V1.0.0\n版权:Calm")
return
'''
@fn CryptGUILoopRun(self)
@brief 开始对GUI时间监听。
@details 无。
@return None
@note 无。
@attention 无
'''
def CryptGUILoopRun(self):
mainloop()
return
最后是入口函数模块
模块封装好后调用就非常简单了。
'''
@file main.py
@brief 入口文件
@details 无
@author Calm
@data 2020-03-13
@version 1.0.0
@copyright Calm
'''
from CryptGUI import *
'''
@fn main()
@brief 入口函数
@details 无
@return None
@note 无。
@attention 无
'''
def main():
#创建加密GUI窗口对象
HDSCCryptGUI = CryptGUI("明文: ", "密文: ", "加密", "关于")
HDSCCryptGUI.CryptGUILoopRun()
return
if __name__ == "__main__":
main()