news 2026/9/10 6:37:36

Matlab RSA图像加密:密钥生成、像素分块与模幂运算实现拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Matlab RSA图像加密:密钥生成、像素分块与模幂运算实现拆解

简介:这是一份面向图像加密入门者与Matlab开发者的RSA图像加密解密完整实现,可用于数字图像保密传输、教学实验与算法复现。代码基于Matlab 2019b编写,主程序main.m可直接运行,配套一系列功能函数完成密钥生成、像素级加密与解密还原,操作门槛低,适合初学者对照学习。压缩包共收录29个文件,其中包含13个.m源码文件、14张运行效果图以及txt与md说明文档,整体大小仅750KB,结构简洁、便于快速定位核心代码与验证结果。目前已有114人学习,程序经过亲测可运行,替换自己的图像数据即可观察RSA加密解密效果,同时附带的说明文档有助于理解算法流程与参数设置。

1. RSA图像加密:公钥密码用在像素矩阵上的第一道坎

拿一张灰度图直接套RSA,很多人第一次跑出来的密文图像根本不是噪声,而是还能看出原图轮廓的“马赛克”。原因倒不在RSA本身,而是像素值0到255这个取值范围,远小于公钥模数n,加密后统计分布被n“吸收”得不彻底,直接从密文灰度直方图就能反推明文分布。这套基于Matlab的RSA图像加密解密源码(SIT_Image_Encryption.m那一组文件,含密钥生成、进制转换、加解密主函数)解决的正是这条链路上的三个问题:把图像像素高效地转成RSA能处理的大整数、在n的比特长度约束下做分块、以及解密端如何无损还原成灰度矩阵。适合正在做数字图像处理课设、或者想把公钥密码和图像内容保护结合起来的Matlab开发者。下面先从RSA的参数含义讲起,再逐层拆到源码调用链。

2. RSA数学基础与图像二进制表达的映射关系

2.1 密钥生成三个参数的实际含义

RSA走的是“大整数分解困难”这条路。密钥生成阶段要做的事,是随机挑两个不相等的大素数p和q,算模数n=p×q,再算欧拉函数φ(n)=(p−1)(q−1)。之后选一个公钥指数e,要求1<e<φ(n)且gcd(e,φ(n))=1,最后用扩展欧几里得算法求e关于φ(n)的模逆元素d,满足e×d ≡ 1 (mod φ(n))。

这里有个关键点:e和n组成公钥,对外公开;d是私钥核心,必须保密。p、q在算出n和d之后,原则上是销毁的,工程实现里也有存起来给CRT解密用的做法。从图像加密的角度看,这三个参数直接决定了两件事:密文块长度(由n的比特位数决定)和加解密耗时(e和d的比特长度决定模幂运算次数)。源码里的SF_Key_Gen.m做的就是这件事,从函数命名习惯看,它内部大概率调用了Q_fun.m和P_fun.m来做素数候选生成与素性检测。

参数符号在图像加密中的作用常见设置
模数n决定每个密文块的比特长度,必须大于明文块数值灰度演示建议512位
公钥指数e加密端模幂运算的指数,影响加密速度常用65537
私钥指数d解密端模幂运算的指数,位数接近n由扩展欧几里得算出
欧拉函数φ(n)中间量,生成密钥后通常不参与图像运算随私钥保存或丢弃

公钥指数e直接写65537是工程上的常见做法。这个数是费马数F4=2¹⁶+1,二进制表示只有两个比特为1,模幂运算可以用平方-乘算法大幅减少乘法次数。源码里如果看到e是写死的大数,走的多半也是这个思路。反过来,如果e取得太小(比如e=3),在同余方程上有小指数攻击的风险,图像又是高度结构化的数据,明文块与加密结果的对应关系容易被穷举验证。

2.2 灰度图像为什么要做进制转换

图像像素和RSA之间的第一个矛盾就是进制。一幅M×N的8位灰度图,在Matlab里是一个uint8矩阵,每个元素是0到255的整数。RSA加密要求明文m是整数且满足0≤m<n,直接把0到255的像素值作为m去加密,数学上没问题,效果上很差:一方面明文字节只有8位,加密后密文c=m^e mod n的取值范围被n的位数撑大,解密后虽然能还原,但密文没法直接显示成有意义的图像;另一方面,全图只有256个不同明文值,相同像素加密结果完全相同,密文图像会呈现明显的分层轮廓。

