尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Matlab图像加密:Arnold置乱、Logistic混沌、DNA编码等实现

Matlab图像加密:Arnold置乱、Logistic混沌、DNA编码等实现 简介这是一套面向图像处理初学者与科研入门者的Matlab图像加密实践资源聚焦混沌理论、DNA编码、光学变换与小波分析等前沿加密技术的代码实现与可视化验证。资源共42个文件包含23个核心m函数如FHCCS.m、scramble.m、TpEncryptE.m等、6个备份文件.zbak、6个说明文本含LICENSE与banner系列、4幅标准测试图像bmp/jpg格式及README.md文档整体压缩包大小为10.69MB结构清晰、模块解耦便于理解算法流程与调试修改。已有25人学习下载所有程序均基于Matlab 2019b实测可运行主程序main.m一键调用全部功能自动生成置乱图、密文图、解密图及性能评估曲线配套函数覆盖五维哈密顿混沌系统、Arnold变换、Logistic/Tent/Kent/Hent混沌映射、双随机相位编码、RSA与DWT多模态加密方案支持密钥生成、像素扩散、DNA编码运算等关键环节。 做图像加密这块最容易踩的坑就是以为“把像素打乱顺序”就算加密完成了。真正上手用Matlab做一轮完整实现后会发现置乱、扩散、密钥设计、灰度统计特性、抗攻击能力每一环都有讲究。这篇文章是我把几类主流图像加密方法Arnold置乱、Logistic混沌映射、DNA编码、Josephus遍历在Matlab里逐一实现并跑通后的完整记录包含可直接复用的函数、完整加密流程、效果评估指标以及各种调试时踩过的坑。适合正在做Matlab图像处理大作业、信息安全课程设计或者想快速对比不同加密算法效果的同学。从能看懂到能自己改再到能跑出一套像样的安全性指标这篇文章尽量把路给你铺平。1. 图像加密的整体设计思路1.1 图像加密不是“把明文变乱码”这么简单图像数据有三个特点数据量大、相邻像素相关性强、冗余度高。拿一张512x512的灰度图来说直接把它当成一段文本来做AES加密虽然可行但效率低而且很多传统加密方案没有充分利用图像的二维空间结构。更重要的是图像加密通常要求加密后的图像仍然是一张“能看”的图直方图均匀、相邻像素相关性低这样才能抵抗统计分析攻击。所以图像加密的主流思路不是“整个文件加密”而是围绕像素做两层操作置乱Permutation改变像素的位置破坏图像的空间结构让原来相邻的像素变得不相邻。典型方法有Arnold变换、Josephus遍历、随机行列置换等。扩散Diffusion改变像素的值让一个像素的变化能够扩散到尽可能多的密文像素上。典型方法有混沌序列异或、模运算、DNA编码运算等。置乱解决的是“空间可视性”问题扩散解决的是“统计分布”问题。只做置乱图像的直方图不会变统计特征依然暴露只做扩散空间结构还在人眼还是能看到轮廓。所以实际工程中几乎所有合格的图像加密方案都是“先置乱、再扩散反复多轮”的结构。1.2 为什么选这几类算法搭配我这套方案里选了四类算法不是随便堆砌而是各有考量。下面这个表是我实践后的直观对比算法核心原理密钥形式优点明显短板Arnold置乱猫脸变换坐标线性变换取模迭代次数实现简单破坏相邻像素关系效果明显只改变位置不改变值且要求方形图像Logistic混沌映射一维混沌系统生成伪随机序列初值和控制参数初值极端敏感可生成无限长密钥流有限精度下序列会周期退化需要处理DNA编码像素转碱基序列做四进制运算碱基映射规则/运算规则含义新颖非线性度强适合论文加分纯粹用DNA编码效率不高需要配合密钥流Josephus遍历约瑟夫环淘汰顺序作为置乱映射步长、起点置乱随机性强顺序不容易反推时间复杂度高不适合直接处理大图选这套组合的核心理由是覆盖了“位置置乱”和“数值扩散”两大环节而且每种算法在Matlab里的实现复杂度差异很大适合一边跑通、一边理解图像加密的完整逻辑。实际做的时候也没必要一次性全上可以先ArnoldLogistic跑通一个最小版本再看效果决定要不要追加DNA和Josephus。2. 常用加密算法的Matlab实现2.1 Arnold置乱简单但有效的“猫脸变换”Arnold变换也叫猫脸变换公式很简单x (x y) mod N y (x 2y) mod N这里的(x, y)是像素坐标N是图像边长。变换的本质是对图像做一次线性坐标拉伸再取模。因为取模运算的存在图像会“被折叠”原来相邻的像素会被甩到不同位置。迭代多次后图像会完全看起来像噪声。Matlab实现时有个关键细节图像的矩阵索引从1开始而Arnold变换的数学定义用的是0到N-1的坐标。所以我习惯先把坐标减1做完变换再加1避免踩到mod边界。function A arnold_permute(I, rounds) % I: 灰度图像矩阵要求为方形 % rounds: Arnold变换迭代次数 [M, N] size(I); if M ~ N error(Arnold变换要求图像为方形请先对图像做裁剪或填充); end A I; for k 1:rounds % 生成0到N-1的坐标网格 [x, y] meshgrid(0:N-1, 0:N-1); % Arnold正变换 x_new mod(x y, N); y_new mod(x 2*y, N); % Matlab索引从1开始转换后映射像素 idx_old sub2ind([M, N], y 1, x 1); idx_new sub2ind([M, N], y_new 1, x_new 1); B zeros(M, N); B(idx_new) A(idx_old); A B; end end这段代码看起来有点绕但核心只有一句话把原图每个坐标的像素搬到新坐标上去。sub2ind的作用是把二维坐标转成一维索引效率比双层for循环高很多。解密时Arnold变换有两种方式。一种是利用周期性Arnold变换对N阶图像存在一个周期T迭代T次后图像恢复原样所以解密时迭代T-rounds次即可。不过这个周期未必好算尤其N较大时。另一种更直接的做法是逆变换x (2x - y) mod N y (-x y) mod Nfunction A arnold_inverse(I, rounds) [M, N] size(I); if M ~ N error(Arnold逆变换要求图像为方形); end A I; for k 1:rounds [x, y] meshgrid(0:N-1, 0:N-1); % Arnold逆变换 x_old mod(2*x - y, N); y_old mod(-x y, N); idx_old sub2ind([M, N], y_old 1, x_old 1); idx_new sub2ind([M, N], y 1, x 1); B zeros(M, N); B(idx_new) A(idx_old); A B; end end注意Arnold置乱只改变像素位置不改变像素值。单独用Arnold加密的图像直方图与明文完全一致这在实际安全性评估里是不合格的。所以Arnold通常只作为加密流程的前置环节后面一定要接扩散操作。2.2 Logistic混沌序列从初值到密钥流Logistic映射是混沌加密里最经典的一维映射x(n1) u * x(n) * (1 - x(n))当控制参数u取值在(3.57, 4]区间时系统进入混沌状态序列表现出极强的初值敏感性。也就是说只要初值x(0)相差一点点经过若干次迭代后序列就完全不同。这个特性非常适合做密钥把u和x(0)作为密钥生成的序列就是密钥流。生成混沌序列本身不难难在两点。第一初值不能选固定点。比如x(0)0、0.5、1这些值序列会产生固定点或短周期完全失去混沌特性。第二要舍弃序列前段的一部分值。因为混沌系统从初值出发后前期可能处于暂态过程直接使用会影响随机性。我习惯丢弃前500个点。function seq logistic_sequence(u, x0, len, discard) % u: 控制参数建议取3.99附近 % x0: 初值不能取0、0.25、0.5、0.75、1 if nargin 4 discard 500; end seq zeros(1, len discard); x x0; for i 1:len discard x u * x * (1 - x); seq(i) x; end seq seq(discard1 : end); end有了混沌序列怎么用它做扩散最直接的方式是量化成密钥流然后和图像逐像素异或。这里有几种常见的量化方式直接取小数部分映射到0~255的整数。对混沌序列排序用排序索引作为置乱表。将混沌序列二值化再组合成8位密钥。我比较常用的做法是第一种简单直接function key_stream logistic_key_stream(u, x0, total_pixels) seq logistic_sequence(u, x0, total_pixels); % 映射到0~255 key_stream mod(floor(seq * 10^12), 256); key_stream uint8(key_stream); end注意这里的模运算一定要在double类型下做。Matlab的uint8类型在做乘法或加法时会产生截断容易导致解密后无法恢复原文。Logistic扩散加密的核心逻辑就是异或function C logistic_diffusion(I, u, x0) [M, N] size(I); total M * N; key logistic_key_stream(u, x0, total); key reshape(key, M, N); C bitxor(I, key); end解密时用相同的u和x0生成同样的密钥再异或一次即可。异或运算是对称的这也是混沌扩散比模加法扩散方便的地方。2.3 DNA编码加密把图像变成碱基序列DNA编码是近几年图像加密论文里的热门素材。核心思路是把每个像素的8位二进制数拆成4个2位二进制段再用00、01、10、11分别对应A、C、G、T四种碱基。这样一张灰度图就变成了一段“DNA序列”。然后可以在碱基层面上做运算比如DNA加法、DNA异或等。其实DNA运算的本质就是四进制运算。比如把A0、C1、G2、T3那么DNA加法就是对4取模的加法A A A0 0 0 C G G? 不对1 2 3 - T这样做的好处是运算规则不再局限于二进制的异或而是可以灵活定义多种碱基运算表增加加密的非线性度。即使攻击者知道用的是DNA编码不知道具体的碱基映射规则和运算规则也很难还原明文。在Matlab里实现时我建议不要真的用字符串去拼DNA序列那样运行速度太慢。更好的方式是直接用数值矩阵模拟四进制运算function J dna_encode_diffusion(I, key_q) % I: double类型灰度矩阵像素值0~255 % key_q: 与图像等大的四进制密钥矩阵元素0~3 % 将每个像素拆成4个四进制位与密钥做模4加法 [M, N] size(I); % 像素转四进制矩阵每个像素变成1x4行向量 I_bin de2bi(double(I), 8, left-msb); % M*N x 8 的二进制矩阵 I_q zeros(M*N, 4); for col 1:4 % 每两位二进制转成一个四进制数 I_q(:, col) I_bin(:, 2*col-1) * 2 I_bin(:, 2*col); end % 四进制加法模4 key_q_vec reshape(key_q, M*N, 4); J_q mod(I_q key_q_vec, 4); % 四进制转回二进制再合成像素 J_bin zeros(M*N, 8); for col 1:4 J_bin(:, 2*col-1) floor(J_q(:, col) / 2); J_bin(:, 2*col) mod(J_q(:, col), 2); end J reshape(bi2de(J_bin, left-msb), M, N); end解密时需要做模4减法也就是 mod(J_q - key_q, 4)。这段代码在逻辑上是可逆的。实际操作中有一点要留意DNA编码本身并不带来安全性真正的安全性来自“密钥序列运算规则”。如果把DNA编码当成固定变换它的作用更像是把二进制的异或运算换成了四进制的非线性运算从而提升抗线性分析的能力。所以在整个加密流程里DNA扩散不能单独使用一定要配合混沌序列生成密钥否则等于拿一把固定的锁锁门。2.4 Josephus遍历置乱换个顺序打乱图像Josephus问题是个古老的数学问题一堆人围成圈从某个位置开始报数每报到k就淘汰一个人然后继续从下一个人报数直到所有人都被淘汰。淘汰顺序可以当成一个排列用来做图像置乱。思路是把图像展成一维向量然后根据Josephus淘汰顺序重新排列像素。由于这个顺序强烈依赖于步长k所以k就是密钥的一部分。function perm josephus_permutation(n, step) % n: 元素总数 % step: 报数步长 idx 1:n; perm zeros(1, n); pos 0; for t 1:n pos pos step - 1; pos mod(pos, length(idx)) 1; perm(t) idx(pos); idx(pos) []; end end这段代码逻辑很直接但有一个明显的问题当n很大时idx(pos) []这一步涉及数组元素的整体搬移时间复杂度接近O(n^2)。512x512的图像展平后n262144跑起来会非常慢。所以在实际加密流程里我通常不会对整张图做Josephus置乱而是把它用在两个地方对256x256以下的小图像做完整置乱作为演示。对大图像先分块比如分成8x8的小块对每个块展开后做Josephus置乱再拼回去。这样既控制了计算量又保留了Josephus的随机性。我实践下来把Josephus当作“第二轮置乱”放进流程里效果不错。第一次Arnold置乱负责全局打散Josephus负责局部细粒度打乱两层互补。3. 完整加密流程与操作指南3.1 主程序框架把各模块串成完整流程模块单独跑通后最关键的是怎么组合成一套可逆的加密流程。我采用的流程是加密方向读取图像统一转成灰度图double类型。Arnold置乱迭代n1次。Logistic混沌序列异或扩散。DNA四进制加法扩散密钥序列同样来自Logistic混沌序列的派生。Josephus局部置乱得到密文图。解密方向Josephus逆置乱。DNA四进制减法解码。再次用相同的Logistic密钥流异或。Arnold逆变换得到明文图。主程序结构大概长这样function varargout image_encrypt_demo() % 读取图像 I imread(cameraman.tif); I im2gray(I); I double(I); % 密钥 n_arnold 20; u1 3.999; x0_1 0.3154; u2 3.98; x0_2 0.7312; % 加密 A arnold_permute(I, n_arnold); [M, N] size(I); key1 logistic_key_stream(u1, x0_1, M*N); A bitxor(uint8(A), reshape(key1, M, N)); A double(A); key_q generate_dna_key(u2, x0_2, M, N); A dna_encode_diffusion(A, key_q); A josephus_block_permute(A, 5); % 步长5分块置乱 C uint8(A); % 解密 ... % 显示结果 figure; subplot(1,3,1); imshow(uint8(I)); title(明文); subplot(1,3,2); imshow(C); title(密文); subplot(1,3,3); imshow(uint8(decrypted)); title(解密结果); end这里有个容易出问题的点bitxor要求两个输入是相同类型所以我先把图像转成uint8异或完再转回double。后面的DNA操作都建议在double下进行避免uint8的截断误差。整个过程里数据类型变来变去就是图像加密在Matlab里最让人头疼的细节建议每一步都检查一下矩阵类型。3.2 密钥设计与保存密钥设计直接决定加密方案的安全性。我的建议是不要把单个参数当密钥而是把整套参数打包成一个结构体。比如Arnold迭代次数 n1Logistic初值 x0_1、参数 u_1DNA密钥派生用初值 x0_2、参数 u_2Josephus步长 step保存密钥时直接用Matlab的mat文件keys.n_arnold 20; keys.u1 3.999; keys.x0_1 0.3154; keys.u2 3.98; keys.x0_2 0.7312; keys.josephus_step 5; save(encryption_keys.mat, keys);需要特别注意浮点数的精度对解密成功与否影响极大。如果保存密钥时用了单精度或者从文件读取时精度不足解密结果可能面目全非。建议保存时用save(..., -v7.3)读取后用double()做一次强制转换。混沌系统还有一个天然特点初值差一点点结果完全不同。这在加密里是优点但在工程实现里是风险。如果为了测试方便可以在密钥里额外保存一个校验值比如明文图像的SHA-256哈希这样解密后可以自动校验是否成功。3.3 加密效果评估怎么判断加密质量很多初学者看到密文图“像雪花一样”就觉得加密成功了实际评估加密效果不能靠人眼。业界常用的几个指标在Matlab里都能快速算出来。第一个是灰度直方图。加密后的直方图应该尽量平坦说明密文像素值分布均匀没有泄露明文统计特征。看直方图用imhist即可。第二个是相邻像素相关系数。明文图像的水平、垂直、对角方向的相邻像素相关性很高通常接近1加密后应该接近0。实现起来就是对相邻像素对做相关系数计算function r adjacent_corr(I, direction) [M, N] size(I); switch direction case h x I(:, 1:end-1); y I(:, 2:end); case v x I(1:end-1, :); y I(2:end, :); case d x I(1:end-1, 1:end-1); y I(2:end, 2:end); end x double(x(:)); y double(y(:)); r corr(x, y); end第三个是信息熵。8位灰度图的信息熵理想最大值是8加密图像的熵越接近8说明像素分布越均匀。function H entropy_img(I) p imhist(I) / numel(I); p p(p 0); H -sum(p .* log2(p)); end第四个是NPCR和UACI这两个指标用来衡量“明文中一个像素改变后密文会不会发生剧烈变化”。具体做法是明文改一个像素分别加密两次然后对比密文差异。NPCR要接近99.6%UACI要接近33.4%才算有良好的抗差分攻击能力。注意这个测试要求加密流程是确定性的也就是相同的明文和密钥必须产生完全相同的密文。整套指标跑下来如果都达标说明这个加密方案不是花架子。我自己的经验是单独用ArnoldLogistic异或NPCR和UACI往往勉强达标但不够优秀加上DNA扩散后数值会有明显提升。4. 常见问题与实战排查4.1 uint8与doubleMatlab图像加密最大的坑这个问题我反复遇到过也建议每个做图像加密的人都重视。Matlab默认用imread读图得到的是uint8矩阵像素范围0~255。uint8类型参与算术运算时一旦超出0~255范围就会截断。比如200 100在uint8下不是300而是44。做混沌异或扩散时如果直接对uint8矩阵做乘法和模运算结果会完全不对而且解密时无法恢复。我的习惯是图像读取后立刻转double所有中间计算都在double下进行。只在需要显示或保存时转成uint8。bitxor操作时保证两个输入类型一致必要时候先转uint8算完再转回double。mod运算的除数必须是整数混沌量化结果建议用floor后再mod。4.2 彩色图像的处理方式彩色图像是三维矩阵MxNx3分别对应R、G、B三个通道。加密时有两种思路一是三个通道当成三个独立灰度图分别加密二是先把RGB转换到其他色彩空间再加密。我建议的做法是拆通道分别加密。理由是实现简单而且三个通道的密钥可以不同增加安全性。但要注意三个通道的像素值分布不一样如果共用同一个密钥流通道之间可能存在相关性泄露。更稳妥的做法是为每个通道生成独立的混沌序列。function C_rgb encrypt_rgb(I_rgb, keys) C_rgb zeros(size(I_rgb)); for ch 1:3 I_ch double(I_rgb(:, :, ch)); % 每个通道用不同的混沌初值 x0 keys.x0 ch * 0.0001; A arnold_permute(I_ch, keys.n_arnold); key logistic_key_stream(keys.u, x0, numel(I_ch)); A bitxor(uint8(A), reshape(key, size(I_ch))); C_rgb(:, :, ch) uint8(A); end end这里每个通道的初值在基础密钥上加了0.0001的偏差。这个偏差虽然小但混沌序列的敏感性足以让三个通道的密钥流完全不同。当然这个偏差必须是约定的否则解密时无法还原。4.3 Matlab执行效率优化图像加密做原型验证时速度慢能忍但如果慢到几百毫秒一帧调试起来就很痛苦。我优化性能主要从三个方向入手第一尽量避免嵌套for循环。比如Arnold变换用meshgrid生成坐标网格一次性完成所有像素的坐标映射比双循环快一到两个数量级。第二矩阵操作向量化。DNA编码里逐像素拆二进制位如果实在不想写向量化版本可以先通过reshape把二维矩阵变一维再用矩阵运算处理比for循环快很多。第三预分配输出矩阵。像Josephus置乱里idx(pos) []这种操作会产生不必要的内存搬移能避免就避免。如果只是做原型验证可以接受慢但要知道性能瓶颈在哪。4.4 安全性分析中的几个误区有一种常见的做法是只看加密效果图“看不出来”就觉得安全。实际上图像加密要能抵抗至少以下三类攻击统计攻击、已知明文攻击、差分攻击。直方图均匀只是最基础的要求。另一个误区是觉得密钥空间越大越好。Logistic映射的两个参数u和x0在双精度下有足够大的密钥空间但混沌系统在有限精度实现中会退化为周期序列。这意味着理论上的密钥空间并不等于实际实现中的有效密钥空间。缓解办法有两个一是丢弃足够长的暂态序列二是考虑使用更高精度的运算或者组合多级混沌系统。还有一点必须正视这篇文章里的实现方案属于教学演示级别重点在于理解图像加密的完整流程和Matlab实现技巧。如果要用在真实场景建议在基础框架上增加更强的密码学原语比如引入SHA-256哈希作为密钥因子或者采用置乱-扩散多轮迭代结构。最后再分享一个小经验调试图像加密代码时不要等到全部写完才测试每一步都要做可逆性验证。我的习惯是每写一个模块立刻用一个小矩阵比如直接用magic(8)生成的8x8矩阵测一遍“加密-解密后能否完全恢复”。如果恢复不了马上定位问题如果等整个流程串完再查问题输出结果乱七八糟根本不知道错在哪一层。这套框架跑通之后扩展空间其实很大。比如把Logistic映射换成二维超混沌系统把DNA加法换成自定义碱基运算表或者在每一轮置乱之间动态更新密钥。有心深入的同学可以从这几个方向做对比实验跑出来的数据会比我这里的基准版本更有说服力。本文还有配套的精品资源点击获取
返回列表