全部例程

硬件加密中实现DES / 3DES功能

W55MH32

更新于 2026年9月11日

本篇总结

使用硬件加密引擎,实现 DES/3DES 的 ECB/CBC 模式加解密,串口打印测试通过结果。

🔑 KEIL MDK、UART调试、DES、3DES

硬件平台:本例程基于 W55MH32L-EVB 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB

WIZnet W55MH32 集成硬件加密引擎,支持多种对称加密算法,包括 DES 和 3DES。通过硬件加速,W55MH32 可高效执行加密任务,显著减轻主 CPU 运算负担,提升系统数据安全性。本示例演示如何调用硬件引擎完成 DES / 3DES 的 ECB/CBC 模式加解密。

前置工具准备

开始本例程前,请确认以下工具已安装:

硬件连接

加密引擎为片内外设,无需额外连线。结果通过 USART1 串口打印,USART1 的 TX(PA9) 和 RX(PA10) 通过板载 USB 转串口芯片连接到 DEBUG USB 口。仅需一根 Micro USB 线连接开发板 DEBUG USB 口到 PC 即可。

引脚功能方向模式
PA9USART1_TX开发板 → PC复用推挽输出 (AF_PP)
PA10USART1_RXPC → 开发板浮空输入 (IN_FLOATING)

工程结构

工程位于 SDK 内 1.SDK\ModuleDemo\ 下硬件加密例程目录,核心文件如下:

硬件加密例程/
├── USER/
│   ├── main.c            ← 主流程:时钟/USART 初始化 + 调用 DES/3DES 测试
│   ├── debug.c/h         ← DES_Func_Test() / TDES_Func_Test() 实现
│   ├── w55mh32_conf.h    ← 外设头文件集中包含
│   ├── w55mh32_it.c/h    ← 中断服务函数
│   ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│   └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
    └── delay/
        └── delay.c/h      ← SysTick 延时函数

DES/3DES 简介

DES(数据加密标准)是一种对称加密算法,以 64 位数据块为单位加密,每个块用 56 位有效密钥(64 位中 8 位为奇偶校验)处理。由于密钥较短,DES 已易受暴力破解,不再推荐用于新应用。
3DES(三重 DES)对每个数据块执行三次 DES 运算(加密-解密-加密),可使用 2 组(112 位)或 3 组(168 位)独立密钥,安全性显著提升,约为 DES 速度的 1/3。

特性DES3DES
密钥长度56 位有效112 位(2 密钥)/ 168 位(3 密钥)
加密过程单次 DES三次 DES 运算(加密-解密-加密)
安全性易受暴力破解显著更高(但不如 AES)
速度快约为 DES 的 1/3
适用场景仅兼容性金融、老旧系统兼容

ECB(电子密码本)模式中每个数据块独立加密,相同明文块生成相同密文块,易暴露数据模式,简单且支持并行,不推荐用于敏感数据。
CBC(密码块链接)模式中每个明文块加密前先与前一个密文块(首块与 IV)异或,即使明文重复密文也不同,更安全,但块间存在依赖。

操作流程

初始化加密引擎 → 选择算法(DES/3DES)与块模式(ECB/CBC)→ 配置密钥及 IV(CBC 需要)→ 按 64 位(8 字节)分块输入数据 → 引擎输出加/解密结果。

步骤1:包含头文件

为激活 DES 功能,需包含 WIZnet 加密库中的 DES 头文件及相关依赖。

#include "debug.h"
#include "w55mh32.h"
#include "wiz_des.h"
#include "wiz_rand.h"
#include <stdio.h>
#include <string.h>

步骤2:配置加密结构体

将所有参数填入 WIZ_SYM_CRYPT_CALL 结构体后调用引擎执行。ECB 与 CBC 操作相同,CBC 仅需额外传入 IV。

