1. OTA升级包生成与签名

OTA升级包生成与签名 学习卡片

任务说明

本案例实现一个最小可用的OTA升级包生成与签名工具:将版本信息与升级脚本打包为ZIP归档,使用RSA私钥对归档内容计算SHA-256摘要并签名,最终生成 .zip 升级包与 .sig 签名文件。验证方可通过公钥验签确认升级包未被篡改且来源可信。

环境依赖

需要Python 3.8+,仅使用标准库 zipfile、hashlib、cryptography。安装依赖:

pip install cryptography

完整实现

import zipfile
import hashlib
import json
from pathlib import Path
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding

# ---------- 1. 生成RSA密钥对(仅演示用,实际应由安全机构保管) ----------
def generate_keypair(key_path: Path):
    private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    public_key = private_key.public_key()
    key_path.parent.mkdir(parents=True, exist_ok=True)
    with open(key_path, "wb") as f:
        f.write(private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption()
        ))
    with open(str(key_path) + ".pub", "wb") as f:
        f.write(public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo
        ))
    return private_key, public_key

# ---------- 2. 构建OTA升级包 ----------
def build_ota_package(version: str, script_content: str, output_zip: Path):
    manifest = {
        "version": version,
        "files": ["update.sh"],
        "created_by": "ota-builder"
    }
    with zipfile.ZipFile(output_zip, "w", zipfile.ZIP_DEFLATED) as zf:
        zf.writestr("manifest.json", json.dumps(manifest, indent=2))
        zf.writestr("update.sh", script_content)
    return output_zip

# ---------- 3. 计算摘要并签名 ----------
def sign_package(zip_path: Path, private_key, sig_path: Path):
    digest = hashlib.sha256(zip_path.read_bytes()).digest()
    signature = private_key.sign(
        digest,
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    sig_path.write_bytes(signature)
    return digest.hex()

# ---------- 4. 验证签名 ----------
def verify_signature(zip_path: Path, sig_path: Path, public_key) -> bool:
    digest = hashlib.sha256(zip_path.read_bytes()).digest()
    try:
        public_key.verify(
            sig_path.read_bytes(),
            digest,
            padding.PKCS1v15(),
            hashes.SHA256()
        )
        return True
    except Exception:
        return False

# ---------- 主流程 ----------
if __name__ == "__main__":
    # 配置路径
    base = Path("./ota_demo")
    key_file = base / "keys/ota_private.pem"
    zip_file = base / "ota_package_v1.0.0.zip"
    sig_file = base / "ota_package_v1.0.0.zip.sig"

    # 1. 生成密钥(首次运行)
    if not key_file.exists():
        generate_keypair(key_file)
    with open(key_file, "rb") as f:
        private_key = serialization.load_pem_private_key(f.read(), password=None)
    public_key = private_key.public_key()

    # 2. 构造升级包内容
    version = "1.0.0"
    update_script = """#!/bin/bash
# 示例升级脚本:实际项目中在此执行文件替换、服务重启等操作
echo "OTA update to version 1.0.0 started..."
# 模拟升级动作
touch /tmp/ota_update_marker
echo "Update completed."
"""
    build_ota_package(version, update_script, zip_file)

    # 3. 签名
    digest_hex = sign_package(zip_file, private_key, sig_file)
    print(f"升级包: {zip_file}")
    print(f"签名文件: {sig_file}")
    print(f"SHA-256摘要: {digest_hex[:16]}...")

    # 4. 验证(自检)
    ok = verify_signature(zip_file, sig_file, public_key)
    print(f"签名验证: {'通过' if ok else '失败'}")

运行与验证

  1. 将上述代码保存为 ota_builder.py,在终端执行:
python ota_builder.py
  1. 程序将自动完成密钥生成、打包、签名和验证,输出类似以下内容:
升级包: ./ota_demo/ota_package_v1.0.0.zip
签名文件: ./ota_demo/ota_package_v1.0.0.zip.sig
SHA-256摘要: 3f7a9c2e...
签名验证: 通过
  1. 验证篡改检测:手动修改 ota_package_v1.0.0.zip 中任意字节(例如用文本编辑器在文件末尾追加一个空格),重新运行验证代码(可单独执行 verify_signature 函数),签名验证应返回 False。

关键说明

  • 签名对象是ZIP文件的完整二进制内容,任何字节改动都会导致SHA-256摘要变化,从而使验签失败。
  • 实际OTA系统中,私钥应保存在离线安全环境中,公钥预置于车机端用于验签。
  • 本案例使用2048位RSA密钥,满足基本安全要求;生产环境可根据安全策略升级到4096位或使用ECC。