密码学--希尔密码

一、实验目的

 1、通过实现简单的古典密码算法,理解密码学的相关概念

  2、理解明文、密文、加密密钥、解密密钥、加密算法、解密算法、流密码与分组密码等。

二、实验内容

1、题目内容描述

①定义分组字符长度

随机生成加密密钥并验证密钥的可行性

plain文件读入待加密明文

④判断明文长度是否需要填充

进行希尔密码加密

⑥将加密后得到的密文放入cipher文件

根据加密密钥计算解密密钥

进行解密运算

⑨将解密后的文本放入plain_decrypted文件,与明文进行对比

⑩仿射密码与希尔密码的异同

  1. 关键代码的设计、实现与执行

设计思路:

在最开始以宏定义的方式定义分组字符长度,关键代码:
#define M 2 //定义最大分组长度

先以时间产生随机数作为加密密钥矩阵enkey,但是这里要通过求最大公因数的方式来验证加密密钥enkey的可行性,即密钥矩阵enkey的行列式detb和N的最大公因数为1,即detb,N互质(其中N为定义在Zn上的N):

下面代码是运用Euclid算法计算最大公因数的关键代码:

srand(time(NULL));//以时间取随机数

do {

for (int i = 0; i < M; i++) {

for (int j = 0; j < M; j++) {

enkey[i][j] = rand() % N;//随机生成密钥矩阵

}

}

detb = enkey[0][0] * enkey[1][1] - enkey[0][1] * enkey[1][0];//计算加密矩阵的行列式

} while (gcd(detb, N) != 1);//检验密钥的可行性,也就是行列式与N是否互质,能否得到行列式的逆元

得到加密密钥后,从文件读入已经准备好的加密明文也可通过记事本修改),下面代码是以读的方式打开文件,并判断是否在文件夹中存在命名的文件的关键代码部分:

 FILE *plain_file = fopen("plain.txt", "r");//以读的方式打开

    if (plain_file == NULL) {

        printf("无法打开明文文件!\n");//判断无法打开给出提示

        exit(1);//无法打开只能以异常终止结束运行

}

得到明文后,先调用read_palinfile()函数,统计其长度,看是或否刚好为M的倍数,若不是就进行填充,此处对于剩余需要填充的部分都填的x,再重新计算需要加密解密的文本长度,于是有msize = msize + M - r,关键代码部分如下:

int msize = read_plainfile(plain_file, &plain_text[0]);

int r = msize % M;//判断是否需要填充

if (r < 0)

r += M;

if (r > 0)//如需填充,都填为0,加密后统一得到x

{

for (int i = msize; i < msize + M - r; i++)

plain_text[i] = 23;//x的ASCII码为120,'a'-'x'=120-97=23

msize = msize + M - r;//重新得到加密或解密文本新的长度

}

在得到明文文件后,调用函数read_plainflie(),将明文文件里的有效内容(即字母)放入字符数组,方便后续分组,且可以有效字符的长度,关键代码部分如下:

while ((ch = getc(plain_file)) != EOF)//getc()用于在打开文件中获取一个字符

{

if (ch >= 'a' && ch <= 'z')

*plain = ch - 97;//ASCII码转换

else if (ch >= 'A' && ch <= 'Z')

*plain = ch - 65;//ASCII码转换

else continue;

i++;//计数有效字符数量

*plain++;//指针加加,字符数组向下移一位,好存放下一次的字符

}

return i;//返回文件中有效字符的数量,以方便后续分组

以M为分组数量单位,进行加密,其中运用了二阶矩阵的运算,并判断得到的新的是否有负数,如有,加模,使其转化为正数,再加上a的ASCII码,使其统一为大写字母,*cipher++,指向下一次计算得到的字符存放的地址,关键代码部分如下:

for (i = 0; i < M; i++)//矩阵乘法

