# 嵌入式产品加密方案

> 作者: Elaine
> 日期: 2026-03-13
> 标签: 安全, 嵌入式

---

<h1>🔐 嵌入式产品软件加密方案</h1>
        <p class="subtitle">适用于 ARM Cortex-M0/M3/M4 等资源受限 MCU | 附 C 代码示例</p>

        <div class="warning">
            <div class="warning-title">⚠️ 免责声明</div>
            <p>本方案仅供技术参考，无法保证完全防止破解。硬件安全需结合软件防护，必要时请咨询专业安全公司。</p>
        </div>

        <div class="section">
            <h3>一、背景与威胁模型</h3>
            <p>嵌入式产品面临的常见攻击手段包括：</p>
            <ul>
                <li><strong>电压/时钟 glitching（故障注入）</strong> — 通过短暂干扰芯片供电或时钟，使加密验证跳过执行</li>
                <li><strong>DPA / CPA（差分功率分析）</strong> — 通过分析芯片运行时的功耗曲线推测密钥</li>
                <li><strong>芯片 decap + 固件提取</strong> — 物理方法去除封装，用微探针直接读取 flash 内容</li>
                <li><strong>调试接口攻击</strong> — 通过 SWD/JTAG 接口读取内存或控制执行</li>
            </ul>
        </div>

        <div class="section">
            <h3>二、芯片级安全特性（选型建议）</h3>
            <p>如果预算允许，优先选择带硬件安全特性的 MCU：</p>
            <div class="chip-level">
                <div class="chip-card">
                    <div class="name">STM32L4+ 系列</div>
                    <div class="desc">含 Secure Boot + 主动防篡改</div>
                </div>
                <div class="chip-card">
                    <div class="name">NXP LPC55Sxx</div>
                    <div class="desc">内置 Secure Boot、可信区域</div>
                </div>
                <div class="chip-card">
                    <div class="name">Infineon PSoC 6</div>
                    <div class="desc">双核架构，安全隔离区</div>
                </div>
                <div class="chip-card">
                    <div class="name">TI MSP432E4</div>
                    <div class="desc">安全启动 + 加密引擎</div>
                </div>
            </div>
        </div>

        <h2>三、软件层面加密方案（含代码示例）</h2>

        <div class="section">
            <h3><span class="method-num">1</span>代码混淆</h3>
            <p>将关键算法拆分、跳转嵌套，使反汇编难以理解。攻击者看到的是一堆跳转和"垃圾"代码，无法直接看出算法逻辑。</p>
            
            <div class="why-box">
                <div class="why-box-title">💡 为什么难以破解？</div>
                <p>正常代码是线性的：<code>A → B → C → 结果</code>。混淆后变成：<code>A → 随机跳转 → 垃圾代码 → 函数指针表 → 间接调用 → 解密结果</code>。攻击者需要手动追踪每一条跳转，耗时增加 10-100 倍。</p>
            </div>

            <pre><code>// 方法1：函数指针间接调用（增加分析难度）
typedef int (*CalcFunc)(int a, int b);

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }

// 函数指针数组，但索引不直接使用
static CalcFunc funcs[] = {sub, mul, add};  // 顺序打乱

// 通过计算得到索引，而不是直接写 add[2]
int secure_calc(int type, int a, int b) {
    int idx = (type * 7) % 3;  // 攻击者不知道这个算法
    return funcs[idx](a, b);
}

// 方法2：插入无效代码干扰分析
void process_key(uint8_t *key) {
    uint8_t dummy1 = key[0] ^ 0x55;  // 垃圾代码
    uint8_t dummy2 = key[1] + 0xAA;  // 垃圾代码
    
    // 真正的处理
    for(int i = 0; i < 16; i++) {
        key[i] = key[i] ^ 0x42;
    }
    
    // 更多垃圾代码
    volatile uint8_t waste = dummy1 + dummy2;
    (void)waste;  // 防止优化
}</code></pre>
        </div>

        <div class="section">
            <h3><span class="method-num">2</span>运行时动态解密</h3>
            <p>关键数据不以明文存储在 Flash 中，运行时才解密。即使攻击者读取了 Flash，看到的也只是密文。</p>
            
            <div class="why-box">
                <div class="why-box-title">💡 为什么难以破解？</div>
                <p>Flash 中存的是 <code>0xA3 0x7F 0x12...</code>，攻击者不知道密钥就无法还原。即使提取了固件，没有正确的解密 key 也无法得到原始算法。</p>
            </div>

            <pre><code>// 加密的密钥存储在 Flash（攻击者读不到明文）
