手把手教你用Python脚本实现Keil编译后自动AES加密(附工程目录陷阱解析)
手把手教你用Python脚本实现Keil编译后自动AES加密(附工程目录陷阱解析)
在嵌入式开发中,固件升级是常见需求,而保障传输安全则是重中之重。AES加密因其高效可靠成为首选方案,但每次手动加密bin文件既耗时又容易出错。本文将带你用Python脚本实现Keil编译后自动完成AES加密的全流程,特别针对工程目录与脚本路径不一致这一"隐形杀手"提供完整解决方案。
1. 环境准备与基础配置
1.1 工具链检查
开始前请确保已安装:
- Keil MDK(建议≥5.25)
- Python 3.8+(需安装pycryptodome库)
- 目标芯片的Device Family Pack
验证Python环境:
pip install pycryptodome python -c "from Crypto.Cipher import AES; print('AES模块可用')"1.2 加密密钥管理
推荐采用.ini文件存储密钥,避免硬编码风险。示例config.ini:
[aes] key = 2B7E151628AED2A6ABF7158809CF4F3C iv = 000102030405060708090A0B0C0D0E0F注意:实际项目中应通过密钥派生函数动态生成iv,此处简化演示
2. Python加密脚本开发
2.1 基础加密函数实现
创建aes_encryptor.py,核心代码如下:
from Crypto.Cipher import AES from Crypto.Util.Padding import pad import configparser def encrypt_file(input_path, output_path): config = configparser.ConfigParser() config.read('config.ini') key = bytes.fromhex(config['aes']['key']) iv = bytes.fromhex(config['aes']['iv']) cipher = AES.new(key, AES.MODE_CBC, iv) with open(input_path, 'rb') as f: plaintext = f.read() ciphertext = cipher.encrypt(pad(plaintext, AES.block_size)) with open(output_path, 'wb') as f: f.write(ciphertext)2.2 增强型参数处理
改进版本支持命令行参数:
import sys import os if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: python aes_encryptor.py <input_bin> <output_enc>") sys.exit(1) input_file = os.path.abspath(sys.argv[1]) output_file = os.path.abspath(sys.argv[2]) if not os.path.exists(input_file): print(f"Error: Input file {input_file} not found") sys.exit(2) encrypt_file(input_file, output_file) print(f"Success: Encrypted file saved to {output_file}")3. Keil工程集成方案
3.1 编译后命令配置
在Keil中设置编译后自动执行:
- 打开Options for Target → User
- 在"After Build/Rebuild"勾选"Run #1"
- 输入命令:
python path/to/aes_encryptor.py ./Objects/your_firmware.bin ./Objects/encrypted.bin关键参数说明:
| 参数 | 说明 | 示例值 |
|---|---|---|
| %L | 工程文件路径 | C:/Projects/STM32 |
| @L | 工程名称 | MyProject |
| .\ | 工程所在目录 | - |
3.2 目录陷阱解决方案
针对工程与脚本路径不一致问题,提供三种可靠方案:
方案一:绝对路径传递
python D:\scripts\aes_encryptor.py ^ %D\Objects\%@L.bin ^ %D\Objects\%@L_enc.bin方案二:环境变量法
- 设置系统变量
PY_SCRIPT_DIR - Keil命令改为:
python %PY_SCRIPT_DIR%\aes_encryptor.py ...方案三:批处理封装创建run_encrypt.bat:
@echo off pushd "%~dp0" python aes_encryptor.py %* popdKeil中调用:
cmd /c path/to/run_encrypt.bat ...4. 调试与异常处理
4.1 常见错误排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| "python不是内部命令" | Python未加入PATH | 使用完整python路径 |
| 找不到config.ini | 工作目录错误 | 使用os.path.dirname(__file__)定位 |
| 加密文件为空 | 源文件读取失败 | 检查文件权限和路径 |
| 密钥错误 | .ini格式问题 | 验证hex字符串长度 |
4.2 日志增强实践
在脚本中添加日志记录:
import logging logging.basicConfig( filename='encryption.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s' ) try: encrypt_file(input_file, output_file) except Exception as e: logging.error(f"加密失败: {str(e)}") raise5. 进阶优化技巧
5.1 多线程加密加速
当需要批量处理时:
from concurrent.futures import ThreadPoolExecutor def batch_encrypt(file_pairs): with ThreadPoolExecutor() as executor: futures = [ executor.submit(encrypt_file, inp, out) for inp, out in file_pairs ] for future in futures: future.result() # 触发异常传播5.2 固件校验机制
加密后添加CRC校验:
import zlib def add_crc(output_path): with open(output_path, 'rb+') as f: data = f.read() crc = zlib.crc32(data).to_bytes(4, 'big') f.write(crc)实际项目中,我们团队发现最稳定的方案是方案三的批处理封装,配合日志记录可以快速定位90%以上的路径相关问题。当工程结构复杂时,建议在脚本初始处打印当前工作目录和参数解析结果,这对调试有极大帮助。
