尚未崩坏 发表于 2025-1-16 01:06:42

《比你见过的所有古典密码概述都细?没错,就是这篇!》

古典密码

古典密码的概念

古典密码是密码学发展的早期阶段,主要包罗置换密码和代换密码两大类。
古典密码的界说

古典密码是指 1976 年从前的密码算法,主要通过手工或简单的机器操作进行信息的加密息争密。其焦点头脑是通过替换和置换来隐蔽信息的真实内容。
古典密码的分类


[*]置换密码:将明文中的字符重新排列,字母本身不变,但其位置改变了,如许编成的密码称为置换密码。最简单的置换密码是把明文中的字母顺序倒过来,然后截成固定长度的字母组作为密文。
[*]代换密码:将明文中的字符替换成其他字符。代换密码又可分为单表代换密码和多表代换密码。

[*]单表代换密码:如凯撒密码,通过将字母表中的每个字母移动固定位置来实现加密。
[*]多表代换密码:如维吉尼亚密码,利用一个关键词来控制字母的替换。

古典密码的常见算法

凯撒密码

通过将字母表中的每个字母移动固定位置来实现加密,是最简单的替换密码之一。
凯撒密码(英语:Caesar cipher),或称凯撒加密、凯撒变换、变换加密,是一种最简单且最广为人知的加密技能。凯撒密码是一种替换加密技能,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例如,当偏移量是 3 的时候,所有的字母 A 将被替换成 D,B 变成 E,以此类推。这个加密方法是以罗马共和时期凯撒的名字命名的,据称当年凯撒曾用此方法与其将军们进行联系。
凯撒密码通常被作为其他更复杂的加密方法中的一个步调,例如维吉尼亚密码。凯撒密码还在现代的 ROT13 系统中被应用。但是和所有的利用字母表进行替换的加密技能一样,凯撒密码非常轻易被破解,而且在现实应用中也无法保证通信安全。
https://files.mdnice.com/user/77755/77908d59-b5af-4c83-bfa8-60c36c1e6f8a.pnghttps://files.mdnice.com/user/77755/080b978e-80c0-4de1-a9d3-df4aae6e0bda.png当偏移量是 3 的时候,所有的字母 A 将被替换成 D,B 变成 E,以此类推。
eg:凯撒密码的替换方法是通过排列明文和密文字母表,密文字母表示通过将明文字母表向左或向右移动一个固定数目标位置。例如,当偏移量是左移 3 的时候(解密时的密钥就是 3):
明文字母表:ABCDEFGHIJKLMNOPQRSTUVWXYZ
密文字母表:DEFGHIJKLMNOPQRSTUVWXYZABC利用时,加密者查找明文字母表中必要加密的消息中的每一个字母所在位置,并且写下密文字母表中对应的字母。必要解密的人则根据事先已知的密钥反过来操作,得到原来的明文。例如:
明文:THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
密文:WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ凯撒密码的加密、解密方法还能够通过同余的数学方法进行计算。首先将字母用数字代替,A=0,B=1,...,Z=25。此时偏移量为 n 的加密方法即为:https://files.mdnice.com/user/77755/bba07cf6-0e58-4d00-80d3-cc3835ab37ed.png
特定凯撒密码名称
根据偏移量的不同,还存在多少特定的凯撒密码名称:

[*]偏移量为 10:Avocat(A→K)
[*]偏移量为 13:ROT13
[*]偏移量为-5:Cassis (K 6)
[*]偏移量为-6:Cassette (K 7)
密码破解

