C# 密码学高级教程(一)

原文:Pro Cryptography and Cryptanalysis

协议:CC BY-NC-SA 4.0

一、密码学基础

介绍

密码学的历史很长,也很有趣。完整的密码学非技术参考,推荐破译者 [1 ] *。*这本书介绍了从大约 4000 年前埃及人首次使用密码学到近代历史,当时密码学在两次世界大战的结局中发挥了至关重要的作用。这本书写于 1963 年,涵盖了对密码学发展意义重大的历史方面。密码学被视为一门艺术,它与外交服务、军事人员和政府有关。密码术已经被用作保护与国家安全相关的策略和不同秘密的工具。

密码学历史上最重要的发展是在 1976 年,当时迪菲和赫尔曼[发表了题为“密码学的新方向”的工作论文这篇论文介绍了彻底改变了人们对密码学的看法的概念:公钥密码学。作者还介绍了一种新颖巧妙的密钥交换方法。该方法的安全性是基于离散对数问题的难解性。当时,作者们还没有公钥加密方案的实际实现,这个想法非常清晰,并开始在加密社区中产生巨大的兴趣。从 1978 开始,Rivest、Shamir 和 Adleman(现在称为 RSA [ 3 )提出了公钥加密和签名方案的第一个实现。RSA 方案基于分解大整数的困难性。如果我们在 RSA 的整数因式分解和 Shor 的算法之间进行比较,我们将观察到最后一个算法将在量子计算机的多项式时间内运行,这将对任何基于大整数因式分解困难假设的加密方法提出重大挑战 [62 ]。大整数因式分解的应用及其目的增加了因式分解方法的数量。在 **1980 年,**在这个领域有了重要的进步,但是没有一个进步表明 RSA 的安全性有任何改进。ElGamal4 于 1985 年 ?? 发现并提出了一类非常重要的实用公钥方案。他的方案也是基于离散对数的问题。

数字签名代表了公钥加密提供的最重要和最有意义的贡献。在 1991 年,采用了 ISO/IEC 9796 数字签名国际标准 5 。该标准基于 RSA 公钥方案。在 1994 ,美国政府采用了数字签名标准,这是一个基于离散对数问题的强大方案。

如今,寻找新的公钥方案、改进当前的密码机制以及设计新的安全证明仍在进行,并继续带来显著的改进。

这本书的目标和目的是解释在实践中最重要的密码学方面的原理、技术、算法和实现的最新更新。重点是最实用和应用的方面。您将了解代表问题的方面,我们将指出文献中的参考资料和提供解决方案的最佳实践。由于涵盖了大量的材料,大多数结果将伴随着实施。这也有助于不模糊加密的真正本质。这本书为执行者和研究者提供了强有力的材料。这本书描述了算法和软件系统及其相互作用。

信息安全和密码学

在本书中,信息的术语和概念可以理解为*量。*为了通过算法和实现技术(如 C#)介绍密码学及其应用,您需要了解与信息安全相关的问题。参与某项交易的所有各方必须确信,与信息安全相关的特定目标已得到遵守。目标列于表 1-1 中。

表 1-1

安全目标

|

安全目标

|

描述

|
| — | — |
| 隐私/保密 | 对未经授权查看信息的人保密 |
| 签名 | 将签名绑定到实体(例如文档)的方法 |
| 批准 | 从一个实体发送到另一个实体,代表官方授权做某事或成为某事 |
| 消息认证 | 称为数据的认证来源;一些文本使用信息来源的确证作为定义 |
| 数据完整性 | 确保信息未被未授权人员或通过其他未知手段更改的过程 |
| 实体认证/识别 | 比较实体的身份(例如,计算机、个人、信用卡等。). |
| 确认 | 为授权提供及时性的一种方式,以便使用或操作信息或资源 |
| 证书 | 表示受信任实体对信息的确认 |
| 访问控制 | 将资源限制到特权实体的过程 |
| 证书 | 可信认证对信息的确认 |
| 时间戳 | 代表信息创建或存在时间的记录 |
| 见证 | 一种验证由不同于创建者的实体所表示的信息的创建存在的方法 |
| 收据 | 表示已收到信息的确认 |
| 所有权 | 向一个实体提供使用或转移特定资源给其他实体的合法权利的一种方式 |
| 确认 | 表示对服务提供是否成功的确认 |
| 取消 | 撤销认证或授权的过程 |
| 不可否认性 | 防止否定其他先前的承诺或行动的过程 |
| 匿名 | 特定过程中涉及的实体的身份的隐藏过程 |

已经创建了一组协议和机制来处理当通过物理文档发送信息时由信息安全引起的问题。信息安全的目标也可以通过数学算法和协议来实现,同时需要程序技术并通过实现预期结果来遵循法律。作为一个例子,让我们考虑信件的隐私,这是由合法的邮件服务提供的密封信封提供的。信封的物理安全性有其自身的局限性,并且法律是以这样的方式建立的,如果邮件被未被授权的人打开,他们可能会被指控犯有刑事罪。在有些情况下,这种安全性不仅是通过信息本身实现的,也是通过记录信息原始性的纸质文件实现的。作为一个例子,考虑纸币,它需要特殊的墨水和材料,以避免和防止伪造。

从概念上讲,信息存储、注册、解释和记录的方式没有太大变化。复制和修改信息的能力代表了操纵已被显著改变的信息的最重要的特征之一。

信息安全中使用的最重要的工具之一由 s 签名 *表示。*它代表了多种服务的构建模块,例如不可否认性、数据源认证、身份识别和见证。

在基于电子通信的社会中,实现信息安全意味着满足基于法律和技术技能的要求。同时,也不能保证信息安全的目标能够相应地实现。信息安全的技术部分由密码术 来保证。

密码学代表研究与信息安全相关的数学技术的学科,如机密性、完整性(数据)、认证(实体)和认证的来源。密码学不仅仅包括提供信息安全,还包括一套特定的技术。

加密目标

从表 1-1 中列出的与信息安全相关的目标列表中,接下来的四个目标代表了一个框架的基础,这将有助于推导出其他目标:

  • 隐私/保密性(定义 1.5 和 1.8)

  • 数据完整性(定义 1.9)

  • 认证(定义 1.7)

  • 不可否认性(定义 1.6)

让我们分别考虑每个目标,看看它们各自的目标和目的:

  • 保密性代表一种服务,用于保护信息内容,防止未经授权的人查看。有不同的方法来提供保密性,从数学算法到物理保护,以不可理解的方式扰乱数据。

  • 数据完整性表示处理未经授权的数据更改的服务。为了确保数据的完整性,需要有能力检测未经授权方对数据的操纵。

  • 认证代表在数据或应用的认证中起重要作用的服务,处理识别 该功能应用于处理信息的实体的两端。参与通信的双方有必要向对方展示他们的身份(参与方可以是一个人或一个系统)。通过通信信道传递和传输的信息应在来源、数据内容、发送时间等方面得到认证。基于这些原因,密码学的这一方面分为两大子领域:实体认证数据来源认证 。**数据源认证提供数据完整性。

  • 不可否认性由一个帮助防止实体否认之前行为的服务来表示。

当一个实体否认采取了某些行动而导致冲突存在时,有一种必要的力量可以解决这种情况。

密码学的一个基本目标是确保上面列出的四个领域在理论和实践两方面都得到恰当的处理。

这本书将描述一些基本的密码工具,也被称为原语,用于提供信息安全。原语的示例被描述为加密方案(定义 1.5 和 1.8)、散列函数(定义 1.9)和数字签名方案(定义 1.6)。在图 1-1 中,我们提供了密码原语以及它们如何交叉和关联的示意图。本书涵盖了图 1-1 中描述的许多密码原语,并附有实际实现。这些图元应通过一个评估过程,评估标准如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-1

密码原语分类法

  • *安全等级。*当我们必须量化安全级别时,它是相当困难的。它可以被看作是达到所提出的目标所需的操作次数。通常,安全级别是根据实现目标所需的工作量来定义的。

  • *功能。*为了满足不同的信息安全目标,需要对原语进行组合。

  • 操作方法。当原语以不同的方式应用于不同的输入时,通常会发展出不同的特征。一个原语能够根据操作模式提供非常不同的功能。

  • *性能。*性能的概念是指原语在特定的操作模式下可以提供的效率。

  • *易于实施。*这个概念代表了一个过程,而不仅仅是在实际使用中实现原语的标准。

各种标准的重要性在很大程度上取决于可用的应用和资源。

密码学已经被许多从业者和专业人员视为一门艺术,他们已经构思了不同的专门技术,目标是满足重要的信息安全需求。在过去的二十年里,我们看到了一个从艺术到科学的学科过渡时期。目前,有几个非常重要的科学和实践国际会议完全致力于密码学。国际密码研究协会(IACR)旨在促进该领域的最佳研究成果。

这本书是关于密码学和密码分析,实现算法和机制的标准。

数学函数背景

这本书并不代表抽象数学的专著。熟悉一些基本和基本的数学概念是必要的,并且将被证明在实际实现中非常有用。密码学中一个非常重要的基础概念是基于数学意义上的 ?? 函数 ??。一个函数也被称为变换映射

功能–1-1,单向,单向活板门

我们将把一个基于不同对象的集合视为概念,这些不同对象被称为该特定集合的元素。让我们以集合 A 为例,它具有元素 abc ,这被表示为 A = { abc }。

定义 1.1。 密码学代表对数学技术的研究,这些技术与信息安全的各个方面相关,如机密性、完整性(数据)、认证(实体)和数据来源的认证。

**定义 1.2。**两个集合, AB ,以及一个规则, f ,定义了一个函数。规则 f 将为 A 中的每个元素分配一个 B 中的元素。集合 A 被称为特征功能的,而 B 代表域。如果 a 表示 A 中的一个元素,记为 aA ,则 a图像借助规则 fB 中的元素表示; a 的图像 b 标注为b=f(a)。从 set A 到 set B 的函数 f 的标准符号表示为f:AB。如果 bB ,那么我们有一个 b 的前像,它是一个元素 aA 对于它f(A)=B。至少有一个原像的 B 中的整个元素集被称为 f图像,记为 Im ( f )。

例 1.3。 *(函数)*把集合 A = { abc }和 B = {1,2,3,4},把规则 fAB 定义为 f ( a 图 1-2 显示了集合 AB 和功能 f 的描述。元素 2 的原像是 af 的图像为{1,2,4}。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-2

从三元构成的集合 A 到五行构成的集合 B 的函数 f

例 1.4。 *(函数)*让我们考虑下面的一组 A = {1,2,3,…,10}并考虑 f 为规则对于每个 aAf(A)=r**A其中

)

)

)

)

)

f 的图像用集合 Y = {1,3,4,5,9}表示。

根据图 1-2 中描述的方案(在文献中,它被称为功能图)来考虑功能,其中来自域 A 的每个元素恰好有一个源自它的箭头。对于共域 B 中的每个元素,我们可以有任意数量的附带箭头(也包括零线)。

