C++文件加密:简介

文件加密

一、概述

文件加密是保护数据安全的核心手段,根据加密对象(整个文件/文件内容)、密钥管理方式(对称/非对称)和应用场景(本地存储/传输),主要有以下解决方案,各有适用场景和优缺点:

1.1 基于对称加密的文件加密(最常用)

核心原理:用同一密钥加密和解密文件,速度快,适合大文件。
典型算法:AES(推荐,支持128/256位密钥)、ChaCha20(适合嵌入式设备)。

1. 整体文件加密(加密整个文件)

  • 实现方式
    将文件二进制数据整体加密(按块处理,结合CBC/CTR/GCM模式),生成加密后的文件,解密时还原。
    例:用AES-256-GCM加密document.pdf,生成document.pdf.enc,需密钥和解密工具还原。

  • 工具/库

    • 命令行工具:openssl enc(如 openssl aes-256-gcm -in file.txt -out file.enc)、gpg(支持对称加密模式)。
    • 编程库:OpenSSL(C/C++)、PyCryptodome(Python)、mbedtls(嵌入式)。
  • 优点:实现简单,加密彻底,适合本地文件存储(如个人文档、备份文件)。

  • 缺点:密钥管理复杂(需安全保存密钥,丢失则文件永久无法解密)。

2. 文件系统级加密(透明加密)

  • 核心特点:操作系统或驱动层自动加密文件,用户无感知(像操作普通文件一样)。

  • 实现方式

    • 加密整个分区(如Windows BitLocker、macOS FileVault、Linux LUKS)。
    • 加密指定文件夹(如EFS加密文件系统、第三方工具VeraCrypt)。
  • 优点:透明化操作(无需手动加密解密),适合保护整个硬盘/分区数据(如笔记本丢失时防数据泄露)。

  • 缺点:依赖操作系统,跨平台兼容性差(如BitLocker加密的分区在Linux下需特殊工具访问)。

1.2 基于非对称加密的文件加密(适合密钥交换场景)

核心原理:用公钥加密、私钥解密(或反之),无需传输对称密钥,适合多用户/跨网络场景。
典型算法:RSA(2048位以上)、ECC(椭圆曲线加密,如secp256r1)。

1. 混合加密方案(主流)

  • 实现逻辑

    1. 生成临时对称密钥(如AES-256密钥),用对称加密加密文件(速度快)。
    2. 用接收方的公钥加密临时对称密钥(安全性高)。
    3. 传输“加密后的文件 + 加密后的临时密钥”,接收方用私钥解密临时密钥,再解密文件。
  • 工具/场景

    • 邮件附件加密(如GnuPG)、云存储加密(如加密后上传至网盘)。
    • 企业内部文件共享(管理员用员工公钥加密,员工用私钥解密)。
  • 优点:解决对称加密的密钥传输问题,适合多用户场景。

  • 缺点:实现复杂,非对称加密速度慢(不适合直接加密大文件)。

1.3 应用层文件格式加密(特定格式保护)

核心原理:针对特定文件格式(如文档、图片、视频),在格式内部嵌入加密逻辑,需专用软件解密。

1. 文档加密(如PDF、Office)

  • 实现方式

    • PDF:通过Adobe Acrobat设置打开密码(AES-256加密),或限制编辑/打印权限。
    • Office:Word/Excel的“加密文档”功能(默认AES-128加密)。
  • 优点:与文件格式深度集成,支持权限控制(如只读、禁止复制)。

  • 缺点:依赖特定软件,破解工具较多(弱密码易被暴力破解)。

2. 媒体文件加密(视频、图片)

  • 实现方式

    • 视频:DRM(数字版权管理,如Widevine、FairPlay),加密视频内容+授权播放(需密钥和许可证)。
    • 图片:隐写术(将文件加密后嵌入图片像素中,表面看是普通图片)。
  • 应用场景:付费视频平台(如Netflix)、企业敏感图片保护。

  • 优点:隐蔽性强(DRM还能控制播放设备/时效)。

  • 缺点:DRM依赖特定播放器,兼容性差;隐写术可能降低图片质量。

1.4 开源工具与解决方案推荐

工具/方案核心技术适用场景优点缺点
VeraCryptAES-256/XTS模式分区/容器加密开源免费,支持多平台,防暴力破解手动操作,不适合普通用户
OpenSSLAES/GCM/RSA混合加密编程实现文件加密灵活可控,适合开发者集成需手动处理密钥管理和错误逻辑
GnuPG(GPG)混合加密(AES+RSA/ECC)跨平台文件加密/签名支持非对称加密,适合多用户共享命令行操作复杂,学习成本高
7-ZipAES-256压缩包加密(如.zip/.7z)简单易用,支持密码保护压缩包仅加密压缩包,不适合单独文件加密
BitLockerAES-128/256+TPM芯片Windows系统分区加密系统级集成,透明加密,安全性高仅限Windows专业版,兼容性差

1.5 选型建议(按场景)

  1. 个人本地文件加密

    • 简单需求:用7-Zip加密压缩包(设置强密码)。
    • 高安全需求:VeraCrypt创建加密容器(存敏感文件)。
  2. 企业文件共享

    • 内部共享:GPG混合加密(用接收方公钥加密,确保只有指定人解密)。
    • 全公司数据保护:文件系统级加密(如LUKS+密钥管理服务器)。
  3. 跨网络文件传输

    • 小文件:直接用GPG非对称加密。
    • 大文件:AES加密文件 + RSA加密AES密钥(混合方案)。
  4. 媒体/文档版权保护

    • 文档:PDF设置AES密码+权限控制。
    • 视频:集成DRM方案(如Widevine)+ 时效限制。