// 这段数据是用 AES 加密后的结果，密钥通过 OTP 或外部芯片提供
const uint8_t encrypted_key[16] __attribute__((section(".encrypted"))) = {
    0x8A, 0x7B, 0x3C, 0xD1, 0x5E, 0x9F, 0x22, 0x77,
    0x44, 0xB1, 0x6E, 0xC3, 0x9A, 0x08, 0xF5, 0x6B
};

// 运行时解密（密钥从安全区域读取）
void get_decrypted_key(uint8_t *out_key) {
    // 从 OTP 或安全引导区读取原始密钥
    uint8_t master_key[16];
    read_from_otp_secure_region(master_key);  // 这是一个"不可能被读"的区域
    
    // AES 解密
    AES_decrypt(encrypted_key, out_key, master_key);
    
    // 使用后立即清除（防止内存泄漏）
    memset(master_key, 0, 16);
}

// 验证流程：每次需要用密钥时临时解密
int verify_license(uint8_t *license_key) {
    uint8_t real_key[16];
    get_decrypted_key(real_key);  // 临时解密
    
    int result = memcmp(license_key, real_key, 16);
    
    // 关键：使用后立即清除！
    memset(real_key, 0, 16);
    
    return result;
}</code></pre>
        </div>

        <div class="section">
            <h3><span class="method-num">3</span>校验和反调试</h3>
            <p>检测调试器连接或代码被篡改，主动破坏数据让攻击者即使破解也无法使用。</p>
            
            <div class="why-box">
                <div class="why-box-title">💡 为什么难以破解？</div>
                <p>攻击者想调试代码来理解逻辑？但一旦检测到调试器连接，芯片会主动擦除关键数据或跳转到死循环，等于白忙一场。</p>
            </div>

            <pre><code>// 方法1：检测调试器（检查 CPU 状态寄存器）
int check_debugger(void) {
    // Cortex-M 的 DHCSR 调试寄存器
    // 如果 bit0 = 1，说明调试器已连接
    volatile uint32_t *DHCSR = (uint32_t *)0xE000EDF0;
    if (*DHCSR & 0x00000001) {
        return 1;  // 检测到调试器
    }
    return 0;
}

// 方法2：代码段 CRC 校验（检测被篡改）
uint32_t calculate_crc(uint32_t start, uint32_t length) {
    uint32_t crc = 0xFFFFFFFF;
    for (uint32_t i = 0; i < length; i++) {
        crc ^= *(uint8_t *)(start + i);
        for (int j = 0; j < 8; j++) {
            crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
        }
    }
    return ~crc;
}

// 启动时和关键操作前校验
void secure_verify(void) {
    // 预期值：编译时计算好并固化
    const uint32_t expected_crc = 0xDEADBEEF;
    
    uint32_t actual_crc = calculate_crc(0x08003000, 0x1000);  // 校验关键代码段
    
    if (actual_crc != expected_crc) {
        // 代码被篡改！自毁
        erase_flash_secure_region();      // 擦除加密密钥
        memset((void*)0x20000000, 0, 4096);  // 清 RAM
        while(1);  // 死循环
    }
}

// 方法3：蜜罐函数（诱杀攻击者）
void secret_decrypt(uint8_t *data, int len) {
    // 这是一个假函数，攻击者破解后会调用它
    // 但它会触发自毁
    trigger_self_destruct();
}

// 在某处埋下陷阱
void maybe_call_honeypot(void) {
    // 5% 概率调用蜜罐（随机爆炸）
    if ((RTC->TR & 0xFF) % 20 == 0) {
        secret_decrypt(NULL, 0);
    }
}</code></pre>
        </div>

        <div class="section">
            <h3><span class="method-num">4</span>时间随机化</h3>
            <p>关键操作加入随机延时，使 glitching 攻击难以找到正确的时序窗口。</p>
            
            <div class="why-box">
                <div class="why-box-title">💡 为什么难以破解？</div>
                <p>Glitching 攻击依赖精确的时序。加入了随机延时后，攻击者需要尝试 10^6-10^9 次才可能成功一次，成本极高。</p>
            </div>

            <pre><code>// 伪随机数生成（基于 ADC 噪声，更难预测）
uint32_t get_random_delay(uint32_t min_us, uint32_t max_us) {
    uint32_t noise = 0;
    // 读取 ADC 噪声作为随机种子
    ADC1->CR |= ADC_CR_ADSTART;
    while(!(ADC1->ISR & ADC_ISR_EOC));
    noise = ADC1->DR;  // ADC 噪声值
    
    uint32_t range = max_us - min_us;
    return min_us + (noise % range);
}