即使利用唯密文攻击,凯撒密码也是一种非常轻易破解的加密方式。可能有两种环境必要思量:
攻击者知道(或者猜测)密码中利用了某个简单的替换加密方式,但是不确定是凯撒密码;攻击者知道(或者猜测)利用了凯撒密码,但是不知道其偏移量。对于第一种环境,攻击者可以通过利用诸如频率分析或者样式单词分析的方法,马上就能从分析结果中看出规律,得出加密者利用的是凯撒密码。对于第二种环境,办理方法更加简单。由于利用凯撒密码进行加密的语言一般都是字母文字系统,因此密码中可能是利用的偏移量也是有限的,例如利用 26 个字母的英语,它的偏移量最多就是 25(偏移量 26 等同于偏移量 0,即明文;偏移量超过 26,等同于偏移量 1-25)。因此可以通过穷举法,很轻易地进行破解。此中一种方法是在表格中写下密文中的某个小片段利用所有可能的偏移量解密后的内容——称为候选明文,然后分析表格中的候选明文是否具有现实含义,得出正确的偏移量,解密整个密文。例如,被选择出的密文片段是"EXXEGOEXSRGI",可以从右表中的候选明文里很快看出其正确的偏移量是 4。也可以通过在每一个密文单词的每一个字母下面,纵向写下整个字母表其他字母,然后可以通过分析,得出此中的某一行便是明文。
另外一种攻击方法是通过频率分析。当密文长度充足大的环境下,可以先分析密文中每个字母出现的频率,然后将这一频率与正常环境下的该语言字母表中所有字母的出现频率做比较。例如在英语中,正常明文中字母 E 和 T 出现的频率特别高,而字母 Q 和 Z 出现的频率特别低,而在法语中出现频率最高的字母是 E,最低的是 K 和 W。可以通过这一特点,分析密文字母出现的频率,可以估计出正确的偏移量。此外,有时还可以将频率分析从字母推广到单词,例如英语中,出现频率最高的单词是:the, of, and, a, to, in...。通过将最常见的单词的所有可能的 25 组密文,编组成字典,进行分析。好比 QEB 可能是 the,MPQY 可能是单词 know(当然也可能是 aden)。但是频率分析也有其范围性,它对于较短或故意省略元音字母或者其他缩写方式写成的明文加密出来的密文进行解密并不适用。
偏移量候选明文0exxegoexsrgi1dwwdfndwrqfh2cvvcemcvqpeg3buubdlbupodf4attackatonce5zsszbjzsnmbd6yrryaiyrmlac……………………………………………………………23haahjrhavujl24gzzgiqgzutik25fyyfhpfytshj另外,通过多次利用凯撒密码来加密并不能获得更大的安全性,因为利用偏移量 A 加密得到的结果再用偏移量 B 加密,等同于利用 A+B 的偏移量进行加密的结果。
https://files.mdnice.com/user/77755/47965d97-b7f2-4b6b-90e2-6b17c9e0aa23.png用典型的英语书写的文字样本中各字母出现频率https://files.mdnice.com/user/77755/cf9f0422-1d5e-4b9c-bc9a-9d9afac52529.png用典型的法语书写的文字样本中各字母出现频率
凯撒在线解密网站:https://ctf.bugku.com/tool/caesar维吉尼亚密码