二、加密算法(AES)

AES(Advanced Encryption Standard,高级加密标准)是美国国家标准与技术研究院(NIST)于2001年确定的对称加密算法,旨在替代安全性不足的DES算法,目前是全球应用最广泛的加密标准之一,广泛用于数据存储、网络传输、支付系统等场景。

2.1 AES 核心定义与本质

AES 本质是一种 分组密码算法(Block Cipher),即每次处理固定长度的数据块(称为“分组”),并通过对称密钥(加密和解密使用同一密钥)实现数据转换。其核心特征可概括为:

  • 对称加密:加密密钥 = 解密密钥(需安全保管和传输,是安全的核心)。
  • 固定分组长度:无论密钥长度如何,分组大小固定为 128位(16字节)(即每次处理16字节数据)。
  • 可变密钥长度:支持 128位、192位、256位 三种密钥长度(对应密钥字节数为 16/24/32 字节),密钥越长,安全性越高(256位密钥目前无实用破解方法)。

2.2 AES 加密核心流程(以128位密钥为例)

AES 加密过程是对 128位分组的多轮“变换操作”,轮数由密钥长度决定:

  • 128位密钥:10轮变换
  • 192位密钥:12轮变换
  • 256位密钥:14轮变换

每轮变换包含4个核心步骤(最后一轮少1个步骤),整体流程可简化为:

  1. 初始轮密钥加(AddRoundKey)
    将128位分组与初始轮密钥(从原始密钥扩展而来)按位进行“异或(XOR)”操作,将密钥信息融入数据。

  2. 轮函数(多轮重复,以128位密钥为例共9轮)
    每轮包含3个步骤,实现数据的“混淆”(掩盖数据与密钥的关联)和“扩散”(让数据变化影响更多位):

    • 字节代换(SubBytes):通过固定的“S盒”(16×16的查找表),将分组中每个字节替换为另一个字节,打破数据的规律性。
    • 行移位(ShiftRows):对分组的每行字节进行循环移位(如第2行移1位、第3行移2位),打乱字节的位置。
    • 列混合(MixColumns):对分组的每列字节进行线性变换,让每列4个字节相互影响,实现数据扩散。
    • 轮密钥加(AddRoundKey):再次与当前轮的扩展密钥进行异或,更新数据与密钥的融合状态。
  3. 最终轮(第10轮)
    省略“列混合”步骤,仅执行“字节代换→行移位→轮密钥加”,确保最终输出的密文与初始数据差异最大化。

2.3 AES 工作模式(解决“长数据加密”问题)

AES 本身是分组密码,只能处理16字节数据,为加密任意长度的数据,需结合“工作模式”(Mode of Operation)。常见模式及特点如下:

工作模式核心原理安全性适用场景
ECB(电子密码本模式)将长数据拆分为16字节分组,每个分组独立用AES加密低(相同明文分组生成相同密文,易被破解)仅用于加密单个16字节数据(如密钥),禁止用于敏感数据
CBC(密码分组链接模式)前一个分组的密文与当前分组的明文异或后,再用AES加密;需16字节随机IV(初始化向量)中(解决ECB的重复问题,但IV需随机)文件加密、数据存储(如本地配置加密)
CTR(计数器模式)生成一个“计数器”,对计数器加密后与明文异或;需初始计数器(可含IV)高(流密码特性,支持并行加密,无填充需求)实时数据传输(如视频流、IoT设备通信)
GCM(伽罗瓦/计数器模式)在CTR基础上增加“消息认证码(MAC)”,同时保证加密和数据完整性极高(防篡改、防重放,安全性最优)网络传输(如HTTPS、VPN)、支付系统

关键注意:除ECB外,其他模式均需“初始向量(IV)”或“初始计数器”,且需满足:

  • IV/初始计数器需随机生成(每次加密用新值),但无需保密(可随密文一同传输);
  • 同一密钥下,IV/初始计数器不可重复(重复会导致密文安全性崩溃)。

2.4 AES 数据填充(解决“分组长度不匹配”问题)

若待加密数据长度不是16字节的整数倍(如18字节),需在末尾“填充”字节至16字节倍数,解密时再“去填充”。常用填充标准为 PKCS#7(应用最广泛):

  • 填充规则:若需填充 n 个字节(n 范围1~16),则填充 n 个值为 n 的字节。
    示例:数据长度20字节(需填充12字节),则填充12个 0x0C 字节。
  • 优势:解密时可通过最后一个字节的值直接确定填充长度,无需额外标记。

2.5 AES 安全性与应用场景

1. 安全性

  • 抗破解能力:128位密钥的AES,理论破解需尝试 (2^{128}) 种密钥组合,当前算力下完全不可行;256位密钥安全性更高,可抵御未来数十年的算力增长。
  • 标准化保障:AES经过全球密码学家的长期验证,无已知“有效攻击方法”(仅存在理论上的差分攻击,无实用价值)。
  • 安全风险点:风险不来自算法本身,而来自“密钥管理”(如密钥泄露、硬编码)、“模式选择”(如用ECB)或“IV重复”。

