FEATURED · 精选文章

Matlab实现RSA加解密:从欧拉定理到powermod完整代码

发布时间 / 2026/9/14 6:14:57
来源 / 创域科博编辑部
栏目 / 资讯中心
Matlab实现RSA加解密:从欧拉定理到powermod完整代码 简介一份面向密码学初学者的基于RSA图像加密MATLAB源码包覆盖密钥生成、公钥加密、私钥解密及图像像素预处理等关键环节。资源共6个文件包括4个MATLAB脚本、1个Markdown说明文档和1张示例图片整个压缩包大小仅348KB已有963人学习下载。脚本内置扩展欧几里得算法求解私钥d采用常用公钥指数65537并利用大素数p、q及欧拉函数生成密钥对将图像像素值作为明文先转换为二进制序列完成像素分组再通过MATLAB大整数运算完成C M^e mod n与M C^d mod n的标准RSA加解密流程。说明文档对算法原理、参数选取和运行步骤做了系统梳理其中示例图片用于直观验证解密后图像的还原效果。代码包结构紧凑便于在课程设计或信息安全入门实验中快速复现尤其适合本科信息安全课程设计或RSA入门实践。1. 先用 Matlab 把 RSA 的加密解密过程跑通再谈参数和安全很多工程师第一次在 Matlab 里接触 RSA是从一个需求开始的要给某个接口传一段密文对方只发来一串公钥却没有现成的加解密库可调。RSA 的加密解密过程在书面上只有两行——公钥加密、私钥解密本质上都是模幂运算——但真要在 Matlab 里走通会撞上大整数精度、密钥格式、字节编码等一系列问题。这篇文章按数学原理到可复现代码的顺序把 RSA 在 Matlab 中的完整实现路径拆开讲适合需要在 Matlab 里做 RSA 加解密原型、毕设或工具脚本的读者。先给结论借助符号数学工具箱的sym和powermod十几行代码就能在 Matlab 里跑通 RSA 的加密解密过程真正的难点不在算法而在参数和编码细节。2. RSA 加密解密过程的数学骨架欧拉定理、扩展欧几里得与模幂2.1 从欧拉定理开始理解 RSA 加解密过程RSA 加解密过程的第一步是生成一对密钥。选两个不相等的大素数p和q计算模数n p * q再计算欧拉函数φ(n) (p-1)*(q-1)。然后选一个与φ(n)互质的公钥指数e私钥指数d满足e * d ≡ 1 (mod φ(n))。公钥是(n, e)私钥是(n, d)p、q、φ(n)必须保密。加密时把消息m看成一个大整数要求0 m n计算c powermod(m, e, n)解密时计算m powermod(c, d, n)。这里能成立的理论基础是欧拉定理当gcd(m, n) 1时m^φ(n) ≡ 1 (mod n)。由e*d k*φ(n) 1可以推出m^(e*d) ≡ m^(k*φ(n)1) ≡ m (mod n)恰好符合加密再解密的往返条件。在实际使用时e一般固定取65537也就是十六进制的0x10001。这个数是费马数二进制表示里只有两个 1做模幂运算时乘法次数少速度比随机选取的e快得多同时它在安全上已经被广泛验证。Matlab 里构造这些数学量时推荐直接用符号整数参与运算后续章节会解释原因。2.2 扩展欧几里得求模逆d 的计算计算私钥指数d是 RSA 加解密过程中的关键一步核心是求e在模φ(n)下的乘法逆元。最常用的手算思路是扩展欧几里得算法通过反复取余得到一组贝祖系数使得U*e V*φ(n) gcd(e, φ(n))。因为e与φ(n)互质右边等于 1所以U*e ≡ 1 (mod φ(n))U对φ(n)取正余数后就是d。在 Matlab 里不需要手动写辗转相除循环符号数学工具箱的gcd函数直接支持返回贝祖系数% 选两个小素数做演示仅用于理解过程 p sym(61); q sym(53); n p * q; phi (p - 1) * (q - 1); % 公钥指数取 65537 的简化版本 17便于观察数字关系 e sym(17); % [G, U, V] 满足 G U*e V*phi且 G gcd(e, phi) [G, U, V] gcd(e, phi); % 因为 e 与 phi 互质G 等于 1所以 U 就是 e 的模逆 d mod(U, phi); fprintf(n %s\n, char(n)); fprintf(phi %s\n, char(phi)); fprintf(d %s\n, char(d));逻辑说明gcd的第二个和第三个返回值就是贝祖系数U*e V*phi 1成立时U直接满足U*e ≡ 1 (mod phi)。最后用mod把U归一到[0, phi-1]区间避免出现负数。注意这里所有变量都包了sym()没有这一步gcd在 64 位整数范围内也能算但一旦模数超过int64上限就会出错。2.3 快速模幂 powermod加密解密过程中的数学核心RSA 加解密过程在数学上就是大整数的模幂也就是计算a^b mod n。如果先算a^b再取模a、b、n都是几百位的大整数时中间结果会大到耗尽内存计算时间也完全不可接受。业界通行做法是平方乘算法把指数按二进制拆开边平方边取模让每一步的中间值都控制在n的规模以内。这个算法的复杂度是O(log2(b))次模乘所以指数e固定为65537时只需约 17 次模乘。Matlab 里不需要自己实现平方乘符号数学工具箱提供的powermod函数就是干这件事的。下面这段代码展示最小闭环% 明文必须小于 n这里取一个小于 n 的整数 m sym(42); % 加密 c powermod(m, e, n); % 解密 m2 powermod(c, d, n); % 校验往返结果 assert(isequal(m, m2), RSA 往返失败);参数说明powermod(m, e, n)计算m^e mod n三个参数都支持符号整数返回值也是sym类型。这里m不能大于等于n否则加密解密结果不保证还原这是一种需要在实际分块时处理的消息长度约束。初学者最容易踩的坑是直接用mod(m^e, n)一旦m^e的位数达到几十万位Matlab 会长时间卡在求幂阶段看起来像死循环实际上是在做超大整数乘法。用powermod是绕开这个性能陷阱最直接的方式。3. Matlab 实现 RSA 加解密的最小完整代码3.1 用 sym 保留大整数精度Matlab 加解密不能直接用 doubleMatlab 默认数值类型是 double尾数只有 53 位能精确表示的整数上限大约是2^53 ≈ 9e15。RSA 的模数n只要超过这个范围double 就会发生舍入密钥从一开始就是错的。演示用的小素数61和53乘积只有3233double 能装下但真实场景里 1024 位以上的n远远超出 double 的表示能力。因此所有关键量必须用符号整数保存。符号整数通过sym构造支持从字符串、double 或者 Java 对象转换。下面两种方式都合法% 从字符串构造避免先经过 double 截断 bigN sym(114894136969824880933); % 从 double 构造仅当输入值小于 2^53 时安全 smallN sym(3233);参数说明sym(114894136969824880933)中的字符串会被原样解析成符号整数不存在精度损失。sym(3233)是从 double 转过去的安全前提是 double 本身没有舍入。所以在 RSA 加解密过程中建议所有参与计算的整数都以字符串形式进入sym或者用 Java 的BigInteger生成后再转换。另一个细节是sym不支持0x开头的字符串遇到十六进制公钥参数时要先用hex2dec转成十进制字符串再用sym。3.2 密钥生成p、q、e、d 的生成函数把密钥生成封装成独立函数后续所有测试脚本都能复用。演示版本采用随机选素数的方式bits参数控制每个素数的二进制长度function [n, e, d, p, q] rsaKeygen(bits) % 生成 RSA 密钥对演示用bits 建议不超过 24 % 返回公钥 (n, e)、私钥 (n, d)以及用于验证的 p、q if bits 3 error(bits 至少为 3); end lo 2^(bits-1); hi 2^bits - 1; % 随机生成 p要求是素数 p sym(0); while isprime(p) ~ 1 p sym(randi([lo hi])); end % 随机生成 q要求是素数且不等于 p q sym(0); while isprime(q) ~ 1 || q p q sym(randi([lo hi])); end n p * q; phi (p - 1) * (q - 1); % 优先使用 65537若不互质则继续找下一个奇数 e sym(65537); while gcd(e, phi) ~ 1 e e 2; end % 利用扩展欧几里得计算私钥指数 [~, U, ~] gcd(e, phi); d mod(U, phi); end逻辑说明randi负责生成候选素数范围isprime对sym输入也能正常工作。因为q的生成条件里同时要求q ~ p可以避免模数被素数平方替代。e从65537开始递增检查只取奇数确保与phi互质。这个函数在bits不超过 24 时运行很快用于教学和功能验证足够了。如果需要生成 1024 位以上的大素数randi会因为 double 精度限制直接失败。常见做法是借助 Matlab 内置的 Java 接口调用java.math.BigInteger.probablePrime然后把结果转成sym% 生成 512 位素数的 Java 版做法注意随机源单独传入 bj java.math.BigInteger.probablePrime(512, java.util.Random()); p sym(char(bj.toString()));说明probablePrime内部已经做了 Miller-Rabin 素数测试调用成本远低于在 Matlab 里自己写循环试探适合生成生产级 RSA 密钥时使用。3.3 加密解密全过程验证从密钥到密文再到明文下面的脚本把密钥生成、逐字节加密、逐字节解密串成一条完整链路。为了直观这里用一个短字符串作为消息展示全部数据处理过程% 生成 16 位素数模数约 32 位仅供测试 [n, e, d, p, q] rsaKeygen(16); msg hello rsa; % 将字符串转为 uint8 字节数组每个字节值都在 0~255 之间 m_vec uint8(msg); % 转为符号整数数组 m_sym sym(m_vec); % 逐字节加密 c_sym powermod(m_sym, e, n); % 逐字节解密 r_sym powermod(c_sym, d, n); % 符号数组转回 uint8再还原字符串 r_bytes uint8(double(r_sym)); restored char(r_bytes); assert(strcmp(msg, restored), 还原失败); disp([还原结果: , restored]);逻辑说明sym(m_vec)会把整个 uint8 数组整体转换成符号数组powermod对数组输入逐元素运算所以不需要显式写 for 循环。解密后double(r_sym)能安全转回 double是因为 n 是 32 位模数时每个解密的字节值最大也只有 255。这个脚本验证了 RSA 加解密过程的核心逻辑公钥加密私钥解密结果可逆。如果消息字节值超过 n逐字节加密会失效这就需要引入分块逻辑下一章专门处理。4. 把 demo 变成工程参数选择、中文编码与常见报错4.1 参数选择p、q、e 到底该怎么选RSA 的安全性建立在 n 的因式分解难度上所以 p 和 q 的位数直接决定安全等级。下面是不同用途的参数参考表使用场景模数 n 位数p/q 位数e 推荐值备注教学演示16~32 位8~16 位17 或 65537秒级完成仅用于理解过程实验室内部128~256 位64~128 位65537能抵御教学级别的攻击互联网公钥2048 位1024 位65537当前实际部署的通用标准长期敏感数据3072 位及以上1536 位以上65537安全性冗余较高选择 p、q 时还要注意两点。第一p 和 q 的数值不能太接近否则可以用费马因式分解快速破解常规做法是要求abs(p - q)的位数与 p 接近。第二p-1 和 q-1 都不能只有很小的素因子否则 Pollard 算法会很快分解 n。这两点在教学 demo 里体现不出来但写进生成逻辑里会稳妥很多。e 在绝大多数情况下固定为 65537不要因为看到其他值就随意替换尤其是不能选太小的 e否则在满足特定消息条件和极低填充质量时存在被低指数攻击的风险。4.2 处理中文和长消息UTF-8 编码与分块策略上一章的脚本对 ASCII 字符串有效但直接对中文调用uint8会得到错误的字节序列因为 Matlab 的char采用 UTF-16 编码。正确做法是通过unicode2native把字符串转成 UTF-8 字节解密后再用native2unicode还原。下面是完整示例% 初始化和编码 [n, e, d] rsaKeygen(16); msg RSA 加密解密过程; % UTF-8 编码得到字节数组 bytes unicode2native(msg, UTF-8); % 逐字节加密演示逻辑真实场景应改为分块加密 c_bytes powermod(sym(bytes), e, n); r_bytes double(powermod(c_bytes, d, n)); % UTF-8 字节还原为字符串 restored native2unicode(r_bytes, UTF-8); assert(strcmp(msg, restored), 中文还原失败); disp(restored);分块策略说明真实 RSA 加密中明文必须小于 n逐字节加密只是演示性质。实际项目里通常先把消息编码后的字节数组按floor(log2(n)/8)字节分成若干块每块转换成一个符号整数后再做模幂。更标准的做法是使用 OAEP 填充通过加入随机数让相同明文每次生成不同密文避免确定性加密带来的模式泄露。Matlab 没有内建的 OAEP 实现团队项目中一般采用 Java 的Cipher接口或调用 OpenSSL 命令行完成这类需求。4.3 常见排错rsa public key not find 与符号运算的坑很多读者在联调时会遇到形如rsa public key not find的报错。这类错误大多发生在尝试用第三方库读取 PEM 公钥时常见原因是公钥文件路径不对或者 PEM 内容缺失。但需要注意Matlab 本身没有内建的 RSA 公钥解析函数rsa public key not find更常见的诱发点是外部库在解析 DER 编码的密钥时失败。解决办法是先用 OpenSSL 命令把密钥参数提取成十进制或十六进制文本再交给sym读取# 提取 PEM 中的模数 n输出为十六进制字符串 openssl rsa -pubin -in public.pem -modulus -noout # 得到 ModulusBD31A99C0A2...将其作为 sym 的输入然后把十六进制转成十进制字符串再用sym构造大整数。另一个容易踩的坑是sym不识别0x前缀必须先用hex2dec% 错误示例sym 无法解析 0x 前缀 % n sym(0xBD31A99C); % 正确做法先把十六进制转十进制字符串 hexStr BD31A99C0A2; n sym(hex2dec(hexStr));符号数学工具箱里的mod同样需要留意。mod(-3, 5)在 Matlab 中返回 2rem(-3, 5)返回 -3计算私钥指数 d 时只能用mod确保结果落在[0, phi-1]区间。如果发现 d 的数值带负号多半是用了rem或手动取余实现不正确。处理符合同余式时始终使用mod是最安全的约定。提示R2023b 及更高版本的系统路径中若同时装有多个 Matlab 版本调用 Java 接口时可能因为java.util.Random类的可见性问题报错遇到时重启 Matlab 并在新的会话中执行脚本即可。5. 用 gcd 自查 RSA 公钥从公因数攻击案例看参数边界5.1 公因数网络攻击案例与检查代码现实中发生过多次基于 RSA 公钥共因数的网络攻击案例攻击者从互联网上批量抓取 RSA 公钥对模数两两计算最大公约数。如果两个模数之间存在大于 1 的 gcd说明这两个公钥共享了某个素因子。一旦出现这种情况攻击者立刻就能分解两个模数进而还原私钥。这类攻击不是因为 RSA 算法本身被破解而是因为部分设备在生成密钥时使用了质量不高的随机数源导致不同证书间出现重复素数。在 Matlab 里检测自己的多个公钥是否共享素因子代码非常短。假设有两个公钥模数n1和n2% 两个公钥的模数单位是十六进制 n1Hex BD31A99C0A2; n2Hex C456D1E03F1; n1 sym(hex2dec(n1Hex)); n2 sym(hex2dec(n2Hex)); g gcd(n1, n2); if g 1 fprintf(发现共享素因子: %s\n, char(g)); p g; q n1 / p; phi (p - 1) * (q - 1); % 用常见的 e 65537 恢复私钥 e sym(65537); [~, U, ~] gcd(e, phi); d mod(U, phi); fprintf(私钥已被还原d %s\n, char(d)); else fprintf(当前两个模数没有公共因子\n); end逻辑说明如果n1和n2都包含因子 p那么gcd一定会返回 p 或更大的公因数。得到 p 后q n1 / p直接完成因式分解接下来欧拉函数、私钥指数 d 的计算链路就与正常密钥生成完全一致。这段代码对教学层面的密钥安全自检很有意义也解释了为什么参数章节强调 p 和 q 必须独立生成、不可复用随机种子。在实际工作中可以把这段 gcd 检查脚本放在密钥生成流水线后。对批量生成的密钥对逐一做两两互检虽然随着公钥数量增加计算量会平方增长但 n 的位数在 2048 位时几十个公钥的检查耗时也就几秒。相比密钥泄露的后果这十秒成本完全值得。生成时随机源必须是密码学安全随机数不能用系统时间做种子否则即使检查通过了下一次生成仍可能落入相同随机序列的陷阱。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