4. 升级包签名与验签流程设计
任务目标
设计并实现一个OTA升级包的签名与验签流程,确保升级包在传输和存储过程中未被篡改,且来源可信。本案例使用ECDSA P-256(secp256r1)签名算法,完成从密钥生成、升级包签名到验签的完整闭环。
环境与依赖
- Python 3.8+
- 依赖库:
cryptography(安装:pip install cryptography)
核心实现
以下代码实现完整流程:生成密钥对 → 对升级包二进制内容计算签名 → 验签并返回结果。签名使用SHA-256哈希,私钥签名、公钥验签。
import os
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.exceptions import InvalidSignature
# ---------- 1. 生成密钥对(实际部署时由OTA服务端保存私钥,设备内置公钥) ----------
def generate_keypair():
private_key = ec.generate_private_key(ec.SECP256R1())
public_key = private_key.public_key()
return private_key, public_key
# ---------- 2. 签名升级包 ----------
def sign_package(private_key, package_bytes: bytes) -> bytes:
"""返回DER格式签名"""
signature = private_key.sign(
package_bytes,
ec.ECDSA(hashes.SHA256())
)
return signature
# ---------- 3. 验签升级包 ----------
def verify_package(public_key, package_bytes: bytes, signature: bytes) -> bool:
"""验签成功返回True,失败返回False"""
try:
public_key.verify(
signature,
package_bytes,
ec.ECDSA(hashes.SHA256())
)
return True
except InvalidSignature:
return False
# ---------- 4. 密钥序列化(用于存储/分发) ----------
def serialize_keys(private_key, public_key):
# 私钥PKCS8 PEM格式(OTA服务端保存)
priv_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
# 公钥SubjectPublicKeyInfo PEM格式(烧录到设备)
pub_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
return priv_pem, pub_pem
# ---------- 5. 最小可运行闭环 ----------
if __name__ == "__main__":
# 模拟升级包内容(实际为固件二进制)
firmware = os.urandom(2048) # 2KB随机数据模拟固件
# 生成密钥
priv, pub = generate_keypair()
# 签名
sig = sign_package(priv, firmware)
print(f"签名长度: {len(sig)} 字节 (DER格式)")
# 正常验签
ok = verify_package(pub, firmware, sig)
print(f"原始包验签结果: {ok}")
# 篡改检测:修改1字节后验签
tampered = bytearray(firmware)
tampered[100] ^= 0xFF # 翻转第100字节的每一位
ok_tampered = verify_package(pub, bytes(tampered), sig)
print(f"篡改包验签结果: {ok_tampered}")
# 密钥序列化示例
priv_pem, pub_pem = serialize_keys(priv, pub)
print(f"私钥PEM行数: {len(priv_pem.splitlines())}")
print(f"公钥PEM行数: {len(pub_pem.splitlines())}")
运行与验证
- 将上述代码保存为
ota_sign_verify.py。 - 运行:
python ota_sign_verify.py - 通过条件:输出中
原始包验签结果: True且篡改包验签结果: False。这证明签名能识别任何字节级篡改。
OTA流程中的接入位置
- 服务端:固件编译完成后调用
sign_package(),将签名与固件打包下发。 - 设备端:下载完成后,使用预置公钥调用
verify_package(),验签通过才允许写入Flash。 - 密钥管理:私钥仅在服务端;公钥在设备出厂时烧录,不可更新(或通过安全通道更新)。
常见问题
- 验签失败:确认设备端公钥与服务端私钥是否同一对;确认签名传输过程中未截断。
- 性能:ECDSA P-256验签在常见MCU(如Cortex-M4 @ 100MHz)上约需200~500ms,若需更快可改用Ed25519,但需确认硬件支持。
- 防回滚:签名仅保证完整性,如需防版本回滚,需在固件中附加版本号并一并签名。