利用一个关键词来对明文进行多表替换加密,是古典密码中较为复杂的一种。
维吉尼亚密码(法语:Chiffre de Vigenère,又译维热纳尔密码)是利用一系列凯撒密码组成密码字母表的加密算法,属于多表密码的一种简单情势。
维吉尼亚密码曾多次被发明。该方法最早记录在吉奥万·巴蒂斯塔·贝拉索( Giovan Battista Bellaso)于 1553 年所著的书《吉奥万·巴蒂斯塔·贝拉索先生的密码》(意大利语:La cifra del. Sig. Giovan Battista Bellaso)中。然而,后来在 19 世纪时被误传为是法国外交官布莱斯·德·维吉尼亚(Blaise De Vigenère)所创造,因此如今被称为“维吉尼亚密码”。
维吉尼亚密码以其简单易用而著称,同时初学者通常难以破解,因而又被称为“不可破译的密码”(法语:le chiffre indéchiffrable)。这也让很多人利用维吉尼亚密码来加密的目标就是为了将其破解。在一个凯撒密码中,字母表中的每一字母都会作一定的偏移,例如偏移量为 3 时,A 就转换为了 D、B 转换为了 E……而维吉尼亚密码则是由一些偏移量不同的凯撒密码组成。
为了生成密码,必要利用表格法。这一表格包罗了 26 行字母表,每一行都由前一行向左偏移一位得到。具体利用哪一行字母表进行编译是基于密钥进行的,在过程中会不断地变换。
例如,假设明文为:
ATTACKATDAWN选择某一关键词并重复而得到密钥,如关键词为 LEMON 时,密钥为:
LEMONLEMONLE对于明文的第一个字母 A,对应密钥的第一个字母 L,于是利用表格中 L 行字母表进行加密,得到密文第一个字母 L。雷同地,明文第二个字母为 T,在表格中利用对应的 E 行进行加密,得到密文第二个字母 X。以此类推,可以得到:
明文:ATTACKATDAWN密钥:LEMONLEMONLE密文:LXFOPVEFRNHR
https://files.mdnice.com/user/77755/bbdf0878-8d4d-4b1a-8896-4ab6c69fa71b.png用来加密解密的维吉尼亚表格
解密的过程则与加密相反。
eg:根据密钥第一个字母 L 所对应的 L 行字母表,发现密文第一个字母 L 位于 A 列,因而明文第一个字母为 A。密钥第二个字母 E 对应 E 行字母表,而密文第二个字母 X 位于此行 T 列,因而明文第二个字母为 T。以此类推便可得到明文。
用数字 0-25 代替字母 A-Z,维吉尼亚密码的加密文法可以写成同余的情势:
https://files.mdnice.com/user/77755/48a0926d-ec74-4381-bbf2-6350352f3e18.png密码破解

