在车载AI系统中,模型文件(如ONNX, PyTorch JIT, 或TensorFlow Lite文件)是核心知识产权。一旦这些模型文件被恶意攻击者或竞争对手获取并逆向工程,将导致重大的商业损失。为了“防君子也防小人”,我们不仅需要文件系统级别的安全保护,还需要在运行时对模型数据本身进行混淆和加密。
本文将聚焦一个具体且实用的技术点:使用强大的AES-256对称加密技术对模型文件进行加密存储,并在车载运行时通过内存加载的方式,避免明文模型文件出现在磁盘上,从而有效阻止简单的文件窃取攻击。
1. 技术栈准备
我们将使用Python来模拟模型文件的加密和解密加载过程。实际在嵌入式Linux或QNX环境中,您可以使用C/C++实现相同的加密和ONNX Runtime加载逻辑。
依赖库安装:
pip install cryptography onnx onnxruntime
2. 模拟模型文件与加密脚本
首先,我们假设我们有一个待保护的ONNX模型文件 ai_model.onnx。我们现在编写一个脚本来对其进行加密。
为了演示的完整性,我们先创建两个虚拟文件:ai_model.onnx(用一些随机数据模拟)和加密脚本 encrypt_model.py。
# encrypt_model.py
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
# 假设我们有一个模型文件,这里用随机数据模拟
MODEL_FILENAME = "ai_model.onnx"
ENCRYPTED_FILENAME = "ai_model.enc"
# 步骤 1: 创建模拟模型文件(实际场景中替换为你的真实模型)
if not os.path.exists(MODEL_FILENAME):
with open(MODEL_FILENAME, "wb") as f:
# 写入1MB的随机数据模拟模型权重
f.write(os.urandom(1024 * 1024))
print(f"[INFO] Created dummy model: {MODEL_FILENAME}")
# 步骤 2: 定义加密参数 (必须安全存储)
# 实际应用中,Key和IV/Nonce需要从安全硬件(如TPM/HSM)或安全存储中获取。
# 这里为了演示,我们使用随机生成的KEY。
KEY = os.urandom(32) # AES-256 Key
IV = os.urandom(12) # GCM Nonce/IV
print(f"[SECURITY] Key (Store Securely): {KEY.hex()}")
print(f"[SECURITY] IV (Store Securely): {IV.hex()}")
# 读取原始模型数据
with open(MODEL_FILENAME, "rb") as f:
model_data = f.read()
# 使用AES-256 GCM模式进行加密 (GCM提供认证)
backend = default_backend()
cipher = Cipher(algorithms.AES(KEY), modes.GCM(IV), backend=backend)
encryptor = cipher.encryptor()
ciphertext = encryptor.update(model_data) + encryptor.finalize()
tag = encryptor.tag
# 将加密后的数据和认证标签(Tag)一起写入文件
# IV/Nonce通常也需要与加密数据一起存储,因为它是公开的,用于解密。
with open(ENCRYPTED_FILENAME, "wb") as f:
f.write(IV + tag + ciphertext)
print(f"[SUCCESS] Model encrypted and saved to {ENCRYPTED_FILENAME}. Original size: {len(model_data)} bytes.")
print("[ACTION] Now delete the original file: rm ai_model.onnx")
3. 在内存中解密并加载模型
现在,我们模拟车载AI推理服务,它只读取加密文件 ai_model.enc,并在内存中完成解密和模型加载。由于模型数据从未以明文形式写入磁盘,大大增加了逆向的难度。
# load_protected_model.py
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import onnxruntime as ort
ENCRYPTED_FILENAME = "ai_model.enc"
# !!!重要:此处必须使用与加密时相同的KEY!!!
# 在实际应用中,此KEY应该通过安全机制(如硬件加密芯片)在运行时动态获取,而不是硬编码。
# 假设我们在安全存储中检索到以下KEY
KEY_HEX = "<INSERT THE KEY HEX OUTPUTTED BY ENCRYPT_MODEL.PY HERE>"
KEY = bytes.fromhex(KEY_HEX)
# 步骤 1: 读取加密文件
with open(ENCRYPTED_FILENAME, "rb") as f:
encrypted_file_data = f.read()
# 步骤 2: 解析 IV, Tag, 和密文
IV_SIZE = 12
TAG_SIZE = 16
IV = encrypted_file_data[:IV_SIZE]
tag = encrypted_file_data[IV_SIZE:IV_SIZE + TAG_SIZE]
ciphertext = encrypted_file_data[IV_SIZE + TAG_SIZE:]
print("[INFO] Starting in-memory decryption...")
# 步骤 3: 使用AES-256 GCM模式进行解密
backend = default_backend()
cipher = Cipher(algorithms.AES(KEY), modes.GCM(IV, tag), backend=backend)
decryptor = cipher.decryptor()
try:
# Decryption is done entirely in memory
decrypted_model_bytes = decryptor.update(ciphertext) + decryptor.finalize()
print(f"[SUCCESS] Decryption complete. Decrypted model size: {len(decrypted_model_bytes)} bytes.")
# 步骤 4: 直接从内存字节流加载模型
# onnxruntime.InferenceSession 支持直接从字节加载模型
session = ort.InferenceSession(decrypted_model_bytes)
print("[SUCCESS] Model loaded into ONNX Runtime session successfully (In-Memory).")
print(f"Inputs: {session.get_inputs()[0].name}")
# 此处即可开始正常的模型推理逻辑
except Exception as e:
print(f"[ERROR] Decryption failed (Key/IV/Tag mismatch or tampering): {e}")
# 生产环境中应立即退出或触发安全警告
4. 更进一步的混淆技术
虽然加密和内存加载可以有效防止文件窃取,但高级攻击者仍可能通过内存转储或动态调试来获取解密后的模型权重。为了进一步提高难度,建议结合使用:
- 权重打乱 (Weight Shuffling): 在加密前对模型权重进行非标准的排列操作,即使内存被转储,权重也是乱序的。
- 自定义操作符 (Custom Operators): 将部分核心逻辑或激活函数替换为自定义的、非标准的ONNX/TFLite操作符,使得标准推理工具无法直接运行。
- 模型结构混淆: 使用量化、裁剪或结构重排等方式,使模型图结构不那么容易被识别和理解。
- 运行时密钥分散 (Key Splitting): 将解密密钥分成多份,存储在不同的内存区域或通过复杂的计算动态生成,防止单点密钥泄露。
汤不热吧