例 1.5。 (函数)让我们考虑下面定义为 A = {1,2,3,…,10 50 的集合并考虑 f 是规则f(A)=rA,其中 r a 表示余数在这种情况下,像我们在示例 1.4 中所做的那样明确地写下 f 是不可行的。也就是说,该函数完全由描述规则 f 的定义域和数学描述来定义。

1-1(一对一)函数

**定义 1.6。**如果在共域 B 中找到的每个元素都被表示为域 A 中至多一个元素的图像,我们可以说函数或变换是 11(一对一)。

**定义 1.7。**我们可以说一个函数或变换是的,如果在余域 B 中找到的每个元素都代表至少一个可以在该域中找到的元素的图像。同时,一个函数 f : AB 被称为 on ifIm(f)=B

**定义 1.8。**如果函数 f : AB 要考虑 11 和Im(f)=B,那么函数 f 就叫做双射。

**结论 1.9。**如果 f : AB 被认为是 1-1,那么f:AIm(f)表示双射。在特殊情况下,如果将f:AB表示为 1-1,将 AB 表示为大小相同的有限集,则 f 表示一个双射。

基于该方案及其表示,如果 f 表示一个双射,那么来自 B 的每个元素恰好有一条线伴随着它。示例 1.3 和 1.4 中所示和所述的函数不代表双射。正如您在示例 1.3 中看到的,元素 3 没有域中可以找到的任何其他元素的图像。在例 1.4 中,共域中的每个元素都用两个前像来标识。

**定义 1.10。如果 f 是从 AB 的双射,那么定义从 BA 的双射 g 就相当简单了:对于每个 bB 我们将定义g(B)=函数 gf 得到,称为 f反函数,记为g=f1

例 1.11。 *(反函数)*让我们考虑以下几组 A = { abcde }和 Y = {1,2,3,4,5},并考虑由图中的线给出并表示的规则 f f 代表一个双射,它的反义词 g 是通过反转箭头的方向而形成的。 g 的域用 B 表示,共域为 A

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-3

双射的表示 f 及其逆,g=f—1

请记住,如果 f 表示双射词,f—1也是双射词。密码学中的双射被用作消息加密的工具,逆变换被用于解密。解密的基本条件是变换是双射的。

单向函数

在密码学中,有某些类型的函数起着重要的作用。由于严格性,单向函数的定义如下。

**定义 1.12。**让我们考虑从集合 A 到集合 B 的函数 f ,如果 f ( a )被证明是简单的并且对于所有 aA 来说容易计算但是对于“基本上所有”元素 b 来说 以这种方式设法找到任何一个 aA 使得f(A)=b在计算上是不可行的。

**注 1.13。**本注释是对定义 1.12 中所用术语的一些附加注释和澄清

  1. 对于术语容易计算上不可行来说,一个严格的定义是必要的,但它会分散人们对正在达成一致的总体想法的注意力。对于本章的目标,简单直观的意义就足够了。

  2. 短语“基本上全部”指的是这样一种想法,即有几个值 bB ,很容易找到一个 aA ,使得B=f(A)。作为一个例子,可以为少量的 a 值计算b=f(a),然后对于这些值,通过查表知道其倒数。描述单向函数这一性质的另一种方式如下:对于任意随机的bIm(f),以这样的方式拥有并找到任意的 aA 在计算上是可行的,即f(A)=b

以下示例显示了单向函数背后的概念。

例 1.14。 (单向函数)考虑 A = {1,2,3,…,16}我们来定义一下f(A)=rA对于所有的元素 aA 其中rA

*
|

|

one

|

Two

|

three

|

four

|

five

|

six

|

seven

|

eight

|

nine

|

Ten

|

Eleven

|

Twelve

|

Thirteen

|

Fourteen

|

Fifteen

|

Sixteen

|
| — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
| f(a | three | nine | Ten | Thirteen | five | Fifteen | Eleven | Sixteen | Fourteen | eight | seven | four | Twelve | Two | six | one |

有一个介于 1 和 16 之间的数字,很容易在 f 下找到它的图像。例如,如果你面前没有桌子,对于 7,很难找到 a ,因为 f ( a ) = 7。如果给你的数字是 3,那么很容易看出 a = 1 是你实际需要的。

请记住,这是一个基于非常小的数字的示例。这里重要的方面是计算 f ( a )的工作量和给定 f ( a )求 a 的工作量是有区别的。此外,对于大数, f ( a )可以使用平方乘算法 [20 ]来有效地计算,而从 f ( a )中找到 a 的过程更难找到。

例 1.15。 (单向函数)**素数代表大于 1 的正整数,其正整数因子为 1 和自身。选择以下素数 p = 50633, q = 58411,computen=pq= 50633 58411 = 2957524163,我们来考虑一下 A = {1,2,3,…,n—1 }。我们通过f(A)=rA为每个 aA 定义一个 A 上的函数 f ,其中 r a 表示当x时的余数比如我们考虑 f (2489991 = 1981394214 既然 24899913= 5881949859n+1981394214。计算 f ( a )表示要做的一件简单的事情,但是颠倒程序是相当困难的。

陷门单向函数

**定义 1.16。**一个陷门单向函数被定义为一个具有附加属性的单向函数 f : AB ,通过具有额外的信息(被称为陷门信息)将变得可行,以找到并识别任何给定的BIm(f),用 a

在例 1.15 中,我们展示了单向活板门函数的概念。有了关于因子 n = 2957524163 的额外信息,反转该函数将变得容易得多。2957524163 的因数很大,手工计算很难找到。在任何计算机软件的帮助下,我们都能很快找到这些因素。例如,如果我们有非常大的不同的质数(每个数大约有 200 个十进制数字), pq ,利用今天的技术,即使是最强大的计算机也很难从 n 中找到 pq 。这是众所周知的问题,名为整数因式分解问题,对于量子计算机来说,这不是问题。

单向函数和陷门单向函数是公钥加密的基础。这些概念非常重要,当它们在加密技术中的应用被实现和讨论时,它们将变得更加清晰。记住本节中的这些抽象概念是非常重要的,因为它们是具体的方法,也是本书后面将要实现的加密算法的主要基础。

排列

置换表示密码结构中的函数。

**定义 1.17。**考虑 S 是由元素组成的有限集。 S 上的置换 p 表示定义 1.8 中定义的双射。双射从 S 到自身表示为p:SS

**例 1.18。**这个例子代表一个排列的例子。我们来考虑以下排列: S = {1,2,3,4,5}。排列 p : SS 定义如下:

)

排列可以用不同的方式来描述。它可以像上面那样编写,也可以像

)

其中数组的顶行由域表示,底行由映射的 p 下的图像表示。

因为排列是双射的,所以它们有逆。如果将排列写成 away(第二种形式),通过交换数组中的行并从新的顶行和底行重新排序元素,将很容易找到它的逆。在这种情况下, p 的倒数定义如下:

)

**例 1.19。这个例子代表一个排列的例子。让我们考虑一下 A 是整数{0,1,2,…,pq1 }的集合,其中 pq 代表两个不同的大素数。我们还需要假设,p1 和 q1 都不能被 3 整除。函数p(a)=ra,其中 r a 表示 a 3 除以 pq 的余数,可以演示并表示为逆变换。现在的计算机计算逆序是不可行的,除非已知 pq

对合

对合被认为是有自己逆的函数。

**定义 1.20。**让我们考虑定义为双射体 SS 的有限集合 Sf ,记为f:SS。在这种情况下,如果f=f-1,功能 f 将记为渐开线。另一种定义方式是f(f(a)=a对于任何 aS

**例 1.21。**这个例子代表一种对合情况。在图 1-4 中,我们描绘了一个对合的例子。在图 1-4 中,注意如果 j 代表 i 的图像,那么 i 代表 j 的图像。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-4

具有五个元素的集合的对合的表示

概念和基本术语

当我们处理密码学学科的科学研究时,我们看到它是建立在从基本概念产生的硬的和抽象的定义上的。在本节中,我们将列出本书中使用的最重要的术语和关键概念。

用于加密的域和共域
  • )被表示为一个有限集合,称为定义的字母表。让我们考虑一个例子,),这是二进制字母表,这是一个经常使用的定义。

  • )表示被称为消息空间的集合。消息空间包含字母表中的符号串,)。例如,)可能包含二进制字符串、英语文本、法语文本等。

  • )代表称为密文空间的集合。)包含来自字母表)的符号串,该字母表不同于为)定义的字母表。来自)的一个元素叫做密文

加密和解密转换
  • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传是一组被称为的密钥空间外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传中的一个元素被称为键。

  • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传的每个元素定义了从外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传的唯一双射,记为 E * e E e 被称为加密函数加密变换*。请记住, E * e * 必须是双射的,如果过程相反,并且为每个不同的密文恢复唯一的明文消息。

  • 对于每一个外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传,我们都有 D * d ,是从外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传的双射(例如外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传)。 D d 被称为解密函数解密变换。*

  • 当我们将变换EE应用于消息外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传时,通常称为加密 m 或 m 的加密

  • 当我们将变换 D d 应用于密文 c 时,我们称之为解密 c解密 c

  • 加密方案基于代表加密变换的集合外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传和相应的集合外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传,它们是解密变换,具有对于每个外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传都有唯一密钥外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传的属性,使得外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传;所以DD(EE(m)=m为所有外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传。加密方案也被称为密码。

  • ed 表示的密钥在以上定义中被称为密钥对,并且在一些文档中被标注为( ed )。在某些情况下, ed 可能相同。

  • 为了构造一个加密方案,我们必须选择一个消息空间外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传,一个密文外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传,一个密钥空间外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传,一组加密变换外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传,以及一组相应的解密变换外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

沟通过程中的参与者

从图 1-5 开始,定义了以下术语:

  • 一个实体代表发送、接收或操纵信息的某人或某物。在图 1-5 、爱丽丝鲍勃被表示为实体或当事人、计算机等。

  • 发送方代表双方通信中的实体。它代表了信息的合法传递者。在图 1-5 中,发送方爱丽丝表示。

  • 接收者代表双方通信中的实体。它代表信息的预期接收者。在图 1-5 中,接收器摆锤表示。

  • 一个对手(攻击者,或者有时为了简化例子,它被称为 Oscar 或 Eve1*)*代表两方通信中的一个实体。它既不是发送者,也不是接收者。对手试图破坏在发送者和接收者之间提供的信息安全服务。文献中发现的对手的其他名称有敌人、攻击者、对手、窃听者、入侵者和闯入者。通常,对手要么扮演合法发送者的角色,要么扮演合法接收者的角色。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-5

使用加密的双方通信过程

数字签名

在本书中,我们也将讨论数字签名。数字签名代表一种加密原语,它是认证、授权和不可否认性过程中的基础。数字签名的目标是为实体提供一种将其身份与一条信息进行映射的方法。签名的过程意味着将消息和由实体持有的被称为秘密信息的部分转换成被称为签名的标签*。*

大致描述如下:

  • )表示有可能被签名的消息集合。

  • )代表称为*签名的元素集合。*签名可以是固定长度的二进制字符串。

  • )被定义为从消息集)到签名集)的转换,称为实体 A (Alice)的签名转换)由 A 存储为秘密,并用于为来自)的消息创建签名。

  • VT3A表示从集合)到集合{ }的变换。)由所有对( ms )组成,其中)和),称为)和)的笛卡尔积。 V

签名程序

我们将使用一个实体 A ,我们将其命名为签名者。我们将通过应用以下消息为特定消息)创建一个签名:

  • 算出S=SA(m)。

  • 发送一对( ms ),其中 s 代表消息 m 的签名。

验证程序

为了验证消息 m 的签名 s 是由 A 创建的,扮演验证者角色的另一个实体 B (称为 Bob)执行以下步骤:

  • 获得对VT3AA 的验证功能。

  • 计算u=V??(ms )。

  • 如果 u = 为真,则同意由 A 创建的签名,如果 u = 为假,则拒绝该签名。