对包罗维吉尼亚密码在内的所有多表密码的破译都是以字母频率为底子的,但直接的频率分析却并不适用。例如,如果 P 是密文中出现次数最多的字母,则 P 很有可能对应 E(前提是明文的语言为英语)。原因在于 E 是英语中利用频率最高的字母。然而,由于在维吉尼亚密码中,E 可以被加密成不同的密文,因而简单的频率分析在这里并没有效。
破译维吉尼亚密码的关键在于它的密钥是循环重复的。如果我们知道了密钥的长度,那密文就可以被看作是交织在一起的凯撒密码,而此中每一个都可以单独破解。利用卡西斯基试验和弗里德曼试验来得到密钥的长度。
卡西斯基试验弗里德里希·卡西斯基于 1863 年首先发表了完整的维吉尼亚密码的破译方法,称为卡西斯基试验(Kasiski examination)。早先的一些破译都是基于对于明文的认识、或者利用可辨认的词语作为密钥。而卡西斯基的方法则没有这些限制。然而,在此之前,已经有人意识到了这一方法。1854 年,英国数学家、发明家兼机器工程师查尔斯·巴贝奇受到斯维提斯(John Hall Brock Thwaites)在《艺术协会杂志》(Journal of the Society of the Arts)上声称发明白“新密码”的鼓励,从而破译了维吉尼亚密码。巴贝奇发现斯维提斯的密码只不外是维吉尼亚密码的一个变种而已,而斯维提斯则向其挑战,让他尝试破译用两个不同长度的密钥加密的密文。巴贝奇乐成地进行了破译,得到的明文是丁尼生所写的诗《罪过的想象》(The Vision of Sin),利用的密钥则是丁尼生妻子的名字 Emily(艾米莉)。巴贝奇从未对他的方法进行过表明 。在对巴贝奇生前笔记的研究中发现,早在 1846 年巴贝奇就利用了这一方法,与后来卡西斯基发表的方法相同。
卡西斯基试验是基于雷同 the 如许的常用单词有可能被同样的密钥字母进行加密,从而在密文中重复出现。例如,明文中不同的 CRYPTO 可能被密钥 ABCDEF 加密成不同的密文:
密钥:ABCDEF AB CDEFA BCD EFABCDEFABCD
明文:CRYPTO IS SHORT FOR CRYPTOGRAPHY
密文:CSASXT IT UKSWT GQU GWYQVRKWAQJB此时明文中重复的元素在密文中并不重复。然而,如果密钥相同的话,结果可能便为(利用密钥 ABCD):
密钥:ABCDAB CD ABCDA BCD ABCDABCDABCD
明文:CRYPTO IS SHORT FOR CRYPTOGRAPHY
密文:CSASTP KV SIQUT GQU CSASTPIUAQJB此时卡西斯基试验就能产生效果。对于更长的段落此方法更为有效,因为通常密文中重复的片段会更多。如通过下面的密文就能破译出密钥的长度:
密文:DYDUXRMHTVDVNQDQNWDYDUXRMHARTJGWNQD此中,两个 DYDUXRMH 的出现相隔了 18 个字母。因此,可以假定密钥的长度是 18 的约数,即长度为 18、9、6、3 或 2。而两个 NQD 则相距 20 个字母,意味着密钥长度应为 20、10、5、4 或 2。取两者的交集,则可以基本确定密钥长度为 2。
弗里德曼试验弗里德曼试验由威廉·F·弗里德曼(William F. Friedman)于 1920 年代发明。他利用了重合指数(index of coincidence)来描述密文字母频率的不匀性,从而破译密码。κp 指目标语言中两个任意字母相同的概率(英文中为 0.067),κr 指字母表中这种环境出现的概率(英文中为 1/26=0.0385),从而密钥长度可以估计为:
https://files.mdnice.com/user/77755/7eeeef1b-60d6-4f22-99d5-ceddbbd0d278.png此中,c 是指字母表的长度(英文为 26),N 指文本的长度,n1 到 nc 是指密文的字母频率,为整数。
此方法只是一种估计,会随着文本长度的增加而更为精确。在实践中,会尝试接近此估计的多个密钥长度。一种更好的方法是将密文写成矩阵情势,此中列数与假定的密钥长度一致,将每一列的重合指数单独计算,并求得平均重合指数。对于所有可能的密钥长度,平均重合指数最高的最有可能是真正的密钥长度。如许的试验可以作为卡西斯基试验的补充。
频率分析:一旦能够确定密钥的长度,密文就能重新写成多列,列数与密钥长度对应。如许每一列其实就是一个凯撒密码,而此密码的密钥(偏移量)则对应于维吉尼亚密码密钥的相应字母。与破译凯撒密码雷同的方法,就能将密文破译。
柯克霍夫方法作为卡西斯基试验的改进,由奥古斯特·柯克霍夫(Auguste Kerckhoffs)提出。它将每一列的字母频率与转换后的明文频率相对应而得出每一列的密钥字母。一旦密钥中每一个字母都能确定,就能很简单地破译密文,从而得到明文。如果维吉尼亚字母表表格本身是杂乱而非按通常字母表顺序的话,那柯克霍夫方法就会无效,但卡西斯基试验和重复指数对于决定密钥长度仍旧是有效的。
更细致的破解讲解可以参考:https://blog.csdn.net/qq_42650988/article/details/104343051维吉尼亚密码的变体


