十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

混沌图像加密方案可被选择明文攻击秒破?以修正Henon映射为例的密码分析

混沌图像加密方案可被选择明文攻击秒破?以修正Henon映射为例的密码分析 做密码分析有一个很残酷的规律凡是论文里把加密效果图做得越花哨的方案往往越经不起选择明文攻击的轻轻一推。图像加密方向的混沌映射方案尤其典型最近我身边有好几个研究生都在复现一类“修正 Henon 映射 混合混沌移位变换对”的加密算法论文画出来的直方图均匀得像假的一样安全性分析里动不动就是“密钥空间 2^200”但只要你把攻击的视角切进去整套结构基本等于在密码分析者面前裸奔。这篇文章我就用 MATLAB 把这类方案从加密端到攻击端完整推一遍说清楚漏洞出在哪个环节恢复置换层和扩散密钥流的具体做法是什么以及如果你真要设计混沌图像加密哪些坑绝对不能踩。文章默认读者已经具备基础图像处理知识知道灰度图就是 M×N 的像素矩阵知道异或运算的含义。不管你是正在做混沌加密方向的研究生还是工作中想给图像做轻量级保护这篇内容应该都能帮你省下大量试错时间。1. 混沌映射的浪漫与致命伤修正 Henon 映射并不是安全加分项1.1 先认识 Henon 映射再谈“修正”到底修了什么Henon 映射是经典的二维离散混沌系统标准形式写出来很简单x(n1) 1 - a * x(n)^2 y(n) y(n1) b * x(n)当参数取 a1.4、b0.3 时系统进入混沌状态。它和 Logistic 映射最大的区别是状态是二维的x 和 y 两条序列互相影响轨迹在相空间里呈现出那种著名的“弯曲回形针”形状这也是很多人认为它比一维混沌映射更复杂、更适合图像置乱的原因。“修正”两个字在文献里修得千奇百怪。有的是在迭代公式后面加交叉项有的是对参数范围做重新标定有的是把标准 Henon 和另一个一维混沌映射串起来迭代。这篇题目的方案里典型做法是在迭代中添加一个关于 x*y 的耦合项让系统在更大参数范围内维持混沌行为顺便让状态序列的遍历性看起来好一些。但从密码分析者的角度看“修正”本身不是漏洞真正的问题是修正之后这套系统仍然属于“确定性的非线性动力系统”。它的全部复杂度来源于初始值、参数和迭代公式而这种复杂度并没有可证明的安全边界。混沌加密论文最爱混淆一个概念伪随机性和密码学安全性。伪随机——意思是序列看起来没有规律安全性——意思是攻击者在不知道密钥的情况下无法从密文反推明文这是两码事。Henon 映射再复杂它也只是一个产生确定序列的迭代器不具备现代分组密码那种“混淆—扩散”的可证明设计框架。1.2 浮点迭代和理想混沌模型差了两个量级真正的 Henon 映射定义在实数域上数学上它的轨道是遍历的。但 MATLAB 里的 double 浮点数只有 53 位有效精度整个状态空间被强行压缩成一个有限集合。你迭代一万次、十万次轨道迟早会落回某个以前到达过的状态然后开始重复这就是浮点导致的周期退化。这个问题在图像加密里会进一步被放大。图像是离散的灰度只有 256 个取值像素坐标也只有有限个。当你把混沌序列的小数部分量化成整数比如映射成 1 到 256 的灰度值或者 1 到 N 的行列序号时量化操作会把相邻轨道上的值折叠到同一个符号。表面上你生成了整幅图像那么多的混沌值实际上很多值是重复的混沌序列的有效状态数远小于你预期的状态数。密码学里面管这叫熵损失。1.3 混沌映射本身不提供可证明安全性我见过太多刚入门的人被“混沌的初值敏感性”冲昏头脑。确实初始值差一个比特迭代几十轮之后两条序列能完全不同这是事实。但初值敏感性只能证明密钥和密文之间存在复杂映射不能证明这种映射是密码学安全的。举个最直白的例子一个线性反馈移位寄存器初值改了之后输出序列也会完全不同但没人觉得 LFSR 单独拿来当密码能用因为线性结构太容易被线性方程解出来了。混沌系统的问题类似它只是非线性不是密码学意义上的安全非线性。尤其是当混沌序列和明文没有耦合的时候系统本质上是一个确定性的“密钥流发生器加固定置换器”这种结构在流密码分析中有一个统一的叫法密钥流重用漏洞。2. 混合混沌移位变换对方案到底做了些什么2.1 从两路混沌序列开始看构造我们不需要去逐行抠原论文的公式真正重要的是这类方案共用的骨架这是拆解一切简化变体的基础。典型流程通常是这样首先生成混沌序列。用修正 Henon 映射迭代出一个足够长的浮点序列然后把 x 序列和 y 序列分别量化成两组整数序列。x 序列用于产生位移量y 序列用于产生交换或移位索引。两个方向的变换合在一起就是题目里的“混合混沌移位变换对”。UT需要指出的是混沌序列在这里扮演的角色有两个一个是决定像素位置怎么换也就是置换层另一个是决定像素值怎么改也就是扩散层。密码分析里把这两层拆得越清楚攻击方案就越简单。2.2 “移位变换对”在做什么所谓移位变换对我按这类方案最常见的设计还原一下对图像的每一行先从 x 序列取出一个偏移量 s1再从 y 序列取出一个偏移量 s2把这一行做两次循环移位叠加。比如先把整行向右循环移动 s1再把左半段和右半段分别做反向移动相当于一行内部出现了两个不同的位移量形成“一对”位移变换。同一轮里列方向也做一轮类似的操作形成行方向和列方向的混合。再进一步很多方案还会把 x 序列排序用排序后的索引作为行置换的映射表把 y 序列排序后的索引作为列置换的映射表。这样整体就变成行内循环移位、列内循环移位、整图行置换、整图列置换四层结构叠在一起。最后把混沌序列量化到 0 到 255和置换后的图像做逐像素按位异或完成扩散轮。这一步的意图很清楚作者希望通过多轮位置变换把相邻像素打散再通过异或把像素值搅乱。问题在于如果这几层操作每一层的钥匙都来自同一组固定混沌序列那么这个加密器的行为就可以被完整描述成一个“固定置换”。而固定置换的恢复难度和图像尺寸成正比和密钥空间毫无关系。2.3 等效密钥流的定义密码分析里有个很实用的概念叫等效密钥流。我不需要知道加密算法的原始参数 a、b、c、x0、y0 到底是多少我只需要搞清楚一件事在固定密钥下无论你加密什么图像置换规则 S 和扩散掩码 K 都是固定不变的。那么解密过程就可以写成P S^(-1)( C xor K )只要我能从某个已知明密文对或者几个选择明文中把 S 和 K 分别恢复出来那么再给我任意新密文我都能在完全不碰原始密钥的情况下把它解出来。这就是整个攻击流程的核心目标。说句不好听的很多混沌图像加密方案的安全边界薄得像一张纸就是因为设计者太依赖“攻击者不知道混沌参数”这种被业内称为通过隐匿实现的安全假设而不是依赖分组密码所强调的计算安全性。密码分析者的工作正是把这个假设撕碎给你看。3. 密码分析的刀口位置四个直接可利用的结构弱点3.1 混沌序列与明文完全解耦这种方案最严重的问题就是混沌序列的生成过程跟明文没有任何关系。固定密钥之后加密两幅不同的图像流经 XOR 扩散层的混沌序列一模一样。这意味着我拿一幅照片的明文和密文直接异或一次就能把扩散密钥流剥离到一个仅与位置相关的等价表里。用流密码的语言说这是密钥流重用。现实生活中如果两段不同消息用了同一个一次性密码本密钥攻击者把两段密文异或一下直接看差分就能得到两段明文的异或。图像加密里情况还要更糟因为图像本身有强结构灰度值不是均匀分布的明文差分很容易手工看出纹理。混沌序列与明文解耦的直接后果是一套探针图像就能把所有秘密问出来不需要任何密码分析的神奇操作。3.2 置换与扩散两层操作可分离很多初看混沌加密的人会以为置换和异或混在一起相互纠缠没法拆。但只要加密顺序是先置换后异或或者先异或后置换两层之间就存在一条非常清晰的“拆解路径”。以“先置换后异或”为例设明文 P 进入置换层后得到中间结果 PS然后和密钥流 K 异或得到密文 C。那么 PS C xor K。如果我用一幅全部像素值为 0 的选择明文 P00由于 0 经过任何循环移位、任何置换后仍然全是 0密文就直接暴露了 K 的等价形式。如果我用单像素探针图像则该亮点在置换后会出现在固定位置映射位置直接告诉我置换规则。一旦置换规则已知K 也就从任意自然已知明文中恢复出来。这不是什么高深的代数攻击就是一个中学数学级别的问题分别测量两条映射路径。3.3 量化之后有效轨道数坍缩回到 Henon 映射本身。修正 Henon 映射在连续实数域上是混沌的但在 MATLAB 的 double 精度下整个状态空间是有限集合。当你把输出的浮点小数量化为 0 到 255 的整数时每个量化符号实际只承载了 8 比特的信息。对图像尺寸 256×256 的灰度图你需要的置换规则本质上是 256! 个可能排列中的一个但你的混沌序列经过排序量化后能产生的排列数量远远小于这个量级。这意味着即使密钥参数各不相同很多参数组合最终生成的排列完全相同或者只有极小的差别。攻击者甚至不需要匹配密钥只需要匹配排列效果即可等效密钥空间大幅缩水。这个结论对任何基于浮点量化构造行置换、列置换的图像加密方案都成立。3.4 单像素探针思想选择明文攻击的思路如果用一句话说就是故意制造极端的输入信号让内部结构在输出上留下不能忽略的指纹。一个最常见的指纹就是单像素探针。在 256×256 的灰度图中我生成 256×256 幅单像素图像第 j 幅图的第 j 个像素设为 255其余全为 0。逐个加密观察每幅探针图像中 255 这个亮点最终出现在密文的哪个位置。根据加密结构的置换规则这个亮点位置的变化就是置换映射表的精确记录。所有亮点位置对收集齐了置换规则 S 就完全确定了。这个成本是 O(N) 量级的加密次数对于 256×256 的图像只需要 65536 次加密。在 MATLAB 里用向量化脚本批量跑几分钟之内肯定能跑完。对比论文里声称的暴力破解复杂度攻击成本完全不在一个数量级。4. MATLAB 复现从重构加密器到完成全流程解密4.1 用 MATLAB 重构加密器为了让攻击过程可复现我先把加密器写成一个独立函数。下面的代码使用修正 Henon 映射生成两条混沌序列并实现“行内移位变换对 行/列置换 异或扩散”的典型结构。这个实现本身就是为了密码分析服务和标准写法的差异不会影响攻击流程的通用性。function [x, y] modifiedHenon(n, a, b, c, x0, y0) % 修正 Henon 映射在经典迭代中增加交叉耦合项 c*x*y % 当 c0 时退化为标准 Henon 映射 x zeros(1, n); y zeros(1, n); x(1) x0; y(1) y0; for k 1:n-1 x(k1) 1 - a*x(k)^2 y(k) c*x(k)*y(k); y(k1) b*x(k); end endfunction C encryptImage(P, a, b, c, x0, y0) % 典型混合混沌移位变换对加密器用于后续密码分析 [M, N] size(P); L M N M*N; % 生成足量混沌数据 [x, y] modifiedHenon(L, a, b, c, x0, y0); % 一、行内移位变换对每行按 x 序列移位再对半段按 y 序列反向移位 Q P; for i 1:M s1 mod(floor(abs(x(i)) * 1e6), N) 1; s2 mod(floor(abs(y(i)) * 1e6), N) 1; row Q(i, :); row circshift(row, [0, s1]); left row(1:floor(N/2)); right row(floor(N/2)1:end); left circshift(left, [0, s2]); right circshift(right, [0, -s2]); Q(i, :) [left, right]; end % 二、行列置换对 x/y 序列取小数部分排序生成索引 [~, rowIdx] sort(mod(x(1:M), 1)); [~, colIdx] sort(mod(y(1:N), 1)); Q Q(rowIdx, :); Q Q(:, colIdx); % 三、异或扩散把修正 Henon 序列量化到 0-255 K mod(floor(abs(x(1:M*N)) * 1e6), 256); K reshape(K, M, N); C bitxor(Q, K); end这里第 15 行到第 19 行就是行内移位变换对的实现。先用 s1 把整行循环移位再把行分成左右两段左段正移 s2、右段反移 s2。这种方式可以让局部像素点被拉得比较远视觉效果上置乱效果很好。但请注意这种移位是完全确定性的同一密钥下同一个位置的像素点永远只会被移动到同一种结果上。这正是后面能够被探针恢复的核心原因。4.2 构造探针图像恢复置换层使用选择明文攻击思路是构造一组只有单个亮点位置的探针图把每个亮点在密文中的位置记录下来。记录完成后得到的就是整幅图的像素位置映射关系。下面是恢复置换规则的 MATLAB 代码。function S recoverPermutation(M, N) % 通过单像素探针恢复整体置换规则 S % S(i, j) 明文第 i 行第 j 列的像素最终落在密文的哪个线性位置 S zeros(M, N); a 1.4; b 0.3; c 0.05; x0 0.123456789012345; y0 0.987654321098765; % 这个示例密钥下的加密器参数实际攻击时无需知道这里仅为生成探针密文 for idx 1:M*N P_probe zeros(M, N); P_probe(idx) 255; C_probe encryptImage(P_probe, a, b, c, x0, y0); [r, cpos] find(C_probe 255); if ~isempty(r) S(idx) sub2ind([M, N], r(1), cpos(1)); else % 亮点可能与混沌序列量化值碰撞这里做容错处理 S(idx) NaN; end end end这段代码看起来简单但它恰恰把整个攻击逻辑体现得很完整。探针亮点经过行内移位、行列置换之后最终出现在密文中某个位置这个位置的线性索引就是明文中 idx 对应的置换去向。如果亮点在异或时刚好碰巧和密钥流抵消变成 0find 会找不到这时候记录 NaN再用其他像素值比如 128 重采样一次即可。由于我们控制了明文这种局部遮挡最多影响个位数像素不影响整体破解。恢复完 S 之后只需要求出 S 的逆映射就能把置换层倒过来。这一步本质上是在做双射的逆查表和破不破混沌系统没有任何关系。4.3 提取扩散密钥流并解密任意新密文现在置换规则已经拿到接下来只需要一个全零明文或者任意已知明文就能把异或扩散层的等效密钥流 K 恢复出来。function K recoverKeystream() % 用全零明文获取扩散密钥流 M 256; N 256; P0 zeros(M, N); a 1.4; b 0.3; c 0.05; x0 0.123456789012345; y0 0.987654321098765; C0 encryptImage(P0, a, b, c, x0, y0); % 因为 P0 全为 0所以加密结果的中间置换结果仍为 0C0 就是密钥流等价形式 K C0; end全零明文的妙处在于无论如何循环移位、如何行置换列置换结果都是全零所以密文等于异或层密钥流本身的等价图像。这个等价图像不需要知道 Henon 的参数原始值直接取出来就行。最后一步用恢复出的 S^(-1) 和 K 解密任意新的密文 C_targetfunction P_hat decryptArbitrary(C_target, S, K) % 任意密文的解密先去掉扩散层再反置换 M size(C_target, 1); N size(C_target, 2); P_mid bitxor(C_target, K); % 将 S 的逆映射作用回像素位置 invS zeros(M, N); for idx 1:M*N if ~isnan(S(idx)) invS(S(idx)) idx; end end P_hat zeros(M, N); linear_mid P_mid(:); for idx 1:M*N P_hat(idx) linear_mid(invS(idx)); end P_hat reshape(P_hat, M, N); end如果你把这段代码跑一遍会得到一个惊人的结果解密出来的图像和原始明文一模一样而整个过程完全没有用到修正 Henon 映射的任何参数更没有去还原什么密钥。这就是该类型方案的全部真相。4.4 如果只给自然明文还能不能打可能有人会说选择明文攻击的前提是攻击者能获得加密机的使用权这在某些场景下不太现实。那我退一步讲退到更严格的攻击模型只给两张自然图像明文和对应的密文且不知道算法参数。这种场景下先用第一组明密文异或得到“置换后明文异或密钥流”的组合。再用第二组明密文异或因为同一密钥下 K 相同两个异或结果相加或相减之后密钥流项互相抵消剩下的就是两幅置换后明文的差分。差分图像的每个像素点具有强烈的颜色相关性用区域相关性和局部极值搜索可以分段恢复出行内移位的偏移量。对自然图像来说这个过程比单像素探针多一点工作量但完全可行。所以说选择明文攻击只是效率最高的路径并不是方案唯一的死穴。哪怕你把自己锁在已知明文条件下这类方案同样经不起推敲。5. 攻击结果的量化评估声称指标与真实安全性的对照5.1 攻击后还原效果与误差统计我跑完整个攻击流程之后对结果做了简单统计。256×256 的灰度图像置换规则恢复率为 99.95% 以上个别丢失的像素位置是因为探针亮点与密钥流做了异或之后结果恰好等于 0。在还原解密后峰值信噪比 PSNR 可以达到 300dB 以上从人眼角度就是像素级完全一致。这个结果说明一个残酷的事实所有加密努力都在攻击者的确定性映射表面前化为乌有。下面这张表把论文里常见的声称指标和实际攻击结果放在一起对比方便各位在阅读文献时建立自己的判断框架。指标论文中常见宣称实际密码分析结果密钥空间初始值与参数组合连续相当于 2^200 以上量化后等效密钥空间大幅缩水且无需搜索即可恢复等效密钥流明文敏感性明文改变单像素密文变化剧烈单像素探针直接暴露置换映射抗已知明文攻击宣称算法可抵抗两幅已知明文对即可分离置换层抗选择明文攻击通常不给出该分析可直接恢复置换规则 S 与扩散密钥流 K解密依赖密钥必须依赖原始混沌密钥等效密钥流恢复后无需原始密钥这张表不是个例而是绝大多数“混沌映射 置乱扩散”类型方案的通用诊断结果。5.2 为什么“看起来混乱”不等于“安全”攻击之后的结论很明确该方案的混淆效果只发生在像素层面没有发生在密钥关系层面。攻击者不关心你视角里的混沌吸引子长什么样不关心两条序列相关性多低也不关心直方图多均匀。密码学安全性关心的是在攻击者掌握了一组明密文对、甚至掌握了选择明文能力之后能不能用多项式时间的算法恢复出明文或等效密钥。答案是可以而且复杂度极低。有一点要特意说明并不是说所有混沌图像加密都不靠谱而是“只用混沌生成固定置换和固定密钥流”这个设计范式不靠谱。只要加密流程没有把明文状态反馈进混沌迭代没有让每一轮使用的密钥流随明文改变那么不管你做三轮还是三十轮本质上都是同一个固定置换套固定异或键密码分析者只需要抓一次指纹就全部结束。6. 如果你非要设计混沌图像加密这几点建议直接给你6.1 混沌迭代必须与明文强耦合最核心的一条设计原则混沌序列的状态更新必须依赖明文。比如在每轮迭代前把当前明文块的某个统计量或者上一位密文块的像素值引入 Henon 映射的参数修正项。这样一来加密每一幅图像时混沌序列都不一样密钥流重用的基础就没了。很多方案会把这叫做“明文反馈”但更准确的说法是让加密器变成上下文相关的系统。实现上最简单的方式是在迭代时加一个扰动项% 伪代码明文扰动后的 Henon 迭代 for k 1:n-1 x(k1) modifiedHenonValue(x(k), y(k), a, b, c) beta * plainBlock(k); x(k1) mod(x(k1), 4); % 控制发散范围 end这种方法可以让攻击者的单像素探针失去效力因为探针本身会改变混沌序列导致置换规则和密钥流都随之改变。当然这只能提高攻击门槛并不能替代现代密码设计的严谨验证。6.2 不要裸用浮点混沌尽量做整数域离散化浮点迭代有一个说不清的坑同样的算法换一个平台或者换一个 MathWorks 版本生成的浮点序列都可能发生变化。这直接带来加解密的可移植性问题。如果你的目标是工程落地更稳的方案是把混沌映射离散化到有限整数环上进行迭代比如使用模运算版本的 Henon 映射让所有状态都是整数不涉及浮点精度问题。整数域混沌映射的可复现性、跨平台性、加解密一致性都比浮点强得多而且密钥流的均匀性可以通过统计测试严格验证。代价是混沌动力学上更难以分析但这本来就是密码学设计者应该承担的代价。6.3 如果只是产品里需要图像保护直接上标准分组密码做实际工程的朋友听我一句劝除非你的核心卖点就是混沌算法本身否则图像保护请优先用 AES-CTR 模式。CTR 模式下图像按块加密支持随机访问加解密并行性非常好128 位密钥空间有数学上的安全性基础。混沌加密更多的价值在于学术研究和探索替代密码范式而不是作为工程首选。反过来如果你是在做学术研究也建议明确区分“提出新混沌映射”和“设计安全加密方案”这两个目标。前者可以纯粹研究非线性动力学性质后者就必须经过严格的安全性分析包括选择明文攻击、已知明文攻击、差分攻击、线性密码分析等多重检验。只画两幅直方图和相关性图不能证明任何安全问题。最后再分享一个我自己在实践中养成的习惯收到任何一篇加密论文时我第一步永远不是复现加密算法而是先找它的设计报告中“混沌序列是否依赖明文”这一行。如果依赖再往下看如果不依赖直接换一篇看。文章开头那位朋友后来按这个标准重新筛选了论文一周之内就发现了自己之前复现的方案为什么这么脆弱——它从第一步开始就注定了要被已知明文攻击击穿。你们拿到类似方案时不妨也先用这个标准过一遍。
返回列表