公钥密码学

公钥密码学在 .NET 以及我们需要实现相关算法的时候。有几个重要的商业库为开发者实现了公钥加密解决方案,比如 [21 - 30 。

为了更好地理解公钥加密的工作原理,让我们考虑一组定义为)的加密转换和一组定义为)的匹配解密转换,其中)代表密钥空间。考虑下面的加密/解密变换对关联( E * e D d ),并且让我们假设每个对都具有知道 E e * 的属性,这在计算上是不可实现的,具有随机密文)来管理以这样的方式识别消息)E定义的性质包括对于任何给定的 e 来说,确定相应的解密密钥 d 是不现实的。

有了上面的假设,让我们考虑如图 1-6 所示的 Alice 和 Bob 之间的双方通信。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-6

使用公开密钥机制的加密过程

  • 鲍勃将选择一个密钥对( ed )。

  • 鲍勃将通过任何信道将被称为公钥的加密密钥 e 发送给爱丽丝,并将保持被称为私钥的解密密钥 d 的安全和秘密。

  • 之后,爱丽丝将通过应用由鲍勃的公钥计算和确定的加密变换来发送消息 m 给鲍勃,以便得到c=EE(m)。鲍勃将使用由 d 唯一确定的逆变换 D d 对密文 c 进行解密。

加密密钥 e 不需要保密。可能会公之于众。任何实体都可以向 Bob 发送加密的消息,只有 Bob 有能力解密这些消息。图 1-7 说明了其中 A 1A 2A 3 代表不同实体的想法。记住如果 A 1 在将消息 m 1 加密到 c 1 后将其销毁,那么即使 A 1 在无法从 c 1 中恢复 m 1 的位置发现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-7

如何使用公钥加密

为了清楚起见,让我们以一个盒子为例,盒子的盖子被一把带有特定密码的锁锁住。鲍勃是唯一知道密码的人。如果锁由于任何原因保持未锁状态,并且因此是公开可用的,任何人都可以进入盒子内部并在里面留下消息并锁上锁。

哈希函数