[*]维吉尼亚密码的变体滚动密钥密码也曾一度被认为是不可破译的。这种变体的密钥与密文的长度一致,因此卡西斯基试验和弗里德曼试验即变得无效。1920 年,弗里德曼首先发现了此方法的弱点。由于滚动密钥密码的密钥是一段真实的语言,因而破译者便能了解密钥文本的统计信息,而这种信息也会反映到密文当中。
[*]如果密钥是完全随机、与明文的长度一致且只利用过一次,维吉尼亚密码理论上是不可破译的。然而,这种环境下密钥本身而非密文便成了关键,这被称为一次性密码本。
[*]维吉尼亚本人确实发明白一种更强的维吉尼亚密码变体——主动密钥密码。巴贝奇所破译的其实是这种主动密钥密码,而卡西斯基则通常被认为是首先发表了破译固定密钥多表密码的方法。
[*]还有一种简单的变体利用维吉尼亚的解码方法进行加密,同时利用维吉尼亚的加密方法进行解密,这被称为变异博福特密码。此方法与弗朗西斯·博福特创造的博福特密码不同,后者固然也与维吉尼亚密码相似,但利用了修改过的加密方式和表格,是一种对等加密。
[*]维吉尼亚密码外貌上的强度并没能使其在欧洲得到广泛利用。由 Gronsfeld 伯爵所创造的 Gronsfeld 密码基本与维吉尼亚密码相同,不外它只利用 10 个不同的密码字母表(对应字母 0 到 9)。Gronsfeld 密码的强度很高,这是因为它的密钥并不是一个单词,但缺点在于字母表数目过少。尽管云云,Gronsfeld 密码仍在德国和整个欧洲有着广泛的应用。
栅栏密码

将明文按列写入,密文按行输出,通过置换字母的位置来加密。
栅栏密码是古典密码的一种,其原理是将一组要加密的明文划分为 n 个一组(n 通常根据加密需求确定,且一般不会太大,以保证密码的复杂性和安全性),然后取每个组的第一个字符(有时也涉及取其他位置的字符,但规则需事先约定),根据环境将这些字符重新排列组合成一段无规律的话,形成密文。
栅栏密码的“栏”数,即分组后形成的“列”数或“行”数(具体取决于加密时的排列方式),是栅栏密码的一个重要参数。根据栏数的不同,栅栏密码可以分为多种类型,此中比较常见的是 2 栏栅栏密码。但理论上,栏数 n 可以是任何正整数(现实应用中受限于明文的长度和加密的安全性要求)。
加密原理

分栏:首先将必要加密的明文(即原始信息)中的字母交替排成多行(通常称为“栏”),栏数可以是任意的,但通常不会超过 30 个字母(大约一两句话的长度)
重新组合:接着,按照特定的顺序(例如,从上到下,再从下到上)将各栏中的字母重新组合成一行,形成密文。
以明文“THE LONGEST DAY MUST HAVE AN END”为例,如果选择两栏进行加密,过程如下:
分栏:
第一栏:T E O G S D Y U T A E N N
第二栏:H L N E T A M S H V A E D重新组合:将第二栏的字母放在第一栏的背面,得到密文
TEOGSDYUTAENN HLNETAMSHVAED加密算法

# Rail-Fence Cipher Encode Algorithm
"""
加密信息 THE LONGEST DAY MUST HAVE AN END
key = 2
分栏第一栏:T E O G S D Y U T A E N N
分栏第二栏:H L N E T A M S H V A E D
将第二栏的字母放在第一栏的后面,得到密文 TEOGSDYUTAENNHLNETAMSHVAED
"""

def splitString(string, step):
    length = len(string)
    count = length % step
    if length % step != 0:
        for i in range(step - count):
            string += ' '
    # 将字符串分隔成若干个子串, 子串最大长度为 key
    return  for i in range(0, length, step)]

if __name__ == '__main__':
    string = input("Encode Info:").replace(' ', '')
    key = int(input("Key:"))
    secret = ''
    tempList = splitString(string, key)

    # 遍历二维数组, 取出每栏中的字符串
    for i in range(key):
        for j in range(len(tempList)):
            secret += tempList

    print("Encode-Secret: %s" % secret)代码说明


[*]splitString 函数:

[*]该函数将输入字符串按照指定的步长(key)进行分割,生成一个二维列表。
[*]如果字符串长度不能被步长整除,则在字符串末尾添加空格,以确保每个子串的长度一致。

[*]主程序:

[*]从用户输入获取要加密的信息和步长(key)。
[*]去除输入信息中的空格。
[*]调用 splitString 函数将字符串分割成二维列表。
[*]遍历二维列表,按列取出字符,拼接成最终的密文。
[*]输出加密后的密文。

利用示例