2. 典型应用场景

  • 数据存储:本地文件加密(如压缩包密码、磁盘加密)、数据库敏感字段加密(如手机号、银行卡号)。
  • 网络传输:HTTPS/TLS协议(浏览器与服务器通信加密)、VPN隧道加密、IoT设备数据传输(如智能家居通信)。
  • 身份认证:加密Cookie、Token(如JWT中的加密字段)、硬件设备身份验证(如USB密钥)。

2.6 AES 与其他加密算法对比

对比维度AES(对称加密)RSA(非对称加密)DES(对称加密,已淘汰)
密钥类型对称(1个密钥)非对称(公钥+私钥)对称(1个密钥)
速度极快(适合大数据)慢(仅适合小数据,如加密密钥)慢(分组小、轮数少但算法落后)
密钥长度128/192/256位2048/4096位(需更长才安全)56位(安全性不足)
适用场景大数据加密(文件、流)密钥传输、数字签名无(已被AES替代)

典型组合:实际应用中常“对称+非对称”结合,如HTTPS:用RSA传输AES密钥,再用AES加密后续大量通信数据(兼顾安全性和速度)。

AES是目前最安全、最高效、最通用的加密标准,核心优势在于“对称加密的高速性”和“强抗破解能力”。使用时需注意:选择GCM/CTR/CBC模式(避免ECB)、确保密钥随机且安全存储、IV每次加密随机生成,即可最大化其安全性。

三、 AES 关键参数与应用场景

以下清单覆盖 AES 加密的核心参数(密钥长度、工作模式、填充方式),结合不同应用场景给出最优配置方案,同时标注风险点和注意事项,可直接用于开发中的参数选型。

3.1 核心参数定义与选型标准

1. 密钥长度(Key Length)

密钥长度字节数安全等级性能消耗适用场景风险提示
128位16高(当前完全安全,可抵御绝大多数攻击)低(轮数10轮,速度最快)对性能敏感的场景(如 IoT 设备、实时视频流加密、移动端应用)无已知实用破解方法,适合99%的非顶级敏感场景
192位24极高中(轮数12轮,性能比128位低约15%)对安全性要求略高,但不希望性能损耗过大的场景(如企业级数据存储)兼容性略差(部分嵌入式加密芯片仅支持128/256位)
256位32顶级(可抵御未来10+年算力增长,符合军工/金融级安全要求)中高(轮数14轮,性能比128位低约30%)顶级敏感数据(如支付密码、身份证信息、军事通信数据)需确保硬件支持(如老旧CPU可能无AES-NI指令集优化)

选型原则

  • 优先选 128位(平衡安全与性能,满足绝大多数场景);
  • 仅当数据属于“顶级敏感”(如金融核心数据)时,才选 256位
  • 192位性价比最低,除非有强制合规要求(如某些行业标准),否则不推荐。

2. 工作模式(Mode of Operation)

工作模式核心特性安全性并行加密填充需求适用场景风险提示
ECB(电子密码本模式)每个16字节分组独立加密,相同明文分组生成相同密文极低(易被统计分析攻击,可还原明文结构)支持(分组间无依赖)需填充禁止用于任何敏感数据!仅允许用于“加密单个16字节固定值”(如硬件密钥标识)绝对禁止用于文件、传输数据等长数据,属于“安全红线”
CBC(密码分组链接模式)前一分组密文与当前分组明文异或后加密,需16字节IV中(解决ECB重复问题,但不支持并行加密)不支持(分组依赖前一分组结果)需填充本地文件加密(如日志文件、配置文件)、非实时数据存储1. IV必须随机生成(每次加密用新IV);2. 无完整性校验(需额外加HMAC)
CTR(计数器模式)生成“计数器+IV”的加密流,与明文异或(流密码特性)高(无分组依赖,支持并行,无填充需求)支持(加密/解密可并行处理)无需填充(流密码特性,可处理任意长度数据)实时数据传输(如视频流、IoT设备实时通信)、大文件分块加密1. 计数器+IV组合必须唯一(同一密钥下不可重复);2. 无完整性校验(需额外加HMAC)
GCM(伽罗瓦/计数器模式)基于CTR,增加“消息认证码(MAC)”,同时实现加密+完整性校验极高(防篡改、防重放,安全性最优)支持(加密/解密/MAC计算可并行)无需填充网络传输(如HTTPS/TLS、VPN)、支付系统、云数据传输1. IV建议12字节(兼顾安全与性能);2. MAC值需与密文一同传输(解密时验证)

选型原则

  • 网络传输/支付场景:强制选 GCM(唯一同时保障加密和完整性的模式);
  • 实时流/大文件场景:选 CTR(无填充、支持并行,性能最优);
  • 本地存储(非实时):选 CBC(实现简单,兼容性好);
  • 任何场景禁止选 ECB(无论数据是否敏感)。

3. 填充方式(Padding Scheme)

仅当工作模式为 CBC/ECB 时需填充(CTR/GCM为流密码,无需填充),常用填充方式对比:

