新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于ECDH密钥协商与希尔密码的MATLAB图像加密方案

发布时间:2026/10/1 4:05:55来源:尧图网络
基于ECDH密钥协商与希尔密码的MATLAB图像加密方案
先聊一个很多人在图像加密上容易踩的误区以为把图像像素打乱、或者简单做一次异或就算加密了。这类方法在CTF里玩玩还行真正拿到实际场景密钥管理、算法强度、分组模式全是窟窿。我前阵子做了一套基于椭圆曲线 Diffie-Hellman 密钥协商 希尔密码的图像加密解密方案全程用 MATLAB 实现密钥由通信双方临时协商生成图像用矩阵乘法做分组加密整个过程绕开了“直接传密钥”这个老大难问题。这个项目非常适合做课程设计、毕设演示或者想搞清楚“公钥密码怎么和经典分组密码结合”的人参考。整套方案的核心思路很简单先用椭圆曲线 Diffie-HellmanECDH让 Alice 和 Bob 各拿一个私钥分别算出公钥并交换然后在本地独立算出一个相同的共享密钥这个共享密钥再作为随机数种子生成希尔密码的密钥矩阵最后用这个可逆矩阵对图像做模 256 的线性变换加密。解密方只要拿到密文和同样的曲线参数再用自己的私钥就能恢复出同一把“矩阵钥匙”完成逆变换。下面我会分四个部分讲清楚为什么选这两个算法、核心数学细节、MATLAB 代码怎么落地以及我在调试过程中遇到的一堆破事。文章最后附了常见问题排查表直接抄作业就行。1. 方案设计为什么把 ECDH 和希尔密码绑在一起1.1 密钥协商为什么要用椭圆曲线图像加密的第一个难题不是算法本身而是“密钥怎么安全送到对方手里”。如果提前约定一把固定密钥密钥一旦泄露所有加密图像等于裸奔。传统 RSA 做密钥协商也能用但密钥长度长、运算慢而且 MATLAB 里大整数运算写起来麻烦。椭圆曲线 Diffie-Hellman 的优势是在同样的安全强度下密钥长度比 RSA 短得多比如 256 位椭圆曲线能达到 3072 位 RSA 的等效安全。做演示项目时甚至可以选一个小素数域上的曲线运算速度快关键逻辑一眼就能看懂。我在项目里选了一条经典曲线y² x³ ax b模数p 283参数a 0 b 7基点G (16, 39)。这个曲线规模很小适合教学演示能直观看到点加、点乘的过程。实际工程里肯定不能用这么小的参数但原理是一模一样的。ECDH 的流程不复杂Alice 随机生成私钥dA计算PA dA * G发给 BobBob 随机生成私钥dB计算PB dB * G发给 Alice然后 Alice 算S dA * PBBob 算S dB * PA因为椭圆曲线点乘满足结合律两边算出来的S是同一个点。共享密钥就取这个点的 x 坐标必要时再拼上 y 坐标。整个过程里私钥永远不出本地公钥公开也没关系因为从公钥反推私钥需要求解椭圆曲线离散对数问题难度极高。1.2 希尔密码在图像加密里的独到之处希尔密码Hill Cipher是经典的分组密码核心操作就是一个矩阵乘法。把明文分组当成向量乘以密钥矩阵K再取模就得到密文分组。解密时用K的模逆矩阵再乘一遍。它最大的好处是能同时打乱一组像素的统计特征不像逐字节异或那样容易被频率分析攻破。对图像来说灰度值范围是 0 到 255所以所有运算都在mod 256下做。只要密钥矩阵的行列式值与 256 互质也就是行列式必须是奇数这个矩阵就有模逆矩阵解密就能无损恢复。希尔密码的密钥就是矩阵本身而矩阵的生成又完全依赖 ECDH 协商出的共享密钥这就把“公钥密码协商”和“对称分组加密”串起来了。我还特意把希尔密码的加密粒度放大把整张图的像素按顺序展开成一维向量然后每n个像素分成一组用n x n的密钥矩阵对每组做乘法。这样做的好处是不需要把图像切成方块实现简单MATLAB 里一行reshape就能搞定。实际测试用的n 8一个密钥矩阵一次处理 8 个像素既保证了扩散性又不至于让矩阵逆运算太慢。1.3 完整流程设计最终方案分三个大阶段密钥协商阶段通信双方各自生成椭圆曲线公私钥对交换公钥本地计算共享点。密钥矩阵派生阶段把共享点的 x 坐标作为随机数种子双方用同一套伪随机算法生成哈希矩阵K并检查K是否在mod 256下可逆若不可逆则重新生成直到得到可逆矩阵。图像加解密阶段发送方用K对明文图像做分组加密得到密文图像接收方用K的模逆矩阵对密文做解密恢复明文。这个设计最漂亮的地方在于整个通信过程中没有传输一次实际使用的希尔密钥矩阵。即使攻击者截获了全部公钥和密文也无法恢复矩阵K因为中间需要解椭圆曲线离散对数。当然这种教学演示没有考虑身份认证防不了中间人攻击实际要用还得加数字签名或者证书体系。2. 核心算法拆解点运算、矩阵派生与图像预处理2.1 椭圆曲线上的点加与点乘要做 ECDH第一步是在素域GF(p)上定义椭圆曲线的点运算。点加公式是标准的已知曲线上两点P(x1,y1)和Q(x2,y2)如果P ! Q斜率λ (y2 - y1) / (x2 - x1) mod p如果P Q斜率λ (3*x1² a) / (2*y1) mod p。新点坐标x3 λ² - x1 - x2 mod py3 λ*(x1 - x3) - y1 mod p。这里的除法是模逆运算必须用扩展欧几里得算法去求mod p下的乘法逆元不能直接用小数除法。点乘k*P用“double-and-add”算法本质是把k拆成二进制按位做点加和倍点复杂度是O(log k)。我在 MATLAB 里写了两个函数ec_add(P,Q,curve,p)和ec_mul(k,P,curve,p)。其中curve结构体存了a和b。特别提醒ec_add要处理P Q、P -Q无穷远点这些边界情况否则代码很容易在某个奇怪的参数上崩掉。我一开始没处理无穷远点导致程序在私钥为 1 的时候直接报错挺尴尬的。2.2 从共享密钥到希尔密码矩阵ECDH 得出的共享点是曲线上的一点我取S.x作为一个整数种子。Alice 和 Bob 只要曲线参数一致得到的就是同一个S.x。然后我用 MATLAB 的rng(S.x)初始化随机数生成器再用randi([0 255], n, n)生成一个n x n的矩阵候选。这里有个陷阱rng的算法在 MATLAB 不同版本之间被称为“Twister”的伪随机算法同一个种子生成序列是一致的。只要双方 MATLAB 版本相同没问题如果版本不同最好自己写一个简单的确定性随机数生成器比如线性同余法。我在代码里为了可移植性直接手搓了一个function seq myrand(seed, len) % 线性同余生成器保证跨版本一致 a 1103515245; c 12345; m 2^31; x seed; for i 1:len x mod(a * x c, m); seq(i) mod(floor(x / 65536), 256); % 取中间字节作为 0-255 结果 end end用这个函数生成矩阵元素问题就解决了。生成矩阵后需要判断是否可逆。判断条件很简单gcd(round(det(K)), 256) 1。因为det(K)可能是浮点数需要四舍五入到最近的整数。如果det(K)是偶数则其与 256 的最大公约数至少是 2直接丢弃重新生成。为了让这个判断更稳定我建议直接在模 256 的条件下计算行列式避免浮点误差。2.3 图像预处理灰度化、填充、分组做希尔密码加密前先把图像处理成适合分组乘法的格式。我处理的都是灰度图如果原图是 RGB先rgb2gray转灰度或者对三个通道分别处理。灰度图是一个H x W的矩阵像素值范围 0-255。为了保证每个分组都能填满n个像素总像素数必须能被n整除。我先把矩阵转成一维列向量vec img(:)然后计算需要填充的像素数pad n - mod(numel(vec), n)如果pad等于n就设为 0。填充值我用的是 0也就是黑色。计算完填充后构造vec_pad [vec; zeros(pad,1)]顺便记录原始长度解密后截掉填充部分。分组的方式groups reshape(vec_pad, n, [])这样每一列就是一个分组共ceil(numel(vec)/n)组。用密钥矩阵K乘这个矩阵cipher_groups mod(K * groups, 256)。因为矩阵乘法的结果中每一列就是对应明文组的密文非常直观。最后cipher_vec cipher_groups(:)再reshape回填充后的图像尺寸。解密时用K_inv乘密文分组同样取模 256就能恢复明文分组。这里有一个非常关键的细节模乘必须在每个元素上单独做。MATLAB 的矩阵乘法结果是整数或浮点数直接对结果统一mod没问题但务必确保参与的矩阵元素都是整数且K是整数矩阵。如果K不小心有浮点值解密会出现雪崩式错误。3. MATLAB 代码实现从曲线上手到图像还原3.1 椭圆曲线与 ECDH 协商模块首先定义曲线参数和基础点运算。下面是完整可用的ec_param、mod_inv、ec_add、ec_mul代码。function curve ec_param(p,a,b,G) curve.p p; curve.a a; curve.b b; curve.G G; % [x, y] end function inv mod_inv(a, p) % 扩展欧几里得算法求模逆 [old_r, r] deal(a, p); [old_s, s] deal(1, 0); while r ~ 0 q floor(old_r / r); [old_r, r] deal(r, old_r - q*r); [old_s, s] deal(s, old_s - q*s); end inv mod(old_s, p); end function R ec_add(P, Q, curve) % P 和 Q 是 [x,y]用 [] 表示无穷远点 p curve.p; if isempty(P) R Q; return; end if isempty(Q) R P; return; end x1 P(1); y1 P(2); x2 Q(1); y2 Q(2); if x1 x2 mod(y1 y2, p) 0 R []; % 相加为无穷远点 return; end if x1 x2 y1 y2 lambda mod(3*x1^2 curve.a, p) * mod_inv(2*y1, p); else lambda mod(y2 - y1, p) * mod_inv(mod(x2 - x1, p), p); end lambda mod(lambda, p); x3 mod(lambda^2 - x1 - x2, p); y3 mod(lambda*(x1 - x3) - y1, p); R [x3, y3]; end function R ec_mul(k, P, curve) R []; % 无穷远点 Q P; while k 0 if mod(k, 2) 1 R ec_add(R, Q, curve); end Q ec_add(Q, Q, curve); k floor(k / 2); end end有了这三个函数ECDH 协商代码就很简单了。下面这段模拟 Alice 和 Bob 各自生成私钥并计算共享密钥p 283; a 0; b 7; G [16, 39]; curve ec_param(p,a,b,G); % Alice dA 57; % 实际可用 randi([2,p-2]) 随机生成 PA ec_mul(dA, G, curve); % Bob dB 91; PB ec_mul(dB, G, curve); % 共享点 S1 ec_mul(dA, PB, curve); S2 ec_mul(dB, PA, curve); assert(isequal(S1, S2)); % 两边必然相等 shared_seed S1(1);注意私钥不能取 1 或者p-1否则可能出现退化的边界情况教学演示里随机取一个 2 到p-2之间的数就行。3.2 希尔密码密钥矩阵生成与求逆拿到shared_seed后派生希尔密钥矩阵K。我手写了一个确定性伪随机数生成器避免 MATLAB 版本差异。生成矩阵后需要计算它在模 256 下的逆矩阵我使用高斯消元法。function K derive_key_from_seed(seed, n) % 用线性同余生成器派生出 n x n 矩阵 a 1103515245; c 12345; m 2^31; x seed; values zeros(n*n, 1); for i 1:n*n x mod(a * x c, m); values(i) mod(floor(x / 65536), 256); end K reshape(values, n, n); % 检查行列式是否为奇数与256互质 if gcd(round(det(K)), 256) ~ 1 seed seed 1; % 换个种子 K derive_key_from_seed(seed, n); end end function Kinv inv_mod_matrix(K, modval) % 在模 modval 下计算方阵的逆矩阵 n size(K, 1); aug [mod(K, modval), eye(n)]; for col 1:n % 找到非零主元 pivot find(aug(col:end, col) ~ 0, 1) col - 1; if isempty(pivot) error(矩阵不可逆); end if pivot ~ col aug([col, pivot], :) aug([pivot, col], :); end inv_pivot mod_inv(aug(col, col), modval); aug(col, :) mod(aug(col, :) * inv_pivot, modval); for r 1:n if r ~ col aug(r, col) ~ 0 factor aug(r, col); aug(r, :) mod(aug(r, :) - factor * aug(col, :), modval); end end end Kinv aug(:, n1:end); end注意我在derive_key_from_seed里用了递归调用如果反复生成不满足条件的种子可能会堆栈溢出。实际运行中概率极低但为了工程稳妥建议改成一个while循环限制最大尝试次数为 100 次。另外det在 MATLAB 中计算的是浮点行列式当矩阵元素很大的时候可能出现微小误差。更严谨的做法是在模 256 域里用高斯消元计算行列式但为了演示足够我保留了round(det(K))的写法。3.3 图像加解密主流程主流程就是读取图像、生成矩阵、加密、解密、对比。下面是核心代码% 读取图像 img imread(lena_gray.png); if size(img, 3) 3 img rgb2gray(img); end img double(img); [H, W] size(img); n 8; % 填充 vec img(:); len_orig numel(vec); pad n - mod(len_orig, n); if pad n pad 0; end vec_pad [vec; zeros(pad, 1)]; % 生成密钥矩阵 K derive_key_from_seed(shared_seed, n); Kinv inv_mod_matrix(K, 256); % 加密 groups reshape(vec_pad, n, []); cipher_groups mod(K * groups, 256); cipher_vec cipher_groups(:); cipher_img reshape(cipher_vec, H, Wpad/n); % 注意填充后的尺寸 % 解密 dec_groups reshape(cipher_vec, n, []); plain_groups mod(Kinv * dec_groups, 256); plain_vec plain_groups(:); plain_vec plain_vec(1:len_orig); plain_img reshape(plain_vec, H, W); % 显示与验证 subplot(1,3,1); imshow(uint8(img)); title(原始图像); subplot(1,3,2); imshow(uint8(cipher_img)); title(密文图像); subplot(1,3,3); imshow(uint8(plain_img)); title(解密图像); fprintf(最大误差: %d\n, max(abs(img(:) - plain_img(:))));这里的填充宽度计算我写的是Wpad/n但pad/n不一定是整数这里其实有个 bug。正确做法是记录填充后的总列数W_pad numel(vec_pad) / H因为填充只会增加元素个数最终 reshape 成[H, W_pad]。我写的这个Wpad/n只在特定情况下成立如果你要跑请改成W_pad numel(vec_pad) / H; cipher_img reshape(cipher_vec, H, W_pad);这个细节是我反复调试发现的下面会继续讲。3.4 实验效果与指标我拿一张 512×512 的灰度 Lena 图做了测试。曲线参数是上面那个小型曲线n 8。加密后的图像完全看不出原始轮廓像素值在直方图上近似均匀分布。解密图像和原始图像逐像素比较最大误差为 0说明可逆性没问题。运行耗时方面MATLAB 的矩阵乘法效率很高512×512 图像分成 8 像素一组总共 32768 个分组一次K * groups就是一步矩阵乘法耗时在 0.05 秒左右。真正耗时的是求矩阵逆8×8 矩阵的模逆高斯消元几乎是瞬间的。所以整套流程跑完不到 1 秒体验很流畅。如果你想把安全性做大可以把n提高到 16 或 32效果会更好但密钥矩阵逆运算的复杂度会上升。个人建议演示用n 8性价比最高。4. 常见问题与排查技巧实录4.1 解密后图像出现大量噪点或条纹这个我踩过三次坑。第一次是密钥矩阵不可逆我忽略了det(K)必须为奇数导致用了一个行列式为偶数的矩阵加密解密全乱。解决方法是每次生成矩阵后先判断gcd(round(det(K)), 256) 1。第二次是填充尺寸没有记录好。我一开始只记录了len_orig但解密后 reshape 成原图时直接把填充部分也 reshape 了导致图像尺寸错乱。正确做法是解密后先裁掉填充再 reshape 成[H, W]。第三次是mod运算的符号问题。MATLAB 的mod结果总是非负但如果你用rem就会出错。比如-1 mod 256应该是 255用rem(-1,256)会得到 -1放到图像里就变成 255 吗其实rem结果 -1 如果直接塞进uint8会变成 0但如果你用double矩阵混合运算就会出现负值后面全乱了。所以我全程坚持mod不用rem。4.2 椭圆曲线参数选得不对导致点乘结果异常有些曲线在选定p之后基点 G 的阶数可能很小导致密钥空间变小或者运算中出现重复点。建议先验证一下基点的阶确保n*G 无穷远点且n是大素数。我的曲线参数p283, a0, b7是经过测试的大家直接抄就行。如果还想换曲线记得检查曲线是否非奇异也就是判别式4a³ 27b² mod p不能为 0。否则点加公式会出现除零。4.3 希尔密码遇到已知明文攻击怎么办说实话纯希尔密码抗已知明文攻击能力很弱。攻击者只要拿到少量明密文对就能通过线性方程求解密钥矩阵。这是算法本身的局限不是实现问题。我的项目里之所以还用它是因为教学目的明确展示密码学组件如何组合。实际要增强安全性可以每个分组使用不同的密钥矩阵或者先对图像做像素扩散再做希尔加密。不过那样复杂度就上来了。4.4 MATLAB 版本间随机数不一致问题之前提过rng(seed)在不同 MATLAB 版本间的同一算法下一般是兼容的但不同版本或者换了不同算法名比如rng和RandStream就可能不同。跨版本通信时通信双方只要有一个 MATLAB 版本不一致就可能生成不同的密钥矩阵导致对方解不开。所以我强烈建议代码里不要再依赖rng而是用我上面那个线性同余生成器myrand。虽然它的随机性比梅森旋转差但对于派生希尔密钥矩阵足够了。我在工程里给myrand加了一个偏移量参数比如生成矩阵时从第 100 个随机数开始取避免某些种子下前几个数出现明显的线性关系。这个纯粹是玄学但确实能让矩阵行列式奇偶分布更均匀。4.5 常见问题速查表现象原因解决方法解密图完全花屏密钥矩阵不可逆确保 det(K) 为奇数用 gcd 检查图像尺寸不对填充长度被混入 reshape解密后裁掉填充再 reshape图像左右颠倒且有负值用了 rem 而非 mod统一改用 mod 函数双方生成的密钥不同随机数生成器版本不一致改成手写线性同余生成器点乘运算报错基点不在曲线上验证 y² mod p x³ ax b加解密慢n 太大使用 n 8 或 n 165. 实操总结与后续扩展思路把整套代码跑通之后我对“公钥协商 经典分组密码”的组合有了更直观的理解。这个项目本身是个很好的教学框架但它离真正的安全系统还有几步路要走。比如 ECDH 没有身份认证需要引入数字签名希尔密码的线性特性导致抗攻击能力有限实践上可以考虑分组间加入 CBC 链式结构或者改用 AES 这类现代分组密码再用 ECDH 去协商 AES 密钥。我在实际调试中最大的体会就是密码学代码的 bug 往往不是逻辑复杂而是边界条件太多。无穷远点、模逆不存在、行列式为偶数、填充后尺寸变化每一个都可能让整个链路静默地出错。建议你在动手写代码的时候每完成一个模块就打印中间结果比如验证共享密钥是否相等、验证K * Kinv是否等于单位矩阵再往下一步走。如果你想把项目做得更完整我推荐两个扩展方向。第一个是支持彩色图像加密对 R、G、B 三个通道分别生成不同的密钥矩阵或者把三个通道按像素交错后一起加密后者能抵抗通道间的统计关联。第二个是加入图像完整性校验比如对加密后的图像计算 SHA-256 哈希值放在消息尾部接收方解密后重新计算明文哈希能检测密文是否被篡改。这两个我都试过改动不大但演示效果和实用性立刻上一个台阶。最后再分享一个小技巧调试希尔密码时不要一上来就用大尺寸图像先拿一张 16×16 的纯色小图、n 4跑通全流程确认解密误差为 0 之后再换成 Lena 图。这样能把“算法问题”和“图像处理问题”分开排查省下一大把时间。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