Encode Info: THE LONGEST DAY MUST HAVE AN END
Key: 2
Encode-Secret: TEOGSDYUTAENNHLNETAMSHVAED解密原理

解密是加密的逆过程。已知密文和栏数,解密者可以按照相反的顺序将密文重新排列成多栏,然后再将各栏的字母按照原始的顺序合并成明文。
例如,对于上述密文“TEOGSDYUTAENN HLNETAMSHVAED”,已知为两栏加密,解密过程如下:
分栏:将密文分成两行
第一栏:T E O G S D Y U T A E N N
第二栏:H L N E T A M S H V A E D重新组合:按照上下上下的顺序将两行字母合并成一行,得到明文“THE LONGEST DAY MUST HAVE AN END”。
解密算法

# Rail-Fence Cipher Decode
"""
栅栏密码解密(即栅栏密码加密逆过程)
解密思路如下:
1. 分栏计算长度等于最大长度的栏的个数和长度等于最小长度的栏的个数
2. 分栏之后执行加密的逆过程
"""

def split_string(string, step):
    length = len(string)
    if length % step == 0:
        # 计算子串长度
        child_string_length = int(length / step)
        return child_string_length,  for i in range(0, length, child_string_length)]
    else:
        # 最大长度和最小长度之差恒等于1
        min_string_length = int(length / step)
        max_string_length = min_string_length + 1
        max_length_count = length % step
        min_length_count = step - max_length_count
        max_string_length_sum = (min_string_length + 1) * max_length_count

        # 将长度等于最大长度的栏和长度等于最小长度的栏分离
        max_string_sum = string
        min_string_sum = string

        # 将字符串彻底分栏
        max_string_list =  for i in range(max_length_count)]

        # 注意: 在分离最小长度栏时需要在每个子串后面加上空字符 ' ', 防止后面索引越界
        min_string_list =  + ' ' for i in range(min_length_count)]

        # 将最大长度栏集合和最小长度栏集合进行合并
        string_list = max_string_list + min_string_list

        return max_string_length, string_list


if __name__ == '__main__':
    encode_secret = input("encodeSecret: ")
    key = int(input("key: "))
    child_string_length, temp_list = split_string(encode_secret, key)
    secret = ''
    for i in range(child_string_length):
        for j in range(key):
            secret += temp_list
    secret_info = secret.replace(' ', '')
    print(secret_info)主要改动点:


[*]函数和变量命名:将函数名和变量名改为符合 Python 命名规范的情势(利用小写字母和下划线)。
[*]表明:增加了一些表明以提高代码的可读性。
[*]代码布局:调整了代码布局,使其更加清晰和易读。
[*]输入处理:去掉了不必要的 str() 转换,因为 input() 函数已经返回字符串类型。
仿射密码

结合了凯撒密码和乘法密码的特点,通过线性变换进行加密。
加解密函数

密钥:k = (m, n)
C = Ek(m) = (k1*m + k2) mod n ;
M = Dk(c) = k3(c-k2) mod n (其中(k3×k1) mod 26 = 1);仿射密码具有可逆性的条件是:gcd(k1, n)=1. 当 k1=1 时,仿射密码变为加法密码,当 k2=0 时,仿射密码变为乘法密码。仿射密码中的密钥空间的大小为nφ(n),当 n 为 26 字母,φ(n)=12,因此仿射密码的密钥空间为12×26 = 312。
https://files.mdnice.com/user/77755/7f392277-8e5f-49b0-89e7-cb3a2a2f15c4.png加密

设密钥 K= (7, 3), 用仿射密码加密明文 hot。 三个字母对应的数值是 7、14 和 19。
分别加密如下:
(7×7 + 3) mod 26 = 52 mod 26 =0
(7×14 + 3) mod 26 = 101 mod 26 =23
(7×19 + 3) mod 26 =136 mod 26 =6三个密文数值为 0、23 和 6,对应的密文是 AXG。
解密