所以实际做法是先把像素序列按n的比特长度分块,再把每个块拼成一个大整数。源码里那一堆进制转换函数,Dec2Bin.m、Binary2Dec.m、Hex2Bin.m、h2b.m、convert2bin.m,就是在做这个桥接。Dec2Bin把十进制像素值转成二进制位串,convert2bin负责把位串按块拼接成RSA能处理的大整数,Hex2Bin和h2b处理的是密钥或中间结果以十六进制表达时的转换。为什么要经过二进制而不是直接拼十进制?因为RSA密文的中间计算经常以十六进制展示,二进制位串与十六进制之间的转换可以用查表法一次映射4位,比反复除以256的十进制余数运算快得多。

2.3 分块与填充:明文块数值必须小于n

分块大小的计算规则:设n的二进制位数为bits_n,每个块能承载的明文最大值由log₂(m) < bits_n约束。对8位灰度图,如果n是512位,每个块最多放64字节,也就是64个相邻像素拼成一个大整数。用整数表达就是每个块的值等于把64个字节视为一个bignum,低位在前或高位在前都行,只要加解密两端约定一致。Matlab里做这个拼接,常见写法是把像素行向量按块长做加权累加。

% 分块拼接示例:将像素向量按块长拼成大整数序列 % pix_vec : uint8 行向量,长度能被 block_len 整除 % block_len : 每个块包含的像素个数,条件 256^block_len < n function blocks = pixel_blocks_to_bigint(pix_vec, block_len) n_blocks = numel(pix_vec) / block_len; % 重排成 [block_len, n_blocks] 矩阵,每列是一个块 pix_mat = reshape(pix_vec, block_len, n_blocks); % 列方向加权累加:第一个像素为最低字节,weight 按 256 幂递增 weights = uint64(256) .^ (0:block_len-1)'; blocks = zeros(1, n_blocks, 'uint64'); for k = 1:n_blocks tmp = uint64(pix_mat(:, k)); blocks(k) = sum(tmp .* weights); end end

这段代码的逻辑:每列代表一个块,块的第一个像素作为最低有效字节,权重数组weights按照256的幂递增,累加结果blocks就是可以直接送进SF_Encrypt.m的明文大整数序列。参数block_len不能随意放大,粗略估算条件是256^block_len < n,即block_len×8 < bits_n。超过这个限制,拼出来的大整数会大于n,RSA模运算会对明文做隐式取模,解密还原不回原始像素,这是最常见的解密后图像花屏的原因。

排错时优先级最高的检查项:先算n的位数,再算block_len×8,两者必须满足严格小于关系。另外要留意数据类型上限,用uint64做累加意味着这个写法更适用于n在512位以内、block_len不超过8的小规模演示。真正512位以上的RSA,Matlab自带类型装不下密文,需要借助Symbolic Math Toolbox的vpa或Java的BigInteger接口。源码里Scalling.m的出现,很可能就是在加密前后对数据做移位或缩放,保证整数转换和像素显示范围不互相冲突。

3. Matlab源码拆解:从SF_Key_Gen到SF_Decryption的调用路径

3.1 文件清单与职责划分

压缩包里那套代码,按功能分四组:密钥生成、格式转换、加解密核心、运行入口。SIT_Image_Encryption.m是主入口,加解密逻辑分散在多个SF_前缀的m文件里。从命名习惯和调用关系看,函数划分比较细,这也符合Matlab图像加密demo的一贯风格。

文件名分组职责推测
SIT_Image_Encryption.m入口读图、调度密钥生成与加解密、输出密文和还原图
SF_Key_Gen.m密钥生成p、q、n、e、d,返回公私钥结构体
Q_fun.m / P_fun.m密钥素数候选生成与Miller-Rabin素性检测
f_fun.m密钥计算欧拉函数φ(n)或辅助验证gcd(e,φ)=1
SF_Encrypt.m加密输入明文大整数序列与公钥,输出密文大整数序列
SF_Decryption.m解密输入密文与私钥,用模幂运算还原明文大整数序列
Dec2Bin.m / Binary2Dec.m转换十进制像素与二进制位串互转
Hex2Bin.m / h2b.m转换十六进制与二进制互转
convert2bin.m转换将像素矩阵转换成按块划分的二进制位序列
Scalling.m显示加解密结果与灰度显示范围之间的缩放映射

