
简介这份基于Chen混沌系统与DNA编码的图像加密MATLAB源码包面向密码学、图像安全领域的初学者和研究人员聚焦图像数据在传输前的隐私保护难题帮助理解混沌序列生成、DNA分块编解码及加密解密全流程。包内共16个文件以7个m脚本为核心覆盖chen_output混沌生成、DNA_bian编码、DNA_yunsuan运算、fenkuai分块、main_jiami/main_jiemi加解密等模块m脚本串联起从混沌初始化到最终解密的完整链路另含bmp、png、jpg图像既提供原始测试图也保存加密结果方便逐段对照验证md文档则梳理算法步骤与实现思路压缩包大小4.61MB。目前已有433人学习源码结构清晰、注释明确可直接在MATLAB中运行并替换自己的图像测试加密效果。借助Chen系统对初值的极强敏感性与DNA操作的多样性这套代码能直观展示双重复合加密的强度适合课程设计、论文复现或二次开发。1. 混沌图像加密为什么需要 Chen 系统又为什么叠一层 DNA把一张明文图像加密成完全不可读的密文常见错误做法是只做像素置乱用随机序列把像素位置打乱结果直方图没变、相邻像素相关性依然很高攻击者用统计分析和已知明文攻击就能还原。混沌图像加密的标准做法是置乱加扩散同时做置乱破坏位置关系扩散改变像素值本身。Chen 系统的价值在于它比 Logistic 映射更快进入混沌、序列在相空间的折叠更剧烈抗相空间重构能力更强而 DNA 编码把二进制异或升维成碱基层面上的代数运算让明文中一个比特的变化能扩散到多个碱基。本文把从 Chen 系统数值求解、DNA 编码规则映射到 Matlab 完整加解密源码和验证指标这条路走通新手能照步骤跑出密文熟手能直接拿参数表和排错清单改自己的方案。2. Chen 系统在 Matlab 里的数值求解与密钥流生成2.1 Chen 系统状态方程与混沌初值敏感性Chen 系统的连续时间状态方程如下dx/dt a(y - x) dy/dt (c - a)x - xz c*y dz/dt xy - b*z当参数取 a35、b3、c28 时系统处于混沌状态。相轨迹被约束在一个有界区域内但任意两条初始距离极小的轨迹会以指数速度分离。这个性质直接决定了密钥设计的核心原则把初值 (x0, y0, z0) 当密钥哪怕密钥只差 1e-15生成的密钥流也会完全不同解密端自然得到白噪。和工程里常用的 Logistic 映射相比Chen 系统是三维连续系统序列复杂度更高单变量序列被用来做相空间重构攻击时的嵌入维数需求更大。和 Lorentz 系统相比Chen 系统在方程结构上多了一个线性项拓扑不等价Lyapunov 指数谱分布不同工程实现时离散化后的统计特性更好。因此在 DNA 混沌图像加密的工作里Chen 系统是比一维混沌更稳妥的选择。2.1.1 从 Logistic 迁移到 Chen 时要注意什么做过 Logistic 混沌加密的同行切到 Chen 系统时最常见的误解是沿用一维混沌的初值设置方式只给一个初值或者把三个初值设成相同值。Chen 系统三个初值必须各有独立性否则轨迹可能落入同一个吸引域的不同位置导致加解密密钥流不对称。我一般建议 x0、y0、z0 分别取 (0.1, 0.2, 0.3) 附近的值并保证它们互不相等。2.2 用 RK4 在 Matlab 里求解 Chen 系统的核心代码直接调 ode45 也能解但 ode45 使用自适应步长同样的时间跨度在不同初值或不同机器上得到的抽样点数可能不一致这会给加解密同步带来隐患。图像加密需要确定性的密钥流所以自己实现固定步长的经典四阶 Runge-KuttaRK4是更可靠的做法。% chen_rk4.m function [X, Y, Z] chen_rk4(x0, y0, z0, dt, total_steps, a, b, c) X zeros(1, total_steps); Y zeros(1, total_steps); Z zeros(1, total_steps); X(1) x0; Y(1) y0; Z(1) z0; for i 1:total_steps - 1 [kx1, ky1, kz1] chen_deriv(X(i), Y(i), Z(i), a, b, c); [kx2, ky2, kz2] chen_deriv(X(i) kx1*dt/2, Y(i) ky1*dt/2, Z(i) kz1*dt/2, a, b, c); [kx3, ky3, kz3] chen_deriv(X(i) kx2*dt/2, Y(i) ky2*dt/2, Z(i) kz2*dt/2, a, b, c); [kx4, ky4, kz4] chen_deriv(X(i) kx3*dt, Y(i) ky3*dt, Z(i) kz3*dt, a, b, c); X(i1) X(i) (kx1 2*kx2 2*kx3 kx4) * dt / 6; Y(i1) Y(i) (ky1 2*ky2 2*ky3 ky4) * dt / 6; Z(i1) Z(i) (kz1 2*kz2 2*kz3 kz4) * dt / 6; end end % 子函数Chen 系统右侧导数 function [dx, dy, dz] chen_deriv(x, y, z, a, b, c) dx a * (y - x); dy (c - a) * x - x * z c * y; dz x * y - b * z; end这段代码的关键点是 RK4 的四个斜率计算在每个步长内复用同一个时刻的状态公式里的dt/2和dt/6不能随意改动否则截断误差会从 O(dt^4) 退化到 O(dt)。加密时用 X 序列、解密时也从头重新算一遍相同步长的 X 序列只要初值和 dt 一致数值结果就是逐位相同的。2.2.1 参数说明与瞬态丢弃实际加密时不能直接用从第 1 步开始的序列因为混沌系统从初值出发的前若干步还处于瞬态过渡阶段序列分布不够均匀。常见做法是丢弃前 N 步。下表是我在 matlab 实现里常用的参数组合直接可抄参数取值说明a35Chen 系统标准混沌参数b3同上c28同上dt0.005步长越小精度越高但序列长度不变时耗时线性增长丢弃段长度1000瞬态丢弃保证序列进入吸引子稳态初值范围x00.1, y00.2, z00.3作为密钥一部分传入dt 取 0.005 时要得到 100000 个有效序列点实际需要迭代 101000 步耗时在毫秒级对 512x512 图像完全可接受。如果图像是 1024x1024序列点数需要扩展到 MN4步长可适当放大到 0.01但要确认解密端仍能逐位复现。3. DNA 编码规则表与密码学映射从像素到碱基序列3.1 DNA 编码的 8 套规则及互补约束DNA 序列由四种碱基 A、T、C、G 组成二进制两个比特正好有 00、01、10、11 四种状态所以一个碱基可以表示两个比特。把二进制状态映射到碱基时还需要满足 DNA 的碱基互补配对规则A 与 T 互补C 与 G 互补。对一个 2bit 编码在二进制层面上的互补必须同时满足按位取反的关系才能让编码规则在加解密时保持对称性。编码规则00011011规则 1ACGT规则 2AGCT规则 3TCGA规则 4TGCA规则 5CATG规则 6CTAG规则 7GATC规则 8GTAC规则 1 中00 对应 A11 对应 TA 和 T 的二进制码 00 和 11 互为按位取反满足两两互补01 对应 C10 对应 G同样互补。换规则时需要检查每组互补碱基对对应的二进制码是否互为反码如果不是扩散阶段的碱基异或就会出现不对称。3.1.1 加解密对称性为什么依赖编码规则DNA 层面的异或运算定义在碱基状态上比如定义 A 异或 A 等于 AA 异或 T 等于 T这种运算要满足交换律和结合律。当编码规则本身满足互补对称时碱基异或和解码到二进制再异或才是等价的否则加密端做的 DNA 运算在解密端无法直接逆运算。实际工程里我通常会固定使用表里的规则 1 做像素到 DNA 的编码用规则 3 做密钥流到 DNA 的编码这样两个层使用不同映射增加密钥空间。3.2 灰度图像到 DNA 序列的 Matlab 转换灰度图像一个像素取值 0 到 255占 8 个比特可以拆成 4 组 2bit每组映射一个碱基。下面这个函数把一个像素向量转成 DNA 字符串数组function dna_seqs pixels_to_dna(pixel_vec, rule_id) % 输入 pixel_vec 为列向量范围 0~255 % 输出 dna_seqs 为字符串数组每个元素长度 4 rules dna_rule_table(); map rules{rule_id}; % map(1)~map(4) 对应 00,01,10,11 的碱基字符 binary_mat dec2bin(pixel_vec, 8); % Nx8 字符矩阵 N length(pixel_vec); dna_seqs strings(N, 1); for i 1:N b binary_mat(i, :); % 每两位一组: b(1:2) b(3:4) b(5:6) b(7:8) pair_vals [bin2dec(b(1:2)) 1, bin2dec(b(3:4)) 1, ... bin2dec(b(5:6)) 1, bin2dec(b(7:8)) 1]; dna_seqs(i) [map(pair_vals(1)), map(pair_vals(2)), ... map(pair_vals(3)), map(pair_vals(4))]; end end function rules dna_rule_table() % 按规则编号存放四种碱基字符 rules{1} [A, C, G, T]; rules{2} [A, G, C, T]; % 实际使用只保留规则 1 和规则 3其余按表填充 rules{3} [T, C, G, A]; end解码是反向过程把每个碱基查表转回两位二进制再拼成 8bit 整数。需要注意char运算在 Matlab 里的拼接效率对 512x512 的图有 262144 个像素循环完全可接受如果处理视频帧或超大遥感图可以改成containers.Map查表避免重复调用bin2dec。3.2.1 为什么用 2bit 表示一个碱基而不是 4bit如果不小心把每个碱基对应 4bit8bit 像素只拆成两组DNA 编码后的序列长度减半扩散时一个碱基的改变会影响 4 个比特扩散粒度变粗。2bit 表示是 DNA 图像加密论文里的标准做法它让一个像素的 8bit 均匀分布到 4 个碱基上任何单个碱基的翻转最多影响原像素的一个比特信息扩散更细。4. 基于 DNA 置乱-扩散的混沌图像加密完整流程与 Matlab 源码4.1 加密总框架与密钥设计整个加密流程按“混沌序列生成 - 像素编码 - 置乱 - DNA 扩散 - 解码”五段式组织。密钥由 Chen 系统的三个初值组成即 key[x0, y0, z0]。为了增强安全性可以把迭代轮数、DNA 编码规则编号也并入密钥但前提是解密端必须能完整复现这些参数。置乱阶段我一般使用混沌序列的排序索引对 DNA 序列进行重排而不是对像素坐标直接交换。原因是 DNA 序列重排后再扩散能让置乱和扩散两个阶段的操作对象统一代码上少一次像素到 DNA 的转换。4.1.1 密钥明文关联细节如果做已知明文攻击测试建议把明文图像的 SHA-256 哈希值混入初值比如 x0 x0_base mod(sum(img(:)), 256) / 10000。这样同一把密钥加密两张不同明文图得到完全不同的密钥流能有效抵抗差分攻击。这个技巧在论文写作里叫明文关联密钥生成。4.2 置乱阶段使用 Chen 序列排序索引打乱 DNA 矩阵% 生成混沌序列取 x 分量做置乱 total_len h * w * 4; [X, ~, ~] chen_rk4(0.1, 0.2, 0.3, 0.005, total_len 1000, 35, 3, 28); seq X(1001:end); % 丢弃前 1000 步瞬态 [~, sort_idx] sort(seq); % 排序索引 % 置乱 DNA 序列dna_img 是 h*w 行、每行 4 个碱基的字符串数组 dna_shuffled dna_img(sort_idx);排序索引sort_idx是一个完全由混沌序列决定的排列它的可逆性体现在只要拿到相同的seq排序得到的sort_idx就相同解密时用dna_shuffled(sort_idx)就能恢复原序吗不对解密需要恢复的是原位置上的碱基正确做法是dna_restored zeros(size(dna_shuffled)); dna_restored(sort_idx) dna_shuffled;加密时用dna_img(sort_idx)做正向置乱解密时用dna_restored(sort_idx) dna_cipher做逆向还原这利用了 matlab 索引赋值的特性一次排序生成的sort_idx同时服务加解密两端不需要存储额外的置乱映射表。4.2.1 排序置乱 vs 取模坐标交换另一种常见做法是把混沌序列取模生成新坐标然后两两交换。排序置乱的优势是计算量稳定sort在 Matlab 里的复杂度是 O(N log N)对 512x512 图像 N 为 262144耗时远小于取模加循环。劣势是排序序列不能重复取样如果密钥流中有相等值sort的行为是稳定的但依赖实现版本。解决方法是先对seq做unique检查或取seq (1:length(seq))*1e-14加入微小扰动。4.3 扩散阶段DNA 碱基异或运算与像素恢复DNA 异或规则定义在四种碱基上等价于把每个碱基映射回 2bit 二进制后按位异或。这里直接给出完整加解密函数包含编码、置乱、扩散、解码的全过程function cipher_img dna_chen_encrypt(img, x0, y0, z0) [h, w] size(img); N h * w; total_len N * 4; % 步骤 1: 生成混沌序列 [X, Y, ~] chen_rk4(x0, y0, z0, 0.005, total_len 1000, 35, 3, 28); seq_x X(1001:end); seq_y Y(1001:end); % 步骤 2: 排序置乱索引 [~, sort_idx] sort(seq_x); % 步骤 3: 像素向量 - DNA 序列 img_vec img(:); dna_img pixels_to_dna(img_vec, 1); % 规则 1 编码明文 % 步骤 4: 置乱 dna_shuffled dna_img(sort_idx); % 步骤 5: 生成扩散密钥流并转 DNA key_stream mod(floor(seq_y * 1e15), 256); % 浮点转 0~255 整数 dna_key pixels_to_dna(key_stream, 3); % 规则 3 编码密钥 % 步骤 6: DNA 异或扩散 dna_cipher dna_xor(dna_shuffled, dna_key); % 步骤 7: 解码回像素 cipher_img reshape(dna_to_pixels(dna_cipher, 1), h, w); end function plain_img dna_chen_decrypt(cipher_img, x0, y0, z0) [h, w] size(cipher_img); N h * w; total_len N * 4; [X, Y, ~] chen_rk4(x0, y0, z0, 0.005, total_len 1000, 35, 3, 28); seq_x X(1001:end); seq_y Y(1001:end); [~, sort_idx] sort(seq_x); cipher_vec cipher_img(:); dna_cipher pixels_to_dna(cipher_vec, 1); key_stream mod(floor(seq_y * 1e15), 256); dna_key pixels_to_dna(key_stream, 3); % 先逆扩散 dna_shuffled dna_xor(dna_cipher, dna_key); % 再逆置乱 dna_restored strings(N, 1); dna_restored(sort_idx) dna_shuffled; plain_img reshape(dna_to_pixels(dna_restored, 1), h, w); end这里的加解密顺序完全对称加密先置乱再扩散解密先逆扩散再逆置乱。mod(floor(seq_y * 1e15), 256)是把浮点混沌序列整数化的常用手段乘以 1e15 是为了利用双精度浮点的尾数部分直接取mod(seq_y, 256)会因为序列值分布不在固定区间而产生大量偏差。4.3.1 为什么解码规则要和编码规则相同明文编码用规则 1解码也必须用规则 1密钥流编码用规则 3但密钥流不参与逆向解码它只被用于异或。如果把规则写反加解密端对同一像素的二进制拆分不同密文长度没错但内容完全错乱。排查此类问题的最快方式是用一个 2x2 的全零图像跑通后再换真实图。4.4 Matlab 源码中的关键参数表与调用方式参数位置参数名建议值修改注意事项chen_rk4dt0.005加解密必须保持一致chen_rk4丢弃步数1000改大更安全改小影响序列均匀性置乱混沌分量取 x也可以取 y但要固定扩散混沌分量取 y避免和置乱用同一个分量整数化放大倍数1e15低于 1e12 时序列尾部精度损失明显编码规则明文规则 / 密钥规则1 / 3修改后解码规则要同步调用方式分三步读图、加解密、写文件。加密时注意 Matlab 读入的灰度图是 uint8 类型pixels_to_dna接收的是数值向量不需要转 double但chen_rk4接收的初值是 double两者类型不要混用。4.4.1 uint8 溢出陷阱扩散阶段如果不用 DNA 异或而改用普通加法扩散比如mod(img key, 256)在 Matlab 里必须把 img 转成 double 再运算否则 uint8 加法溢出会直接报错或静默截断。本文的 DNA 方案绕开了加法但dna_to_pixels内部拼二进制时要注意dec2bin输出是字符矩阵拼完要uint8(bin2dec(...))转回图像类型。5. 加密效果验证与三个最容易翻车的细节5.1 NPCR 和 UACI 的计算脚本写完加解密函数后第一件事不是看密文图像而是跑差分攻击指标。NPCR像素变化率统计两幅密文图像中不同像素的比例UACI统一平均变化强度统计差异的平均幅度。对 256 级灰度图理想 NPCR 应大于 99.6%UACI 在 33% 左右。function [npc, uaci] npcr_uaci(cipher1, cipher2) if ~isequal(size(cipher1), size(cipher2)) error(两幅密文尺寸不一致); end diff_map cipher1 ~ cipher2; npc sum(diff_map(:)) / numel(cipher1) * 100; diff_double abs(double(cipher1) - double(cipher2)); uaci sum(diff_double(:)) / (numel(cipher1) * 255) * 100; end测试方法对明文图像改变一个像素的值分别用相同密钥加密对两幅密文计算 NPCR 和 UACI。如果 NPCR 偏低说明扩散轮数不够或密钥流与明文的关联不足需要检查扩散阶段是否真的把整幅图的能量分散开。这里有个常见误区cipher1 ~ cipher2是矩阵逻辑运算sum默认按列求和再累加所以必须用diff_map(:)展平否则得到的是每列的统计值。5.2 解密白噪的排查顺序解密出来全是白噪新手第一反应是密钥记错了但绝大多数情况不是。按照以下顺序排查先检查加解密用的 dt 是否一致比如加密用了 0.005解密不小心用了 0.01序列前几十步差异很小后面彻底发散再检查混沌序列的截取位置加密丢弃了 1000 步解密也必须丢弃 1000 步差一步整个序列就错位最后检查 sort_idx 的赋值方向解密时dna_restored(sort_idx) dna_shuffled写反成dna_restored dna_shuffled(sort_idx)重排两次等于没恢复。还有一个隐蔽问题floor(seq_y * 1e15)在浮点数的边界上可能因为四舍五入产生 1 的差异。解决办法是在加解密两端同时改用round(seq_y * 1e15)并强制转 uint64确保边界行为一致。5.3 一个能直接改用的技巧彩色图像分通道复用同一密钥流对 RGB 彩色图像不要直接把三通道拼接成一维向量加密那样通道间的空间相关性仍然存在。我一般这样处理三个通道分别独立编码成 DNA 序列但共用同一组 Chen 系统生成的置乱索引和扩散密钥流只是把明文编码规则分别换成规则 1、2、3。这样三个通道的位置置乱是同步的但扩散阶段因为编码规则不同所以得到的密文不同既保证了通道内的加密强度又破坏了三通道之间的相关性。验证时对三个通道的密文分别计算 NPCR/UACI如果某个通道指标偏低单独检查该通道的编码规则和解码规则是否匹配。最后把三个通道合并成彩色图输出时注意 Matlab 的cat(3, r, g, b)顺序不要颠倒解密时也要按同样的通道顺序拆开再恢复。本文还有配套的精品资源点击获取