/* 将所有变量传入 callDes 结构体 */
callDes.pu8In     = au8Plain;                 // 输入数据
callDes.u32InLen  = DES_TEST_LEN;             // 输入长度(8 字节倍数)
callDes.pu8Out    = au8Result;                // 输出数据
callDes.u32OutLen = DES_TEST_LEN;             // 输出长度
callDes.u16Opt    = WIZ_DES_OPT_BLK_ECB | WIZ_DES_OPT_MODE_ENCRYPT;  // ECB 加密
callDes.pu8Key    = au8Key;                   // 密钥
callDes.u32Crc    = WIZCRC_CalcBuff(0xffff, &callDes, sizeof(WIZ_SYM_CRYPT_CALL) - 4);
WIZDES_EncDec(&callDes);                     // 运行硬件 DES

/* CBC 解密需额外配置 IV */
callDes.pu8IV  = au8Iv;
callDes.u16Opt = WIZ_DES_OPT_BLK_CBC | WIZ_DES_OPT_MODE_DECRYPT;

/* 3DES 加密(ECB,3 组密钥)*/
callTdes.u16Opt = WIZ_DES_OPT_BLK_ECB | WIZ_DES_OPT_MODE_ENCRYPT | WIZ_TDES_OPT_KEY_3;

如果你想自定义

  • 切换 ECB / CBC → 修改 u16Opt 的块模式位:WIZ_DES_OPT_BLK_ECB 或 WIZ_DES_OPT_BLK_CBC
  • 切换加密 / 解密 → 修改模式位:WIZ_DES_OPT_MODE_ENCRYPT 或 WIZ_DES_OPT_MODE_DECRYPT
  • 3DES 密钥组数 → 通过 WIZ_TDES_OPT_KEY_3 选择 3 组密钥(24 字节)

步骤3:DES 加解密主代码

文件:USER/debug.c | 函数:DES_Func_Test() — 演示 DES 的 ECB 加密与 CBC 解密:ECB 加密后用预设密文比对,CBC 解密后用原文比对,通过 r_printf() 打印测试结果。

#define DES_TEST_LEN (64)