填充方式规则安全性兼容性适用场景
PKCS#7需填充n字节(n=1~16),填充值均为n(如缺3字节则填充3个0x03)高(可明确识别填充长度,无歧义)极高(所有主流加密库均支持)推荐首选,适用于所有CBC/ECB场景(如文件加密、数据存储)
PKCS#5仅支持8字节分组(AES为16字节,需扩展后使用),填充规则同PKCS#7中(部分老旧库仅支持PKCS#5)仅用于需兼容老旧系统的场景(如传统金融终端)
Zero Padding填充0x00字节,缺多少填多少低(无法区分“真实0x00”和“填充0x00”,易导致解密错误)禁止用于文本/二进制数据,仅允许用于“明文末尾无0x00”的特殊场景(如固定长度指令)

选型原则

  • 优先选 PKCS#7(无歧义、兼容性最好,是行业标准);
  • 仅当需兼容老旧系统时,才选 PKCS#5(需确保扩展为16字节分组);
  • 任何场景禁止选 Zero Padding(易引发解密数据截断错误)。

3.2 场景化最优配置方案

1. 场景1:HTTPS/TLS 网络传输

参数配置理由
密钥长度256位网络传输风险高,需顶级安全(TLS 1.3默认推荐256位)
工作模式GCM必须保障加密+完整性,防止中间人篡改
填充方式无需填充GCM为流密码,无分组长度限制
额外要求IV=12字节,MAC值(16字节)与密文绑定传输12字节IV兼顾安全与性能,MAC验证防篡改

2. 场景2:IoT设备实时数据采集(如传感器数据)

参数配置理由
密钥长度128位IoT设备算力有限,128位足够安全且性能最优
工作模式CTR传感器数据为实时流(任意长度),无需填充,支持并行处理
填充方式无需填充CTR模式特性,避免填充开销
额外要求IV=16字节(随机生成,每次采集会话重置)确保同一密钥下IV不重复,避免流密码碰撞

3. 场景3:本地文件加密(如用户敏感文档)

参数配置理由
密钥长度128位本地存储风险低于网络,128位足够安全
工作模式CBC文件读写非实时,CBC实现简单且兼容性好
填充方式PKCS#7无歧义,支持任意文件类型(文本/二进制)
额外要求IV=16字节(随机生成,存储在文件头部)解密时需读取文件头部IV,确保每次加密用新IV

4. 场景4:金融支付密码加密(如银行卡PIN码)

参数配置理由
密钥长度256位支付数据属顶级敏感,需最高安全等级
工作模式GCM防止PIN码被篡改,MAC验证确保数据完整性
填充方式无需填充GCM模式无需填充,避免PIN码长度变化
额外要求密钥存储在硬件加密模块(HSM)中,禁止软件存储防止密钥泄露,符合金融行业合规要求

3.3 风险点与避坑指南

  1. 密钥管理风险

    • 禁止硬编码密钥(如写在代码中、配置文件明文存储);
    • 推荐存储方式:硬件加密模块(HSM)、可信执行环境(TEE)、系统密钥管理服务(如Windows KMS、AWS KMS)。
  2. IV/计数器风险

    • CBC/GCM/CTR模式:IV必须随机生成(用密码学安全随机数生成器,如OpenSSL的RAND_bytes、mbedtls的mbedtls_ctr_drbg_random);
    • 同一密钥下,IV/计数器绝对不可重复(重复会导致密文被破解,尤其是CTR/GCM模式)。
  3. 完整性校验缺失风险

    • CBC/CTR模式:需额外添加HMAC-SHA256(将密文+IV的哈希值与密文一同传输,解密时验证);
    • 禁止仅依赖加密(无完整性校验),否则数据被篡改后无法发现。

四、mbedtls 库

4.1 mbedtls 简介

mbedtls(原名PolarSSL)是一款轻量级、可移植的加密库,由ARM公司维护,专注于嵌入式系统和资源受限环境。与OpenSSL等重型库相比,它以代码精简、内存占用低、模块化设计为核心优势,广泛应用于物联网(IoT)、嵌入式设备、工业控制系统等场景。

  1. 轻量级设计

    • 代码量仅约100KB(按需裁剪后可更小),远小于OpenSSL(数MB级)。
    • 内存占用低(最小运行时内存可至数KB),适合MCU等资源受限设备。
  2. 高可移植性

    • 支持多种架构:ARM、x86、RISC-V、MIPS等。
    • 兼容多种环境:Linux、Windows、FreeRTOS等操作系统,甚至裸机(无OS)系统。
  3. 模块化与可裁剪

    • 功能按模块划分(如aes.csha256.cssl.c),可通过配置文件(mbedtls_config.h)禁用不需要的模块(如仅保留AES算法)。
  4. 安全合规

    • 支持主流加密标准:AES、RSA、ECC、SHA系列、TLS 1.3等。
    • 定期更新安全补丁,通过多项安全认证(如FIPS 140-2)。
  5. 易用性

    • API设计简洁(如mbedtls_aes_setkey_enc初始化AES加密密钥)。
    • 文档完善,提供丰富示例代码。

4.2 mbedtls 核心模块与功能

mbedtls的功能按模块划分,核心模块包括:

模块类别主要功能关键API示例
对称加密AES(CBC/CTR/GCM模式)、DES、ChaCha20等mbedtls_aes_crypt_cbc(AES-CBC加密)
非对称加密RSA、ECC(椭圆曲线加密)、DSA等mbedtls_rsa_pkcs1_encrypt(RSA加密)
哈希算法SHA-1、SHA-256、SHA-512、MD5(不推荐)等mbedtls_sha256_update(SHA-256更新)
TLS/SSL支持TLS 1.0~1.3,包含证书验证、密钥交换等mbedtls_ssl_handshake(TLS握手)
密钥派生PBKDF2、HKDF等(将密码转换为加密密钥)mbedtls_pbkdf2_hmac
随机数生成密码学安全随机数生成(CSRNG)mbedtls_ctr_drbg_random