阅读顺序建议从SIT_Image_Encryption.m开始,沿调用链往下走,先看每个函数的输入输出,再回头读内部实现。比从头到尾逐个文件扫效率高,也更容易定位解密还原失败时到底断在哪一环。

3.2 加密链路:像素块如何变成密文

加密端的核心逻辑是取出每个明文大整数m,用公钥e和n做模幂运算c=m^e mod n。Matlab里对几百位的大整数做模幂,不能用内置的power或^直接算,必须自己实现平方-乘算法,或者借助Java的BigInteger.modPow。

% 模幂运算示例:计算 base^exp mod mod % 采用二进制展开的平方-乘算法,避免中间结果溢出 function result = mod_pow(base, exp, mod) result = 1; base = mod(base, mod); e_bits = de2bi(exp); % 转为二进制位,低位在前 for i = numel(e_bits):-1:1 result = mod(result * result, mod); % 每轮先平方 if e_bits(i) == 1 result = mod(result * base, mod); % 当前位为1则乘base end end end

逐行说明:result初始为1,是模幂运算的乘法单位元;循环从指数e的最高位往低位走,每轮先做一次平方再取模,遇到当前位为1再做一次乘法,乘法之后立即取模,保证中间结果始终小于mod。这个算法的乘法次数约等于log₂(e)加上e中比特位为1的个数。对于512位n、e=65537的情况,平方大约512次,乘法只有2次,耗时主要压在加密分块的数量上而不是单次模幂上。

加密链路的数据流是:uint8图像矩阵转成灰度,convert2bin展成位串,Dec2Bin按块拼成大整数,SF_Encrypt逐块模幂,得到密文大整数序列。这里有个容易踩的坑:密文大整数数值远大于255,直接imshow会全白或全黑,必须先对密文矩阵做归一化再转uint8,否则所谓密文图就是一张死黑图,容易误判为加密失败。

3.3 解密链路:CRT加速与Scalling还原

解密端数学上就是再算一次模幂m=c^d mod n。实际工程里很少直接对d做完整比特长度的模幂,而是用中国剩余定理加速:先把d拆成d mod (p−1)和d mod (q−1),分别在模p和模q下做模幂,再用CRT合并结果。对于图像这种海量分组的场景,加速比接近4倍,收益非常明显。

% 基于CRT的RSA解密示例:输入密文c、私钥d、素数p、q、模数n function m = rsa_decrypt_crt(c, d, p, q, n) dp = mod(d, p-1); % 私钥指数在模p-1下的缩减 dq = mod(d, q-1); % 私钥指数在模q-1下的缩减 m1 = mod_pow(c, dp, p); % 模p下的部分解 m2 = mod_pow(c, dq, q); % 模q下的部分解 qinv = modinv(q, p); % q 在模 p 下的逆元 h = mod((m1 - m2) * qinv, p); m = m2 + q * h; % CRT合并,得到模n下的明文 end

参数含义:dp和dq分别是d关于(p−1)、(q−1)的缩减指数,依据费马小定理,指数可以模(p−1)和(q−1)缩小而不影响结果;qinv是q在模p下的逆元,可以在密钥生成阶段预计算并随私钥一起存储。合并时h保证了m在两个模下同时满足同余条件。解密完成后,拿到的m序列再走Binary2Dec和Dec2Bin,把大整数拆回原始像素块,最后用Scalling做反缩放,得到可以imshow的灰度矩阵。

判断标准:解密图与原图的PSNR大于50dB,基本可以认定还原过程没有信息损失。如果出现个别像素偏差1到2个灰度级,多半是Scalling缩放取整导致的,不一定是RSA解密本身出错。

4. 跑通demo:Matlab 2019b下的完整操作与参数验证

4.1 运行环境与版本兼容

这套源码的开发版本标注是Matlab 2019b。操作前的准备:把解压得到的文件夹整体加入Matlab搜索路径,而不是只双击打开某个m文件。因为SIT_Image_Encryption.m会调用同目录下的SF_Key_Gen.m、SF_Encrypt.m等函数,脚本所在目录必须是当前路径,否则直接提示Undefined function或file not found。