void DES_Func_Test()
{
    uint8_t au8Plain[DES_TEST_LEN] = {0x01, 0x02, ..., 0x40};
    uint8_t au8DESCipher_ECB[DES_TEST_LEN] = {0x77, 0xA7, ..., 0x04};
    uint8_t au8DESCipher_CBC[DES_TEST_LEN] = {0xB0, 0x73, ..., 0xD5};
    uint8_t au8Result[DES_TEST_LEN] = {0};
    uint8_t au8Iv[8]  = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
    uint8_t au8Key[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
    WIZ_SYM_CRYPT_CALL callDes;

    memset(&callDes, 0, sizeof(callDes));

    // ECB ENC
    callDes.pu8In    = au8Plain;
    callDes.u32InLen = DES_TEST_LEN;
    callDes.pu8Out   = au8Result;
    callDes.u32OutLen= DES_TEST_LEN;
    callDes.u16Opt   = WIZ_DES_OPT_BLK_ECB | WIZ_DES_OPT_MODE_ENCRYPT;
    callDes.pu8Key   = au8Key;
    callDes.u32Crc   = WIZCRC_CalcBuff(0xffff, &callDes, sizeof(WIZ_SYM_CRYPT_CALL) - 4);
    WIZDES_EncDec(&callDes);
    r_printf((0 == memcmp(au8DESCipher_ECB, au8Result, DES_TEST_LEN)), "wiz_des_ecb test\n");

    // CBC DEC
    callDes.pu8In    = au8DESCipher_CBC;
    callDes.u32InLen = DES_TEST_LEN;
    callDes.pu8Out   = au8Result;
    callDes.u32OutLen= DES_TEST_LEN;
    callDes.pu8IV    = au8Iv;
    callDes.u16Opt   = WIZ_DES_OPT_BLK_CBC | WIZ_DES_OPT_MODE_DECRYPT;
    callDes.pu8Key   = au8Key;
    callDes.u32Crc   = WIZCRC_CalcBuff(0xffff, &callDes, sizeof(WIZ_SYM_CRYPT_CALL) - 4);
    WIZDES_EncDec(&callDes);
    r_printf((0 == memcmp(au8Plain, au8Result, DES_TEST_LEN)), "wiz_des_cbc test\n");
}

void DES_Test() { DES_Func_Test(); }

关键点:u32Crc 字段需用 WIZCRC_CalcBuff() 对结构体(除最后 4 字节 CRC 自身)计算,引擎据此校验配置完整性,缺失会导致执行失败。

步骤4:3DES 加解密主代码

文件:USER/debug.c | 函数:TDES_Func_Test() — 与 DES 流程一致,区别在于密钥 24 字节(3 组)、选项加 WIZ_TDES_OPT_KEY_3、调用 WIZTDES_EncDec()。

void TDES_Func_Test()
{
    uint8_t au8Plain[DES_TEST_LEN] = {0x01, 0x02, ..., 0x40};
    uint8_t au8TDESCipher_ECB[DES_TEST_LEN] = {0x7D, 0x78, ..., 0xD2};
    uint8_t au8TDESCipher_CBC[DES_TEST_LEN] = {0xAD, 0x17, ..., 0xD4};
    uint8_t au8Result[DES_TEST_LEN] = {0};
    uint8_t au8Key[24] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                          0x09, 0x0A, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
                          0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
    uint8_t au8Iv[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
    WIZ_SYM_CRYPT_CALL callTdes;

    memset(&callTdes, 0, sizeof(callTdes));

    // ECB ENC
    callTdes.pu8In    = au8Plain;
    callTdes.u32InLen = DES_TEST_LEN;
    callTdes.pu8Out   = au8Result;
    callTdes.u32OutLen= DES_TEST_LEN;
    callTdes.u16Opt   = WIZ_DES_OPT_BLK_ECB | WIZ_DES_OPT_MODE_ENCRYPT | WIZ_TDES_OPT_KEY_3;
    callTdes.pu8Key   = au8Key;
    callTdes.u32Crc   = WIZCRC_CalcBuff(0xffff, &callTdes, sizeof(WIZ_SYM_CRYPT_CALL) - 4);
    WIZTDES_EncDec(&callTdes);
    r_printf((0 == memcmp(au8TDESCipher_ECB, au8Result, DES_TEST_LEN)), "wiz_tdes_ecb test\n");

    // CBC DEC
    callTdes.pu8In    = au8TDESCipher_CBC;
    callTdes.u32InLen = DES_TEST_LEN;
    callTdes.pu8Out   = au8Result;
    callTdes.u32OutLen= DES_TEST_LEN;
    callTdes.pu8IV    = au8Iv;
    callTdes.u16Opt   = WIZ_DES_OPT_BLK_CBC | WIZ_DES_OPT_MODE_DECRYPT | WIZ_TDES_OPT_KEY_3;
    callTdes.pu8Key   = au8Key;
    callTdes.u32Crc   = WIZCRC_CalcBuff(0xffff, &callTdes, sizeof(WIZ_SYM_CRYPT_CALL) - 4);
    WIZTDES_EncDec(&callTdes);
    r_printf((0 == memcmp(au8Plain, au8Result, DES_TEST_LEN)), "wiz_tdes_cbc test\n");
}

void TDES_Test() { TDES_Func_Test(); }

步骤5:主函数调用

文件:USER/main.c | 函数:main() — 配置系统时钟 72MHz、初始化 USART1、打印加密库版本,然后依次调用 DES_Test() 与 TDES_Test() 执行测试。

void main(int args)
{
    uint32_t ver = 1;
    RCC_ClocksTypeDef clocks;

    RCC_DeInit();
    RCC_HSEConfig(RCC_HSE_ON);
    RCC_WaitForHSEStartUp();

    RCC_PLLCmd(DISABLE);
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);  // 8MHz × 9 = 72MHz
    RCC_PLLCmd(ENABLE);
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    RCC_HCLKConfig(RCC_SYSCLK_Div1);
    RCC_PCLK1Config(RCC_HCLK_Div2);
    RCC_PCLK2Config(RCC_HCLK_Div1);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    SysTick->CTRL |= 0x04;

    /* 使能加密引擎时钟/配置寄存器 */
    *(uint32_t *)(0x400210F0) = 0x01;
    *(uint32_t *)(0x40016C00) = 0xCDED3526;
    *(uint32_t *)(0x40016CCC) = 0x07;

    UART_Configuration(115200);
    WIZRAND_Init((uint32_t *)&u8RngBuff, sizeof(u8RngBuff));

    ver = wiz_crypt_version();
    DBG_PRINT("WIZnet CARD Secure Test Demo V1.0, secure lib version is V%02x.%02x.%02x.%02x\n",
              ver >> 24, (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
    DBG_PRINT("WIZnet CARD Crypt Test V1.0 start......\r\n");

    DES_Test();
    TDES_Test();

    // AES_Test();
    // SHA_Test();
    // SM1_Test();
    // SM3_Test();
    // SM4_Test();
    // SM7_Test();

    DBG_PRINT("DES/3DES test finished, program halted.\r\n");

    while (1) {}
}

注意:调用加密引擎前必须执行那三行寄存器写入(0x400210F0、0x40016C00、0x40016CCC),这是 W55MH32 加密引擎的时钟与配置寄存器初始化,缺失会导致加密结果全 0。

运行验证

打开 WIZ UartTool V1.0,选择开发板对应 COM 口,波特率 115200 / 8N1,打开串口。程序运行后串口依次打印库版本、测试开始,以及四项测试的通过结果。预期输出如下:

WIZnet CARD Secure Test Demo V1.0, secure lib version is V03.01.04.00
WIZnet CARD Crypt Test V1.0 start......

Pass wiz_des_ecb test
Pass wiz_des_cbc test
Pass wiz_tdes_ecb test
Pass wiz_tdes_cbc test
DES/3DES test finished, program halted.
WIZ UartTool 串口输出 — DES/3DES 硬件加密测试结果

四项测试全部通过(Pass)表示硬件加密引擎计算的密文/明文与预设测试向量完全一致,DES 与 3DES 的 ECB/CBC 模式加解密功能正常。

常见问题

Q: 编译报错 "cannot open source file wiz_des.h"

检查 Project → Options → C/C++ → Include Paths,确保 WIZnet 加密库的头文件目录已添加。

Q: 加密结果全 0 或测试不通过(Fail)

检查两点:① 加密引擎初始化那三行寄存器写入是否执行(0x400210F0 / 0x40016C00 / 0x40016CCC);② u32Crc 字段是否用 WIZCRC_CalcBuff(0xffff, &callDes, sizeof(...) - 4) 正确计算。

Q: CBC 模式解密结果与原文不符

CBC 链式依赖 IV,解密时 pu8IV 必须与加密时完全一致;且首块需先与前一块密文异或。确认 IV 未被改动。

Q: 数据长度非 8 字节倍数时异常

DES/3DES 按 64 位(8 字节)块处理,输入长度必须是 8 的整数倍。本示例用 DES_TEST_LEN = 64,正好是 8 的整数倍。

Q: 3DES 密钥长度报错

3 密钥模式(WIZ_TDES_OPT_KEY_3)需 24 字节密钥;2 密钥模式为 16 字节。确认 pu8Key 缓冲区长度与选项匹配。

总结

本文基于 W55MH32 硬件加密引擎实现了 DES / 3DES 的加解密:

  • 硬件加速:DES/3DES 运算由片内引擎完成,主 CPU 零参与,显著减轻运算负担
  • 双模式支持:ECB 简单适合非敏感数据,CBC 链式更安全;通过 u16Opt 选项位切换
  • 结构体配置:输入/输出/密钥/IV/模式集中填入 WIZ_SYM_CRYPT_CALL,配合 CRC 校验后一次调用引擎
  • 选型建议:DES 已不安全,3DES 用于兼容场景,新设计优先选 AES(W55MH32 同样支持硬件 AES)

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5 配套开发板

Andy

W5500 技术支持专家