4.3 mbedtls 安装与基础使用

1. 安装(以Linux为例)

  • 包管理器安装(适合快速测试):

    sudo apt-get install libmbedtls-dev  # Ubuntu/Debian
    
  • 源码编译(适合嵌入式或定制化需求):

    # 下载源码
    git clone https://github.com/Mbed-TLS/mbedtls.git
    cd mbedtls
    git checkout v3.5.1  # 选择稳定版本
    
    # 配置(按需裁剪模块,默认全量编译)
    cmake -DCMAKE_INSTALL_PREFIX=/usr/local .
    
    # 编译安装
    make -j4
    sudo make install
    

2. 基础使用示例(AES-256-CBC加密解密)

以下是使用mbedtls实现AES加密解密的完整代码,包含密钥生成、IV处理、PKCS#7填充等核心步骤:

#include "mbedtls/aes.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/entropy.h"
#include "mbedtls/pkcs5.h"
#include <iostream>
#include <vector>
#include <string>
#include <cstring>

// 错误处理函数
void handle_error(int ret, const std::string& msg) {
    if (ret != 0) {
        char err_buf[1024];
        mbedtls_strerror(ret, err_buf, sizeof(err_buf));
        std::cerr << msg << " (错误码: " << ret << "): " << err_buf << std::endl;
        exit(1);
    }
}

// 生成随机密钥(256位 = 32字节)
std::vector<unsigned char> generate_aes_key(mbedtls_ctr_drbg_context& ctr_drbg) {
    std::vector<unsigned char> key(32);  // 256位密钥
    int ret = mbedtls_ctr_drbg_random(&ctr_drbg, key.data(), key.size());
    handle_error(ret, "生成密钥失败");
    return key;
}

// 生成随机IV(16字节,AES块大小)
std::vector<unsigned char> generate_iv(mbedtls_ctr_drbg_context& ctr_drbg) {
    std::vector<unsigned char> iv(16);  // AES块大小固定为16字节
    int ret = mbedtls_ctr_drbg_random(&ctr_drbg, iv.data(), iv.size());
    handle_error(ret, "生成IV失败");
    return iv;
}

// PKCS#7填充(让数据长度为16字节的整数倍)
std::vector<unsigned char> pkcs7_pad(const std::vector<unsigned char>& data) {
    size_t block_size = 16;
    size_t pad_len = block_size - (data.size() % block_size);
    std::vector<unsigned char> padded = data;
    padded.insert(padded.end(), pad_len, static_cast<unsigned char>(pad_len));
    return padded;
}

// PKCS#7去填充
std::vector<unsigned char> pkcs7_unpad(const std::vector<unsigned char>& data) {
    if (data.empty()) return {};
    unsigned char pad_len = data.back();
    if (pad_len > data.size()) {
        throw std::invalid_argument("无效的填充长度");
    }
    return std::vector<unsigned char>(data.begin(), data.end() - pad_len);
}

// AES-256-CBC加密
std::vector<unsigned char> aes_encrypt(
    const std::vector<unsigned char>& plaintext,
    const std::vector<unsigned char>& key,
    const std::vector<unsigned char>& iv
) {
    mbedtls_aes_context aes;
    mbedtls_aes_init(&aes);

    // 初始化加密密钥
    int ret = mbedtls_aes_setkey_enc(&aes, key.data(), key.size() * 8);  // 第二个参数为密钥长度(位)
    handle_error(ret, "设置加密密钥失败");

    // 填充明文(确保长度为16字节的整数倍)
    std::vector<unsigned char> padded_plaintext = pkcs7_pad(plaintext);
    std::vector<unsigned char> ciphertext(padded_plaintext.size());

    // 执行CBC模式加密
    ret = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT,
                              padded_plaintext.size(),
                              iv.data(),
                              padded_plaintext.data(),
                              ciphertext.data());
    handle_error(ret, "加密失败");

    mbedtls_aes_free(&aes);
    return ciphertext;
}

// AES-256-CBC解密
std::vector<unsigned char> aes_decrypt(
    const std::vector<unsigned char>& ciphertext,
    const std::vector<unsigned char>& key,
    const std::vector<unsigned char>& iv
) {
    mbedtls_aes_context aes;
    mbedtls_aes_init(&aes);

    // 初始化解密密钥
    int ret = mbedtls_aes_setkey_dec(&aes, key.data(), key.size() * 8);
    handle_error(ret, "设置解密密钥失败");

    std::vector<unsigned char> plaintext(ciphertext.size());

    // 执行CBC模式解密
    ret = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT,
                              ciphertext.size(),
                              iv.data(),
                              ciphertext.data(),
                              plaintext.data());
    handle_error(ret, "解密失败");

    // 去除填充
    std::vector<unsigned char> unpadded_plaintext = pkcs7_unpad(plaintext);

    mbedtls_aes_free(&aes);
    return unpadded_plaintext;
}