% 将代码根目录加入搜索路径并确认 addpath('你的解压路径/SIT_Image_Encryption'); savepath; % 保存路径设置,避免下次重来 which SF_Key_Gen % 验证能找到密钥生成函数

执行完这段再打开SIT_Image_Encryption.m直接点Run。如果首次运行报错,优先检查两个地方:一是路径里有没有中文或空格,Matlab对含中文的路径支持不稳定,建议把整个文件夹放到纯英文路径下;二是Images子目录下的原图文件名是否与脚本里写死的路径一致,demo常见的坑是脚本找lena.png而实际放的是Orignal.jpg。

4.2 一次完整的加密解密运行

主脚本内部大致是下面这段调用链。用这段代码可以单独验证某个环节是否正常,也方便替换成自己的测试图。

% 主流程示例:读图、生成密钥、加密、解密、对比 img = imread('Images/Orignal.jpg'); if size(img, 3) == 3 img = rgb2gray(img); % 统一转灰度,简化处理 end [pub_key, priv_key] = SF_Key_Gen(512); % 512位RSA密钥 cipher_blocks = SF_Encrypt(img, pub_key); plain_blocks = SF_Decryption(cipher_blocks, priv_key); restored = reshape(plain_blocks, size(img)); psnr_val = psnr(uint8(restored), uint8(img)); fprintf('PSNR = %.2f dB\n', psnr_val);

逻辑说明:先读图并转灰度,保证输入是单通道uint8矩阵;SF_Key_Gen(512)里的512是模数n的比特位数,传得越大越安全但耗时指数级上升;加密返回密文大整数序列,解密后restored是double矩阵,需要转回uint8再和原图比较。PSNR超过50dB说明加解密闭环无损;PSNR为Inf说明restored与原图完全一致,这在尺寸较小的测试图上很常见。

参数方面的实际经验:n设512位时,一次完整加解密演示在普通笔记本上大约2到4秒,主要耗时在SF_Encrypt对每个分块做模幂;n=1024位时时间会拉到几十秒量级,不适合大图演示。如果只是想看流程跑通,建议先用128×128或256×256的小图,把n设成256位,几秒内就能看到结果。

4.3 运行结果图里能观察到的现象

源码压缩包里带了三张运行结果图,正常输出应当包含三类图像:

  • 原始灰度图:作为对照基准,显示原图纹理细节。
  • 加密后的密文图:看起来是均匀的椒盐噪声,肉眼读不出任何轮廓;如果还能看出原图边缘,说明分块拼接方式有问题或e取值太小。
  • 解密还原图:与原图逐像素一致,肉眼不可分辨差异。

判断加密效果可以自己算一个量化指标。明文图像的像素值高度集中,加密后的密文大整数序列如果映射回0到255显示,直方图应当接近均匀分布。

% 加密效果检查:密文灰度分布的均匀性 cipher_img = mat2gray(cipher_blocks); % 归一化到[0,1] cipher_img = im2uint8(cipher_img); % 转uint8显示 histogram(cipher_blocks, 64); % 观察分布是否平坦

如果直方图呈现明显多峰或与原图直方图形状类似,说明加密没有把统计特征打散。这时候按两个方向排查:p、q生成时是否太接近(比如相差小于10的几十次方量级),这会让n容易被费马分解法在可接受时间内破解;以及e是否过小,小指数在明文块较短的场景里容易被低密度明文攻击还原。

5. 进阶:RSA图像加密的小明文攻击防护与混合加密

5.1 确定性加密带来的轮廓泄露

RSA是确定性加密算法:同样的明文m,在相同密钥下永远得到同样的密文c。图像里大面积平坦区域,比如天空、白墙、桌面,存在大量连续相同像素。直接逐块加密后,这些区域的密文块完全一致,映射成灰度图后还是会形成一片均匀区域,攻击者可以从密文图里直接数出平坦区域的位置和形状。这个性质不是RSA的漏洞,而是确定性公钥加密在结构化数据上的固有特征。