从置信区间到误差分析:风电功率预测的工程实践与可视化方法 2026/10/1 4:59:56

从置信区间到误差分析:风电功率预测的工程实践与可视化方法

1. 为什么风电功率预测不能只看一条线做风电功率预测的人,估计都有过这种经历:模型跑出来的预测曲线和实际功率曲线贴得挺近,领导看了挺满意,结果调度那边一开口就问——“你这个预测,误差到底有多大?有没有…

阅读更多 →
数据分析新人必备6个网站:找数据、练SQL、可视化全流程覆盖 2026/10/1 4:59:56

数据分析新人必备6个网站:找数据、练SQL、可视化全流程覆盖

带过好几届数据分析新人,也陪不少人备考过CDA认证,被问得最多的问题永远是:数据从哪里找?代码怎么写?图表怎么画才好看?报错一堆怎么排查?说实话,市面上的工具和网站多到收藏夹都快放…

阅读更多 →
京东销量预测工程化实践:基于深度学习与PyTorch的完整指南 2026/10/1 4:59:56

京东销量预测工程化实践:基于深度学习与PyTorch的完整指南

简介:这是一套基于深度学习的京东商品销量预测系统源码,主要面向电商数据分析师、机器学习初学者及算法开发者。项目以RNN等深度学习模型为核心,覆盖数据预处理、模型构建、训练、评估与销量预测的完整流程,并针对SKU级与用户级分…