.NET 提供了HashAlgorithm类,它是名称空间System.Security.Cryptography [19 的一部分。类表示当加密哈希算法的所有实现都必须派生时需要使用的基类。

作为一个例子(参见图 1-8 和清单 1-1 ),下面的 C# 代码示例将计算特定数组的SHA1CryptoServiceProvider散列。这个例子是基于我们已经有了一个预定义的字节数组dataArray[] 的假设。 SHA1CryptoServiceProvider代表一个从HashAlgorithm派生出来的类:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-8

SHA256 实现示例

HashAlgorithm sha = new SHA1CryptoServiceProvider();
byte[] result = sha.ComputeHash(dataArray);

哈希函数代表了现代密码学中最重要的原语之一。哈希函数也称为单向哈希函数。散列函数表示一种计算效率高的函数,它将任意长度的二进制串映射到固定长度的二进制串,称为散列值。

using System;
using System.IO;
using System.Security.Cryptography;

public class Program
{
    public static void Main(String[] args)
    {
        ApplyingHashOverADirectory obj = new ApplyingHashOverADirectory();
        obj.Compute();
    }

}

public class ApplyingHashOverADirectory
{
    public void Compute()
    {
        //if (args.Length < 1)
        //{
        //    Console.WriteLine("There is no directory selected to hash.");
        //    return;
        //}

        Console.Write("Enter the directory path: ");

        //string directory = args[0]; //D:\Apps C#\Chapter 1 - Cryptography Fundamentals
        string directory = Console.ReadLine();
        if (Directory.Exists(directory))
        {
            //** creating an object as DirectoryInfo
            //** which will represent the
            //** directory selected for hash
            var directories = new DirectoryInfo(directory);

            //** Obtaing the informations of the files from
            //** the directory select as FileInfo objects
            FileInfo[] files_from_directory = directories.GetFiles();

            //** create and SHA256 object and initialize it
            using (SHA256 mySHA256Object = SHA256.Create())
            {
                //** find the hash value for each
                //** of the file from the directory
                foreach (FileInfo file_information in files_from_directory)
                {
                    try
                    {
                        //** for each of the file
                        //** create a file stram
                        FileStream file_stream = file_information.Open(FileMode.Open);

                        //** put the position at
                        //** the beginning of the stream
                        file_stream.Position = 0;

                        //** find the hash of the
                        //** fileStream object
                        byte[] hash_value = mySHA256Object.ComputeHash(file_stream);

                        //** show the name and hash
                        //** value of the file in the console
                        Console.Write($"{file_information.Name}: ");
                        PrintByteArray(hash_value);

                        //** make sure that you close the file
                        file_stream.Close();
                    }
                    catch (IOException e)
                    {
                        Console.WriteLine($$"I/O Exception: { e.Message}");
                    }
                    catch (UnauthorizedAccessException e)
                    {
                        Console.WriteLine($"There is an error with accessing the file: { e.Message}");
                    }
                }
            }
        }
        else
        {
            Console.WriteLine("The directory selected couldn't be located or found. Please, select another one.");
        }
    }

    //** Show the byte array for the
    //** user under a readable structure
    public static void PrintByteArray(byte[] array)
    {
        for (int i = 0; i < array.Length; i++)
        {
            Console.Write($"{array[i]:X2}");
            if ((i % 4) == 3) Console.Write(" ");
        }
        Console.WriteLine();
    }
}

Listing 1-1C# Implementation of SHA256 for Files

哈希函数广泛用于数字签名和数据完整性。当我们处理数字签名时,长消息通常被散列,并且只有散列值被签名。然后,将接收消息的一方将对接收到的消息进行散列,并且他们将验证接收到的签名对于该散列值是正确的。这将通过直接对消息进行签名来节省时间和空间,直接签名包括将消息分割成适当大小的块并单独对每个块进行签名。

表 1-2 提供了带密钥的加密哈希函数的分类,表 1-3 提供了不带密钥的加密哈希函数。大多数功能已经在中实现 .NET 中的名称空间和类。

表 1-3

未加密的加密哈希函数

|

名字

|

长度

|

类型

|

文献学

|
| — | — | — | — |
| 布莱克-256 | 256 位 | 海法结构 [41 | [40 |
| 布莱克-512 | 512 位 | 海法结构 [41 | [40 |
| 幽灵 | 256 位 | 混杂 | [43 |
| MD2 | 128 位 | 混杂 |   |
| MD4 | 128 位 | 混杂 | [44 |
| 讯息摘要 5 | 128 位 | Merkel-damgard 建筑 [36 | [45 |
| MD6 | 高达 512 位 | 梅克尔树 NLFSR | [37 |
| 重复一遍 | 128 位 | 混杂 | [46 |
| ripd-128 战斗机 RIPEMD-256 战斗机 ripd-160 战斗机 ripd-320 战斗机 | 128 位-160 位 320 位 | 混杂混杂混杂混杂 | [46][47][48 |
| SHA-1 | 160 位 | Merkel-damgard 建筑 [36 | [61 |
| SHA-256SHA-384SHA-512 | 256 位 384 位 512 位 | 默克-达姆加德建筑公司 | [50][51][54 [52 ] [54 [53 ] [54 |
| 沙-224 | 224 位 | 默克-达姆加德建筑公司 | [55 |
| 沙-3 | 武断的 | 海绵功能 [50 | [56 ] [57 |
| 漩涡 | 512 位 | 混杂 | [58][59][60 |

表 1-2

键控加密散列函数

|

名字

|

标签的长度

|

类型

|

文献学

|
| — | — | — | — |
| BLAKE2 | 武断的 | 带前缀的键控哈希函数-MAC | [31 ] [42 |
| BLAKE3 | 武断的 | 带有提供的初始化向量的键控散列函数(IV) | [32 |
| HMAC | - | - | [33 |
| 断续器 | 武断的 | 基于凯克克 | [34 ] [35 |
| MD6 | 512 位 | 具有 NLFSR 的 Merkle 树 | [37 |
| 醋酸苯汞 | - | - | [38 |
| 一个 | - | - | [39 |

个案研究

凯撒密码的 C# 实现

在这一节中,我们将给出一个凯撒密码的 C# 实现。本节的目的是说明上面列出的数学基础如何在实施过程中发挥作用,以及理解算法背后的基本数学机制的优势。在本书中,我们不会关注算法的数学背景。如果想深入了解数学背景,建议参考以下参考文献 [6 - 18 ]。

凯撒密码使用的加密过程可以表示为模运算,首先将字母转换为数字。为此,我们将使用下面的字母 ),使得 A = 0, B = 1,…, Z = 25。字母 x 的加密通过移位 n 完成,数学上可以描述为

)

解密以类似的方式完成:

)

让我们开始算法的实现。在解决方案资源管理器中,我们只有一个单独的文件,StartCaesar.cs(见图 1-9 )

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-9

凯撒密码工程的结构(主窗体,Program.cs)

该应用(见图 1-10 和清单 1-2 )非常简单,易于交互。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-10

凯撒密码(编码和解码)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleCaesarCipher
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Enter the plaintext/ciphertext: \n");
            string text = Convert.ToString(Console.ReadLine());

            Console.WriteLine("\nChoose a cryptographic method:");
            Console.WriteLine("\t\t 1 -> Encrypt");
            Console.WriteLine("\t\t 2 -> Decrypt\n\n");
            int option = Convert.ToInt32(Console.ReadLine());

            if (option == 1)
            {
                Console.WriteLine("\nEnter the key: ");
                int key = Convert.ToInt32(Console.ReadLine());
                Console.WriteLine("The encryption of text is {0}. ", Encode(text, key));
            }
            if (option == 2)
            {
                Console.WriteLine("\nEnter the key: ");
                int key = Convert.ToInt32(Console.ReadLine());
                Console.WriteLine("The decryption of the ciphertext is {0}. ", Decode(text, key));
            }

            Console.ReadKey();
        }

        private static string Encode(string plaintext, int key)
        {
            string alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            string final = "";

            int indexOfChar = 0;
            char encryptedChar;

            //Convert/encrypt each and every character of the text
            foreach (char c in plaintext)
            {
                //Get the index of the character from alphabets variable
                indexOfChar = alphabets.IndexOf(c);

                //if encounters an white space
                if (c == ' ')
                {
                    final = final + c;
                }

                //if encounters a new line
                else if (c == '\n')
                {
                    final += c;
                }

                //if the character is at the end of the string "alphabets"
                else if ((indexOfChar + key) > 25)
                {
                    //encrypt the character
                    encryptedChar = alphabets[(indexOfChar + key) - 26];

                    //add the encrypted character to a string every time to get an encrypted string
                    final += encryptedChar;
                }
                else
                {
                    //encrypt the character
                    //add the encrypted character to a string every time to get an encrypted string
                    encryptedChar = alphabets[indexOfChar + key];
                    final += encryptedChar;
                }
            }

            return final;
        }

        private static string Decode(string ciphertext, int key)
        {
            string alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            string final = "";

            int indexOfChar = 0;
            char decryptedChar;

            //Convert/decrypt each and every character of the text
            foreach (char c in ciphertext)
            {
                //Get the index of the character from alphabets variable
                indexOfChar = alphabets.IndexOf(c);

                //if encounters a white space
                if (c == ' ')
                {
                    final = final + c;
                }

                //if encounters a new line
                else if (c == '\n')
                {
                    final = final + c;
                }

                //if the character is at the start of the string "alphabets"
                else if ((indexOfChar - key) < 0)
                {
                    //decrypt the character
                    //add the decrypted character to a string every time to get a decrypted string
                    decryptedChar = alphabets[(indexOfChar - key) + 26];
                    final = final + decryptedChar;
                }
                else
                {
                    //decrypt the character
                    //add the decrypted character to a string every time to get a decrypted string
                    decryptedChar = alphabets[indexOfChar - key];
                    final = final + decryptedChar;
                }
            }

            //Display decrypted text
            return final;
        }
    }
}

Listing 1-2Caesar Cipher Implementation

用 C# 实现 Vigenére 密码

Vigenére 密码(见图 1-11 和清单 1-3 )代表了一种经典的加密字母文本的方法,它使用一系列不同的基于关键字字母的凯撒密码。一些文献显示它是一种多字母替换的形式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-11

Vigenére 密码、加密和解密操作

该密码的简短代数描述如下。这些数字将作为数字( A = 0, B = 1,)并且加法运算被执行为 26。使用 K 作为密钥的 Vigenére 加密 E 可以写成

)

并且使用密钥 K 作为解密 D

)

其中M=M1Mn为消息,C=C1Cn代表密文, K = K

我们的实现有两个重要的文件*。*文件Program.cs包含加密和解密操作的函数。参见图 1-12 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-12

Vigenére 密码项目的结构

清单 1-3 中显示了Program.cs的源代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleVigenereCipher
{
    class Program
    {
        static void Main(string[] args)
        {
            VigenereCipher vigenere_engine = new VigenereCipher();

            //the key used for encryption or decryption
            string key = "apress";

            //the text that will encrypted
            //encrypted value is "WTCGGEEIFEHJEHJ"
            string text_for_encryption = "WELCOMETOAPRESS";
            string ciphertext = "WTCGGEEIFEHJEHJ";

            //You can use also Decrypt
            Console.WriteLine("The text that will be encrypted is: {0}", text_for_encryption);
            Console.WriteLine("The key used is: {0}", key);
            Console.WriteLine("The encryption is {0}. ", vigenere_engine.Encrypt(key, text_for_encryption));
            Console.WriteLine("The decryption is {0}. ", vigenere_engine.Decrypt(key, ciphertext));
            Console.ReadKey();
        }
    }
    class VigenereCipher
    {
        Dictionary<sbyte, char> TheAlphabet = new Dictionary<sbyte, char>();

        public VigenereCipher()
        {
            //Me from the future: wtf lol
            TheAlphabet.Add(0, 'A');
            TheAlphabet.Add(1, 'B');
            TheAlphabet.Add(2, 'C');
            TheAlphabet.Add(3, 'D');
            TheAlphabet.Add(4, 'E');
            TheAlphabet.Add(5, 'F');
            TheAlphabet.Add(6, 'G');
            TheAlphabet.Add(7, 'H');
            TheAlphabet.Add(8, 'I');
            TheAlphabet.Add(9, 'J');
            TheAlphabet.Add(10, 'K');
            TheAlphabet.Add(11, 'L');
            TheAlphabet.Add(12, 'M');
            TheAlphabet.Add(13, 'N');
            TheAlphabet.Add(14, 'O');
            TheAlphabet.Add(15, 'P');
            TheAlphabet.Add(16, 'Q');
            TheAlphabet.Add(17, 'R');
            TheAlphabet.Add(18, 'S');
            TheAlphabet.Add(19, 'T');
            TheAlphabet.Add(20, 'U');
            TheAlphabet.Add(21, 'V');
            TheAlphabet.Add(22, 'W');
            TheAlphabet.Add(23, 'X');
            TheAlphabet.Add(24, 'Y');
            TheAlphabet.Add(25, 'Z');
        }

        private bool CheckIfEmptyString(string Key, string Text)
        {
            if (string.IsNullOrEmpty(Key)
                || string.IsNullOrWhiteSpace(Key))
            {
                return true;
            }
            if (string.IsNullOrEmpty(Text)
                || string.IsNullOrWhiteSpace(Text))
            {
                return true;
            }
            return false;
        }

        public string Encrypt(string Key, string Text)
        {
            try
            {
                Key = Key.ToUpper();
                Text = Text.ToUpper();

                if (CheckIfEmptyString(Key, Text))
                    { return "Enter a valid string"; }

                string ciphertext = "";

                int i = 0;

                foreach (char element in Text)
                {
                    //** if we are having a character
                    //** that is not in the alphabet
                    if (!Char.IsLetter(element))
                        { ciphertext += element; }
                    else
                    {
                        //Obtain from the dictionary TKey by the TValue
                        sbyte T_Order = TheAlphabet.FirstOrDefault
                            (x => x.Value == element).Key;
                        sbyte K_Order = TheAlphabet.FirstOrDefault
                            (x => x.Value == Key[i]).Key;

                        sbyte Final = (sbyte)(T_Order + K_Order);
                        if (Final > 25) { Final -= 26; }
                        ciphertext += TheAlphabet[Final];
                        i++;
                    }
                    if (i == Key.Length) { i = 0; }
                }

                return ciphertext;
            }
            catch (Exception E)
            {
                return "Error: " + E.Message;
            }
        }

        public string Decrypt(string Key, string Text)
        {
            try
            {
                Key = Key.ToUpper();
                Text = Text.ToUpper();

                if (CheckIfEmptyString(Key, Text)) { return "Enter a valid string value!"; }

                string plaintext = "";

                int i = 0;

                foreach (char element in Text)
                {
                    //if the character is not an alphabetical value
                    if (!Char.IsLetter(element)) { plaintext += element; }
                    else
                    {
                        sbyte TOrder = TheAlphabet.FirstOrDefault
                            (x => x.Value == element).Key;
                        sbyte KOrder = TheAlphabet.FirstOrDefault
                            (x => x.Value == Key[i]).Key;
                        sbyte Final = (sbyte)(TOrder - KOrder);
                        if (Final < 0) { Final += 26; }
                        plaintext += TheAlphabet[Final];
                        i++;
                    }
                    if (i == Key.Length) { i = 0; }
                }

                return plaintext;
            }
            catch (Exception E)
            {
                return "Error: " + E.Message;
            }
        }
    }
}

Listing 1-3Vigenére Implementation

我们将继续进行源代码分析,如下所示。让我们检查来自Program.cs的源代码,更准确地说,是代表主操作EncryptDecrypt的两个操作背后的源代码。

清单 1-4 中显示了Encrypt方法的源代码。

public string Encrypt(string Key, string Text)
    {
        try
        {
            Key = Key.ToUpper();
            Text = Text.ToUpper();

            if (CheckIfEmptyString(Key, Text))
                { return "Enter a valid string"; }

            string ciphertext = "";

            int i = 0;

            foreach (char element in Text)
            {
                //** if we are having a character
                //** that is not in the alphabet
                if (!Char.IsLetter(element))
                    { ciphertext += element; }
                else
                {
                    //Obtain from the dictionary TKey by the TValue
                    sbyte T_Order = TheAlphabet.FirstOrDefault
                        (x => x.Value == element).Key;
                    sbyte K_Order = TheAlphabet.FirstOrDefault
                        (x => x.Value == Key[i]).Key;

                    sbyte Final = (sbyte)(T_Order + K_Order);
                    if (Final > 25) { Final -= 26; }
                    ciphertext += TheAlphabet[Final];
                    i++;
                }
                if (i == Key.Length) { i = 0; }
            }

            return ciphertext;
        }
        catch (Exception E)
        {
            return "Error: " + E.Message;
        }
    }

Listing 1-4Source Code for the Encryption Method

清单 1-5 中显示了Decrypt方法的源代码。

public string Decrypt(string Key, string Text)
        {
            try
            {
                Key = Key.ToUpper();
                Text = Text.ToUpper();

                if (CheckIfEmptyString(Key, Text)) { return "Enter a valid string value!"; }

                string plaintext = "";

                int i = 0;

                foreach (char element in Text)
                {
                    //if the character is not an alphabetical value
                    if (!Char.IsLetter(element)) { plaintext += element; }
                    else
                    {
                        sbyte TOrder = TheAlphabet.FirstOrDefault
                            (x => x.Value == element).Key;
                        sbyte KOrder = TheAlphabet.FirstOrDefault
                            (x => x.Value == Key[i]).Key;
                        sbyte Final = (sbyte)(TOrder - KOrder);
                        if (Final < 0) { Final += 26; }
                        plaintext += TheAlphabet[Final];
                        i++;
                    }
                    if (i == Key.Length) { i = 0; }
                }

                return plaintext;
            }
            catch (Exception E)
            {
                return "Error: " + E.Message;
            }
        }

Listing 1-5Souce Code for the Decryption Method

结论

在这一章中,我们简要介绍了加密原语和机制的基础知识。本章包括以下内容:

  • 安全和信息安全目标

  • 1-1、单向和陷门单向函数在设计和实现密码函数中的重要性

  • 数字签名及其工作原理

  • 公钥加密及其对开发应用的影响

  • 哈希函数

  • 案例研究,说明读者在学习高级加密概念之前需要了解的基本概念

在下一章,我们将学习概率论、信息论、数论和有限域的基础知识。我们将讨论它们的重要性,以及它们在已经存在的实现中是如何关联的 .NET 以及它们如何对开发人员有用。

文献学

  1. 卡恩大卫。《解密者:秘密写作的故事》,1967 年。

  2. W.《密码学的新方向》IEEE Trans。信息论。22,6(2006 年 9 月),第 644-654 页。土井:??

  3. R.L. Rivest、A. Shamir 和 L. Adleman,“获取数字签名和公钥密码系统的方法”,《通信 ACM》,第 21 卷,第 2 期,第 120-126 页,1978 年。

  4. T.基于离散对数的公钥密码系统和签名方案。在:布莱克里 G.R .,乔姆 d .(编辑)密码学进展。密码 1984。计算机科学讲义,第 196 卷。斯普林格,柏林,海德堡。

  5. ISO/IEC 9796-2:2010–信息技术–安全技术–提供消息恢复的数字签名方案。可用: www.iso.org/standard/54788.html

  6. Bruce Schneier 和 Phil Sutherland,应用密码学:C 语言中的协议、算法和源代码(第二版),ISBN: 978-0-471-12845-8。美国约翰·威利&儿子公司。1995.

  7. 加密和网络安全:原理和实践。新泽西州上马鞍河:普伦蒂斯霍尔出版社,1999 年。

  8. 密码学:理论与实践(第一版。),ISBN: 978-0-8493-8521-6,美国 CRC 出版社。1995

  9. Neal Koblitz,数论和密码学课程。纽约:施普林格出版社,1994 年。

  10. Neal Koblitz 和 A J. Menezes,密码学的代数方面,1999 年。

  11. Oded Goldreich,密码学基础:基本工具。剑桥:剑桥大学出版社,2001。打印。

  12. 现代密码学,概率证明和伪随机性。柏林:施普林格,1999 年。打印。

  13. Michael G. Luby,伪随机性和密码应用。新泽西州普林斯顿:普林斯顿大学出版社,1996 年。打印。

  14. Bruce Schneier,秘密与谎言:网络世界的数字安全。纽约:约翰·威利出版社,2000 年。

  15. 彼得·索斯坦森和阿伦·加内什。网络安全与密码学。普伦蒂斯霍尔专业技术参考,2003 年。

  16. Adrian Atanasiu,cryptografie(密码术)–第 1 卷,InfoData,2007,ISBN: 978-973-1803-29-6,978-973-1803-16-6。有罗马尼亚语版本。

  17. Adrian Atanasiu,protocol de Securitate(安全协议)–第 2 卷, InfoData,2007,ISBN: 978-973-1803-29-6,978-973-1803-16-6。有罗马尼亚语版本。

  18. Alfred J. Menezes,Scott A. Vanstone 和 Paul C. Van Oorschot,应用密码学手册(第一版)。美国 CRC 出版社,ISBN: 978-0-8493-8523-0。1996.

  19. 命名空间系统。安全.密码学, https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography?view=netframework-4.8

  20. 亨利·科恩、格哈德·弗雷、罗伯托·阿万齐、克里斯托夫·多切、坦贾·朗格、阮金和弗雷德里克·维考特伦,《椭圆和超椭圆曲线密码手册》,第二版(第二版)。查普曼&霍尔/CRC。2012.

  21. OpenPGP 库 for .NET .可用: www.didisoft.com/net-openpgp/

  22. 充气城堡. NET .可用: www.bouncycastle.org/csharp/

  23. 尼瑟姆。雪崩: https://github.com/Nethereum

  24. 博坦。可用: https://botan.randombit.net/

  25. Cryptlib。可用: www.cs.auckland.ac.nz/~pgut001/cryptlib/

  26. Crypto++。可用: www.cryptopp.com/

  27. Libgcrypt。可用: https://gnupg.org/software/libgcrypt/

  28. 利布钠。可用: https://nacl.cr.yp.to/

  29. 荨麻。可用:www . lysator . Liu . se/~ nisse/nettle/

  30. OpenSSL。可用: www.openssl.org/

  31. J.郭,P. Karpman,I. Nikoli,L. Wang,S. Wu,BLAKE 的分析 2。摘自:Benaloh J. (eds)“密码学主题–CT-RSA 2014。”CT-RSA 2014。计算机科学讲义,第 8366 卷。施普林格,查姆。

  32. Blake3。可用: https://github.com/BLAKE3-team/BLAKE3/

  33. H.科劳兹克,m .贝拉里,r .卡内蒂,“HMAC:信息认证的密钥散列法”,RFC 2104,1997。

  34. API KMAC。可用: www.cryptosys.net/manapi/api_kmac.html

  35. John Kelsey,Shu-jen Chang,Ray Perlner, SHA-3 衍生函数:cSHAKE,KMAC,TupleHash 和 ParallelHash ,NIST 特刊 800-185,美国国家标准与技术研究所,2016 年 12 月。

  36. I.B. Damgard,“哈希函数的设计原则”,LNCS 435 (1990),页 516-527。

  37. Ronal L. Rivest,“MD6 哈希函数。向 NIST 提交的 SHA-3 建议可用: http://groups.csail.mit.edu/cis/md6/submitted-2008-10-27/Supporting_Documentation/md6_report.pdf

  38. PMAC。可用: https://web.cs.ucdavis.edu/~rogaway/ocb/pmac.htm

  39. UMAC。可用: http://fastcrypto.org/umac/

  40. 布雷克-256。可用: https://docs.decred.org/research/blake-256-hash-function/

  41. Eli Biham 和 Orr Dunkelman,“迭代哈希函数的框架——HAIFA”第二届 NIST 密码散列研讨会-通过密码学 ePrint 档案:报告 2007/278。24.2006.

  42. BLAKE2 正式实现。可用: https://github.com/BLAKE2/BLAKE2

  43. GOST。可用: https://tools.ietf.org/html/rfc5830

  44. Roland L. Rivest,“MD4 报文摘要算法”,LNCS,537,1991 年,第 303-311 页。

  45. Roland L. Rivest,“MD5 消息摘要算法”,RFC 1321,1992 年。

  46. RIPEMD-128。可用: https://homes.esat.kuleuven.be/~bosselae/ripemd/rmd128.txt

  47. RIPEMD-160。可用: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html

  48. RIPEMD-160。可用: https://ehash.iaik.tugraz.at/wiki/RIPEMD-160

  49. 海绵和双层结构。可用: https://keccak.team/sponge_duplex.html

  50. 亨利·吉尔伯特,海伦娜·汉初。" SHA-256 姐妹的安全分析."2003 年密码学的选定领域:第 175-193 页。

  51. SHA256 .NET 类。可用: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha256?view=netframework-4.8

  52. SHA384 .NET 类。可用: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha384?view=netframework-4.8

  53. SHA512 .NET 类。可用: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha512?view=netframework-4.8

  54. 对 SHA-256、SHA-384 和 SHA-512 的描述。可用: www.iwar.org.uk/comsec/resources/cipher/sha256-384-512.pdf

  55. 一个 224 位的单向散列函数:SHA 224。可用: www.iwar.org.uk/comsec/resources/cipher/sha256-384-512.pdf

  56. Paul Hernandez,“NIST 发布 SHA-3 加密哈希标准”,2015 年。

  57. Morris J. Dworkin,“SHA-3 标准:基于排列的散列和可扩展输出函数”。联邦 Inf。流程。性病。(NIST FIPS)-202。2015.

  58. 保罗. S. L. M .巴雷托《漩涡散列函数》。2008.2017-11-29 从原件存档。检索到 2018-08-09。

  59. 保罗. S. L. M .巴雷托和文森特.里门,《漩涡散列函数》。2003.存档自(ZIP)2017-10-26。检索到 2018-08-09。

  60. 惠而浦 C# 实现。可用: http://csharptest.net/browse/src/Library/Crypto/WhirlpoolManaged.cs

  61. 王晓云,尹依群,和,在完全中寻找碰撞,密码 2005。

  62. 肖尔算法。网上有: https://en.wikipedia.org/wiki/Shor%27s_algorithm

https://en.wikipedia.org/wiki/Alice_and_Bob 爱丽丝和鲍勃

*

二、数学背景及其在密码学中的应用

概率论

基础

本章将讨论*实验、概率分布、事件、互补事件、互斥的概念。*这些概念将帮助读者理解密码和密码分析机制的基本概念,以及它们是如何根据概率设计的 [1 ]。

定义 2.1 。一个实验代表产生一组给定结果之一的过程。结果因人而异。可能的事件被称为简单事件。所有可能的结果被称为样本空间。

在这一章中,我们将讨论离散样本空间,即可能结果有限的样本空间。我们将一个样本空间的简单事件表示为 G 标记为 g 1g 2 ,…, g n

定义 2.2 。在 G 上的一个概率分布 O 由一系列非负且和等于 1 的数字 o 1o 2 、…、 o n 表示。数字 o i 有一种解释为 g i概率,代表实验的结果。

定义 2.3 。一个事件 E 代表样本空间 G 的一个子集。在这种情况下,事件 E 将发生的概率,记为 P ( E ),代表属于 E 的所有简单事件 g i 的概率 o i 之和。如果gISP({SI})简称为P(S**I)。

定义 2.4 。假设 E 是一个事件,那么互补事件代表不属于 E 的简单事件集合,记为)。

演示 2.1 。如果 ES 代表一个事件,考虑以下情况:

  • 0 ≤ P ( E ) ≤ 1。另外, P ( S ) = 1, P ( ϕ ) = 0,其中 ϕ 代表空集。

  • )。

  • 如果在 S 的结果也是一样的可能性,我们可以考虑)。

定义 2.5 。让我们考虑两个互斥事件,E1 和E2。如果下面的表达式等于 0,p(e1e2)= 0,则它们是互斥的。一两个事件的出现将排除其他事件发生的可能性。

定义 2.6。让我们考虑以下两个事件, E 1E2。

  • p(e1)≤T6【p】(e**将成为

** P(E1E2)+P(E1E2)=P(E1)+P 相应地,如果 E 1E 2 被认为是互斥的,则出现以下表达式:P(E1E*2=P(E1**

*### 条件概率

定义 2.7 。我们将考虑以下两个事件, E 1E 2 ,其中P(E2)>0。条件概率对于 E 1E 2 写成P(E1|E2)并表示为

)

P(E1|E2)衡量事件 E 1 发生的概率,假设 E 2 已经发生。

定义 2.8 。让我们考虑以下两个事件, E 1E2。说他们是独立如果p(e1⋂ee2)=p(e1)p(e2)。

定义 2.9 。(贝叶斯定理)如果我们有两个事件, E 1E 2 ,用P(E2)>0,那么

)

随机变量

让我们开始用分布概率 P 定义一个样本空间 S

定义 2.10 。让我们考虑一下 X随机变量、与应用于 S 上的函数的实数集合。对于每个事件,SISX 会分配一个实数X(SI)。

定义 2.11 。设 XS 上的随机变量。表示 X期望值

)

关于平均值或期望值的 C# 实现,请参考案例研究 3:计算概率分布的平均值。

演示 2.12 。考虑一下 XS 上的随机变量。在这种情况下,

)

演示 2.13 。我们来考虑一下 S 上的以下随机变量: X 1X 2 ,…, X m 。以下为实数: a 1a 2 ,…, a m 。然后我们会有

)

定义 2.14 。让我们考虑随机变量 X 。均值 μX方差

)

定义的非负数表示

关于平均值或期望值的 C# 实现,请参考案例研究 4:计算方差

X标准差Var ( X )的非负平方根表示。

关于平均值或期望值的 C# 实现,请参考案例研究 5:计算标准偏差

生日问题

定义 2.15.1 。我们来考虑两个正整数 abab ,其中数字m(n)定义如下:

)

定义 2.15.2 。我们来考虑两个非负整数 abab 。第二类的斯特林数,记为),为

)

)的情况被认为是例外。

演示 2.16 。我们将通过一个骨灰盒的例子来检验经典的占用问题,这个骨灰盒有从 1 到 m 的球。让我们假设 b 球从骨灰盒中一次取出一个,然后放回原处,并列出它们的编号。 l 不同的球被抽中的概率是

)

生日问题代表了占用问题的一个最特殊的情况。

演示 2.17 。让我们考虑一下生日问题,我们有一个骨灰盒,里面有从 1 到 T4 到 T5 的球。假设从瓮中一次取出一个特定数量的球 h ,然后放回原处,并列出它们的号码。

案例 2.17.1 。我们至少有一个巧合的概率,例如一个球至少被从瓮中抽出两次,就是

)

案例 2.17.2 。让我们考虑一下 h 从骨灰盒中取出的球的具体数量。如果)和一个 → ∞,那么

)

提供的演示将解释为什么概率分布被称为生日惊喜生日悖论 一个 23 人的房间里至少有 2 个人生日相同的概率是P2(365,23) ≈ 0.507,大得惊人。数量 P 2 (365, h )的增加与 h 的增加一样快。举个例子,P2(365,30) ≈ 0.706。

关于生日悖论的 C# 实现,请参考案例研究 4:生日悖论。

信息论

让我们考虑 X 一个随机变量,它取一组有限的值 x 1x 2 ,…, x n ,其概率为P(X=XI=) 其中 0≤pI≤1 对于每个 i ,1 ≤ in ,其中

)

同样,让我们考虑随机变量 YZ ,它们将取一组有限的值 [1 ]。

X 的熵代表一个观测值 x 提供的信息量的数学度量。

定义 2.18 。让我们把 X 看作一个随机变量,把X 的不确定性定义为

)

根据惯例,

)如果 p * i * = 0。

定义 2.19 [1 ] [5 。我们来考虑 XY ,两个随机变量。将联合熵定义为

)

其中 xy 的范围是随机变量的所有值, XY

定义 2.20 。先考虑随机变量 XY ,再考虑 X条件熵给定 Y = y

)

其中 x 将在随机变量 X 的所有值范围内。在这种情况下, X条件熵给定 Y ,也叫 Y 的关于 X ,为

)

其中索引 y 将覆盖 Y 的所有值。

数论

整数

我们将从一组整数{…,3,2,1,0,1,2,3,…}用符号ℤ.来表示的想法开始

定义 2.21 。让我们考虑两个整数, ab 。假设有一个整数 c 存在,那么 a 除以 b 使得b=ac。如果我们处于 a 除以 b 的情况,那么我们可以说 ab

定义 2.22 ( 整数的除法算法)。让我们考虑两个整数, ab ,其中 b ≥ 1,然后我们将 a 除以 b 得到整数 q ( )和 r ( 余数,这样

a=q**b+r,其中 0≤r<b

定义 2.23 。让我们把 c 看作整数。 ab 的公约数如果 cacb

定义 2.24 。我们将考虑一个非负整数 d ,称为整数 ab最大公约数(gcd) 。我们将它记为 d = gcd ( ab ),如果

  1. d 是公约数 ab

  2. cacb ,然后是 cd

定义 2.25 。我们将考虑一个非负整数 d ,整数 ab最小公倍数(lcm) ,表示为d=LCM(ab ,如果

  1. adbd

  2. acbc ,然后是 dc

ℤ的算法

我们将考虑两个非负整数, ab ,每个小于 n 或等于 n 。请记住, n 的二进制表示中的位数是⌊ lg n ⌋ + 1,这个数字将由 lg n 来近似表示。表 2-1 总结了使用经典算法对整数进行四种基本运算所涉及的位操作数。

表 2-1

ℤ基本运算的位复杂度

|

操作

|

比特复杂度

|
| — | — |
| 加法 a + b | (LGA+【lgb】=(【lgn】 |
| 减法ab | (LGA+【lgb】=(【lgn】 |
| 乘a〔t1〕T2〕b〔T3〕 | O((LGA)(lgb)=O((lgn)2) |
| 师a=q**b+r | O((lgq)(lgb)=O((lgn)2) |

**演示 2.26。**整数 ab 都是正数带 a > b ,那么我们就有了 gcd( ab ) = gcd ( ba mod b )。

算法 2.271。欧几里德算法用于计算***【gcd】***两个整数

)

)

)

)

)

欧几里德算法具有被扩展的可能性,使得它不仅将产生两个整数 ab 的 gcd d ,还将产生整数 xy ,这将满足ax+by=d

算法 2.281。扩展的欧几里德算法

)

)

  1. If b = 0, then set

    )

    )

    )

    )

  2. 集 x;;;【0,【2】;【0,】