{

tmp = 0;

for (j = 0; j < M; j++)

tmp = tmp + plain[j] * enkey[j][i];// 分组加密计算

tmp %= N;//模N

if (tmp < 0)

tmp += N;//负数转正数

*cipher = tmp + 97;//统一转换为大写字母

*cipher++;

将字符数组分组,并调用分组加密的函数进行加密。tmpplain,tmpcipher是以M为单位长度的字符数组,将需要加密的文本分组放入tmpplain,再调用encrypt_block进行以M为单位长度的分组加密,得到的结果返回给同样是长度为M的tmpcipher字符数组,再依此传给cipher,关键代码部分如下:

for (i = 0; i < msize; i = i + M)//依此从字符数组中取M个字符,调用分组函数encrypt_block()进行加密或解密

{

for (j = 0; j < M; j++)

tmpplain[j] = plain[i + j];//在要加密或者解密的文件中连续截取M个字符放入存放M个字符的数组进行加密或解密

encrypt_block(tmpplain, enkey, &tmpcipher[0]);//调用分组加密函数

for (j = 0; j < M; j++)//将加密或者解密后得到的字母依次放入cipher数组

{

*cipher = tmpcipher[j];//赋值

*cipher++;//指针后移,下依此循环存放新的字幕

}

}

计算解密矩阵,由于计算矩阵的逆矩阵,是行列式的倒数(也就是此处的逆元)*伴随矩阵,二阶矩阵的伴随矩阵直接用口诀主对调,副变号即可,过程即是先调用inverse_a()计算行列式逆元,并判断其是否为负数,如是,加上N,使其转化成正数,再通过公式计算出解密矩阵,并判断矩阵中每个数是否为负数,如是,加上N使其转化成正数,得到解密所需的解密矩阵,以下是关键代码部分:

int a1 = inverse_a(detb);//计算加密矩阵行列式的逆元

if (a1 < 0)

a1 += N;

dekey[0][0] = enkey[1][1] * a1 % N;//主对调,副变号

dekey[0][1] = (-enkey[0][1] * a1) % N;

dekey[1][0] = (-enkey[1][0] * a1) % N;

dekey[1][1] = enkey[0][0] * a1 % N;

for (int i = 0; i < M; i++)//遍历检查解密矩阵中是否有负数,如有,加N,转换成正数

{

for (int j = 0; j < M; j++)

{

if (dekey[i][j] < 0)

dekey[i][j] += N;//加N

}

}

结果截图如下:

3、实验结果分析

多测几组后实验得到结果下:

由以上结果即分析可知加密的时候程序自动跳过了空格即字符进行了加密解密,且最后全部由小写字母输出,当然也可全部由大写字母输出,只需要将+97换成+65即可,因为分别对应着小写字母‘a’的ASCII码和大写字母‘A’的ASCII码

而在第二个实验的结果和第三个实验的结果发现多出了x,这是因为在以2分组时,会有剩余,于是由x进行了填充。

对于仿射密码和希尔密码的异同:
综上可知,希尔密码和仿射密码都是需要求逆元,只是仿射密码是直接对密钥求逆元得到的就是解密的密钥,而希尔是对加密矩阵的行列式求逆元,得到的也只是行列式的逆元,而还需要*加密矩阵的伴随矩阵,得到的新的矩阵才是解密的矩阵

在代码中也可以看到,希尔密码和仿射密码一样解密和加密几乎一样只是密钥矩阵不一样,步骤一样,所以可以直接调用一个函数,只是传参不一样。

不同的是,仿射密码是直接对明文的每个字符进行依此进行加密解密,而希尔密码需要对对需要加密或者解密问文本进行分组,每M个一组一起进行加密或者解密,如是在n个M组以后还有剩余不够M个,即进行填充,可以以任何形式进行填充,在此次实验中,我选择了小写字母x,因为在一般情况下,其出现频率最小。

三、实验思考

1、实验过程总结

在此次实验中,直接在仿射密码的代码上进行修改,就简单很多,只需要将不同的地方进行修改即可,如最大公因式函数、求逆元函数、以及文件打开等部分都保持不变,但在随机生成函数部分,由一个改成一个矩阵,然后就是分组进行加密解密部分进行修改。

但是在代码实现过程中由于编程能力问题,传参部分一直出现问题,最终经过查询相关知识得以解决,此外,在解密过程中,一开始没有调用read_plainfile()函数,读cipher文件里的东西直接进行解密,导致解密出来的的文本与明文一直不匹配,经过不断看看,分析代码,发现问题进行修改后解密出来的文本终于能够和原明文匹配。

通过此次实验使我加深了对希尔密码的理解,清晰了希尔密码加密解密每一步的原理,也知道了希尔密码和仿射密码的区别和相同点,同时加强了我自己的代码能力。

  1. 回答实验指导书最后提出的问题

对合密钥是一种弱密钥,不应该在实际加密中使用,这是为什么?

对合密钥使用了相同的密钥进行加密解密,这意味着加密和解密在同一个用户之间进行,这使得对合密钥的安全性受到很大的限制,因为如果一个用户失去了对合密钥的控制,他也将无法保护自己的信息;而如果两个用户使用同一个对合密钥进行加密和解密操作,那么他们的信息很容易被第三方窃取。这是因为他们涉及一个公共密钥,这意味着第三方也可以用这个公共密钥进行通信,从而获得敏感信息。

②若仿射密码定义在Z26上,其对合密钥有多少个?

仿射密码的加密函数为E(x) = (ax + b) mod 26,解密函数为D(x) = a^-1(x - b) mod 26,其中a和b为密钥,a^-1为a的模26乘法逆元。

由于对合函数要求f(f(x))=x,即E(E(x))=x,则有(E(E(x))) mod 26 = x,即((a(ax + b) + b) mod 26) mod 26 = x。解方程得到a^2x + ab + b = x,整理得到(a^2 - 1)x = (1 - ab) mod 26。

因为a^2 - 1必须是26的乘法逆元,所以a^2 - 1的因子必须是26的因子,即a^2 - 1必须是2或13的倍数,即a^2与26互质。根据这个条件,可以求出满足条件的a的取值,而对b没有什么限制,最后计算出满足条件的对合密钥的数量,代码计算结果如下:

若2×2的希尔密码定义在Z26上,其对合密钥有多少个?

希尔密码的加密函数为E(x) = Kx mod 26,其中K为2×2的密钥矩阵,解密函数为D(x) = K^-1x mod 26,其中K^-1为K的模26乘法逆元的矩阵。

对合函数要求f(f(x))=x,即E(E(x))=x,则有(E(E(x))) mod 26 = x,即((KKx) mod 26) mod 26 = x。解方程得到(KKx) mod 26 = x,即K*K是26n的单位矩阵[1,0;0,1]。根据这个条件,可以求出满足条件的2×2的对合密钥的数量。

以上是对合密钥数量的计算过程及代码运行结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905165.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[C++] 一个线程打印奇数一个线程打印偶数

要求开辟两个线程打印从0-100的数&#xff0c;一个线程打印奇数一个线程打印偶数&#xff0c;要求必须按照1,2,3,4,5,6…100这种按照顺序打印 使用std::shared_mutex的版本 #ifndef PrintNumber2_H_ #define PrintNumber2_H_#include <shared_mutex>class PrintNumber2…

MySQL全量、增量备份与恢复

目录 数据备份 一、数据备份类型 二、常见备份方法 扩展&#xff1a;GTID与XtraBackup ‌一、GTID&#xff08;全局事务标识符&#xff09;‌ ‌1. 定义与核心作用‌ ‌2. GTID在备份恢复中的意义‌ ‌3. GTID配置与启用‌ ‌二、XtraBackup的意义与核心价值‌ ‌1. 定…

木马查杀篇—Opcode提取

【前言】 介绍Opcode的提取方法&#xff0c;并探讨多种机器学习算法在Webshell检测中的应用&#xff0c;理解如何在实际项目中应用Opcode进行高效的Webshell检测。 Ⅰ 基本概念 Opcode&#xff1a;计算机指令的一部分&#xff0c;也叫字节码&#xff0c;一个php文件可以抽取出…

DeepSeek-R1-Distill-Qwen-1.5B代表什么含义?

DeepSeek‑R1‑Distill‑Qwen‑1.5B 完整释义与合规须知 一句话先行 这是 DeepSeek‑AI 把自家 R1 大模型 的知识&#xff0c;通过蒸馏压缩进一套 Qwen‑1.5B 架构 的轻量学生网络&#xff0c;并以宽松开源许可证发布的模型权重。 1 | 名字逐段拆解 片段意义备注DeepSee…

Megatron系列——张量并行

本文整理自bilibili Zomi视频 1、行切分和列切分 注意&#xff1a; &#xff08;1&#xff09;A按列切分时&#xff0c;X无需切分&#xff0c;split复制广播到A1和A2对应设备即可。最后Y1和Y2需要拼接下&#xff0c;即All Gather &#xff08;2&#xff09;A按行切分时&#…

java agent技术

从JDK1.5之后引入了java angent技术 Java Agent 是一种强大的技术&#xff0c;它允许开发者在 JVM 启动时或运行期间动态地修改类的字节码&#xff0c;从而实现诸如性能监控、日志记录、AOP&#xff08;面向切面编程&#xff09;等功能 java agent依赖于Instrumentation API&…

LLaMA Factory 深度调参

注意&#xff0c;本文涵盖从基础调参到前沿研究的完整知识体系&#xff0c;建议结合具体业务场景灵活应用。一篇“参考文献”而非“可运行的代码”。https://github.com/zysNLP/quickllm 初始指令&#xff1a; llamafactory-cli train \--stage sft \--do_train True \--mode…

Linux驱动:驱动编译流程了解

要求 1、开发板中的linux的zImage必须是自己编译的 2、内核源码树,其实就是一个经过了配置编译之后的内核源码。 3、nfs挂载的rootfs,主机ubuntu中必须搭建一个nfs服务器。 内核源码树 解压 tar -jxvf x210kernel.tar.bz2 编译 make x210ii_qt_defconfigmakeCan’t use ‘…

Redis集群模式、持久化、过期策略、淘汰策略、缓存穿透雪崩击穿问题

Redis四种模式 单节点模式 架构​​&#xff1a;单个Redis实例运行在单台服务器。 ​​优点​​&#xff1a; ​​简单​​&#xff1a;部署和配置容易&#xff0c;适合开发和测试。 ​​低延迟​​&#xff1a;无网络通信开销。 ​​缺点​​&#xff1a; ​​单点故障​​&…

1.2 函数

函数的本质是描述变量间的依赖关系&#xff1a;​​一个变量&#xff08;自变量&#xff09;的变化会唯一确定另一个变量&#xff08;因变量&#xff09;的值​​。 ​​基本构成​​&#xff1a;通过符号&#xff08;如YF(X)&#xff09;表达规则&#xff0c;X输入 → F处理 …

2025数字孪生技术全景洞察:从工业革命到智慧城市的跨越式发展

引言 数字孪生技术&#xff0c;这一融合物理世界与虚拟镜像的革新性工具&#xff0c;正以惊人的速度重塑产业格局。2025年&#xff0c;中国数字孪生市场规模预计达214亿元&#xff0c;工业制造领域占比超40%&#xff0c;其技术深度与行业落地成果令人瞩目。本文将结合最新数据与…

RabbitMQ 工作模式

RabbitMQ 一共有 7 中工作模式&#xff0c;可以先去官网上了解一下&#xff08;一下截图均来自官网&#xff09;&#xff1a;RabbitMQ 官网 Simple P&#xff1a;生产者&#xff0c;要发送消息的程序&#xff1b;C&#xff1a;消费者&#xff0c;消息的接受者&#xff1b;hell…

VBA会被Python代替吗

VBA不会完全被Python取代、但Python在自动化、数据分析与跨平台开发等方面的优势使其越来越受欢迎、两者将长期并存且各具优势。 Python以其易于学习的语法、强大的开源生态系统和跨平台支持&#xff0c;逐渐成为自动化和数据分析领域的主流工具。然而&#xff0c;VBA依旧在Exc…

【开源工具】深度解析:基于PyQt6的Windows时间校时同步工具开发全攻略

&#x1f552; 【开源工具】深度解析&#xff1a;基于PyQt6的Windows时间校时同步工具开发全攻略 &#x1f308; 个人主页&#xff1a;创客白泽 - CSDN博客 &#x1f525; 系列专栏&#xff1a;&#x1f40d;《Python开源项目实战》 &#x1f4a1; 热爱不止于代码&#xff0c;热…

大模型项目:普通蓝牙音响接入DeepSeek,解锁语音交互新玩法

本文附带视频讲解 【代码宇宙019】技术方案&#xff1a;蓝牙音响接入DeepSeek&#xff0c;解锁语音交互新玩法_哔哩哔哩_bilibili 目录 效果演示 核心逻辑 技术实现 大模型对话&#xff08;技术&#xff1a; LangChain4j 接入 DeepSeek&#xff09; 语音识别&#xff08;…

qt命名空间演示

#ifndef CIR_H #define CIR_Hnamespace cir {double PI3.141592653;//获取圆行周长double getLenthOfCircle(double radius){return 2*PI*radius;}//获取圆形面积double getAreaOfCircle(double radius){return PI*radius*radius;}} #endif // CIR_H#include <iostream> …

使用 Java 反射动态加载和操作类

Java 的反射机制(Reflection)是 Java 语言的一大特色,它允许程序在运行时检查、加载和操作类、方法、字段等元信息。通过 java.lang.Class 和 java.lang.reflect 包,开发者可以动态加载类、创建实例、调用方法,甚至在运行时构造新类。反射是 Java 灵活性的核心,广泛应用于…

《 C++ 点滴漫谈: 三十七 》左值?右值?完美转发?C++ 引用的真相超乎你想象!

摘要 本文全面系统地讲解了 C 中的引用机制&#xff0c;涵盖左值引用、右值引用、引用折叠、完美转发等核心概念&#xff0c;并深入探讨其底层实现原理及工程实践应用。通过详细的示例与对比&#xff0c;读者不仅能掌握引用的语法规则和使用技巧&#xff0c;还能理解引用在性能…

【AutoGen深度解析】下一代AI代理编程框架实战指南

目录 &#x1f31f; 前言&#x1f3d7;️ 技术背景与价值&#x1f6a7; 当前技术痛点&#x1f6e0;️ 解决方案概述&#x1f465; 目标读者说明 &#x1f50d; 一、技术原理剖析&#x1f5bc;️ 核心概念图解&#x1f4a1; 核心作用讲解⚙️ 关键技术模块说明&#x1f504; 技术…

Python-AI调用大模型 给出大模型人格案例

Python调用通义千问模拟原神雷电将军口吻 最近在用AI编辑器写AI对话 尝试给AI对话增加人格 以下是使用阿里通义千问大模型模拟《原神》中雷电将军(雷电影)口吻的代码案例&#xff0c;包含典型的高傲威严、略带古风的说话风格。 完整后端代码示例 import dashscope from dash…