int main() {
    // 初始化随机数生成器(依赖熵源)
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    const char* personalization = "aes_example";  // 个性化字符串,增加随机性

    mbedtls_entropy_init(&entropy);
    mbedtls_ctr_drbg_init(&ctr_drbg);

    // 初始化CTR-DRBG随机数生成器
    int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
                                   reinterpret_cast<const unsigned char*>(personalization),
                                   strlen(personalization));
    handle_error(ret, "随机数生成器初始化失败");

    // 1. 生成密钥和IV
    auto key = generate_aes_key(ctr_drbg);
    auto iv = generate_iv(ctr_drbg);

    // 2. 待加密的明文
    std::string plaintext_str = "Hello, mbedtls! 这是一段测试文本,使用AES-256-CBC加密。";
    std::vector<unsigned char> plaintext(plaintext_str.begin(), plaintext_str.end());

    std::cout << "明文: " << plaintext_str << std::endl;

    // 3. 加密
    std::vector<unsigned char> ciphertext = aes_encrypt(plaintext, key, iv);
    std::cout << "加密后的密文长度: " << ciphertext.size() << "字节" << std::endl;

    // 4. 解密
    std::vector<unsigned char> decrypted_data = aes_decrypt(ciphertext, key, iv);
    std::string decrypted_str(decrypted_data.begin(), decrypted_data.end());
    std::cout << "解密后的明文: " << decrypted_str << std::endl;

    // 验证结果
    if (decrypted_str == plaintext_str) {
        std::cout << "✅ 加密解密成功!" << std::endl;
    } else {
        std::cout << "❌ 加密解密失败!" << std::endl;
    }

    // 清理资源
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);

    return 0;
}

3. 编译与运行

编译时需链接mbedtls库:

g++ aes_example.cpp -o aes_demo -lmbedtls -lmbedcrypto -lmbedx509
./aes_demo

运行输出示例:

明文: Hello, mbedtls! 这是一段测试文本,使用AES-256-CBC加密。
加密后的密文长度: 64字节
解密后的明文: Hello, mbedtls! 这是一段测试文本,使用AES-256-CBC加密。
✅ 加密解密成功!

4.4 mbedtls 与 OpenSSL 对比及选型建议

特性mbedtlsOpenSSL
体积与资源占用极小(适合嵌入式)较大(适合服务器/桌面)
功能完整性精简(核心加密算法+基础TLS)全面(支持更多协议/扩展)
易用性API简洁,学习成本低API复杂(历史兼容原因)
可裁剪性极强(通过配置文件禁用模块)较弱(模块耦合度高)
适用场景物联网、嵌入式设备、资源受限环境服务器、桌面应用、功能需求复杂的场景

选型建议

  • 嵌入式/IoT设备:优先选mbedtls(资源占用低,可裁剪)。
  • 服务器/桌面应用:选OpenSSL(功能全面,社区支持强)。