** While b > 0 then

![$$ 3.1.q\leftarrow \left\lfloor \frac{a}{b}\right\rfloor $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equab.png)

![$$ r\leftarrow a- qb $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equac.png)

![$$ x\leftarrow {x}_2-q{x}_1 $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equad.png)

![$$ y\leftarrow {y}_2-q{y}_1 $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equae.png)

![$$ 3.2.a\leftarrow b $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equaf.png)

![$$ b\leftarrow r $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equag.png)

![$$ {x}_2\leftarrow {x}_1 $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equah.png)

![$$ {x}_1\leftarrow x $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equai.png)

![$$ {y}_2\leftarrow {y}_1 $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equaj.png)

![$$ {y}_1\leftarrow y $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equak.png)

 *   *Set d* ← *a*, *x* ← *x*<sub>2</sub>, *y* ← *y*<sub>2</sub>

![$$ Return\ \left(d,x,y\right) $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_2_Chapter_TeX_Equal.png)

 *

在案例研究 7 的部分:(扩展)欧几里德算法*,欧几里德和扩展欧几里德算法这两种类型都有一个 C# 实现。

例 2.29 。扩展欧几里德算法的例子。在表 2-2 中,我们展示了上述算法的步骤(算法 2.28)。至于输入,我们有如下: a = 4864 和 b = 3458。因为 gcd(4864,3458) = 38 且(4864)(32)+(3458)(45)= 38。