// 关键操作前加入随机延时
void secure_critical_operation(void) {
    // 验证通过后，不要立刻执行
    // 加入 100-500us 随机延时
    uint32_t delay_us = get_random_delay(100, 500);
    delay_us(delay_us);  // 硬件延时
    
    // 再执行真正的操作
    execute_critical_task();
}

// 加密操作加入随机填充（打乱功耗曲线）
void random_delay_aes_encrypt(uint8_t *input, uint8_t *output, uint8_t *key) {
    // 随机延时使 DPA 攻击失效
    uint32_t delay1 = get_random_delay(50, 200);
    delay_us(delay1);
    
    // 执行 AES
    AES_encrypt(input, output, key);
    
    // 再加一段随机延时
    uint32_t delay2 = get_random_delay(30, 150);
    delay_us(delay2);
}</code></pre>
        </div>

        <div class="section">
            <h3><span class="method-num">5</span>多级验证机制</h3>
            <p>分散验证点，攻击者必须全部突破才能破解，任何一处失败都会触发保护。</p>
            
            <div class="why-box">
                <div class="why-box-title">💡 为什么难以破解？</div>
                <p>传统方案只有一个验证点，破解就全部搞定。多级验证把安全分散到多个位置，攻击者需要同时突破所有关卡，难度指数级上升。</p>
            </div>

            <pre><code>// 三级验证机制

// 第一级：启动验证（bootloader 阶段）
int level1_boot_verify(void) {
    uint32_t stored_crc = *((uint32_t *)FLASH_SIGNATURE_ADDR);
    return check_crc(stored_crc);  // 验证固件完整性
}

// 第二级：功能级验证（每次关键操作前）
int level2_feature_verify(uint8_t *license) {
    // 验证 license 格式 + 签名
    return verify_license_signature(license);
}

// 第三级：运行时随机验证（定时触发）
int level3_runtime_verify(void) {
    // 随机抽取某个内存区域验证完整性
    uint32_t check_addr = get_random_ram_region();
    return verify_ram_integrity(check_addr);
}

// 组合调用：全部通过才算成功
int secure_activate(uint8_t *license_key) {
    if (level1_boot_verify() != 0) return -1;  // 第一级失败
    if (level2_feature_verify(license_key) != 0) return -2;  // 第二级失败
    
    // 每 10 次调用，随机触发第三级验证
    static int call_count = 0;
    if (++call_count % 10 == 0) {
        if (level3_runtime_verify() != 0) return -3;  // 第三级失败
    }
    
    return 0;  // 全部通过
}</code></pre>
        </div>

        <div class="section">
            <h3><span class="method-num">6</span>密钥分片管理</h3>
            <p>密钥不存放在单一位置，攻击者即使找到一部分也无法拼出完整密钥。</p>
            
            <div class="why-box">
                <div class="why-box-title">💡 为什么难以破解？</div>
                <p>密钥被拆成 3-5 份，分别存放在 Flash 的不同位置，有的甚至通过算法临时计算。攻击者必须同时获取所有分片并知道拼接算法。</p>
            </div>

            <pre><code>// 密钥分 3 片存储
// 分片1：直接存放在 Flash 某处
const uint8_t key_part1[4] __attribute__((section(".key1"))) = {0x12, 0x34, 0x56, 0x78};

// 分片2：通过计算得到（基于 UID）
// 攻击者即使读到 key_part1，不知道这个算法也拼不出来
void get_key_part2(uint8_t *out) {
    uint32_t uid = get_chip_uid();  // 芯片唯一 ID
    out[0] = (uid >> 24) ^ 0xAA;
    out[1] = (uid >> 16) ^ 0x55;
    out[2] = (uid >> 8) ^ 0xCC;
    out[3] = uid ^ 0x33;
}

// 分片3：存在外部 EEPROM 或通过通信获取
void get_key_part3(uint8_t *out) {
    // 从外部安全芯片读取
    external_secure_chip_read(0x10, out, 4);
}