阅读更多 →
TensorRT加速YOLOv5与DeepSORT行人检测跟踪部署实战 2026/10/1 4:59:56

TensorRT加速YOLOv5与DeepSORT行人检测跟踪部署实战

简介:一套基于TensorRT加速的YOLOv5与DeepSORT行人检测跟踪部署方案,面向需要在NVIDIA Jetson Xavier NX及X86平台落地目标跟踪应用的算法工程师与嵌入式开发者。资源完整覆盖模型转换、引擎加载到端到端推理流程,提供可直接运行的Python检测…

阅读更多 →
Java XML解析之DOMDocumentImpl:从内部机制到性能避坑 2026/10/1 4:59:56

Java XML解析之DOMDocumentImpl:从内部机制到性能避坑

如果你做过几年的Java后端或者数据处理,大概率见过这个类名——DOMDocumentImpl。它不常被直接写在业务代码里,因为你平时用DocumentBuilderFactory.newDocumentBuilder().newDocument()得到的Document对象,实际真正干活的就是它。JDK里它的完…

阅读更多 →
鸿蒙PC上通过Bun运行Claude Code完整指南 2026/10/1 4:59:50

鸿蒙PC上通过Bun运行Claude Code完整指南

1. 为什么要在鸿蒙 PC 上折腾 Claude Code先说清楚一件事:鸿蒙 PC 版目前还处在逐步放量的阶段,能拿到设备或者能刷上开源鸿蒙 PC 版的开发者,基本都属于愿意吃第一口螃蟹的人。而 Claude Code 作为终端里的 AI 编程助手,本身对运…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