表 2-2

扩展欧几里德算法

| *问* | *r* | *x* | *y* | *答* | *b* | *x*2 | *x*T2 1 | *y*2 | *y*T2 1 | | *N* / *A* | *N* / *A* | *N* / *A* | *N* / *A* | Four thousand eight hundred and sixty-four | Three thousand four hundred and fifty-eight | one | Zero | Zero | one | | one | One thousand four hundred and six | one | -1 | Three thousand four hundred and fifty-eight | One thousand four hundred and six | Zero | one | one | -1 | | Two | Six hundred and forty-six | -2 | three | One thousand four hundred and six | Six hundred and forty-six | one | -2 | -1 | three | | Two | One hundred and fourteen | five | -7 | Six hundred and forty-six | One hundred and fourteen | -2 | five | three | -7 | | five | Seventy-six | -27 | Thirty-eight | One hundred and fourteen | Seventy-six | five | -27 | -7 | Thirty-eight | | one | Thirty-eight | Thirty-two | -45 | Seventy-six | Thirty-eight | -27 | Thirty-two | Thirty-eight | -45 | | Two | Zero | -91 | One hundred and twenty-eight | Thirty-eight | Zero | Thirty-two | -91 | -45 | One hundred and twenty-eight |

整数模 n

我们将认为 p 是一个正整数。

定义 2.30 。让我们把 xy 看作两个整数。我们说 x 全等于 y 模 p 。使用的符号是

xy(mod p),如果 p 会除(xy)

这个 p 叫做同余的

定义 2.31 。我们来考虑一下mpmp乘逆的定义是一个整数yp这样 m y ≡ 1 ( mod p )。如果我们有这样的 m 存在,那么这个 m 是唯一的,并且 m 被称为可逆单元。m的倒数记为m1。

参考案例分析 8:计算模 m f 下的乘法逆或者模 m 下的乘法逆的 C# 实现。

定义 2.32。****【中国剩余定理】。我们来考虑以下几个整数 n 1n 2 ,…, n k ,一个两两相对的素数。于是我们有了下面的同余系统

)

)

)

)

**那有模的唯一解,n=n1n2nk

m 下乘法逆运算的 C# 实现参见案例分析 9:中国剩余定理

定义 2.33。 高斯算法 **。**在中国剩余定理中,同余式的解法 x 可以计算为)其中NI=N/NI)。所列操作可以在O((lgn)2)位操作中完成。

算法ℤ n

让我们考虑一个正整数 n 。正如我们观察到的,元素ℤ n ,然后是

)

算法 2.341。计算乘法逆运算n

)

)

)

)

算法 2.351。ℤn

)

)

中反复求幂的平方乘算法

  1. 设置 b ← 1。如果 k = 0,则返回 ( b )

  2. 设一个设一个

  3. 如果 k 0 = 1,则设置 ba

  4. 对于 I 从 1 到 t做如下:

    1. 4.1 .设置 a*【a】*【2】修改 n

    2. 若 k i = 1,则设置 bA b mod n

  5. 返回 ( b )。

勒让德和雅可比符号

勒让德符号是监控一个整数 a 是否是一个素数的二次剩余模的最有用的工具之一。

定义 2.36 [1 。让我们把 p 看作一个奇数质数,把看作一个整数。勒让德符号记为),定义如下:

)

属性 2.37。勒让德符号的属性[1**。让我们考虑以下性质,称为勒让德符号的性质。我们认为 p 是一个奇素数。两个整数 ab ∈ ℤ.在这种情况下,将考虑 Legendre 符号的以下属性:**

**1. )在这种特殊情况下,)和)自 1∈Qpifp≡1(mod4)和)ifp≡3(mod4)。

  1. )既然如果),那么)

  2. 如果ab(mod p),那么)

  3. )自) if * p * ≡ 1 或 7 ( * mod * 8),if * p * ≡ 3 或 5 ( * mod * 8)。

  4. If q represent an odd prime distinct from p, we have

    )

Jacoby 符号代表 Legendre 符号对于非奇数且不一定是质数的整数 n 的推广。

定义 2.38。雅可比定义[1**。考虑一个整数 n ≥ 3 是奇数,一个质因数分解为**

)

**雅可比符号)定义如下:

)

请注意,如果 n 是质数,则雅可比符号就是勒让德符号。

属性 2.39。属性为雅各比符号[1**。认为 m ≥ 3、 n ≥ 3 为奇数,认为 ab ∈ ℤ.雅可比符号具有以下属性:**

**1. )。不止于此,)当且仅当 gcd( an ) ≠ 1。

  1. )。因此如果)那么)。

  2. )

  3. 如果ab(mod n),那么)

  4. )

  5. )因此) if * n * ≡ 1 ( * mod * 4),if * n * ≡ (3 * mod * 4)。

  6. )因此) if * n * ≡ 1 或 7 ( * mod * 8),if * n * ≡ 3 或 5 ( * mod * 8)。

  7. )换句话说,)除非 mn 都同余于 3 模 4,在这种情况下)。

算法 2.40。雅可比符号(和勒让德符号)计算[1

)

)

)

**1. 如果 = 0,则返回 0。

  1. 如果一个 = 1,则返回 1。

  2. 写一个 = 2 e 一个1其中一个 1 为奇数

  3. 如果 e 是偶数那么设置 s ← 1。否则设置 s ← 1 如果 n ≡ 1 7 ( mod 8),或者设置 s←1如果 n ≡ 3 5 ( mod 8)。

  4. 如果 n ≡ 3 ( mod 4) 和 a1≡3(mod4),则设置 s↓↓s。

  5. 集 n【1】【n 修改 a】

  6. 如果一个 1 = 1,则返回(s);else return(s雅可比(n1,a1)。

有限域

基础

定义 2.41 。让我们考虑 F 是一个包含有限个元素的有限域。F 的阶表示 F中的元素个数*。*

定义 2.42 。有限域的存在唯一性。

  1. 我们假设如果 F 是有限域,那么 F 将有 p m 个元素为素数 p 和整数 m ≥ 1。

  2. 对于每一个素数幂阶 p m ,我们都有一个唯一的有限阶域 p m 。该字段被标注为)或在其他一些参考文献中被标注为GF(pm)。

定义 2.43 。假设一个Fq表示一个有限阶域q=pmp 是素数,那么)的特征是 p 。不止如此,)还会有一个ℤ * p 的副本作为子字段,因为)可以看作是程度 m 的ℤ p * 的扩展字段。

多项式和欧几里德算法

以下两个算法对于理解我们如何获得两个多项式的 gcdg ( x )和 h ( x ),两者都在ℤp[x]是必要的:

算法 2.43。 欧几里得算法 为ℤpx1

)

)

****1. 而 h ( x ) ≠ 0,
1. 设置 r(x);g(x)修改 h ( x ),

** 返回 g ( x )。

 *

算法 2.44。扩展的欧几里德算法为ℤpx1*

)

)

****1. 如果 h ( x ) = 0,然后设置(x);g(x),【如果
1. 返回 ( d ( x ), s ( x ), t ( x ))。

  1. sets(x)←1,s);←0,

** 而 h ( x ) ≠ 0,
1. g(x);g(x)div h(x),

**   *s*(*x*);*s**(*x*)】q(*

    ***   *g*(*x*);*h*(*x*);*h*(*x*);

    **   *s*<sub>【2】(*x*)</sub>*<sub>*),*【s】**</sub>***** ***   set*d*(*x*);*g*(*x*);*s*(*x*);

 *   返回 *d* ( *x* ), *s* ( *x* ), *t* ( *x* )。

 ***