// 运行时动态拼接完整密钥
void assemble_full_key(uint8_t *full_key) {
    // 拼接 3 个分片
    memcpy(full_key, key_part1, 4);           // 直接拷贝
    get_key_part2(full_key + 4);              // 动态计算
    get_key_part3(full_key + 8);              // 外部读取
    
    // 可选：再做一个 XOR 混淆
    for (int i = 0; i < 12; i++) {
        full_key[i] ^= 0x5A;
    }
}</code></pre>
        </div>

        <div class="section">
            <h3><span class="method-num">7</span>轮换密钥机制</h3>
            <p>每次固件升级更换密钥，旧版固件无法在新硬件上运行，防止历史漏洞被利用。</p>
            
            <div class="why-box">
                <div class="why-box-title">💡 为什么难以破解？</div>
                <p>即使攻击者破解了 v1.0 版本并提取了密钥，v1.1 升级后旧密钥立即失效。攻击者必须重新破解，但每次升级都换密钥，成本太高。</p>
            </div>

            <pre><code>// 版本号存储在 OTP 或专用区域
#define CURRENT_VERSION  3

// 版本密钥表（每个版本对应一个密钥）
const uint8_t version_keys[4][16] __attribute__((section(".version_keys"))) = {
    // v0（已废弃，密钥可公开让旧固件失效）
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
    // v1
    {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00},
    // v2
    {0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11},
    // v3（当前版本）
    {0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22}
};

// 升级时调用：擦除旧版本，使用新版本
void upgrade_version(uint8_t new_version) {
    // 从服务器获取新密钥（通过安全通道）
    uint8_t new_key[16];
    server_fetch_new_key(new_version, new_key);
    
    // 写入新密钥（写入 OTP 区域）
    write_to_otp(CURRENT_VERSION_ADDR, new_version);
    write_key_to_otp(new_version, new_key);
    
    // 旧版本密钥立即失效
    invalidate_old_version();
}

// 运行时检查版本密钥
int verify_version_key(uint8_t *input_key) {
    uint8_t current_ver = read_from_otp(CURRENT_VERSION_ADDR);
    const uint8_t *valid_key = version_keys[current_ver];
    
    return memcmp(input_key, valid_key, 16);
}</code></pre>
        </div>

        <div class="section">
            <h3><span class="method-num">8</span>通讯层加密</h3>
            <p>产品与服务器/APP 通信必须加密，防止中间人攻击和重放攻击。</p>
            
            <div class="why-box">
                <div class="why-box-title">💡 为什么难以破解？</div>
                <p>即使攻击者抓到了通信包，没有密钥也看不懂内容。加上时间戳和序列号，即使重放旧包也会被识别为无效。</p>
            </div>

            <pre><code>// 轻量级通信加密（适用于资源受限的 M0）
// 使用 XOR + 时间戳 + 简单哈希

typedef struct {
    uint8_t data[32];      // 实际数据
    uint8_t hash[4];       // 简化 HMAC
    uint32_t timestamp;    // 时间戳
    uint16_t sequence;     // 序列号防重放
} SecurePacket;

// 简化版 HMAC（资源受限时使用）
void simple_hmac(uint8_t *key, uint8_t *data, int len, uint8_t *out) {
    uint32_t h = 0x12345678;  // 初始化值
    for (int i = 0; i < len; i++) {
        h = h ^ (data[i] * 31 + key[i % 16]);
        h = (h << 5) | (h >> 27);  // 循环左移
    }
    memcpy(out, &h, 4);
}

// 发送加密数据
void send_secure(uint8_t *payload, int len) {
    static uint16_t seq = 0;
    
    SecurePacket pkt;
    memcpy(pkt.data, payload, len > 32 ? 32 : len);
    pkt.timestamp = get_rtc_time();  // 获取当前时间
    pkt.sequence = seq++;
    
    // 生成 HMAC（简单版）
    uint8_t session_key[16];
    get_session_key(session_key);  // 动态生成的会话密钥
    simple_hmac(session_key, (uint8_t*)&pkt, sizeof(pkt) - 4, pkt.hash);
    
    // 发送（可能是明文，但有 hash 保护完整性）
    uart_send((uint8_t*)&pkt, sizeof(pkt));
}

