ARTICLE DETAIL

资讯详情

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

应用密码学实验从零实现:DES、RSA与哈希签名核心要点详解

应用密码学实验从零实现:DES、RSA与哈希签名核心要点详解 简介本资源是吉林大学应用密码学课程配套的完整实验实现包面向密码学初学者、信息安全专业学生及密码算法实践者聚焦分组密码设计、公钥密码实现、混合加密系统构建与盲签名协议落地四大核心能力训练。压缩包共27个文件含5个C源码.cpp、5个可执行程序.exe、7个编译中间文件.o及4个工程配置文件.cfp/.cfpg辅以1份Word实验说明文档和1份文本说明总大小1.02MB结构清晰便于按实验模块快速定位代码与运行环境。已有1731人学习下载涵盖Feistel结构128位分组密码含LFSR轮函数、基于NTL库的RSA密钥生成/加解密、数字信封RSAOFB混合加密及Chaum盲签名四个完整可运行方案所有算法均提供源码级实现与对应可执行文件支持开箱即用与原理验证。 大家做应用密码学实验的时候八成都会陷入同一个状态上课听算法流程感觉每个字都懂一旦自己动手写代码才发现连密钥编排时先把数组左移几位这种细节都能纠结一下午。吉林大学这门应用密码学实验课我当年也是一路踩坑走过来的网上能找到的参考代码要么只有片段要么和老师要求的实验报告格式对不上。所以这次把整套实验从环境搭建到核心算法实现再到最后报告验收的完整思路整理出来纯粹是我个人基于实操经验的总结给后来的人做个参照。课程内容本身用的都是公开教材里的经典算法属于密码学本科阶段的必学内容我把细节和坑都摊开来说希望对大家有帮助。1. 实验整体设计与思路拆解1.1 应用密码学实验到底在做什么应用密码学这门课和纯理论的密码学不太一样它强调的是“把算法真正跑起来”。吉林大学的这门实验课核心目标就是把教材上的几个经典密码算法用代码实现出来包括古典密码、对称密码、公钥密码和哈希签名。表面上看是写程序其实更偏向算法理解和工程实现的结合。我在做之前把实验要求从头到尾读了几遍发现老师真正想考查的核心点有两个第一你是否真的理解算法的数学原理而不是只会调用现成库第二你是否能够在动手实现的过程中发现算法在工程落地时的细节问题比如字节序、数据填充、边界条件这些。第三个隐藏的考查点是代码质量比如是否有内存越界、是否考虑了异常输入、是否输出了清晰的调试信息。这些才是拉开差距的地方。所以我的整体设计思路是先吃透每个算法的数学原理再用C语言从零实现一遍最后用标准测试向量验证正确性。有些算法比如AES和SHA-256从零实现的代码量不小如果时间紧张可以参考OpenSSL的实现思路但绝不能只看代码不思考因为实验验收时会提问。1.2 实验环境选型与准备工作考虑到吉大实验室的机器大多安装Windows系统而且很多同学之前只学过C语言我用的是Visual Studio加纯C语言的组合。这个选择有两个原因一是VS的调试功能对看中间值非常友好尤其是数组越界和内存泄漏这类问题调试器能直接帮你定位二是C语言在密码算法实现里依然是主流语言无论是参考国外教材的示例代码还是查阅论文C代码的资料最全。有个建议可以提前做好在电脑上装一个Python环境虽然实验要求用C写但你可以用Python来验证结果的正确性。比如RSA计算出来的密文直接用Python的pow(c, d, n)跑一遍立刻就能知道自己写的模幂运算对不对。这种“C语言实现加Python验证”的双轨思路能让调试效率翻倍因为实验课的时间本来就紧哪里要手写哪里能依赖工具自己心里要有数。代码规范上我也踩过坑刚开始图省事把所有函数堆在main函数里结果改一个bug要翻几十行才找到。后来老老实实分成模块比如DES的s_box、key_schedule、feistel都单独成文件每个模块配一个简单的自测函数这样每次改完就能单独测试。2. 核心实验细节解析与实操要点2.1 古典密码实验从凯撒到维吉尼亚古典密码是实验里最简单的部分但千万别掉以轻心它有几个容易翻车的地方第一个就是字符取模的负数问题。凯撒密码的加密公式是(ch - a key) % 26 a看似简单但在C语言里如果明文是akey是3那没问题可如果做解密ch - a - key可能变成负数C语言的%运算结果也可能是负数直接加上a就变成乱码。所以解密时要写成((ch - a - key) % 26 26) % 26这一步当时坑了好几个同学。维吉尼亚密码的难度略有提升因为它引入了密钥流。我当时的实现方式是维护一个密钥索引j只对字母字符进行加密非字母字符保持原样。这里有一个典型的工程细节即密钥索引该不该跟随非字母字符前进。正确答案是不前进否则同一份明文带上标点符号加密结果就会不同不符合密码算法输入输出确定性的要求。仿射密码的难度主要集中在求乘法逆元上。加密公式是E(x) (a * x b) mod 26穷举法或扩展欧几里得算法都可以求出逆元。我建议老老实实用扩展欧几里得算法因为RSA实验里也要用到现在练熟了后面就轻松多了。注意a必须和26互质否则加密不是一一映射解密会得到错误结果。我当时验证互质时偷懒没有检查结果解密出来前几个字符是对的后面全乱了排查了半天才发现是选错了a值。2.2 对称密码实验DES的Feistel网络实现要点DES实验是很多人的分水岭因为它的流程长涉及初始置换、16轮Feistel轮函数、逆初始置换还有子密钥生成。我的实现建议是先把三个关键表IP表、E扩展表、S盒表照抄进代码然后用标准测试向量逐步验证明文0123456789ABCDEF密钥133457799BBCDFF1经过第一轮之后的中间结果也应该符合教材上的参考值。这个方法能快速定位是哪一步表抄错或移位写错。DES里最容易出错的是子密钥生成中的循环左移。每轮移位的位数不是固定的第一轮左移1位第二轮也左移1位第三轮开始左移2位这个表记错就全错。我当时把每轮左移位数的数组定义为{1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1}但代码里循环移位用函数实现时容易把移位逻辑搞反导致生成的全部子密钥都不对。建议在生成子密钥时把每一轮的K_i打印出来和教材或网上的参考值比对任何一个不对都别急着往下跑。轮函数F里的S盒替换下标索引也有讲究。S盒的输入是6比特输出4比特其中第一位和最后一位组成行号中间四位组成列号。我当时第一次实现时直接把6比特当列号去查表结果自然全错。还有一个容易忽视的点是E扩展表的索引是从1开始的还是0开始的建议所有置换表统一减1后存成数组避免记混。2.3 公钥密码实验RSA的大数运算与素性检测RSA实验是整个应用密码学实验里最能体现数学功底的。教材上轻描淡写的一句“生成两个大素数p和q”真实现起来相当棘手因为C语言内置的int类型根本装不下大数。我当时实现了一个最基础的bignum结构用数组存储十进制数字然后实现加减乘除和模运算。这个方法虽然效率不高但胜在直观、不容易写错对于本科实验完全够用。素性检测方面我用的是Miller-Rabin算法。为什么要用概率性检测而不是直接试除因为1024比特级别的RSA模数用试除法等到天荒地老也测不完。Miller-Rabin的思路本质上是利用费马小定理的逆否命题如果a^(n-1) mod n ! 1那n一定是合数如果等于1n大概率是素数。多选几个不同的底数a比如前几个小素数误判的概率会低到可以忽略。我当时选了{2, 3, 5, 7, 11}这组底数对实验要求的密钥长度来说已经足够可靠。大数模幂运算也必须自己实现不能用乘法再求模因为中间结果会溢出。标准做法是快速幂取模也就是把指数拆成二进制从低位到高位逐位处理每一步都取模这样能保证中间结果始终可控。代码结构大概是void mod_pow(BigNum *result, BigNum *base, BigNum *exp, BigNum *mod) { BigNum temp; copy_base(result, 1); // 初始化为1 copy_base(temp, base); // temp base while (!is_zero(exp)) { if (exp-digits[0] 1) { // 当前位为1 mul_mod(result, result, temp, mod); } mul_mod(temp, temp, temp, mod); // temp temp^2 mod mod shift_right(exp); // 指数右移一位 } }需要特别注意这里对指数进行移位时会破坏原始指数所以调用前最好先备份。我当时就在这里吃过亏做完一次加密后想再解密发现指数已经被改写成0了又得重新生成密钥浪费时间。2.4 哈希与数字签名实验从原理到实现哈希实验和签名实验往往放在一起做。MD5和SHA-1的算法细节都比较繁琐核心是搞清楚消息填充和轮函数。消息填充这一块规则是先在消息后面补一个1再补若干0直到长度模512等于448最后再用64比特存原始消息长度。这个规则里最容易出错的是长度用小端还是大端存储MD5用小端、SHA-1用大端我当时没注意这一点算出来的摘要一直和标准值对不上。数字签名实验我基于RSA做了一次签名验证流程先对消息做哈希得到摘要再用自己的私钥对摘要加密得到签名接收方用公钥解密签名得到摘要再和重新计算的消息摘要做比对。签名就是“私钥加密公钥验证”这个流程用代码实现时和加解密几乎一模一样但思想上要清楚RSA加密用的是公钥签名用的是私钥两者不能混。这里有一个容易犯的错误签名的摘要应该用十进制转换成大数后参与模幂运算而不是直接把字符串丢进去逐字节运算。我当时直接对字符数组做模幂算出来的结果当然不对。正确做法是把摘要按字节转成一个大整数再走RSA的模幂流程。3. 实操过程与核心环节实现3.1 从实验报告要求反推实现清单吉大应用密码学实验通常要求每一份报告包含实验目的、实验原理、实验步骤、核心代码、实验结果截图和实验心得。我一般先看老师有没有给评分细则根据细则来安排时间和重点。如果评分重点在原理部分那代码注释和流程图就要写得详细如果评分重点在结果验证那测试向量的截图和对比就要充分。我给自己定了一个实现顺序先完成相对独立、代码量小的实验比如凯撒密码和维吉尼亚密码一来能快速进入状态二来可以在实验课当场找老师验收通过后面压力就小了。DES和AES这类大型算法优先把标准测试向量跑通确认无误后再扩展功能比如增加文件加密、命令行参数解析等这些扩展功能往往是加分项也能体现你对算法的掌握不局限于课本。3.2 关键代码实现与参数选择以RSA实验为例我给出一个直接可用的参数选择思路。实验并没有要求极长的密钥我选的是64位十进制数级别的RSA实际上为了便于调试先选择小素数比如p61q53。这样n3233φ(n)3120选e17验证gcd(17,3120)1然后用扩展欧几里得算法求出d2753。这个经典的RSA例子里任何人都可以口算验证加密结果最适合用来调试代码。扩展欧几里得求逆元的核心代码int gcd_extended(int a, int b, int *x, int *y) { if (b 0) { *x 1; *y 0; return a; } int x1, y1; int gcd gcd_extended(b, a % b, x1, y1); *x y1; *y x1 - (a / b) * y1; return gcd; }注意求出来的x可能是负数需要调整成模φ(n)下的正数即d (x % phi phi) % phi。我实测试过不少同学在这一步没做归一化结果解密时d是负数模幂循环里的移位逻辑直接出错。素性检测的Miller-Rabin实现我建议单次测试的底数不要固定写死在函数里而是作为参数传入。这样既能测试默认底数也能在调试时替换为小底数跑得更快。我当时写完检测函数后先测试了一堆已知素数和小合数比如15、21、341著名的伪素数确保它能正确区分才应用到密钥生成里。3.3 标准测试向量验证流程验证这一步的重要性怎么强调都不为过。DES的标准测试向量是明文0123456789ABCDEF、密钥133457799BBCDFF1、密文85E813540F0AB405只要你的实现完全正确这个结果必须完全一致。AES-128的标准测试向量是密钥000102030405060708090A0B0C0D0E0F、明文00112233445566778899AABBCCDDEEFF、密文69C4E0D86A7B0430D8CDB78070B4C55A。我当时通过比对中间值来排查发现S盒输出和标准文本不一样最后查出来是S盒表格数据抄错了几行。MD5验证可以用字符串abc标准摘要应该是900150983CD24FB0D6963F7D28E17F72。这些标准向量网上一搜一大把但是要养成把验证结果截图保存的习惯因为实验报告的“实验结果”部分通常都需要展示。验证工具方面Linux下的openssl enc -des-ecb和Windows下的在线工具都可以做交叉验证。我自己的习惯是写一个小型Python脚本直接调用pycryptodome库计算标准向量然后对比C程序的输出。如果两个结果不一致就逐步输出C程序的中间状态比如DES第一轮L0、R0RSA加密后的密文和Python的逐段计算结果比对用二分法定位出错的地方。3.4 实验报告的高效整理方式实验报告不是写小说我总结了一套高效的整理方式先画一张算法流程图再贴核心代码和注释最后放测试向量验证截图。流程图可以用PPT或者draw.io画只要清晰表达算法的输入输出和核心循环结构即可不要求美观。代码部分不要贴全部源码贴关键模块比如DES的轮函数、RSA的mod_pow、Miller-Rabin的检测过程。每个模块配一段注释解释“这段代码在做什么”和“为什么这么做”而不是逐行翻译代码。老师在验收时会看代码是否能回答“为什么这么设计”的问题所以注释里最好出现算法本身的术语比如“Feistel结构保证加密解密的对称性”“快速幂取模避免中间结果溢出”。实验心得尽量写自己实际踩过坑、最后怎么解决的。例如“在实现DES时我最初把E扩展表下标当成从0开始结果和教材上的二进制E扩展表对应不上后来统一把所有置换表都转换成从0开始的数组才解决”。这种真实的反思比“通过本次实验加深了对密码算法的理解”这类空话更有价值。4. 常见问题与排查技巧实录老话讲得好代码写得再顺出了问题不会排查也是白搭。我把自己做这套实验时遇到的高频问题整理成一个速查表大家可以直接对照使用。常见现象可能原因排查方法解密结果前几个字符正确后面乱码仿射密码的a和26不互质检查gcd(a,26)是否等于1确保密钥合法DES密文和标准向量不一致S盒表抄错或轮密钥生成移位方向错打印第一轮和最后一轮的L、R中间值和子密钥对照教材参考值AES输出完全不对S盒表顺序错或列混合矩阵系数错误先用标准向量验证S盒输出再单独测试列混合函数RSA解密失败或返回错误值明文大于n确保明文转换后长度小于n的二进制位数RSA解密结果时对时错d不是模φ(n)下的逆元检查扩展欧几里得后是否做了负数归一化MD5摘要和标准不一致长度填充使用了错误端序打印填充后消息的十六进制参考RFC 1321逐步比对程序调试时数组越界置换表下标没减1检查所有查表操作下标是否超出数组范围这里再分享几个我后来才悟到的排查技巧。第一个技巧是调试时不一定要用完整的大向量。DES的测试向量虽然标准但太长不便于观察中间状态。你可以临时把密钥设为0000000000000000明文设为0000000000000000然后和网上的参考值对比这样中间结果看起来更清晰因为全是0很容易发现哪一步被错误地改变了。AES同理可以用全零密钥和全零明文作为调试基线。第二个技巧是对RSA这种带随机性的算法调试时先用固定的小素数比如p61、q53避免随机素数导致验证困难。调试通过后再改回大素数。第三个技巧是多利用条件断点。比如DES的16轮循环你可以在循环体里设置条件断点当round 8时打印当前L和R的值。这一步能直观看到中间状态的变化比事后打印一堆日志更容易定位问题。我自己印象很深的一个bug是AES的轮数搞错了。AES-128应该是10轮AES-192是12轮AES-256是14轮。我当时误写成11轮结果密文自然完全不对。排查了半天才意识到这个低级错误。大家在写代码时建议把密钥长度和轮数用宏定义写清楚。5. 实验验收的注意事项实验课的最后一步是验收这个环节比想象中重要因为老师打分并不只看你代码能不能跑还会随机问问题。我在验收时被问过“为什么RSA要选大素数”“DES的Feistel结构有什么好处”虽然没有标准答案但能感觉到老师更在意你是否真的理解算法。我的建议是在演示代码前先把每个算法的核心流程在脑子里过一遍。比如问你DES你要能说出“它把64位明文分成左右两半右半部分经过扩展置换、密钥加、S盒、P置换后和左半部分异或然后交换左右两半继续下一轮”。不需要背到每个表的具体数字但整体流程必须清晰。如果老师问到你没准备的问题别慌先说“我对这个方面可能理解还不够”再从自己实现时的实际体会出发回答。比如问“为什么AES比DES快”可以说“AES的轮变换可以在硬件上高效实现而DES的位操作在软件上开销大”这能体现你确实做过对比和思考。验收时还要注意代码的可读性。函数命名要见名知意比如generate_subkeys、des_encrypt_block、mod_exp不要全都叫func1、func2否则代码跑通了老师也觉得你没有工程素养。变量也不要用a、b、c这种无法识别的名字哪怕代码长一点也要保证可读性。我见过一个同学代码功能完全正确但因为变量名混乱注释又几乎没有老师让他现场解释某一段代码时他自己都找不到逻辑最终分数被压了不少。密码学实验的关键不只是算法本身对不对更是你有没有用工程的方式把算法表达出来。最后提一个比较现实的经验实验课时间总体比较紧张不要总想着一口气把算法从零写得完美。建议先跑通最核心的功能再考虑扩展。比如先实现RSA的加密和解密再加数字签名先让DES能加密一个8字节块再做文件加密。功能跑通了再优化代码结构和界面这样即使最后来不及做加分项基本分也已经拿到手心态也会稳很多。本文还有配套的精品资源点击获取
返回列表