**## 案例研究 1:通过大量试验计算事件发生的概率

下面的应用处理计算某一事件在一定数量的试验中发生的概率。下面的应用解释了这样一个事实,即一个事件在每次试验中都有发生的概率,在经过 N 次试验后,我们得到一次试验导致该事件发生的概率为 1(1P)N。见图 2-1 和清单 2-1 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-1

计算概率申请表

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CompProb
{
    class Program
    {
        static void Main(string[] args)
        {
            string consoleInput;
            Console.Write("Enter the event probability: ");
            consoleInput = Console.ReadLine();
            ComputeTheProbability(consoleInput);
            Console.ReadKey();
        }

        private static void ComputeTheProbability(string eventProbability)

        {
            // See if the probability contains a % sign.
            bool percent = eventProbability.Contains("%");

            // Get the event probability.
            double event_prob =
                double.Parse(eventProbability.Replace("%", ""));

            // If we're using percents, divide by 100.
            if (percent) event_prob /= 100.0;

            // Get the probability of the event not happening.
            double non_prob = 1.0 - event_prob;

            for (int i = 0; i <= 100; i++)
            {
                double prob = 1.0 - Math.Pow(non_prob, i);

                if (percent)
                {
                    prob *= 100.0;
                    Console.WriteLine(i.ToString() + ": " + prob.ToString("0.0000") + "%");
                }
                else
                {
                    Console.WriteLine(i.ToString() + ": " + prob.ToString("0.0000"));
                }
            }
        }
    }
}

Listing 2-1Code for Computing the Probability of an Event

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-2

概率分布申请表

案例研究 2:计算概率分布

在这一节中,我们将展示如何计算概率分布。代码如清单 2-2 所示,结果如图 2-2 所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ProbDistribution
{
    class Program
    {
        static List<Distribution> values = new List<Distribution>();
        static void Main(string[] args)
        {
            Console.WriteLine("Press ESC key to exit the process...");

            string X, pOfX;
            Console.Write("X = ");
            X = Console.ReadLine();
            Console.Write("P(X) = ");
            pOfX = Console.ReadLine();
            AddValues(X, pOfX);

            while (Console.ReadKey().Key != ConsoleKey.Escape)
            {
                Console.Write("X = ");
                X = Console.ReadLine();
                Console.Write("P(X) = ");
                pOfX = Console.ReadLine();
                AddValues(X, pOfX);
            }
        }

        static void AddValues(string txtX, string txtPOfTheX)
        {
            int x = 0;
            double p = 0.00, sum = 0.00;
            Distribution dist = null;

            // Check that the user entered a value for x
            if (txtX.Length == 0)
            {
                Console.WriteLine("You must enter the x value.",
                                "Probability Distribution");
                Console.WriteLine();
                return;
            }

            // Test that the user entered a value for P(x)
            if (txtPOfTheX.Length == 0)
            {
                Console.WriteLine("You must enter the P(x) value.",
                                "Probability Distribution");
                Console.WriteLine();
                return;
            }

            // Get the value for x
            try
            {
                x = int.Parse(txtX);
            }
            catch (FormatException)
            {
                Console.WriteLine("The value you entered is invalid.",
                                "Probability Distribution");
                Console.WriteLine();
            }

            // Get the value for P(x)
            try
            {
                p = double.Parse(txtPOfTheX);
            }
            catch (FormatException)
            {
                Console.WriteLine("The value you entered is invalid.",
                                "Probability Distribution");
                Console.WriteLine();
            }

            // Create a Distribution value
            dist = new Distribution(x, p);
            // Add the value to the list
            values.Add(dist);

            ShowValues();

            // Calculate the sum of the P(x) values
            foreach (Distribution d in values)
                sum += d.PofX;
            Console.WriteLine("The sum is: " + sum.ToString("F"));

            // Test the first requirement
            if (sum != 1) // The first rule is not respected
            {
                Console.WriteLine("The first rule is not respected",
                                "Probability Distribution");
                Console.WriteLine("Press ENTER to continue or ESC to exit the process..." + "\n");
                return;
            }

            // Test the second requirement
            foreach (Distribution d in values)
            {
                if ((d.PofX < 0.00) || (d.PofX > 1)) // The second rule is not respected
                {
                    Console.WriteLine("The second rule is not respected",
                                    "Probability Distribution");
                    Console.WriteLine("Press ENTER to continue or ESC to exit the process..." + "\n");
                    return;
                }
            }
        }

        static void ShowValues()
        {
            double sum = 0.00;

            foreach (Distribution dist in values)
            {
                Console.WriteLine("X=" + dist.X.ToString() + "\t" + "P(X)=" + dist.PofX.ToString());
            }

            // Calculate the sum of the P(x) values
            foreach (Distribution d in values)
                sum += d.PofX;

            Console.WriteLine("No. of values: " + values.Count.ToString() + "\t" + "Sum of P(X): " + sum.ToString());
        }
    }

    public class Distribution
    {
        public int X { get; set; }
        public double PofX { get; set; }

        public Distribution(int x, double p)
        {
            X = x;
            PofX = p;
        }
    }
}

Listing 2-2Code for Probability Distribution

案例研究 3:计算概率分布的平均值

在这一节中,我们将展示如何计算概率分布的平均值。代码如清单 2-3 所示,结果如图 2-3 所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CalcMeanProbDistrib
{
    class Program
    {
        static List<Distribution> values = new List<Distribution>();
        static void Main(string[] args)
        {
            Console.WriteLine("Press ESC key to exit the process...");

            string X, pOfX;
            Console.Write("X = ");
            X = Console.ReadLine();
            Console.Write("P(X) = ");
            pOfX = Console.ReadLine();
            AddValues(X, pOfX);

            while (Console.ReadKey().Key != ConsoleKey.Escape)
            {
                Console.Write("X = ");
                X = Console.ReadLine();
                Console.Write("P(X) = ");
                pOfX = Console.ReadLine();
                AddValues(X, pOfX);
            }
        }

        static void ShowValues()
        {
            double sum = 0.00;
            double mean = 0.00;

            foreach (Distribution dist in values)
            {
                mean += dist.X * dist.PofX;
                Console.WriteLine(dist.X.ToString() + " * " +
                                      dist.PofX.ToString() + " = " +
                                      mean.ToString());
            }

            foreach (Distribution d in values)
                sum += d.PofX;

            Console.WriteLine("Number of values: " + values.Count.ToString() + "\t" + "Sum of P(X): " + sum.ToString() + "\t" + "Mean of probability distribution: " + mean.ToString());
        }

        static void AddValues(string txtValueOfX, string txtValueOfPX)
        {
            int x = 0;
            double p = 0.00, sum = 0.00;
            Distribution dist = null;

            // Check that the user entered a value for x
            if (txtValueOfX.Length == 0)
            {
                Console.WriteLine("You must enter the x value.",
                                "Probability Distribution");
                return;
            }

            // Test that the user entered a value for P(x)
            if (txtValueOfPX.Length == 0)
            {
                Console.WriteLine("You must enter the P(x) value.",
                                "Probability Distribution");
                return;
            }

            // Get the value for x
            try
            {
                x = int.Parse(txtValueOfX);
            }
            catch (FormatException)
            {
                Console.WriteLine("The value you entered is invalid.",
                                "Probability Distribution");
            }

            // Get the value for P(x)
            try
            {
                p = double.Parse(txtValueOfPX);
            }
            catch (FormatException)
            {
                Console.WriteLine("The value you entered is invalid.",
                                "Probability Distribution");
            }

            // Create a Distribution value
            dist = new Distribution(x, p);
            // Add the value to the list
            values.Add(dist);

            ShowValues();

            // Calculate the sum of the P(x) values
            foreach (Distribution d in values)
                sum += d.PofX;
            Console.WriteLine("Sum of P(X): " + sum.ToString("F"));

            // Test the first requirement
            if (sum != 1) // The first rule is not respected
            {
                Console.WriteLine("The first rule is not respected",
                                "Probability Distribution");
                Console.WriteLine("Press ENTER to continue or ESC to exit the process..." + "\n");
                return;
            }

            // Test the second requirement
            foreach (Distribution d in values)
            {
                if ((d.PofX < 0.00) || (d.PofX > 1)) // The second rule is not respected
                {
                    Console.WriteLine("The second rule is not respected",
                                    "Probability Distribution");
                    Console.WriteLine("Press ENTER to continue or ESC to exit the process..." + "\n");
                    return;
                }
            }
        }
    }

    public class Distribution
    {
        public int X { get; set; }
        public double PofX { get; set; }

        public Distribution(int x, double p)
        {
            X = x;
            PofX = p;
        }
    }
}

Listing 2-3Code for Computing the Mean of the Probability

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-3

计算概率分布的平均值

案例研究 4:计算方差

在这一节中,我们将展示如何计算方差。代码如清单 2-4 所示,结果如图 2-4 所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleComputingVariance
{
    class Program
    {
        static void Main(string[] args)
        {
            List<double> dataValues =
                new List<double> { 1, 2, 3, 4, 5, 6 };

            double variance =
                dataValues.ComputingVariance();

            Console.WriteLine("Variance is = {0}",
                    variance);

            Console.ReadKey();
        }
    }

    public static class MyListExtensions
    {
        public static double ComputingMean(this List<double> values)
        {
            return values.Count == 0 ? 0 : values.ComputingMean(0, values.Count);
        }

        public static double ComputingMean(this List<double> values, int start, int end)
        {
            double s = 0;

            for (int i = start; i < end; i++)
            {
                s += values[i];
            }

            return s / (end - start);
        }

        public static double ComputingVariance(this List<double> values)
        {
            return values.ComputingVariance(values.ComputingMean(), 0, values.Count);
        }

        public static double ComputingVariance(this List<double> values, double mean)
        {
            return values.ComputingVariance(mean, 0, values.Count);
        }

        public static double ComputingVariance(this List<double> values, double mean, int start, int end)
        {
            double variance = 0;

            for (int i = start; i < end; i++)
            {
                variance += Math.Pow((values[i] - mean), 2);
            }

            int n = end - start;
            if (start > 0) n -= 1;

            return variance / (n);
        }

    }
}

Listing 2-4Code for Computing the Variance

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-4

计算方差

案例研究 5:计算标准偏差

在本节中,我们将展示如何计算标准偏差。代码如清单 2-5 所示,结果如图 2-5 所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleComputingVariance
{

    class Program
    {
        static void Main(string[] args)
        {
            List<double> dataValues =
                new List<double> { 1, 2, 3, 4, 5, 6 };

            double mean = dataValues.ComputingMean();
            double variance = dataValues.ComputingVariance();
            double standard_deviation =
                dataValues.ComputingStandardDeviation();

            Console.WriteLine("Mean is = {0}," +
                "Variance is = {1}, " +
                "Standard Deviation is = {2}",
                    mean,
                    variance,
                    standard_deviation);

            Console.ReadKey();
        }
    }

    public static class MyListExtensions
    {
        public static double ComputingMean(this List<double> values)
        {
            return values.Count == 0 ? 0 : values.ComputingMean(0, values.Count);
        }

        public static double ComputingMean(this List<double> values, int start, int end)
        {
            double s = 0;

            for (int i = start; i < end; i++)
            {
                s += values[i];
            }

            return s / (end - start);
        }

        public static double ComputingVariance(this List<double> values)
        {
            return values.ComputingVariance(values.ComputingMean(), 0, values.Count);
        }

        public static double ComputingVariance(this List<double> values, double mean)
        {
            return values.ComputingVariance(mean, 0, values.Count);
        }

        public static double ComputingVariance(this List<double> values, double mean, int start, int end)
        {
            double variance = 0;

            for (int i = start; i < end; i++)
            {
                variance += Math.Pow((values[i] - mean), 2);
            }

            int n = end - start;
            if (start > 0) n -= 1;

            return variance / (n);
        }

        public static double ComputingStandardDeviation(this List<double> values)
        {
            return values.Count == 0 ? 0 : values.ComputingStandardDeviation(0, values.Count);
        }

        public static double ComputingStandardDeviation(this List<double> values, int start, int end)
        {
            double mean = values.ComputingMean(start, end);
            double variance = values.ComputingVariance(mean, start, end);

            return Math.Sqrt(variance);
        }
    }
}