// 接收并验证
int recv_verify(uint8_t *packet, int len) {
    SecurePacket *pkt = (SecurePacket*)packet;
    
    // 1. 检查序列号（防重放）
    if (!is_sequence_valid(pkt->sequence)) {
        return -1;  // 可能是重放攻击
    }
    
    // 2. 检查时间戳（5分钟内的包才有效）
    uint32_t now = get_rtc_time();
    if (abs(now - pkt->timestamp) > 300) {
        return -2;  // 包太旧了
    }
    
    // 3. 验证 HMAC
    uint8_t session_key[16];
    get_session_key(session_key);
    uint8_t calc_hash[4];
    simple_hmac(session_key, (uint8_t*)pkt, sizeof(*pkt) - 4, calc_hash);
    
    if (memcmp(calc_hash, pkt->hash, 4) != 0) {
        return -3;  // 数据被篡改
    }
    
    return 0;  // 验证通过
}</code></pre>
        </div>

        <h2>四、相关诉讼案例参考</h2>

        <div class="section">
            <h3>📋 案例一：以色列公司 vs 深圳某科技公司（MAC 软件侵权案）</h3>
            <p><strong>案情：</strong>以色列某公司拥有 WHDI 无线高清视频传输芯片的 MAC 软件著作权。深圳某公司从正规渠道购买了芯片后，将配套软件用于自己的产品，被原公司起诉侵权。</p>
            <p><strong>结果：</strong>一审原告胜诉，被告赔偿 30 万元 + 11 万维权费用。二审<strong>翻案，被告胜诉</strong>。</p>
            <p><strong>关键点：</strong></p>
            <ul>
                <li>芯片和软件"一并销售"被视为发行权用尽，购买者有权合理使用</li>
                <li>销售合同中对第三方的限制，不能约束合同外第三方</li>
                <li>软件与芯片"一对一绑定"关系，购买者获得的是所有权而非许可权</li>
            </ul>
            <div class="why-box">
                <div class="why-box-title">💡 启示</div>
                <p>单纯买芯片送软件的方式很难阻止二次销售和合理使用。需要在软件层面做<strong>加密绑定</strong>，使软件与产品唯一ID绑定才能运行。</p>
            </div>
        </div>

        <div class="section">
            <h3>📋 案例二：艾为电子 vs 芯海科技（IPO 期间专利战）</h3>
            <p><strong>案情：</strong>2021 年艾为电子 IPO 关键期，竞争对手芯海科技突然发起专利诉讼，指控其压力触控芯片侵权，向客户发律师函。</p>
            <p><strong>结果：</strong>影响 IPO 进程，最终双方和解。</p>
            <p><strong>关键点：</strong></p>
            <ul>
                <li>专利诉讼成为商业竞争武器，尤其瞄准 IPO 节点</li>
                <li>诉讼本身即使不赢，也能恶心对手</li>
            </ul>
        </div>

        <div class="section">
            <h3>📋 案例三：台积电 vs 中芯国际（商业秘密）</h3>
            <p><strong>案情：</strong>2003 年台积电指控中芯国际通过跳槽员工窃取商业秘密，侵犯多项专利。</p>
            <p><strong>结果：</strong>2005 年和解，中芯国际支付 1.75 亿美金。2006 年台积电再次起诉，中芯国际最终<strong>赔偿 2 亿美金 + 转让 8% 股份</strong>。</p>
            <p><strong>关键点：</strong></p>
            <ul>
                <li>商业秘密侵权证据难取证，但一旦认定赔偿极高</li>
                <li>员工竞业协议和保密协议是关键证据</li>
            </ul>
        </div>

        <div class="section">
            <h3>📋 案例四：芯片抄板侵权（行业潜规则）</h3>
            <p><strong>现状：</strong>国内存在完整产业链，抄板破解费用从几百到几千元不等。</p>
            <p><strong>维权难点：</strong></p>
            <ul>
                <li>证明"实质性相似"很难，需要专业鉴定</li>
                <li>证据保全困难，对方可以随时删除</li>
                <li>跨境维权成本极高</li>
            </ul>
            <div class="why-box">
                <div class="why-box-title">💡 建议</div>
                <p>如果发现被抄袭：<br>
                1. 第一时间公证购买对方产品<br>
                2. 申请法院证据保全<br>
                3. 委托专业机构进行技术鉴定<br>
                4. 同时从专利、著作权、商业秘密多条路径维权</p>
            </div>
        </div>

        <h2>五、总结</h2>
        <div class="section">
            <p>在资源受限的 Cortex-M0/M3 芯片上，无法使用复杂的加密方案，但可以通过<strong>多层叠加</strong>的方式提高破解成本：</p>
            <ul>
                <li><strong>基础层：</strong>读保护 + 加密存储 + 密钥分片</li>
                <li><strong>进阶层：</strong>代码混淆 + 反调试 + 时间随机化</li>
                <li><strong>高级层：</strong>动态解密 + 多级验证 + 轮换密钥</li>
            </ul>
            <p>目标是让破解成本高于产品利润，使抄袭者无利可图。</p>
        </div>