进阶使用要点:

  1. 模块裁剪
    通过修改mbedtls_config.h禁用不需要的功能(如注释#define MBEDTLS_RSA_C可移除RSA模块),减少代码体积。

  2. 密钥管理

    • 避免硬编码密钥,推荐用mbedtls_pbkdf2_hmac从用户密码派生密钥(增加暴力破解难度)。
    • 敏感设备中,密钥应存储在硬件安全模块(HSM)或安全存储区。
  3. TLS通信
    mbedtls的ssl模块支持TLS客户端/服务器实现,适合设备间加密通信(如IoT设备与云平台通信),示例可参考源码中的programs/ssl/ssl_client1.c

  4. 错误处理
    利用mbedtls_strerror将错误码转换为可读信息(如示例中的handle_error函数),便于调试。

五、 AES 加密解密工具类

AESCrypto 是一个基于 mbedtls 库实现的 AES 加密解密工具类,支持 AES-128、AES-192、AES-256 三种密钥长度,采用 CBC 模式进行加密解密,使用 PKCS#7 填充方式处理非块大小对齐的数据。该类封装了密钥生成、加密、解密等核心功能,提供简洁的接口供上层调用。

5.1 基本信息

  • 类名:AESCrypto
  • 头文件:AESCrypto.h
  • 源文件:AESCrypto.cpp
  • 依赖库:mbedtls(需链接 mbedtls 库以支持 AES 算法、随机数生成等功能)

1. 核心功能

  1. 支持 128/192/256 位 AES 密钥的生成与管理
  2. 基于 CBC 模式的加密操作(自动生成随机 IV)
  3. 基于 CBC 模式的解密操作(从密文中提取 IV)
  4. PKCS#7 填充 / 去填充处理

2. 重要常量

常量名类型值 / 说明
BLOCK_SIZEsize_tAES 块大小(固定 16 字节)
KEY_LENGTH_128size_t128 位密钥长度(16 字节)
KEY_LENGTH_192size_t192 位密钥长度(24 字节)
KEY_LENGTH_256size_t256 位密钥长度(32 字节)

3. 重要变量

变量名类型值 / 说明
keystd::vector<unsigned char>存储 AES 密钥的字节向量,长度由key_len指定(16/24/32 字节),析构时会被安全清空(填充 0)以防止密钥泄露。
key_lensize_t密钥长度(字节),仅支持 16(128 位)、24(192 位)、32(256 位),由构造函数或set_key函数设置,是 AES 算法强度的核心参数。
key_setbool密钥有效性标志,true表示密钥已正确设置(生成或传入),false表示密钥未就绪(此时加密解密操作会直接失败),用于避免无效密钥导致的错误。

5.2 接口说明

1. 公共接口

AESCrypto(size_t key_len = KEY_LENGTH_256) 构造函数

AESCrypto(size_t key_len = KEY_LENGTH_256);
  • 功能:初始化 AESCrypto 对象,指定密钥长度(默认 256 位)
  • 参数key_len - 密钥长度(字节),仅支持 16/24/32,无效值将默认使用 32 字节
  • 说明:构造后需调用 generate_key() 生成密钥或 set_key() 设置已有密钥

AESCrypto(const unsigned char* existing_key, size_t key_len) 带现有密钥的构造函数

AESCrypto(const unsigned char* existing_key, size_t key_len);
  • 功能:使用已有密钥初始化对象
  • 参数
    • existing_key - 现有密钥的字节数组(非空)
    • key_len - 密钥长度(字节),需为 16/24/32
  • 说明:若密钥无效(空指针或长度错误),key_set 标志将为 false,需重新设置密钥

~AESCrypto() 析构函数

~AESCrypto();
  • 功能:销毁对象,清空密钥数据(安全擦除密钥,防止内存泄露)
  • 说明:析构函数会调用mbedtls_cipher_free释放密钥上下文,确保密钥数据被安全清除。

int generate_key() 生成随机密钥

int generate_key();
  • 功能:生成符合当前密钥长度的随机密钥
  • 返回值:0 表示成功,非 0 表示失败(错误码来自 mbedtls 随机数生成失败)
  • 说明:生成成功后 key_set 标志为 true

void set_key(const unsigned char* existing_key, size_t len) 设置已有密钥

void set_key(const unsigned char* existing_key, size_t len);
  • 功能:使用外部密钥替换当前密钥
  • 参数
    • existing_key - 外部密钥的字节数组(非空)
    • len - 密钥长度(字节),需为 16/24/32
  • 说明:若参数无效(空指针或长度错误),密钥设置失败,key_set 保持 false

const unsigned char* get_key() const 获取当前密钥

const unsigned char* get_key() const;
  • 功能:获取当前密钥的字节数组指针
  • 返回值:密钥指针(key_settrue 时有效,否则返回 nullptr

size_t get_key_len() const 获取密钥长度

size_t get_key_len() const;
  • 功能:获取当前密钥长度(字节)
  • 返回值:密钥长度(16/24/32)
  • 说明:若密钥未设置,返回 0

int encrypt(const std::string& plaintext, std::vector<unsigned char>& ciphertext) 加密为字节向量

int encrypt(const std::string& plaintext, std::vector<unsigned char>& ciphertext);
  • 功能:对明文进行加密,密文包含 16 字节 IV + 加密数据
  • 参数
    • plaintext - 待加密的明文(字符串)
    • ciphertext - 输出的密文(字节向量,前 16 字节为 IV)
  • 返回值:0 表示成功,非 0 表示失败(如密钥未设置、明文为空、随机数生成失败等)
  • 说明
    • 自动生成 16 字节随机 IV 并附加在密文头部
    • 明文会先经过 PKCS#7 填充以满足块大小要求

int encrypt_to_string(const std::string& plaintext, std::string& ciphertext_str) 加密为字符串

int encrypt_to_string(const std::string& plaintext, std::string& ciphertext_str);
  • 功能:对明文进行加密,密文以字符串形式输出
  • 参数
    • plaintext - 待加密的明文(字符串)
    • ciphertext_str - 输出的密文(字符串,包含 IV)
  • 返回值:0 表示成功,非 0 表示失败
  • 说明:内部调用 encrypt(),将字节向量转换为字符串返回

int decrypt(const std::vector<unsigned char>& ciphertext, std::string& plaintext) 解密为字节向量

int decrypt(const std::vector<unsigned char>& ciphertext, std::string& plaintext);
  • 功能:对密文(字节向量)解密,提取明文
  • 参数
    • ciphertext - 待解密的密文(需包含前 16 字节 IV,且总长度 ≥ 16 字节)
    • plaintext - 输出的明文(字符串)
  • 返回值:0 表示成功,非 0 表示失败(如密钥未设置、密文长度不足、去填充失败等)
  • 说明
    • 从密文前 16 字节提取 IV
    • 解密后的数据会经过 PKCS#7 去填充处理

int decrypt_from_string(const std::string& ciphertext_str, std::string& plaintext) 从字符串解密

int decrypt_from_string(const std::string& ciphertext_str, std::string& plaintext);
  • 功能:对密文(字符串)解密,提取明文
  • 参数
    • ciphertext_str - 待解密的密文(字符串,需包含 IV)
    • plaintext - 输出的明文(字符串)
  • 返回值:0 表示成功,非 0 表示失败
  • 说明:内部将字符串转换为字节向量,再调用 decrypt()

2. 私有方法

int generate_random_data(unsigned char* buffer, size_t length) 生成随机数据

int generate_random_data(unsigned char* buffer, size_t length);
  • 功能:生成指定长度的随机数据,用于生成密钥(generate_key)和加密时的初始向量(IV)。
  • 参数
    • buffer:存储随机数据的缓冲区(非空);
    • length:需要生成的随机数据长度(字节,>0)。
  • 返回值:0表示成功,非0表示失败(错误码来自 mbedtls 库,如随机数生成器初始化失败)。
  • 实现细节:基于 mbedtls 的entropy(熵源)和ctr_drbg(计数器模式随机数生成器)实现,确保生成的随机数据具有足够的随机性,满足加密安全要求。

std::vector<unsigned char> pkcs7_pad(const unsigned char* data, size_t length, size_t block_size) PKCS#7 填充

std::vector<unsigned char> pkcs7_pad(const unsigned char* data, size_t length, size_t block_size);
  • 功能:对数据进行 PKCS#7 填充,使数据长度为块大小(block_size)的整数倍,满足 AES 块加密算法的输入要求。
  • 参数
    • data:待填充的原始数据(非空);
    • length:原始数据长度(字节,>0);
    • block_size:块大小(此处固定为BLOCK_SIZE=16)。
  • 返回值:填充后的字节向量(长度为block_size的整数倍);若输入无效(data为空或length=0),返回空向量。
  • 填充规则:计算需要填充的字节数pad_length = block_size - (length % block_size),然后在数据末尾追加pad_length个值为pad_length的字节(例如:若需填充 3 字节,则追加0x03 0x03 0x03)。

std::vector<unsigned char> pkcs7_unpad(const unsigned char* data, size_t length) PKCS#7 去填充

std::vector<unsigned char> pkcs7_unpad(const unsigned char* data, size_t length);
  • 功能:移除解密后数据的 PKCS#7 填充,恢复原始明文。
  • 参数
    • data:解密后带填充的数据(非空);
    • length:带填充数据的长度(字节,>0,且为BLOCK_SIZE的整数倍)。
  • 返回值:去除填充后的原始数据向量;若填充无效(如填充长度超出数据长度、填充字节不一致),返回空向量。
  • 验证逻辑
    1. 取数据最后一个字节作为填充长度pad_length
    2. 检查pad_length是否合法(1 ≤ pad_length ≤ length);
    3. 验证最后pad_length个字节是否均等于pad_length
    4. 若验证通过,返回前length - pad_length字节的原始数据。

bool is_valid_key_len(size_t len) const 检查密钥长度

bool is_valid_key_len(size_t len) const;
  • 功能:检查密钥长度是否为 AES 支持的合法长度(16/24/32 字节)。
  • 参数
    • len:待检查的密钥长度(字节)。
  • 返回值true表示合法(16/24/32),false表示非法。
  • 作用:在构造函数、set_key等方法中用于校验输入的密钥长度,确保仅使用符合 AES 标准的密钥(避免因无效长度导致加密算法错误)。

3. 使用示例

  • 生成密钥并加密解密
#include "AESCrypto.h"
#include <iostream>

int main() {
    // 初始化 256 位 AES 加密器
    AESCrypto aes(AESCrypto::KEY_LENGTH_256);
    
    // 生成随机密钥
    if (aes.generate_key() != 0) {
        std::cerr << "密钥生成失败" << std::endl;
        return -1;
    }
    
    // 打印密钥(十六进制)
    const unsigned char* key = aes.get_key();
    
    // 明文
    std::string plaintext = "Hello, AESCrypto!";
    std::cout << "明文: " << plaintext << std::endl;
    
    // 加密
    std::vector<unsigned char> ciphertext;
    if (aes.encrypt(plaintext, ciphertext) != 0) {
        std::cerr << "加密失败" << std::endl;
        return -1;
    }
    
    // 解密
    std::string decrypted_text;
    if (aes.decrypt(ciphertext, decrypted_text) != 0) {
        std::cerr << "解密失败" << std::endl;
        return -1;
    }
    std::cout << "解密后明文: " << decrypted_text << std::endl;
    
    return 0;
}
  • 使用现有密钥加密解密
#include "AESCrypto.h"
#include <iostream>

int main() {
    // 现有 128 位密钥(16 字节)
    unsigned char existing_key[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                                     0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};
    
    // 使用现有密钥初始化加密器
    AESCrypto aes(existing_key, AESCrypto::KEY_LENGTH_128);
    
    // 明文
    std::string plaintext = "Test with existing key";
    
    // 加密为字符串
    std::string ciphertext_str;
    if (aes.encrypt_to_string(plaintext, ciphertext_str) != 0) {
        std::cerr << "加密失败" << std::endl;
        return -1;
    }
    
    // 解密
    std::string decrypted_text;
    if (aes.decrypt_from_string(ciphertext_str, decrypted_text) != 0) {
        std::cerr << "解密失败" << std::endl;
        return -1;
    }
    std::cout << "解密结果: " << decrypted_text << std::endl;  // 应输出原明文
    
    return 0;
}

5.3 注意事项

  1. 密钥安全性:析构函数会自动清空密钥内存,避免密钥泄露;生成的密钥需妥善保管,丢失则无法解密数据。
  2. IV 处理:加密时自动生成 16 字节随机 IV,并附加在密文头部;解密时需从密文前 16 字节提取 IV,因此密文长度必须 ≥ 16 字节。
  3. 数据校验:解密时会验证密文长度(需为块大小的倍数)和 PKCS#7 填充的有效性,无效数据会返回失败。
  4. 错误处理:所有接口通过返回值标识成功(0)或失败(非 0),失败时可通过 std::cerr 输出的日志排查问题(如 mbedtls 错误码)。
  5. 字符串编码encrypt_to_stringdecrypt_from_string 直接将字节数据转换为字符串,若密文包含不可打印字符,建议先转为十六进制字符串存储 / 传输。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值