Listing 2-5Code for Computing Standard Deviation

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-5

计算标准偏差

案例研究 6:生日悖论

在这一节中,我们将展示如何对给定数量的人应用生日悖论。代码如清单 2-6 所示,结果如图 2-6 所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleBirthdayParadox
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Enter the number of people: ");
            int people_number = Convert.ToInt32(Console.ReadLine());

            const double number_of_trials = 100000.0;

            int number_of_birthday_matches = 0;
            double sum_of_unique_birthday = 0;

            List<int> personBirthdays;
            Random rnd = new Random();

            for (int trial_number = 1; trial_number <= number_of_trials; trial_number++)
            {
                //** we will generate birthdays
                personBirthdays = new List<int>(people_number);
                for (int personNum = 1; personNum <= people_number; personNum++)
                    personBirthdays.Add(rnd.Next(1, 366));

                if (personBirthdays.Count != personBirthdays.Distinct().Count())
                    number_of_birthday_matches++;

                sum_of_unique_birthday += personBirthdays.Distinct().Count();
            }

            double percentage_matched =
                number_of_birthday_matches / number_of_trials * 100.0;

            double uniqueness_per_trial = sum_of_unique_birthday / number_of_trials;

            Console.WriteLine("There are {0} people. " +
                "There is at least one matching birthday {1}% " +
                "of the time. Average number of " +
                "unique birthdays is {2}.",
                    people_number.ToString(),
                    percentage_matched.ToString(),
                    uniqueness_per_trial.ToString());

            Console.ReadKey();
        }
    }
}

Listing 2-6Code for Birthday Paradox Application

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-6

生日悖论应用

案例研究 7:(扩展的)欧几里德算法

欧几里德算法有两个版本,普通版(见图 2-7 和清单 2-7 )和扩展版(见图 2-8 和清单 2-8 ) *。*两个版本的欧几里德算法的区别在于,常规版本基于连续的减法运算来计算两个数字之间的 GCD,而扩展版本应用于多项式(参见算法 2.43 和算法 2.44)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-7

欧几里德算法应用表格

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleExtendedEuclidean
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("a = ");
            int a = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("b = ");
            int b = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("X = ");
            int x = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("Y = ");
            int y = Convert.ToInt32(Console.ReadLine());

            int g;

            g = gcdExtended(a, b, x, y);

            Console.WriteLine("Extended Euclidean Algorithm = {0}.", g.ToString());
            Console.ReadKey();
        }

        public static int gcdExtended(int a, int b, int x, int y)
        {
            // Base Case
            if (a == 0)
            {
                x = 0;
                y = 1;
                return b;
            }

            // To store results of
            // recursive call
            int x1 = 1, y1 = 1;
            int gcd = gcdExtended(b % a, a, x1, y1);

            // Update x and y using
            // results of recursive call
            x = y1 - (b / a) * x1;
            y = x1;

            return gcd;
        }
    }
}

Listing 2-8Code for Extended Euclidean Algorithm

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleEuclideanAlgorithm
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("a = ");
            int a = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("b = ");
            int b = Convert.ToInt32(Console.ReadLine());

            int g = gcd(a, b);

            Console.WriteLine("GCD = {0}", g.ToString());
            Console.ReadKey();
        }

        public static int gcd(int x, int y)
        {
            if (x == 0)
                return y;
            return gcd(y % x, x);
        }
    }
}

Listing 2-7Code for Computing the Euclidean Algorithm

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-8

扩展欧几里德算法应用表格

案例研究 8:计算模 m 下的乘法逆运算

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-9

模乘逆应用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleMultiInverse
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("number = ");
            int n = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("modulo = ");
            int m = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("Multiplicative Inverse of n={0} " +
                "under modulo m={1} is {2}",
                n, m, Convert.ToString(modInverse(n, m)));

            Console.ReadKey();
        }
        static int modInverse(int a, int m)
        {
            a = a % m;
            for (int x = 1; x < m; x++)
                if ((a * x) % m == 1)
                    return x;
            return 1;
        }
    }
}

Listing 2-9Code for Computing the Modular Multiplicative Inverse

案例研究 9:中国剩余定理

在这一节中,我们将展示如何应用中国剩余定理。代码如清单 2-10 所示,结果如图 2-10 所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleChineseRemainder
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] num = { 3, 4, 5 };
            int[] rem = { 2, 3, 1 };
            int k = num.Length;

            Console.WriteLine("numbers = {0}",
                "{ " + num[0].ToString() + ", " +
                       num[1].ToString() + ", " +
                       num[2].ToString() + " }");

            Console.WriteLine("remainders = {0}",
                 "{ " + rem[0].ToString() + ", " +
                        rem[1].ToString() + ", " +
                        rem[2].ToString() + " }");

            Console.WriteLine("Applying Chinese Remainder " +
                "Theorem is = {0}",
                findMinX(num, rem, k).ToString());

            Console.ReadKey();
        }

        static int inv(int a, int m)
        {
            int m0 = m, t, q;
            int x0 = 0, x1 = 1;

            if (m == 1)
                return 0;

            // Apply extended
            // Euclid Algorithm
            while (a > 1)
            {
                // q is quotient
                q = a / m;

                t = m;

                // m is remainder now,
                // process same as
                // euclid's algo
                m = a % m; a = t;

                t = x0;

                x0 = x1 - q * x0;

                x1 = t;
            }

            // Make x1 positive
            if (x1 < 0)
                x1 += m0;

            return x1;
        }

        static int findMinX(int[] num, int[] rem, int k)
        {
            // Compute product of all numbers
            int prod = 1;
            for (int i = 0; i < k; i++)
                prod *= num[i];

            // Initialize result
            int result = 0;

            // Apply above formula
            for (int i = 0; i < k; i++)
            {
                int pp = prod / num[i];
                result += rem[i] *
                          inv(pp, num[i]) * pp;
            }

            return result % prod;
        }
    }
}

Listing 2-10Code for the Chinese Remainder Theorem

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-10

中国剩余定理的应用

案例研究 10:勒让德符号

在本节中,我们将展示如何计算勒让德符号。代码如清单 2-11 所示,结果如图 2-11 所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleLegendreSymbol
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("a = ");
            int a = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("b = ");
            int b = Convert.ToInt32(Console.ReadLine());

            int result = Legendre(a, b);

            Console.WriteLine("Legendre Symbol is = {0}", result.ToString());
            Console.ReadKey();
        }

        public static int Legendre(int a, int p)
        {
            if (p < 2)  // prime test is expensive.
                throw new ArgumentOutOfRangeException("p", "p must not be < 2");
            if (a == 0)
            {
                return 0;
            }
            if (a == 1)
            {
                return 1;
            }
            int result;
            if (a % 2 == 0)
            {
                result = Legendre(a / 2, p);
                if (((p * p - 1) & 8) != 0) // instead of dividing by 8, shift the mask bit
                {
                    result = -result;
                }
            }
            else

            {
                result = Legendre(p % a, a);
                if (((a - 1) * (p - 1) & 4) != 0) // instead of dividing by 4, shift the mask bit
                {
                    result = -result;
                }
            }
            return result;
        }
    }
}

Listing 2-11Code for Computing the Legendre Symbol

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-11

勒让德符号的计算

结论

在本章中,我们讨论了在大多数现代加密算法中使用的数学工具的重要性,以及如何实现它们。我们讨论了数学基础的四个重要方面,它们在加密算法的实现过程中会有所帮助:概率论、信息论、数论和有限域。

对于每个数学基础,我们提出了必要的方程和数学表达式,用于算法的实施。每个方程或数学表达式都通过用 C# 实现的代码进行了演示,标题为案例研究。每个案例研究都展示和演示了读者开发安全可靠的代码所需的技能和知识。案例研究从 1 数到 10。在本章结束时,读者应该有大量的知识,包括理论和实践,并且应该学会如何在很短的时间内从理论到实践。

文献学

  1. 阿尔弗雷德·j·梅内塞斯,保罗·范·奥尔肖特,斯科特·a·范斯通。应用密码学手册华润出版社。1996. ISBN 0-8493-8523-7。

  2. R.密码学家代数,第一版。纽约州纽约市:斯普林格,2016 年。

  3. J.霍夫斯坦、j .皮弗和 J. H .西尔弗曼,《数学密码学导论》,第二版。纽约:施普林格,2014 年。

  4. 南《密码学》,第一版。纽约州纽约市:施普林格,2018 年。

  5. W.Stallings ,密码学与网络安全:原理与实践,第 6 版。美国:普伦蒂斯霍尔出版社,2013 年。

  6. K.学院,密码学:数据与应用安全。独立出版,2017。

  7. C.T. Rivers,密码学:解码密码学!从远古时代到新时代时代。JR Kindle 出版社,2014。

  8. D.Stinson,密码学:理论与实践,第二版。CRC/C & H,2002 年。

  9. H.Delfs 和 H. Knebl,密码学导论:原理与应用,第三版。纽约州纽约市:斯普林格,2015 年。

  10. J.Katz 和 Y. Lindell,现代密码学导论,第二版。博卡拉顿:查普曼和霍尔/儿童权利委员会,2014 年。

  11. X.王,徐国光,王明军,孟,公钥密码学的数学基础,第一版。博卡拉顿:CRC 出版社,2015 年。

  12. T.现代密码学和椭圆曲线。罗德岛普罗维登斯:美国数学学会,2017 年。

  13. 南闫玉英,公钥密码学中的素性测试与整数因式分解第一版。斯普林格,2013 年。

  14. 长度 M. Batten,公钥加密:应用和攻击。新泽西州霍博肯:威利-布莱克威尔,2013 年。

  15. J.——p·奥马森,严肃密码学。旧金山:没有淀粉出版社,2017。

  16. 南密码世界:包括密码系统、密码、公钥加密、数据集成、消息认证、数字签名。

  17. 阿德里安·阿塔纳修。Informației 安全(信息安全)-密码加密(密码学)-第 1 卷,信息数据出版社,2007 年。ISBN: 978-973-1803-29-6,978-973-1803-16-6。[罗马尼亚语]

  18. 阿德里安·阿塔纳修。Informației(信息安全)-安全协议(安全协议)-第 2 卷,信息数据出版社,2007 年。ISBN: 978-973-1803-29-6,978-973-1803-18-0。[罗马尼亚语]

  19. 瓦西里·普雷达,埃米尔·西米恩,阿德里安·波佩斯库。 密码分析。数学结果和技术(密码分析。结果与数学技术),布加勒斯特大学出版社,2004 年。国际标准书号:973-575-975-6。[英语]***********************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值