先来引入一个界说.大家知道, 很多多少东西都有逆, 大家读小学时都知道,两个数相乘乘积为 1,则互为倒数, 其实是最简单的逆.后来, 我们到了高中, 我们学习了逆函数; 到了大学, 我们学习线性代数,知道两个矩阵的乘积为单元矩阵的话,则这两个矩阵互为逆矩阵.   如今我跟大家介绍另一种逆. 叫模逆. 其实很好理解的,如下: 若 a,b 两数的乘积对正整数 n 取模的结果为 1. 则称 a,b 互为另外一个的模逆. 好比:
37 = 21; 21 % 20 = 1 ; 所以 3,7 互为 20 的 模逆.93 = 27; 27 % 26 = 1 ; 所以 9,3 互为 26 的 模逆.如何标记?
利用 java 代码求模逆
import java.util.Scanner;

/**
 * 仿射密码的加密息争密
 * n = 26
 */
public class AffineCipher {

    // 模数
    private static final int N = 26;

    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            // 输入密钥
            int k1 = readKey(scanner, "请输入密钥k1 (与26互质的整数): ");
            int k2 = readKey(scanner, "请输入密钥k2 (0-25之间的整数): ");

            // 输入明文
            System.out.println("请输入明文:");
            String plaintext = scanner.nextLine();

            // 加密
            String ciphertext = encrypt(k1, k2, plaintext);
            System.out.println("密文: " + ciphertext);

            // 解密
            String decryptedText = decrypt(k1, k2, ciphertext);
            System.out.println("解密结果: " + decryptedText);
        }
    }

    /**
     * 读取并验证密钥
     */
    private static int readKey(Scanner scanner, String prompt) {
        int key;
        while (true) {
            System.out.print(prompt);
            if (scanner.hasNextInt()) {
                key = scanner.nextInt();
                if (prompt.contains("k1") && gcd(key, N) != 1) {
                    System.out.println("错误: k1 必须与 26 互质。请重新输入。");
                } else if (prompt.contains("k2") && (key = N)) {
                    System.out.println("错误: k2 必须在 0 到 25 之间。请重新输入。");
                } else {
                    break;
                }
            } else {
                System.out.println("错误: 请输入一个整数。");
                scanner.next(); // 清除无效输入
            }
        }
        return key;
    }

    /**
     * 计算最大公约数
     */
    private static int gcd(int a, int b) {
        while (b != 0) {
            int temp = a % b;
            a = b;
            b = temp;
        }
        return a;
    }

    /**
     * 加密操作
     */
    private static String encrypt(int k1, int k2, String plaintext) {
        StringBuilder ciphertext = new StringBuilder();
        for (char c : plaintext.toCharArray()) {
            if (Character.isUpperCase(c)) {
                ciphertext.append((char) ((k1 * (c - 'A') + k2) % N + 'A'));
            } else if (Character.isLowerCase(c)) {
                ciphertext.append((char) ((k1 * (c - 'a') + k2) % N + 'a'));
            } else {
                ciphertext.append(c); // 保留非字母字符
            }
        }
        return ciphertext.toString();
    }

    /**
     * 解密操作
     */
    private static String decrypt(int k1, int k2, String ciphertext) {
        int k3 = modInverse(k1, N);
        StringBuilder plaintext = new StringBuilder();
        for (char c : ciphertext.toCharArray()) {
            if (Character.isUpperCase(c)) {
                plaintext.append((char) ((k3 * (c - 'A' - k2 + N)) % N + 'A'));
            } else if (Character.isLowerCase(c)) {
                plaintext.append((char) ((k3 * (c - 'a' - k2 + N)) % N + 'a'));
            } else {
                plaintext.append(c); // 保留非字母字符
            }
        }
        return plaintext.toString();
    }

    /**
     * 计算模逆元
     */
    private static int modInverse(int a, int m) {
        a = a % m;
        for (int x = 1; x 
页: [1]
查看完整版本: 《比你见过的所有古典密码概述都细?没错,就是这篇!》