对策有两条路。一是给每个明文块加随机填充,PKCS#1 v1.5的随机填充方案就能让相同像素块每次加密产生不同密文,代价是密文膨胀,每个块要额外带上随机字节。二是放弃全图直接加密,改用混合加密框架,这也是实际系统里更常见的工程选型。

5.2 混合框架:RSA只加密置乱参数

考虑到RSA模幂运算的耗时,图像加密里更实用的做法是先对图像做置乱,再用轻量级算法处理像素,RSA只负责保护置乱的密钥参数。Arnold置乱是常见的置乱手段,通过迭代变换把像素位置打散,置乱次数和行列参数作为密钥内容。RSA加密的只是这几十字节的参数,既保证了对攻击者的门槛,又把耗时降了几个数量级。

% 混合加密思路:用RSA加密Arnold置乱的迭代次数 iter = 5; % 置乱迭代次数,作为密钥内容 enc_iter = SF_Encrypt(iter, pub_key); % 用RSA加密这个参数 % 图像本体用Arnold置乱,迭代 iter 次后得到置乱图 % 后续像素级的扩散可以用Logistic映射或DNA编码来完成 dec_iter = SF_Decryption(enc_iter, priv_key); % 解密出迭代次数

这里的关键切换在于:RSA的输入从整幅图像变成了一个标量参数,明文空间从几万字节骤减到几个字节,加密耗时可以忽略不计。图像本体的保护由置乱和后续像素级操作完成。这种方案的附带收益是私钥泄露的风险面缩小了,即使拿到私钥,攻击者也只能还原出置乱参数,图像内容本身还被第二层加密保护着。

5.3 用直方图和像素相关性验证混合加密效果

最后给出一个简便的验证方法。加密算法改没改到位,跑三个指标就知道:明文密文的直方图差异、相邻像素相关系数、解密还原的PSNR。前两个指标不需要额外工具箱,用Matlab自带函数就能算。

% 相邻像素水平相关性计算 img_vec = double(img(:)); shifted = circshift(img_vec, 1); corr_val = corrcoef(img_vec, shifted); fprintf('明文相邻像素相关系数: %.4f\n', corr_val(1,2)); % 密文做同样计算,相关系数应当趋近于0 cipher_vec = double(cipher_img(:)); shifted_c = circshift(cipher_vec, 1); corr_val_c = corrcoef(cipher_vec, shifted_c); fprintf('密文相邻像素相关系数: %.4f\n', corr_val_c(1,2));

corrcoef返回2×2相关系数矩阵,取(1,2)位置就是两组数据的皮尔逊相关系数。明文图像的相邻像素相关系数通常在0.9以上,因为图像内容有强烈的空间连续性;加密后如果算法有效,这个系数会掉到0.1以下,甚至趋近于0。如果测出来密文相关系数还在0.5以上,说明加密只是对数值做了小幅扰动,没有把像素间的统计关联打断。这时候回头检查密钥位数和分块逻辑,比盲目加大图片尺寸更有效。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 6:34:53

瑞数5代反爬破解:补环境与__rsc后缀生成实战指南

简介&#xff1a;本资源聚焦瑞数5代&#xff08;rs5&#xff09;动态反爬机制的实战破解&#xff0c;面向中高级Python爬虫开发者与Web安全研究者&#xff0c;解决rs5环境补全、动态Cookie生成及URL后缀算法还原等核心难点。压缩包共7个文件&#xff0c;含5个JavaScript脚本&am…

作者头像 李华
网站建设 2026/9/10 6:34:22

视频AI中台实战:Docker多架构镜像与K8s弹性调度全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 6:34:13

MySQL误删数据恢复:binlog解析与备份回滚的实战指南

做运维和开发这么多年&#xff0c;几乎每个人都会遇到一次“手一抖&#xff0c;数据没了”的时刻。尤其是MySQL里执行UPDATE忘了加WHERE&#xff0c;或者DELETE的时候条件写错&#xff0c;那一瞬间心跳都会漏一拍。这篇文章不绕弯子&#xff0c;直接讲清楚误删、误更新之后&…

作者头像 李华
网站建设 2026/9/10 6:31:28

理解 AQS 核心原理:从排队模型到自定义同步器实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 6:30:54

基于Vue.js和Node.js的线上美术馆网站平台设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华