C--密码学高级教程-全-

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 中描述的许多密码原语,并附有实际实现。这些图元应通过一个评估过程,评估标准如下:

img/493660_1_En_1_Fig1_HTML.png

图 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}。

img/493660_1_En_1_Fig2_HTML.jpg

图 1-2

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

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

$$ f(1)=1\kern2.75em f(6)=3 $$

$$ f(2)=3\kern2.75em f(7)=5 $$

$$ f(3)=9\kern2.75em f(8)=9 $$

$$ f(4)=5\kern2.75em f(9)=4 $$

$$ f(5)=3\kern2.25em f(10)=1 $$

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

img/493660_1_En_1_Fig3_HTML.jpg

图 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(1)=2,\kern0.5em p(2)=5,\kern0.5em p(3)=4,\kern0.5em p(4)=2,\kern0.5em p(5)=1 $$

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

$$ p=\left(\begin{array}{ccc}1& 2& 3\kern1.25em 4\kern1.5em 5\ {}3& 5& 4\kern1.25em 2\kern1.5em 1\end{array}\right), $$

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

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

$$ {p}^{-1}=\left(\begin{array}{ccc}1& 2& 3\kern1.25em 4\kern1.5em 5\ {}5& 4& 1\kern1.25em 3\kern1.5em 2\end{array}\right) $$

例 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 的图像。

img/493660_1_En_1_Fig4_HTML.jpg

图 1-4

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

概念和基本术语

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

用于加密的域和共域

  • $$ \mathcal{A} $$被表示为一个有限集合,称为定义的字母表。让我们考虑一个例子,$$ \mathcal{A}=\left{0,1\right} $$,这是二进制字母表,这是一个经常使用的定义。

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

  • $$ \mathcal{C} $$代表称为密文空间的集合。$$ \mathcal{C} $$包含来自字母表$$ \mathcal{A} $$的符号串,该字母表不同于为$$ \mathcal{M} $$定义的字母表。来自$$ \mathcal{C} $$的一个元素叫做密文

加密和解密转换

  • $$ \mathcal{K} $$是一组被称为的密钥空间$$ \mathcal{K} $$中的一个元素被称为键。

  • $$ \mathcal{K} $$ $$ e\in \mathcal{K} $$的每个元素定义了从$$ \mathcal{M} $$$$ \mathcal{C} $$的唯一双射,记为 E * e E e 被称为加密函数加密变换。请记住, E e * 必须是双射的,如果过程相反,并且为每个不同的密文恢复唯一的明文消息。

  • 对于每一个$$ d\in \mathcal{K} $$,我们都有 D * d ,是从$$ \mathcal{C} $$$$ \mathcal{M} $$的双射(例如$$ {D}_d:\mathcal{C}\to \mathcal{M} $$)。 D d 被称为解密函数解密变换。*

  • 当我们将变换EE应用于消息$ m\in \mathcal{M} $时,通常称为加密 m 或 m 的加密

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

  • 加密方案基于代表加密变换的集合$$ \left{{E}_e:e\in \mathcal{K}\right} $$和相应的集合$$ \left{{D}_d:d\in \mathcal{K}\right} $$,它们是解密变换,具有对于每个$$ e\in \mathcal{K} $$都有唯一密钥$$ d\in \mathcal{K} $$的属性,使得$$ {D}_d={E}_e^{-1} $$;所以DD(EE(m)=m为所有$$ m\in \mathcal{M} $$。加密方案也被称为密码。

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

  • 为了构造一个加密方案,我们必须选择一个消息空间$$ \mathcal{M} $$,一个密文$$ \mathcal{C} $$,一个密钥空间$$ \mathcal{K} $$,一组加密变换$$ \left{{E}_e:e\in \mathcal{K}\right} $$,以及一组相应的解密变换$$ \left{{D}_d:d\in \mathcal{K}\right} $$

沟通过程中的参与者

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

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

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

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

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

img/493660_1_En_1_Fig5_HTML.jpg

图 1-5

使用加密的双方通信过程

数字签名

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

大致描述如下:

  • $$ \mathcal{M} $$表示有可能被签名的消息集合。

  • $$ \mathcal{S} $$代表称为签名的元素集合。签名可以是固定长度的二进制字符串。

  • $$ {\mathcal{S}}_A $$被定义为从消息集$$ \mathcal{M} $$到签名集$$ \mathcal{S} $$的转换,称为实体 A (Alice)的签名转换$$ {\mathcal{S}}_A $$A 存储为秘密,并用于为来自$$ \mathcal{M} $$的消息创建签名。

  • VT3A表示从集合$$ \mathcal{M}\times \mathcal{S} $$到集合{ }的变换。$$ \mathcal{M}\times \mathcal{S} $$由所有对( ms )组成,其中$$ m\in \mathcal{M} $$$$ s\in \mathcal{S} $$,称为$$ \mathcal{M} $$$$ \mathcal{S} $$的笛卡尔积。 V

签名程序

我们将使用一个实体 A ,我们将其命名为签名者。我们将通过应用以下消息为特定消息$$ m\in \mathcal{M} $$创建一个签名:

  • 算出S=SA(m)。

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

验证程序

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

  • 获得对VT3AA 的验证功能。

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

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

公钥密码学

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

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

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

img/493660_1_En_1_Fig6_HTML.jpg

图 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 的位置发现。

img/493660_1_En_1_Fig7_HTML.jpg

图 1-7

如何使用公钥加密

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

哈希函数

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

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

img/493660_1_En_1_Fig8_HTML.jpg

图 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 ]。

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

$$ {E}_n(x)=\left(x+n\right)\ \mathit{\operatorname{mod}}\ 26 $$

解密以类似的方式完成:

$$ {D}_n(x)=\left(x-n\right)\ \mathit{\operatorname{mod}}\ 26 $$

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

img/493660_1_En_1_Fig9_HTML.jpg

图 1-9

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

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

img/493660_1_En_1_Fig10_HTML.jpg

图 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 )代表了一种经典的加密字母文本的方法,它使用一系列不同的基于关键字字母的凯撒密码。一些文献显示它是一种多字母替换的形式。

img/493660_1_En_1_Fig11_HTML.jpg

图 1-11

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

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

$$ {C}_i={E}_K\left({M}_i\right)=\left({M}_i+{K}_i\right)\ \mathit{\operatorname{mod}}\ 26 $$

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

$$ {M}_i={D}_K\left({C}_i\right)=\left({C}_i-{K}_i\right)\ \mathit{\operatorname{mod}}\ 26 $$

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

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

img/493660_1_En_1_Fig12_HTML.jpg

图 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

*

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

概率论

基础

本章将讨论实验、概率分布、事件、互补事件、互斥的概念。这些概念将帮助读者理解密码和密码分析机制的基本概念,以及它们是如何根据概率设计的 [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 的简单事件集合,记为$$ \overline{E} $$

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

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

  • $$ P\left(\overline{E}\right)=1-P(E) $$

  • 如果在 S 的结果也是一样的可能性,我们可以考虑$$ P(E)=\frac{\left|E\right|}{\left|S\right|} $$

定义 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(E1E2=P(E*1**

*### 条件概率

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

$$ P\left({E}_1|{E}_2\right)=\frac{P\left({E}_1\bigcap {E}_2\right)}{P\left({E}_2\right)} $$

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\left({E}_1|{E}_2\right)=\frac{P\left({E}_1\right)P\left({E}_2|{E}_1\right)}{P\left({E}_2\right)} $$

随机变量

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

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

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

$$ E(X)=\sum \limits_{s_i\in S}X\left({s}_i\right)P\left({s}_i\right). $$

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

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

$$ E(X)=\sum \limits_{x\in \mathbb{R}}x\cdotp P\left(X=x\right) $$

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

$$ E\left({\sum}_{i=1}m{a}_i{X}_i\right)={\sum}_{i=1}m{a}_iE\left({X}_i\right) $$

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

$$ Var(X)=E\left({\left(X-\mu \right)}²\right) $$

定义的非负数表示

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

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

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

生日问题

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

$$ {m}^{(n)}=m\left(m-1\right)\left(m-2\right)\dots \left(m-n+1\right) $$

定义 2.15.2 。我们来考虑两个非负整数 abab 。第二类的斯特林数,记为$$ \left{\begin{array}{c}a\ {}b\end{array}\right} $$,为

$$ \left{\begin{array}{c}a\ {}b\end{array}\right}=\frac{1}{b!}\sum \limits_{i=0}n{\left(-1\right)}{b-i}\left(\begin{array}{c}b\ {}i\end{array}\right){i}^a $$

$$ \left{\begin{array}{c}0\ {}0\end{array}\right}=1 $$的情况被认为是例外。

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

$$ {P}_1\left(a,b,l\right)=\left{\begin{array}{c}b\ {}l\end{array}\right}\frac{a{(l)}}{ab},1\le l\le b $$

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

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

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

$$ {P}_2\left(a,h\right)=1-{P}_1\left(a,h,h\right)=1-\frac{a{(h)}}{ah},1\le h\le m $$

案例 2.17.2 。让我们考虑一下 h 从骨灰盒中取出的球的具体数量。如果$$ h=O\left(\sqrt{a}\right) $$一个 → ∞,那么

$$ {P}_2\left(a,h\right)\to 1-\exp \left(-\frac{h\left(h-1\right)}{2a}+O\left(\frac{1}{\sqrt{a}}\right)\right)\approx 1-\mathit{\exp}\left(-\frac{h²}{2a}\right). $$

提供的演示将解释为什么概率分布被称为生日惊喜生日悖论 一个 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 ,其中

$$ {\sum}_{i=1}^n{p}_i=1. $$

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

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

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

$$ H(X)=-{\sum}_{i=1}^n{p}_i\lg {p}_i={\sum}_{i=1}^n{p}_i\mathit{\lg}\left(\frac{1}{p_i}\right) $$

根据惯例,

$$ {p}_i\cdotp \mathit{\lg}{p}_i={p}_i\cdotp \mathit{\lg}\left(\frac{1}{p_i}\right)=0 $$如果 p * i * = 0。

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

$$ H\left(X,Y\right)=\sum \limits_{x,y}P\left(X=x,Y=y\right)\lg \left(P\left(X=x,Y=y\right)\right), $$

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

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

$$ H\left(X|Y=y\right)=-\sum \limits_xP\left(X=x|Y=y\right)\lg \left(P\left(X=x|Y=y\right)\right), $$

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

$$ H\left(X|Y\right)=\sum \limits_yP\left(Y=y\right)H\left(X|Y=y\right), $$

其中索引 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】两个整数

$$ INPUT:\kern2em a\  and\ b, two\  non- negative\ integers\ with\ respect\ for\ a\ge b $$

$$ OUTPUT:\kern1em The\ \mathit{\gcd} $$

$$ 1. While\ b\ne 0\  then $$

$$ 1.1. Set\ r\leftarrow a\ \mathit{\operatorname{mod}}\ b,\kern0.5em a\leftarrow b,b\leftarrow r $$

$$ 2. Return\ (a) $$

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

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

$$ INPUT:\kern1em a\  and\ b, two\  non- negative\ integers\ with\ respect\ for\ a\ge b $$

$$ OUTPUT:\kern0.5em d=\gcd \left(a,b\right) and\ integers\ x,y\  which\ satisfy\  ax+ by=d $$

  1. If b = 0, then set

    $$ d\leftarrow a $$

    $$ x\leftarrow 1 $$

    $$ y\leftarrow 0 $$

    $$ return\left(d,x,y\right) $$

  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 ,一个两两相对的素数。于是我们有了下面的同余系统

$$ y\equiv {a}_1\kern0.75em \left(\mathit{\operatorname{mod}}\ {n}_1\right) $$

$$ y\equiv {a}_2\kern0.75em \left(\mathit{\operatorname{mod}}\ {n}_2\right) $$

$$ \vdots $$

$$ x\equiv {a}_{k\kern1em }\left(\mathit{\operatorname{mod}}\ {n}_k\right) $$

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

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

定义 2.33。 高斯算法 在中国剩余定理中,同余式的解法 x 可以计算为$$ x={\sum}_{i=1}^k{a}_i\cdotp {N}_i\cdotp {M}_i\ \mathit{\operatorname{mod}}\ n $$其中NI=N/NI$$ {M}_i={N}_i^{-1}\ \mathit{\operatorname{mod}}\ {n}_i $$。所列操作可以在O((lgn)2)位操作中完成。

算法ℤ n

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

$$ \left(a+b\right)\ \mathit{\operatorname{mod}}\kern0.5em n=\left{\begin{array}{c}a+b,\kern3.5em if\ a+b<n,\ {}a+b-n,\kern1.5em if\ a+b\ge n\kern0.5em \end{array}\right. $$

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

$$ INPUT:\kern2.25em a\in {\mathbb{Z}}_n $$

$$ OUTPUT:\kern1.25em {a}^{-1}\kern0.5em \mathit{\operatorname{mod}}\kern0.5em n $$

$$ 1. Apply\ an\ extended\ Euclidean\ algorithm\ in\ order\ to\ find\ integers\ x\  and\ y\  such\ that\  ax+ ny=d, where\ d=\gcd \left(a,n\right) $$

$$ 2. If\ d>1, we\ will\ have\ {a}^{-1}\ \mathit{\operatorname{mod}}\ n\  that\ does\ not\ exist. Else, return\ (x). $$

算法 2.351。ℤn

$$ INPUT:\kern2.5em a\in {\mathbb{Z}}_n, and\ integer\ 0\le k<n\  whose\ binary\ representation\ is\ k={\sum}_{i=0}t{k}_i{2}i. $$

$$ OUTPUT:\kern1.75em {a}^k\kern0.5em \mathit{\operatorname{mod}}\kern0.5em 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 看作一个奇数质数,把看作一个整数。勒让德符号记为$$ \left(\frac{a}{p}\right) $$,定义如下:

$$ \left(\frac{a}{p}\right)=\left{\begin{array}{c}0,\kern6em if\ p\mid a\ {}1,\kern4.5em if\ a\in {Q}_p\ {}-1,\kern3.75em if\ a\in \overline{Q_p}\end{array}\right.. $$

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

**1. $$ \left(\frac{a}{p}\right)\equiv {a}^{\frac{p-1}{2}}\left(\mathit{\operatorname{mod}}\ p\right). $$在这种特殊情况下,$$ \left(\frac{1}{p}\right)=1 $$$$ \left(\frac{-1}{p}\right)={\left(-1\right)}^{\frac{p-1}{2}}. $$自 1∈Qpifp≡1(mod4)和$$ -1\in \overline{Q_p} $$ifp≡3(mod4)。

  1. $$ \left(\frac{ab}{p}\right)=\left(\frac{a}{p}\right)\left(\frac{b}{p}\right). $$既然如果$$ a\in {\mathbb{Z}}_p^{\ast } $$,那么$$ \left(\frac{a²}{p}\right)=1. $$

  2. 如果ab(mod p),那么$$ \left(\frac{a}{p}\right)=\left(\frac{b}{p}\right). $$

  3. $$ \left(\frac{2}{p}\right)={\left(-1\right)}^{\frac{\left({p}²-1\right)}{8}}. $$$$ \left(\frac{2}{p}\right)=1 $$ if * p * ≡ 1 或 7 ( * mod * 8),if * p * ≡ 3 或 5 ( * mod * 8)。

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

    $$ \left(\frac{p}{q}\right)=\left(\frac{q}{p}\right){\left(-1\right)}^{\frac{\left(p-1\right)\left(q-1\right)}{4}}. $$

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

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

$$ n={p}_1{e_1}{p}_2{e_2}\dots {p}_k^{e_k}. $$

**雅可比符号$$ \left(\frac{a}{n}\right) $$定义如下:

$$ \left(\frac{a}{n}\right)={\left(\frac{a}{p_1}\right)}{e_1}{\left(\frac{a}{p_2}\right)}{e_2}\dots {\left(\frac{a}{p_k}\right)}^{e_k}. $$

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

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

**1. $$ \left(\frac{a}{n}\right)=0,1, or-1 $$。不止于此,$$ \left(\frac{a}{n}\right)=0 $$当且仅当 gcd( an ) ≠ 1。

  1. $$ \left(\frac{ab}{n}\right)=\left(\frac{a}{n}\right)\left(\frac{b}{n}\right) $$。因此如果$$ a\in {\mathbb{Z}}_n^{\ast }, $$那么$$ \left(\frac{a²}{n}\right)=1 $$

  2. $$ \left(\frac{a}{nm}\right)=\left(\frac{a}{n}\right)\left(\frac{a}{m}\right). $$

  3. 如果ab(mod n),那么$$ \left(\frac{a}{n}\right)=\left(\frac{b}{n}\right). $$

  4. $$ \left(\frac{1}{n}\right)=1. $$

  5. $$ \left(\frac{-1}{n}\right)={\left(-1\right)}^{\frac{\left(n-1\right)}{2}}. $$因此$$ \left(\frac{-1}{n}\right)=1 $$ if * n * ≡ 1 ( * mod * 4),if * n * ≡ (3 * mod * 4)。

  6. $$ \left(\frac{2}{n}\right)={\left(-1\right)}^{\frac{n²-1}{8}}. $$因此$$ \left(\frac{2}{n}\right)=1 $$ if * n * ≡ 1 或 7 ( * mod * 8),if * n * ≡ 3 或 5 ( * mod * 8)。

  7. $$ \left(\frac{m}{n}\right)=\left(\frac{n}{m}\right){\left(-1\right)}^{\frac{\left(m-1\right)\left(n-1\right)}{4}}. $$换句话说,$$ \left(\frac{m}{n}\right)=\left(\frac{n}{m}\right) $$除非 mn 都同余于 3 模 4,在这种情况下$$ \left(\frac{m}{n}\right)=-\left(\frac{n}{m}\right) $$

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

$$ JACOBI\left(a,n\right) $$

$$ INPUT:\kern2em An\  odd\  integer\ n\ge 3\  and\ an\ integer\ a,0\le a<n $$

$$ OUTPUT:\kern1em The\ Jacobi\ symbol\ \left(\frac{a}{n}\right) $$

**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 。该字段被标注为$$ {\mathbbm{F}}_{p_m} $$或在其他一些参考文献中被标注为GF(pm)。

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

多项式和欧几里德算法

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

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

$$ INPUT:\kern2.75em Two\  polynomials\ g(x),h(x)\in {\mathbb{Z}}_p\left[x\right] $$

$$ OUTPUT:\kern1.5em \gcd of\ g(x)\  and\ h(x) $$

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

** 返回 g ( x )。

 *

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

$$ INPUT:\kern2.25em Two\  polynomials\ g(x),h(x)\in {\mathbb{Z}}_p\left[x\right] $$

$$ OUTPUT:\kern1.25em d(x)=\gcd \left(g(x),h(x)\right)\  and\ polynomials\ s(x),t(x)\in {\mathbb{Z}}_p\left[x\right] which\ will\ satisfy\ s(x)g(x)+t(x)h(x)=d(x). $$

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 。

img/493660_1_En_2_Fig1_HTML.jpg

图 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

img/493660_1_En_2_Fig2_HTML.jpg

图 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

img/493660_1_En_2_Fig3_HTML.jpg

图 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

img/493660_1_En_2_Fig4_HTML.jpg

图 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

img/493660_1_En_2_Fig5_HTML.jpg

图 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

img/493660_1_En_2_Fig6_HTML.jpg

图 2-6

生日悖论应用

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

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

img/493660_1_En_2_Fig7_HTML.jpg

图 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

img/493660_1_En_2_Fig8_HTML.jpg

图 2-8

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

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

img/493660_1_En_2_Fig9_HTML.jpg

图 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

img/493660_1_En_2_Fig10_HTML.jpg

图 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

img/493660_1_En_2_Fig11_HTML.jpg

图 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。[英语]***********************

三、大整数运算

在复杂密码算法的复杂实现中,基于大整数的运算是难以实现的任务。大多数限制是由硬件设备(例如处理器、RAM 存储器)或编程语言造成的。

在 C# 中,整数值表示为 32 位。在这 32 位中,只有 31 位用于表示正整数运算。在密码学中,建议我们处理 20 亿的数字,2 ^ 109

大多数编译器,比如 GCC,都提供了long long type。这提供了拥有大约 9 万亿分之一,9 ^ 1018的整数的能力。

以下示例显示了 RSA (Rivest-Shamir-Adleman)公钥加密密码系统,它需要大约 300 位数字。当处理真实事件及其概率时,在大多数情况下,计算将涉及非常大的数字。对于 C# 来说,获得主要结果可能是准确的,但是与其他困难的计算相比,我们将拥有非常大的数量。

考虑下面这个众所周知的例子:计算只有一张彩票的彩票中头奖的几率。组合数是 50 取 6 一次,“50 选 6”是$$ \frac{50!}{\left(\left(50-6\right)!\cdotp 6!\right)} $$。计算过程得到的数字是 15.890.700,所以在这种情况下,获胜的几率是 1/15,890,700。如果我们用 C# 编程语言来实现,15890700 这个数字可以非常容易地表示出来。这可能会很棘手,然后我们可能会在实施 50 的过程中陷入幼稚!(使用 Windows 中的计算器计算),即 3.041409320171e+64 或

30,414,093,201,713,378,043,612,608,166,064,768,844,377,641,568,960,512,000,000,000,000

用 C # 来表示这个数字几乎是不可能的,即使我们使用的是 64 位平台。

在接下来的几节中,我们将讨论一些可用于大整数算术运算的算法。请注意,由于我们使用的是加密算法和认证协议,这可能很难实现,因为我们处理的是大整数。下面我们将展示如何处理大数的一步一步的方法。

图 3-1 和清单 3-1 显示了大整数功能的完整实现。

img/493660_1_En_3_Fig1_HTML.jpg

图 3-1

计算 GCD 的 BigInteger 示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;

namespace BigIntExample
{
    class Program
    {
        static void Main(string[] args)
        {
            //** compute bigger factorials
            Console.Write("Enter a factorial to be computed (>100) :  ");
            int factorial = Convert.ToInt32(Console.ReadLine());
            ComputeBigFactorial(factorial);

            Console.WriteLine(Environment.NewLine);

            //** compute sum of the first
            //** even number to get Fibonacci Series
            Console.WriteLine("Enter a number for the " +
                "first even set (>= 100 000) : ");
            int evenNumberFib = Convert.ToInt32(Console.ReadLine());
            SumOfFirstOneLacEvenFibonacciSeries(evenNumberFib);
            Console.WriteLine(Environment.NewLine);

            //** computing greatest common divisor
            Console.WriteLine("GCD = " +
                    BigInteger.GreatestCommonDivisor(12, 24));

            //** comparing purpose

            BigInteger value1 = 10;
            BigInteger value2 = 100;

            switch (BigInteger.Compare(value1, value2))
            {
                case -1:
                    Console.WriteLine("{0} < {1}", value1, value2);
                    break;
                case 0:
                    Console.WriteLine("{0} = {1}", value1, value2);
                    break;
                case 1:
                    Console.WriteLine("{0} > {1}", value1, value2);
                    break;
            }

            //** parsing
            Console.WriteLine(BigInteger.Parse("10000000"));

            //** obtaining negation
            Console.WriteLine(BigInteger.Negate(-100));

            //** returning the sign
            Console.WriteLine(BigInteger.Negate(-1).Sign);

            //** returning conversion (int to BigInterger)
            int i = 100;
            BigInteger bI = (BigInteger)i;
            Console.WriteLine(bI);

            //** returning conversion (BigInteger to int)
            BigInteger BI = 200;
            int j = (int)BI;
            Console.WriteLine(j);
            Console.Read();
        }

        //** computing the factorials

        private static void ComputeBigFactorial(int factorial)
        {
            BigInteger number = factorial;
            BigInteger fact = 1;
            for (; number-- > 0; ) fact *= number+1;
            Console.WriteLine(fact);
        }

        //** computing the first even fibonacci series
        private static void SumOfFirstOneLacEvenFibonacciSeries(
                        int evenNumberFib)
        {
            int limit = evenNumberFib;

            BigInteger value1 = 1;
            BigInteger value2 = 2;
            BigInteger theSum = 0;
            BigInteger even_sum = value1 + value2;

            for (int i = 2; i < limit; i++)
            {
                theSum = value1 + value2;
                if (theSum % 2 == 0) even_sum += theSum;
                value1 = value2;
                value2 = theSum;
            }

            Console.WriteLine(even_sum);
        }
    }
}

Listing 3-1BigInteger Implementation

首先,我们需要在一个大整数中使用不同的计算来转换一个标准整数。为了做到这一点,我们编写了一个名为transformIntToBigInt(A, 123)的函数。该功能的目的是将 A 初始化为A[0]=3A[1]=2A[2]=1,并将剩余位置归零为A[3,...N-1]。清单 3-2 展示了如何通过使用一个简单的 C# 实现来完成上面的语句。在这个例子中,基址代表位符号。

public void TransformIntToBigInt(int BigNo[], int number)
{
      Int   k;

      //** start indexing with 0 position
      k = 0;

      // if we still have something left
      // within the number, continue
      while (number) {
            // insert the digit that is least significant
            // into BigNo[k] number
            BigNo[k++] = number % bitSign;

            // we don't need the least significant bit
            number /= BASE;
      }

      // complete the remain of the array with zeroes
      while (k < N)
            BigNo[k++] = 0;
}

Listing 3-2Transforming a Standard Integer Using Different Computations in a Big Integer

来自清单 3-2 的算法有 O ( N )空间和时间。

让我们继续我们的旅程,看看给一个大 int 加 1 的可能性。这个过程非常有用,在密码学中经常使用。清单 3-3 中的函数比总加法简单得多。

public void increment (int BigNo [])
{
      Int i;

      // start indexing with least significant digit
      i = 0;
      while (i < N)
      {

            // increment the digit
            BigNo[i]++;

            // if it overflows
            if (BigNo[i] == BASE)
            {

                  // make it zero and move the index to next
                  BigNo[i] = 0;
                  i++;
            }
            else
                  // else, we are done!
                  break;
      }
}

Listing 3-3Adding One to a Big Int

清单 3-2 中描述的算法对于可能的最坏情况有 O ( n )(想象一下类似于 99999999999999999999…)和ω(1)表示最佳情况。最好的情况发生在最低有效位没有溢出的时候。

继续,让我们仔细看看两个大整数相加的方法。在这种情况下,我们希望在两个不同的数组中添加两个大整数,BigNo1[0,…,N-1]和 BigNo2[0,…,N-1]。结果将保存在另一个数组 BigNo3[0,…,N-1]中。算法很基础,没什么花哨的。参见清单 3-4 。

public void addition(int BigNo1[], int BigNo2[], int BigNo3[])
{
      Int j, overflowCarry, sum;

      //** there is no necessary to carry at this moment
      carry = 0;

      //** move from the least to the most significant digit
      for (j=0; j<N; j++)
      {
            // the digit placed on j'th position of BigNo3[]
            // is the sum of j'th digits of
            // BigNo1[] and BigNo2[] and including the
            // overvflow carry
            sum = BigNo1[j] + BigNo2[j] + overflowCarry;

            // if the sum will go out of the base then
            // we will find ourself in an overflow situation
            if (sum >= BASE)
            {
                  carry = 1;

                  //** making adjustment in such way that
                  //** the sum will fit within a digit
                  sum -= BASE;
            }
            else
                  //otherwise no carryOverflow
                  carry = 0;

            // use the same sum variable to add the
            BigNo3[j] = sum;
      }

      // once we reached to the end
      // we can expect an overflow
      if (carry)
            printf ("There is an overflow in the addition!\n");
}

Listing 3-4Addition Algorithm

接下来,我们将重点讨论乘法。我们将使用一个基本的方法,将两个大数字相乘, XY ,将每个 X 数字与每个 Y 数字相乘,因此输出成为一个组成元素。每个新数字的绩效结果向左移动。我们实现的函数,multiplyingOneDigit,将把一个整数和一个数字相乘。结果将被放入一个新的大整数中。我们还将展示另一个函数,left_shifting,,它将数字向左移动一定数量的空格。用 b i 相乘,其中 b 为基数, i 表示空格数。清单 3-5 展示了乘法算法。

public void multiply (int BigInt1[], int BigInt2[],
                                   int BigInt3[])
{
      Int x, y, P[integer_length];

      // BigInt3 will store the sum of
      // partial products.  The value is set to 0.
      buildInteger (BigInt3, 0);

      // for each digit in BigInt1
      for (x=0; x<length_of_integer; x++)
      {
           // multiply BigInt2 with digit [x]
           multiplyOneDigit(BigInt2, P, BigInt1[x]);

           // left shifting the partial product with i bytes
           leftShifting(P, x);

           // add the output result to the current sum
           addResult(BigInt3, P, BigInt3);
      }
}

Listing 3-5Multiplication Algorithim

在清单 3-6 中,我们将检查一个乘以一位数的函数。

public void multiplyUsingOneDigit (int BigOne1[], int
                                       BigOne2[], int number)
{
      Int k, carryOverflow;

      // we don't need extraflow for the moment
      carryOverflow = 0;

      // for each digit, starting with least significant...
      for (k=0; k<N; k++)
      {
           // multiply the digit with the number,
           // save the result in BigOne2
           BigOne2[k] = number * BigOne1[k];

           // set extra overflow that is taking
           // place starting with the last digit
           BigOne2[k] += carryOverflow;

           // product is too big to fit in a digit
           if (BigOne2[k] >= BASE)
           {
                  //** handle the overflow
                  carryOverflow = B[k] / BASE;
                  BigOne2[k] %= BASE;
           }
           else
                  // no overflow
                  carryOverflow = 0;
      }
      if (carryOverflow)
            printf ("During the multiplication
                            we experienced an overflow!\n");
}

Listing 3-6Multiplying Using a Single Digit

清单 3-7 显示了一个将向左移动特定数量空格的函数操作。

public void leftShifting (int BigInt1[], int number) {
      int i;

      //start from left to right,
      // everything with left n spaces
      for (i=N-1; i>= number; i--)
           BigInt1[i] = BigInt1[i- number];

      // complete the last n digits with zeros
      while (i >= 0) BigInt1[i--] = 0;
}

Listing 3-7Shifting to Left a Specific Number of Spaces

在 C# 中使用大整数

.NET Framework 4.0 提供了System.Numerics.BigInteger类,该类基于Microsoft.SolverFoundation.Common.BigInteger

在设计和开发加密算法时,使用BigInteger非常有用。BigInteger表示不可变类型,用于没有上限或下限的大数。特征BigInteger类的成员是ByteInt(所有三种表示:16、32 和 64)、SByteUInt (16、32 和 64)类型。

如何使用 BigInteger 类

创建BigInteger对象有几种方法:

  • 使用new关键字并将任何浮点值作为参数提供给类的构造函数。参见清单 3-8 。

  • 通过声明一个赋值的BigInteger变量。参见清单 3-9 。

BigInteger publicCryptographyKeyFromDouble
                              = new BigInteger(131312.3454354353);
Console.WriteLine(publicCryptographyKeyFromDouble);

BigInteger publicCryptographyKeyFromInt64
                              = new BigInteger(7587643562354635);
Console.WriteLine(publicCryptographyKeyFromInt64);

Listing 3-8Using the new Keyword with BigInteger

  • 通过给一个BigInteger对象分配一个十进制或浮点值。在赋值发生之前,首先需要将值转换为BigInteger。见清单 3-10 。
long publicCryptographyKey = 87432541632312;
BigInteger cryptoPubKey = publicCryptographyKey;
Console.WriteLine(cryptoPubKey);

Listing 3-9Using a Variable with a Value Assigned

BigInteger publicCryptographyKeyFromDoubleValue
                                 = (BigInteger) 423412.3423;
Console.WriteLine(publicCryptographyKeyFromDoubleValue);

BigInteger publicCryptographyFromDecimal
                                 = (BigInteger) 23452.34m;
Console.WriteLine(publicCryptographyFromDecimal);

Listing 3-10Casting to BigIntegers

的大整数库。网

当我们在密码学中处理大整数时,64 位对于数字来说是不够的。这一节将介绍一个名为 RSA 的复杂密码系统的例子,我们将展示一些关于 .NET BigInteger类和其他。网络图书馆。

如上节所示,System.Numerics.BigIntegers是从开始引入的 .NET Framework 4.0,清单 3-11 中的简单示例展示了如何以不同于上面的其他示例的方式使用它。

var publicCryptoKey = BigInteger.Parse("5845829436572843562734865972843659784564");

var privateCryptoKey = BigInteger.Parse("245245452345252342435543527362524323455");

Listing 3-11Using System.Numerics.BigIntegers

任何运算(加、乘、除、差等。)使用publicCryptoKeyprivateCryptoKey是可能的。

良好操作规范

GMP [2 是一个命名空间,也称为 Emil,以 Emil Stefanov 命名。它结合了 F# 中 BigInt 的强大功能。这个库的缺点是它只能在 32 位系统上实现。C# 中的例子看起来与使用BigInteger的例子非常相似。参见清单 3-12 。

var publicCryptoKey = BigInt ("5845829436572843562734865972843659784564");

var privateCryptoKey = BigInt ("245245452345252342435543527362524323455");

Listing 3-12Using GMP

MPIR

MPIR [3 可以看做是 GMP [2 的一个分叉。MPIR 的目的是给 Windows 上的编译过程一个任何专业开发人员都想在他们的工具包中拥有的有味道和无痛的过程。

为了在美国使用 MPIR .NET 中,一些包装器是必要的,比如DLLImportsGetProcAddress赋值。一个非常好的可以使用的包装器是 X-MPIR,由来自阿尔吉利布 [4 ]的 Sergey Bochkanov 提出并开发。

清单 3-13 中的代码展示了如何使用 MPIR 开发复杂的密码系统。

var publicKey = mpir.mpz_init_set_str("4657334563546345", 10);
var sessionKey = mpir.mpz_init_set_str("784567354634564", 10);
var exampleOfOperation = mpir.mpz_init();

//** random operations
mpir.mpz_mul(exampleOfOperation, publicKey, sessionKey);

//** Implement the cryptosystem operations as you like **//

//** it is recommended to invoke clear
//** method in order to avoid any memory leakage
mpir.mpz_clear(publicKey);
mpir.mpz_clear(sessionKey);
mpir.mpz_clear(exampleOfOperation);

Listing 3-13Using MPIR

结论

在这一章中,我们介绍了表示大整数的最重要的方面,以及它们是如何设计和实现的。

  • 我们分析了处理大数的基本方法的实现的技术方面。

  • 我们展示了来自 .NET Framework 4.0 并展示了如何使用BigInteger对象。

  • 我们包括了其他处理大整数的库,比如 GMP 和 MPIR。

文献学

  1. BigInteger阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.numerics.biginteger?redirectedfrom=MSDN&view=netcore-3.1

  2. 没有限制的 GMP 算法。网上有: https://gm.plib.org /

  3. Mpir.NET。网上有: http://wezeku.github.io/Mpir.NET/

  4. 阿尔格里卜。网上有: www.alglib.net/

四、浮点运算

处理大整数可以被视为一种抽象艺术,如果密码系统没有正确实现,整个密码算法或方案可能会导致真正的灾难。本章重点介绍浮点运算及其对密码学的重要性。

为什么是浮点运算?

浮点运算代表了一种特殊形式的运算,我们需要关注它,因为它在密码学领域中的应用,如基于混沌的密码学、同态加密、量子密码术等等。这种类型的算法可以在基于混沌的加密或同态加密中观察到,这将在第 14 和 16 章中介绍。

在使用小实数和非常大的实数的系统中,也可以找到使用浮点数的计算。在他们的计算过程中,过程必须非常快。

浮点变量是一类特殊的变量,可以保存实数,如 4534.0、5.232 或 0.005443。浮动部分意味着小数点会“浮动”

C# 提供不同的浮点数据类型,如float double SByte 正如您从 C++或其他具有整数情况的编程语言中所知,该语言没有为这些类型定义任何大小。在现代体系结构中,大多数浮点表示都遵循二进制表示格式的 IEEE 754 标准。按照这个标准,float类型有 4 个字节,double有 8 个字节,long double有 8 个字节(与double相同),也是 80 位(通过填充我们有 12 个字节或 16 个字节)。

当您处理浮点值时,请确保至少有一个小数。这有助于编译器区分浮点数和整数。以下声明示例显示了如何声明和初始化变量:

int a = 4;        //** 4 is an integer
double b = 3.0    //** 3.0 represents a floating point (with no
                  //** suffix – double type by default)

显示浮点数

让我们考虑清单 4-1 中的例子。

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

namespace CommonFloatNumbers
{
    class Program
    {
        static void Main(string[] args)
        {
            float cryptoKey1 = 5.0f;
            float cryptoKey2 = 6.7f;

            Console.WriteLine(cryptoKey1);
            Console.WriteLine(cryptoKey2);

            Console.ReadKey();

        }
    }
}

Listing 4-1Displaying Common Float Numbers

输出如图 4-1 所示。

img/493660_1_En_4_Fig1_HTML.jpg

图 4-1

普通浮点数的输出

通过查看程序的输出,您可以发现在第一种情况下,输出是 5 个字节,但是源代码显示的是 5.0。这是因为小数部分等于 0。在第二种情况下,打印的数字与源代码中的数字相同。在第三种情况下,数字使用科学记数法显示,这是加密算法的重要资产。

浮点的范围

让我们来看看 IEEE 754 表示,并考虑以下尺寸及其范围和精度。见表 4-1 。

表 4-1

IEEE 754 标准表示法

|

大小

|

范围

|

精确

|
| --- | --- | --- |
| 4 字节 | 1.18×10—38 3.4 × 10 38 | 6-9 个最重要的数字。通常在 7 位数左右。 |
| 8 字节 | 2.23×10—308 1.80 × 10 308 | 15-18 个最重要的数字。通常在 16 位数左右。 |
| 80 位(通常使用 12 或 16 字节) | 3.36×10—4932 1.18 × 10 4932 | 18-21 个最重要的数字 |
| 16 字节 | 3.36×10—4932 1.18 × 10 4932 | 33-36 位最重要的数字 |

在当今的处理器上,80 位浮点是用 12 或 16 字节实现的。处理器能够管理这种大小是很常见的。

浮点精度

让我们考虑下面这个用分数表示的例子:$$ \frac{1}{3} $$。十进制表示是 0.3333333…无穷大 3 s

使用一台机器,需要无限的内存来存储无限长的数。这种内存限制会将浮点数的存储限制为特定数量的有效数字。浮点数及其精度将决定在不丢失任何知识的情况下可以表达多少有效数字。如果我们确实在密码学中产生了一个浮点数,清单 4-2 中的例子展示了如何将值截断为六位数。输出见图 4-2 。

img/493660_1_En_4_Fig2_HTML.jpg

图 4-2

浮点精度的输出

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

namespace FloatingValues
{
    class Program
    {
        static void Main(string[] args)
        {
            float cryptoKey1 = 7.56756767f;
            float cryptoKey2 = 765.657667f;
            float cryptoKey3 = 345543.564f;
            float cryptoKey4 = 9976544.43f;
            float cryptoKey5 = 0.00043534345f;

            Console.WriteLine("cryptoKey1 = " + cryptoKey1);
            Console.WriteLine("cryptoKey2 = " + cryptoKey2);
            Console.WriteLine("cryptoKey3 = " + cryptoKey3);
            Console.WriteLine("cryptoKey4 = " + cryptoKey4);
            Console.WriteLine("cryptoKey5 = " + cryptoKey5);

        }
    }
}

Listing 4-2Representation of Floating Point Precision

请注意,上面的每个案例只有六个重要数字。

通常,根据所使用的编译器,指数将在最小位数内填充。指数的位数取决于所使用的编译器,例如 Visual Studio 使用 3,而其他编译器使用 2(使用符合 C99 的指令和标准实现)。表示浮点数的位数及其精度取决于大小和存储的特定值。float值的定义精度为 6 和 9 位数字,大多数值至少有 7 位有效数字。double值以 15 位和 18 位精度表示。Long double值至少由 15 或 33 位有效数字表示,这取决于字节的占用情况。

浮点运算的下一级

在第十四章中,我们将介绍一种复杂类型的加密。同态加密代表一种特殊类型的加密,被用作保护隐私的专业技术。这项技术将存储和计算外包出去。这种加密方式允许数据被加密并外包给商业(或公共)环境,目的是处理它们。所有这些过程都是在数据加密时发生的。同态加密源自带错误的环学习(见第十五章),与私有集合交集 [1 ]相关。

继续我们的复杂密码系统之旅,浮点表示法代表了加密/解密机制的热点,它通过寻找适当的方法来近似一个实数,从而支持范围和精度之间的折衷。

如上所述,浮点术语表示数字的小数点可以浮动。总之,它可以设置在数字的重要数字内的任何位置。更具体地说,当我们着眼于复杂的密码体制时,比如同态加密,一个浮点数一个可以表示为四个整数,所以在

$$ a=\pm d\cdotp {n}^{f-j} $$

n 为基数, f 为指数, j 为精度, d 为重要或有效位。重要性或有效位 d 必须满足以下关系:

$$ 0\le d\le {n}^f-1 $$

从 with.NET 框架 4.0 开始,有一个庞大的浮点操作例程集 [8 ]。从 C# 8.0 开始,这些基本函数用于基于与浮点数相关的简单数学计算来工作和完成任务,这是普通编程和加密(低级和简单的概念)所必需的。对于高级加密算法,该函数的能力和对大浮点数的要求非常有限,并且它们不能为密码学家提供合适的设备。

下面我们将研究 IEEE 处理浮点的标准化和实现函数。与上面已经给出的应用相比,这个应用稍微复杂一些。也就是说,应用的结构包含三个类:IEEEStandardization.cs(参见清单 4-3 )、FloatingPoint.cs(参见清单 4-4 )和Program.cs(参见清单 4-5 )。下面的应用是根据 IEEE 标准开发的,展示了如何以较少的努力实现主要操作。

在清单 4-3 中,我们可以看到处理浮点值时返回的值的主要类型。这些值表示为枚举类型,包含从规范化到反规范化的值、非数字字符和符号值。在清单 4-4 中,我们实现了获取双二进制表示部分的方法、IEEE-754 标准操作的实现以及单精度位转换的方法。在清单 4-5 中,我们将所有的方法合并到一个主程序中,并让它工作,展示了我们如何使用不同的方法来计算浮点值、乘法结果和减法。见图 4-3 。

img/493660_1_En_4_Fig3_HTML.jpg

图 4-3

基于 IEEE 标准化的复杂浮点运算

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

namespace FloatingPointArithmetic
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("FLOATING POINT ARITHMETIC " +
                "by Marius Iulian MIHAILESCU " +
                "and Stefania Loredana NITA \n");

            Console.WriteLine("\t\tDifferent ways of computing 1/105.");
            Console.WriteLine("\t\tMultiply the output with 105 and subtract 1");
            Console.WriteLine("\t\tWe will get an error.");

            double d = 1 / 105.0;
            float s = 1 / 105.0F;

            Console.WriteLine("\t\t\tUsing double: {0} * " +
                "105 - 1 = {1} < 0!", d, d * 105.0 - 1.0);
            Console.WriteLine("\t\t\tUsing single: {0} * " +
                "105 - 1 = {1} > 0!", s, s * 105.0 - 1.0);
            Console.WriteLine();

            Console.WriteLine("\t\tComputing a chaos-based " +
                "value for cryptography purpose.");
            float chaotic_value = 4.99F * 17;
            Console.WriteLine("\t\t\tThe chaotic value is " +
                "{0}.", chaotic_value);
            Console.WriteLine();

            Console.WriteLine("\t\tAnother example of chaotic " +
                "value for which we need the integer part.");
            int another_chaotic_value = (int)(100 * (1 - 0.1F));
            Console.WriteLine("\t\t\tAnother chaotic value is {0}.",
                another_chaotic_value);
            Console.WriteLine();

            Console.WriteLine("\t\tFor cryptography is " +
                "important to have an implementation " +
                "for IEEE-754");
            double[] double_values = new double[] { 0, -1 /
                Double.PositiveInfinity, 1, -1,
                //Math.PI,
                //Math.Exp(20),
                //Math.Exp(-20),
                //Double.PositiveInfinity,
                //Double.NegativeInfinity,
                //Double.NaN,
                //Double.Epsilon,
                // -Double.Epsilon,
                //10 / Double.MaxValue
            };

            for (int i = 0; i < double_values.Length; i++)
            {
                Console.WriteLine("\t\t\tIEEE-754 Value Type({0}) = {1}",
                    double_values[i],
                    FloatingPoint.Class(double_values[i]));

                Console.WriteLine("\t\t\t{0,19:E8}{1,19:E8}{2,19}{3,19}",
                    FloatingPoint.ComputerNextValue(double_values[i],
                        Double.PositiveInfinity) - double_values[i],

                    FloatingPoint.ComputerNextValue(double_values[i],
                    Double.NegativeInfinity) - double_values[i],

                    FloatingPoint.ComputingLogB(double_values[i]),
                    FloatingPoint.ReuturnSignificantMantissa(double_values[i]));
            }
            Console.ReadLine();
        }
    }
}

Listing 4-5Main Program Implementation

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

namespace FloatingPointArithmetic
{
    public sealed class FloatingPoint
    {
        #region variable and instances
            // the constructor of the class
            private FloatingPoint() { }

            // the value for conversion is 2⁶⁰
            // 2⁶⁰ = 1,152,921,504,606,846,976 (decimal base)
            // 2⁶⁰ = 10 00 00 00 00 00 00 00 (hex bytes)
            // 8 * 2⁶⁰ = 2 * 1,152,921,504,606,846,976 =
            //          = 2,305,843,009,213,693,952
            // we will use "unchecked" for supressing overflow-checking
            // for integral-type arithmetic operations and conversions
            private static readonly double UnfavorableNegativeValue
                = BitConverter.Int64BitsToDouble(unchecked(8 * 0x1000000000000000));

            // constants
            private const Double minimum_double = 4.94065645841247e-324;

            // 0x7FFFFFFFFFFFFFFF = 9,223,372,036,854,775,807 (decimal)
            private const long mask_sign_value = -1 - 0x7FFFFFFFFFFFFFFF;
            private const long clear_mask_sign = 0x7FFFFFFFFFFFFFFF;

            private const long signficand_mask = 0xFFFFFFFFFFFFF;
            private const long clearing_significand_mask = mask_sign_value | significand_exponent_mask;

            private const long significand_exponent_mask = 0x7FF0000000000000;
            private const long clearing_exponent_mask = mask_sign_value | signficand_mask;

            private const int deviation = 1023;
        private const int significant_bits = 52;
        #endregion

        #region Methods for getting parts of a double's binary representation.

            // the method return the significand of double value
            public static long ReuturnSignificantMantissa(double value)
            {
                return BitConverter.DoubleToInt64Bits(value)
                            & signficand_mask;
            }

            // the method will return the signicand
            // for a floating-point number
            public static double ReturnSignficantForFloatingPoint(double value)
            {
                if (Double.IsNaN(value)) return value;

                if (Double.IsInfinity(value)) return value;

                // computing the exponent using the deviation
                int exponentValue = ComputeDeviationExponent(value);
                long significand = ReuturnSignificantMantissa(value);

                // number 0 and denormalization
                // values has to be treated separetely
                if (exponentValue == 0)
                {
                    // if the significand is equal
                    // with we will return 0
                    if (significand == 0)
                        return 0.0;

                    // otherwise we will shit the significand to the left
                    // until significand will be 53 bits long
                    while (significand < signficand_mask)
                        significand <<= 1;

                        // truncate the leading bit
                        significand &= signficand_mask;
                }
                return BitConverter.Int64BitsToDouble
                    ((deviation << 52) + significand);
            }

            // The function will return a non-deviation
            // exponent for a floating-point value.
            // The non-deviation is computed through
            // substracting the deviation from deviated exponent.
            public static int NonDeviationExponent(double value)
            {
                return (int)((BitConverter.DoubleToInt64Bits(value)
                    & significand_exponent_mask) >> 52) - deviation;
            }

            // The function will return the
            // deviation exponnent for a
            // floating-point value.
            // The returned value is obtained
            // and computed directly from
            // and within binary representation of "value"
            public static int ComputeDeviationExponent(double value)
            {
                return (int)((BitConverter.DoubleToInt64Bits(value)
                    & significand_exponent_mask) >> 52);
            }

            // The function returns the bit sign
            // of a value. The bit sign is obtained
            // directly from the binary representation
            // of the value
            public static int SignBit(double value)
            {
                return ((BitConverter.DoubleToInt64Bits(value)
                    & mask_sign_value) != 0) ? 1 : 0;
            }
        #endregion

        #region Below contains the implementation of the IEEE-754

            // The class represents the implementation
            // of IEEE-754 floating-point
            // References:
            // https://www.geeksforgeeks.org/ieee-standard-754-floating-point-numbers/
            public static IEEEStandardization Class
                (double value)
            {
                long bits_value_representation =
                    BitConverter.DoubleToInt64Bits(value);

                bool positive_value = (bits_value_representation >= 0);

                bits_value_representation =
                    bits_value_representation & clear_mask_sign;

                // case when we have a overflow
                // for Not-A-Number
                if (bits_value_representation
                    >= 0x7ff0000000000000)
                {
                    // this is case of infinity
                    if ((bits_value_representation
                        & signficand_mask) == 0)
                    {
                        if (positive_value)
                            return IEEEStandardization.
                                Value_Positive_Infinity;
                        else
                            return IEEEStandardization.
                                Value_Negative_Infinity;
                    }
                    else

                    {
                        if ((bits_value_representation
                            & signficand_mask)
                            < 0x8000000000000)
                            return IEEEStandardization.
                                Quiet_Not_a_Number;
                        else
                            return IEEEStandardization.
                                Signaling_Not_a_Number;
                    }
                }
                // this is happening when we have
                // 0 or a denormalization value
                else if (bits_value_representation
                    < 0x0010000000000000)
                {
                    if (bits_value_representation == 0)
                    {
                        if (positive_value)
                            return IEEEStandardization.
                                Value_Positive_Zero;
                        else
                            return IEEEStandardization.
                                Value_Negative_Zero;
                    }
                    else
                    {
                        if (positive_value)
                            return IEEEStandardization.
                                Denormalization_Positive_Denormalized;
                        else
                            return IEEEStandardization.
                                Denormalization_Negative_Denormalized;
                    }
                }
                else
                {
                    if (positive_value)
                        return IEEEStandardization.
                            Normalization_Positive_Normalized;
                    else
                        return IEEEStandardization.
                            Normalization_Negative_Normalized;
                }
            }

            // The function copy the
            // sign of the number.
            // theSizeOfTheValue parameter
            //        the number for
            //        which we copy the sign
            // theValueOfTheSign parameter
            //        the value of the number
            //        for which we do the copy
            public static double CopyProcedureForSign
                    (double theSizeOfTheValue,
                     double theValueOfTheSign)
            {
                // we do a bit manipulation
                // do the copying process for
                //* the first bit for theSizeOfTheValue
                // and theValueOfTheSign
                return BitConverter.Int64BitsToDouble(
                    (BitConverter.DoubleToInt64Bits
                        (theSizeOfTheValue) &
                            clear_mask_sign)
                    | (BitConverter.DoubleToInt64Bits
                        (theValueOfTheSign) &
                            mask_sign_value));
            }

            // a boolean function to
            // check if the value is
            // finite or not
            public bool CheckIfIsFinite(double value)
            {
            // Verify the part represented by the exponent.
            // if all the bits are 1, then we
            // are dealing with a infinity (not-a-number).
                long bits = BitConverter.
                    DoubleToInt64Bits(value);
                return ((bits & significand_exponent_mask)
                    == significand_exponent_mask);
            }

            // The function will return the
            // non-biased exponent for a value.
            public static double ComputingLogB(double value)
            {
                // Let's deal with the
                // important situations.
                if (double.IsNaN(value))
                    return value;
                if (double.IsInfinity(value))
                    return double.PositiveInfinity;
                if (value == 0)
                    return double.NegativeInfinity;

                int exponentDeviationValue =
                    ComputeDeviationExponent(value);

                // if we dealing with a denormalization value
                // we need to take the right action
                if (exponentDeviationValue == 0)
                {
                    exponentDeviationValue = -1074;

                    // compute the signficand with no sign
                    long bits = BitConverter.
                        DoubleToInt64Bits(value) & clear_mask_sign;

                    // we move on if we finish dealing with situations
                    // when bits = 0
                    do
                    {
                        bits >>= 1;
                        exponentDeviationValue++;
                    }
                    while (bits > 0);
                    return exponentDeviationValue;
                }

                // exponentDeviationValue was significand,
                // proceed with subtraction the deviation
                // to obtain and compute the non-deviation exponent
                return exponentDeviationValue - deviation;
            }

            // Compute the floating-point
            // number for the next number.
            // 'from' parameter
            //        - represent the starting point
            // 'to' parameter
            //        - represent a value that shows
            //          the direction in which we will
            //          move in order to identity
            //          the next value
            public static double
                ComputerNextValue(double from, double to)
            {
                // If 'to' is equal with from
                // there is no direction to move in,
                // so we will compute and get 'from' value
                if (from == to)
                    return from;

                // if not-a-number occur will
                // be returned by themselves
                if (double.IsNaN(from))
                    return from;
                if (double.IsNaN(to))
                    return to;

                // an infinity will be an infinity all time
                if (double.IsInfinity(from))
                    return from;

                // deal with 0 situation
                if (from == 0)
                    return (to > 0) ?
                        minimum_double : -minimum_double;

                // For the rest of the
                // situation we are dealing.
                // With incrementation or
                // decrementation the bits value.
                // Values for transitions to infinity,
                // denormalized values, and to zero are
                // managed in this way.
                long bits_value = BitConverter.DoubleToInt64Bits(from);

                // A xor here avoids nesting conditionals. We have to increment if
                // fromValue lies between 0 and toValue.

                // XOR operation will help us to
                // not taken into consideration
                // conditionals.
                if ((from > 0) ^ (from > to))
                    bits_value++;
                else
                    bits_value--;
                return BitConverter.
                    Int64BitsToDouble(bits_value);
            }

            // the function compute and return
            // a value that is powered with 2
            public static double Scalb(double number,
                                       int exponent)
            {
                // Treat special cases first.
                if (number == 0 ||
                            double.IsInfinity(number) ||
                            double.IsNaN(number))
                    return number;

                if (exponent == 0)
                    return number;

                int computedExponentValue = ComputeDeviationExponent(number);
                long significand = ReuturnSignificantMantissa(number);
                long getting_sign = ((number > 0) ? 0 : mask_sign_value);

                // check if 'number' is denormalized
                if (computedExponentValue == 0)
                {
                    if (exponent < 0)
                    {
                        // an exponent that is negative
                        // we will shift the significand
                        // -exponent bits to the right
                        significand >>= -exponent;
                        return BitConverter.
                            Int64BitsToDouble(getting_sign | significand);
                    }
                    else

                    {
                        // a number that is positive is
                        // necessary to be shifted on left
                        // and this will be done until a
                        // normalized number is obtained
                        while (significand <= signficand_mask
                            && exponent > 0)
                        {
                            significand <<= 1;
                            exponent--;
                        }

                        if (significand > signficand_mask)
                            exponent++;

                        // test if we have a overflow
                        if (exponent > 2 * deviation)
                            return (number > 0) ?
                                double.PositiveInfinity
                                : double.NegativeInfinity;

                        // the number represents the
                        // significand exponent for the result
                        return BitConverter.Int64BitsToDouble(getting_sign
                            | ((long)exponent << 52) |
                                (significand & signficand_mask));
                    }
                }

                // Once we are reaching here,
                // we are aware that 'exoponent'
                // is normalized.
                // Proceeding with scaling. 'exponent'
                // will be the significand exponent for the result
                computedExponentValue =
                    computedExponentValue + exponent;

                // verify if we have 0 or denormalization
                if (computedExponentValue < 0)
                {
                    significand = ((1L << 52) +
                        significand) >> (1 -
                            computedExponentValue);

                    return BitConverter.
                        Int64BitsToDouble(getting_sign | significand);
                }

                // Veirfy if we have an overflow
                if (computedExponentValue >
                    2 * deviation)
                    return (number > 0) ?
                        double.PositiveInfinity :
                        double.NegativeInfinity;

                // If we're here, the result is normalized.
                long bits = getting_sign |
                    ((long)computedExponentValue << 52) | significand;

                return BitConverter.Int64BitsToDouble(bits);
            }

            // the function computes a value
            // wich will point out if the two
            // values are unordered
            public static bool Unordered(double value1, double value2)
            {
                return double.IsNaN(value1) || double.IsNaN(value2);
            }
        #endregion

        #region Methods for conversion bit with single-precision
            public static unsafe int ConversionSingleToInt32Bits(float val)
            {
                return *((int*)&val);
            }

            public static unsafe float ConversionInt32BitsToSingle(int val)
            {
                return *((float*)&val);
            }
        #endregion
    }
}

Listing 4-4Floating Point Implementation

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

namespace FloatingPointArithmetic
{
    public enum IEEEStandardization
    {
        // the value is a signaling NaN - not a number
        Signaling_Not_a_Number,

        // the value is represented by a quiet
        // NaN - not a number and non-signaling
        Quiet_Not_a_Number,

        // the value represents a positive infinity
        Value_Positive_Infinity,

        // the value represents a negative infinity
        Value_Negative_Infinity,

        // The value represents a normal and positive number
        Normalization_Positive_Normalized,

        // The value represents a normal and negative number
        Normalization_Negative_Normalized,

        // A denormalized positive number
        Denormalization_Positive_Denormalized,

        // The value is a denormalized negative number
        Denormalization_Negative_Denormalized,

        // The value represents a positive zero
        Value_Positive_Zero,

        // the value represents a negative zero
        Value_Negative_Zero
    }
}

Listing 4-3IEEE Standardization Implementation

结论

在这一章中,我们讨论了浮点数的一般表示,以及如何在复杂的密码系统中实现它们。我们分析了最重要的术语和基本概念,专业人员在计划建立一个开发复杂密码系统的环境时应该了解这些概念。

我们还证明了浮点运算在复杂密码系统中的重要性,如同态加密、基于混沌的密码、基于格的密码或带错误的环学习。

文献学

  1. H.来自同态加密的快速私有集合交集。2017 年 ACM SIGSAC 计算机与通信安全会议 CCS '17 论文集,2017。

  2. 长度 Ducas 和 D. Micciancio,“FHEW:在不到一秒的时间内引导同态加密”,载于密码学进展-Eurocrypt 2015,第 617-640 页,施普林格,柏林,德国,2015 年。

  3. 南 Halevi 和 V. Shoup,“HElib 中的算法”,载于Crypto14,第 8616 卷,施普林格,海德堡,德国,2014 年。

  4. J.坎波斯、p .夏尔马、e .詹图宁、d .巴格利和 l .福马加利,“保护高级维护开发所需数据的网络安全框架的挑战”,载于《CIRP 进程报,第 47 卷,第 227 页,2016 年。

  5. c .燃烧物品和 j .Ziegler,“Fast Recursive Division”,inresearchreport max-Planck 计算机科学研究所 report MPI-I-98-1-022,max-Planck-Instituto für informatique,sahabrüber,Germany,1998 年。

  6. 名词(noun 的缩写)Dowlin,R. Gilad-Bachrach,K. Laine,K. Lauter,M. Naehrig 和 J. Wernsing,“使用同态加密进行生物信息学的手册”,载于 IEEE 会议录,第 105 卷,第 3 期,2017 年。

  7. J.H. Cheon、A. Kim、M. Kim 和 Y. Song,“近似数算术的同态加密”,载于《密码学与信息安全的理论和应用国际会议(ASIA-CRYPT'17)会议录(??),第 409-437 页,中国香港,2017 年 12 月。

  8. 浮点支持。网上有: https://docs.microsoft.com/en-us/cpp/c-runtime-library/floating-point-support?view=vs-2019

五、C# 8.0 的新特性

本章将为密码学和密码分析领域的专业人士讲述 C# 8.0 最重要的特性。有关哪个版本的 C# 用于不同版本的 .NET 框架和 .NET Core,我们推荐 [3 ]的资源。

C# 8.0 对 C# 语言进行了许多改进和增强,其中一些可以成功地用于提高加密和密码分析算法以及安全方案的实现过程的性能。上支持 C# 8.0。网芯 3。x 和。网络标准 2.1。关于 C# 语言版本控制的更多细节可以在这里找到 [1 。这些增强功能包括

  • 只读成员

  • 默认接口方法

  • 模式匹配的改进:开关表达式、属性模式、元组模式、位置模式

  • 使用声明

  • 静态局部函数

  • 一次性参考支柱

  • 可为空的引用类型

  • 异步流

  • 异步一次性

  • 指数和范围

  • 零合并赋值

  • 非托管构造类型

  • 嵌套表达式中的 Stackalloc

  • 插值逐字字符串的增强

如前所述,本章将描述有助于专业人员提高加密解决方案质量和性能的特性。要探索环境中 C# 8.0 提供的特性的更多细节,建议使用dotnet try工具。要深入探索这些功能,可以成功使用以下解决方案和步骤:

以下部分将涵盖显著提高加密应用的质量和性能的增强功能。

只读成员

新的 readonly 修饰符可以应用于结构的成员。当我们不希望成员的状态被修改时,使用 readonly。我们将通过对成员应用 readonly 而不是 struct 声明来展示更细粒度的表示。

让我们考虑清单 5-1 中的例子,其中我们实现了一个处理 RSA 密码系统的结构。

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

namespace Listing_5_1___ReadOnlyMembers
{
    class Program
    {
        public int cryptoKey1;

        //** initializing and dealing with
        public readonly int cryptoKey2 = 25;
        public readonly int cryptoKey3;

        public Program()
        {
            // Initialize a readonly instance field
            cryptoKey3 = 24;
        }

        public Program(int value1, int value2, int value3)
        {
            cryptoKey1 = value1;
            cryptoKey2 = value2;
            cryptoKey3 = value3;
        }

        public static void Main()

        {
            Program cryptoProgram1 = new Program(13, 27, 39);
            Console.WriteLine($"Crypto Program 1: crypto_key 1={cryptoProgram1.cryptoKey1}, crypto_key 2={cryptoProgram1.cryptoKey2}, crypto_key 3={cryptoProgram1.cryptoKey3}");

            Program cryptoProgram2 = new Program();
            cryptoProgram2.cryptoKey1 = 55;
            Console.WriteLine($"Crypto Program 2: crypto_key 1={cryptoProgram2.cryptoKey1}, crypto_key 2={cryptoProgram2.cryptoKey2}, crypto_key 3={cryptoProgram2.cryptoKey3}");

            Console.ReadKey();
        }
    }
}

Listing 5-1Applying Readonly for a Cryptographic Purpose

与大多数结构一样,ToString()方法不会更新或修改状态。我们可以通过将readonly放在override关键字的前面来指定发生这种情况(见图 5-1 )。

img/493660_1_En_5_Fig1_HTML.jpg

图 5-1

使用 readonly 关键字的输出示例

模式匹配

在加密模式匹配中,技术可以用在不同的地方,例如解析密码要求、字符串和加密密钥预期。

从 C# 8.0 开始,可以在代码的不同位置使用和实现更多的模式表达式。C# 8.0 的另一个重要增强是递归模式,一个可以在另一个模式表达式结果的输出上使用的模式表达式。让我们考虑清单 5-2 中的例子,我们使用一个enum结构来列出加密算法。

public enum CryptoType
{
    RSA,
    AES,
    TripleDES,
}

Listing 5-2Using an enum struct

如果正在开发的应用包含CryptographicAlgorithm类型的定义,则它是由加密组件(例如加密、解密、计算私钥、计算公钥等)构建的。),所以我们可以使用清单 5-3 中的例子,用一条switch指令将一个CryptographicAlgorithm值转换成CryptoType值。

public static CryptographicAlgorithm
      GetCryptoAlgorithm(CryptographicAlgorithm crypto)
{
   return crypto.cryptosystemType switch
   {
      CryptoType.RSA => new RSA(),
      CryptoType.AES => new AES(),
      CryptoType.TripleDES => new TripleDES(),
      _ => throw new ArgumentException(message: "There is no
                 such cryptographic algorithm ",
                 paramName: nameof(crypto.cryptosystemType))
   };
}

Listing 5-3Using a switch Expression

清单 5-4 给出了一个完整的例子。该列表的结果如图 5-2 所示。

img/493660_1_En_5_Fig2_HTML.jpg

图 5-2

清单 5-4 中代码的结果

using System;

namespace PatternsMatching
{
    class Program
    {
        static void Main(string[] args)
        {
            CryptographicAlgorithm cryptoAlg =
                      new CryptographicAlgorithm();
            Console.WriteLine("Pick a cryptosystem [1=RSA,
                                 2=AES, 3=TripleDES]");

            string type = Console.ReadLine();
            CryptoType ct = type switch
            {
                "1" => CryptoType.RSA,
                "2" => CryptoType.AES,
                "3" => CryptoType.TripleDES,
                _ => throw new ArgumentException(message:
                      "There is no such option ",
                      paramName: nameof(type))
            };

            try
            {
                cryptoAlg.cryptosystemType = ct;
                GetCryptoAlgorithm(cryptoAlg);

                Console.ReadKey();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public static CryptographicAlgorithm
              GetCryptoAlgorithm(
                      CryptographicAlgorithm crypto)
        {
            return crypto.cryptosystemType switch
            {
                CryptoType.RSA => new RSA(),
                CryptoType.AES => new AES(),
                CryptoType.TripleDES => new TripleDES(),
                _ => throw new ArgumentException(message:
                      "There is no such cryptographic algorithm ",
                       paramName: nameof(crypto.cryptosystemType))
            };
        }
    }
    public enum CryptoType
    {
        RSA,
        AES,
        TripleDES,
    }

    public class CryptographicAlgorithm
    {
        internal CryptoType cryptosystemType;
        public CryptographicAlgorithm()
        {

        }
    }

    class RSA : CryptographicAlgorithm
    {
        public RSA()
        {
            Console.WriteLine("RSA chosen!");
        }
    }

    class AES : CryptographicAlgorithm
    {
        public AES()
        {
            Console.WriteLine("AES chosen!");
        }
    }

    class TripleDES : CryptographicAlgorithm
    {
        public TripleDES()
        {
            Console.WriteLine("TripleDES chosen!");
        }
    }
}

Listing 5-4Chosing an Encryption System Based on switch Expressions

模式

属性模式

使用属性模式的新特性让专业人员能够匹配属于对象的属性。让我们考虑一个电子学习平台作为例子(见清单 5-5 )。我们需要基于CryptographicAlgorithm对象的cryptosystemType属性,使用特定的加密算法对消息进行加密。请注意,这些部分中的示例仅用于演示。

public static int Encrypt(CryptographicAlgorithm crypto, Parameters parameters, int message)
{
    return crypto switch
    {
      { cryptosystemType: CryptoType.RSA } => (new RSA()).
Encrypt(parameters.n, parameters.e, message),
      { cryptosystemType: CryptoType.AES } => (new AES()).
                Encrypt(parameters.key, message),
      { cryptosystemType: CryptoType.TripleDES } => (new
             TripleDES().Encrypt(parameters.k1, parameters.k2,
                            parameters.k3, message)),
     _ => throw new ArgumentException(message: "There is no
                           such cryptographic algorithm ",
                           paramName:
                             nameof(crypto.cryptosystemType))
     };
  }

Listing 5-5Using Property Patterns

清单 5-6 中给出了完整的代码,输出如图 5-3 所示。

img/493660_1_En_5_Fig3_HTML.jpg

图 5-3

清单 5-6 中代码的结果

using System;

namespace PropertyPatterns
{
    class Program
    {
        static void Main(string[] args)
        {
            CryptographicAlgorithm cryptoAlg = new
                          CryptographicAlgorithm();
            Console.WriteLine("Pick a cryptosystem [1=RSA,
                              2=AES, 3=TripleDES]");

            string type = Console.ReadLine();
            CryptoType ct = type switch
            {
                "1" => CryptoType.RSA,
                "2" => CryptoType.AES,
                "3" => CryptoType.TripleDES,
                _ => throw new ArgumentException(message:
                     "There is no such option ",
                      paramName: nameof(type))
            };

            //** the parameters should be initialized
            Parameters parameters = new Parameters();
            //** the message that needs to be encrypted
            int message = 0;
            try
            {
                cryptoAlg.cryptosystemType = ct;
                Encrypt(cryptoAlg, parameters, message);
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public static int Encrypt(
                     CryptographicAlgorithm  crypto,
                     Parameters parameters,
                     int message)
        {
            return crypto switch
            {
                { cryptosystemType: CryptoType.RSA } => (new
                        RSA()).Encrypt(parameters.n,
                                       parameters.e,
                                       message),
                { cryptosystemType: CryptoType.AES } => (new
                        AES()).Encrypt(parameters.key,
                                       message),
                { cryptosystemType: CryptoType.TripleDES } =>
                       (new TripleDES().Encrypt(parameters.k1,
                                            parameters.k2,
                                            parameters.k3,
                                            message)),
                _ => throw new ArgumentException(message:
                          "There is no such cryptographic
                           algorithm ",
                           paramName:
                             nameof(crypto.cryptosystemType))
            };
        }
    }
    public enum CryptoType
    { RSA, AES, TripleDES, }

    public class Parameters
    {
        public Parameters() { }
        internal int n, e, k1, k2, k3;
        internal int[,] key;
    }

    public class CryptographicAlgorithm
    {
        internal CryptoType cryptosystemType;
        public CryptographicAlgorithm() { }
        public int Encrypt()
        {
            return 0;
        }
    }

    class RSA : CryptographicAlgorithm
    {
        public RSA()
        {
            Console.WriteLine("RSA chosen!");
        }

        public int Encrypt(int n, int e, int message)
        {
            Console.WriteLine("Here goes the implementation of
                           the RSA encryption algorithm.");
            return 0;
        }

    }

    class AES : CryptographicAlgorithm
    {
        public AES()
        {
            Console.WriteLine("AES chosen!");
        }
        public int Encrypt(int[,] key, int message)
        {
            Console.WriteLine("Here goes the implementation of
                              the AES encryption algorithm.");
            return 0;
        }
    }

    class TripleDES : CryptographicAlgorithm
    {
        public TripleDES()
        {
            Console.WriteLine("TripleDES chosen!");
        }
        public int Encrypt(int k1, int k2, int k3,
                           int message)
        {
            Console.WriteLine("Here goes the implementation of
                       the TripleDES encryption algorithm.");
            return 0;
        }
    }
}

Listing 5-6Demonstration of Using Property Patterns

多重模式

一些加密算法(例如 RSA 或可搜索的加密方案)依赖于各种输入。使用元组模式给了我们在用元组表示的多个值之间切换的可能性。清单 5-7 中的代码说明了如何使用开关表达式在不同的加密算法之间进行切换。

在本例中,您可以根据密钥的数量选择加密系统:一个密钥用于加密和解密,或者一对密钥(用于加密的公钥和用于解密的密钥)。列表 5-7 的结果如图 5-4 所示。

img/493660_1_En_5_Fig4_HTML.jpg

图 5-4

列表结果 5-7

using System;

namespace TuplePatterns
{
    class Program
    {
        static void Main(string[] args)
        {
            CryptographicAlgorithm cryptoAlg =
                           new CryptographicAlgorithm();
            Console.WriteLine("Enter the number of keys:
                         1=(secret key);
                         2=(public key, secret key); ");

            string noOfKeys = Console.ReadLine();

            try
            {
                Console.WriteLine(noOfKeys switch
                {
                    "1" => InitializingAlgKeys(cryptoAlg,
                                                 false, true),
                    "2" => InitializingAlgKeys(cryptoAlg,
                                                  true, true),
                    _ => throw new ArgumentException(message:
                                  "There is no such option ")
                });

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public static string InitializingAlgKeys
                     (CryptographicAlgorithm crypto,
                         bool publicKey,
                         bool secretKey)
        {
            return (publicKey, secretKey) switch
            {
                (true, true) => (new RSA()).InitializeKeys(),
                (false, true) => (new AES()).InitializeKeys(),
                _ => throw new ArgumentException(message:
                                 "There is no such option. ")
            };
        }
    }

    public class CryptographicAlgorithm
    {
        public CryptographicAlgorithm() { }
        public string InitializeKeys()
        {
            return "";
        }
    }

    class RSA : CryptographicAlgorithm
    {
        public string InitializeKeys(int publicKey = -1,
                                     int secretKey = -1)
        {
            return "RSA uses a public key and a secret key.
                    Initializing the keys for RSA...";
        }
    }

    class AES : CryptographicAlgorithm
    {
        public string InitializeKeys(int secretKey = -1)
        {
            return "AES uses a secret key.
                   Initializing the  key for AES...";
        }
    }
}

Listing 5-7Using Tupple Patterns

位置模式

一些应用和实现使用了Deconstruct方法。此方法的目的是将属性解构为离散变量。要使用位置模式,Deconstruct方法应该是可访问的,这样我们将能够查看特征对象的属性,并使用这些属性来生成模式。清单 5-8 显示了CryptoAlgorithms类,它包括一个Deconstruct方法,以便为prvKey(私钥)和pubKey(公钥)创建一个离散变量。清单 5-8 中的代码展示了位置模式在密码学领域的应用。结果如图 5-5 所示。

img/493660_1_En_5_Fig5_HTML.jpg

图 5-5

列表结果 5-8

using System;

namespace PositionalPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            KeysAdministration k1 = new
               KeysAdministration("This is the private key",
                                 "This is the public key");
            KeysAdministration k2 = new
                 KeysAdministration("This is the private key", null);
            KeysAdministration k3 = new
               KeysAdministration(null, "This is the public key");

            CryptographicAlgorithm cryptoAlg;

            cryptoAlg = GetAlgorithmType(k1);
            Console.WriteLine("public and private keys: " +
                              cryptoAlg.ToString());
            cryptoAlg = GetAlgorithmType(k2);
            Console.WriteLine("just the private key: " +
                              cryptoAlg.ToString());
            cryptoAlg = GetAlgorithmType(k3);
            Console.WriteLine("no matching keys: " +
                              cryptoAlg.ToString());

            Console.ReadKey();

        }
        static CryptographicAlgorithm
               GetAlgorithmType(KeysAdministration keys)
               => keys switch
        {
            var (privKey, pubKey) when
                   !string.IsNullOrEmpty(privKey) &&
                   !string.IsNullOrEmpty(pubKey)
                       => CryptographicAlgorithm.RSA,
            var (privKey, pubKey) when
                   !string.IsNullOrEmpty(privKey) &&
                   string.IsNullOrEmpty(pubKey)
                     => CryptographicAlgorithm.AES,
            _ => CryptographicAlgorithm.Unknown
        };
    }

    public enum CryptographicAlgorithm
    {
        Unknown,
        RSA,
        AES,
    }

    public class KeysAdministration
    {
        public string prvKey { get; }
        public string pubKey { get; }

        public KeysAdministration(string PrivateKey,
                                  string PublicKey)
                => (prvKey, pubKey) = (PrivateKey, PublicKey);
        public void Deconstruct(out string PrivateKey,
                                out string PublicKey)
                => (PrivateKey, PublicKey) = (prvKey, pubKey);
    }
}

Listing 5-8Positional Patterns

使用声明

使用声明表示一种在变量(或其类型)前面有using关键字的变量声明。例如,让我们考虑清单 5-9 中的例子,它将消息的加密写入文件。输出如图 5-6 所示。

img/493660_1_En_5_Fig6_HTML.jpg

图 5-6

列表结果 5-9

using System;
using System.Collections.Generic;
using System.Linq;

namespace Declarations
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> encryptedMessageLines = new
                                     List<string>();

            encryptedMessageLines.Add("Option 1 key:
                                  sdkjegiorjgvldmgkA64");
            encryptedMessageLines.Add("This is the message to
                    be encrypted: This is an example of using
                    declarations in C# 8.0.");
            encryptedMessageLines.Add("Option 2 key:
                                  l$klj4grg565j");

            Console.Write("The number of lines skipped: ");
            Console.WriteLine(WriteEncryptedMessages(
               encryptedMessageLines.AsEnumerable<string>())
                                    .ToString());

            Console.ReadKey();
        }

        static int WriteEncryptedMessages(IEnumerable<string>
                                        encryptedMessageLines)
        {
            using var fileWithEncryption = new
             System.IO.StreamWriter("Message.txt");

            //** A note to be done on how we will declare
            //** lines_to_be_skipped after the using
            //** statement.

            int lines_to_be_skipped = 0;

            foreach (string lineMessage in
                     encryptedMessageLines)
            {
                if (!lineMessage.Contains("key"))
                {
                    fileWithEncryption.WriteLine(lineMessage);
                }
                else
                {
                    lines_to_be_skipped++;
                }
            }
            //** Notice how the skipped lines
            //** are the main subject here

            return lines_to_be_skipped;

            //** the file will be disposed once
            //** the compiler reached here
        }
    }
}

Listing 5-9Using Declarations

在图 5-6 中,我们获得了两行,因为列表encryptedMessageLines有两行包含这行代码中提到的字符串key:

if (!lineMessage.Contains("key")) from the WriteEncryptedMessages method

为了检查从encryptedMessageLines开始的两行是否确实被跳过,看一下图 5-7 ,它显示了文本被写入的Message.txt文件的内容。如果没有指定完整路径,那么这个文件可以在项目的bin > Debug > netcoreapp3.1文件夹中找到(如果项目使用的是。网芯 3.1;否则路径是相似的)。

img/493660_1_En_5_Fig7_HTML.jpg

图 5-7

清单 5-9 中使用的文件内容

指数和范围

索引范围为访问序列中的单个元素或范围提供了简短而精确的语法。

  • C# 8.0 提供了对两种新类型和操作符的支持,比如System.Index被定义为序列中的索引。

  • 操作符^末尾的索引指定一个索引在序列的末尾是相对的。

  • System.Range定义一个已声明序列的子范围。

  • 用于范围的运算符,指定操作数范围内的开始和结束。

在清单 5-10 中,我们考虑一个从开始到结束都有索引的数组。

var cryptograms = new string[]
{
                       // beginning index     ending index
    "ghdghdg",         // 0                   ⁹
    "gfhdgfhdgfh",     // 1                   ⁸
    "hsfgd",           // 2                   ⁷
    "dg545ghfd44",     // 3                   ⁶
    "fg435ffdgsdfg",   // 4                   ⁵
    "gdsfg4refgs",     // 5                   ⁴
    "54tgt4gv",        // 6                   ³
    "ge43fsda",        // 7                   ²
    "fdgsdef"          // 8                   ¹
};

Listing 5-10Working with Indexes

如您所见,您可以返回¹索引,如下例所示,列出了 5-11 。

//** this will return the cryptogram value "fdgsdef"
Console.WriteLine($"The last cryptogram (encrypted message) has
                        the following value {cryptograms[¹]}");

Listing 5-11Returning ¹ Index

在清单 5-12 中,我们用密码"gfhdgfhdgfh," "hsfgd,""dg545ghfd44."创建了一个子范围,这包括cryptograms[1]cryptograms[3]。注意,element cryptograms[4]不在这个范围内。

var encryptedTexts = cryptograms[1..4];

Listing 5-12Creating Subranges

在清单 5-13 中,我们使用密码"ge43fsda""fdgsdef."创建了一个子范围,这将包括cryptograms[²]cryptograms[¹]。注意,这个指数是cryptograms[⁰]

var encryptedTexts = cryptograms[²..⁰];

Listing 5-13Another Subrange

在清单 5-14 中,我们为开始、结束或者在某些情况下为两者创建了一个在结束时开放的范围。

//** contains "ghdghdg" through "fdgsdef".
var encryptedTexts = cryptograms[..];

//** contains "ghdghdg" through "dg545ghfd44"
var firstEncryptedText = cryptograms[..4];

//** contains "ghdghdg", "ge43fsda" and "fdgsdef"
var lastEncryptedText = cryptograms[6..];

Listing 5-14Open Range

将所有这些放在一起,我们得到了清单 5-15 中的代码,结果如图 5-8 所示。

img/493660_1_En_5_Fig8_HTML.jpg

图 5-8

指数和范围输出

using System;

namespace IndicesRanges
{
    class Program
    {
        static void Main(string[] args)
        {
            var cryptograms = new string[]
            {
                                    // beginning index     ending index
                "ghdghdg",          // 0                   ⁹
                "gfhdgfhdgfh",      // 1                   ⁸
                "hsfgd",            // 2                   ⁷
                "dg545ghfd44",      // 3                   ⁶
                "fg435ffdgsdfg",    // 4                   ⁵
                "gdsfg4refgs",      // 5                   ⁴
                "54tgt4gv",         // 6                   ³
                "ge43fsda",         // 7                   ²
                "fdgsdef"           // 8                   ¹
            };

            //** this will return the cryptogram value "fdgsdef"
            Console.WriteLine($"The last cryptogram (encrypted message) has the following value { cryptograms[¹]}");

            Console.Write("\n\n" + "Example 1 ~encryptedTexts~: ");
            var encryptedTexts = cryptograms[1..4];
            for (int i=0;i<encryptedTexts.Length;i++)
            {
                Console.Write(encryptedTexts[i] + " ");
            }

            Console.Write("\n\n" + "Example 2 ~encryptedTexts~: ");
            encryptedTexts = cryptograms[²..⁰];
            for (int i = 0; i < encryptedTexts.Length; i++)
            {
                Console.Write(encryptedTexts[i] + " ");
            }

            //** contains "ghdghdg" through "fdgsdef".
            Console.Write("\n\n" + "Example 3 ~encryptedTexts~: ");
            encryptedTexts = cryptograms[..];
            for (int i = 0; i < encryptedTexts.Length; i++)
            {
                Console.Write(encryptedTexts[i] + " ");

            }

            //** contains "ghdghdg" through "dg545ghfd44"
            Console.Write("\n\n" + "Example 4 ~firstEncryptedText~: ");
            var firstEncryptedText = cryptograms[..4];
            for (int i = 0; i < firstEncryptedText.Length; i++)
            {
                Console.Write(firstEncryptedText[i] + " ");
            }

            //** contains "ghdghdg", "ge43fsda" and "fdgsdef"
            Console.Write("\n\n" + "Example 5 ~lastEncryptedText~: ");
            var lastEncryptedText = cryptograms[6..];
            for (int i = 0; i < lastEncryptedText.Length; i++)
            {
                Console.Write(lastEncryptedText[i] + " ");
            }

            Console.ReadKey();
        }
    }
}

Listing 5-15Example of Functional Indices and Ranges

零合并赋值

C# 8.0 提供了一个新特性,可以显著提高表示加密算法的代码的质量和性能。运算符??=(参见清单 5-16 )可用于将右操作数的值赋给左操作数,前提是左操作数的返回输出被评估为null

下面,我们将把所有的东西放在一起,并展示如何将零合并赋值用于加密应用。见清单 5-16 和图 5-9 。

img/493660_1_En_5_Fig9_HTML.jpg

图 5-9

列表结果 5-16

using System;
using System.Collections.Generic;

namespace NullCoalescingAssignment
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> cryptograms = null;
            string val = null;

            cryptograms ??= new List<string>();
            cryptograms.Add(val ??= "fdsfasdf");
            cryptograms.Add(val ??= "dsfasdfads4234");

            //** output: "fdsfasdf" and "fdsfasdf"
            Console.WriteLine(string.Join(" ", cryptograms));

            //** output: "fdsfasdf"
            Console.WriteLine(val);

            Console.ReadKey();
        }
    }
}

Listing 5-16Operator ??=

非托管构造类型

就加密算法和安全解决方案的实现而言,非托管构造类型可能是一个非常有趣的话题。在 C# 7.3 中,构造类型不能是非托管类型(参见清单 5-17 )。当我们使用变量和不同类型来实现加密算法时,这非常有用,特别是当大小在它们的表示中起主要作用时。见清单 5-17 和图 5-10 。

img/493660_1_En_5_Fig10_HTML.jpg

图 5-10

列表结果 5-17

using System;

namespace TheUnmanagedTypes
{
    class Program
  {
        static void Main(string[] args)
     {
      ShowTypeVariableSize<CryptographicAlgorithms<int>>();
      ShowTypeVariableSize<CryptographicAlgorithms<double>>();
      Console.ReadKey();
     }

        public struct CryptographicAlgorithms<T>
        {
            public T prvEncKey;
            public T pubEncKey;
        }

private unsafe static void ShowTypeVariableSize<T>()
where T : unmanaged
        {
            Console.WriteLine($"{typeof(T)} represents an
                           unmanaged type and the size is
                           { sizeof(T) } bytes");
        }
    }
    //** The resulted outputed is:
    //** CryptographicAlgorithms`1[System.Int32] is unmanaged.
    //** The size is 8 bytes
    //** CryptographicAlgorithms`1[System.Double] is
    //** unmanaged.
    //** The size is 16 bytes
}

Listing 5-17Unmanaged Type Example (According to C# 7.3)

如果我们处理泛型结构,我们可以处理非托管和非托管构造类型。清单 5-17 中的例子定义了一个通用结构CryptographicAlgorithms<T>并展示了一个非托管构造类型的例子。如果我们想要一个非托管类型的例子,那就是CryptographicAlgorithms<object>.这不是非托管的,因为它有以对象类型为特征的字段,这是非托管的完美例子。如果我们希望所有构造的类型都是非托管类型,那么我们需要使用非托管约束(参见清单 5-18 )。

public struct CryptographicAlgorithms<T> where T : unmanaged
{
    public T prvEncKey;
    public T pubEncKey;
}

Listing 5-18Using an Unmanaged Constraint (According to C# 7.3)

基于清单 5-18 中的例子,CryptographicAlgorithms<T>类型是从 C# 8.0 和更高版本开始的非托管类型。正如你所看到的,对于一些非托管类型,我们可以声明一个指针指向一个声明为这种类型的变量,或者在堆栈上适当分配一个内存块(参见清单 5-19 )。为此,我们需要关注System.Span [2 。输出见图 5-11 。

img/493660_1_En_5_Fig11_HTML.jpg

图 5-11

清单 5-19 的结果

using System;

namespace MemoryAllocation
{
    class Program
    {
        static void Main(string[] args)
        {
            Random r = new Random();
            int k1 = r.Next(0, 255), k2 = r.Next(0, 255),
                k3 = r.Next(0, 255), k4 = r.Next(0, 255),
                k5 = r.Next(0, 255);

            Span<int> keys = stackalloc[] { k1, k2, k3, k4, k5 };
            Console.Write("The keys are: ");
            for (int i = 0; i < keys.Length; i++)
                Console.Write(keys[i] + " ");
            var ind = keys.IndexOfAny(stackalloc[] { k3, k5});
            Console.WriteLine("\n \n" + ind);  // output: 1

            Console.ReadKey();
        }
    }
}

Listing 5-19Allocation of a Memory Block on the Stack

结论

在本章中,我们介绍了 C# 8.0 最重要的新特性,这些特性可以在加密算法和安全方案的实现过程中使用。我们介绍的新特性集中在加密解决方案的两个主要方面,它们是

  • 代码质量:从传统的源代码转向现代的编写源代码的角度

  • 通过使用高级编程概念提高源代码的性能:经典与现代代码,使用优雅和规范的源代码重写大多数密码原语(如 RSA、AES、DES、Blowfish、Twofish 等)。)关注 lambda 表达式和 LINQ 语言

  • 实施过程中使用的变量大小,并确保分配了适当的大小:分配内存大小并使用适当的大小和类型非常重要,尤其是当应用受到攻击时(软件混淆、缓冲区溢出等)。).这种攻击非常棘手,一旦被利用,会对软件应用造成严重破坏。

文献学

  1. C# 语言版本控制。网上有: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version

  2. 跨度结构。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=netcore-3.1

  3. C# 语言版本控制。网上有: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version

六、安全编程指南

介绍

当软件应用有漏洞时,对应用开发者来说意味着高成本。一些组织为每个安全事件支付超过 50 万美元。用于消除软件应用中的漏洞的工作应该集中在安全编程上,避免在生产阶段部署任何漏洞。

编写安全的源代码是一项艰巨的任务。理解正在编写的代码的含义并有一个需要检查的东西的清单是非常重要的。该清单将帮助开发人员针对众所周知的安全问题快速验证他们的代码。通常,验证是由安全团队完成的,而不是软件开发人员或工程师。软件开发人员不能客观地对待他们自己的代码。

清单的想法应该从以下想法开始:验证将处理其域外数据的源代码,并考虑用户输入、网络通信、二进制文件的处理、从数据库管理系统或服务器接收输出等等。

当我们使用一个软件应用时(不管这个应用是桌面应用、web 应用还是移动应用),认为这个应用是安全的,因为它是由一个知名公司开发的,这只是一个神话。不要相信这种想法。依赖这种想法的公司最终会在安全事故、维护、咨询和审计会议上花费大量的预算。

软件应用有两种工作环境,在每种环境中它的行为是不同的。在一个公司中处于分析和开发过程中的软件应用代表了它的信任圈(至少,大多数公司都是这样想的,并且他们乐于认为他们的基础设施非常能够抵御安全攻击)。软件应用在信任圈中的行为代表了开发和测试应用的最关键环境。没有一个开发人员、IT 安全官员或软件分析师会黑掉自己的代码。为此,一些公司,如微软,曾经有软件开发测试工程师(sdet),他们的任务是破解或试图破解由软件开发工程师(SDE)编写的代码。这个环境是舒适的。一旦应用离开舒适区,进入真实环境,问题就开始出现了。信任边界很难同时也很容易被划定,并且在舒适区和真实区之间创建一个界限。这不是一项容易完成的任务,尤其是如果这些应用运行在虚拟化基础架构、云或大数据环境中。

在舒适区中,恶意的最终用户代表安全威胁。恶意终端用户将攻击软件应用的机密性和/或完整性。其中一个有趣的方法和概念是软件混淆

安全编程清单

在本节中,我们将讨论并提出一个安全的编码清单(也可以将其视为一个过程)。表 6-1 显示了这种清单的一个例子,它可以根据你的需要进行开发。该清单包含了用 C# 编写代码时可以检查的项目的最小示例,无论它是在 Windows 还是 Linux 操作系统上。开发人员经常使用的一种做法是隐藏警告,这是没有好处的。

表 6-1

安全编程清单示例

|

编号#

|

要检查的项目

|

描述

|

是/否

|

笔记

|
| --- | --- | --- | --- | --- |
| one | 编译器警告 |   |   |
| 确保 GCC 编译器将输出,并为接收针对下列项目列出的潜在错误的通知设置了一个标志。对于更多的标志及其定义和操作,建议遵循编译器选项来请求或抑制警告部分 [1 ]。如果要实现复杂的加密算法和安全方案,这将非常有用。 |   |   |
| Two | 处理字符串时分配足够的内存作为缓冲内存。 |   |   |
| 检查以下函数,查看当复制过程完成直到满足 NULL 时,目标缓冲区是否有上限。为了避免这种情况,建议在将数据复制到目标缓冲区之前,为其分配足够的内存空间。 |   |   |
| three | 检查系统安全的直接破坏。 |   |   |
| 检查不可信的输入会直接破坏应用的安全性。通过这一步,您将保护应用免受恶意用户和攻击者使用元字符试图利用程序的攻击。 |   |   |
| four | 检查参数大小是否错误并得到意外结果。 |   |   |
| 当编写复杂的程序时,比如第二章 2 中清单 2-9 中 SHA-256 的实现,给一个参数分配错误的大小或者进行错误的算术运算会导致严重的缺陷,应该立即提供修复。确保在目标端为参数分配的大小相同。作为最佳实践,尤其是在实现加密算法时,最好使用类型的返回大小。保持类型安全,不要造成溢出。 |   |   |
| five | 检查是否分配了过多的内存。 |   |   |
| 分配过多的内存和外部参数代表了一定部分的大小。这意味着处理一个错误的内存分配,你将经历拒绝服务的结果。为了避免这种情况发生,最好遵循以下标准。 |   |   |
| six | 避免投错。 |   |   |
| 避免像下面这样编码。编译器会认为内存分配会返回一个int,这是完全不正确的。它会产生一个漏洞,很容易被黑客利用。 |   |   |
| seven | 避免变量参数表。 |   |   |
| 当您实现基于字符串的安全方案时,您可能会遇到一种新类型的问题,安全分析师或有道德的黑客在执行测试时喜欢玩这种问题。有道德的黑客通常用来检查不可信数据的一个简单测试是检查函数是否允许变量作为参数或自变量的列表,如Console.WriteLine()MessageBox.Show()。不可信数据(由有道德的黑客创建)直接用作字符串格式,而不是参数。对于任何类似的情况,请遵循以下逻辑。 |   |   |
| eight | 同文件操作 |   |   |
| 在加密操作期间处理文件时,使用mkstemp(). |   |   |
| nine | 文件权限 |   |   |
| 不是每个人都有能力读写文件。为了避免给文件分配错误的权限,要养成使用FileIOPermission类的习惯。 |   |   |
| Ten | 避免使用代码访问安全(CAS) |   |   |
| CAS 使用主程序运行过程中可以利用的资源。这种资源的例子是 XML 文件、数据库、图像、设置和配置文件等。因为不建议使用 CAS,所以它在中不受支持。网芯, .NET 5 或更高版本。一般来说,高于 7.0 的 C# 版本不支持 CAS。 |   |   |
| Eleven | 避免使用部分可信的代码。 |   |   |
| 这些程序集代表了应用中的一个脆弱点,它们很容易被利用和覆盖,以便访问函数和方法的核心以及主代码。 |   |   |
| Twelve | 避免使用 AllowPartiallyTrustedCaller 属性。 |   |   |
| 从……开始 .NET Framework 4 中,新的安全规则影响了AllowPartiallyTrustedCallersAttribute属性的行为。从……开始 .NET Core,不再支持部分受信任的代码。建议使用. NET 4 库,并坚持使用 .NET 4 安全模型,并在需要的地方使用适当的SecurityCritical, SecuritySafeCriticalSecurity Transparent属性。 |   |   |
| Thirteen | 避免使用 .NET 远程处理。 |   |   |
| .NET Remoting 是一种技术,它使得一个被称为远程对象的对象可以跨不同的远程边界使用,这些边界基于不同的应用域(AppDomain)、进程或在网络内或通过网络连接的不同计算机。由于这一事实,进程或计算机(网络)可被利用,对象(远程对象)可被覆盖,以便恶意访问应用和应用的资源。这种情况出现在 [21 ]中。 |   |   |
| 14 | 避免使用 DCOM(分布式组件对象模型)。 |   |   |
| DCOM 代表一种编程结构,它允许计算机通过网络在另一台计算机上执行程序,就像程序在本地运行一样。通过中间人攻击,攻击者可以控制通信信道,并攻击应用与其计算机之间的通信。 |   |   |
| 15 | 避免使用二进制格式化程序。 |   |   |
| BinaryFormatter类型从 .NET Framework 是非常危险的,在数据处理完成时不推荐使用。应用应该立即停止使用BinaryFormatter,即使开发人员认为他们正在处理的数据是可信的。的确,BinaryFormatter是不安全的,但某些方面可以变得安全。不过还是建议完全避免 [20 ]。 |   |   |

在后面的部分中,我们将讨论应用于加密算法开发过程的最重要的规则。每条规则在指南中都有很好的解释。

证书编码标准

CERT 编码标准只为 ISO/IEC 14882-2014 标准定义的 C++编程语言版本开发,但是一些编码标准可以成功地用于其他编程语言,如 C#、Java 或 Python。

编码标准组织得非常好,它遵循以下结构:标识符、不符合的代码示例和符合的解决方案、异常、风险评估、自动检测、相关漏洞和相关指南 [7 ]。

在接下来的部分中,我们将检查结构的每一项,并解释主要目标和目的。

标识符

每个标识符有三个部分:

  • 三个字母的助记符,表示标准中的部分。

  • 介于 00 和 99 之间的两位数字的数值。

  • 与之关联的语言,用后缀(-CPP,-C,-J,-PL)表示。

    • –CPP:SEI CERT c# 编码标准 [7

    • –C:SEI CERT C 编码标准 [8

    • –J:SEI CERT Oracle Java 编码标准 [9 ]

    • –PL:SEI CERT Perl 编码标准 [10 ]

三个字母的助记符用于对相关编码实践进行分组,并指出相关编码属于哪个类别。

不兼容的代码示例和兼容的解决方案

不合规代码的例子显示了违反准则的代码。记住这些只是例子,这一点非常重要。示例中所有外观的移除过程并不意味着我们正在分析的代码符合 SEI CERT 标准。

规则的例外

例外具有信息性,不要求遵循。任何规则都可以有一组例外情况,这些例外情况详细说明了在哪些情况下不必遵循指南来确保软件的安全性、可靠性。

与任何类型的异常一样,编程语言并不重要,原理是一样的。有必要格外注意异常,捕捉任何可能的异常,并从中吸取教训。不要忽视他们。不要认为一种编程语言是完美的,它没有任何漏洞或某些可被利用的门。

风险评估

对于 CERT C++编码标准中的每个指南,都有一个指定的风险评估部分。风险评估部分的目的是向软件开发人员提供不遵守或不处理特定规则或建议的潜在后果。风险评估看起来像一个指标,其主要目的是帮助软件应用和复杂项目的补救过程。

每个规则和建议都有一个优先级。为了分配一个优先级,建议了解一下 IEC 60812 [11 ]。使用一个以三种分析类型为特征的指标来评估和分配优先级:故障模式、影响和关键程度。每个规则还将有一个值,该值被分配在 1 到 3 之间,例如严重性、可能性和补救成本(参见表 6-2 )。

表 6-2

为每个规则 [7 ]赋值

|

严重性–如果忽略该规则,会有什么后果?

|
| --- |
|

价值

|

意义

|

不同漏洞的示例

|
| --- | --- | --- |
| one | 低的 | 拒绝服务攻击,意外终止 |
| Two | 中等 | 违反数据完整性,无意泄露信息 |
| three | 高的 | 运行随机代码 |
| 可能性–从统计角度来说,通过避免和忽略规则规范而在代码中引入缺陷,从而导致可能被恶意用户利用的漏洞的可能性有多大? |
| | 定义 |
| one | 不太可能的 |
| Two | 很可能的事 |
| three | 可能的 |
| 补救成本–遵守规则的成本是多少? |
| | 定义 | 检测 | 校正 |
| one | 高的 | 指南 | 指南 |
| Two | 中等 | 自动的 | 指南 |
| three | 低的 | 自动的 | 自动的 |

对于每条规则,这些值会相乘。表 6-3 中的指标为您提供了一种方法,可用于确定应用中规则的优先级。值从 1 到 27。在所有 27 个值中,只有 10 个不同的值出现,并且在大多数情况下是可用的:1、2、3、4、6、8、9、12、18 和 27。表 6-3 显示了优先级和级别的可能解释和含义。

表 6-3

级别和优先级 [7

|

水平

|

优先

|

可能的解释

|
| --- | --- | --- |
| 腰神经 2 | 12, 18, 27 | 严重程度高,可能修复成本低 |
| L2 | 6, 8, 9 | 中等严重性,便携式,中等修复成本 |
| L3 | 1, 2, 3, 4 | 严重性低,不太可能,维修费用高 |

自动检测

规则和建议包含描述自动检测过程的部分。上述部分提供了多套工具,可用作自动诊断违规的分析器。安全编程验证套件 [12 ]可用于测试分析仪提供违反 ISO/IEC TS 17961:2013[14]规定的规则的诊断信息的能力,这与 SEI CERT C 编码标准 [13 ]的规则相关。

相关指南

根据标准,当开发软件应用时,这个部分有一个特殊的槽。它还包含链接、技术规范和指南集,如“信息技术-编程语言、其环境和系统软件接口-C 安全编程规则[14”;信息技术-编程语言-通过语言选择和使用避免编程语言漏洞的指南[15];MISRA C# 2008:在关键系统中使用 C# 语言的指南[16];和 *CWE IDs 在米特里的常见弱点枚举(CWE) * [17 ]”18

规则

在接下来的几节中,我们将给出一个主要规则的简要概述,这些规则非常适用于使用 C# 实现加密算法和安全方案。尤其是有了新版本的 it,最好了解以下规则。请注意,我们将只研究 10 条规则中的 6 条。指南 [19 ]中提供了所有解释和示例。

对于某些规则,也有一些来自 C 编程语言的规则适用于 C#。以下规则可用于表 6-1 中说明的程序。

任何信息安全官、安全分析师、道德黑客等的职责。就是通过设计这样一个清单来改进代码。此外,开发人员在开发关键的加密算法时,也可以使用该清单作为指南。建议对算法中非常脆弱的部分进行代码审查,并确保尽可能遵守规则(规则 01、规则 02、规则 03、规则 05、规则 06 和规则 07)。

遵循这些规则将使您作为安全分析师或有道德的黑客对安全机制(加密算法、安全协议、安全方案和其他加密原语)的正确实施以及常见漏洞的消除有一定程度的信任。参见表 6-4 至 6-9 。

表 6-9

规则 07–输入/输出 [19

|

规则

|

在 C# 中应用

|

标题

|
| --- | --- | --- |
| 导线 50-CPP | Y | 在没有插入定位调用的情况下,不要交替输入和输出文件流。 |
| 导线 51-CPP | Y | 不再需要文件时将其关闭。 |
| 电线 30-C | Y | 从格式字符串中排除用户输入。 |
| 32-C 导线 | Y | 不要在仅适用于文件的设备上执行操作。 |
| 34-C 导线 | Y | 区分从文件中读取的字符和 EOF 或 WEOF。 |
| 线 38-C | 普通 | 不要复制文件对象。 |
| 电线 39-C | Y | 在没有插入刷新或定位调用的情况下,不要从流中交替输入和输出。 |
| 42-C 导线 | Y | 不再需要文件时将其关闭。 |
| 电线 44-C | 普通 | 仅使用从fgetpos()返回的fsetpos()值。 |
| 导线 45-C | 普通 | 访问文件时避免 TOCTOU 竞争条件。 |
| 导线 46-C | Y | 不要访问关闭的文件。 |
| 导线 47-C | Y | 使用有效的格式字符串。 |

表 6-8

规则 06–内存管理 [19

|

规则

|

在 C# 中应用

|

标题

|
| --- | --- | --- |
| MEM50-CPP | Y | 不要访问释放的内存。 |
| MEM51-CPP | Y | 正确释放动态分配的资源。 |
| MEM52-CPP | Y | 检测和处理内存分配错误。 |
| MEM53-CPP | Y | 手动管理对象生存期时,显式构造和析构对象。 |
| MEM54-CPP | Y | 为新的放置提供正确对齐的指针,以提供足够的存储容量。 |
| MEM55-CPP | Y | 遵守替换动态存储管理要求。 |
| MEM56-CPP | Y | 不要将已经拥有的指针值存储在不相关的智能指针中。 |
| MEM57-CPP | Y | 避免对过度对齐的类型使用默认运算符new。 |
| MEM30-C | Y | 不要访问释放的内存。 |
| MEM31-C | Y | 不再需要时释放动态分配的内存。 |
| MEM34-C | Y | 仅动态分配空闲内存。 |
| MEM35-C | Y | 为对象分配足够的内存。 |
| MEM36-C | 普通 | 不要通过调用realloc()来修改对象的对齐。 |

表 6-7

规则 05–字符和字符串 [19

|

规则

|

在 C# 中应用

|

标题

|
| --- | --- | --- |
| STR50-CPP | Y | 确保字符串存储有足够的空间来存储字符数据和空终止符。 |
| STR52-CPP | 普通 | 使用有效的引用、指针和迭代器来引用basic_string的元素。 |
| STR53-CPP | Y | 范围检查元素访问。 |
| STR30-C | Y | 不要试图修改字符串文字。 |
| STR31-C | Y | 确保字符串存储有足够的空间来存储字符数据和空终止符。 |
| STR32-C | Y | 不要将非空字符序列传递给需要字符串的库函数。 |
| STR34-C | Y | 在转换为更大的整数之前,将字符转换为无符号字符。 |
| STR37-C | Y | 字符处理函数的参数必须可以表示为无符号字符。 |
| STR38-C | Y | 不要混淆窄和宽的字符串和函数。 |

表 6-6

规则 03–整数 [19

|

规则

|

在 C# 中应用

|

标题

|
| --- | --- | --- |
| INT50-CPP | Y | 不要强制转换为超出范围的枚举值。 |
| INT30-C | Y | 确保无符号整数运算不换行。 |
| INT31-C | Y | 确保整数转换不会导致数据丢失或被误解。 |
| INT32-C | Y | 确保对有符号整数的操作不会导致溢出。 |
| INT33-C | Y | 确保除法和余数运算不会导致被零除的错误。 |
| INT34-C | Y | 不要将表达式移位负数位数或大于或等于操作数中存在的位数。 |
| INT35-C | Y | 不要用不匹配的语言链接调用函数。 |

表 6-5

规则 02–表达式 [19

|

规则

|

在 C# 中应用

|

标题

|
| --- | --- | --- |
| EXP50-CPP | Y | 不要依赖于副作用的评估顺序。 |
| EXP51-CPP | Y | 不要通过不正确类型的指针删除数组。 |
| EXP52-CPP | Y | 不要依赖未赋值操作数的副作用。 |
| EXP53-CPP | Y | 不要读取未初始化的内存。 |
| EXP54-CPP | Y | 不要访问超出其生存期的对象。 |
| EXP56-CPP | Y | 不要用不匹配的语言链接调用函数。 |
| EXP57-CPP | Y | 不要强制转换或删除指向不完整类的指针。 |
| EXP60-CPP | Y | 不要跨越执行边界传递非标准布局类型的对象。 |
| EXP61-CPP | Y | lambda 对象不能比它的任何引用捕获对象活得长。 |
| EXP62-CPP | Y | 不要访问对象表示中不属于对象值表示的位。 |
| EXP63-CPP | Y | 不要依赖移出对象的值。 |

表 6-4

规则 01–声明和初始化 [19

|

规则

|

在 C# 中应用

|

标题

|
| --- | --- | --- |
| DCL51-CPP | Y | 不要声明或定义保留标识符。 |
| DCL52-CPP | Y | 永远不要用 const 或 volatile 限定引用类型。 |
| DCL53-CPP | Y | 不要写语法不明确的声明。 |
| DCL54-CPP | Y | 重载分配和解除分配在同一范围内成对运行。 |
| DCL55-CPP | Y | 在跨越信任边界传递类对象时避免信息泄漏。 |
| DCL56-CPP | Y | 避免静态对象初始化期间的循环。 |
| DCL57-CPP | Y | 不要让异常从析构函数或释放函数中逃脱。 |
| DCL58-CPP | Y | 不要修改标准名称空间。 |
| DCL59-CPP | Y | 不要在头文件中定义未命名的命名空间。 |
| DCL60-CPP | Y | 遵守一个定义规则。 |
| DCL30-C | Y | 声明具有适当存储期限的对象。 |
| DCL39-C | Y | 在跨信任边界传递结构时避免信息泄漏。 |
| DCL40-C | Y | 不要创建同一函数或对象的不兼容声明。 |

规则 01。声明和初始化(DCL)

规则 02。表达式(表达式)

规则 03。整数(INT)

规则 05。字符和字符串(STR)

规则 06。内存管理(MEM)

规则 07。输入/输出

结论

在本章中,你学习了规则建议。您踏上了在开发加密算法和安全方案的过程中需要考虑的最重要的安全方面的旅程。

理解规则建议之间的区别非常重要。一般的想法是,与建议相比,规则必须遵循特定数量的标准,而建议代表了改进代码质量的建议。

你已经获得了大量的知识。在本章结束时,您现在能够执行源代码的安全性分析,创建安全的编码清单,筛选对您的应用至关重要的方面,并指导开发人员在实现加密算法和编写相关源代码时如何进行。

文献学

  1. 请求或禁止警告的 GCC 选项。网上有: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#Warning-Options

  2. HXprox_*,libHX–把事情做好。网上有: http://libhx.sourceforge.net/

  3. [ISO/IEC TR 24772:2013] ISO/IEC。信息技术——编程语言——通过语言选择和使用避免编程语言漏洞的指南。TR 24772-2013。ISO。2013 年 3 月。

  4. [ISO/IEC TS 17961:2012]ISO/IEC TS 17961。信息技术——编程语言及其环境和系统软件接口——C 安全编程规则。ISO。2012.

  5. [ISO/IEC 14882-2014]ISO/IEC 14882-2014。编程语言— C#,第四版。 2014 年。

  6. 鲍尔曼,2016 年。 SEI CERT C# 编码标准(2016 版) 435。

  7. SEI CERT C# 编码标准:网上有:https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=88046682(4 . 9 . 20 访问)。

  8. 编码标准。在线可用:https://wiki.sei.cmu.edu/confluence/display/c(4 . 9 . 20 访问)。

  9. Oracle Java 编码标准。在线可用:https://wiki.sei.cmu.edu/confluence/display/java(4 . 9 . 20 访问)。

  10. CERT Perl 编码标准。在线可用:https://wiki.sei.cmu.edu/confluence/display/perl(4 . 9 . 20 访问)。

  11. [IEC 60812 2006]国际电工委员会。系统可靠性分析技术——故障模式和影响分析程序(FMEA) ,第二版。(IEC 60812)。瑞士日内瓦:国际电工委员会,2006 年。

  12. 安全编程验证套件。网上有: https://github.com/SEI-CERT/scvs

  13. SEI CERT C 编码标准:开发安全、可靠、可靠系统的规则(2016 版),n.d. 534。

  14. [ISO/IEC TS 17961:2012]ISO/IEC TS 17961。信息技术——编程语言及其环境和系统软件接口——C 安全编程规则。ISO。2012.

  15. [ISO/IEC TR 24772:2013] ISO/IEC。信息技术——编程语言——通过语言选择和使用避免编程语言漏洞的指南。TR 24772-2013。ISO。2013 年 3 月。

  16. [米斯拉 2008]米斯拉有限公司。 MISRA C# 2008 在关键系统中使用 C# 语言的指南。ISBN 978-906400-03-3(平装本);ISBN 978-906400-04-0 (PDF)。2008 年 6 月。

  17. 斜接。常见弱点列举,1.8 版。2010 年 2 月。网上有: http://cwe.mitre.org/

  18. "这个编码标准是如何组织的."网上有: https://wiki.sei.cmu.edu/confluence/display/cplusplus/How+this+Coding+Standard+Is+Organized

  19. “规则。”网上有: https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=88046322

  20. BinaryFormatter 类。网上有: https://docs.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide

  21. “寻找和开发 .NET Remoting over HTTP,使用反序列化。网上有: www.nccgroup.com/uk/about-us/newsroom-and-events/blogs/2019/march/finding-and-exploiting-.net-remoting-over-http-using-deserialisation/

七、.NET 加密服务

在本章中,我们将讨论主要的服务和加密原语 .NET 框架和。提供给专业人士的核心网。了解开发技术必须提供哪些服务和加密原语非常重要,尤其是如果您不想从头开始开发加密算法和安全方案的话。

将涵盖以下主题:

  • 密码原语

  • 使用秘密密钥加密

  • 使用公钥加密

  • 数字签名

  • 哈希值

  • 随机数生成

  • 支持套件 B

  • 下一代加密技术(CHG)类别

通过互联网的通信本身并不安全,在这种情况下,需要使用加密来保证这种通信的安全性。这些网络实体之间的通信很可能被未授权的第三方读取,甚至被更改。正如您之前所看到的,加密的目的是保护数据不被未经授权的人查看或修改。在的帮助下。在. NET 框架中,我们有被设计用于System.Security.Cryptography(见第九章)的加密类,一个处理加密函数及其操作的命名空间。此外,我们正在处理非托管 CryptoAPI(微软加密 API)的包装器。与此同时,其他的都得到了充分的实施和相应的测试。好的一面是,一旦我们创建了一个特定加密算法类的新实例,密钥就会自动生成,以便尽可能容易地使用,并使用非常安全的默认属性。

也就是说,在下面几节中,我们将简要介绍支持的最重要的加密算法 .NET Framework (ClickOnce、Suite B 和 CNG),从 .NET 框架 3.5。

使用秘密密钥加密

基于密钥的加密算法在加密和解密过程中使用单个密钥。保证密钥的安全性以防止未经授权的方或服务是非常重要的。任何有权使用该密钥的未授权方都可以使用它来解密数据或加密其他数据,声称并冒充真正的授权方。

使用秘密密钥的加密也被称为对称加密,因为相同的密钥用于两个过程:加密和解密操作(见图 7-1 )。与基于公钥的算法相比,基于秘密密钥的加密算法非常快。同时,它们最适合对大型数据流进行加密编码。另一方面,我们有基于非对称加密的算法,如 RSA,它们的数学限制是基于需要加密多少数据。

img/493660_1_En_7_Fig1_HTML.jpg

图 7-1

对称加密

那个 .NET Framework 包含以下类,用于帮助专业人员使用相同的密钥实现加密和解密操作:

img/493660_1_En_7_Fig2_HTML.jpg

图 7-2

AES 执行

  • AesManaged [38 ](始于 .NET 框架 3.5)。参见清单 7-1 和图 7-2 中的实现。

  • RijndaelManaged41。参见清单 7-2 中的实现。

  • DESCryptoServiceProvider [39 。参见清单 7-3 中的实现。

  • RC2CryptoServiceProvider40。实现与清单 7-3 中的类似。

  • TripleDESCryptoServiceProvider42。

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

namespace AESExampleOfImplementation
{
    class AESExampleOfImplementation
    {
        public static void Main()
        {
            string genuineMessage = "Welcome to Apress!";

            //** Declare a new instance
            //** of the class AESManaged.
            //** With its help a new key
            //** and initialization vector is generated
            using (AesManaged aes_encryption =
new AesManaged())
            {
                //** the string is encrypted and
                //** stored as an array of bytes
                byte[] message_encrypted =
EncryptStringToBytes_Aes(genuineMessage,
aes_encryption.Key,
aes_encryption.IV);

                //** the decryption will take place as
                //** decrypting the bytes into a string
                string tripRound =
DecryptStringFromBytes_Aes(
message_encrypted,
aes_encryption.Key,
aes_encryption.IV);

                //** Shows in the console the original
                //** message and the data decrypted
Console.WriteLine("The original message is:
{0}", genuineMessage);

Console.WriteLine("The trip round is: {0}",
tripRound);

Console.WriteLine("The encrypted message is
(byte-by-byte view): {0}", PrintByteArray(message_encrypted));

                Console.WriteLine("The encrypted message is
(default view): {0}", Encoding.Default.GetString
(message_encrypted));

                Console.WriteLine("The encrypted message is
(UTF8 view): {0}", Encoding.UTF8.GetString(message_encrypted));

                Console.WriteLine("The encrypted message is
(UTF32 view): {0}",
Encoding.UTF32.GetString
(message_encrypted));
                Console.ReadKey();

            }
        }

       //** processing byte values to display them
     static string PrintByteArray(byte[] encrypted_message)
        {
            var build_string =
new StringBuilder("new byte[] { ");
            foreach (var each_byte in encrypted_message)
            {
                build_string.Append(each_byte + " ");
            }
            build_string.Append("}");
            return build_string.ToString();
        }

        static byte[] EncryptStringToBytes_Aes
(string genuineText,
 byte[] crypto_key,
 byte[] initializationVector)
        {
            //** verify the arguments
            if (genuineText == null ||
genuineText.Length <= 0)
               throw new ArgumentNullException("genuineText");

            if (crypto_key == null || crypto_key.Length <= 0)
                throw new ArgumentNullException("crypto_key");

 if (initializationVector == null ||
initializationVector.Length <= 0)
                throw new ArgumentNullException("IV");

            byte[] encryptionRepresentation;

            //** declare an AesManaged instance
            //** Create an AesManaged object
           //** the declaration should include the specified
           //** key and initialization vector.
            using (Aes aes_algorithm = Aes.Create())
            {
                aes_algorithm.Key = crypto_key;
                aes_algorithm.IV = initializationVector;

                //** do the stream transformation
               //** for this declare an ecnryptor
              //** using ICryptoTransform
                ICryptoTransform crypto_transformation =
aes_algorithm.CreateEncryptor
(aes_algorithm.Key, aes_algorithm.IV);

                //** use the streams and work with the
                //** encryption Create the streams
                //** used for encryption
                using (MemoryStream memoryStreamForEncryption
= new MemoryStream())
                {
                    using (CryptoStream cryptoStreamEncryption
 = new CryptoStream(memoryStreamForEncryption,
 crypto_transformation, CryptoStreamMode.Write))
                {
                        using (StreamWriter
  streamWriterForEncryption = new
                        StreamWriter(cryptoStreamEncryption))
                        {
                            //** write the entire volume of
                           //** data with the stream
streamWriterForEncryption.
Write(genuineText);
                        }
                        encryptionRepresentation =
    memoryStreamForEncryption.ToArray();
                    }
                }
            }

            //** Return the encrypted bytes from
 //** the memory stream.
            return encryptionRepresentation;
        }

        static string DecryptStringToBytes_Aes
(byte[] encryptedText,
 byte[] encryption_key,
 byte[] initialization_vector)
        {
            //** verify the arguments
            if (encryptedText == null ||
encryptedText.Length <= 0)
              throw new
ArgumentNullException("encryptedText");

            if (encryption_key == null ||
encryption_key.Length <= 0)
                throw new ArgumentNullException("Key");

 if (initialization_vector == null ||
initialization_vector.Length <= 0)
                throw new ArgumentNullException("IV");

            //** the string used to store
            //** the original decrypted text
            string original_text = null;

            //** declare an AesManaged instance
            //** using the encryption key
            //** and initialization vector
            using (Aes aes_algorithm = Aes.Create())
            {
                aes_algorithm.Key = encryption_key;
                aes_algorithm.IV = initialization_vector;

                //** do the stream transformation
               //** for this declare an
               //** encryptor using ICryptoTransform
                ICryptoTransform decrypt_transformation =
aes_algorithm.CreateDecryptor(
aes_algorithm.Key,
aes_algorithm.IV);

                //** use the streams and work
                //** with the encryption
                //** Create the streams used for encryption
                using (MemoryStream memoryStreamDecryption =
                       new MemoryStream(encryptedText))
                {
                    using (CryptoStream cryptoStreamDecryption
= new CryptoStream(
memoryStreamDecryption,
decrypt_transformation,   CryptoStreamMode.Read))
                    {
                        using (StreamReader
streamReaderDecryption =
                         new StreamReader(
cryptoStreamDecryption))
                        {
                  //** read the decrypted bytes from
                  //** the stream reader
                  //** and save it in
                 //** original_text variable
                           original_text =
streamReaderDecryption.
ReadToEnd();
                        }
                    }
                }
            }
            return original_text;
        }
    }
}

Listing 7-1

AES Implementation Using AesManaged

在清单 7-2 和图 7-3 中,您可以观察 Rijndael 是如何实现的。

img/493660_1_En_7_Fig3_HTML.jpg

图 7-3

RijndaelManaged 示例

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

namespace RijndaelManagedImplementationExample
{
    class RijndaelManagedImplementationExample
    {
        public static void Main()
        {
            try
            {
                string genuineMessage = "Folks, Welcome to Apress!";

                //** declare a new instance of the
                //** RijndaelManaged class with this
                //** instance a new key and
                //** initialization vector (IV)
                using (RijndaelManaged rijndeal_crypto = new RijndaelManaged())
                {
                    rijndeal_crypto.GenerateKey();
                    rijndeal_crypto.GenerateIV();

                    //** encrypt the message (string)
                    //** and store the content to an
                    //** array of bytes
                    byte[] encrypted = EncryptStringToBytes(genuineMessage, rijndeal_crypto.Key, rijndeal_crypto.IV);

                    //** Decrypt the bytes to a string
                    string tripRound = DecryptStringFromBytes(encrypted, rijndeal_crypto.Key, rijndeal_crypto.IV);

                    //** Display the original data
                    //** and the decrypted data
                    Console.WriteLine("Original Message:{0}", genuineMessage);
                    Console.WriteLine("Round Trip: {0}",tripRound);

                    Console.WriteLine("\nThe encrypted message is (byte - byt - byte view): {0}", PrintByteArray(encrypted));
                    Console.WriteLine("\nThe encrypted message is (default view): {0}", Encoding.Default.GetString(encrypted));
                    Console.WriteLine("\nThe encrypted message is (UTF8 view): {0}", Encoding.UTF8.GetString(encrypted));
                    Console.WriteLine("\nThe encrypted message is (UTF32 view): {0}", Encoding.UTF32.GetString(encrypted));

                    Console.ReadKey();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("There is an error:{0}", e.Message);
            }
        }

        //** processing byte values to display them
        static string PrintByteArray(byte[] encrypted_message)
        {
            var build_string = new StringBuilder("new byte[] { ");
            foreach (var each_byte in encrypted_message)
            {
                build_string.Append(each_byte + " ");
            }
            build_string.Append("}");
            return build_string.ToString();
        }

        static byte[] EncryptStringToBytes(string genuineText, byte[] encryption_key, byte[] initialization_vector)
        {
            //** verify the arguments
            if (genuineText == null ||
            genuineText.Length <= 0)
                throw new ArgumentNullException("genuineText");

            if (encryption_key == null ||
                encryption_key.Length <= 0)
                throw new ArgumentNullException("encryption_key");

            if (initialization_vector == null ||
                initialization_vector.Length <= 0)
                throw new ArgumentNullException("IV");

            byte[] encryption_content;

            //** Create an RijndaelManaged object
            //**  with the specified key and IV.
            using (RijndaelManaged rijndaelAlgorithm = new RijndaelManaged())
            {

                rijndaelAlgorithm.Key = encryption_key;

                rijndaelAlgorithm.IV = initialization_vector;

                //** Create an encryptor to perform
                //** the stream transform.
                ICryptoTransform encryptorTransformation = rijndaelAlgorithm.CreateEncryptor(rijndaelAlgorithm.Key, rijndaelAlgorithm.IV);

                //** Create the streams used for encryption
                using (MemoryStream memoryStreamEncrypt = new MemoryStream())
                {
                    using (CryptoStream cryptoStreamEncrypt = new CryptoStream(memoryStreamEncrypt, encryptorTransformation, CryptoStreamMode.Write))
                    {
                        using (StreamWriter streamWriterEncrypt = new StreamWriter(cryptoStreamEncrypt))
                        {

                            //** write the entire volume of
                            //** data to the stream
                            streamWriterEncrypt.Write(genuineText);
                        }
                        encryption_content = memoryStreamEncrypt.ToArray();
                    }
                }
            }

            //** get the encrypted bytes
            //** from the memory stream.
            return encryption_content;
        }

        static string DecryptStringFromBytes(byte[] encrypted_text, byte[] encryption_key, byte[] initialization_vector)
        {
            //** verify the arguments
            if (encrypted_text == null ||
                encrypted_text.Length <= 0)
                throw new ArgumentNullException("encrypted_text");

            if (encryption_key == null ||
                encryption_key.Length <= 0)
                throw new ArgumentNullException("encryption_key");

            if (initialization_vector == null ||
                initialization_vector.Length <= 0)
                throw new ArgumentNullException("initialization_vector");

            //** Declare the string used to hold
            //** the decrypted text.
            string original_text = null;

            //** Create an RijndaelManaged object
            //** with the specified key and IV.
            using (RijndaelManaged rijndael_algorithm = new RijndaelManaged())
            {
                rijndael_algorithm.Key = encryption_key;

                rijndael_algorithm.IV = initialization_vector;

                //** Create a decryptor to
                //** perform the stream transform.
                ICryptoTransform decryptionTransformation = rijndael_algorithm.CreateDecryptor(rijndael_algorithm.Key, rijndael_algorithm.IV);

                //** Create the streams used for decryption.
                using (MemoryStream memoryStreamDecryption = new MemoryStream(encrypted_text))
                {
                    using (CryptoStream cryptoStreamDecrypt = new CryptoStream(memoryStreamDecryption, decryptionTransformation, CryptoStreamMode.Read))
                    {
                        using (StreamReader streamReaderDecryption = new StreamReader(cryptoStreamDecrypt))
                        {
                            //** Read the decrypted bytes
                            //** from the decrypting stream
                            //** and place them in a string.
                            original_text = streamReaderDecryption.ReadToEnd();
                        }
                    }
                }
            }

            return original_text;
        }
    }
}

Listing 7-2

RijndaelManaged Implementation

img/493660_1_En_7_Fig6_HTML.jpg

图 7-6

存储器中的 DES 加密和解密

img/493660_1_En_7_Fig5_HTML.jpg

图 7-5

DES 加密结果

img/493660_1_En_7_Fig4_HTML.jpg

图 7-4

文件加密/解密的 DES 算法

  • 在下面的例子中,我们将实现 DES 算法的两种情况。尽管这个事实被克服了,但它是一个很好的例子,说明了加密算法和原语应该如何被实际对待。这两个实现与实际中存在的其余实现的不同之处在于,第一个示例(参见图 7-4 、图 7-5 和清单 7-3 )在文件内进行加密/解密,第二个示例(参见图 7-6 和清单 7-4 )在内存中进行加密/解密。当我们处理使用文件的复杂系统时(例如 Hadoop 系统或大数据环境的 clear JSON 文件),解决方案之一是以这样的方式设计加密原语,即我们正确地对文件或内存进行加密/解密,而不暴露可能影响业务的东西。
using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;

class DESSample
{
    static void Main()
    {
        try
        {
            //** instance of the DES algorithm and
            //** with help of Create() method
            //** an initialization vector is generated
            DES des_algorithm = DES.Create();

            //** declare a string for being encrypted
            string plaintext = "Welcome to Apress. Enjoy reading.";
            string file_name = "encrypted_file.txt";

            //** do the encryption to the file based on
            //** the file name, key, and initialization vector
            DesEncryptionToFile(plaintext, file_name, des_algorithm.Key, des_algorithm.IV);

            //** Decrypt the text from a file using the file name, key, and IV.
            string ciphertext = DesDecryptionFromFile(file_name, des_algorithm.Key, des_algorithm.IV);

            //** Show in the console the results
            Console.WriteLine("The message for encryption is: {0}", plaintext);
            Console.WriteLine("The message has been encrypted with success.");
            Console.WriteLine("\tCheck your file \"{0}\" at the following location: {1}", file_name, Path.GetFullPath("encrypted_file.txt"));
            Console.ReadKey();
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception.Message);
        }
    }

    public static void DesEncryptionToFile(String text_to_encrypt, String file_name, byte[] encryption_key, byte[] initialization_vector)
    {
        try
        {
            //** create the file or open the file if exist
            FileStream file_stream = File.Open(file_name, FileMode.OpenOrCreate);

            //** declare a DES object
            DES des_algorithm = DES.Create();

            //** use the key and initialization vector.
            //** pass them to the CryptoStream together with the file stream
            CryptoStream crypto_stream = new CryptoStream(file_stream,
                des_algorithm.CreateEncryptor(encryption_key, initialization_vector),
                CryptoStreamMode.Write);

            //** based on a crypto_stream create an instance of stream writer
            StreamWriter stream_writer = new StreamWriter(crypto_stream);

            //** take data and write it within the stream writer
            stream_writer.WriteLine(text_to_encrypt);

            //** make sure that the file stream, crypto stream and stream writer are closed
            stream_writer.Close();
            crypto_stream.Close();
            file_stream.Close();
        }
        catch (CryptographicException exception){
            Console.WriteLine("There is an error regarding your encryption cryptographic process: {0}", exception.Message);
        }
        catch (UnauthorizedAccessException exception){
            Console.WriteLine("There is an error regarding creating/accessing the file: {0}", exception.Message);
        }
    }

    public static string DesDecryptionFromFile(String file_name, byte[] decryption_key, byte[] initialization_vector)
    {
        try
        {
            //** create the file or open the file if exist
            FileStream file_stream = File.Open(file_name, FileMode.OpenOrCreate);

            //** declare a DES object
            DES des_algorithm = DES.Create();

            //** use the key and initialization vector.
            //** pass them to the CryptoStream together with the file stream
            CryptoStream crypto_stream = new CryptoStream(file_stream,
                des_algorithm.CreateDecryptor(decryption_key, initialization_vector),
                CryptoStreamMode.Read);

            //** based on a crypto_stream create an instance of stream reader
            StreamReader stream_reader = new StreamReader(crypto_stream);

            //** before decryption take place
            //** we need to read the data from the stream
            string val = stream_reader.ReadLine();

            //** make sure that the file stream, crypto stream and stream writer are closed
            stream_reader.Close();
            crypto_stream.Close();
            file_stream.Close();

            //** return the decryption value
            return val;
        }
        catch (CryptographicException cryptoException)
        {
            Console.WriteLine("There was a cryptographic error. Please see: {0}. Correct the error and try again.", cryptoException.Message);
            return null;
        }
        catch (UnauthorizedAccessException unauthorizedException)
        {
            Console.WriteLine("There was an error with the file (unauthorized/existance). Please, check: {0}", unauthorizedException.Message);
            return null;
        }
    }
}

Listing 7-3Example of Implementation of the DES Algorithm Using DESCryptoServiceProvider

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

class DESMemoryImplementationExample
{
    static void Main()
    {
        try
        {
            //** declare a DES instance and with Create()
            //** generate the key and initialization vector
            DES des_algorithm = DES.Create();

            //** declare a variable that contains the string to encrypt
            string data_to_encrypt = "Welcome to Apress. Enjoy your adventure.";

            //** use a memory buffer and proceed encrypting the text
            byte[] encrypted_data = EncryptionOfTextInMemory(data_to_encrypt, des_algorithm.Key, des_algorithm.IV);

            //** use the buffer and proceed with decryption and obtain the plaintext
            string decrypted_data = DecryptionOfTheTextFromMemory(encrypted_data, des_algorithm.Key, des_algorithm.IV);

            //** show in the console the encrypted and decrypted values
            Console.WriteLine("The original message is: {0}", data_to_encrypt);
            Console.WriteLine("\nThe encrypted message is (byte-by-byte view): {0}", PrintByteArray(encrypted_data));
            Console.WriteLine("\nThe encrypted message is (default view): {0}", Encoding.Default.GetString(encrypted_data));
            Console.WriteLine("\nThe encrypted message is (UTF8 view): {0}", Encoding.UTF8.GetString(encrypted_data));
            Console.WriteLine("\nThe encrypted message is (UTF32 view): {0}", Encoding.UTF32.GetString(encrypted_data));
            Console.WriteLine("\nThe original text is: {0}.", decrypted_data);

            Console.ReadKey();
        }
        catch (Exception general_exception)
        {
            Console.WriteLine(general_exception.Message);
        }
    }

    static string PrintByteArray(byte[] encrypted_message)
    {
        var build_string = new StringBuilder("new byte[] { ");
        foreach (var each_byte in encrypted_message)
        {
            build_string.Append(each_byte + " ");
        }
        build_string.Append("}");
        return build_string.ToString();
    }

    public static byte[] EncryptionOfTextInMemory(string data_to_encrypt, byte[] key_for_encryption, byte[] initialization_vector)
    {
        try
        {
            //** declare an instance for the memory buffer
            MemoryStream memory_stream = new MemoryStream();

            //** declare an instance of DES
            DES DESalg = DES.Create();

            //** declare an crypto stream instance and use
            //** the key used for encryption and the initialization vector
            CryptoStream crypto_stream = new CryptoStream(memory_stream,
                DESalg.CreateEncryptor(key_for_encryption, initialization_vector),
                CryptoStreamMode.Write);

            //** the string that has been passed will be converted to a byte array
            byte[] for_encryption = new ASCIIEncoding().GetBytes(data_to_encrypt);

            //** take the byte array and write it in the crypto stream
            crypto_stream.Write(for_encryption, 0, for_encryption.Length);

            //** don't forget to flush it
            crypto_stream.FlushFinalBlock();

            //** take the memory stream and write
            //** it as an array of bytes and store it accordingly
            byte[] stream_content_byteArray = memory_stream.ToArray();

            //** make sure to have the streams closed
            crypto_stream.Close();
            memory_stream.Close();

            //** the buffer with the encrypted content
            return stream_content_byteArray;
        }
        catch (CryptographicException cryptoException)
        {
            Console.WriteLine("There was an cryptographic error expected. Please see: {0}", cryptoException.Message);
            return null;
        }
    }

    public static string DecryptionOfTheTextFromMemory(byte[] data_for_decryption, byte[] decryption_key, byte[] initialization_vector)
    {
        try
        {
            //** declare an memory stream instance used for decryption
            //** and as parameter pass the encrypted data
            MemoryStream memory_stream_decryption = new MemoryStream(data_for_decryption);

            //** create an instance of DES object
            DES DESalg = DES.Create();

            //** declare an crypto stream instance based on
            //** the memory stream instance and pass as
            //** parameters the decryption and initialization vector
            CryptoStream crypto_stream_decryption = new CryptoStream(memory_stream_decryption,
                DESalg.CreateDecryptor(decryption_key, initialization_vector),
                CryptoStreamMode.Read);

            //** declare a buffer and we will use it
            //** to store the decrypted data
            byte[] from_encryption = new byte[data_for_decryption.Length];

            //** proceed reading the decrypted data from the crypto stream
            //** and store its content in a temporary buffer
            crypto_stream_decryption.Read(from_encryption, 0, from_encryption.Length);

            //** do the conversion of the buffer in a string
            //** and return its value
            return new ASCIIEncoding().GetString(from_encryption);
        }
        catch (CryptographicException cryptoException)
        {
            Console.WriteLine("There was an cryptographic error. Please see: {0}", cryptoException.Message);
            return null;
        }
    }
}

Listing 7-4Implementation of DES Encryption and Decryption in Memory

使用公钥加密

公钥加密使用私钥,私钥必须以未经授权方或恶意用户无法访问的方式秘密存储。此外,它使用一个任何人都可以使用的公钥。公钥私钥之间的关系是基于数学的。使用公钥加密的数据只能使用私钥解密。在这种情况下,已经使用私钥签名的数据可以仅使用公钥来验证。

让我们考虑两方(经典的称为 Alice 和 Bob,如图 7-7 所示)将使用所述的公钥加密:Alice 将生成一个由公钥和私钥组成的对。当 Bob 决定向 Alice 发送加密消息时,他将需要 Alice 的公钥,因此他会向 Alice 索要公钥。Alice 将通过不安全的网络通信信道向 Bob 发送她的公钥。鲍勃会用它来加密信息。Bob 将把加密的消息发送给 Alice,Alice 将使用她的私钥解密它。

img/493660_1_En_7_Fig7_HTML.jpg

图 7-7

公钥加密

以下列表显示了公钥和私钥算法之间的比较:

  • 基于公钥的算法使用固定的缓冲区大小。值得一提的是,密钥和基于它的算法使用一个表示为变量的缓冲区,其大小是可变的。

  • 关于数据链,公钥算法和对称密钥算法是有区别的。对称密钥算法将数据链接成流,而公钥算法没有这种能力。非对称操作和对称操作的流模型不同。

  • 使用公钥的加密使用更大的空间来表示密钥。与公钥加密相比,对称密钥加密使用的空间要小得多。

  • 公钥非常容易分发,因为不需要有安全机制。如有必要,有验证和检查发送者身份的机制。

  • 为了创建数字签名以验证和检查发送者的身份,可以成功地使用 RSA 和 DSA 等公钥算法。

  • 关于速度,如果我们与对称算法相比,公钥算法是非常慢的。它们被设计成不能加密大量数据。因此,只建议对少量数据使用公钥算法。

那个 .NET Framework 包含以下类,这些类已经包含了公钥加密算法和操作的实现:

img/493660_1_En_7_Fig8_HTML.jpg

图 7-8

RSA 示例加密

  • DSACryptoServiceProvider [1 。实现是相似的,它们可以用与清单 7-1 、 7-2 或 7-3 相同的方式来完成。

  • RSACryptoServiceProvider2。有关实施的示例,请参见清单 7-5 和图 7-8 。

  • ECDiffieHellman3。实现是相似的,它们可以用与清单 7-1 、 7-2 或 7-3 相同的方式来完成。

  • ECDiffieHellmanCng4。实现是相似的,它们可以用与清单 7-1 、 7-2 或 7-3 相同的方式来完成。

  • ECDiffieHellamnCngPublicKey5。实现是相似的,它们可以用与清单 7-1 、 7-2 或 7-3 相同的方式来完成。

  • ECDiffieHellmanKeyDerivationFunction6。实现是相似的,它们可以用与清单 7-1 、 7-2 或 7-3 相同的方式来完成。

  • ECDsaCng7。有关 ECDSA(椭圆曲线数字签名算法)的实现示例,请参见清单 7-5 。

using System;
using System.ComponentModel;
using System.Security.Cryptography;
using System.Text;

class RSAExampleOfImplementation
{
    static void Main()
    {
        try
        {
            //** the object will be used for
            //** conversion between the bytes
            //** of the array and string
            UnicodeEncoding byte_converter = new UnicodeEncoding();

            //** Create byte arrays to hold original,
            //** encrypted, and decrypted data.
            string plaintext = "Hi, Apress!";
            byte[] data_to_be_encrypted = byte_converter.GetBytes(plaintext);
            byte[] encrypted_data;
            byte[] decrypted_data;

            //** declare a new object of
            //** RSACryptoServiceProvider
            //** to generate the public
            //** and private key data.
            using (RSACryptoServiceProvider rsa_algorithm = new RSACryptoServiceProvider())
            {
                //** the data to be encrypted
                //** will be passed
                //** to EncryptionWithRSA along with the
                //** informations related to public key
                encrypted_data = EncryptionWithRSA(data_to_be_encrypted, rsa_algorithm.ExportParameters(false), false);
                //** the data to be encrypted
                //** will be passed to DecryptionWithRSA
                //** along with the informations related //** to public key
                decrypted_data = DecryptionWithRSA(encrypted_data, rsa_algorithm.ExportParameters(true), false);

                //** shows in the console the
                //** decryption of the plaintext
                Console.WriteLine("The plaintext for encryption is: {0}", plaintext);
                Console.WriteLine("\nPlaintext for encryption is: {0}", PrintByteArray(data_to_be_encrypted));
                Console.WriteLine("\nDecrypted plaintext: {0}", byte_converter.GetString(decrypted_data));

                Console.ReadKey();
            }
        }
        catch (ArgumentNullException)
        {
            //** in case that something is going wrong with
            //** the encryption, catch the exception
            Console.WriteLine("Encryption has failed.");
        }
    }

    static string PrintByteArray(byte[] encrypted_message)
    {
        var build_string = new StringBuilder("new byte[] { ");
        foreach (var each_byte in encrypted_message)
        {
            build_string.Append(each_byte + " ");
        }
        build_string.Append("}");
        return build_string.ToString();
    }

    public static byte[] EncryptionWithRSA(byte[] data_to_be_encrypted, RSAParameters rsa_key_info, bool oaep_padding)
    {
        try
        {
            byte[] encrypted_data;

            //** declare a new instance of
            //** RSACryptoServiceProvider.
            using (RSACryptoServiceProvider rsa_csp = new RSACryptoServiceProvider())
            {
                //** do the import for RSA Key information
                rsa_csp.ImportParameters(rsa_key_info);

                //** the byte passed array
                //** will be encrypted
                //** and mention OAEP padding
                encrypted_data = rsa_csp.Encrypt(data_to_be_encrypted, oaep_padding);
            }
            return encrypted_data;
        }

        //** in case that something is going wrong with
        //** the encryption, catch the exception
        catch (CryptographicException exception)
        {
            Console.WriteLine(exception.Message);

            return null;
        }
    }

    public static byte[] DecryptionWithRSA(
        byte[] data_to_be_decrypted,
        RSAParameters rsa_key_info,
        bool oaep_padding)
    {
        try
        {
            byte[] decrypted_data;

            //** declare a new instance of
            //** RSACryptoServiceProvider.
            using (RSACryptoServiceProvider rsa_csp = new RSACryptoServiceProvider())
            {
                //** do the import for RSA Key information
                rsa_csp.ImportParameters(rsa_key_info);

                //** the byte passed array will be decrypted
                //** and mention OAEP padding
                decrypted_data = rsa_csp.Decrypt(data_to_be_decrypted,     oaep_padding);
            }
            return decrypted_data;
        }

        //** in case that something is going wrong with
        //** the encryption, catch the exception
        catch (CryptographicException exception)
        {
            Console.WriteLine(exception.ToString());

            return null;
        }
    }
}

Listing 7-5Implementation of RSA Using RSACryptoServiceProvider

数字签名

公钥算法用于形成数字签名。数字签名的作用是验证发送者的真实身份,并提供有关数据完整性的保护。数字签名路径见图 7-9 。

img/493660_1_En_7_Fig9_HTML.jpg

图 7-9

ECDsaCng 示例

.NET Framework 为实现数字签名算法提供了以下类:

img/493660_1_En_7_Fig10_HTML.jpg

图 7-10

数字签名

  • DSACryptoServiceProvider [8

  • RSACryptoServiceProvider [9

  • ECDsa10。实施细节见清单 7-6 和图 7-10 。

  • ECDsaCng [11 ]。具体实施见清单 7-6 和图 7-10 。

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

class AliceUser
{
    public static void Main(string[] args)
    {
        BobUser bob = new BobUser();
        using (ECDsaCng dsaAlgorithm = new ECDsaCng())
        {
            dsaAlgorithm.HashAlgorithm = CngAlgorithm.Sha256;
            bob.theKey = dsaAlgorithm.Key.Export(CngKeyBlobFormat.EccPublicBlob);

            byte[] dataStructure = new byte[] { 21, 5, 8, 12, 207 };

            byte[] the_signature = dsaAlgorithm.SignData(dataStructure);

            bob.Receive(dataStructure, the_signature);
        }
    }
}

public class BobUser
{
    public byte[] theKey;

    public void Receive(byte[] data, byte[] the_signature)
    {
        using (ECDsaCng ecsdKey = new ECDsaCng(CngKey.Import(theKey, CngKeyBlobFormat.EccPublicBlob)))
        {
            if (ecsdKey.VerifyData(data, the_signature))
                Console.WriteLine("Data is good");
            else
                Console.WriteLine("Data is bad");
        }
    }
}

Listing 7-6Implementation of ECDsaCng

哈希值

哈希算法的目的是将表示为具有任意长度的二进制值映射到具有固定长度的较小二进制值。哈希值表示为代表特定数据的数值。

那个 .NET Framework 已经实现了以下类,这有助于我们快速实现哈希算法:

  • HMACSHA1 [12

  • MACTripleDES [13

  • MD5CryptoServiceProvider [14

  • RIPEMD160 [15

  • SHA1Managed [16

  • SHA256Managed [17

  • SHA384Managed [18

  • SHA512Managed [19

实现的例子,我们来看看RIPEMD160(见清单 7-7 和图 7-11 )和SHA256Managed(见清单 7-8 和图 7-12 )。

img/493660_1_En_7_Fig12_HTML.jpg

图 7-12

SHA256 实施示例

img/493660_1_En_7_Fig11_HTML.jpg

图 7-11

带有*哈希值的 RIPEMD160 示例。iso 文件

using System;
using System.IO;
using System.Security.Cryptography;
using System.Windows.Forms;

public class RIPEMD160Example
{

    [STAThreadAttribute]
    public static void Main(String[] args)
    {
        string directory_path = "";
        if (args.Length < 1)
        {
            FolderBrowserDialog folder_browser_dialog = new FolderBrowserDialog();
            DialogResult dialog_result = folder_browser_dialog.ShowDialog();
            if (dialog_result == DialogResult.OK)
            {
                directory_path = folder_browser_dialog.SelectedPath;
            }
            else
            {
                Console.WriteLine("There is no directory selected.");
                return;
            }
        }
        else
        {
            directory_path = args[0];
        }

        try
        {
            //** store the selected directory
            DirectoryInfo directoryInfo = new DirectoryInfo(directory_path);

            //** select the informations of
            //** each file from the folder
            FileInfo[] files = directoryInfo.GetFiles();

            //** declare a RIPEMD160 object
            RIPEMD160 myRIPEMD160 = RIPEMD160Managed.Create();
            byte[] hash_value;

            //** get the hash value of each file
            //** from the selected directory
            foreach (FileInfo infoOfTheFile in files)
            {
                //** declare a file stream for the file
                FileStream stream_file = infoOfTheFile.Open(FileMode.Open);

                //** put its position at the
                //** beginning of the stream

                stream_file.Position = 0;

                //** for the declared stream
                //** calculate the hash value
                hash_value = myRIPEMD160.ComputeHash(stream_file);

                //** show the name of the value in the console
                Console.Write(infoOfTheFile.Name + ": ");

                //** show the hash value in the console
                ShowReadableByteArray(hash_value);

                //** make sure that the file will
                //** closed properly
                stream_file.Close();
            }
            return;
            Console.ReadKey();
        }
        catch (DirectoryNotFoundException)
        {
            Console.WriteLine("Error: The selected directory could not be found.Try again!.");
        }

        catch (IOException)
        {
            Console.WriteLine("Error: There is a problem with the access of the file.");
        }

    }

    //** show the byte array in a possible readable format
    public static void ShowReadableByteArray(byte[] byteArray)
    {
        for (int k = 0; k < byteArray.Length; k++)
        {
            Console.Write(String.Format("{0:X2}",byteArray[k]));
            if ((k % 4) == 3)
                Console.Write(" ");
        }
        Console.WriteLine();
    }
}

Listing 7-7RIPEMD160 Implementation

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

public class HASH256ManagedImplementation
{
    public static void Main(String[] args)
    {
        string path_to_folder = @"D:\SHA256FileExample";
        //if (args.Length < 1)
        //{
        //    Console.WriteLine("There is no directory selected.");
        //    return;
        //}

        string directoryPathSelected = path_to_folder;
        if (Directory.Exists(directoryPathSelected))
        {
            //** the directory that has been selected
            var theDirectory = new DirectoryInfo(directoryPathSelected);

            //** get details about every file
            //**in the directory
            FileInfo[] theFiles = theDirectory.GetFiles();

            //** declare an instance of SHA256
            using (SHA256 theSha256 = SHA256.Create())
            {
                //** calculate the hash value
                //** of each of the files
                foreach (FileInfo informationOfTheFile in theFiles)
                {
                    try
                    {
                        //** declare a stream for the file

                        FileStream file_stream = informationOfTheFile.Open(FileMode.Open);

                        //** set the cursor at the
                        //** beginning of the stream
                        file_stream.Position = 0;

                        //** get the hash of the stream
                        byte[] the_hash_value =
                        theSha256.ComputeHash(file_stream);

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

                        ShowReadableByteArray(the_hash_value);

                        //** make sure that once is
                        //** done the file is closed
                        file_stream.Close();
                    }
                    catch (IOException e)
                    {
                        Console.WriteLine($"There is an Input / Output Exception:{ e.Message}");
                    }
                    catch (UnauthorizedAccessException e)
                    {
                        Console.WriteLine($"There is an error with the access: { e.Message}");
                    }
                }
            }

        }
        else
        {
            Console.WriteLine("The directory cannot be found. Select another one.");
        }

    }

    //** show the byte array in a
    //** possible readable format
    public static void ShowReadableByteArray(byte[] byteArray)
{
    for (int k = 0; k < byteArray.Length; k++)
    {
        Console.Write($"{byteArray[k]:X2}");
        if ((k % 4) == 3) Console.Write(" ");
    }
    Console.WriteLine();
}
}

Listing 7-8SHA256Managed Implementation

随机数生成

许多加密操作都是基于随机数的生成。作为一个例子,让我们考虑要求尽可能随机的密钥,这样就不可能复制它们。

RNGCryptoServiceProvider类 [20 ]基于加密服务提供商(CSP)提供的实现,提供了加密随机数生成(RNG)的专业实现。

ClickOnce 清单

ClickOnce [21 ]是一种部署技术,它提供了创建自我更新的基于 windows 的应用的能力。它提供了最少的用户交互,并且尽可能易于使用。

ClickOnce 技术仍然在中小型企业中使用。由于签名的安全性和签名的收集存在挑战。消费者应用主要在 UWP 或 Win32 中开发,并在部署过程中使用不同的技术。如今,一些内部业务应用使用不同的部署技术。

.NET Framework 3.5 提供了加密类,允许专业人员获取并验证分配给已部署的 ClickOnce 清单的签名信息。这些类别如下:

  • ManifestSignatureInformation班 [22 ]。用于获取与清单签名相关的信息。这可以借助于VerifyMethod方法来实现。清单 7-9 给出了一个验证清单签名的示例方法。

  • ManifestKinds枚举 [23 ]。此枚举用于指出应该检查哪些清单。检查过程的输出由SignatureVerificationResult枚举类型 [24 ]内的枚举值表示。

  • ManifestSignatureInformationCollection班 [25 ]。该类提供了与用于验证签名的ManifestSignatureInformation类 [26 ]的对象的只读集合相关的指导。

public static System.Security.Cryptography.
      ManifestSignatureInformationCollection
      VerifyingTheSignature
           (ActivationContext nameOfApplication,
           System.Security.ManifestKinds listOfManifests,
           System.Security.Cryptography.X509Certificates.
                      X509RevocationFlag theRevocationFlag,
           System.Security.Cryptography.X509Certificates.
                      X509RevocationMode theRevocationMode);

Listing 7-9Verifying the Signature with X509

以下类提供了有关签名的信息:

  • StrongNameSignatureInformation班 27 。该类保存签名的(强)名称及其特定清单的信息。

  • AuthenticodeSignatureInformation级 28 。表示一种特殊类型的签名,称为 authenticode,它与特定清单的签名信息包含在一起。

  • TimestampInformation级 29 。获取 authenticode 签名上的时间戳。

  • TrustStatus枚举。检查验证码签名是否可信。

套件 B 支持

.NET Framework 3.5 提供了对 Suite B 的支持,Suite B 是美国国家安全局(NSA)发布的一组重要的加密算法。2018 年,NSA 用商用的国家安全算法套件(CNSA) [31 ]替换了套件 B。

以下是一些被包含和认可的算法:

  • 高级加密标准(AES)算法。支持密钥大小 128、192 和 256。

  • 安全散列算法 SHA-1、SHA-256、SHA-384 和 SHA-512

  • 椭圆曲线数字签名算法。支持 256 位、384 位和 521 位曲线。

  • 椭圆曲线迪菲-赫尔曼(ECDH)算法。支持 256 位、384 位和 521 位曲线。这用于密钥交换和秘密协议。

下一代密码学课程

CNG 类为与本地 CNG 操作和函数相关的托管包装器提供支持。值得一提的是,CNG 是 CryptoAPI 的替代品。所有类的名称中都包含“CNG”。主类或 CNG 包装器是CngKey [32 ],它是一个容器类,包含存储和 CNG 密钥使用方式的抽象。

在这个类的帮助下,我们可以以一种安全的方式存储一个公钥或密钥对,并且有可能基于一个字符串名称来引用它。与椭圆曲线相关的类,比如用于签名的ECDsaCng,用于加密运算的ECDiffieHellmanCng [33 ,可以使用CngKey实例 [34 ]。

.NET Framework 3.5 支持不同的 CNG 类,例如

  • CngProvider级 35 。与密钥存储提供商进行交易。

  • CngAlgorithm班 [36 ]。处理 CNG 算法。

  • CngProperty类 37 。包含经常使用的关键属性。

结论

本章介绍了中最重要的加密服务 .NET 框架,从 3.5 版开始。专业人员可以执行不同加密算法和提供商的复杂实现任务。

本章就如何实现中可用的加密服务和提供程序提供了简短而全面的指南 .NET,而不必从头开始实现它们。涵盖的加密服务和提供者有

  • 对称加密密钥算法

  • 非对称加密密钥算法

  • 数字签名

  • 哈希算法及其值的使用

  • 随机数生成

  • ClickOnce 清单和用于检查其身份的解决方案

  • 套件 B 支持及其主要作用

文献学

  1. DSACryptoServiceProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.dsacryptoserviceprovider?view=netcore-3.1

  2. RSACryptoServiceProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsacryptoserviceprovider?view=netcore-3.1

  3. ECDiffieHellman阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdiffiehellman?view=netcore-3.1

  4. ECDiffieHellmanCng阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdiffiehellmancng?view=dotnet-plat-ext-3.1

  5. ECDiffieHellmanCngPublicKey阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdiffiehellmancngpublickey?view=dotnet-plat-ext-3.1

  6. ECDiffieHellmanKeyDerivation函数枚举。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdiffiehellmankeyderivationfunction?view=dotnet-plat-ext-3.1

  7. ECDsaCng阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdsacng?view=dotnet-plat-ext-3.1

  8. DSACryptoServiceProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.dsacryptoserviceprovider?view=netcore-3.1

  9. RSACryptoServiceProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsacryptoserviceprovider?view=netcore-3.1

  10. ECDsa阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdsa?view=netcore-3.1

  11. ECDsaCng阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdsacng?view=dotnet-plat-ext-3.1

  12. HMACSHA1阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.hmacsha1?view=netcore-3.1

  13. MACTripleDES阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.mactripledes?view=netframework-4.8

  14. MD5CryptoServiceProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.md5cryptoserviceprovider?view=netcore-3.1

  15. RIPEMD160阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ripemd160?view=netframework-4.8

  16. SHA1Managed阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha1managed?view=netcore-3.1

  17. SHA256ManagedClass。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha256managed?view=netcore-3.1

  18. SHA384Managed阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha384managed?view=netcore-3.1

  19. SHA512Managed阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha512managed?view=netcore-3.1

  20. RNGCryptoServiceProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rngcryptoserviceprovider?view=netcore-3.1

  21. ClickOnce 安全和部署。网上有: https://docs.microsoft.com/en-us/visualstudio/deployment/clickonce-security-and-deployment?view=vs-2019

  22. ManifestSignatureInformation阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.manifestsignatureinformation?view=netframework-4.8

  23. ManifestKinds枚举。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.manifestkinds?view=netframework-4.8

  24. SignatureVerificationResult枚举。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.signatureverificationresult?view=netframework-4.8

  25. ManifestSignatureInformationCollection阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.manifestsignatureinformationcollection?view=netframework-4.8

  26. ManifestSignatureInformation阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.manifestsignatureinformation?view=netframework-4.8

  27. StrongNameSignaureInformation阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.strongnamesignatureinformation?view=netframework-4.8

  28. AuthenticodeSignatureInformation阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.authenticodesignatureinformation?view=netframework-4.8

  29. TimestampInformation阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.timestampinformation?view=netframework-4.8

  30. TrustStatus枚举。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.truststatus?view=netframework-4.8

  31. 商业国家安全算法。网上有: https://apps.nsa.gov/iaarchive/programs/iad-initiatives/cnsa-suite.cfm

  32. CngKey阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cngkey?view=dotnet-plat-ext-3.1

  33. ECDiffieHellmanCng阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdiffiehellmancng?view=dotnet-plat-ext-3.1

  34. CngKey阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cngkey?view=dotnet-plat-ext-3.1

  35. CngProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cngprovider?view=dotnet-plat-ext-3.1

  36. CngAlgorithm阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cngalgorithm?view=dotnet-plat-ext-3.1

  37. CngProperty结构。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cngproperty?view=dotnet-plat-ext-3.1

  38. AesManaged阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.aesmanaged?view=netcore-3.1

  39. DesCryptoServiceProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.aesmanaged?view=netcore-3.1

  40. RC2CryptoServiceProvider阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.aesmanaged?view=netcore-3.1

  41. RijndelManaged阶级。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rijndaelmanaged?view=netcore-3.1

  42. TripleDESCryptoService提供者类别。网上有: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.tripledescryptoserviceprovider?view=netcore-3.1

八、Security.Cryptography命名空间概述

本章简要概述了System.Security.Cryptography名称空间的主要类、结构和枚举,包括加密服务、数据的安全编程和解码过程以及许多其他操作,如哈希、随机数生成和消息认证。关于密码服务及其实现的更多细节可以在章节 7 中看到。

本章的目标是为专业人员提供一个全面的路线图,清晰地概述 the.NET 框架中的加密服务。

班级

表 8-1 列出了处理加密算法的实现和封装过程的主要类,在下一代密码学(CNG)中。

表 8-1

系统。Security.Cryptography 命名空间

|

班级

|

描述

|
| --- | --- |
| AesCng | 实现对高级加密(AES)算法的 CNG 支持 |
| CngAlgorithm | 封装加密算法的名称 |
| CngAlgorithmGroup | 与 cngal 算法相同,但封装是针对一组加密算法完成的 |
| CngKey | 定义 CNG 使用的加密密钥的功能 |
| CngKeyBlobFormat | 该密钥被声明为 BLOB 格式,用于 CNG 对象。 |
| CngKeyCreationParameters | 对有关密钥创建的属性的高级支持 |
| CngPropertyCollection | 为与 CNG 属性相关的类型化集合提供强有力的支持 |
| CngProvider | 密钥存储提供者(KSP)的名称被封装,以便进一步用于 CNG 对象。 |
| CngUIPolicy | 用户界面(UI) CNG 的可选配置参数在访问受保护的密钥时显示。它还为配置提供封装。 |
| DSACng | 支持数字签名算法(DSA)的 CNG 实现 |
| DSAOpenSsl | 利用 OpenSSL 支持实现 DSA |
| ECDiffieHellmanCng | 为椭圆曲线 Diffie-Hellman (ECDH)算法的 CNG 实现提供支持。该类的目的是执行加密操作。 |
| ECDiffieHellmanCngPublicKey | 为 ECDH 创建一个公钥,它可以与ECDiffieHellmanCng类一起使用 |
| ECDiffieHellmanOpenSsl | 为 OpenSSL 支持的椭圆曲线 Diffie-Hellman (ECDH)算法的实现提供支持 |
| ECDsaCng | 为 ECDSA 提供 CNG 支持和实施 |
| ECDsaOpenSsl | 为基于 OpenSSL 的椭圆曲线数字签名算法(ECDSA)的实现提供支持 |
| ProtectedData | 提供加密和解密数据的方法。该类不能被继承。 |
| RSACng | 为 RSA 算法提供 CNG 支持和实施 |
| RSAOpenSsl | 通过 openssl 支持为 rsa 提供支持和实施 |
| SafeEvpPKeyHandle | 代表 OpenSSL 的特殊指针(EVP_PKEY*) |
| TripleDESCng | 为三重数据加密标准(3DES)算法提供 CNG 支持和实施 |

结构

表 8-2 显示了处理 CNG 密钥提供者属性封装的最重要的结构之一。

表 8-2

系统中的结构。Security.Cryptography 命名空间

|

结构体

|

描述

|
| --- | --- |
| CngProperty | 它为 CNG 密钥或提供者的属性提供封装。 |

枚举数

.NET Framework 从 3.5 开始,与加密和安全机制相关,它有一组枚举,为处理策略、密钥创建和加密操作提供了重要的选项。表 8-3 提到了可以在System.Security.Cryptography namespace中找到的主要重要枚举。

表 8-3

系统中的枚举。Security.Cryptography 命名空间

|

列举型别

|

描述

|
| --- | --- |
| CngExportPolicies | 加密密钥的密钥导出策略 |
| CngKeyCreationOptions | 关键创意的选择 |
| CngKeyHandleOpenOptions | 打开钥匙把手的选项 |
| CngKeyOpenOptions | 打开钥匙的选项 |
| CngKeyUsages | 与其一起使用的 CNG 密钥的加密操作 |
| CngPropertyOptions | CNG 关键属性选项 |
| CngUIProtectionLevels | 与用户界面及其场景中使用的密钥相关的保护级别 |
| DataProtectionScope | 使用Protect(Byte[], Byte[], DataProtectionScope)方法获取可应用的数据保护范围 |
| ECKeyXmlFormat | 定义并支持椭圆曲线密钥的 XML 序列化格式 |

中的安全模型 .NET 框架

图 8-1 显示了一个通用的安全模型,该模型提供了基于角色的安全如何在认证过程中发挥重要作用的快速概述。由于安全架构中最重要组件的分布,如应用域验证流程代码访问安全性,认证流程扮演着至关重要的角色。

img/493660_1_En_8_Fig1_HTML.jpg

图 8-1

。具有基于角色的安全性的. NET Framework 安全体系结构

看看在实现加密机制的过程中构成安全体系结构的组件,理解每个组件的定义和边界是很重要的。应用域提供了一定程度的隔离进程,对于确保应用中运行的代码不受对手影响是必要的。基于这个方面,应用域中的隔离边界为安全性、可靠性和版本控制提供了隔离边界。大多数应用域都是在运行时宿主期间创建的。

在图 8-2 中,您可以看到基于角色的安全所关注的主要组件,如认证、授权、主体和身份。

img/493660_1_En_8_Fig2_HTML.jpg

图 8-2

。具有基于角色的安全性的. NET Framework 安全体系结构

在图 8-3 中,应用域的组件应该在实现过程中以最大责任来对待。在实现过程中,有一个布尔属性叫做IsFullyTrusted

img/493660_1_En_8_Fig3_HTML.jpg

图 8-3

。具有应用域组件的. NET Framework 安全体系结构

实现第一个组件的目标,完全信任部分信任,列表 8-1 展示了这样一个过程如何在一个真实案例中实现和转置。同样的方法和过程可以用来证明是异构、同构还是应用域沙箱化。输出见图 8-4 。

img/493660_1_En_8_Fig4_HTML.jpg

图 8-4

应用域的输出

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

namespace FullyOrPartiallyTrustedExample
{
    class Program
    {
        public class InstanceWorker : MarshalByRefObject
        {
            static void Main()
            {
                InstanceWorker iw = new InstanceWorker();
                iw.RunningTestIfItIsFullyTrusted();

                AppDomain adSandbox = GetInternetSandbox();
                iw = (InstanceWorker)
                        adSandbox.CreateInstanceAndUnwrap(
                        typeof(InstanceWorker).Assembly.FullName,
                        typeof(InstanceWorker).FullName);
                iw.RunningTestIfItIsFullyTrusted();
            }

            public void RunningTestIfItIsFullyTrusted()
            {
                AppDomain app_domain = AppDomain.CurrentDomain;
                Console.WriteLine("\r\nApplication domain '{0}': IsFullyTrusted = {1}", app_domain.FriendlyName, app_domain.IsFullyTrusted);

                Console.WriteLine("IsFullyTrusted = {0} for the current assembly", typeof(InstanceWorker).Assembly.IsFullyTrusted);

                Console.WriteLine("IsFullyTrusted = {0} for mscorlib", typeof(int).Assembly.IsFullyTrusted);
            }

            static AppDomain GetInternetSandbox()
            {
                System.Security.Policy.Evidence theEvidenceOfHost = new System.Security.Policy.Evidence();

                theEvidenceOfHost.AddHostEvidence(new System.Security.Policy.Zone(System.Security.SecurityZone.Internet));

                System.Security.PermissionSet thePermissionSet = System.Security.SecurityManager.GetStandardSandbox(theEvidenceOfHost);

                AppDomainSetup appDomainSetup = new AppDomainSetup
                {
                    ApplicationBase = System.IO.Directory.GetCurrentDirectory()
                };

                return AppDomain.CreateDomain("Sandbox", theEvidenceOfHost, appDomainSetup, thePermissionSet, null);
            }
        }
    }
}

Listing 8-1Fully Trusted or Partially Trusted Example

在图 8-5 中,您可以看到哪些组件和模块涉及验证过程。你可以看到验证过程看起来像一个步骤的集合, C# 编译,JIT 编译,原生映像生成器PEVerify 工具。在接下来的章节中,我们将分析上面列出的最重要的组件。

img/493660_1_En_8_Fig5_HTML.jpg

图 8-5

.NET 框架安全体系结构与验证过程组件

JIT 编译(见图 8-6 )是一个复杂的过程,其中的关键点可以被用来访问应用的不同部分。JIT 编译的目标是加速代码的执行,并为多个平台提供支持。这种支持是棘手的,因为它会暴露和破坏系统的安全。

img/493660_1_En_8_Fig6_HTML.jpg

图 8-6

JIT 编译过程

许多安全专家和专业人员认为这是确保应用安全性的过程中的一个缺口,因为攻击者可以利用多个点,例如使用适当的工具来反汇编.exe.dll文件,并继续进行软件混淆攻击。

一旦 JIT 过程完成,我们可以进一步移动到本地生成器映像(NGEN) NGEN 是提高托管应用性能的强大工具。NGEN(可以通过Ngen.exe,找到)创建(编译)本机映像(例如,ISO 文件),其中包含以特定于处理器的方式编译为机器代码的文件,并将它们部署在最终用户本地计算机的本机映像缓存中。这是一个危险的点,因为我们永远不知道谁和什么用户会在他们的计算机上,如果一个恶意用户正在等待适当的时机来利用,以找到他们的漏洞点。

最后一步是基于 PE 验证工具或Peverify.exe. It's a非常有用的工具,它为正在生成微软中间语言(MSIL)的开发人员(如编译器编写者)提供了大量的帮助,目的是确定他们的 MSIL 代码是否符合安全要求。

图 8-7 显示我们在的最后一个组件上 .NET 框架安全架构,名为代码访问安全。它的所有组件都很重要,每个专业开发人员都应该将它们纳入开发过程中的应用以及安全分析和设计中。

img/493660_1_En_8_Fig7_HTML.jpg

图 8-7

那个 .NET 框架安全体系结构与代码访问安全组件

策略 权限 实施应该被视为一个整体单元,作为运行在同一应用中的不同代码上的每个信任级别的“心脏”。

熟悉以下代码访问安全概念和机制将为专业人员提供关于代码访问安全(CAS)内部机制的深厚知识背景。也就是说,为了编写以公共语言运行时为目标的有效应用,我们有以下几点:

  • 类型安全代码:一种特殊类型的代码,只访问那些定义明确的类型

  • 命令式和声明式语法:面向公共语言运行时的代码能够与安全系统进行交互。交互是请求权限并覆盖某些安全设置。

  • 安全类库(Secure class libraries):类库有安全需求的用法,它们的主要目标是确保那些使用和调用类库的人有适当的权限访问类库及其资源。

  • 透明码:带 .NET Framework 4 专业人员有能力确定代码是否能够以安全透明的方式运行。此外,确定权限是需要通过确定透明许可来完成的任务之一。

声明性安全

声明性安全所使用的语法基于用于在专业开发人员编写的代码的元数据中插入安全信息的属性。

要使用声明性安全调用,第一步是初始化权限对象中的数据状态。清单 8-2 展示了一个声明性语法的例子。这个例子很有启发性,如果你有一个名为UserPermission.的许可,你可以提供代码调用者

using System.Security.Permissions;
[UserPermission(SecurityAction.Demand, Unrestricted = true)]
public class Example
{
   public Example()
   {
      //** is beging protected by the security call
   }

   public void SomeMethodForUserA()
   {
      //** is beging protected by the security call
   }

   public void SomeOtherMethodForUserB()
   {
      //** is beging protected by the security call
   }
}

Listing 8-2Example of Declarative Security

SecurityAction.Demand指定调用程序需要运行代码的权限。

必要的安全

命令式安全性提供的语法调用安全性调用来创建持有权限的对象的新实例。强制安全性可用于调用要求和重写。不要做任何请求,这一点非常重要。这在绝对安全的情况下是不可能实现的。

清单 8-3 展示了命令式语法如何请求代码的调用者拥有某个名为UserPermission的权限。

public class Example
{
   public Example()
   {
       //** the constructor of the class
   }

   public void SomeMethodForUserA()
   {
       //** UserPermission has been demanded
       //** to use imperative syntax.
       UserPermission user_permission = new UserPermission();
       user_permission.Demand();
       //** here is being protected by the security call
   }

   public void SomeOtherMethodForUserB()
   {
      //** is not being protected by the security call
   }
}

Listing 8-3Example of Imperative Security

结论

在这一章中,我们讨论了System.Security.Cryptography名称空间,并通过指出它们的主要名称给出了它的类、结构和枚举的概述。我们继续我们的旅程,解释为什么专业人士应该根据 .NET Framework 安全模型,尊重它们的组件并遵循工作流作为必要的准则。

在本章结束时,您将拥有

  • System.Security.Cryptography名称空间的概貌

  • 理解类、结构和枚举的主要名称

  • 了解内部安全模型的要点。这可能会导致安全灾难

  • 主要组件如何工作和通信的清晰图像,例如应用域、验证过程和代码访问安全性

  • 对 JIT 编译及其过程的深入理解

  • 了解验证过程如何暴露安全漏洞

  • 了解如何、何时以及为什么使用声明性和命令性安全性,并概述它们在实现过程中的重要性

文献学

  1. 讽刺瓦斯卡兰。交互式 C#:基础、核心概念和模式。Apress,2018。

  2. 安德鲁·特罗尔森和菲利普·贾皮克塞。Pro C# 7 与 .NET 和。网芯。Apress,2017。

  3. 乔希·比平。从 C# 开始 XML 7:面向 C# 开发人员的 XML 处理和数据访问。Apress,2017。

九、C# 和 .NET

在这一章中,我们将介绍一些可以在中使用的最重要的加密库 .NET 和 C# 应用。选择一个加密库,尤其是开源的加密库,与 .NET 框架本身。你需要确定你在做什么,并且知道你在使用什么样的数据。如果开源项目中存在可被攻击者利用的漏洞,那么在安全方面依赖开源项目可能会导致重大的安全事故。

在接下来的章节中,我们将介绍这些库:NSec [1 ]、弹力城堡 [2 ]、地狱 [3 ]和 SecureBlackbox [4 。

毫微秒或纳秒(nanosecond 或 nanoseconds)

NSec [1 ]是最现代、最易于使用的加密库之一 .NET 核心。该库基于 lib 钠 [5 ]。

NSec 的一些特点如下:

  • 现代方法。libna 提供了一小组加密算法和原语。与其他库相比,NSec 和 lib 钠的优点是支持 X25519、Ed25519 和 ChaCha-Poly1305 等特性。NSec 内的性能和功能实现方式基于现代。基于类型Span<T>ReadOnlySpan<T>.开发的. NET API

  • 极其好用。这是非常有用和易于使用的。NSec 的优雅和可靠性意味着它易于实施。它提供了对类型数据模型的支持,这种模型是基于专用类而不是空字节数组来设计的,考虑到了键和共享秘密。这有助于开发人员避免在错误的算法中使用密钥。

  • 安全。NSec 的任务是使密码原语尽可能简单。

  • 。NSec 和 libna 在加密过程中速度非常快。堆没有分配内存。NSec 旨在避免任何类型的内存分配或无用的副本。

  • 敏捷。NSec 中实现的大多数算法都是从一小组基类中派生出来的。目的是提供一种针对算法接口而不是特定算法编写代码的高效方法。

在下面的例子中,您将看到 NSec 是如何工作的,以及它是多么容易使用。NSec 成功地使用了 C# 8.0 的一些特性,它们非常容易识别和理解。

清单 9-1 展示了如何使用 Ed25519 签名算法和签名消息。图 9-1 显示了输出。

img/493660_1_En_9_Fig1_HTML.jpg

图 9-1

NSec 输出

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

namespace NSecLibrary
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("NSec Library");

            //** select the Ed25519 signature algorithm
            var algorithm = SignatureAlgorithm.Ed25519;

            //** create a new key pair
            var key = Key.Create(algorithm);

            //** generate some data to be signed
            var data = Encoding.UTF8.GetBytes("Use the Force, Luke!");

            //** sign the data using the private key
            var signature = algorithm.Sign(key, data);

            //** verify the data using the signature and the public key
            if (algorithm.Verify(key.PublicKey, data, signature))
            {
                Console.WriteLine("The message ");

                for (int i = 0; i < data.Length; i++)
                {
                    Console.Write(data[i].ToString() + " ");
                }

                Console.WriteLine("\n has been verified with success.");
            }
            else

            {
                Console.WriteLine("The message: {0} has not been verified.", data.ToString());
            }
        }
    }
}

Listing 9–1Ed25519 Signature

NSec 安装

可以使用 NuGet 包 [6 ]找到并安装 NSec,它包含安装过程所需的所有细节(见图 9-2 )。

img/493660_1_En_9_Fig2_HTML.jpg

图 9-2

NSec。加密 NuGet 包

还可以通过以下不同方式将 NSec 添加到项目中:

  • 使用点网命令行界面:

  • 使用 Visual Studio :

$ dotnet add package NSec.Cryptography –-version XX.X.X

  • 使用 *中的引用。csproj 文件:
PM> Install-Package NSec.Cryptography -Version XX.X.X

<PackageReference Include="NSec.Cryptography" Version="XX.X.X"/>

大型充气城堡型玩具

充气城堡是最重要和最著名的图书馆之一。它包含加密算法和协议的 C# 实现。

它有很多有用的特性。最重要的特性列表如下所示:

  • “支持解析和生成 PKCS-12 文件” 7 。

  • "X.509 :支持 V1 和 V3 证书(生成和解析)。此外,V2 CRL 和证书基于属性“ 7 ”。

  • “性能支持的 PBE 算法”[7。

  • “签名者支持的签名算法”[7。

  • “对称密钥算法 : AES,Blowfish,Camellia,CAST5,CAST6,ChaCha,DES,DESede,GOST28147,HC-128,HC-256,IDEA,ISAAC,Noekeon,RC2,RC4,RC5-32,RC5-64,RC6,Rijndael,Salsa20,SEED,Serpent,Skipjack,TEA/XTEA,Threefish,Tnepres,Twofish,VMPC 和 xsalsa 20”7

  • “对称密钥模式 : CBC、CFB、CTS、GOFB、OFB、OpenPGPCFB、SIC(或 CTR)”[7。

  • “对称密钥填充:PKCS ISO10126d2、ISO7816d4-5/7、TBC、X.923、零字节” 7 。

  • “非对称密钥算法 : ElGamal,DSA,ECDSA,NaccacheStern,RSA(带盲)” [7 。

  • “非对称密钥填充/编码s:OAEP、PKCS 和 ISO9796d1-1”7。

  • “AEAD 分组密码模式 : CCM、EAX、GCM、OCB”7。

  • “摘要 : GOST3411,Keccak,MD2,MD4,MD5,RIPEMD128,RIPEMD160,RIPEMD256,RIPEMD320,SHA-1,SHA-224,SHA-256,SHA-384,SHA-512,SHA3,老虎,漩涡” 7 。

  • " XOFs:SHAKE "7。

  • “签名人机制 : DSA,ECDSA,ECGOST3410,ECNR,GOST3410,ISO9796d2,PSS,RSA,x 9.31-1998”7。

  • “密钥协议:迪菲-海尔曼,EC-DH,EC-MQV,J-帕克,SRP-6a”7。

  • MAC:cbcbl lock cipher、CFBBlockCipher、CMAC、GMAC、GOST28147、HMac、ISO9797 Alg。3、聚 1305、SipHash、SkeinMac、vmpcm AC "[7。

  • “PBE 发电机 : PKCS-12,PKCS-5——方案一和方案二” 7 。

  • open PGP(RFC 4880)[7]t1。

  • “加密消息语法(CMS,RFC 3852),包括流式 API”7。

  • “在线证书状态协议(OCSP,RFC 2560)”[7。

  • “时间戳协议(TSP,RFC 3161)”[7。

  • “TLS/DTLS 客户端/服务器版本 1.2,支持最常用的密码套件和扩展,以及许多不常用的密码套件和扩展。可用的非阻塞 API "7。

  • 椭圆曲线密码

充气城堡示例

清单 9-2 展示了一个使用 Bouncy Castle 生成密钥的例子。在图 9-3 中,您可以看到密钥大小设置为 256 的输出。

img/493660_1_En_9_Fig3_HTML.jpg

图 9-3

使用 BouncyCastle 生成加密密钥

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;

namespace BouncyCastleLibrary
{
    class Program
    {
        public static string Xvalue;
        public static string Yvalue;
        public static int Dvalue;

        static void Main(string[] args)
        {
            Console.WriteLine("Using BouncyCastle Library to show how we can generate cryptography keys (private and public).\n");
            Console.WriteLine("Please, choose the size of the keys (128 or 256)");
            int key_size = Convert.ToInt32(Console.ReadLine());

            if (key_size == 128)
                KeyGeneration(128);
            else if (key_size == 256)
                KeyGeneration(256);
        }

        public static AsymmetricCipherKeyPair GenerateKeys(int keySize)
        {
            //** choosing ECDSA for key generation
            var key_generation = new ECKeyPairGenerator("ECDSA");

            //** for creating randomly values
            var randomly_secure_value = new SecureRandom();

            //** generating the parameters based on the random value and size of the key
            var key_generation_parameters = new KeyGenerationParameters(randomly_secure_value, keySize);

            //** proceed with the initialization of the generation algorithm with parameters
            key_generation.Init(key_generation_parameters);

            //** the key pair generation
            return key_generation.GenerateKeyPair();
        }

        public static void KeyGeneration(int key_size)

        {
            //** generating process
            var key_pair = GenerateKeys(key_size);
            TextWriter text_writer = new StringWriter();
            PemWriter pem_writer = new PemWriter(text_writer);

            pem_writer.WriteObject(key_pair.Private);
            pem_writer.Writer.Flush();

            string privateKey = text_writer.ToString();
            Console.WriteLine("The private key is: {0}", privateKey);

            ECPrivateKeyParameters privateKeyParam = (ECPrivateKeyParameters)key_pair.Private;
            Console.WriteLine("D value is: {0}", privateKeyParam.D.ToString());
            text_writer = new StringWriter();
            pem_writer = new PemWriter(text_writer);
            pem_writer.WriteObject(key_pair.Public);
            pem_writer.Writer.Flush();

            ECPublicKeyParameters publicKeyParam = (ECPublicKeyParameters)key_pair.Public;
            string publickey = text_writer.ToString();
            Console.WriteLine("The public key is: {0}", publickey);
            Console.ReadKey();

            Xvalue = publicKeyParam.Q.XCoord.ToBigInteger().ToString();
            Yvalue = publicKeyParam.Q.YCoord.ToBigInteger().ToString();
        }
    }
}

Listing 9-2AES – Encryption with CBC Mode and PKCS5/7 Padding

接下来的三个例子(见清单 9-3 、清单 9-4 和清单 9-5 )展示了如何处理密钥协商和交换算法。

如果您正在处理一个基本协议(参见清单 9-3 ),该协议使用了一个具有约定值的余因子,下面的例子应该很能说明问题。

public byte[] AgreementOnAgreedValue
                      (AsymmetricECPrivateKey privateKey,
                      AsymmetricECPublicKey otherEncKey)
{
      IAgreementCalculatorService createService =
            CryptoServicesRegistrar.CreateService(privateKey);

      IAgreementCalculator<FipsEC.AgreementParameters>
          contract_agreement =
          createService.CreateAgreementCalculator(FipsEC.Cdh);

      return contract_agreement.Calculate(otherEncKey);
}

Listing 9-3Basic Agreement with an Agreed Value

在清单 9-4 中,您可以看到如何使用 SHA256 函数和 PRF(伪随机函数)函数来计算商定的值。

public byte[] AgreementOnAgreedValueWithPrf
              (AsymmetricECPrivateKey privateKey,
               AsymmetricECPublicKey otherEncKey,
               byte[] paddingSalt)
{
       IAgreementCalculatorService createService =
            CryptoServicesRegistrar.CreateService(privateKey);

       IAgreementCalculator<FipsEC.AgreementParameters>
            contract_agreement = dhFact.CreateAgreementCalculator
            (FipsEC.Cdh.WithKeyMaterialGenerator
            (new FipsPrfKmg
                         (FipsPrfAlgorithm.Sha256HMac,
     paddingSalt));

      return contract_agreement.Calculate(otherEncKey);
}

Listing 9-4Agreement Using PRF

清单 9-5 显示了与 X9.63 KDF(密钥派生函数)的另一种协议。

public byte[] AgreementWithCofactorAndKdf
      (AsymmetricECPrivateKey privateKey,
       AsymmetricECPublicKey otherEncKey,
       byte[] paddingSalt)
{
     IAgreementCalculatorService createService =
           CryptoServicesRegistrar.CreateService(privateKey);

     IAgreementCalculator<FipsEC.AgreementParameters>
         contract_agreement =
              dhFact.CreateAgreementCalculator
              (FipsEC.Cdh.WithKeyMaterialGenerator
              (new FipsKdfKmg(FipsKdf.X963,
                     paddingSalt, 32));

     return agreement.Calculate(otherEncKey);
}

Listing 9-5Working with an Agreement Based on X9.63 KDF

充气城堡装置

充气城堡可以使用 NuGet 包 [8 ]找到并安装,它包含了安装过程所需的所有细节(见图 9-4 )。

img/493660_1_En_9_Fig4_HTML.jpg

图 9-4

充气城堡 NuGet 套餐

弹力城堡也可以通过以下方式添加到项目中:

  • 使用点网命令行界面:

  • 使用 Visual Studio :

$ dotnet add package BouncyCastle –-version XX.X.X

  • 使用 *中的引用。csproj 文件:
PM> Install-Package BouncyCastle -Version XX.X.X

<PackageReference Include="BouncyCastle" Version="XX.X.X"/>

地狱

Inferno 是另一个有趣的加密库 .NET 使用 C# 开发。它在编写代码时提供了一种独特的优雅,在处理代码时获得的性能非常有前途。

以下列表代表了 Inferno 的特性 [9 :

  • "[random]: CryptoRandom( .NET Random done right)" [9

  • "[密码]:仅 AES-256(快速、恒定时间、抗旁路 AES-NI)" [9 ]

  • "[高级]: AEAD (AES-CTR-HMAC)。流式 AEAD (EtM 转换)" [9

  • "[ciphers-misc]: AES-CTR 实现(密码转换)" [9 ]

  • "[ciphers-misc]:AEAD(AES-CBC-HMAC)"[9]

  • "[哈希]: SHA2 哈希工厂(256,384,512)。推荐 SHA-384(默认)" [9 ]

  • "[hash]: SHA1 哈希工厂(主要用于遗留集成)" [9 ]

  • “[mac]: HMAC2( .NET HMAC 做得对) [9

  • "[mac]: HMAC-SHA1,HMAC-SHA2 工厂"9

  • “[kdf]: HKDF,PBKDF2,SP800_108_Ctr。支持任何 HMAC 工厂" 9

  • "[助手]" [9 ]

    • “常量时间字节和字符串比较”9

    • “安全的 UTF8”9

    • “快速 64 位字节数组异或”9

地狱的例子

以下示例展示了如何使用该库的案例研究。在清单 9-6 中,您可以看到处理加密、解密和认证的函数的基本声明。注意函数的灵活性和提示性声明。

//** The namespace that has to be used when working with
//** Inferno is: SecurityDrive.Inferno
//** If SecurityDrive.Inferno is not visible it means that is
//** not properly installed and it is necessary to check the
//** section "Inferno Installation"

public static class BasicOperations
{
    public static byte[] Encrypt(byte[] master_crypto_key,
         ArraySegment<byte> clearText,
         ArraySegment<byte>? saltPadding = null);

    public static byte[] Decrypt(byte[] master_crypto_key,
         ArraySegment<byte> cryptotext,
         ArraySegment<byte>? saltPadding = null);

    public static bool Authenticate(byte[] master_crypto_key,
         ArraySegment<byte> cryptotext,
         ArraySegment<byte>? saltPadding = null);
}

Listing 9-6Encryption, Decryption, and Authentication Functions

使用散列函数(见清单 9-7 )非常有趣和简单。一旦你到达流程和应用的末尾,调用dispose是非常重要的。

public static Func<SHA384> HashFactory

Listing 9-7Working with Hash

清单 9-8 展示了如何使用 HMAC。

var dataForHmac = Utils.SafeUTF8.GetBytes("Welcome To Apress!");

//** this is for HMACSHA384
using (var theHmac = SuiteB.HmacFactory())
{
      theHmac.Key = new byte[] { 6, 5, 4, 3, 2 };
      theHmac.ComputeHash(dataForHmac).ToBase16().Dump();
}

Listing 9-8Using HMAC

清单 9-9 显示了 DSA(数字签名算法)的使用情况。图 9-5 显示了输出。

img/493660_1_En_9_Fig5_HTML.jpg

图 9-5

输出

using SecurityDriven.Inferno.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace InfernoLibrary
{
    class Program
    {
        static void Main(string[] args)
        {
            //** generate the DSA keys
            CngKey thePrivateKey = CngKeyExtensions.CreateNewDsaKey();

            //** generate the cryptographic keys
            //** that will be used with DSA
            byte[] dsa_private_key_blob = thePrivateKey.GetPrivateBlob();

            //** convert and store private key as bytes
            byte[] dsa_public_key_blob = thePrivateKey.GetPublicBlob();

            //** convert and store public key as bytes
            CngKey dsa_public_key = dsa_public_key_blob.ToPublicKeyFromBlob();

            //** some data (sample)
            byte[] data_sample = Guid.NewGuid().ToByteArray();
            byte[] theSignature = null;

            //** using the private key, generate the
            //** DSA signature and store it properly
            using (var ecdsaAlgorithm = new ECDsaCng(thePrivateKey) { HashAlgorithm = CngAlgorithm.Sha384 })
            {
                theSignature = ecdsaAlgorithm.SignData(data_sample);
            }

            //** play with the data
            data_sample[5] ^= 1;

            Console.WriteLine("Private key is: {0}", BitConverter.ToString(dsa_private_key_blob));
            Console.WriteLine("\nPublic key is: {0}", BitConverter.ToString(dsa_public_key_blob));
            Console.WriteLine("\nSample data: {0}", BitConverter.ToString(data_sample));
            Console.WriteLine("\nThe signature is: {0}", BitConverter.ToString(theSignature));

            //** using the public key, verify the DSA signature
            using (var ecdsaAlgorithm = new ECDsaCng(dsa_public_key){ HashAlgorithm = CngAlgorithm.Sha384})
            {
                if (ecdsaAlgorithm.VerifyData(data_sample, theSignature))
                {
                    Console.WriteLine("\nOups! Something went wrong. Signature was unable to be verified properly.");
                }
                else
                {
                    Console.WriteLine("\nThe signature has been verified with success.");
                }

                Console.ReadKey();
            }
        }
    }
}

Listing 9-9Dealing with a DSA

地狱装置

使用 NuGet 包 [10 ]可以找到并安装 Inferno,它包含了安装过程所需的所有细节(参见图 9-6 )。

img/493660_1_En_9_Fig6_HTML.jpg

图 9-6

Inferno NuGet 包

Inferno 也可以通过以下方式添加到项目中:

  • 使用点网命令行界面:

  • 使用 Visual Studio :

$ dotnet add package Inferno –-version XX.X.X

  • 使用 *中的引用。csproj 文件:
PM> Install-Package Inferno -Version XX.X.X

<PackageReference Include="Inferno" Version="XX.X.X"/>

安全黑盒

SecureBlackbox 是处理数字安全和网络安全的最全面的工具和类集之一。

开发安全解决方案的方式与 NSec 或 Inferno 相同。方法和功能类似于 NSec 和 Inferno 的方法和功能。差异非常小,并且是特定于缓冲区数组的分配。

图书馆不是免费的。要运行清单 9-10 中的示例,您需要一个许可证。一旦运行该应用,将显示来自图 9-7 的消息。

img/493660_1_En_9_Fig7_HTML.jpg

图 9-7

运行该示例所需的许可证

出于测试目的,对于清单 9-10 中的示例,可以联系库的所有者,他们会向您提供一个临时许可证密钥。

一旦收到许可证(通常是一个文本文件,如LicenseKey.txt),就可以调用两个函数,并将许可证作为它们的关键参数。这两个功能是void SetLicenseKey(ByteArray key)void SetLicenseKey(string key).

如果您同时需要更多的密钥许可,只需使用不同的文件名多次调用该函数。每个许可证应该有不同的名称。

using SBSymmetricCrypto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace SecureBlackBoxLibrary
{
    class Program
    {
        static void Main(string[] args)
        {
            var key = "b14ca5898a4e4133bbce2ea2315a1916";
            byte[] encryptionKeyBuffer;
            byte[] initializationBuffer = new byte[16];

            using (Aes aes = Aes.Create())
            {
                aes.Key = Encoding.UTF8.GetBytes(key);
                aes.IV = initializationBuffer;

                encryptionKeyBuffer = aes.Key;
                initializationBuffer = aes.IV;
            }

            //** create a crypto factory instances
            //** used for symmetric encryption
            TElSymmetricCryptoFactory symmetric_factory_container = new TElSymmetricCryptoFactory();

            //** use the factory container to
            //** declare an appropriate algorithm
            TElSymmetricCrypto symmetric_encryption = symmetric_factory_container.CreateInstance (SBConstants.Unit.SB_ALGORITHM_CNT_AES256, TSBSymmetricCryptoMode.cmDefault);

            //** declare a key and assign the proper
            //** value for the secret key and the initialization vector
            TElSymmetricKeyMaterial km = new TElSymmetricKeyMaterial(null);
            km.Key = encryptionKeyBuffer;
            km.IV = initializationBuffer;

            //** assign the key container to the cryptographic
            //** object using the property KeyMaterial
            symmetric_encryption.KeyMaterial = km;

            //** proceed further with the encryption
            byte[] input_buffer = Encoding.UTF8.GetBytes("Welcome to Apress!");

            //** declare the output_buffer and output_size and initialize //** them with 0\. In this way we will point out that the first //** invoked is getting the call.
            byte[] output_buffer = null;
            int output_size = 0;

            //** Finding out about the output
            //** length - this may be approximate
            symmetric_encryption.Encrypt(input_buffer, 0, input_buffer.Length, ref output_buffer, 0, ref output_size);

            //** create an allocation as an array for the output data
            output_buffer = new byte[output_size];

            //** do the encryption
            symmetric_encryption.Encrypt(input_buffer, 0, input_buffer.Length, ref output_buffer, 0, ref output_size);

            //**copy the data that has been encrypted to a dedicate buffer
            output_buffer = SBUtils.Unit.CloneArray(output_buffer, 0, output_size);

            //** before continue with the encryption
            //** invoke InitializeEncryption method
            //** within the cryptographic object
            symmetric_encryption.InitializeEncryption();

            //** as much as it is required, pass the data as necessary
            //** using invokations of EncryptUpdate()
            symmetric_encryption.EncryptUpdate(input_buffer, 0, input_buffer.Length, ref output_buffer, 0,ref output_size);
            output_buffer = SBUtils.Unit.CloneArray(output_buffer, 0, output_size);

            //** Once we reach at the end of EncryptUpdate(),
            //** we need to endup the encryption
            //** by invoking FinalizeEncryption() method
            symmetric_encryption.FinalizeEncryption(ref output_buffer, 0, ref output_size);
            output_buffer = SBUtils.Unit.CloneArray(output_buffer, 0, output_size);

            Console.WriteLine("The encryption key is: {0}", key);

            Console.WriteLine("\nThe buffer for encryption key is: {0}", BitConverter.ToString(encryptionKeyBuffer));
            Console.WriteLine("\nThe initialization buffer is: {0}", BitConverter.ToString(initializationBuffer));
            Console.WriteLine("\nThe encryption is: {0}", symmetric_encryption.ToString());
            Console.ReadKey();
        }
    }
}

Listing 9-10Example of Symmetric Encryption with SecureBlackbox

SecureBlackbox 安装

可以使用 NuGet 包 [10 ]找到并安装 SecureBlackbox,它包含安装过程所需的所有细节(参见图 9-8 )。

img/493660_1_En_9_Fig8_HTML.jpg

图 9-8

SecureBlackbox NuGet 包

还可以通过以下方式将 SecureBlackbox 添加到项目中:

  • 使用点网命令行界面:

  • 使用 Visual Studio :

$ dotnet add package SecureBlackbox –-version XX.X.X

  • 使用 *中的引用。csproj 文件:
PM> Install-Package SecureBlackbox -Version XX.X.X

<PackageReference Include=" SecureBlackbox" Version="XX.X.X"/>

结论

在本章中,我们介绍了最重要的加密库(NSec、Bouncy Castle、Inferno 和 SecureBlackbox ),它们可以作为指南、额外的库和工具,用于在专业人员开发的应用中实现数据的机密性、完整性和真实性。用于选择这些库的标准是它们被该领域的其他专业人员认可以及 FIPS 和 NIST 标准。

这些库可以与System.Security.Cryptography名称空间并行使用,提供了更全面的加密原语。

文献学

  1. NSec。在线可用: https://nsec.rocks/ .

  2. 充气城堡。在线可用: https://cryptobook.nakov.com/crypto-libraries-for-developers/c-crypto-libraries .

  3. 地狱。网上有: https://nugetmusthaves.com/Package/Inferno

  4. SecureBlackbox。在线可用: https://nugetmusthaves.com/Package/SecureBlackbox .

  5. libna for . net .在线可用: https://nugetmusthaves.com/Package/libsodium-net .

  6. NSec.Cryptography .可在线获得: www.nuget.org/packages/NSec.Cryptography/20.2.0 .

  7. 充气城堡特色。网上有: www.bouncycastle.org/csharp/index.html

  8. 充气城堡 NuGet 套餐。网上有: www.nuget.org/packages/BouncyCastle/

  9. 地狱的特点和项目。网上有: https://securitydriven.net/inferno/

  10. 地狱 NuGet 包。网上有: www.nuget.org/packages/Inferno/

  11. SecureBlackbox NuGet 包。网上有: www.nuget.org/packages/SecureBlackbox/

十、椭圆曲线密码术

椭圆曲线加密(ECC)是一种基于有限域上椭圆曲线的代数结构的公钥加密方法。ECC 可用于密码学应用和原语,如密钥协议、数字签名、伪随机发生器。它们可用于加密等操作,这种操作通过密钥协议与对称加密方案的组合来实现。在基于椭圆曲线(EC)的整数因式分解算法的几次尝试中可以看到一些其他有趣的用法,在密码学中的应用,例如伦斯特拉椭圆曲线因式分解(L-ECC) [1 ]。

微软 .NET 通过对椭圆曲线 Diffie-Hellman (ECDH)算法的下一代加密(CNG)实现,为 ECC 提供了强有力的支持。

对于密码分析使用的扩展功能,很少有开放源代码库可用于 C# 中的 ECC。使用最多的一个是 Bouncy Castle,支持 P-128 曲线,并计划将支持扩展到 P-256 曲线。

在这一章中,我们将提供两个例子。第一个例子(清单 10-1 )使用了微软的ECDiffieHellmanCng类。第二个例子(清单 10-2 )使用 Bouncy Castle 库来说明这些功能。

ecdiffiehellmancng class

清单 10-1 展示了如何使用ECDiffieHellmanCng类来设置密钥交换。我们将解释密钥如何进一步用于通过公共信道发送并由接收者解密的消息加密(见图 10-1 )。

img/493660_1_En_10_Fig1_HTML.jpg

图 10-1

使用 ECDiffieHellmanCng 的加密输出

下面,我们来仔细看看实现。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;

namespace ECDiffiedHellmanCng
{
    class Program
    {
        //** Alice User
        class UserA
        {
            //** represents the public key of alice
            public static byte[] pk_alice;

            public static void Main(string[] args)
            {
                string message_send_by_alice = "Hello Bob,
                                        Welcome to CryptoWorld!";

                using (ECDiffieHellmanCng user_alice = new
                                        ECDiffieHellmanCng())
                {
                    user_alice.KeyDerivationFunction =
                    ECDiffieHellmanKeyDerivationFunction.Hash;

                       user_alice.HashAlgorithm =
                          CngAlgorithm.Sha256;
                    pk_alice =
                               user_alice.PublicKey.ToByteArray();

                    //** we send to Bob
                    UserB user_bob = new UserB();
                    CngKey k = CngKey.Import(user_bob.pk_bob,
                                        CngKeyBlobFormat.EccPublicBlob);

                    byte[] alice_key =
                               user_alice.DeriveKeyMaterial(CngKey
                             .Import(user_bob.pk_bob,
                                  CngKeyBlobFormat.EccPublicBlob));

                    byte[] encryptionOfMessage = null;
                    byte[] initialize_vector = null;

                    //** sending the message
                    SendMessage(alice_key,
                                message_send_by_alice,
                                out encryptionOfMessage,
                                out initialize_vector);

                    Console.WriteLine("\n\nALICE Side");
                    Console.WriteLine("==========\n");
                    Console.WriteLine("\tAlice sends: {0}",
                                       message_send_by_alice);
                   Console.WriteLine("\tAlice public key:{0}",
                       PrintByteArray(pk_alice).ToString());

                   Console.WriteLine("\tThe initialization
                         vector (IV): {0} ",
                    PrintByteArray(initialize_vector).
                                                  ToString());

             Console.WriteLine("\tLength of the message: {0}”,
                        message_send_by_alice.Length.ToString());

             //** receiving message
             user_bob.ReceivingMessage(encryptionOfMessage,
                                           initialize_vector);
                }
            }

            //** the function will help us
            //** to convert a byte to string.
            public static StringBuilder PrintByteArray(byte[] bytes)
            {
                var string_builder = new StringBuilder("new byte[] { ");

                foreach (var theByte in bytes)
                {
                    string_builder.Append(theByte + " ");
                }

                string_builder.Append("}");
                return string_builder;
            }

            private static void SendMessage(byte[] key,
                                string theSecretMessage,
                                out byte[] encryption_message,
                                out byte[] initialize_vector)
            {
                //** we will use AES cryptography
                //** algorithm for encryption
           using (Aes aes_crypto_alg = new
                                   AesCryptoServiceProvider())
                {
                    aes_crypto_alg.Key = key;
                    initialize_vector = aes_crypto_alg.IV;

                    //** we encrypt the message using AES
                    using (MemoryStream encrypted_text = new
                              MemoryStream())
                    using (CryptoStream crypto_stream = new
                           CryptoStream(encrypted_text,
                           aes_crypto_alg.CreateEncryptor(),
                           CryptoStreamMode.Write))
                    {
                        byte[] clear_text =
                            Encoding.UTF8.GetBytes(theSecretMessage);
                        crypto_stream.Write(clear_text,
                                               0, clear_text.Length);

                  //** close the stream!
                        crypto_stream.Close();
                        encryption_message =
                        encrypted_text.ToArray();
                    }

                    Console.WriteLine("\n\n(Encrypted) Message
                               sent from Alice -> Bob");

                    Console.WriteLine("\tSecret message is: {0}", theSecretMessage.ToString());

                    Console.WriteLine("\tEncryptedMessage is: {0}", PrintByteArray(encryption_message).ToString());

                    Console.WriteLine("\tInitialize vector is: {0}", PrintByteArray(initialize_vector).ToString());
                }
            }
        }

        //** User Bob
        public class UserB
        {
            //** the public key of bon
            public byte[] pk_bob;
            private byte[] bob_key;

            public UserB()
            {
                using (ECDiffieHellmanCng user_bob = new
                ECDiffieHellmanCng())
                {
                    user_bob.KeyDerivationFunction =
                    ECDiffieHellmanKeyDerivationFunction.Hash;

                    user_bob.HashAlgorithm =
                                       CngAlgorithm.Sha256;

                    pk_bob = user_bob.PublicKey.ToByteArray();
                    bob_key =
                       user_bob.DeriveKeyMaterial(CngKey.Import
                        (UserA.pk_alice, CngKeyBlobFormat.EccPublicBlob));
                }
            }

            public void ReceivingMessage(
                           byte[] message_encrypted,
                           byte[] initialize_vector)
            {
                using (Aes aes = new
                            AesCryptoServiceProvider())
                {
                    aes.Key = bob_key;
                    aes.IV = initialize_vector;

                    //** let's decrypt the message
                    using (MemoryStream plaintext = new
                                          MemoryStream())
                    {
                        using (CryptoStream crypto_stream =
                           new CryptoStream(plaintext,
                           aes.CreateDecryptor(),
                           CryptoStreamMode.Write))
                        {
                           crypto_stream.Write(
                                   message_encrypted,
                                    0,
                                     message_encrypted.Length);

                            crypto_stream.Close();
                            string message = Encoding.UTF8.GetString(plaintext.ToArray());

Console.WriteLine("\n\nBOB Side");
Console.WriteLine("The plaintext message is: {0}", message);
Console.WriteLine("The length of the message received by Bob is : {0}",
message.Length.ToString());
Console.ReadKey();
                        }
                   }
               }
           }
       }
    }
}

Listing 10-1Implementation of ECDiffieHellmanCng

将 ECC 用于弹力城堡库

Bouncy Castle 库 [2 ]代表了一个可以在密码学中使用的 API 集合。对于这个例子,我们将使用 P-128 曲线支持,我们将为选定的曲线生成关键点,并且我们将在控制台中输出结果。

下面的例子由两个类组成,代表项目主类的Program类和EllipticCurveKeyGenerator类。项目结构的快速概述见图 10-2 。在运行项目之前,请从 Microsoft Visual Studio 或使用 PowerShell 安装 Bouncy 库。在“Bouncy Castle 安装”一节中,有一个关于如何使用 Microsoft Visual Studio 执行安装的指南。

img/493660_1_En_10_Fig2_HTML.jpg

图 10-2

充气城堡项目结构

The Program类(见清单 10-2 )代表项目的主类,包含三个功能:MainGeneratePKeys()AsymmetricCipherKeyPair GenerateKeys()。每个功能及其用途的描述如下:

  • Main函数:首先执行的主函数。包含一般的输出消息,并针对两种类型的大小(128 和 256 字节)调用GeneratePKeys()函数。

  • GeneratePKeys(int intSize)函数:该函数根据在Main函数中作为参数发送的大小生成一对密钥。TextWriter类帮助我们按顺序书写字符。它是一个抽象类。PemWriter类代表一个通用类,帮助我们处理 OpenSSL PEM 对象。ECPrivateKeyParameters类用于指定椭圆曲线的私钥参数,与ECPublicKeyParameters类的方式相同。

  • AsymmetricCipherKeyPair GenerateKeys(int keySize)函数:该函数使用 ECDSA 生成密码的密钥。为了生成参数,我们将使用KeyGenerationParameters构造函数类,我们将发送两个参数,随机安全值(secureRandom)和密钥大小(keySize)。使用Init() function,我们将初始化 ECDSA 算法,并使用GenerateKeyPair()函数生成密钥对。

EllipticCurveKeyGenerator类(参见清单 10-3 )覆盖了来自IAsymmetricCipherKeyPairGenerator接口的函数和方法。这个类有两个构造函数,EllipticCurveKeyGenerator()EllipticCurveKeyGenerator(string choose_algorithm)。注意,第一个在我们的例子中从未使用过。其余的功能及其目标如下:

  • Init(KeyGenerationParameters)函数:该函数的目标是初始化用于生成密钥的参数。

  • AsymmetricCipherKeyPair GenerateKeyPair()函数:顾名思义,该函数用于生成一对密钥。

  • ECMultiplier CreateBasePointMulitplier()函数:该函数返回一个定点乘数。

  • X9ECParameters IdentifyEllipticCurveByObjectIdentifier (DerObjectIdentifier object_identifier)函数:基于对象标识符,该函数返回符合 X9 格式的椭圆曲线的参数。

  • ECPublicKeyParameters GetCorrespondingPublicKey(ECPrivateKeyParameters private_key)函数:该函数是一个内部静态函数,其目的是根据作为私有参数为椭圆曲线设置的私钥,获得相应的公钥。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org;
namespace BCCase
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Generate private
                               /public key pair");

            //** 1st case when the key size is 128 b
            Console.WriteLine("CASE 1 - 128 Bytes\n");
            GeneratePKeys(128);

            //** 2nd case when the key size is 256 b
            Console.WriteLine("\n\n\nCASE 2 - 256 Bytes\n");
            GeneratePKeys(256);

        }

        public static void GeneratePKeys(int intSize)
        {
            //Generating p-128 keys 128 specifies strength
            var keyPair = GenerateKeys(intSize);
            TextWriter textWriter = new StringWriter();

            Org.BouncyCastle.OpenSsl.PemWriter pemWriter = new
               Org.BouncyCastle.OpenSsl.PemWriter(textWriter);

            pemWriter.WriteObject(keyPair.Private);
            pemWriter.Writer.Flush();

             string privateKey = textWriter.ToString();

            Console.WriteLine("\tThe private key is:");
            Console.WriteLine("\t\t\t {0}", privateKey.ToString());

            ECPrivateKeyParameters privateKeyParam =
                                 (ECPrivateKeyParameters)keyPair.Private;

            Console.WriteLine("\tD parameter: {0}",
                                 privateKeyParam.D.ToString());

            textWriter = new StringWriter();

            pemWriter = new
                   Org.BouncyCastle.OpenSsl.PemWriter(textWriter);

            pemWriter.WriteObject(keyPair.Public);
            pemWriter.Writer.Flush();

                 ECPublicKeyParameters publicKeyParam =
                            (ECPublicKeyParameters)keyPair.Public;

            string publickey = textWriter.ToString();

            Console.WriteLine("\nThe public key is:");
            Console.WriteLine("\t\t\t{0}", publickey.ToString());

             Console.WriteLine("\nX parameter: {0}",
               publicKeyParam.Q.XCoord.ToBigInteger().ToString());

             Console.WriteLine("Y parameter: {0}",
               publicKeyParam.Q.YCoord.ToBigInteger().ToString());
        }

          public static AsymmetricCipherKeyPair GenerateKeys(int keySize)
        {
            //** we will choose ECDSA for generating the keys
            var gen = new
                          Org.BouncyCastle.Crypto.Generators.
                             EllipticCurveKeyGenerator("ECDSA");

            //** randomly generation
            var secureRandom = new SecureRandom();

            //Parameters creation using the random and keysize
            var keyGenParam = new
                                KeyGenerationParameters(secureRandom,
                                          keySize);

            //** send the parameters for generating
            gen.Init(keyGenParam);

            //** generate the key pair
            return gen.GenerateKeyPair();
        }
    }
}

Listing 10-2Generating Cryptographic Keys Using Bouncy Castle

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.TeleTrust;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.EC;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math.EC.Multiplier;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;

namespace Org.BouncyCastle.Crypto.Generators
{
    class EllipticCurveKeyGenerator : IAsymmetricCipherKeyPairGenerator
    {
        private readonly string theAlgorithm;

        private ECDomainParameters theParameters;
        private DerObjectIdentifier publicKeyParamSet;
        private SecureRandom random;

        //** first constructor
        public EllipticCurveKeyGenerator()
            : this("EC")
        {
        }

        //** second constructor
        public EllipticCurveKeyGenerator(
            string choosen_algorithm)
        {
            this.theAlgorithm = choosen_algorithm ??
                throw new ArgumentNullException("algorithm");
        }

        public void Init(KeyGenerationParameters theParams)
        {
            if (theParams is ECKeyGenerationParameters
                                           elliptic_curve_parameters)
            {
                this.publicKeyParamSet =
                  elliptic_curve_parameters.PublicKeyParamSet;
                this.theParameters =
                   elliptic_curve_parameters.DomainParameters;
            }
            else
            {
                DerObjectIdentifier oid;
                switch (theParams.Strength)
                {
                    case 192:
                        oid = X9ObjectIdentifiers.Prime192v1;
                        break;
                    case 224:
                        oid = SecObjectIdentifiers.SecP224r1;
                        break;
                    case 128:
                        oid = SecObjectIdentifiers.SecP128r1;
                        break;
                    case 239:
                        oid = X9ObjectIdentifiers.Prime239v1;
                        break;
                    case 256:
                        oid = X9ObjectIdentifiers.Prime256v1;
                        break;
                    case 384:
                        oid = SecObjectIdentifiers.SecP384r1;
                        break;
                    case 521:
                        oid = SecObjectIdentifiers.SecP521r1;
                        break;
                    default:
                        throw new
                              InvalidParameterException("The key
                              size is not
                            defined or it is unknown.");
                }

                X9ECParameters ecps = IdentifyEllipticCurveByObjectIdentifier (oid);

                this.publicKeyParamSet = oid;
                this.theParameters = new ECDomainParameters(
                    ecps.Curve,
                    ecps.G,
                    ecps.N,
                    ecps.H,
                    ecps.GetSeed());
            }

            this.random = theParams.Random;

            if (this.random == null)
            {
                this.random = new SecureRandom();
            }
        }

        public AsymmetricCipherKeyPair GenerateKeyPair()
        {
            BigInteger n = theParameters.N;
            BigInteger d;
            int minWeight = n.BitLength >> 2;

            for (; ; )
            {
                d = new BigInteger(n.BitLength, random);

                if (d.CompareTo(BigInteger.Two) < 0 ||
                                              d.CompareTo(n) >= 0)
                    continue;

                if (WNafUtilities.GetNafWeight(d) < minWeight)
                    continue;

                break;
            }

            ECPoint ellipticCurvePoint =
                       CreateBasePointMultiplier().
                        Multiply(theParameters.G, d);

            if (publicKeyParamSet != null)
            {
                return new AsymmetricCipherKeyPair(
                          new ECPublicKeyParameters(theAlgorithm, ellipticCurvePoint, publicKeyParamSet),
                      new ECPrivateKeyParameters(theAlgorithm,
                                        d,
                                        publicKeyParamSet));
            }

            return new AsymmetricCipherKeyPair(
                new ECPublicKeyParameters(theAlgorithm,
                                           ellipticCurvePoint,
                                           theParameters),
                new ECPrivateKeyParameters(theAlgorithm,
                                                      d,
                                                      theParameters));
        }

        protected virtual ECMultiplier
                                     CreateBasePointMultiplier()
        {
            return new FixedPointCombMultiplier();
        }

        internal static X9ECParameters
                     IdentifyEllipticCurveByObjectIdentifier
                    (DerObjectIdentifier
                             object_identifier)
        {
            X9ECParameters x9_elliptic_curve_parameters =
                     CustomNamedCurves.GetByOid(object_identifier);
            if (x9_elliptic_curve_parameters == null)
            {
                x9_elliptic_curve_parameters =
                            ECNamedCurveTable.GetByOid(
                                                    object_identifier);
            }
            return x9_elliptic_curve_parameters;
        }

        internal static ECPublicKeyParameters
                 GetCorrespondingPublicKey(
            ECPrivateKeyParameters private_key)
        {
            ECDomainParameters ellipticCurve_DomainParameters
                                  = private_key.Parameters;

            ECPoint ellipticCurvePoint =
                    new FixedPointCombMultiplier().

               Multiply(ellipticCurve_DomainParameters.
                    G, private_key.D);

            if (private_key.PublicKeyParamSet != null)
            {
                return new
                   ECPublicKeyParameters(
                       private_key.AlgorithmName,
                       ellipticCurvePoint,
                       private_key.PublicKeyParamSet);
            }

            return new
                     ECPublicKeyParameters(
                              private_key.AlgorithmName,
                      ellipticCurvePoint,
                               ellipticCurve_DomainParameters);
        }
    }
}

Listing 10-3The Generating of Elliptic Curve Keys

输出如图 10-3 所示。

img/493660_1_En_10_Fig3_HTML.jpg

图 10-3

钥匙

充气城堡装置

为了运行代码,您必须确保 Bouncy Castle 已正确安装在您的计算机上。为了实现这一点,首先你需要进入微软 Visual Studio ➤工具➤ NuGet 包经理➤管理 NuGet 包的解决方案,如图 10-4 所示。

img/493660_1_En_10_Fig4_HTML.jpg

图 10-4

充气城堡的安装

接下来的步骤是选择弹力城堡库,点击安装(见图 10-5 ,点击确定同意将要进行的更改(见图 10-6 )。图 10-7 显示了安装的确认。

img/493660_1_En_10_Fig7_HTML.jpg

图 10-7

安装确认

img/493660_1_En_10_Fig6_HTML.jpg

图 10-6

安装前预览更改

img/493660_1_En_10_Fig5_HTML.jpg

图 10-5

从列表中选择充气城堡进行安装

结论

在本章中,我们讨论了如何使用椭圆曲线加密技术,以及如何实现生成密钥和加密消息的实用解决方案。

在本章结束时,您现在已经了解了以下内容:

  • 微软的区别 .NET 椭圆曲线支持和开源库,如弹力城堡

  • 如何使用ECDiffieHellmanCng类加密消息和生成密钥

  • 如何使用 Bouncy Castle 库生成加密密钥对

在微软使用 ECC .NET 可能非常棘手,如果您不完全了解框架/库,代码的性能和质量可能会下降一大步。

文献学

  1. 伦斯特拉椭圆曲线密码。网上有: https://en.wikipedia.org/wiki/Lenstra_elliptic-curve_factorization

  2. 充气城堡。网上有: www.bouncycastle.org/

十一、基于格的密码学

在这一章中,我们将讨论基于格的密码学,它在密码学领域的重要性及其未来的挑战,以及我们如何设计和开发适合密码学应用的实用解决方案。

随着我们进入一个量子时代,量子密码原语的数量越来越多,基于格的密码的重要性是至关重要的。目前,基于格的构造是后量子密码术的重要候选。根据理论家的观点,RSA、Diffie-Hellman 或椭圆曲线密码系统等密码系统很容易被量子计算机攻击。一些基于格的密码构造在抵抗经典计算机和量子计算机攻击方面取得了很好的效果。

在实践中应用格作为一种加密解决方案并不是一件容易实现的任务,因为数学背景是复杂的,涉及到理解不同的抽象概念。在这一章中,我们将使用 NTRU-夏普库 [1 ],这是一个免费/开源库,包含公钥密码系统 [2 ]的 NTRU 实现。

数学背景

在这一节中,我们将提供一个基本概念和方法的快速概述,这些概念和方法构成了你应该知道的晶格数学背景的最小理论知识。

让我们考虑一下空间 n 和它的一个基础为( b 1 ,…, b nb 1 ,…,bn【ℝ】一个格表示与基构成线性组合的所有整数的集合,即$$ \mathcal{L}=\left{\sum {a}_i{b}_i\right|\ {a}_i\in \mathbb{Z}\Big}. $$格的一个例子是 n ,它是通过经典基 生成的。在密码学中,来自格装置的一个常见的困难假设是最短向量问题(SVP)。SVP 要求我们估计非零点阵向量的欧几里德距离的最小值。

使用格的密码系统之一是 NTRU [2 ],它可以抵抗使用 Schor 算法的攻击。NTRU 的 SVP 硬度假设包括将特定多项式分解成两个系数极低的多项式。注意,需要分解的特定多项式应该从截断的多项式环中定义。此外,以下是 NTRU 的密钥生成、加密和解密算法:

  • 密钥生成。选择 n 和两个多项式 fg ,它们的最大次数为n1,系数来自集合{ 1,0,1}。多项式 f 应该有一个附加的性质,即fpfq应该存在其中ffp= 1(mod p)和 f私钥是kpriv=(ff pg )公钥是kpub=p

** 加密。需要加密的消息是多项式 m ,其系数在{ 1,0,1}中。随机选择一个低系数的多项式 r ,由加密消息的发送者保密。 m 的加密计算为c=rh+m(mod q)。

*   **Decryption** . To decrypt the message, the following computations need to be made:

![$$ a=f\bullet c\ \left(\mathit{\operatorname{mod}}\ q\right) $$](https://gitee.com/OpenDocCN/vkdoc-csharp-zh/raw/master/docs/pro-crypto-cs/img/493660_1_En_11_Chapter_TeX_Equa.png)* 

*$$ b=a\ \left(\mathit{\operatorname{mod}}\ p\right) $$

$$ m={f}_p\bullet b $$

实际实施

如上所述,为了实现和说明加密/解密的基本操作,我们使用了 NTRU-夏普库。该库的供应商做出了惊人的努力,并根据自由软件基金会发布的 GNU 通用公共许可证(许可证的第 3 版)的条款免费提供该库。NTRU-夏普是一个非常复杂的库(见图 11-1 ),包含了对密码学原语和基于格的原语的重要贡献。

img/493660_1_En_11_Fig1_HTML.jpg

图 11-1

NTRU 图书馆结构

实际实现的目的是展示如何使用该库来提供消息的加密和解密。要实现这一点,你需要熟悉NTRUEngine项目。为此,一旦项目加载到您喜欢的 IDE(集成开发环境)中,您需要导航到NTRU文件夹(参见图 11-1 )。在这里,您可以找到您需要的所有类、结构和方法/函数,例如NTRUParametersNTRUParamSetsNTRUKeyPairNTRUKeyGeneratorNTRUEncryptNTRUDecrypt。我们将仔细查看它们,并提供一些细节(见表 11-1 和表 11-2 )以便您快速了解这些类的用途及其功能/方法。

表 11-2

NTRU 方法

|

字段、属性、构造函数、方法、函数名

|

描述

|

类型

|
| --- | --- | --- |
| NTRUParamSets.``APR2011743FAST | 该参数集提供 256 位安全性,但使用乘积形式多项式和 f=1+pF。 | 田 |
| NTRUKeyGenerator(new NTRUParameters()) | 用一组加密参数构造一个新实例 | 构造器 |
| GenerateKeyPair() | NTRU 密钥对容器 | 方法 |
| Initialize() | 初始化加密的密码。这个Initialize()方法只是为了加密。 | 方法 |
| Encrypt(byte[] Input) | 要加密的消息 | 方法 |
| Decrypt(byte[] Input) | 要解密的消息 | 方法 |

表 11-1

你上过课吗

|

类别名

|

描述

|
| --- | --- |
| NTRUParameters | 创建、读取和写入NTRUEncrypt的参数设置 |
| NTRUParamSets | 一组预定义的 EES 加密参数集 |
| NTRUKeyPair | NTRU 密钥对容器 |
| NTRUKeyGenerator | 这个类实现了 NTRU 公钥加密系统的密钥对生成 |
| NTRUEncrypt | 一种 NTRU 非对称密码的实现 |

现在让我们看一下这个例子,了解它是如何工作的。例如,我们选择了一个简单的消息进行加密,如图 11-2 所示。

img/493660_1_En_11_Fig2_HTML.jpg

图 11-2

基于 NTRU 格的密码术加密实例

清单 11-1 包含了用于加密和解密的主要函数。请注意,要运行此示例,您应该做以下事情:

  • https://github.com/Steppenwolfe65/NTRU-NET [1 下载库。

  • 转到名为 Test 的项目,通过复制并粘贴清单 11-1 中的代码来修改名为Program.cs的文件。

  • 作为第二种选择,使用本书提供的 GitHub 库。

using System;
using Test.Tests.Arith;
using Test.Tests.Encode;
using Test.Tests.Encrypt;
using Test.Tests.Polynomial;
using System.Runtime.InteropServices;
using System.Diagnostics;

//** NTRU Engine libraries
using VTDev.Libraries.CEXEngine.
                      Crypto.Cipher.Asymmetric.Interfaces;
using VTDev.Libraries.CEXEngine.
                      Crypto.Cipher.Asymmetric.Encrypt.NTRU;
using VTDev.Libraries.CEXEngine.Tools;
using VTDev.Libraries.CEXEngine.Crypto.Prng;
using VTDev.Libraries.CEXEngine.Crypto;

//** The test project
using Test.Tests;

using System.Text;
using System.ComponentModel;

namespace Test

{
    static class Program
    {
        static void Main(string[] args)

        {
            //** we will use as an example the
            //** following example
            //** "Welcome to Apress and Enjoy the adventure"
            byte[] data = new byte[41] { 87, 101, 108, 99,
                                  111, 109, 101, 32, 116, 111,
                                  32, 65, 112, 114, 101, 115,
                                  115, 32, 97, 110, 100, 32,
                                  69, 110, 106, 111, 121, 32,
                                  116, 104, 101, 32, 97, 100,
                                  118, 101, 110, 116, 117, 114,
                                  101 };
            Console.WriteLine("Text to encrypt is:
                               { Welcome to Apress
                                  and Enjoy the adventure}");

            Console.WriteLine("Byte representation (ASCII):
                              {0}\n\n", PrintByteArray(data));

              //** Enc() function will do the
              //** encryption and decryption
            Enc();

            Console.ReadKey();
        }

        private static void Enc()
        {
            //** the predefines parameters
            NTRUParameters prm = NTRUParamSets.APR2011743FAST;
            NTRUKeyPair keyPair;
            byte[] enc, dec;
            byte[] data = { 87, 101, 108, 99, 111, 109, 101,
                            32, 116, 111, 32, 65, 112, 114,
                            101, 115, 115, 32, 97, 110, 100,
                            32, 69, 110, 106, 111, 121, 32,
                            116, 104, 101, 32, 97, 100, 118,
                            101, 110, 116, 117, 114, 101 };

            //** generate and display the key pair

            using (NTRUKeyGenerator gen = new
                                          NTRUKeyGenerator(prm))
            {
                keyPair = (NTRUKeyPair)gen.GenerateKeyPair();
                Console.WriteLine("\t\tKey pair is:");

                Console.WriteLine("\t\t\tPUBLIC KEY is: {0}",
                   PrintByteArray(keyPair.PublicKey.ToBytes()));

                 Console.WriteLine("\n\t\t\tPRIVATE KEY is:
                    {0}",
                   PrintByteArray(keyPair.PrivateKey.ToBytes()));
            }

            // encrypt a message
            using (NTRUEncrypt ntru = new NTRUEncrypt(prm))
            {
                // initialize with public key for encryption
                ntru.Initialize(keyPair.PublicKey);
                // encrypt using public key
                enc = ntru.Encrypt(data);
                Console.WriteLine("\n\n\t\t\tTEXT ENCRYPTED:
                               {0}", PrintByteArray(enc));
            }

            // decrypt a message
            using (NTRUEncrypt ntru = new NTRUEncrypt(prm))
            {
                // initialize with both keys for decryption
                ntru.Initialize(keyPair);
                // decrypt using key pair
                dec = ntru.Decrypt(enc);

                Console.WriteLine("\n\t\t\tTEXT DECRYPTED:
                         {0}", PrintByteArray(dec));
            }
        }

        //** conversion of ascii to text
        private static string ConvertASCII_To_Text(byte[] val)
        {
            //** Instantiate an ASCII encoding object
            ASCIIEncoding ascii = new ASCIIEncoding();

            foreach (var value in val)
                Console.Write(value);
            Console.WriteLine();

            //** Decode the bytes and display
            //** the resulting Unicode string.
            String decoded = ascii.GetString(val);

            return decoded.ToString();
        }

        //** perform parsing of the byte values byte by byte
        public static string PrintByteArray(byte[] bytes)
        {
            var string_builder = new StringBuilder
                                           ("new byte[] { ");
            foreach (var theByte in bytes)
            {
                string_builder.Append(theByte + " ");
            }
            string_builder.Append("}");
            return string_builder.ToString();
        }
    }
}

Listing 11-1Encryption/Decryption Operation

Using NTRU-Sharp

结论

在这一章中,我们讨论了基于格的密码学及其重要性。在本章结束时,您现在已经了解了以下内容:

  • 基于格的密码学的重要性及其对密码学未来的影响

  • 如何应用 NTRU-夏普商业软件库

  • 如何实现与网格相关的实用功能和方法

  • 如何在复杂流程中使用byte类型

文献学

  1. NTRU 夏普。网上有: https://github.com/Steppenwolfe65/NTRU-NET

  2. J.霍夫斯坦,j .皮弗和 J.H .西尔弗曼,“NTRU:一个基于环的公钥密码系统。”在 J. P .布勒(编)算法数论中。蚂蚁 1998。计算机科学讲义,第 1423 卷。斯普林格,柏林,海德堡。1998.

  3. D.铜匠和 a .沙米尔,《晶格对 NTRU 的攻击》在 W. Fumy (ed) 密码学进展— EUROCRYPT '97。欧洲墓穴 1997。计算机科学讲义,第 1233 卷。施普林格,柏林,海德堡。1997.

  4. C.Gentry 和 M. Szydlo,“修改的 NTRU 签名方案的密码分析”密码学的进展——欧洲密码 2002 年。欧洲密码 2002。计算机科学讲义,第 2332 卷。斯普林格,柏林,海德堡。2002.*

十二、可搜索加密

可搜索加密(SE)允许将加密数据外包给可能不可信的第三方服务提供商,同时让用户能够以安全可靠的方式直接搜索加密数据。可搜索加密是同态加密的一个特例,这将在第十三章中讨论。

考虑以下情况。数据所有者 A 想要将一些文档存储在服务器上,但是同时接收者 B 需要能够访问和搜索这些数据。为了实现数据的这一属性,A 选择使用 B 的公钥加密文档,然后 A 将它们存储在服务器上。当 B 想要搜索包含特定关键字的文档时,比如说“programming”,他们使用“programming”关键字和他们的私钥生成一个名为 trapdoor 的值。然后,B 将陷门提交给服务器,服务器将根据算法执行搜索,并将符合标准的文档发送给 B。

下面是一个更实际的场景:一家公司正在开发一个软件解决方案,其中包括从客户那里获得的社会安全号(SSN)。规则和良好实践表明,SSN 在不参与数据处理时必须加密。另一方面,当员工需要搜索用户帐户时,他们使用 SSN。如果员工在工作时没有“看到”社会保障网络,该怎么办?使用可搜索的加密方案将使这成为可能。

有了这些例子,我们可以说可搜索加密是一种技术,通过这种技术,用户可以在加密的内容中搜索特定的数据。它可以应用于多个领域,例如医疗保健、医生处理患者的医疗文件、教育等等。

成分

可搜索加密方案有两种类型的组件:可搜索加密过程中涉及的实体和方案的算法。接下来我们将介绍这些组件。

实体

当一个软件解决方案被实现时,更多的方面应该在实现之前被澄清。谁将使用该应用?谁来维护?数据有哪些类型,谁可以访问它们?数据将存储在哪里?等等。从可搜索加密的角度来看,整个过程涉及以下实体:

  • 数据所有者。数据所有者拥有一套由关键字描述的 n 文档 D = { D 1 ,…, D n 】。文档和关键词都将被外包出去,比如放在云服务器上。在将文档存储在服务器上之前,数据所有者将使用特定的加密方案对它们进行加密。数据所有者被认为是可信的实体。

  • 数据用户。这是可以触发搜索过程的授权用户。该用户根据需要在加密内容中搜索的关键字生成陷门值。此外,如果资料使用者拥有私人密码匙,他们可将收到的文件解密。

  • 服务器。服务器存储加密数据,并根据从数据用户处收到的陷门值执行搜索算法。服务器被认为是半可信的或诚实但好奇的,这意味着它按照指示执行搜索算法,但可以对提供给它的数据执行额外的分析。

注意,从上面的描述中,数据所有者可以是数据用户。

类型

可搜索加密方案可以根据密码类型分为两类:对称可搜索加密 (SSE)方案和带关键字搜索的公开加密 (PEKS)方案。在对称可搜索加密方案中,只有一个密钥用于加密或解密内容,在其他特定算法中也是如此,这一点您将在本节后面看到。具有关键字搜索的公共加密方案使用两种类型的密钥,即用于加密的公共密钥和用于解密的私有(秘密)密钥。

SSE 方案的算法是 [1

  • KeyGeneration。密钥生成算法由数据所有者运行。为了调用它,数据所有者需要一个安全参数 λ 作为输入,基于这个参数,算法将输出一种类型的密钥,即秘密密钥 SK

  • BuildIndex。构建索引结构的算法由数据所有者运行。该算法需要密钥 SK 和文档集 D 作为输入,输出是索引结构 I 。更确切地说,该算法从一个空的索引结构开始,然后它获取集合中的每个文档,并且对于每个文档,该算法向索引结构添加一些描述该文档的关键字。注意,在添加到索引结构之前,关键字是加密的(使用秘密密钥 SK )。

  • Trapdoor。数据用户运行这个算法。为了生成陷门值,该算法将搜索所基于的所需关键字 kw 和密钥 SK 作为输入,输出是陷门值 T kw 。请注意,陷门算法不只是对 kw 进行简单的加密。相反,它会添加一个噪波值或使用某种控制。

  • Search。服务器执行搜索算法。输入是之前生成的陷门值Tkw,索引结构 IBuildIndex算法产生。注意,搜索算法并不做 ITkw的简单匹配。搜索算法要知道如何处理或者如何处理特殊值Tkw(别忘了Tkw不是简单的加密)。

如果服务器找到一份或多份符合标准的文件,它将把它们发送给数据用户;否则,它会发送一个消息。注意,上面没有提到加密和解密算法。数据所有者可以为文档本身和可搜索的加密方案选择不同的加密方案。这是因为文档不与可搜索加密方案的任何组件直接交互。上述所有算法仅适用于关键字和/或加密关键字的索引结构。

此外,PEKS 方案的算法是 [2

  • KeyGeneration。与 SSE 的密钥生成类似,数据所有者也运行这个算法。输入是安全参数 λ ,输出是一对密钥,公钥和私钥( PKSK )。

  • Encryption。加密算法由数据所有者运行,输入是公钥 PK 和一个关键字 KW 。输出为 KW 的加密 SW

  • Trapdoor。这个算法类似于 SSE 的算法。数据用户使用他们的密钥 SK 和关键字 KW 作为输入来生成陷门值,以触发搜索。输出为陷门值 T KW

  • Test。服务器执行测试算法,将公钥 PK、加密文本 C (加密文本KW)和陷门值 T KW 作为输入。如果'=KW,测试算法的输出为 1,否则为 0。

*对于陷门算法和测试算法也出现了相同的观察结果;它们不仅仅分别是常规加密和常规匹配。然而,上述 SSE 和 PEKS 方案的算法是在本领域的早期工作中介绍的,分别在 [1 和 [2 中介绍。随着时间的推移,随着更多类型的搜索可能性被探索,它们经历了转变。例如,一些作品允许多个关键字搜索,另一些作品允许基于关键字 [3 , 4 ]的模糊搜索(允许小的错别字或格式不一致),还有一些作品允许语义搜索(在这里,数据用户从查询关键字的语义字段接收包含关键字的文档) [5 ]等。其他工作以文档为中心,即直接在服务器上更新的文档,而不需要从服务器上检索、解密、更新、加密并再次存储在服务器上或直接在服务器上更新的索引结构 [6 ]。然而,在任何可搜索的加密方案中存在的算法是陷门和搜索/测试算法,当然还有加密和解密。

安全特征

可搜索加密需要保护的两件事情是搜索模式和访问模式。搜索模式是可以从两个搜索结果来自相同查询关键词的事实中获知的信息。访问模式是针对给定关键字 KW 返回的一组文档。此外,可搜索加密方案也应该满足关于搜索查询的安全要求。根据 [7 ],SE 方案应该提供受控搜索(只有授权用户可以提交搜索查询)、加密查询(查询搜索本身需要在被发送到服务器之前被加密)和查询隔离(服务器从它接收的查询中什么也不知道)。

SSE 方案应该确保索引结构不被破坏,因此应该抵抗 IND1-CKA 和/或 IND2-CKA(为索引选择的关键字攻击)。在 IND1-CKA 中,为所有文档选择相同数量的关键词,而在 IND2-CKA 中,文档可以由不同数量的关键词来描述。类似地,PEKS 方案应该抵抗选择关键字攻击(由攻击者和管理 PEKS 方案的结构之间的挑战组成)。

最近的安全需求是所谓的动态 SE 方案的前向前向和后向隐私,该方案支持直接在服务器上以加密格式插入、更新或删除一组文档或关键字的操作。向后和向前隐私涉及在插入/删除/更新过程中获知的信息。后向隐私处理当搜索基于在当前搜索之前被删除的文档中存在的关键字时获得的信息,而在前向隐私中,当前更新操作与先前操作无关。

简单的例子

然而,即使可搜索加密有很大的潜力,由于其抽象性,在常规应用中从零开始实现一个方案是非常困难的(错误的实现导致错误的加密,因此导致更少/没有安全性,并且可能消耗更多的资源)。Crypteron 1 开发了为数不多的提供可搜索加密工具的库之一。在他们的网站上,他们解释如何安装他们的库并把它们包含在项目中。在安装了所需的库之后,开发人员需要做的就是在允许基于关键字的搜索查询的类的字段上放置一个标志。受他们的例子的启发,清单 12-1 展示了如何将它应用到一个名为Student的类,并基于他们的个人代码进行搜索(参见清单 12-1 )。

public class Student
{
    public int StudentId {get; set;}

    [Secure]
    public string StudentName {get; set;}

    [Secure(Opt.Search)]
    public string StudentPersonalCode {get; set;}
}

var queryKeywod =
    SecureSearch.GetPrefix("123456789");

var resultStudent = myDatabase.Students.Where(p =>
    p.StudentPersonalCode.StartsWith(searchPrefix))

Listing 12-1A Simple Example Framework for Searchable Encryption Implemention

在本例中,请注意为StudentPersonalCode字段启用可搜索属性的Opt.Search选项。在搜索之前,查询关键字由SecureSearch.GetPrefix处理。

复杂的例子

在下一个示例 [18 ]中,您将看到可搜索加密代表了一种高级且非常强大的加密技术,它为用户在加密文档中搜索关键字提供了优势。了解系统中的参与者非常重要。为此,我们对他们进行了如下分类:数据用户,他拥有一组文档 S = { D 1 ,…,Dn},通过生成密钥、加密密钥并将其存储在云服务器上,使系统准备就绪;数据所有者,他有在云服务器上提交搜索查询的可能性和优势;以及云服务器,存储加密文档并调用搜索算法。典型的 SE 技术包含以下随机化算法:

  • 密钥生成(λ)(KpK s ):密钥的生成基于安全参数。基于安全参数 λ ,生成一对公钥和私钥组成的集合,分别为(Kp)(Ks)。

  • 加密(DIKp)→CI:算法会根据取公钥K的加密,输出加密后的文件 C i

  • build index(DIwKp)→I:一个构建索引算法有以下参数作为输入:文档 D i ,与文档关联的关键字 w,以及公钥K输出由基于文档和关键字之间的关联的索引结构表示。**

  • 陷门 ( wKs)→tw:陷门算法有两个参数作为输入:进行搜索的关键字和密钥。输出是一个陷门值 t w

  • 搜索(twK pI ) → C :搜索算法有以下参数:陷门值和公钥。输出表示带有关键字的加密文档$$ C=\left{{C}_{i_1},\dots, {C}_{i_w}\right} $$

  • D ( CKs)→D:解密算法将加密文件的 C 和密钥 K s 作为输入参数。输出由解密文档的集合$$ D=\left{{D}_{i_1},\dots, {D}_{i_w}\right}\subset S $$表示。

如今,椭圆曲线(见第十章)被用于区块链( [14 、 [15 )或物联网( [16 、 [17 )等重要课题。

在图 12-1 [18 ]中,你可以看到一个结合椭圆曲线加密和大数据环境的可搜索加密方案的真实例子(参见第十六章)。我们使用椭圆曲线数字签名算法(ECDSA)来保护通过电子学习平台向学生提供的课程。来自 ECDSA 算法的私钥被传递给可搜索的加密方案,并被用作安全参数( λ )。

img/493660_1_En_12_Fig1_HTML.jpg

图 12-1

一个实用的可搜索加密方案的例子 [18

没有可搜索加密的实现 .NET 或开源。我们深入研究了最近的研究,目前我们找不到任何作为库、模块或框架的真正实现。为了实现可搜索的加密方案,在执行实现之前应该考虑一些基本准则。指导方针如下:

  • 软件应用的架构(服务器、数据库、服务等)。)

  • 硬件设备以及如何为当前使用安全和加密机制的应用构建硬件设备

  • 设计架构时,将可搜索的加密步骤表示为独立的算法,并且可以在整个网络基础架构的最终用户之间正确部署

如果你看一个可搜索的加密方案(见图 12-1 ),你可以看到它被分为多个步骤。每一步都有一个算法,可以作为可搜索加密方案的一个独立实例。这些实例可以表示为软件模块、软件服务,甚至物联网设备(例如英特尔 NUC 电脑或 Raspberry PI 设备)。软件模块或服务可以在分布式网络的用户之间分布和部署,例如云计算或用于中小型企业架构的普通网络基础设施。

以下算法 [18 描述了图右侧标题为 大数据环境 的图 12-1 中的步骤。为了实现适当的实施,有必要且要求对作为单独和独立算法的步骤有清晰的印象。这些步骤是

  1. ( K OK sPP)←key generation(1λPS )。这表示由数据 O 的所有者运行的概率算法,该算法将采用安全参数 λ ,策略 P ,以及服务器 S 的身份,并且基于这些参数将输出所有者的秘密密钥 K O ,服务器密钥 K s

  2. ID←build index(DaugK OPP )。这表示由所有者调用的概率算法。它将获取数据集Daug的描述和所有者的密钥( K O )并输出一个索引 I D

  3. KU(Uλ ( u ), K OPP )。这是一种概率算法,由所有者调用,用于在电子学习平台系统中注册新用户。该算法将获取用户的新身份和用户的访问级别,以及所有者的 O 密钥,并为新用户输出秘密。

*** 陷门 ( ωλ(u)←查询 ( ωK u )。这是一种确定性算法,由具有适当权限 λ ( u )的用户调用,以生成搜索查询。该算法将把关键字ω∈δ(其中δ表示关键字的字典)和用户的密钥作为输入,并输出查询令牌陷门(ωλ(u)。

 *   **R** <sub>( **ω** ,**λ**(**u**)</sub>**←搜索** ( **陷门** <sub>( **ω** ,**λ**(**u**)</sub>, **I** <sub>**D**</sub> 确定性算法由服务器( *S* )运行,以在索引中搜索包含关键字 *ω* 的数据项。它将接受搜索查询和索引,并将搜索结果返回为***R***<sub>(***ω***, ***λ** ,*(**)**</sub>**,包括一组数据项的标识符 *d* <sub>*j*</sub> *λ* ( *u* ) 其中包含 *ω* 使得*λ*(*d*<sub>*j*</sub>)≤*λ*(*u*),其中 *λ* ( *u* )**

***   (**K**<sub>OT5)**←revoke user**(**u**, **K** <sub>**O**</sub> , **PP** )。概率算法由所有者*或*运行,以从系统中撤销特定用户。它将获取用户的 id 以及数据所有者和服务器的密钥,并为所有者和服务器输出新的密钥。</sub>

 ****

如果对于所有的 k ,对于所有的 K OK Skey gen(1λP ,对于所有的d 对于 BuildIndex 输出的所有ID(DaugK O ),对于所有ω∈δ,对于所有UU *λ ( u ), PP ),搜索(IDT ω* , λ ( u ) )****

清单 12-2 中的例子是使用 LINQ 到 SQL 开发的,是实现图 12-1 中提出的可搜索加密方案的实用方法。必须提到的是,实现纯粹是指示性的,因为实现(框架、库等。)不存在。

public class KeyGeneration
{
   //** Step 1
   //** the algorithm from KeyGeneration step (algorithm)
   //** are runned and invoked by the data owner

   //** global variables
   public string securityParameter = string.Empty();
   public string ownerID = string.Empty();
   public string policyContent = string.Empty();
   public string serverIdentity = string.Empty();

   //** the function will return the policy,
   //** as a content or file
   public string GetPolicy(IServiceCollection policyService)
   {
      policyService.AddAuthorization(policyChoices =>
      {
        policyChoices.AddPolicy("Policy content", policy
                          =>policy.Requirements.Add(new
                           UserPolicy()));
    });

    policyContent = policyChoices.ToString();
    }
    //** getting server identity can be tricky and it has
    //** different meanings, such as the name of computer,
    //** IP, active directory reference name etc...
    //** For the current example we will use the hardware ID
    public string GetServerIdentity()
    {
        ManagementObject hardwareID = new
        ManagementObject(@"win32_logicaldisk.
        deviceid=""c:""");
        hardwareID.Get();
        string idOfTheHardware = hardwareID
        ["VolumeSerialNumber"].ToString();

        serverIdentity = ifOfTheHardware;

        return serverIdentity;
}

    //** class constructor
    public KeyGeneration(){}

    //** let's generate the secret key, server key
    //** and public parameters
    //** "#" represents the separator
    public string ReturnParameters()
    {
         StringBuilder sbParameters = new StringBuilder();

         sbParameters.Append(ownerSecretKey + "#" +
         serverKey + "#" +);
    publicParameters);
    }
}

public class BuildIndex
{
    //** Step 2
    //** the algorithm from BuildIndex step (algorithm)
    //** are runned and invoked by the data owner

    //** constructor of the class
    public void BuildIndex(){}

    //** the function centralize the build index parameters
    //** after their initialization and processing
    public void UseBuildIndexParameters()
    {
          LinkedList descriptionDataSet =
          new LinkedList();
          string ownerPrivateKey = string.Empty();
          string outputIndex = string.Empty();
    }

    //** simulation of getting the data set and their
    //** descriptions
    public LinkedList GetDataSet()
    {
        for(int i=0; i<dataSet.Length; i++)
        {
            LinkedList ll = new LinkedList();
            ll.Items.Add(dataSet[i],description[i]);
       }
    }

    //** getting the private of the owner
    public string ownerPrivateKey()
    {
        string privateKey = string.Empty();

        //** get the private key and work with it arround

        return privateKey;
    }

    //** get the index
    public string Index()
    {
         string index = string.Empy();

         //** implement the query for getting
         //** or generating the index

         return index;
    }
}

public class AddUser
{
    //** Step 3
    //** the algorithm from AddUser step (algorithm)
    //** are runned and invoked by the data owner
    //** constructor of the class AddUser
    public string AddUser() {}

    //** property for getting the identity of the user
    //** see below the Class Student
    public string IdentityOfTheUser()

        string identity = string.Empty();

        //** implement the way of getting
        //** the identity of the user

       return identity;
    }

    //** property for getting the owners key
    public string OwnerSecretKey()
    {
         string secretKey = string.Empty();

         //** implement the way of querying
         //** for secret key

         return secretKey;
    }

public void AssignSecretKeyToUser()
{
    Student stud = new
    Student(OwnerSecretKey.ToString());
}
}

public class Query
{
    //** Step 4
    //** the algorithm from Query step (algorithm)
    //** are runned and invoked by the user

    //** constructor of the class Query
    public void Query() {}

    //** function for getting the keywords
    public string Keyword()
    {
        string kw = string.Empty();

        //** query for the keywords;

        Return kw;
    }

    //** function for getting the secret key of the users
    public string UserSecretKey()
    {
        string secretKey = string.Empty();

        //** implement the way of querying
        //** for secret key

        return secretKey;
    }

    //** the generation of the output as query
    //** token for the trapdoor
    public string QueryToken()
    {
         string query_token = string.Empty();

        //** generate and build
        //** the query token for trapdoor

        return query_token
    }
}

public class Search
{
     //** Step 5
     //** the algorithm from Search step (algorithm)
     //** are runned and invoked by the server

     //** the constructor of the Search class
     public void Search() {}

     public string SearchQuery()
     {
         string query = string.Empty();

         //** take the search query

         return query;
    }

    public string Index()
    {
        string index = string.Empty();

        //** take the search query

        return index;
    }

    public string ReturnResult()
    {
        string result = string.Empty();
        string setOfIdentifiers = string.Empty();

        //** based on the search query and index,
        //** get the set identifiers of the data items
        setOfIdentifier = "query for identifiers";

        //** build the result. "#" is the separator for
        //** illustration purpose only
        result = SearchQuery + "#" + Index;

        return result;
    }
}

public class RevokeUser
{
    //** Step 6
    //** the algorithm from Search step (algorithm)
    //** are runned and invoked by the data owner

    //** constructor of RevokeUser class
    public void RevokeUser(){}

    //** second constructor of the class
    //** this can be implemented as a
    //** solution for revoking a user
    public void RevokeUser(string userID,
                           string secretKeyDataOwner,
                           string secretKeyServer)
    {
         //** implement the revoking process

        //** output the new key for data owner

       //** output the new key for server
    }
}

public class Course
{
     //** the db_panel represents an instance of the generated
     //** DBML file which contains classes for each of tables
     //** from the database
     DatabaseDBPanel db_panel = new DatabaseDBPanel();

     //** Class Courses it is a generated class and assigned
     //** to the table Courses from the database
     Courses c = new Courses();

     //** student ID
     string demoStudentID = "435663";

     //** select the course ID based on the student
     public string GetCourse()
     {
          //** select the courses for a
          //** specific user (student)
          var c = (from x in db_panel.Courses
          where x.StudentID ==
                   demoStudentID.ToString()).Single();

          return c.ToString();
     }

}

public class Student
{
    public string secretKey {get; set;}
    public int StudentId {get; set;}
    public string CourseID {get; set;}

    [Secure]
    public string StudentName {get; set;}

    [Secure]
    public string StudentIdentity {get; set;}

    [Secure(Opt.Search)]
    public string StudentPersonalCode {get; set;}

     //** constructor of the class
     public void Student(string secret_key)
     {
          secretKey = secret_key;
     }

}

var queryKeywod =
    SecureSearch.GetPrefix("123456789");

var resultStudent = myDatabase.Students.Where(p =>
    p.StudentPersonalCode.StartsWith(searchPrefix))

Listing 12-2Implementation of Searchable Encryption Scheme

摘要

在本章中,描述了可搜索加密方案,并提供了一个支持可搜索加密的库的简单示例。

可搜索加密是同态加密的一个特例,在许多活动领域都有很大的潜力。如果您对可搜索加密的更多理论方面感兴趣,任何参考资料都提供了对 SE 的更深入的了解。有关伪代码的一些最新示例,请参考 [11 或 [12 。

文献学

  1. E.J. Goh,“安全索引”IACR 密码学 ePrint 档案,2003,216。

  2. D.Boneh,G. Di Crescenzo,R. Ostrovsky 和 G. Persiano,“使用关键字搜索的公钥加密”在国际密码技术理论与应用会议(第 506-522 页)。2004.斯普林格,柏林,海德堡。

  3. J.李,王,王,曹,任,楼伟,“云计算中加密数据的模糊关键词搜索”。在 2010 年 IEEE INFOCOM 会议录(第 1-5 页)。2010.IEEE。

  4. J.《容错可搜索加密》在 2009 IEEE 国际通信会议(第 1-6 页)。IEEE。2009.

  5. J.赖,周,邓瑞宏,李彦宏,陈国光,“加密数据的表现性搜索”。第八届 ACM SIGSAC 信息、计算机和通信安全研讨会会议录(第 243-252 页)。2013.

  6. R.Bost,“∑ oφoς:转发安全可搜索加密。”在2016 年 ACM SIGSAC 计算机与通信安全会议论文集(第 1143-1154 页)。2016.

  7. D.X. Song、D. Wagner 和 A. Perrig,“加密数据搜索的实用技术”在 2000 年 IEEE 安全和隐私研讨会上。S & P 2000(第 44-55 页)。IEEE。2000.

  8. J.Ghareh Chamani、D. Papadopoulos、C. Papamanthou 和 R. Jalili,“前向和后向私有对称可搜索加密的新构造”在2018 ACM SIGSAC 计算机与通信安全会议论文集(第 1038-1055 页)。2018.

  9. C.左,孙,刘,邵,皮普日克,“动态可搜索对称加密的前向和强后向保密性。”在欧洲计算机安全研究研讨会(第 283-303 页)。施普林格,查姆。2019.

  10. 加密文档, www.crypteron.com/docs/

  11. C.马,谷耀辉,李。"实用的可搜索对称加密支持合取查询,没有关键字对结果模式泄漏."

  12. 南支持加密云数据逻辑查询的隐私保护模糊搜索方案。在移动网络和应用中,1-12。2010.

  13. 丹·博纳等人,《使用关键字搜索的公钥加密》密码技术理论与应用国际会议。施普林格,柏林,海德堡,2004。

  14. 欧内斯特·博纳和朱·石光,“区块链边缘计算环境中的隐私增强方案(PES)”(2019 年 10 月)IEEE 接入 2020。

  15. Mohammad Shahriar Rahman 等人,“在宽松的信任假设下使用区块链进行可问责的跨境数据共享”2020 年 IEEE 工程管理汇刊

  16. C.Bö sch,P. Hartel,W. Jonker 和 A. Peter,“可证明安全的可搜索加密的调查”,载于 ACM Comput。调查,第 47 卷,第 2 期,第 1–51 页,2014 年。

  17. Prabhat Kumar Panda 和 Sudipta Chattopadhyay。“物联网环境的安全相互认证协议”,载于可靠智能环境杂志(第 1-16 页),2020 年。

  18. Mihailescu Marius Iulian、Nita Stefania Loredana 和 Pau Valentin Corneliu。"使用椭圆曲线加密和可搜索加密的电子学习系统框架."《电子学习和教育软件国际科学会议论文集》第 1 卷(第 545-552 页),2020 年。DOI: 10.12753/2066-026X-20-071。

*****

十三、同态加密

同态加密(HE)代表一种支持对加密数据进行计算的加密技术,在计算时不需要解密。对明文数据应用的计算结果必须与通过对加密数据应用反映对明文数据应用的相应计算而获得的结果的解密结果相同。这些类型的加密方案有很大的潜力,因为第三方可以在加密数据上计算函数(并因此应用算法),而无需访问明文数据。因此,数据得到处理,同时其隐私和安全得到保证。例如,您正在旅行,想要搜索附近的餐馆。这个简单的搜索可能会透露您的信息,例如您在哪个城镇以及您搜索的时间。如果搜索引擎使用同态加密方案,那么其他人就看不到你的任何信息,你收到的搜索结果也是如此。可能使用同态加密的重要领域包括金融/商业部门、医疗保健和其他使用敏感数据的领域。

如果一个函数 f : AB 满足以下性质,则称它在运算“”上是同态的:*

$$ f\left({x}_1\right)\ast f\left({x}_2\right)=f\left({x}_1\ast {x}_2\right),\forall {x}_1,{x}_2\in A $$

除了密钥生成、加密和解密,同态加密方案还有一个算法Eval (,称为评估,它将加密数据作为输入,并输出加密数据。在Eval算法中,函数f应用于加密数据c1 和c2,而不访问普通数据m1 和m2,具有以下属性:

$$ Dec\left( ke{y}_{priv}, Eval\left( ke{y}_{eval},{c}_1,{c}_2\right)\right)=f\left({m}_1,{m}_2\right) $$

在同态加密中,同态被认为仅用于加法和乘法这两种数学运算,因为对应于函数的任何电路都可以使用对应于这两种运算的门来表示。同态加密在Unpadded RSA算法中遇到的第一个观察,同态运算是乘法,

$$ Enc\left({m}_1\right)\bullet Enc\left({m}_2\right)={m}_1e{m}_2e \operatorname {mod}\ n $$

$$ ={\left({m}_1{m}_2\right)}^e \operatorname {mod}\ n $$

$$ = Enc\left({m}_1\bullet {m}_2\right), $$

其中 m 1m 2 是两个普通消息, Enc 代表加密功能。

有三种同态加密方案:

  • 部分同态加密(PHE) 。接受单个操作的次数不限。代表其他加密方案基础的重要 PHE 方案是 RSA [1 ]、Goldwasser-Micali [2 ]和 El-Gamal [3 ]。

  • 有点同态加密(SWHE) 。接受有限次数的多重操作。一个 SWHE 方案的例子是 [4 ]。

  • 全同态加密(FHE) 。接受多次操作,次数不限。FHE 被称为“密码学的圣杯”或“密码学的瑞士军刀” 5 因为它可以对加密数据应用任意次数的函数。第一个 FHE 方案是在 2009 年提出的,它基于理想晶格 [6 ]。虽然这个计划有很大的潜力,并为 FHE 开辟了道路,但它也有一些缺点,比如它的复杂性和抽象性,这使得它在最初的形式下不切实际。此外,许多研究使用 [6 中提出的 FHE 通用框架来创建或改进完全同态方案。

全同态加密

深入了解 FHE 是值得的,因为它是一项伟大的技术,有可能解决(几乎)所有的安全、隐私和保密问题。

第一个 FHE 方案是在 2009 年提出的,基于理想格 [6 ]和理想陪集问题硬度假设,随后又有许多其他方案提出,为第一代 FHE 方案开辟了道路。紧随其后的工作是 [7 ],其中作者提出了一个更简单的基于整数运算的 FHE 方案。然而,这两种方案的缺点是快速的噪声增长,这对实际实现和安全性有很大的影响,并且限制了同态能力。此外,噪音的增长在某种程度上也使得解密变得不可能。

第二代开始采用更有效地控制噪声的方案 [8 , [9 ],这在某些条件下带来了更好的性能和更强的安全性(称为硬度假设)。这一代产生了支持给定多项式深度的电路评估的分级方案,以及能够产生 FHE 方案的自举方案。如果加密方案可以评估它自己的解密电路以及一个与非门,则该加密方案称为可自举的。

第三代从 [10 开始,引入了全新的噪点处理方式。第二代方案比第三代方案提供了更好的效率,但第三代方案的硬度假设可能较弱。从第三代开始的很多方案都是基于非对称乘法,也就是说,对于两个密文 c 1c 2c1c2的乘积不同于c2∙c

FHE 在密码学方面有更多的应用:

  • 外包。存储和计算可以外包,而不会泄露私人信息。例如,一家小公司希望将其数据存储在云中,但它应该对数据进行加密,以防止云提供商访问这些数据。FHE 在这方面会有所帮助,因为云提供商可能会以加密格式处理公司的数据,而无法访问明文数据。此外,云提供商还会将计算结果以加密格式发送给数据所有者,只有私钥持有者才能解密。

  • 私人信息检索 (PIR) 私人查询。这些在数据库查询或搜索引擎中是很重要的,例如,当客户机想要向存储在服务器上的大型数据库提交查询时,但是客户机不希望服务器了解关于查询的任何信息。因此,客户端对查询进行加密,并发送给服务器;然后,服务器对加密数据应用加密查询,并用加密结果进行响应。

  • 两个实体之间的一般计算(双方计算)。让我们假设有两方,每一方都拥有一个私有输入,A方的xB方的y,以及一个双方都知道的约定函数F。当第A方想要计算拥有的输入的函数时,它将带来x并计算r = F(x, y),并且它将学习结果r,但是将不会学习关于y的任何东西。另一方面,B方将不会了解到关于xr的任何信息。在一个半诚实的模型中,这将等同于B计算F y (x),其中A加密x并发送给B,因为语义安全保证了这样一个事实,即B不会了解到与x相对应的普通值。如果在这里使用同态加密来评估函数,A将只学习F(x,y)的结果,而不学习其他任何东西。

  • 零知识。零知识证明协议可以使用同态加密来在 NP(非确定性多项式时间)中对每种语言 L 应用它。

微软海豹突击队公司实施的 FHE 方案

BFV [11 和 CKKS [12 加密方案在微软的 FHE 库中实现,分别叫做SEAL [13 , 14 。

在 [11 ]中,加密函数定义在次数最大的多项式集合n上,系数以 t 为模计算。该集合表示为rt=t[x]/(xn+另一方面,密文位于 R q 集合中,其中多项式的系数取模q。该方案中的同态运算是加法和乘法,保留了 R t 的环结构。要加密一个普通值,应该首先使其成为结构 R t 接受的多项式形式。在 11 中提出的方案的算法是SecretKeyGen(基于安全参数生成密钥)、PublicKeyGen(基于密钥生成公钥)、EvaluationKeyGen(基于密钥生成评估密钥)、Encrypt(使用公钥加密普通值)、Decrypt(使用密钥解密加密值)、Add(执行两个加密值的加法)和Multiply(执行两个加密值之间的乘法)。注意加法和乘法的结果必须落在同一个结构 R q 。更多细节和理论方面的内容请咨询 [11 ]。

虽然 [11 提供了一种对整数应用模运算的方法,但在 [12 中,作者也提供了对实数和复数应用模运算的方法。无论如何,在 [12 中,结果是近似的,但这些技术是以加密格式对实数求和、对加密数据应用机器学习算法或计算加密位置之间距离的最佳技术之一。

海豹突击队图书馆

SEAL的名字来源于简单加密算法库 [15 ],写在C++中。SEAL的最新稳定版本是 3.5(2020 年 5 月发布),它支持 .NET 标准包装器,使得使用公共 API 更容易,也更容易被非密码学专家使用。该库有一个 GitHub 页面 [16 ],可以从那里下载。

SEAL库是独立的,它不像其他 FHE 方案实现那样需要外部库。这使事情变得更容易,因为它包含自动选择加密方案参数的模块和噪声估计模块。在 [17 中,证明了海豹突击队 v2.0 在双重格攻击下是安全的。

安装海豹突击队库最简单的方法是使用 Visual Studio 中的 NuGet 包管理器控制台(本章我们将使用 Windows 10 Education x64 上的 Visual Studio 2019 社区)。

在 Visual Studio 中创建新项目(我们创建了一个名为SEALDemo的项目,其类型为ConsoleApp(.NET Core))然后打开选择Tools > NuGet Package Manager > Package Manager Console,并键入

PM> Install-Package Microsoft.Research.SEALNet -Version 3.5.6

如果安装成功,控制台上会显示类似图 13-1 的信息。

img/493660_1_En_13_Fig1_HTML.jpg

图 13-1

使用软件包管理器控制台安装海豹突击队

在继续之前,我们重复海豹突击队的 GitHub 页面上的警告:

“如果不阅读所有示例或简单地重复使用示例中的代码,就不可能正确使用微软海豹突击队。任何试图这样做的开发人员都不可避免地会产生易受攻击、故障或极其缓慢的代码。”

——https://github.com/microsoft/SEAL/blob/master/README.md#getting-started

在新创建的项目中,添加一个名为Example的新类,并在这个类中添加static void方法EasyExample()。代码应该如清单 13-1 所示。

using System;
using System.Collections.Generic;
using System.Text;

namespace SEALDemo
{
    class Example
    {
        public static void EasyExample()
        {
        }
    }
}

Listing 13-1The Initial Structure

请注意,当您创建一个新类时,库SystemSystem.Collections.GenericSystem.Text会自动添加。对于 Visual Studio 的其他版本,这些库可能会有所不同,或者根本不会出现。删除上面的三条指令,用Microsoft.Research.SEAL;代替。这样,你就可以在你的类中使用海豹突击队库中的方法。

此外,让我们使用 BFV 加密方案将代码添加到EasyExample()方法中,并添加来自SEAL库的功能。首先,您需要指定加密参数。它们是多项式模数的次数(n)、密文的系数模数(q)和明文的模数(t)。为了告诉应用您想要定义 BFV 方案的参数,您在EasyExample()中添加清单 13-2 中的以下代码行。

EncryptionParameters parameters = new EncryptionParameters(SchemeType.BFV);

Listing 13-2Defining the BFV Parameters

接下来,您需要为每个参数提供值。多项式模次数的值代表2的一次幂,实际上就是一个分圆多项式1的次数。这个度数的推荐范围是{1024, 2048, 4096, 8192, 16384, 32768}。该值越高,对加密数据的计算就越困难,但速度会变得更慢。中间值是 4096,这允许合理的计算次数,所以为您的应用选择这个值。下一个参数是密文系数模数,代表一个大整数。它应该表示为素数的乘积,其位数应该是其素数因子的位数之和。该值越高,支持的加密计算越多。总之,在多项式模次数和系数模的最大比特长度之间存在一种关系。例如,多项式模次数的值 4096 对应于系数模的比特大小的值 109。最后一个参数是明文的模数,通常是一个正整数。在本例中,您将使用 2 的幂对其进行初始化。在其他情况下,模数应该是一个质数。该值的目的是给出普通数据的位大小,并设置乘法运算的消耗限制。有关加密参数的更多信息可在 [13 和 [14 中找到。

我们需要解释的另一个概念是噪声预算*,用位数表示。简而言之,初始噪声预算*由加密参数给出,并且在同态运算(加法和乘法)期间消耗噪声的速率也由加密参数给出。系数模的值对初始噪声预算有重要影响;系数模数值越高,初始噪声预算值越高。如果加密值的噪声预算达到值 0,则密文不能再被解密,因为噪声量太高。**

**回到示例,在EasyExample()方法中添加清单 13-3 中的以下初始化:

parameters.PolyModulusDegree = 4096;
parameters.CoeffModulus = CoeffModulus.BFVDefault(4096);
parameters.PlainModulus = new Modulus(1024);

Listing 13-3Initialization

使用海豹突击队上下文检查加密参数的正确性:

SEALContext SEALctx = new SEALContext(parameters);

接下来的步骤更容易。您需要实例化提供 BFV 加密方案算法的类,如清单 13-4 所示。

KeyGenerator keyGenerator = new KeyGenerator(SEALctx);
PublicKey pK = keyGenerator.PublicKey;
SecretKey sK = keyGenerator.SecretKey;
Encryptor encrypt = new Encryptor(SEALctx, pK);
Evaluator evaluate = new Evaluator(SEALctx);
Decryptor decrypt = new Decryptor(SEALctx, sK);

Listing 13-4Instantiate the Classes for BFV

举个简单的例子,我们对多项式p(x)= 3x4+6x3+9x2+12x+6 求值 x = 3。

为了检查加密和解密是否有效,让我们加密值 x = 3,然后解密结果。同时,您检查一些度量标准(清单 13-5 )。

int value = 3;
Plaintext plainValue = new Plaintext(value.ToString());
Console.WriteLine($"The value = {value} is expressed as a plaintext polynomial 0x{plainValue}.");
Console.WriteLine();
Ciphertext encryptedValue = new Ciphertext();
encrypt.Encrypt(plainValue, encryptedValue);
Console.WriteLine($"- the size of the freshly encrypted value is: {encryptedValue.Size}");
Console.WriteLine("- the initial noise budget of the encrypted value: {0} bits",
      decrypt.InvariantNoiseBudget(encryptedValue));
Plaintext decryptedValue = new Plaintext();
Console.Write("- the decryption of encrypted value: ");
decrypt.Decrypt(encryptedValue, decryptedValue);
Console.WriteLine($"0x{decryptedValue}");

Listing 13-5Encryption of x=3 and Metrics Checking

现在您应该运行应用了。为此,您需要转到您的解决方案中的Program类(清单 13-6 ,并调用EasyExample()方法:

public class Program
{
   static void Main(string[] args)
   {
      Example.EasyExample();
      GC.Collect();
   }
}

Listing 13-6The Main Method

清单 13-6 中需要GC.Collect,因为它准确地显示了内存池的使用情况。运行该应用,您将获得如清单 13-7 和图 13-2 所示的输出。

img/493660_1_En_13_Fig2_HTML.jpg

图 13-2

运行应用的结果

The value = 3 is expressed as a plaintext polynomial 0x3.

- the size of the freshly encrypted value is: 2
- the initial noise budget of the encrypted value: 55 bits
- the decryption of encrypted value: 0x3

Listing 13-7The Result of Running the Application

让我们回到加密/解密验证。通过Plaintext构造函数,普通值被转换成比模数多项式阶数更低的多项式,其系数被表示为十六进制值。使用海豹突击队库得到的密文被表示为两个或更多的多项式,这些多项式的系数被计算为整数模来自CoeffModulus的素数相乘的结果。您用对象encryptedValue实例化了Ciphertext类,其中您放置了plainValue的加密。您可以使用encrypt对象的Encrypt方法来实现。很容易观察到,Encrypt方法在这里有两个参数:表示明文值的源的参数(plainValue)和表示源明文值的加密目的地的第二个参数(encryptedValue)。密文的大小由多项式的个数给出,新加密的密文的大小总是 2。该值由encryptedValue对象的Size方法返回。此外,decrypt对象的InvariantNoiseBudget方法为我们计算了普通值3加密的初始噪声预算。InvariantNoiseBudget被实现到Decryptor类中,因为它显示了解密是否会在你的计算中的某一点起作用。要解密结果,使用由decrypt对象调用的Decrypt方法。最后,解密确实有效,因为十六进制表示中的值0x3表示3

为了优化工作,建议采用乘法运算较少的多项式形式,因为乘法运算成本较高,可以更快地降低噪声预算。因此, p ( x )可能被因式分解为p(x)= 3(x2+2)(x+1)2,也就是说你会先求值( x 2 + 2),再求值( x + 1)

要计算( x 2 + 2),如清单 13-8 所示进行。

Console.WriteLine("Compute squareValuePlusTwo (x²+2).");
Ciphertext squareValuePlusTwo = new Ciphertext();
evaluate.Square(encryptedValue, squareValuePlusTwo);
Plaintext plainTextTwo = new Plaintext("2");
evaluate.AddPlainInplace(squareValuePlusTwo, plainTextTwo);

Console.WriteLine($"- the size of squareValuePlusTwo:
      {squareValuePlusTwo.Size}");
Console.WriteLine("- the noise budget in plainTextTwo: {0} bits",
      decrypt.InvariantNoiseBudget(squareValuePlusTwo));

Plaintext decryptedResult = new Plaintext();
Console.Write("- the decryption of squareValuePlusTwo: ");
decrypt.Decrypt(squareValuePlusTwo, decryptedResult);
Console.WriteLine($"0x{decryptedResult}");

Listing 13-8Computing (x2 + 2) 

清单 13-9 和图 13-3 显示了运行应用后的输出。

img/493660_1_En_13_Fig3_HTML.jpg

图 13-3

计算的结果(x2+2)

Compute squareValuePlusTwo (x²+2).
- the size of squareValuePlusTwo: 3
- the noise budget in squareValuePlusTwo: 33 bits
- the decryption of squareValuePlusTwo: 0xB

Listing 13-9The Result of Computing (x2 + 2)

确实,如果计算 3 2 + 2,得到11,其十六进制表示为0xB;噪声预算大于 0,所以解密仍然有效。请注意,evaluate对象允许您直接对加密数据进行操作。本例中的收集器变量是squareValuePlusTwo。首先,你用Square的方法将2幂的加密值,即x2 放入这个变量。接下来,您添加一个明文值2,使用方法AddPlainInplace,导致 x 2 + 1。注意,在这个例子中, x = 3。此外,SquareAddPlainInplace方法有两个参数,一个源和一个目的。

类似地,您计算( x + 1) 2 ,使用作为收集器变量valuePlusOneSquare,如清单 13-10 所示。

Console.WriteLine("Compute valuePlusOneSquare ((x+1)²).");
Plaintext plainTextOne = new Plaintext("1");
Ciphertext valuePlusOneSquare = new Ciphertext();
evaluate.AddPlain(encryptedValue, plainTextOne,
       valuePlusOneSquare);
evaluate.SquareInplace(valuePlusOneSquare);
Console.WriteLine($"- the size of valuePlusOneSquare:
       {valuePlusOneSquare.Size}");
Console.WriteLine("- the noise budget in valuePlusOneSquare:
       {0} bits", decrypt.InvariantNoiseBudget(valuePlusOneSquare));
Console.Write("- decryption of valuePlusOneSquare: ");
decrypt.Decrypt(valuePlusOneSquare, decryptedResult);
Console.WriteLine($"0x{decryptedResult}");

Listing 13-10Computing (x + 1)2 

清单 13-11 和图 13-4 显示了运行应用的结果。

img/493660_1_En_13_Fig4_HTML.jpg

图 13-4

计算的结果( x + 1) 2

Compute valuePlusOneSquare ((x+1)²).
- the size of valuePlusOneSquare: 3
- the noise budget in valuePlusOneSquare: 33 bits
- decryption of valuePlusOneSquare: 0x10

Listing 13-11The Result of Computing (x + 1)2

确实,如果计算(3 + 1) 2 ,得到10,其十六进制表示为0x10;噪声预算大于 0,所以解密仍然有效。

3(x2+2)(x+1)2的最终结果被收集到encryptedOutcome变量中,如清单 13-12 所示。

Console.WriteLine("Compute encryptedOutcome 3(x² + 2)(x + 1)² .");
Ciphertext encryptedOutcome = new Ciphertext();
Plaintext plainTextThree = new Plaintext("3");
evaluate.MultiplyPlainInplace(squareValuePlusTwo, plainTextThree);
evaluate.Multiply(squareValuePlusTwo, valuePlusOneSquare, encryptedOutcome);
Console.WriteLine($"- size of encryptedOutcome:
      {encryptedOutcome.Size}");
Console.WriteLine("- the noise budget in encryptedOutcome:
      {0} bits", decrypt.InvariantNoiseBudget(encryptedOutcome));
decrypt.Decrypt(encryptedOutcome, decryptedResult);
Console.WriteLine("- decryption of 3(x²+2)(x+1)² = 0x{0}",
decryptedResult);

Listing 13-12Computing 3(x2 + 2)(x + 1)2

清单 13-13 和图 13-5 显示了结果。

img/493660_1_En_13_Fig5_HTML.jpg

图 13-5

计算 3 的输出(x2+2)(x+1)2

Compute encryptedOutcome 3(x² + 2)(x + 1)² .
- size of encryptedOutcome: 5
- the noise budget in encryptedOutcome: 2 bits
- decryption of 3(x²+2)(x+1)² = 0x210

Listing 13-13The Output of Computing 3(x2 + 2)(x + 1)2

的确,如果你计算 3(3 2 + 2)(3 + 1) 2 ,我们得到528。别忘了明文模数是1024,所以528 mod 1024 = 528,它有0x210的十六进制表示。噪声预算大于 0,这允许您解密最终的加密结果。

清单 13-14 将所有这些放在一起。

class Example
{
  public static void EasyExample()
  {
    EncryptionParameters parameters = new
    EncryptionParameters(SchemeType.BFV);

    parameters.PolyModulusDegree = 4096;
    parameters.CoeffModulus = CoeffModulus.BFVDefault(4096);
    parameters.PlainModulus = new Modulus(1024);

    SEALContext SEALctx = new SEALContext(parameters);

    KeyGenerator keyGenerator = new KeyGenerator(SEALctx);
    PublicKey pK = keyGenerator.PublicKey;
       SecretKey sK = keyGenerator.SecretKey;
       Encryptor encrypt = new Encryptor(SEALctx, pK);
       Evaluator evaluate = new Evaluator(SEALctx);
       Decryptor decrypt = new Decryptor(SEALctx, sK);

       Console.WriteLine("Evaluation of 3(x² + 2)(x + 1)²");
       Console.WriteLine();
       int value = 3;
       Plaintext plainValue = new Plaintext(value.ToString());
       Console.WriteLine($"The value = {value} is expressed as
             a plaintext polynomial 0x{plainValue}.");
       Console.WriteLine();
       Ciphertext encryptedValue = new Ciphertext();
       encrypt.Encrypt(plainValue, encryptedValue);

       Console.WriteLine($"- the size of the freshly encrypted
             value is: {encryptedValue.Size}");
       Console.WriteLine("- the initial noise budget of the
             encrypted value: {0} bits",
       decrypt.InvariantNoiseBudget(encryptedValue));

       Plaintext decryptedValue = new Plaintext();
       Console.Write("- the decryption of encrypted value: ");
       decrypt.Decrypt(encryptedValue, decryptedValue);
       Console.WriteLine($"0x{decryptedValue}");

       /* Compute (x² + 2).*/
       Console.WriteLine("Compute squareValuePlusTwo (x²+2).");
       Ciphertext squareValuePlusTwo = new Ciphertext();
       evaluate.Square(encryptedValue, squareValuePlusTwo);
       Plaintext plainTextTwo = new Plaintext("2");
       evaluate.AddPlainInplace(squareValuePlusTwo, plainTextTwo);

       Console.WriteLine($"- the size of squareValuePlusTwo:
           {squareValuePlusTwo.Size}");
       Console.WriteLine("- the noise budget in
           squareValuePlusTwo: {0} bits",

       decrypt.InvariantNoiseBudget(squareValuePlusTwo));

    Plaintext decryptedResult = new Plaintext();
    Console.Write("- the decryption of squareValuePlusTwo: ");
    decrypt.Decrypt(squareValuePlusTwo, decryptedResult);
    Console.WriteLine($"0x{decryptedResult}");

    /*Compute (x + 1)².*/
    Console.WriteLine("Compute valuePlusOneSquare ((x+1)²).");
    Plaintext plainTextOne = new Plaintext("1");
    Ciphertext valuePlusOneSquare = new Ciphertext();
    evaluate.AddPlain(encryptedValue, plainTextOne,
         valuePlusOneSquare);
    evaluate.SquareInplace(valuePlusOneSquare);
    Console.WriteLine($"- the size of valuePlusOneSquare:
         {valuePlusOneSquare.Size}");
    Console.WriteLine("- the noise budget in
         valuePlusOneSquare: {0} bits",

    decrypt.InvariantNoiseBudget(valuePlusOneSquare));
    Console.Write("- decryption of valuePlusOneSquare: ");
    decrypt.Decrypt(valuePlusOneSquare, decryptedResult);
    Console.WriteLine($"0x{decryptedResult}");

    /* Multiply (x² + 2) * (x + 1)² * 3\. */

    Console.WriteLine("Compute encryptedOutcome
      3(x² + 2)(x + 1)² .");
    Ciphertext encryptedOutcome = new Ciphertext();
    Plaintext plainTextThree = new Plaintext("3");
    evaluate.MultiplyPlainInplace(squareValuePlusTwo,
        plainTextThree);
    evaluate.Multiply(squareValuePlusTwo, valuePlusOneSquare,
         encryptedOutcome);
    Console.WriteLine($"- size of encryptedOutcome:
             {encryptedOutcome.Size}");
    Console.WriteLine("- the noise budget in encryptedOutcome:
          {0} bits", decrypt.InvariantNoiseBudget(encryptedOutcome));
    decrypt.Decrypt(encryptedOutcome, decryptedResult);
    Console.WriteLine("- decryption of 3(x²+2)(x+1)² =
             0x{0}", decryptedResult);
      }
}

Listing 13-14The Entire Code

整个输出如图 13-6 所示。

img/493660_1_En_13_Fig6_HTML.jpg

图 13-6

全部输出

这个多项式求值代表了如何使用海豹突击队库的一个简单例子。当然,在实际应用中,您将需要处理更复杂的函数或算法。甚至这个简单的例子也可以通过使用重新线性化技术进行显著优化,该技术将加密文本的大小重置为初始值 2。简而言之,应该在每次乘法之后应用重新线性化。这种技术在海豹突击队库中可用,它在RelinearizeInplace下的Evaluator类中实现,它有两个参数:需要重新线性化的加密文本和重新线性化密钥。上述示例可以修改如下:首先,定义重新线性化关键字:

RelinKeys relinearizationKeys = keyGenerator.RelinKeys();

然后,在每次乘法之后,进行重新线性化。例如,重新线性化squareValuePlusTwo变量:

evaluate.RelinearizeInplace(squareValuePlusTwo, relinearizationKeys);

请注意,您需要重新线性化valuePlusOneSquareencryptedOutcome变量,以实现最大优化。

正如您在这个简单的例子中看到的,SEAL库非常强大,一个很大的优点是它独立于其他外部库。当应用处理整数的精确值时,在SEAL库中实现的BFV加密方案非常棒。如果应用需要处理实数或复数,那么CKKS加密方案是最好的选择,它也在SEAL库中实现。

结论

在本章中:

  • 您已经了解了什么是同态加密以及同态加密的类型。

  • 您对全同态加密有了更深入的了解,并且看到了它如此重要的原因。

  • 在一个简单的例子中,您使用了微软的SEAL库,它实现了BFV加密方案,并进行多项式求值。

文献学

  1. 阿迪·萨莫尔·罗纳德·L·李维斯特和伦纳德·阿德曼,“获取数字签名和公钥密码系统的方法”《化学文摘》第 21.2 节的来文(1978 年):第 120-126 页。

  2. 沙菲·戈德瓦瑟和希尔维奥·米卡利,“概率加密和如何玩心理扑克保密所有部分信息。”第十四届美国计算机学会计算理论年会会议录。1982.

  3. 基于离散对数的公钥密码系统和签名方案。信息理论汇刊 31.4 (1985): 469-472。

  4. Dan Boneh、Eu-Jin Goh 和 Kobbi Nissim,“评估密文上的 2-DNF 公式”密码学理论会议。施普林格,柏林,海德堡,2005。

  5. B.巴拉克和 z .布拉克斯基。《密码学的瑞士军刀》, http://windowsontheory.org/2012/05/01/the-swiss-army-knife-of-cryptography/ ,2012 年。

  6. 使用理想格的全同态加密。第 41 届美国计算机学会计算理论年会会议录。2009.

  7. Marten Van Dijk 等人,“整数上的全同态加密”密码技术理论与应用国际年会。施普林格,柏林,海德堡,2010。

  8. Zvika Brakerski 和 Vinod Vaikuntanathan,“来自(标准)LWE 的高效全同态加密”SIAM 计算期刊 43.2 (2014): 831-871。

  9. Craig Gentry、Z. Brakerski 和 V. Vaikuntanathan,“无引导的全同态加密”安全 111.111 (2011): 1-12。

  10. Craig Gentry,Amit Sahai 和 Brent Waters,“从错误中学习的同态加密:概念上更简单,渐近更快,基于属性”年度密码学会议。施普林格,柏林,海德堡,2013。

  11. 范俊峰和弗雷德里克·维考特伦,“有点实用的全同态加密”IACR 密码学 ePrint 档案 2012 (2012): 144。

  12. Jung Hee Cheon 等人,“近似值算术的同态加密”密码学与信息安全理论及应用国际会议。施普林格,查姆,2017。

  13. 陈皓、金·莱恩和瑞秋·普莱耶,“简单加密算法库-海豹突击队 2.1 版”,金融加密和数据安全国际会议。施普林格,查姆,2017。

  14. Kim Laine,《简单加密算法库 2.3》。1."微软研究院, www.microsoft.com/en-us/research/uploads/prod/2017/11/sealmanual-2-3-1.pdf (2017)。

  15. 微软海豹突击队, www.microsoft.com/en-us/research/project/microsoft-seal/

  16. 微软/海豹突击队, https://github.com/Microsoft/SEAL

  17. 对小秘密 LWE 的双重格攻击和在赫里布和海豹突击队的参数选择密码技术理论与应用国际年会。施普林格,查姆,2017。

**

十四、带错误的环学习密码术

在这一章中,我们将讨论带错误的环学习密码术(RLWE ),它是开发专业和复杂的应用和系统的最强大和最具挑战性的方法之一。

2005 年,Oded Regev 引入了带错误的学习(LWE) [4 ]问题,它被证明是密码学未来的一个惊人的通用基础,并提供了复杂的密码构造。如果你想了解更多关于 LWE 和相关问题的信息,我们列出了一些关于基于格的密码学的最佳调查 [5 、 6 、 7 、 8 ]。

LWE 代表一个计算问题,其主要目的是用作新的密码算法的基础,并构建新的、实用的密码构造。一个例子是 NewHope [9 ,一种后量子密钥封装。NewHope 旨在提供针对量子计算机发起的密码分析攻击的保护,同时也是同态加密的基础。

R-LWE 代表了基于有限域上的特殊多项式环的更大的 LWE 问题。基于即使使用量子计算机也难以解决 RLWE 问题的假设,RLWE 密码被视为未来公钥密码的根本基础(见图 14-2 和清单 14-2 的示例)。

RLWE 密码术相对于 LWE 的重要优势在于公钥和私钥的大小。RLWE 生成的密钥代表从 LWE 获得的密钥的平方根。为了便于讨论,让我们考虑下面的例子:我们使用 128 位的安全级别,RLWE 加密算法将使用 7000 位的公钥。

有三类 RLWE 加密算法:

  • 密钥交换(RLWE-柯):2011 年,丁金泰在辛辛那提大学提出了和进行密钥交换的设想。他的基本思想 [10 诞生于矩阵乘法的结合律。这种情况下的错误用于提供安全性。论文发表于 2012 年,专利填写于同年。2014 年,Chris Peikert 根据丁的想法 [11 ]推出了一个关键运输方案。

  • RLWE 签名(RLWE-S):2011 年,柳巴舍夫斯基提出了格非、菲亚特、沙米尔 [12 ]提出的身份认证协议的改进版本,将其转换为数字签名 [13 ]。2012 年,GLP (Gunesyu,Lyubashevsky 和 Popplemann)延长了签名。

  • RLWE 同态加密(RLWE-何):同态加密的主要目标是允许对敏感数据进行计算。2011 年,Brakeersky 和 Vaikuntanathan 提出了一个使用 RLWE 的全同态加密方案,并使用密钥相关消息实现安全性 [15 ]。

在接下来的部分中,我们将通过一个简短的数学背景来展示在进一步实现之前你所知道的最基本的必要理论信息。

数学背景

带着错误学习

随着量子计算机的发展,许多现有的公钥密码系统将很容易被破解。所以抗量子计算机的加密方案有必要的硬度假设。其中一种方法是带着错误学习。通俗地说,LWE 问题是基于发现解决以下等式的值的难度:

$$ b= as+e $$

在上面的等式中, ab 是公钥, s 代表密钥, e 代表误差值(或噪声)。

LWE 问题在密码学的许多领域都有应用。例如,它用于使公钥加密安全地抵御选择明文攻击或选择密文攻击、不经意传输、基于身份的加密和完全同态加密。

在论文 [1 ]中,上面的等式应用于线性方程组,变成b=A×s+e,其中(1) A 是二维矩阵,如果 s 是一维矩阵或(2)

*使用 LWE 的简单加密方案可以定义如下,考虑质数 p :

  • 密钥生成:随机选择一个向量$$ s\in {\mathbb{Z}}_p^n $$,选择一个矩阵 A ,其中 m 行代表来自均匀分布的 m 个独立向量,从 上定义的误差分布中随机选择向量e=(e1,…emb = 计算为 + e 。公钥是对( Ab ),私钥是 s

  • 加密:给定矩阵、 Ab 以及需要加密的消息 m ∈ {0,1},对 Ab 进行随机采样,得到vA=∈AI m 的加密是一对( uv )。

  • 解密:计算值val=vb——SVA(mod p)。如果$$ val\le \frac{p}{2} $$,则消息为m= 0;否则,消息是 m = 1。

上面的例子只是一个非常简单的加密方案,用来演示 LWE 问题是如何工作的。基于 LWE 问题的重要公钥加密方案是 Regev [2 ]和林德纳-佩克特加密方案。

有两种类型的 LWE 问题:LWE 搜索和 LWE 决策。在下文中,我们提供了 LWE 问题的这些变体的正式定义:

定义 LWE 搜索。给定定义在整数集合 上的值 mnp 和分布 χ sχ e ,选择$$ s\leftarrow {\chi}_s^n,{e}_i\longleftarrow {\chi}_e $$$$ {a}_i\leftarrow \mathcal{U}\left({\mathbb{Z}}_p^n\right) $$并计算$$ {a}_i\leftarrow \mathcal{U}\left({\mathbb{Z}}_p^n\right) $$的值 s〉+eImod p,其中 i = 1,…, m带错误学习搜索变量问题的元组( nmpχ * s χ e * )在于确定 *s *

注意,在上面的定义中, s 是具有 n 值的列向量, a i 是具有来自 pn 值的行向量, b 是具有来自 m 元素的列向量

LWE 决定的定义。给定整数集合 上定义的值 np 和分布 χ sχ e ,选择$$ s\leftarrow {\chi}_s^n $$并建立两个预言如下:

  • $$ \mathcal{O}:a\leftarrow \mathcal{U}\left({\mathbb{Z}}_p^n\right),e\leftarrow {\chi}_e $$;输出( a ,〈 as 〉 + e mod p )

  • $$ U:a\leftarrow \mathcal{U}\left({\mathbb{Z}}_p^n\right),u\leftarrow \mathcal{U}\left({\mathbb{Z}}_p\right) $$;输出( au )

带错误学习决策代表元组( npχ sχ e )的一个变体问题,其目的是区分$$ \mathcal{O} $$U

带错误的环形学习

LWE 问题可以应用于系数来自有限域的多项式环。在这种情况下,LWE 问题被称为带错误的环学习 (RLWE)。在密码学中,RLWE 用于密钥交换、签名或同态加密等主题。原理类似于简单 LWE 的原理,在第一等式中 abse 都是多项式。LWE 问题的两种变体变成了

定义(RLWE 搜索)。给定值 npwithn= 2k,设 R$$ R=\frac{\mathbb{Z}\left[X\right]}{\left\langle {X}^n+1\right\rangle } $$$$ {R}_p=\frac{R}{pR} $$以及分布χsχ * e * eχe$ a\leftarrow \mathcal{U}\left({R}_p\right) $并计算 b的值为 + e 。 带错误的环学习搜索元组的变体问题( npχ * s χ e * )在于确定 s 知道(

注意,在上面的定义中,$$ {R}_p=\frac{{\mathbb{Z}}_p\left[X\right]}{\left\langle {X}^n+1\right\rangle } $$

定义(RLWE 决定)。给定值 np + 以及环上定义的分布 χ sχerp,选择 s

  • $$ \mathcal{O}:a\leftarrow \mathcal{U}\left({R}_p\right),e\leftarrow {\chi}_e $$;输出( a + e )

  • $$ U:a\leftarrow \mathcal{U}\left({R}_p\right),u\leftarrow \mathcal{U}\left({R}_p\right) $$;输出( au )

元组( npχ sχ e )的环学习带错误决策变式问题是为了区分$$ \mathcal{O} $$U

为了使基于(R)LWE 的加密方案安全,需要任何多项式时间算法$$ \mathcal{A} $$(称为攻击者)解决(R)LWE 问题的优势是可忽略的函数。

实际实施

LWE 代表了一种量子加密的健壮方法。当您决定转向 LWE 的实用方面,实现一个处理 LWEs 的简单方法时,您需要记住,您需要创建一个密钥值( sk )和另一个值( random _ value )。下一步非常简单,因为您将需要一组值(setofvalues[])并在稍后计算p[]=t[] × sk + e.p []和setofvalues[])将成为公钥。

我们将展示的第一个例子(见图 14-1 )代表了 Oded Regev 在 [4 ]中定义的一种加密方法,称为带错误学习。算法的工作流程很简单:消息(见图 14-1 第一行)的每一位都是按照 Oded Regev from4 的方案加密的。代码如清单 14-1 所示。

img/493660_1_En_14_Fig1_HTML.jpg

图 14-1

Oded Regev 使用的加密方法

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

namespace LWECase1
{
    class Program
    {
        public static int[] public_key = new int[200];
        public static int[] values = new int[]
                                 { 5, 8, 12, 16, 2, 6, 11, 3, 7, 10 };
        public static int s = 5;
        public static int e = 12;
        public static int message = 1;
        public static int val = 0;
        public static int sum = 0;
        public static int remainder = 0;

        static void Main(string[] args)
        {
            Random randValue = new Random();
            int[] res = new int[200];
            int k = 0;

            //**
            for (int x=0; x<values.Length; x++)
            {
                public_key[k] = values[x] * s + e;
                k++;
            }

            for(int i=0; i< public_key.Length; i++)
            {
                res[i] = randValue.Next(public_key[i],
                                    public_key.Length / 2);
            }

            for(int j=0; j<res.Length; j++)
            {
                sum += res[j];
            }

            Console.WriteLine("The message to be send: {0}", message);
            Console.WriteLine("The random values:");
            PrintValues(values);
            Console.WriteLine("The public key is: ");
            PrintValues(public_key);
            Console.WriteLine("The selected values are:");
            PrintValues(res);

            //** compute the sum
            if (message == 1)
                sum += 1;

            Console.WriteLine("The sum is: {0}", sum);

            Console.WriteLine("The encrypted message is: {0}", sum);
           //** compute the remainder
            remainder = sum % s;

            if(remainder % 2 == 0)
                Console.WriteLine("The message received is 0");
            else
                Console.WriteLine("The message received is 1");

            Console.ReadKey();
        }

        //** dealing with arrays
        public static void PrintValues(Object[] myArr)
        {
            foreach (Object i in myArr)
            {
                Console.Write("\t{0}", i);
            }
            Console.WriteLine();
        }

        //** dealing with arrays
        public static void PrintValues(int[] myArr)
        {
            foreach (int i in myArr)
            {
                Console.Write("\t{0}", i);
            }
            Console.WriteLine();
        }
    }
}

Listing 14-1Implementation of LWE Encryption Method by Oded Regev

在清单 14-2 中,我们将提供一个基于 LWE 的公钥加密的例子。我们将从创建一个秘密值( s )开始,它将代表私钥。下一步是创建由随机数的值形成的公钥( A )。我们将根据 As 和随机误差 e 生成另一组数字( B )。这个例子是针对单个位完成的,与清单 14-1 中的前一个例子一样。

这个例子的简单工作流程是

  • Between 0 and 100, we will select a random set of 20 values which represent the public key (A). Let’s assume that those 20 values are

    $$ A=\left[80,86,19,62,2,83,25,47,20,58,45,15,30,68,4,13,8,6,42,92\right] $$

  • Next, we will define a list (B) and each element will be as Bi = Ai × s + ei(mod q), where s represents the secret key and e represents a list of small random values, called the errors values. As an example of the statement made, let’s consider a prime number (q) equal with 97, and the error vector (eas

    $$ e=\left[3,3,4,1,3,3,4,4,1,4,3,3,2,2,3,2,4,4,1,3\right] $$

  • A and B lists are considered the public key and s represents the secret key. At this point, we are able to distribute A and B to everybody who wishes to proceed with an encryption of a message (with the condition to keep s secret). In order to proceed with the encryption, we need to use the samples from A and B. Moving forward, based on those sample we take a bit from the message (M) and compute the following two values:

    $$ u=\sum \left({A}_{samples}\right)\left(\mathit{\operatorname{mod}}\ q\right) $$

$$ v=\sum \left({B}_{samples}\right)+\frac{q}{2}\times M\left(\mathit{\operatorname{mod}}\ q\right) $$

  • At this point, we say that the encrypted message is (u, v). To proceed with the decryption, we need to compute

    $$ decryption=v-s\times u\ \left(\mathit{\operatorname{mod}}\ q\right) $$

  • 如果$$ decryption<\frac{q}{2} $$消息等于 0,否则为 1。

上面描述的程序是从 Oded Regev 的论文 [5 ]中总结出来的,以便于理解和清楚地理解如何在现实中转换 LWE 的复杂性。接下来,让我们看看清单 14-2 ,看看这个实现会是什么样子。输出如图 14-2 所示。

img/493660_1_En_14_Fig2_HTML.jpg

图 14-2

使用 LWE 输出公钥加密的例子

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

namespace PublicKeyEncLWE_Case2
{
    class Program
    {
        public static int[] A = new int[30];
        public static int[] B = new int[30];
        public static int[] e = new int[30];
        public static int s = 20;
        public static int message = 1;
        public static int q = 15;
        public static int nvals = 20;

        static void Main(string[] args)
        {
            Random randomSample = new Random();
            IEnumerable<int> q_values = Enumerable.
                                      Range(0, q);
            IEnumerable<int> n_values = Enumerable.Range(
                                     nvals - 1, nvals / 4);
            double u = 0;
            double v = 0;
            int sample = 0;

            foreach (int q_value in q_values)
            {
                for (int i = 0; i < q; i++)
                {
                    A[i] = randomSample.Next(q_value, nvals);
                }
            }
            for (int x = 0; x < A.Length; x++)
            {
                e[x] = randomSample.Next(1, 4);
                B[x] = (A[x] * s + e[x]) % 2;
            }

            Console.WriteLine("PARAMETERS SECTION\n");
            Console.WriteLine("\tMessage to be send: {0}", message);
            Console.WriteLine("\tThe public key (A):");
            PrintValues(A);
            Console.WriteLine("\tThe public key (B):");
            PrintValues(B);
            Console.WriteLine("\tThe errors (e) are: ");
            PrintValues(e);
            Console.WriteLine("\tThe secret key is: {0}", s);
            Console.WriteLine("\tPrime number is: {0}", q);

            foreach(int n_value in n_values)
            {
                sample = randomSample.Next(nvals - 1, n_value);
                Console.WriteLine("The sample is {0}", sample);
            }

            IEnumerable<int> samples = Enumerable.Range(0, sample);
            string errors = string.Empty;

            for (int x = 0; x < samples.Count(); x++)
            {
                errors = "[" + A[x] + ", " + B[x] + ", ], end
                                                = " + u + A[x];
            }

            Console.WriteLine(errors);

            double flooring = q / 2;

            v += Math.Floor(flooring) * message;

            u = v % q;
            v = u % q;

            Console.WriteLine("u = {0}", u);
            Console.WriteLine("v = {0}", v);

            double res = (v - s * u) % q;
            Console.WriteLine("The result is: {0}", res);
            if (res > q / 2)
                Console.WriteLine("Message is 1");
            else
                Console.WriteLine("Message is 0");

            Console.ReadKey();
        }

        public static void PrintValues(Object[] myArr)
        {
            foreach (Object i in myArr)
            {
                Console.Write("\t{0}", i);
            }
            Console.WriteLine();
        }

        public static void PrintValues(int[] myArr)
        {
            foreach (int i in myArr)
            {
                Console.Write("\t{0}", i);
            }
            Console.WriteLine();
        }
    }
}

Listing 14-2Implementation of Public Key Encryption Example Using LWE

结论

在本章中,您使用 C# 编程语言尝试了第一种实用的带错误的环学习加密方法。它可以为专业人士提供多种挑战的空间,也是对这种加密原语做出重大贡献的起点。

在本章结束时,您现在有以下内容:

  • R-LWE 所基于的主要概念和定义的坚实但简短的数学背景,如果没有它,实际实现将有许多空白需要填补

  • 体验 RLWE 的数学概念及其在实践中的转换带来的挑战

文献学

  1. Oded Regev,《学习与错误问题》, https://cims.nyu.edu/~regev/papers/lwesurvey.pdf

  2. O.格子上,错误学习,随机线性码和密码术。在《美国计算机学会杂志(JACM)】,第 56 卷第 6 期,第 1-40 页。2009.

  3. R.林德纳和 c .佩克特,“基于 LWE 的加密的更好的密钥大小(和攻击)”在RSA 会议上的密码学家跟踪(第 319-339 页)。斯普林格,柏林,海德堡。2011.

  4. O.Regev,“带错误的学习问题(邀请调查)”,载于 2010 IEEE 第 25 届计算复杂性年会(第 191-204 页)。马萨诸塞州剑桥市 doi: 10.1109/CCC.2010.26. 2010。

  5. D.Micciancio 和 O. Regev,“基于格的密码学”在 D. J. Bernstein 和 J. Buchmann (eds),后量子密码术。斯普林格,2008 年。

  6. C.佩克特。“基于格的密码学的一些最新进展。”TCC’09,2009 特邀教程幻灯片。

  7. D.米西安西奥。“最坏情况复杂性假设的加密函数。”载于:P. Q. Nguyen 和 b . Vall ee(编辑),《LLL 算法:调查和应用,信息安全和密码学》,第 427-452 页。斯普林格,2008 年。预赛。LLL25 版本,2007。

  8. O.雷格夫。"基于格的密码术。"In CRYPTO (第 131–141 页)。2006.

  9. 新希望——后量子密钥封装。网上有: https://newhopecrypto.org/

  10. 丁金泰,谢翔和林小东,“一个简单的基于错误学习问题的可证明安全的密钥交换方案”在线可用: https://eprint.iacr.org/2012/688 2012 年。

  11. C.Peikert,“互联网的格子密码术”在 M. Mosca (ed) 后量子密码术中。PQCrypto 2014。计算机科学讲义,第 8772 卷。施普林格,查姆。2014.

  12. Y.“菲亚特-沙米尔识别协议和格非-菲亚特-沙米尔签名方案”在 H.C.A. van Tilborg 和 S. Jajodia(编辑)的《密码学与安全百科全书。马萨诸塞州波士顿斯普林格。2011.

  13. 动词 (verb 的缩写)柳巴舍夫斯基,“没有陷门的格子签名”在 D. Pointcheval 和 T. Johansson (eds )的《密码学进展——EUROCRYPT 2012》中。欧洲密码 2012。计算机科学讲义,第 7237 卷。斯普林格,柏林,海德堡。2012.

  14. Tim Güneysu、Vadim Lyubashevsky 和 Thomas Pö ppelmann,“实用的基于格的加密:嵌入式系统的签名方案”在伊曼纽尔普鲁夫和帕特里克绍蒙特(编辑。)计算机科学讲义,(第 530–547 页)。斯普林格·柏林·海德堡。doi:10.1007/978-3-642-33027-8 _ 31。国际标准书号 978-3-642-33026-1。2012.

  15. 兹维卡·布拉克斯基和维诺德·维昆塔纳森,“来自环 LWE 的全同态加密和密钥相关消息的安全性”在菲利普·罗加威(编辑。)计算机科学讲义(第 505–524 页)。斯普林格·柏林·海德堡。doi:10.1007/978-3-642-22792-9 _ 29。国际标准书号 978-3-642-22791-2。2011.*

十五、基于混沌的密码学

基于混沌的密码学是应用混沌理论及其数学背景来创造新的独特的密码算法。1989 年,罗伯特·马修斯在 [1 ]首次对其进行了研究,并引起了人们极大的兴趣。

与我们每天使用的普通密码原语相比,为了以有效的方式使用混沌理论和系统,混沌映射必须在考虑混淆和扩散的情况下实现。在本章中,我们将使用混沌系统的概念来指代加密算法。

为了理解混沌系统和密码算法之间的区别和相似之处,让我们来看看表 15-1 中所示的以下关系,这些关系是由 L. Kocarev 在 [2 中建立的。

表 15-1

混沌系统与密码算法的异同

|

Chaotic System

|

Cryptographic Algorithm

|
| --- | --- |
| Phase space: subset of real numbers | Phase space: finite set of integers |
| Iterations | Rounds |
| Parameters | Key |
| Sensitivity to a change in initial conditions and parameters | Diffusion |
| ? | Security and performance |

为了展示表 16-1 中所示的相似性和差异,我们将把换挡图 :

$$ x\left(t+1\right)= ax(t)\ \left(\mathit{\operatorname{mod}}\ 1\right) $$

作为一个混沌系统的例子来考虑

其中相空间x=【0,1】表示单位区间, a > 1 表示整数。

不同的函数和离散时间系统已被提出用于密码学。一旦你看了它们,你会看到相空间代表一个有限的整数集合,参数等于整数。一个最常见的例子是由上面作为例子给出的移位映射的离散相空间版本表示:

$$ p\left(t+1\right)= ap(t)\ \left(\mathit{\operatorname{mod}}\ N\right) $$

其中 a > 1、 Np 表示整数,p∈【0,1,…、N—1】。如果 Na 互质,则换挡图的第二表示是可逆的。这意味着有限相空间动力系统内的所有轨迹都被特征为周期性的。在这种情况下,您可以看到正在引入的新概念,周期函数 P N 将特征图 F 的最小周期,即$$ {F}^{P_N} $$为其单位, P * N * 表示最小值,为系统大小 N 的函数。

当处理实际的混沌系统时,另一个非常重要的度量是由李亚普诺夫指数来特征的,它通常等于 0。这是因为轨道是周期性的,它会重复自己。

也就是说,我们将给出两个框图(文本加密和图像加密)来展示使用混沌方法的加密方案是什么样子。在图 15-1 和图 15-2 中,可以看到使用 logistic 映射对一个明文进行加密和解密的过程。对于图像加密和解密,图 15-3 给出了一个示例模型。

img/493660_1_En_15_Fig3_HTML.jpg

图 15-3

图像加密密码系统框图 [15

img/493660_1_En_15_Fig2_HTML.jpg

图 15-2

使用逻辑映射 [14 ]进行文本加密的框图

img/493660_1_En_15_Fig1_HTML.jpg

图 15-1

使用逻辑映射 [14 ]进行文本加密的框图

基于作为示例提出的框图,您可以查看原始论文和本章末尾列出的其他论文,并看到加密模型和构建它们可以根据所使用的混沌映射而有所不同。在开始设计新的方法和混沌加密机制之前,理解特定的混沌映射是如何工作的是非常重要的。使用以下框图作为从理论到实践的实践指南是一个很好的起点,因为模型的创建考虑了表 15-1 中列出的相似性和差异性。

证券分析

本节将把安全分析称为发现密码系统的弱点和安全漏洞、获得全部或部分加密图像/明文或在不知道算法或解密密钥的情况下找到密钥的方法。

在 [3 和 [4 中可以找到一些有趣的对加密图像的攻击。当设计混沌系统或进行密码分析攻击时,考虑以下标准和分析是很重要的:

  • 关键空间分析。该分析处理寻找解密密钥的尝试次数。这是通过在密码系统的密钥空间内尝试所有可能的密钥来实现的。值得注意的是,密钥空间随着密钥大小的增加而呈指数增长。

  • 关键敏感度分析。为了有一个好的图像加密系统,密钥的敏感性是重要的,需要加以考虑。如果密钥中只有一位被更改,这将输出完全不同的图像(加密或解密)。

  • 统计分析。执行这种类型的分析的目的是证明真实图像和加密图像之间的关系。

  • 相关系数分析。直方图是一种重要的图形工具,用于研究由动态系统的轨迹产生的值的分布。除了直方图分析之外,用于研究普通图像和/或加密图像的相邻像素的相关性的另一个重要工具是基于垂直、水平和对角分布的两个像素的相关性。

  • 信息熵分析。熵分析用于测试加密算法的鲁棒性。比较普通图像和加密图像的熵是非常重要的。通过这样做,我们能够看到加密图像的熵大约等于 8 位深度。这将有助于我们证明对抗熵攻击的加密技术。

  • 微分分析。在差分分析的帮助下,我们将确定加密算法对算法中产生的任何微小变化的敏感性。有两个标准用于测试灵敏度。一个是 NPCR(像素数变化率),第二个是 UACI(统一平均变化强度)。一旦计算出这两个标准,基于高值,我们可以看到在普通图像中发生的任何微小变化的迹象,这将在加密图像中产生显著变化。

用于明文和图像加密的混沌映射

在这一节中,我们将介绍基于加密目标(文本加密或图像加密)的混沌映射。

如上所述,表 15-2 中列出的大多数图像加密算法都由参考文献中提到的作者使用不同的安全性分析进行了测试。对性能进行验证和对密码系统的健壮性进行评估是非常有用的。根据他们的分析和测试,所有的参考文献都被检查和选择为好的参考文献。

表 15-2

用于图像加密的混沌映射(系统)

|

混沌映射(系统)

|

韵律学

|

钥匙

|

文献学

|
| --- | --- | --- | --- |
|

|

NPCR(美国国家广播公司)

|

阿兵哥

|

空间

|

敏感性

|
| --- | --- | --- | --- | --- |
| 罗伦兹面包师 | 7.9973 | - | - | 2 128 | 高的 | [5 |
| 罗伦兹 | - | - | - | 大型 | 中等 | [6 |
| 海农 地图 | 7.9904 | 0.0015% | 0.0005% | 2 128 | 高的 | [7 |
| 物流地图 | 7.9996 | 99.6231% | 33.4070% | 10 45 | 高的 | [8 |
| 三角地图 | - | 0.25% | 0.19% | 2 302 | - | [9 |
| 阿诺德 cat map | 7.9981 | 99.62% | 33.19% | 2 148 | 高的 | [10 |
| 切比雪夫地图 | 7.9902 | 99.609% | 33.464% | 2 167 | 高的 | [11 |
| 圆形地图 | 7.9902 | 99.63% | 33% | 2 256 | 高的 | [12 |
| 阿诺德地图 | - | 0.0015% | 0.004% | - | - | [13 |

实际实施

混沌系统的大部分应用可以在明文加密图像加密上看到。正如我们在本书中讨论的其他密码学领域一样,研究团体提供了大量的理论贡献。实际实施的缺乏给研究人员和专业人员带来了多重困难和挑战。

混沌系统加密的实际实现并不多,但是在 [16 ]中可以找到一些实用的方法(这里我们指的是伪代码算法)。该工作有一个非常深入的结构,并给出了一些关于如何通过提供伪代码来实现基于混沌理论的不同密码系统的想法。这项工作涵盖以下密码系统类型:

  • 基于混沌的公钥密码

  • 密码学中的伪随机数生成

  • 高维混沌映射的形成及其在密码学中的应用

  • 基于混沌的哈希函数

  • 基于混沌的视频加密算法

  • 混沌密码的密码分析

  • 基于混沌的密码的硬件实现

  • 混沌保密光通信系统的硬件实现

在 [16 ]中,从第二章开始,作者提出了一个有趣的公钥密码系统,它使用混沌方法,由三个步骤组成:密钥生成算法(见伪代码 15-1)、加密算法(见伪代码 15-2)和解密算法(见伪代码 15-3)。该场景是两个用户实体 Alice 和 Bob 之间的典型通信。让我们看看每个算法的结构,最后我们将提供一个简单的定向实现,作为专业人员未来的参考指南。

伪代码 15-1。16

**** 必须生成一个大整数 a

  • 根据选择为p∈[1,1]的随机数,计算Ga(p)。

  • 爱丽丝将她的公钥设为( pG ( p )),私钥设为 a

Start. Alice will need, before the communication, to generate the keys. For this, she will accomplish the following:

伪代码 15-2。16

**** 获取爱丽丝的可信公钥( pGa(p))。

  • 计算并将消息表示为数字M∈[1,1]。

  • 生成一个大整数 r

  • 计算【r】(),【g9】【r】****

*** 取密文作为C=(Gr(p), X )发给爱丽丝。**

Start. Bob wants to encrypt a messsage. To achieve this, the following must be done:

伪代码 15-3。 解密算法16

*** 爱丽丝要用她的私钥 a 并计算Gat=Ga(Gr(p)。

  • 通过计算$$ M=\frac{X}{G_{a\cdotp r}(p)} $$获得消息 M
Start. Alice wants to read the text and to do this she will have to recover M from the ciphertext C. To achieve this, the following steps are done:

基于上面列出的算法,我们将移动到一个实际的实现作为例子。清单 15-1 展示了基于二维环面的自同构的加密和解密。应用有三个类:类TorusAutomorphism(见清单 15-1 )、类GenerateChaosValues(见清单 15-2 )和主Program类(见清单 15-3 )。该应用已经按照上面的伪代码清单进行了相应的修改。您可以在图 15-4 中看到输出。

img/493660_1_En_15_Fig4_HTML.jpg

图 15-4

混沌环面自同构的输出

每个类都有其用途,如下所示:

  • TorusAutomorphism类:该类包含处理整个字符串的char encryption (object CharacterEncryption), char decryption (object CharacterDecryption)main encryption (object[] Encryption)decryption (object[] Decryption)函数的函数和方法。

  • 类:这个类处理自同构的结构函数,比如旋转操作和设置元素的索引。

  • 类:类代表应用的主入口点。

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

namespace ChaosSystemCryptography
{
    class Program
    {
        public static void Main(string[] args)
        {
            bool logging = true;
            string key = "$6G";
            string input_value = string.Empty;
            Console.Write("What to you want to do? Encryption
                                          (e) or Decryption (d): ");
            string option = (Console.ReadLine() == "encrypt")
                                                    ? "e" : "d";

            int zero_location = Convert.ToInt32(key[0]);
            int one_location = Convert.ToInt32(key[1]);
            int two_location = Convert.ToInt32(key[2]);

            TorusAutomorphism torus_automorphism = new
                                         TorusAutomorphism();

            GenerateChaosValues generator0 = new
                        GenerateChaosValues((logging == true) ?
                        "generator0" : null);
            GenerateChaosValues generator1 = new
                        GenerateChaosValues((logging == true) ?
                        "generator1" : null);
            GenerateChaosValues generator2 = new
                        GenerateChaosValues((logging == true) ?
                        "generator2" : null);
            GenerateChaosValues[] generators = new
                        GenerateChaosValues[] { generator0,
                        generator1, generator2 };

            generator0.GeneratorRotation(zero_location);
            generator1.GeneratorRotation(one_location);
            generator2.GeneratorRotation(two_location);

            if (option == "e")

            {
                Console.Write("Enter the text for
                                              encryption: ");
                input_value = Console.ReadLine();

                if (logging == true)
                {
                    generator0.PrintInConsole(zero_location);
                    generator1.PrintInConsole(one_location);
                    generator2.PrintInConsole(two_location);
                }

                Console.WriteLine("");
                Console.WriteLine($"The input message:
                                          {input_value}");

                object[] finalValue =
                            torus_automorphism.Encryption(
                                input_value, generators, logging);

                Console.WriteLine("");
                Console.Write("\nThe output message: ");
                for (int j = 0; j < finalValue.Length; j++) {
                           Console.Write($"{finalValue[j]}"); }
            }
            else if (option == "d")
            {
                Console.Write("What is the ciphertext for
                                                decryption: ");
                string ciphertext_input = Console.ReadLine();
                if (logging == true)

                {
                    generator0.PrintInConsole(zero_location);
                    generator1.PrintInConsole(one_location);
                    generator2.PrintInConsole(two_location);
                }
                Console.WriteLine($"\nEncryption for input
                                      string: {ciphertext_input}");
                     object[] finalDecrypted =
                          torus_automorphism.Decryption(ciphertext
                            _input, generators, logging);
                Console.Write("\nThe decrypted text is: ");
                for (int j = 0; j < finalDecrypted.Length;
                                                          j++)
                       { Console.Write(finalDecrypted[j]); }
            }
            Console.WriteLine("\n Press any key to exit...");
            Console.ReadKey();
        }
    }
}

Listing 15-3Main Program

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

namespace ChaosSystemCryptography
{
    class GenerateChaosValues
    {
        private object[] items = new object[94];
        private char last_direction;
        private readonly string generator_ID;

        //** the property will return the index of the item
        //** for a specific value as character
        public int ItemIndex(char inputValue)
        {
            int locationElement;
            locationElement = Array.IndexOf(items,
                                        inputValue);
            return locationElement;
        }

        //** the property will return the index of the item
        //** for a specific value as integer
        public object ItemIndex(int inputValue)
        {
            object locationElement;
            locationElement = items.GetValue(inputValue);
            return locationElement;
        }

        //** constructor
        public GenerateChaosValues(
                        string generator_name = null)
        {
            generator_ID = generator_name;
            for (int i = 32; i <= 125; i++)
            {
                items[i - 32] = (char)i;
            }

        }

        public void GeneratorRotation(int rotation,
                                          char chosen_direction)
        {
            object[] rotation_done = new object[items.Length];
            int length = items.Length;
            int rotation_location = (rotation % length);

            //** the torus will have a right rotation
            if (chosen_direction == 'R')
            {
                for (int in_direction = 0; in_direction <
                                     length; in_direction++)
                {
                    if (rotation_location + in_direction
                                      >= length)
                    {
                        int suplimentary_rotation =
                             rotation_location - (length –
                                               in_direction);
                        rotation_done[suplimentary_rotation] =
                                           items[in_direction];
                    }
                    else
                    {
                        rotation_done[in_direction +
                                    rotation_location] =
                                   items[in_direction];
                    }
                }
                last_direction = 'R';
            }

            else
            {
                for (int in_direction = 0; in_direction <
                                        length; in_direction++)
                {
                    if (rotation_location + in_direction
                                         >= length)
                    {
                        int suplimentary_rotation =
                                  rotation_location - (length –
                                      in_direction);
                        rotation_done[in_direction] =
                                 items[suplimentary_rotation];
                    }
                    else
                    {
                        rotation_done[in_direction] =
                               items[in_direction +
                               rotation_location];
                    }
                }
                last_direction = 'L';
            }
            Array.Copy(rotation_done, items,
                             rotation_done.Length);

        }
        public void GeneratorRotation(int rotation)
        {
            object[] rotation_done = new object[items.Length];
            int length = items.Length;
            int rotation_location = (rotation % length);

            for (int in_direction = 0; in_direction < length;
                                              in_direction++)
            {
                if (rotation_location + in_direction
                                              >= length)
                {
                    int suplimentary_rotation =
                          rotation_location –
                                (length - in_direction);
                    rotation_done[suplimentary_rotation] =
                                        items[in_direction];
                }

                else
                {
                    rotation_done[in_direction +
                              rotation_location] =
                              items[in_direction];
                }
            }
            last_direction = 'R';
            Array.Copy(rotation_done, items,
                              rotation_done.Length);
        }
        public void PrintInConsole(int rotated = 0)
        {
            Console.Write($"{generator_ID} rotated {rotated}
            {((last_direction == 'L') ? "left" : "right")}: ");
            for (int i = 0; i < items.Length; i++)
            {
                Console.Write($"{items[i]}, ");
            }
            Console.WriteLine("");
        }
    }
}

Listing 15-2Generating Chaotic Values for Torus

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

namespace ChaosSystemCryptography
{
    class TorusAutomorphism
    {
        private object CharacterEncryption(char
                  input_characters, GenerateChaosValues[]
                  iterations, int generators, bool logging)
        {
            int spotted_difference;
            spotted_difference = iterations[(generators % 2 ==
                   0) ? 0 : 1].ItemIndex(input_characters);
            foreach (GenerateChaosValues iteration_generator
                                              in iterations)
            {
               iteration_generator.GeneratorRotation(
                             spotted_difference, 'L');
                if (logging == true) {
                       iteration_generator.PrintInConsole(
                            spotted_difference + generators); }
            }
            return iterations[2].ItemIndex(0);
        }

        public object[] Encryption(string input_locations,
                        GenerateChaosValues[]
                        generators_locations, bool logging)
        {
            object[] finalOutputObject = new
                             object[input_locations.Length];
            for (int i = 0; i < input_locations.Length; i++)
            {
                finalOutputObject[i] =
                        CharacterEncryption(input_locations[i],
                        generators_locations, i, logging);
            }
            return finalOutputObject;
        }

        private object CharacterDecryption(char
                        input_characterst, GenerateChaosValues[]
                        iterations, int generators, bool
                        logging)
        {
            int spotted_difference;
            spotted_difference =
                   iterations[2].ItemIndex(input_characterst);
            foreach (GenerateChaosValues
                   iteration_generator in iterations)
            {
                   iteration_generator.GeneratorRotation(
                        spotted_difference, 'L');
                if (logging == true) {
                    iteration_generator.PrintInConsole(
                        spotted_difference + generators); }
            }
            return iterations[(generators % 2 == 0) ?
                              0 : 1].ItemIndex(0);
        }

        public object[] Decryption(string
                              encryption_locations,
                              GenerateChaosValues[]
                              generators_locations, bool logging)
        {
            object[] finalOutputObject = new
                       object[encryption_locations.Length];
            for (int i = encryption_locations.Length - 1;
                                              i >= 0; i--)
            {
                finalOutputObject[i] =
                     CharacterDecryption(encryption_locations[i],
                            generators_locations, i, logging);
            }
            return finalOutputObject;
        }
    }
}

Listing 15-1Chaos Encryption/Decryption

Using Torus Automorphism

结论

在这一章中,我们讨论了一种有趣的加密方法,基于混沌的加密。新的密码算法正在使用混沌映射以不同于过去使用的方式生成新的密码原语。

在本章结束时,您现在已经了解了以下内容:

  • 基于混沌的密码原语是如何构建的,以及它们与普通密码原语的不同之处

  • 混沌系统如何设计用于文本加密和图像加密

  • 如何用混沌系统和自同构环面实现一个密码系统

文献学

  1. 罗伯特·马修斯,“关于‘混沌’加密算法的推导”,载于 Cryptologia 13 ,第 1 期(1989): 29-42 页。

  2. 长度 Kocarev,“基于混沌的密码学:概述”,IEEE 电路和系统杂志,第 1 卷,第 3 期,第 6-21 页,2001 年,doi: 10.1109/7384.963463。

  3. Ali Soleymani、Zulkarnain Md Ali 和 Md Jan 诺丁,“关于安全图像传输的主要方面的调查”,载于世界科学、工程和技术学院,2012 年第 66 期,第 247–254 页。

  4. D.Chattopadhyay、M. K. Mandal 和 D. Nandi,“使用圆映射的对称密钥混沌图像加密”,载于印度科技期刊,第 4 卷第 5 期(2011 年 5 月)ISSN: 0974- 6846,第 593-599 页。

  5. Anto Steffi 和 Dipesh Sharma,“使用混沌映射对图像进行加密和解密的改进算法”,载于《国际科学与研究杂志(IJSR)】,印度在线 ISSN: 2319-7064,第 2 卷第 2 期,2013 年 2 月。

  6. K.Sakthidasan Sankaran 和 B.V. Santhosh Krishna,“一种用于数字彩色图像加密和解密的新混沌算法”,载于国际信息与教育技术期刊,第 1 卷第 2 期,2011 年 6 月。

  7. Somaya Al-Maadeed、Afnan Al-Ali 和 Turki Abdalla,“一种新的基于混沌的图像加密和压缩算法”,载于电气和计算机工程杂志,2012 卷,文章 ID 179693,Hindawi 出版公司。

  8. Hazem Mohammad Al-Najjar 和 Asem Mohammad AL-Najjar,“基于逻辑映射和像素映射表的图像加密算法”

  9. 索德伊夫·阿哈德普尔和亚塞尔·萨德拉,“使用混沌耦合映象格子的基于混沌的图像加密方案”

  10. Kamlesh Gupta 和 Sanjay Silakari,“使用混沌映射进行快速彩色图像加密的新方法”,载于信息安全杂志,2011 年 2 月 139-150 日。

  11. 崇富,,,孟伟红,,杨勇,雅文,“一种基于混沌的改进扩散策略的数字图像加密方案”,美国光学学会,【2012 年 1 月 30 日,第 20 卷第 3 期,第 2363–2378 页。

  12. D.Chattopadhyay、M. K. Mandal 和 D. Nandi,“使用圆映射的对称密钥混沌图像加密”,载于印度科技期刊,第 4 卷第 5 期,2011 年 5 月,ISSN: 0974- 6846,第 593-599 页。

  13. Shima Ramesh Maniyath 和 Supriya M,“一种基于 DNA 序列的未压缩图像加密算法”,载于计算机科学&信息技术(CS & IT) ,CCSEA 2011,CS & IT 02,第 258–270 页。

  14. Miguel Murillo-Escobar,“基于逻辑映射的新型对称文本加密算法”,2014 年。

  15. K.Sakthidasan 和 B. V. Santhosh Krishna,“数字彩色图像的图像加密和解密的新协同算法”载于《国际信息与教育技术杂志》,第 1 卷,第 2 期,2011 年 6 月。网上有: http://www.ijiet.org/papers/23-E20098.pdf

  16. 柳普科科卡列夫和连信国。基于混沌的密码学——理论、算法和应用。斯普林格,2011 年。**********

十六、大数据加密

大数据被定义为提取、操纵和分析无法正常处理的大规模数据集(例如 TB 或 Pb 的数据)的过程。

大数据加密处理确保数据的机密性、完整性和真实性。这是一个敏感的话题,需要小心对待,因为每个业务和架构环境的计算模型都是不同的。大数据加密不同于其他加密原语,因为在访问控制策略、云基础架构和存储机制方面,概念和方案的设计方式不同。

本章将从基于云环境的通用计算模型开始,这将允许并促进基于大数据分析的应用。我们将首先根据云计算节点在大数据分析中扮演的角色对其进行分类。我们的旅程基于[3,我们将通过定义以下节点来稍微扩展符号:

  • I N 是用于正被使用的应用的原始数据的输入节点。这种节点用于前端用户输入的数据或来自读取或捕获传感器(如指纹读取器、全息签名、人脸识别等)的数据。).

  • C N计算节点,由于应用的计算过程而发挥重要作用。这种节点基于并包括摄取节点,在我们的例子中,我们称之为消费节点。他们的目标是扫描输入数据并对其进行提炼,以便为分析过程做好准备,并准备好传递给富集节点,目标是实际的分析处理

  • S N存储节点,对于对数据应用加密机制非常重要。它的作用是存储在不同类别的用户或第三方之间发生的计算过程中发现的数据。理解输入数据输出数据存储在该节点内非常重要。

  • R N结果节点,处理接收正在执行的一些计算过程的输出。它能够自动决定是使用上面的输出作为基础,还是将输出发送到特定的客户端。

图 16-1 显示了基于上述概念的大数据分析云架构示例。该模型可以被视为描述基于大数据的各种应用的模式。话虽如此,我们将注意到以下一组或多组类型为 H 的节点,如下所示H+,其中H∈{INC NS N

img/493660_1_En_16_Fig1_HTML.jpg

图 16-1

具有大数据分析应用的云架构示例

在图 16-1 中,你可以看到一个通用的云模型,它是一个查询数据集的应用的例子。示例中的IN表示启动收集参考数据集过程的工具。序列数据由输入节点发送到CN(摄取)节点。这里,计算过程是针对需要被解析的序列进行的,并且在过程结束时被组织到文件或数据库中。下一步是将文件和数据库发送到SN(存储)。有时,CN(浓缩)会对存储的数据执行额外的计算。这个过程大部分时间是离线完成的,方法是根据用户的需求更新相关的元数据。在我们的示例中,RN(数据接收者)代表将数据集与参考数据集关联的用户。

云计算对于在其实体之间传输的任何敏感数据都有一定的风险。为了实现云中加密技术提供的保护,我们将从三个最重要的安全目标来考虑安全性,即 CIA(机密性、完整性和可用性):

  • 机密性:敏感数据,严格地说是指计算输入和输出,必须保持秘密,以防止不受信任的一方或其他潜在的对手。

  • 完整性:必须尽快检测出任何未经授权更新敏感数据的修改。

  • 可用性:数据所有者可以访问数据和计算资源。

让我们关注一下可用性、,因为它在当今大多数云环境中都没有任何加密手段。我们将需要在云计算的保密性和完整性以及如何存储数据上做出让步。如何部署云将决定如何实现机密性和完整性。从一开始就确定谁控制云的每个组件以及组件之间的信任度是非常重要的。基于此,我们将考虑以下场景:

  • 可信云:这种类型的云由政府部署,它与任何外部网络和对手完全隔离。用户和客户可以存储他们的文件,而不用担心它们会被破坏或被盗。这并不意味着完全隔离将提供全面保护。一些节点是暴露的,它们很容易被恶意软件或内部人员破坏。

  • 半可信云:这是一个有趣的场景,因为它是云资源的真实部署。在这种类型的云中,客户端不需要完全信任云,同时也没有指定/提到云是完全不可信的。相反,云的一些部分和组件处于控制之下,提供了在特定时间监控对手活动的解决方案。

  • 不受信任的云:数据的所有者不信任云或任何云节点。在这种情况下,我们将无法保持和提供外包给云的数据或计算的一定程度的机密性或完整性。在这种情况下,客户需要有解决方案和保护措施,以确保机密性和完整性。这种模式被分配给公共云。

在我们经历了一些基本元素以形成云计算和大数据的基础之后,可以在这种环境中应用以确保大数据基础架构中数据安全性的加密技术非常复杂,如果没有专用的第三方软件库或经验丰富的专业人员,它们的适用性很难实现。

在本章中,我们将重点介绍三种加密技术,它们特别适用于实现部署在云中的大数据应用的安全性:

  • 同形加密(HE)在第十三章中介绍。

  • 可验证计算(VC)代表了本章的第一个目标。

  • 安全多方计算(MPC)是本章的第二个目标。

还有其他加密技术可以成功应用于实现云计算的安全性,例如

  • 功能加密

  • 基于身份的加密

  • 基于属性的加密

此外,我们将专注于我们认为有前途并可应用于真实环境的技术。这并不是说 FE、IE 或 AE 等加密类型在真实环境中不具有适用性,而是此时此刻,许多作品都是基于理论假设,并且它们中的大多数是在没有考虑到商业和行业应用的要求和需求的情况下编写的。从理论到实践是一条漫长的道路,需要理论家和实践者携手同行。他们需要坐在圆桌旁,找到当今真正重要的问题的解决方案,以解决存在的问题和差距。

可验证的计算

可验证计算,或可验证计算,是一台或多台计算机将某些功能的计算量卸载给其他人(如具有不可信状态的客户端)的可能性,同时结果被连续验证。见图 16-2 。

img/493660_1_En_16_Fig2_HTML.jpg

图 16-2

可验证的计算场景。云的节点没有任何完整性保护的信任级别。

VC 在真实环境中一个非常有趣的应用是 Merkle 树,目的是验证数据的完整性。对于大数据环境,Merkle 树被定义为可用于验证项目、数据、行、数据集合等不同属性的完整性的数据结构。Merkle 树的好处是它可以处理大量的数据。将可验证的计算算法与 Merkle 树结合起来,在这个方向上已经取得了进展。

在清单 16-1 中,我们将展示一个场景,其中 Merkle 树用于验证来自部署在大数据环境中的数据库的数据。这个例子只是一个模拟(见清单 16-2 和图 16-3 )。在真实的大数据环境中部署应用需要进行适当的调整。

img/493660_1_En_16_Fig3_HTML.jpg

图 16-3

哈希校验完整性

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

namespace VerifiableComputation
{
    class Program
    {
        static void Main(string[] args)
        {
            //** the following items can be seen as big data
            //** items from a database
            //** as being the result of a query
            List<string> bigdata_items = new List<string>
            {
                "WelcomeToApress!",
                "Studying C# is amazing",
                "Adding extra spice, such as cryptography
                   makes it so challenging!",
                "You can master it with passion and
                 dedication!",
                "Good luck!"
            };

            MerkleTreeImplementation tree = new
                      MerkleTreeImplementation(bigdata_items);

            foreach (string s in tree.BigDataOriginalItems)
            {
                Console.WriteLine(s);
            }
            Console.WriteLine("Hash integrity checking is:
                                        {0}", tree.GetRoot());
            Console.ReadKey();
        }
    }
}

Listing 16-2Simple Example of a Merkle Tree as Support for the Practibility of Verifiable Computation

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

namespace VerifiableComputation
{
    class MerkleTreeImplementation
    {
        private List<string> bigDataItems;
        private List<string> bigDataOriginalItems;

        public List<string> BigDataOriginalItems
        {
            get => bigDataOriginalItems;
            private set
            {
                bigDataOriginalItems = value;
                bigDataItems = value;
            }
        }

        public MerkleTreeImplementation(List<string> BigData_OriginalItems)
        {
            BigDataOriginalItems = BigData_OriginalItems;
            CreateTree();
        }

        public string GetRoot()
        {
            return bigDataItems[0];
        }

        private void CreateTree()
        {
            var data_items = bigDataItems;
            var temporary_data_items = new List<string>();

            //** using 2 element go and parse
           //** the list for items
            for (int i = 0; i < data_items.Count; i += 2)
            {
                //** Take the left element
                string left_element = data_items[i];

                //** the element from right is empty
                string right_element = String.Empty;

                //** once we have the proper item we will need
                //** to replace the empty string from above
                //** with the proper one
                if (i + 1 != data_items.Count)
                    right_element = data_items[i + 1];

                //** compute the hash for the left value
                string leftHash = HashTheBigData(left_element);

                //** if we we have the item from
                //** right as being empty we will hash it
                string rightHash = String.Empty;
                if (right_element != String.Empty)
                    rightHash = HashTheBigData(right_element);

                //** if we have the hash for right empty, we
                //** will add the sum of the left with right
                //** into temporary_items
                if (right_element != String.Empty)
                    temporary_data_items.Add
                                      (leftHash + rightHash);
                //** contrary, we will add the left hash only
                else
                    temporary_data_items.Add(leftHash);
            }

            //** if the size of the list is different from 1
            if (data_items.Count != 1)
            {
                //** once we are here we will replace replace
                //** bigDataItems with temporary_data_items
                bigDataItems = temporary_data_items;

               //** call again the function
                CreateTree();
            }
            else
                //** once we get 1 item then we can say that
                //** we have the root for the tree.
                //** we will save it at bigDataItems
                bigDataItems = temporary_data_items;
        }
        private string HashTheBigData(string bigData)
        {
            using (var sha256 = SHA256.Create())
            {
                //** use some big data volume
                byte[] hasshed_bytes_of_bigdata =
                  sha256.ComputeHash(Encoding.UTF8.
                                     GetBytes(bigData));

                //** take the hash value
                //** and work with it accordingly
                string current_hash_bigdata_value =
                       BitConverter.ToString(hasshed_bytes_of_b
                       igdata).Replace("-", "").ToLower();
                return current_hash_bigdata_value;
            }
        }
    }
}

Listing 16-1Implementation of a Merkle Tree as a Solution for a Simple Case of Verifiable Computation

安全多方计算

多方计算(MPC)代表了密码学的一个子领域,其主要目标是为多方提供方法,以加入对其输入的函数的计算过程,并秘密存储这些输入。在 [10 ]中,作者将 MPC 与可搜索加密相结合,以便为在真实的复杂环境中应用 MPC(见图 16-4 )提供一个实际的开端,如大数据和云计算。

img/493660_1_En_16_Fig4_HTML.jpg

图 16-4

安全多方计算

在 [13 ]中,Claudio Orlandi 讨论了一个关于 MPC 适用性的有趣问题和担忧。在将 MPC 作为一种独立的机制在真实环境中使用之前,需要填补多项技术空白。事实上,研究社区已经提供了大量与 MPC 相关的文章,但是 80%以上都是纯理论,没有任何正确应用它的方向或意义。作者在 [6 - 9 ]和 [11 ]中对此方向做了其他贡献。

基于图 16-4 中描述的 MPC 场景,我们将考虑以下 MPC 场景,其组件来自图 16-5 。在 [10 中提出了 MPC 场景,它代表了 MPC 与可搜索加密方案和隐写对象的组合,隐写对象用作唯一识别存储在云和大数据环境中的文件和数据的标识符。为了实现 MPC 协议中数据的安全性,我们将重点介绍加密(图 16-4 中的步骤 3 )和解密(图 16-5 中的步骤 4.3 )。下面提出的算法是针对大数据应用而设计的,并且已经在真实环境中进行了测试,用于天气预报和分析,如 [10 中所述。加密和解密(参见清单 16-3 和 16-4 )是通过 RSA 算法(参见图 16-7 )实现的,该算法经过适当调整和设计,适用于大量数据。

img/493660_1_En_16_Fig5_HTML.jpg

图 16-5

大数据中的 MPC

它与普通 RSA 实现的不同之处在于私钥被表示为一个 XML 文件(见图 16-6 )。您可以看到用于 RSA 初始化的所有必要参数。

img/493660_1_En_16_Fig6_HTML.jpg

图 16-6

RSA XML 私钥

using System;
using System.Security.Cryptography;
using System.Text;

namespace RSACryptography
{
   public static class CryptographyHelper
    {
       private static bool asymmetricEncryptionPadding = false;

       //** keys of 2048 bytes length
       private readonly static string public_key = "MjA0OCE8UlNBS2V5VmFsdWU+PE1vZHVsdXM+djFTTVVyYk5SZW50VDEya0FhWXNRMEh3Y2hjWG9nbnFUWGpYd1NXaGR5Qi9aaTQ5VnF4L0lFdWxSaGFhVjdHOUtENWRmY0I4eEZaZGgyNGJ0MHpZbGFNTlFyRVBNNnQzUEdvZXZmMXVCby9wVnhlcWFocEFkWkIwelNJcjhwTk5UOW52czV5WEN1Q00xRFo0UUR3Q3A3b2U2aXc2ZHZ4VEZNWFZJdW9rSkcrdmlFMWhORDhnbGg0dFVsMWVBdThKT3YyR0tyWmhvTmUxK2tnRzNNUmRueEFGTDQyRDl4eWF5NERvcmpGL2ZjYWNNc3dFYkM3MUo2bFNobnR2YnQ1RnY0elY1bkg0aDhqYzhnV1dQVDUvWG16TElLMmlJRDJ6L3NyeGgvbzdMRkRhWVhXMnVwbUt5VUJQR2k0OGJLUVZKT3JjZU9rd3owWE1nTDFJUk4yWnhRPT08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50PjwvUlNBS2V5VmFsdWU+";
       private readonly static string private_key = "MjA0OCE8UlNBS2V5VmFsdWU+PE1vZHVsdXM+djFTTVVyYk5SZW50VDEya0FhWXNRMEh3Y2hjWG9nbnFUWGpYd1NXaGR5Qi9aaTQ5VnF4L0ldWxSaGFhVjdHOUtENWRmY0I4eEZaZGgyNGJ0MHpZbGFNTlFyRVBNNnQzUEdvZXZmMXVCby9wVnhlcWFocEFkWkIwelNJcjhwTk5UOW52czV5WEN1Q00xRFo0UUR3Q3A3b2U2aXc2ZHZ4VEZNWFZJdW9rSkcrdmlFMWhORDhnbGg0dFVsMWVBdThKT3YyR0tyWmhvTmUxK2tnRzNNUmRueEFGTDQyRDl4eWF5NERvcmpGL2ZjYWNNc3dFYkM3MUo2bFNobnR2YnQ1RnY0elY1bkg0aDhqYzhnV1dQVDUvWG16TElLMmlJRDJ6L3NyeGgvbzdMRkRhWVhXMnVwbUt5VUJQR2k0OGJLUVZKT3JjZU9rd3owWE1nTDFJUk4yWnhRPT08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50PjxQPi8yY1VJS2RlMFB1b2RVaDJQQ3krbFU0aWFvVWtOZ0dOOVhHNmhvcll3c1ovbzdwdTJYZjZmS2E5M09OZ1R0NUpqaW5QL3grZG9ibmFiU1hNNFNwRGJlb3JVRGZBKzhYeDIxTHBCT0FtYUtUVWlkejNjMHlQRXBQZ3lOMlpVb3poUWhjejZlUk01cUdQSlgxU29WMjczM3ZUREFtTEVWS0N4eFRZOHVNSWI3OD08L1A+PFE+djhjYlBmcHh5aXZUelhsV2Q5L3hNK3pRUlJRSk4rTDFIYURiNHYxKzU3dExEb3VlcG03ajI0MkJFZ2U4dTNENmJEanZneWhBWFIxV3IwR09KSjBBb1ZPV2FLLzdvZ3NHZjBnM1dzNzVicWtWSmdNTHZETnFxSVVVd0ZqZml3TllONkJnN0dIdGl2S0VGdmJldTEzcGFxVERyTnFuV0ZQaWFQK1lkQ09xVjNzPTwvUT48RFA+eWVSVDF0UTNjWC9kMUlocFhud0lVOEltRm9vVTY5UWl3YWtiUjR1dWVabXNBR001aVJMOG9WaTFzVXpVTHNRczVRSk1kMklvbTFWdFF1YWtwRUZpZUJxcURvbGtOaUp0WTNDUTN0Zkp4T0szV0J1aVNEUjJ6THEwOEZPc0JjTnp0V2plRXIvendrUm9BYnlsZXdXN281Z2dadDJNWHk4WVRnTSsxQkYvODhVPTwvRFA+PERRPlRGOUxYd1JFbW9HWHFJVkF4UjVlblJJYTR0ZVcwRFhHN1pTbzNKMmRFMFhJSHpQRTYzelBxeGlRSlJFRnZSUEI5cVU1NU41N3UxazZzektGRzltV2JhaXZCbVBHN3dJN0JTZEtQQlNleXMzMUNSMC9hQ1NGdmpTNVRkeFdzYktVU0JyTFhuZWxOS2RkcVJPSkljN0ZiTjNPdXlDY2NoVjkzZGlqNnVSbEtzOD08L0RRPjxJbnZlcnNlUT5rSmYwVHZoNDZjTEQ4OElIVVZ0V3hYaDVsYlNUTWw2ZnB5cFhhUU9laUtpTy9XcnZic21waXdBVEhDQ0pERDhYdDFwbTc5K0hrc21sUjlrYktXR2U4WmNqZHJHdUZlZ3NDUGRpT3VGMVN0a283NWtnblJVY0ZTb1hxSzF1YVgvTWsxTEtDbVpZY3djQ0t2VC9OQUZrWVpVdVNqT3pPckVrRk9VNDdML3VDVE09PC9JbnZlcnNlUT48RD5HbTMyZUZLU0pvODYzZFRFbkFtMVlaRVJRdUZYdldWN1BUcHRLMXdrWXMxVmErc0ZSQnpON3Nza1NIdEUxTXBUbytTQmk2WjBWYmJNY3JIT0dGTUFOQ055Nkh5RzZnOU1pRWJzZWpndzQ2MHJnWUZlWkF1K1RiOG5zMUorR2FNcGNkZGNHa2FPUXMxa0JzaURjZlFZTmMwckNoUVQrMjI5bUVmL3VqUDN6Q1IzcUNzdkZjVTRuMkMwZzBYSWhLQ1dHYXRsbW5MOW9FMWN0MzY4aWZYK0JCUVljUExqSE05TTZaSU9pMWtmR3M2bXhaT0V3cm1BWFB0T0ZweW1tNlZjMUM4WGtVUENCVERtWUZTSFpiaHNaT09IZHpaVVlUa2lmN1VzRk40MjdTSDVrMTNpQTVGRGJTb053bW9kQ0ZrWitENGJNQ2JUZWgwVTNvell6M3FnM1E9PTwvRD48L1JTQUtleVZhbHVlPg==";

       public static string Encrypt(string clearText)
       {
           int size_of_the_key = 0;
           string xmlWithPublicKey = "";

           GetKeyFromEncryptionString(public_key, out size_of_the_key, out xmlWithPublicKey);

           var encrypted = Encrypt(Encoding.UTF8.GetBytes(clearText), size_of_the_key, xmlWithPublicKey);

           return Convert.ToBase64String(encrypted);
       }

       private static byte[] Encrypt(byte[] data, int keySize, string publicKeyXml)
       {
           if (data == null || data.Length == 0)
                throw new ArgumentException("There is empty data", "data");

           int maximum_length = GetMaxDataLength(keySize);

           if (data.Length > maximum_length)
                throw new ArgumentException(String.Format("The maximum length of data is {0}", maximum_length), "data");

           if (!IsKeySizeValid(keySize))
                throw new ArgumentException("The size of the key is not valid", "keySize");

            if (String.IsNullOrEmpty(publicKeyXml))
                throw new ArgumentException("The key is null or empty", "publicKeyXml");

           using (var provider = new RSACryptoServiceProvider(keySize))
           {
               provider.FromXmlString(publicKeyXml);
               return provider.Encrypt(data, asymmetricEncryptionPadding);
           }
       }

       public static string Decrypt(string encryptedText)
       {
           int keySize = 0;
           string publicAndPrivateKeyXml = "";

           GetKeyFromEncryptionString(private_key, out keySize, out publicAndPrivateKeyXml);

           var decrypted = Decrypt(Convert.FromBase64String(encryptedText), keySize, publicAndPrivateKeyXml);

           return Encoding.UTF8.GetString(decrypted);
       }

       private static byte[] Decrypt(byte[] data, int keySize, string publicAndPrivateKeyXml)
       {
           if (data == null || data.Length == 0)
                throw new ArgumentException("The data is empty", "data");

            if (!IsKeySizeValid(keySize))
                throw new ArgumentException("The size of the key is not valid", "keySize");

            if (String.IsNullOrEmpty(publicAndPrivateKeyXml))
                throw new ArgumentException("The key is null or empty", "publicAndPrivateKeyXml");

           using (var provider = new RSACryptoServiceProvider(keySize))
           {
               provider.FromXmlString(publicAndPrivateKeyXml);
               return provider.Decrypt(data, asymmetricEncryptionPadding);
           }
       }

       private static int GetMaxDataLength(int keySize)
       {
           if (asymmetricEncryptionPadding)
           {
               return ((keySize - 384) / 8) + 7;
           }
           return ((keySize - 384) / 8) + 37;
       }

       private static bool IsKeySizeValid(int keySize)
       {
           return keySize >= 384 && keySize <= 16384 && keySize % 8 == 0;
       }

       private static void GetKeyFromEncryptionString(string rawkey, out int keySize, out string xmlKey)
       {
           keySize = 0;
           xmlKey = "";

           if (rawkey != null && rawkey.Length > 0)
           {
               byte[] keyBytes = Convert.FromBase64String(rawkey);
               var stringKey = Encoding.UTF8.GetString(keyBytes);

               if (stringKey.Contains("!"))
               {
                   var splittedValues = stringKey.Split(new char[] { '!' }, 2);

                   try
                   {
                       keySize = int.Parse(splittedValues[0]);
                       xmlKey = splittedValues[1];
                   }
                   catch (Exception e) { }
               }
           }
       }
    }
}

Listing 16-4

CryptographyHelper Class

using System;

namespace RSACryptography
{
    class Program
    {
        public static void Main(string[] args)
        {
            // encryption and decryption a password example
            var password = "P@sswrd123";

            Console.WriteLine("\n Original Password Text: " +
                                          password);

            var textToBeEncrypted =
                              CryptographyHelper.Encrypt(password);
            Console.WriteLine("\n Encrypted Password Text: " +
                                     textToBeEncrypted);

            var textToBeDecrypted =
                  CryptographyHelper.Decrypt(textToBeEncrypted);
            Console.WriteLine("\n Decrypted Password Text: " +
                                       textToBeDecrypted);

            //** encryption and decryption for database
           //** connection string
            var connectionString = "Data Source=USER-
                        Test\\SQLEXPRESS;Initial
                        Catalog=OrderProcessing;Integrated
                        Security=True";

            Console.WriteLine("\n Original Connection String
                                     Text: " + connectionString);

            textToBeEncrypted =
                CryptographyHelper.Encrypt(connectionString);
            Console.WriteLine("\n Encrypted Connection String
                                   Text: " + textToBeEncrypted);
            textToBeDecrypted =
                CryptographyHelper.Decrypt(textToBeEncrypted);
            Console.WriteLine("\n Decrypted Connection String
                                 Text: " + textToBeDecrypted);

            //** encryption and decryption of a very long
            //** query result from DB
            var longTextForEncryption = "Literally, Blockchain is a chain of blocks which could be simply assumed as an immutable data structure. Immutability is one of the most prominent features of a blockchain, which leads us to build trust in completely unreliable environments

.";

           Console.WriteLine("The encryption of the query is:
                  {0}",
                  CryptographyHelper.Encrypt
                         (longTextForEncryption));

            Console.ReadKey();
        }
    }
}

Listing 16-3RSA

for Big Volumes of Data (Encryption and Decryption Operations)

img/493660_1_En_16_Fig7_HTML.jpg

图 16-7

大数据量和其他类型字符串的加密/解密输出。

对于类CryptographyHelperRSACryptographyKeyGenerator,请参见 GitHub 资源库中的完整项目。

结论

在本章中,我们讨论了拥有大数据环境的重要性,以及如何通过不同的加密机制实现安全性,例如可验证计算和多方计算。

在本章结束时,您现在已经了解了以下内容:

  • 云和大数据环境中的主要安全概念

  • 如何实现复杂的密码原语和协议,如可验证计算和多方计算

文献学

  1. 页(page 的缩写)Laud 和 A. Pankova,“诚实多数的多方协议中的可验证计算”,载于 S.S.M. Chow、J.K. Liu、L.C.K. Hui 和 S.M. Yiu (eds) 可证明的安全性。ProvSec 2014。计算机科学讲义,第 8782 卷。施普林格,查姆。2014.

  2. D.安全多方计算的遗忘排序算法的实际分析。在 K. Bernsmed 和 S. Fischer-Hübner (eds) 安全信息技术系统。NordSec 2014。计算机科学讲义,第 8788 卷。施普林格,查姆。2014.

  3. D.Bogdanov、L. Kamm、S. Laur 和 p . prulmann-Vengerfeldt,“安全多方数据分析:最终用户验证和实践实验”,2014 年。

  4. B.奥兹卡马克,a . özbi̇len,u .亚瓦诺格鲁,k . ci̇n,“大数据中的神经和量子密码术:综述”,2019 年 IEEE 大数据国际会议(大数据),美国加利福尼亚州洛杉矶,2019 年,第 2413-2417 页,doi:10.1109/big data 47090 . 20193409

  5. 南 Yakoubov,V. Gadepally,N. Schear,E. Shen 和 A. Yerukhimovich,“保护云中大数据分析的加密方法综述”,发表于 2014 年 IEEE 高性能极限计算会议(HPEC) ,马萨诸塞州沃尔瑟姆,2014 年,第 1-6 页,doi:10.1109/hpec . 2014 . 111191991

  6. 基于椭圆曲线的可搜索加密方案。在 Barolli L .、Amato F .、Moscato F .、Enokido T .、Takizawa m .编的《Web、人工智能和网络应用。怀娜 2020。智能系统和计算进展,第 1150 卷。施普林格,查姆。2020.

  7. 《云计算的混合可搜索加密方案》在 J.L. Lanet 和 C. Toma (eds) 创新的信息技术和通信安全解决方案。SECITC 2018。计算机科学讲义,第 11359 卷。施普林格,查姆。2019.

  8. 动词 (verb 的缩写)C. Pau 和 M. I. Mihailescu,“物联网及其在生物识别技术和电子学习应用中的作用”,载于 2015 年第十三届现代电气系统工程国际会议(EMES) ,(第 1-4 页)Oradea。doi:10.1109/emes . 2015.7158430 . 2015。

  9. 南 L. Nita 和 M. I. Mihailescu,“论用于云计算安全的人工神经网络——一项调查”,载于 2018 年第十届国际电子、计算机和人工智能会议(ECAI) (第 1-6 页)。雅西,罗马尼亚,doi:10.1109/ecai . 2018.86770858866

  10. Marius Iulian Mihailescu、Stefania Loredana Nita 和 Ciprian Racuciu,“适用于地球科学的基于可搜索加密和多方计算的认证协议”,载于海军学院科学通报,第二十三卷(第 221-230 页)。doi:10.21279/1454-864 x-20-I1-0302020.

  11. Marius Iulian Mihailescu、Stefania Loredana Nita 和 Ciprian Racuciu,“使用可搜索对称加密的多级访问及其对地球科学的适用性”,载于海军学院科学通报,第二十三卷(第 221-230 页),doi:10.21279/1454-864 x-20-I1-030。2020.

  12. Stefania Loredana Nita,Marius Iulian Mihailescu 和 Ciprian Racuciu,“使用 MapReduce 在云计算中进行安全文档搜索”,载于海军学院科学通报,第二十三卷(第 221-230 页),doi:10.21279/1454-864 x-20-I1-030。2020.

  13. RSA 大数据分析扩展。网上有: www.rsa.com/en-us/company/news/rsa-extends-big-data-analytics-to-help-organizations-identify

  14. Claudio Orlandi,多方计算在实践中有用吗?网上有: www.cs.au.dk/~orlandi/icassp-draft.pdf

十七、云计算密码术

云计算中的密码学代表了为客户端和复杂网络环境设计和实现安全应用的一个热点。云中的加密技术采用复杂的加密技术,目的是保护将在云环境中存储和稍后使用的数据。

组织已经迅速采用云技术作为 IaaS(基础设施即服务)、PaaS(平台即服务)和 SaaS(软件即服务)。原因包括带来的好处,如效率和灵活性,最终将降低成本。

以下密码学原语和机制也受到了研究团体和行业的关注:

  • 同态加密(参见第十三章

  • 可搜索加密(参见第十二章

  • 结构化加密(SE)

  • 功能加密

  • 私人信息检索

在本章中,我们将重点介绍结构化加密(STE)、函数加密、透明数据加密(TDE)和多方计算(MPC)。前两个原语,同态加密和可搜索加密,由于它们的重要性和发展,在章节中以相同的名称出现。

结构化加密

STE 方案的作用是为数据结构提供加密。STE 方案基于用于查询结构的某个令牌,该令牌能够给出查询的评估,并从结构或查询中了解最明确的泄漏。可搜索对称加密(SSE)(见第十二章)代表了 STE 的一个特例。快速提醒一下,SSE 将搜索结构加密为倒排索引或类似的搜索树。在实践中,SSE 为设计和实现搜索引擎提供了一个自然的解决方案(参见清单 12-2 )

  • 为数据集生成搜索结构并为其提供加密

  • 搜索特定关键字 w 并基于 w 生成令牌并继续查询加密结构

在图加密中可以找到类似的 STE 方案,它为使用图结构的加密数据库的设计阶段提供了一个有趣的解决方案。加密图形数据库以在社交搜索环境中保护其隐私是一个非常好的例子,特别是因为我们正在云计算环境中处理复杂的大数据基础设施,如大数据分析和统计。

作为本节框架的一个例子,我们将考虑一个使用基于图的加密技术的数据库加密算法(参见伪代码 17-1 和清单 17-1 )。在清单 17-2 中,你可以看到清单 17-1 中提出的框架的工作版本。输出如图 17-1 所示。该算法接收将被加密的 n 个字符作为的第一个输入,( ch 1ch 2chn)。从数据库中接收字符作为记录。第二输入由路径的起点( s p )和终点( e p )表示。第三个输入由被分析路径的长度表示( l p )。

img/493660_1_En_17_Fig1_HTML.jpg

图 17-1

使用图加密 SE

话虽如此,让我们看看清单 17-1 中的伪代码。

伪代码 17-1。算法的伪代码

$$ for\ i=1\  to\ n\  do $$

$$ \mathrm{begin} $$

INPUT:
      ch1, ch2, ..., chn − characters as single records
      sp − the starting point of the vertex path
      ep − the ending point of the vertex path
      lp − length path

$$ end $$

      temporary ← chi
      generate a next path for linking sp and ep
      for j = lp downto 1 do begin
            temporary ← temporary XOR kj
            temporary ← temporary ⋘ 1
            temporary ← Π(temporary)
      end
      encryptedch(i) ← temp
      return the encrypted character, encryptedch(i)

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

namespace StructuredEncryptionUsingGraphEncryption
{
    class DBGraphEncrypt
    {
        //** this queries represents some constants which
       //** represents records from the database.
        public const int query1 = 255;
        //public const string query2 = "Address";
        //public const string query3 = "Phone number";

        public DBGraphEncrypt()
        {

        }

        static public byte[] Decryption(byte[] Data,
                    RSAParameters RSAKey, bool DoOAEPPadding)
        {
            try
            {
                byte[] decryptedData;
                using (RSACryptoServiceProvider RSA = new
                               RSACryptoServiceProvider())
                {
                    RSA.ImportParameters(RSAKey);
                    decryptedData = RSA.Decrypt(Data, DoOAEPPadding);
                }
                return decryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());
                return null;
            }
        }

        static public byte[] Encryption(byte[] Data,
                     RSAParameters RSAKey, bool DoOAEPPadding)
        {
            try
            {
                byte[] encryptedData;
                using (RSACryptoServiceProvider RSA = new
                                  RSACryptoServiceProvider())
                {
                    RSA.ImportParameters(RSAKey);
                    encryptedData = RSA.Encrypt(Data, DoOAEPPadding);
                }
                return encryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }

        private static void Print(int[,] distance, int verticesCount)
        {
            Console.WriteLine("Shortest distances between
                                        every pair of vertices:");

            for (int i = 0; i < verticesCount; ++i)
            {
                for (int j = 0; j < verticesCount; ++j)
                {
                    if (distance[i, j] == query1)
                        Console.Write("query1".PadLeft(7));
                    else
                        Console.Write(distance[i,
                                  j].ToString().PadLeft(7));
                }

                Console.WriteLine();
            }
        }

        public static void GraphSimulation(int[,] graph, int verticesCount)
        {
            int[,] distance = new int[verticesCount, verticesCount];

            for (int i = 0; i < verticesCount; ++i)
                for (int j = 0; j < verticesCount; ++j)
                    distance[i, j] = graph[i, j];

            for (int k = 0; k < verticesCount; ++k)
            {

                for (int i = 0; i < verticesCount; ++i)
                {
                    for (int j = 0; j < verticesCount; ++j)
                    {
                        if (distance[i, k] + distance[k, j] <
                                              distance[i, j])
                            distance[i, j] = distance[i, k] +
                                              distance[k, j];
                    }
                }
            }

            Print(distance, verticesCount);
        }

        static void Main(string[] args)
        {
            UnicodeEncoding ByteConverter = new UnicodeEncoding();
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

            int[,] databaseGraphRecords = {
                         { 0,   6,  query1, 11 },
                         { query1, 0,   4, query1 },
                         { query1, query1, 0,   2 },
                         { query1, query1, query1, 0 }
                           };

            byte[,] dataGraphForEncryption = new
                        byte[databaseGraphRecords.Length,
                        databaseGraphRecords.Length];
            byte[] dataForEncryption = new
                        byte[databaseGraphRecords.Length];
            string[] test = new
                        string[databaseGraphRecords.Length];

            int k = 0;
            for (int i = 0; i <
                    Math.Sqrt(databaseGraphRecords.Length); i++)
            {

                for (int j = 0; j <
                    Math.Sqrt(databaseGraphRecords.Length); j++)
                {
                    dataForEncryption[k] =
                          Convert.ToByte(databaseGraphRecords
                                                [i, j]);
                    k++;
                }
            }

            byte[] encryptions = Encryption(dataForEncryption,
                        RSA.ExportParameters(false), false);
            Console.WriteLine("The encryption of the records
                        graph from the database is:");
            for(int i = 0; i < encryptions.Length; i++)
            {
                Console.Write("0x" + encryptions[i].ToString()
                                                 + " ");
            }

            Console.WriteLine("\n");
            GraphSimulation(databaseGraphRecords, 4);

            Console.WriteLine("\n");
            byte[] decryptions = Decryption(encryptions,
                              RSA.ExportParameters(true), false);
            Console.WriteLine("The decryption of the records
                              graph from the database is:");
            for (int i = 0; i < decryptions.Length; i++)
            {
               Console.Write(decryptions[i].ToString() + " ");
            }
        }
    }
}

Listing 17-2Encryption of SE Using Graph

using System;

namespace GraphEncryptionDatabase
{
      class DBGraphEncrypt
      {
            //** starting point
            int sp;

            //** ending point;
            int ep;

            //** path length
            int lp;

            //** constructor
            public DBGraphEncrypt() {}

            public void GraphEncryption()
            {
                  //** let's declare an instance of the context
                  //** we will use Linq-to-Sql for this example

                  Apress_DBGraphEncExampleDataContext dbContext
                  = new Apress_DBGraphEncExampleDataContext();

                  //** create an instance of the class
                  //** generated for the table
                  EmployeeDetail emp_details = new
                                      EmployeeDetail();

                  //** select the record that
                  //** you want to encrypt
                  var query = (from emp in
                   dbContext.EmployeeDetails
                     select emp.Password).Single();

                  //** parse the record

                  string record = query.ToString();
                  for(int i=0; i<record.Length; i++)
                  {
                        char ch = record[i];
                        Encrypt(ch.ToString());
                  }
            }

            public string Encrypt(string ch)
            {
                  Encryption.useEncAlgorithm(new
                  Encryption(Encryption.ALGORITHM.GraphEnc));

                  string encryption_key;

                  Console.WriteLine("Enter the password :");
                       String password = encryption_key.readLine();

                  byte[] secret_key = GenerateKeys.keyGen(256,
                            password, "salt/salt", 100000);

                  Console.WriteLine("Return the records
                           (strings) that will be encrypted.");
                  String pathName = encryption_key.readLine();

                  ArrayList<string> listOfCharacters = new
                                          ArrayList<string>();
                  listOfCharacters.Add(ch);

                  //** parse the listOfCharacters
                  //** parameters of the graph vertex for the
                  //** size of the data set
                  //** change in such way that you will get a
                  //** better performance
                  int big_block = 700;
                  int small_block = 120;
                  int size_of_data = 12000;

                  while (true)
                  {
                        Console.WriteLine("Enter the keyword tosearch for:");
                        String keyword = Console.Read();
                        byte[][] token_encryption_key =
                          GraphEnc.token(secret_key , keyword);
                        Console.WriteLine(GraphEnc.resolve(CryptoPrimitives.
                generateGraphEnc(secret_key, 3 + new String()),
                generateGraphEnc.query(GraphEnc.GeneratePath(sp, ep, lp),
                   GraphEnc.generateListOfCharacters(),
                   GraphEnc.Encrypt())));
                  }
            }
      }
}

Listing 17-1The Implementation

OUTPUT:
      encryptedch(1), encryptedch(2), …, encryptedch(n)

功能加密

函数加密是公钥加密的推广。在这种类型的加密中,拥有秘密密钥允许人们了解正在加密的密文的功能。FE 是复杂的,它有几个子类,如谓词加密、基于身份的加密、基于属性的加密(ABE)、密文策略 ABE、隐藏向量加密和内积谓词。

函数加密方案表示由四种算法组成的元组,例如 setup、keygen、encryption 和 decryption。正确性需要满足一个条件,即 kKxX :

  • 设置 (1 λ ) → ( 公共 按键 按键 )

  • ( ) →

  • 加密 ( 公开密钥消息 ) → 密文

  • 解密 ( 秘密密钥密文 ) → y

上面的步骤可以在下面的清单 17-3 中调换,其中我们介绍了一个通用函数加密方法应该如何实现的通用框架。清单 17-3 中的例子并不代表可以编译和执行的例子。由于其复杂性和应用,给出一个 FE 的实现可能是一个非常具有挑战性的任务。

//** generating master keys and FE key

//** length of input vectors
int input_vector_length = 2;

//** the upper limit for input vector coordinates
Random rand = new Random();
int bound = rand.Next();

//** length in bit of the prime modulus
int length_of_modulus = 2048;

trusted_entity = GenerateNewVector(input_vector_length,
                                     length_of_modulus, bound);
master_key = trusted_entity.GenerateMasterKey();
master_public_key = trusted_entity.GenerateMasterPublicKey();

int y = data. GenerateNewVector ([]*big.Int{big.NewInt(1),
                                     big.NewInt(2)});
functional_encryption_key =
           trusted_entity.KeyDerivation(master_secret_key, y);

//** create a simulation of encryptor
//** our encryption will hide the message (x) using
//** master public key by the trusted entity
Encryption = GenerateParameters(trusted_entity.Parameters);
message = GenerateNewVector([]*rand.Next{rand.Next(3) rand.Next(4)});,
cipher = encryption.Encrypt(message, master_public_key);

//** create simulation of the decryptor that decrypts the cipher
Decryption = GenerateParameters(trusted_entity.Parameters);

//** do the decryption in order to get the result
//** inner prod of x and y
//** we are expecting xy to be 11 (e.g. <[1,2],[3,4]>)
xy = Decrypt(cipher, functional_encryption_key, y) ;

Listing 17-3Simple Implementation and Startup for FE

在清单 17-4 和图 17-4 中,我们提供了一个用于函数加密的公钥加密模块的实现示例。该示例设计用于包含敏感数据(如大量数据、查询结果等)的文件的云架构。).在从 GitHub 库运行清单 17-4 的项目之前,确保您已经使用来自开发者命令提示符的makecert命令正确地创建了证书(参见图 17-2 )。为此,您需要运行以下命令:

img/493660_1_En_17_Fig2_HTML.jpg

图 17-2

使用 makecert 创建证书

makecert -r -pe -n "CN=CERT_APRESS_FUNCTIONALENCRYPTION_CERT" -b 01/01/2020 -e 01/12/2020 -sky exchange -ss my

下一步是通过检查控制台窗口来验证证书是否被正确导入(参见图 17-3 )。

img/493660_1_En_17_Fig3_HTML.jpg

图 17-3

导入证书

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace FunctionalEncryption
{
    class Program
    {
        //** variables for different paths, such as encryption
        //** and decryption path for folder
     private static string encryption_folder = @"D:\Encrypt\";
     private static string decryption_folder = @"D:\Decrypt\";
     private static string genuine_file = "TestData.txt";
     private static string encrypted_file = "TestData.enc";

        static void Main(string[] args)
        {
            //** create a stream as input file
            //** for encrypting test data
            StreamWriter stream_writer =
                    File.CreateText(genuine_file);
            stream_writer.WriteLine("Data as test to be encrypted");
            stream_writer.Close();

            //** use the certificate for encrypting the key
            X509Certificate2 certificateUsedForEncryption =
                  ObtainTheCertificateFromTheStore("CN=CERT_APRE
                  SS_FUNCTIONALENCRYPTION_CERT");
            if (certificateUsedForEncryption == null)
            {
                Console.WriteLine("Certificate
                     'CN=CERT_APRESS_FUNCTIONALENCRYPTION_CERT'
                     was not found.");
                Console.ReadLine();
            }

            //**  Do the encryption of the file using the
            //** public key from the certificate
            FileEncryption(genuine_file,
             (RSA)certificateUsedForEncryption.PublicKey.Key);

            //** Do the decryption of the file based on the
            //** private key obtained from the certificate
            FileDecryption(encrypted_file,
             certificateUsedForEncryption.GetRSAPrivateKey());

            //** Show in the console the
            //** genuine data and the decrypted data
            Console.WriteLine("Genuine message:   {0}",
                             File.ReadAllText(genuine_file));
            Console.WriteLine("Round Trip: {0}",
                          File.ReadAllText(decryption_folder +
                   genuine_file));
            Console.WriteLine("Press any key to continue.");
            Console.ReadLine();
        }

        private static X509Certificate2
                  ObtainTheCertificateFromTheStore(string
                   certificate_name)
        {

            //** go to the certification store and make sure
            //** that you are under the current user
            X509Store certification_store = new
                    X509Store(StoreLocation.CurrentUser);
            try
            {
                certification_store.Open(OpenFlags.ReadOnly);

                //** entire collection of certificates should
                //** be added to X509Certificate2Collection object.
                X509Certificate2Collection
                            collection_of_certifications =
                            certification_store.Certificates;

                //** validate the certificate
                X509Certificate2Collection
                       current_certificates =
                       collection_of_certifications.Find(
                       X509FindType.FindByTimeValid,
                       DateTime.Now, false);
                       X509Certificate2Collection
                       signing_certificate =
                       current_certificates.Find(X509FindType.
                       FindBySubjectDistinguishedName, certificate_name, false);
                if (signing_certificate.Count == 0)
                    return null;

                //** get the first certificate
                //** from the collection
                return signing_certificate[0];
            }
            finally
            {
                certification_store.Close();
            }
        }

        //** based on the public key encrypt the file
        private static void FileEncryption(string input_file,
                                          RSA rsaPublicKey)
        {
            using (Aes aes_algorithm = Aes.Create())
            {
                // Create instance of Aes for
                // symetric encryption of the data.
                aes_algorithm.KeySize = 256;
                aes_algorithm.Mode = CipherMode.CBC;
                using (ICryptoTransform
                              transforming_operations =
                              aes_algorithm.CreateEncryptor())
                {
                    RSAPKCS1KeyExchangeFormatter
                              formatOfTheKey = new
                              RSAPKCS1KeyExchangeFormatter
                                          (rsaPublicKey);
                    byte[] encryption_of_the_key =
                      formatOfTheKey.CreateKeyExchange(
                            aes_algorithm.Key,
                               aes_algorithm.GetType());

                    // Create byte arrays to contain
                    // the length values of the key and IV.
                    byte[] key_length = new byte[4];
                    byte[] initializing_vector_length = new byte[4];

                    int length_of_the_key =
                           encryption_of_the_key.Length;
                    key_length =
                     BitConverter.GetBytes(length_of_the_key);
                    int length_of_initializing_vector =
                                       aes_algorithm.IV.Length;
                    initializing_vector_length =
                           BitConverter.GetBytes(
                               length_of_initializing_vector);

                    //** the following will written in the
                    //** encrypted file
                    //** - length of the key
                    //** - length of the initializing vector
                    //** - encrypted key
                    //** - the initializing vector
                    //** - the encryption of the cipher
                    //** content
                    int start_file_name =
                        input_file.LastIndexOf("\\") + 1;

                    //** change the file's extension to ".enc"
                    string output_file = encryption_folder +
                        input_file.Substring(start_file_name,
                        input_file.LastIndexOf(".") –
                        start_file_name) + ".enc";
         Directory.CreateDirectory(encryption_folder);

                    using (FileStream output_file_stream = new
                               FileStream(output_file,
                               FileMode.Create))
                    {

                        output_file_stream.Write(
                                    key_length, 0, 4);

                        output_file_stream.Write(
                           initializing_vector_length, 0, 4);

                        output_file_stream.Write(
                           encryption_of_the_key, 0,
                           length_of_the_key);

                        output_file_stream.Write(aes_algorithm.
                           IV, 0, length_of_initializing_vector);

                        //** proceed with writting the cipher
                        //** text based on the cryptostream
                        //** used for encryption
                        using (CryptoStream
                                ouput_stream_encrypted = new
                                CryptoStream(output_file_stream,
                                transforming_operations,
                                CryptoStreamMode.Write))
                        {
                        //** save memory by proceeding with encryption of
                        //** different chunks at a time
                            int count = 0;

                        //** size_bytes_of_block can
                        //** represent a randomly size
                            int size_bytes_of_block =
                                      aes_algorithm.BlockSize / 8;
                            byte[] data_block = new
                                      byte[size_bytes_of_block];
                            int bytesRead = 0;

                            using (FileStream
                                      input_file_stream = new
                                             FileStream(input_file,
                                             FileMode.Open))
                            {
                                do
                                {
                                    count =
                                           input_file_stream.Read(
                                         data_block, 0,
                                                size_bytes_of_block);

                                             ouput_stream_encrypted.
                                                   Write(data_block,
                                                   0, count);
                                    bytesRead += count;
                                }
                                while (count > 0);
                                input_file_stream.Close();
                            }

                                ouput_stream_encrypted.
                                      FlushFinalBlock();
                            ouput_stream_encrypted.Close();
                        }
                        output_file_stream.Close();
                    }
                }
            }
        }

        private static void FileDecryption(string inFile, RSA rsaPrivateKey)
        {
            //** create an object as AES for providing
            //** symmetric decryption for our data
            using (Aes aes_algorithm = Aes.Create())
            {
                aes_algorithm.KeySize = 256;
                aes_algorithm.Mode = CipherMode.CBC;

                //** declare byte arrays and obtain their
                //** length for the key encryption and
                //** initializing vector
                byte[] length_of_key = new byte[4];
             byte[] length_initializing_vector = new byte[4];

              //** generate the file name for decryption file
                string output_file = decryption_folder +
                          inFile.Substring(0,
                          inFile.LastIndexOf(".")) + ".txt";

                //** declare file stream objects for reading
                //** the encrypted file (input_file_stream)
                //** and save the decrypted file in
                //** output_file_stream
                using (FileStream input_file_stream = new
                          FileStream(encryption_folder + inFile,
                          FileMode.Open))
                {

                 input_file_stream.Seek(0, SeekOrigin.Begin);
                 input_file_stream.Seek(0, SeekOrigin.Begin);
                 input_file_stream.Read(length_of_key, 0, 3);
                 input_file_stream.Seek(4, SeekOrigin.Begin);

                 input_file_stream.Read(
                         length_initializing_vector, 0, 3);

                    //** do the conversion of
                    //** the lengths to integers
                    int key_length =
                          BitConverter.ToInt32(length_of_key, 0);
                    int initialzing_vector_length =
                          BitConverter.ToInt32(
                                length_initializing_vector, 0);

                    //** check and get the start position for
                    //** the cipher text (start_pos_cipher) and
                    //** the length of it (length_cipher)
                    int start_pos_cipher = key_length +
                                initialzing_vector_length + 8;
                    int length_cipher =
                                (int)input_file_stream.Length –
                                              start_pos_cipher;

                    //** declare byte arrays and use them for
                    //** encrypted AES key, initializing
                    //** vector, and the encrypted text
                    byte[] encryption_of_the_key = new byte[key_length];
                    byte[] initializing_vector = new
                                byte[initialzing_vector_length];

                    //** do the extraction of the keys and
                    //** initializing vector by starting from
                    //** index position 8
                    input_file_stream.Seek(8,
                             SeekOrigin.Begin);

                    input_file_stream.Read(
                             encryption_of_the_key, 0, key_length);
                    input_file_stream.Seek(8 + key_length,
                             SeekOrigin.Begin);

                    input_file_stream.Read(initializing_vector,
                                0, initialzing_vector_length);

                    Directory.CreateDirectory(decryption_folder);

                    //** we will use RSA to decrypt AES key
                    byte[] decryption_key =
                         rsaPrivateKey.Decrypt(
                               encryption_of_the_key,
                               RSAEncryptionPadding.Pkcs1);

                    //** do the decryption of the keys
                    using (ICryptoTransform transform =
                         aes_algorithm.CreateDecryptor(
                         decryption_key, initializing_vector))
                    {
                        //** do the decryption of the cipher
                        //** text from the file stream used
                        //** for encryption (input_file_stream) //** to the file stream used for
                        //** decryption (output_file_stream)
                      using (FileStream output_file_stream  new
                                FileStream(output_file,
                                FileMode.Create))
                        {
                            int count = 0;

                                 int blockSizeBytes =
                                      aes_algorithm.BlockSize / 8;
                            byte[] data = new
                                      byte[blockSizeBytes];

                            //** save memory
                            input_file_stream.Seek(
                                       start_pos_cipher,
                                       SeekOrigin.Begin);
                            using (CryptoStream
                                       outStreamDecrypted = new
                                   CryptoStream(output_file_stream,
                                       transform, CryptoStreamMode.Write))
                            {
                                do
                                {
                                    count =
                                             input_file_stream.Read(
                                             data, 0,
                                             blockSizeBytes);

                                             outStreamDecrypted.Write
                                             (data, 0, count);
                                }
                                while (count > 0);

                                             outStreamDecrypted.FlushFinal
                                             Block();
                                outStreamDecrypted.Close();
                            }
                            output_file_stream.Close();
                        }
                        input_file_stream.Close();
                    }
                }
            }
        }
    }
}

Listing 17-4Implementation of X509 Public Key Encryption as Part of Functional Encryption

img/493660_1_En_17_Fig4_HTML.jpg

图 17-4

使用 X509 证书进行加密和解密的功能加密

私人信息检索

私有信息检索是一种协议,允许客户端检索数据库的元素,而数据库的所有者不能确定选择了哪些元素。

下面,您将看到一个实施 PIR 的示例。以下示例无法编译,因为它们代表了可在现实环境中应用的框架的一般结构。它可以被视为一个应用,帮助最终用户(客户端)从云环境(服务器)与数据库进行交互。清单 17-5 中的实现代表了用参与者的密钥加密参与者的一般方法(见图 17-5 ),这些密钥需要在数据库查询过程中进行交换。使用简单的同态方法生成密钥(参见第十三章)。所提供的例子是为处理数据库记录而设计的,如伪代码 17-2 和 17-3 所示。

伪代码 17-2。实施 PIR–客户端

using System;

namespace PrivateInformationRetrieval
{
      public class PIRExample
      {
            protected PIRExample pir;
            protected PIRExampleServer srv;
            protected Object parameters;

            public PIRExample(PIRExample pir,
                              PIRExampleServer srv)
            {
                  this.pir = pir;
                  this.srv = srv;
                  this.parameters = pir.generateParameters();
            }

            //** i represents the index
            public Object get(int i)
            {
                  try
                  {
                        int size = srv.size();
                        if (i < 0 || i >= size)
                              return null;

                        int maximum_width = srv.width();
                        PIRExample.Query query =
                        pir.generateQuery(parameters,
                                            i, size, maximum_width);

                        Object the_response =
                                       srv.generateResponse(query);

                        Object result_output =
                                    pir.processResponse(parameters,
                                                        the_response,
                                                         i, size,
                                                         maximum_width);

                         return result_output;
                  }

                  catch (Exception e)
                  {
                        return null;
                  }
            }

            public int size() {
                  try
                   {
                       Console.WriteLine(srv.size().ToString());
                       return srv.size();
                  }
                  catch (Exception e)
                  {
                        return 0;
                  }
            }

            public int width() {
                  try
                   {
                       Console.WriteLine(srv.size().ToString());
                       return srv.size();
                  }
                  catch (Exception e)
                  {
                        return 0;
                  }
            }

            public PIRExample pir() {return pir;}

            public PIRExampleServer srv() {return srv;}
      }
}

伪代码 17-3。实施 PIR

using System;

namespace PrivateInformationRetrieval
{
      public interface PrivateInformationRetrieval
      {
            public class TheQuery
            {
                  public int maximum_width;
                  public Object query;

                  public TheQuery(int maximum_width, Object query)
                  {
                        this.maximum_width = maximum_width;
                        this.query = query;
                  }
            }

            public Object    generateParams();
            public TheQuery  generateQuery(Object parameters,
                                          int the_index,
                                          int the_size,
                                          int maximum_width);
            public Object generateResponse(List database,
                                                 int width,
                                                 TheQuery query);
      public BigInteger processResponse(Object parameters,
                                              Object the_response,
                                               int the_index,
                                               int the_size,
                                               int maximum_width);
      }
}

在清单 17-5 中,我们介绍了一种实用的方法,展示了如何使用同态方法为私人信息检索生成密钥。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace PrivateInformationRetrieval
{
    class Program
    {
        static BigInteger PrivateKey;
        static BigInteger[] PublicKey;
        static readonly int val1 = 5;
        static readonly int val2 = 3;
        static string At = string.Empty;
        static string Bt = string.Empty;

        static void Main(string[] args)
        {

            Random rnd = new Random();
            byte[] rand = new byte[16];

            do
            {
                rnd.NextBytes(rand);
                PrivateKey = new BigInteger(rand);
                PrivateKey = BigInteger.Abs(PrivateKey);
            }
            while (BigInteger.GreatestCommonDivisor
                                (PrivateKey, 1000000) != 1);

            PublicKey = new BigInteger[100];

            for (int i = 0; i < 100; i++)
            {
                rnd.NextBytes(rand);
                PublicKey[i] = new BigInteger(rand);
                PublicKey[i] = (BigInteger.Abs(PublicKey[i]) *
                                PrivateKey) + (1000000 *
                                rnd.Next(10, 100));
            }
            Encryption();

            Console.WriteLine("\n The multiplication is:");
            multiplication();
            Console.WriteLine("\n The addition is:");
            addition();

            Console.ReadKey();
        }

        static void Encryption()
        {
           BigInteger A;
           BigInteger.TryParse(Convert.ToString(val1), out A);
           BigInteger B;
           BigInteger.TryParse(Convert.ToString(val2), out B);

            if (!(A < 1000000 && A >= 0) || !(B < 1000000 && B
                                                                >= 0))
            {
                Console.WriteLine("This values are not
                         avaialble. The numbers and the results
                         obtained has to be situated between 0
                         and 1000000\. These values cannot be
                         accepted");

            }
            else if (!(A * B < 1000000) && !(A + B < 1000000))
            {
                Console.WriteLine("These values are not valid
                         because neither multiplication nor sum
                         can be realized due to noise
                         accumulation. The numbers and results of
                         operations must range from zero to
                         999999  \nSuch values are unacceptable.
                         It is impossible to decrypt
                         multiplication and sum due to the NOISE
                         in ciphertext. Numbers and results of
                         operations have to be from zero to
                         999999");
            }
            BigInteger t = new BigInteger(0);

            //** Encryption for A
            Random rand = new Random();
            for (int i = 0; i < 100; i++)
            {
                if (rand.Next(2) == 1)
                    t = t + PublicKey[i];
            }
            At = (A + t).ToString();

            Console.WriteLine(At);

            t = 0;
            rand = new Random();

            //Encryuption for B
            for (int i = 0; i < 100; i++)
            {
                if (rand.Next(2) == 1)
                    t = t + PublicKey[i];
            }
            Bt = (B + t).ToString();
            Console.WriteLine(Bt);
        }

        public static void multiplication()
        {
            BigInteger A;
            BigInteger.TryParse(At, out A);
            BigInteger B;
            BigInteger.TryParse(Bt, out B);

            Console.WriteLine("Multiplication of A={0} and
                   B={1} is {2}", A, B, (A * B).ToString());
        }

        public static void addition()
        {
            BigInteger A;
            BigInteger.TryParse(At, out A);
            BigInteger B;
            BigInteger.TryParse(Bt, out B);

            Console.WriteLine("Addition of A={0} and B={1} is
                                 {2}", A, B, (A + B).ToString());
        }
    }
}

Listing 17-5Practical Approach for Generating Cryptographic Keys Using a Homomorphic Method for Private Information Retrieval

img/493660_1_En_17_Fig5_HTML.jpg

图 17-5

PIR 的同态加法

结论

本章讲述了云环境中最重要的加密原语。现在,您应该理解了高级概念和加密原语,以及它们的一般实现。

本章介绍的概念将为您提供坚实的实践基础,帮助您更深入地了解其他加密原语,如多方计算、透明数据加密或属性保持加密。

云计算密码学代表着强大的挑战,大量的文献提供了多种理论框架,但没有实践方向。这为专业人员和研究人员提供了强大的研究方向和目标,即开发新的想法来提高云环境中的安全性,但云解决方案提供商提供的标准安全策略除外。

文献学

  1. Rishav Chatterjee,Sharmistha Roy 和 Ug Scholar,“云计算中的密码学:确保云中安全性的基本方法”2017.

  2. 名词(noun 的缩写)Jaber 和 Mohamad Fadli Bin Zolkipli,“密码学在云计算中的应用”,2013 年 IEEE 控制系统、计算和工程国际会议(第 179-184 页),Mindeb,2013 年,doi:10.1109/ic 欧安会。20137.888886866103

  3. 云计算中的安全问题和密码学的使用。" 2014.

  4. Melissa Chase 和 Seny Kamara,“结构化加密和受控披露”,载于密码学进展-ASIACRYPT (第 577-594 页)。10.1007/978-3-642-17373-8_33.2010.

  5. 米(meter 的缩写))Brenner、J. Wiebelitz、G. V. Voigt 和 M. Smith,“应用同态加密的云中的秘密程序执行”,载于第 5 届 IEEE 数字生态系统和技术国际会议的会议录(IEEE DEST 2011)。

十八、密码分析入门

本书的第三部分将重点介绍密码分析及其方法。如第一部分所述,密码分析被定义为研究在密码算法和安全系统中寻找漏洞的方法和途径的学科。最重要的目标是获取加密消息或密钥的真实性质。

密码分析是由专业人员(职业黑客、信息安全官员等)进行的过程。).法律框架之外的密码分析活动被称为黑客攻击,涵盖个人和非个人利益。然而,最近,术语黑客已经流行为仅仅写代码,因此有人参与黑客马拉松活动。黑客马拉松一般与安全相关的编程无关,等等。

这本书的第三部分的任务是涵盖最重要的方法和技术,用于进行一般和深入的密码分析。我们将指出必要的密码分析工具,如软件、方法、密码分析类型、密码分析算法和渗透测试平台。

执行密码分析可能是一项棘手而困难的任务。在实现密码分析过程之前,必须考虑多个方面。如果密码分析过程由一个法律实体来执行,事情就变得简单了。如果密码分析过程是由一个非法人实体实现的,那么这将是一个更复杂的过程,并且会涉及黑客手段。黑客方法将在我们后面的讨论中指出。在这两种情况下,手都会变脏。在执行密码分析过程时,要考虑到这样一个事实:由于系统的复杂性、加密密钥的大容量、硬件平台、访问权限等原因,这一过程非常耗时,并且会遇到许多障碍。

与密码学相比,密码分析更有吸引力和挑战性。密码分析人员应该具备的知识非常复杂。密码分析人员应该关注的领域可以分为三大类,信息学(计算机科学)、计算机工程、数学。让我们为每个类别指定如下重要规则:

  • 信息学(计算机科学):计算机网络、编程语言、数据库、操作系统、理论密码学

  • 计算机工程和硬件 : FPGA(现场可编程网关阵列)、编程语言(如 VHDL)、开发平台(Xilinx 等。)

  • 数学:数论、代数、组合学、信息论、概率论、统计分析、椭圆曲线数学、离散数学、微积分、格子、实分析、复分析、傅立叶分析

第三方结构

本书第三部分的结构如下:

  • 第十九章。本章将涵盖密码分析技术的分类。我们将对算法复杂性理论、统计信息分析、无扰动编码、经典密码的密码分析、分组密码的密码分析等做一个快速的概述。

** 第二十章。本章将涵盖线性和差分密码分析,这在进行密码分析时至关重要。

**   *第二十一章*[](21.html)**。本章将涵盖整数密码分析攻击,这种攻击用于建立在置换网络上的分组密码。**

**   *第二十二章*[](22.html)**。本章将讨论攻击以及它们在实践中的应用。我们将介绍一种带有指南的方法,并展示如何使用 C# 设计和实现真正的攻击。**

**   *第二十三章*[](23.html)**。本章将讨论最重要的文本特征化技术。大多数基于文本特征的方法计算字符或字符串出现的数量。我们将讨论卡方统计等算法;monogram、bigram 和 trigram 频率计数;和四元图统计作为适合度度量。**

**   *第二十四章*[](24.html)**。本章将介绍实现密码分析方法的案例研究。******* 

**## 密码分析术语

本节讨论密码分析和道德黑客中最常用的术语。表 18-1 列出了密码分析领域经常使用的关键词和术语。当我们处理密码分析攻击和道德黑客攻击时,这些术语非常重要。以下术语将为您提供该过程的清晰图像,以及谁与什么进行交互。

表 18-1

密码分析术语

|

关键词/术语

|

定义

|
| --- | --- |
| 布莱克有黑客 | 黑帽黑客就是心怀不轨,闯入计算机系统或网络的人。他们的目的是利用任何安全漏洞获取经济利益,窃取和破坏机密和私人数据,关闭系统和网站,破坏网络通信,等等。 |
| 格雷有黑客 | 灰帽黑客是一个被称为黑客的人,他利用计算机系统或软件产品的安全弱点,目的是让这些弱点引起所有者的注意。与黑帽黑客相比,灰帽黑客采取行动没有任何恶意。“灰帽子”的总体目标是提供解决方案,改善计算机系统和网络安全。 |
| 白帽黑客/道德黑客 | 白帽黑客是为政府或组织工作或受雇于政府或组织的授权人员或认证黑客,其目标是执行渗透测试并找出系统中的漏洞。 |
| 格林有黑客 | 一个灰色帽子黑客是一个业余爱好者,但不同于一个 ?? 脚本小子。他们的目的是成为一名成熟的黑客。 |
| 脚本小子 | 脚本小子是最危险的黑客形式。一个脚本小子是一个没有太多技能的人,他使用其他黑客提供的脚本或下载工具。他们的目标是攻击网络基础设施和计算机系统。他们希望给他们的社区或朋友留下深刻印象。 |
| Blue 有黑客 | 蓝帽黑客类似于脚本小子。他们是黑客领域的初学者。如果有人敢嘲笑脚本小子,蓝帽黑客就会报复。蓝帽黑客会报复那些向他们挑战的人。 |
| Red hat 黑客 | 也被称为鹰眼黑客,他们的目标是阻止黑帽黑客。操作方式不同。在处理来自黑帽黑客的恶意软件行为时,他们是无情的。红帽黑客实施的攻击非常具有攻击性。 |
| 黑客活动分子 | 也被称为在线活动家,黑客行动主义者是一群匿名黑客中的一员,他们可以未经授权访问存储在政府计算机和网络中的文件,进而影响社会或政治党派和团体。 |
| 恶意内幕人士/告密者 | 这些人可能是公司或政府机构的员工,他们知道机构内正在发生的非法行为。这可能导致个人通过勒索该机构而获利。 |
| 国家或民族支持的黑客 | 由政府安排和指派的人员,其目标是提供信息安全服务并获取不同国家的机密信息。 |

一点密码分析的历史

写一部全面的密码分析史是具有挑战性的,令人着迷的,也是困难的。在这一节中,我们将介绍影响密码分析作为一个独立领域的一些主要时刻,以及它是如何在不同的历史时期演变的。

密码分析的历史从阿拉伯哲学之父 Al-Kindi (801-873)开始。他开发了一种独特的方法,利用字母出现频率的变化。这种方法帮助他分析和开发了破解密码的不同方法(如频率分析)。Al-Kindi 的工作是基于 Al-Khalil(717-786)的工作。Al-Khalil 的作品名为密码信息之书,描述了大多数可能的阿拉伯语单词的排列和组合,有元音和没有元音。

学习密码分析和密码学历史的最好方法是将时间分成几个短的时间段,并考虑这些时间段内发生的事件。密码分析历史的检查必须在考虑到密码学的情况下进行。以下事件提供了密码分析历史的快速概述,并集中在每个时期最重要的成就。

  • 公元前 600 年 : 斯巴达人创造了《scytale》的基础。镰刀的目的是在战斗中传递秘密信息。这个“装置”由两个部分组成,一条皮带和一根木杆。要解密信息,木杆必须有特定的大小。该大小必须与用于消息加密阶段的大小相同。如果接收者或恶意者没有相同的木杆大小,消息就无法解密。

  • 公元前 60 年 : 朱利叶斯·凯撒设计了第一个替代密码,它使用三点字符的移位技术来加密信息,这样 A 就是 D,B 就是 E,以此类推。这个密码的实现可以在章节 1 中看到。

  • 1474:Cicco Simonetta制作了一本拉丁和意大利文本的解密手册。

  • 1523 : 布莱斯·德·维根内尔创造了一种加密密码的基础,被称为维根内尔密码

  • 1553 : 吉奥万·巴蒂斯塔·贝拉索根据一个加密密钥设计出第一个密码。加密密钥基于发送方和接收方共同同意的一个字。

  • 1854 年 : 查尔斯·惠斯通创造了公平密码。该密码加密一组特定的字母,而不是一个字母一个字母地加密。这给密码带来了更高的复杂性,因此更难破解。

  • 1917 : 爱德华·赫本制造出第一台基于转子的机电机器。转子用于加密操作。加密密钥存储在旋转盘中。它是基于用于替换的表而设计的。该表随着键入的每个字符而被修改。

  • 1918 年 : 亚瑟·谢尔比乌斯发明了恩尼格玛机。第一个模型和原型是为商业目的而设计的。英格玛机器使用几个转子而不是一个,就像爱德华·赫本的机电机器一样。他的发明立即被德国军事情报部门采用,用于对他们的传输进行编码。

  • 1932 年 : 马里安·雷耶夫斯基发现了恩尼格玛机是如何工作的。1939 年,法国和英国情报部门使用波兰提供的信息,帮助艾伦·图灵等密码学家破解密钥,密钥每天都在变化。这对于二战中盟军的胜利至关重要。

  • 1945 年 : Claude E. Shannon 凭借其名为的著作《密码学的数学理论》革新了密码学和密码分析。这是一个里程碑,标志着经典加密技术的终结和现代加密技术的开始。

  • 1970 年底:IBM 的科学家设计出第一个分组密码。目标是保护客户的数据。

  • 1973 年:分组密码被美国采用,并被设定为国家标准,称为 DES(数据加密标准)。

  • 1975 年:公钥密码学问世。

  • 1976 年:介绍了密钥交换协议 Diffie-Hellamn。

  • 1982 :量子计算机的理论模型由理查德·费曼提出。此时此刻,我们即将拥有广泛可用的基于云的量子计算机。这些方面和进步将对今天和未来的计算的许多方面产生深远的影响。

  • 1997 : DES 破解成功。

  • 1994 : 彼得·肖介绍了一种可以在量子计算机中用于整数因式分解的算法。

  • 1998 :量子计算介绍。

  • 2000 年:正式用 AES(高级加密标准)取代 DES。AES 被宣布为公开竞赛的获胜者。

这个列表可以改进,它包含了历史上作为一个概念、模型和框架对密码分析有贡献的主要事件。

渗透工具和框架

本节涵盖了渗透测试过程中成功使用的几个渗透工具和框架。

我们将工具分为两类, Linux 黑客发行版渗透工具/框架。

  • Linux 黑客发行版

    • Kali Linux 代表了最先进的渗透测试平台之一。它可以安装在不同的设备上,可以用来攻击多种类型的设备。

    • BackBox 是一个非常强大的渗透测试发行版,包括强大的安全评估。

    • Parrot 安全 OS 是新发行的。它的设计和实现非常专业。它的目标是提供在线服务和其他类型服务的云环境。

    • BlackArch 是一个非常有趣的渗透测试平台和安全研究。它构建在 Arch Linux 之上。与 Arch Linux 一样,由于其复杂性和不同的安装说明,用户需要注意安装过程。

    • Bugtraq 是一个功能强大且易于使用的平台,包含一套重要的取证和渗透工具。

    • DEFT Linux ( D 数字 E 证据&ForensicsT工具套件)是一个计算机取证的平台。有可能将它作为一个活动系统运行。

    • Samurai Web 测试框架是一个强大的工具集合。它可用于网络渗透测试。它是一个虚拟机文件,可以安装在 VirtualBox 和 VMWare 中。

    • Pentoo Linux 建立在 Gentoo 之上。发行版的目标是提供快速的安全性和渗透测试分析。它以实时分发的形式提供。

    • CAINE(CcomputerAidedIninvestigativeEn environment)是一套复杂的工具,其中包含用于系统取证模块和分析的专业框架和模块。

    • 网络安全工具包是一个流行的工具和发行版。它很容易安装,可以作为 Fedora 上的一个实时 ISO 构建来完成。它有一套强大的开源网络安全工具。它有一个专业的 web 用户界面,提供有关网络和系统管理、网络监控工具和分析的重要细节。

    • Fedora Security Spin 是一个专业发行版,可用于安全审计和渗透测试。从工业界到学术界,各种专业人士都在使用它。

    • ArchStrike 是一个基于 Arch Linux 的测试发行版,可供安全领域的专业人士使用。

    • Cyborg Hawk 拥有 750 多种用于安全专业人员和执行渗透测试的工具。

    • Matriux 还是挺有前途的。它可以用于渗透测试、道德黑客、法医调查、漏洞分析等等。

    • Weakerth4n 在黑客或密码分析领域并不知名。它提供了一种独特的渗透测试方法,并且是使用 Debian (Squeeze)构建的。

  • 渗透工具/框架 (Windows 和 Linux 平台)

    • Wireshark 是一个非常著名的数据包嗅探器。它为网络包流量分析和通信协议分析提供了一套强大的工具。

    • Metasploit 是 pentesting 中使用的最重要的框架之一。

    • Nmap ( N 网络 M apper)是一款面向安全专业人士的专业网络发现和安全审计工具。该工具的目标是利用配置的目标。对于每个扫描的端口,我们可以看到安装了什么操作系统、什么服务正在运行、防火墙的详细信息等。

结论

这一章从总体上讨论了密码分析,涵盖了实用密码分析的基础、工具和工作方法。您现在应该具备以下知识:

  • 密码分析的任务和目标

  • 对历史上的事件以及有多少密码和算法影响了密码分析学科的快速概述

  • 主要术语的定义以及对不同类型黑客之间主要差异的清晰理解

  • 黑客和测试平台发行版的背景

  • 根据用户喜好的操作系统平台,理解独立使用的最重要的框架和渗透工具

文献学

  1. F.科恩,《密码学简史》,1990 年。新世界百科,2007 年。检索于 2009 年 5 月 4 日,来自 www.all.net/books/ip/Chap2-1.html

  2. 密码学。检索于 2009 年 5 月 4 日,来自 www.newworldencyclopedia.org/entry/Cryptography

  3. 米(meter 的缩写))Pawlan,“密码术:秘密信息的古代艺术”1998.检索于 2009 年 5 月 4 日,来自 www.pawlan.com/Monica/crypto/

  4. J.Rubin,Vigenere 密码,2008 年。检索于 2009 年 5 月 4 日,来自 www.juliantrubin.com/encyclopedia/mathematics/vigenere_cipher.html

  5. K.数论 1,2002。检索于 2009 年 5 月 4 日,来自 http://math.usask.ca/encryption/lessons/lesson00/page1.html

  6. 米(meter 的缩写))惠特曼和 H. Mattord,信息安全原则。凤凰城大学定制版电子文本。加拿大,汤姆森学习公司,2005 年。检索于 2009 年 5 月 4 日,来自 CMGT 凤凰城大学资源/432。

  7. 电码书。密码与密码破解秘史。西蒙·辛格,1999 年

  8. A.Ibraham,“Al-Kindi:密码学的起源:阿拉伯的贡献”,《密码学》,第 16 卷,第 2 期(第 97-126 页)。一九九二年四月。 www.history.mcs.st-andrews.ac.uk/history/Mathematicians/Al-Kindi.html

  9. Abu Yusuf yaqub ibn ishaq al-sabbah al-kindi, www.trincoll.edu/depts/phil/philo/phils/muslim/kindi.html

  10. 哲学家:亚库卜·伊本·伊扎克·肯尼迪日、k·金迪、阿布·优素福·亚库卜·伊本·伊沙克(草 866-73)。 www.muslimphilosophy.com/ip/kin.html

  11. Ahmad Fouad Al-Ehwany,穆斯林哲学史第 1 卷中的“Al-Kindi”(第 421-434 页)。新德里:低价出版物。1961.

  12. Ismail R. Al-Faruqi 和 Lois Lamya al-Faruqi,伊斯兰文化地图集,第 305-306 页。纽约:麦克米伦出版公司。1986.

  13. 《大英百科全书》(第 352 页)。芝加哥:威廉·本顿。1969.

  14. 奥康纳和罗伯逊、阿布·优素福·雅各布·伊本·伊斯哈格·萨巴·金迪。1999.**

十九、密码分析攻击和技术

这一章致力于最重要和最有用的密码分析和密码分析标准、验证方法、分类和密码分析攻击的操作。密码分析学科非常复杂,关于它的写作需要数千个研究小时。以下部分包含对您在日常活动中使用至关重要的最重要元素的调查。

标准

标准的重要性至关重要,任何专业人员在进行密码分析攻击时都应该知道。大多数密码分析攻击仅仅是为了商业目的。政府使用和商业目的之外的任何密码分析活动都是不合法的,并且不应该为了个人利益而进行。

组织非常容易受到安全攻击,确保组织满足有关其数据安全性的必要要求非常重要。任何组织都可以雇佣安全专家来执行密码分析攻击,以便测试其安全性并找到以后可能被恶意用户/攻击者利用的漏洞。

这些标准是由机构和组织提供的。它们包含密码学和密码分析方法、框架和算法。这些机构和组织是

  • IEFT 公钥基础设施(X.509) :该组织致力于互联网上使用的基于公钥系统的协议的标准化。

  • 国家标准与技术研究所(NIST) :该研究所专注于为美国政府制定 FIPS 标准。

  • 美国国家标准协会(ANSI) :其目标是维护来自私营部门的标准。

  • 互联网工程任务组(IEFT) :它代表了一个由网络、运营商、服务商和研究人员组成的国际社区。他们的主要目的是通过互联网架构的发展来体现的。

  • 电气和电子工程研究所(IEEE) :其目标是设计不同领域的理论和先进技术,如电子学、计算机科学和信息学。

  • 国际标准化组织(ISO) :它是一个非政府组织,包括 100 多个国家。其主要目标是鼓励标准化的发展,以帮助专业人士促进国际服务交流。

FIPS 140 比 2,FIPS 140 比 3,ISO 15408

1ISO 15408 是 IT 安全评估中最重要的标准之一,在国际上被用作参考系统标准。该标准包括 IT 领域的一系列规则和要求。目标是验证产品和加密系统的安全性。

*FIPS 140-2 和 140-3 提供了需要遵守的指南,以完成一组具体的技术要求,这些技术要求分为四个级别。

在为特定应用或加密模块开发规范或标准时,您需要考虑 FIPS 140-2/FIPS 140-3 和 ISO 15408 这两个标准。

基于标准开发的产品需要进行测试。测试的目标是获得验证,并确认标准得到了正确的遵循和尊重。

密码系统的验证

如果业务需要在软件和通信系统中实施密码分析和密码操作,则需要密码和密码分析服务。这些服务由认证机构授权,它们包括诸如数字签名生成和验证、加密和解密、密钥生成、密钥分发、密钥交换等功能。

图 19-1 中的模型是基于密码和密码分析模块进行安全性测试的通用模型。

表 19-1

对加密算法的攻击

|

对加密算法的攻击类型

|
| --- |
|

攻击标题

|

攻击描述

|
| --- | --- |
| 已知明文攻击 | 密码分析者有一个加密的文本和对应的明文。这种攻击的目的是让密码分析人员从数据中分离出加密密钥。 |
| 选择文本攻击 | 密码分析者可以选择指出要加密的明文。通过使用这种类型的攻击,密码分析者可能试图从加密密钥中分离出文本信息,并有可能通过不同的方法访问加密算法或密钥。 |
| 密码-密文攻击 | 密码分析者保存相同的文本,该文本用明文及其对应的两个或更多不同的密钥加密。 |
| 分割和封锁攻击 | 密码分析者具有实现不同算法输入和输出之间的一组关联的能力,目的是分离不同的算法输入,这让他将问题分成两个或更多容易解决的问题。 |
| 线性综合征发作 | 密码分析方法包括设计和创建特定于伪随机发生器的线性方程组,并用加密文本验证该方程组,从而获得高概率明文。 |
| 一致性线性攻击 | 密码分析方法包括从模拟密钥开始创建特定于伪随机发生器的线性方程方案,并通过概率为 1 的伪随机发生器检查该方案,从而获得高概率明文。 |
| 随机攻击 | 这种攻击被称为预测攻击,如果生成器的输出是自相关的,并且密码分析者设法获得伪随机生成器的输出和加密文本作为输入数据,则这种攻击是可能的。这样就获得了明文。 |
| 信息线性攻击 | 也称为线性复杂度攻击,如果有任何机会使生成器与斐波那契算法相等,并且如果线性复杂度与低生成器相等,则攻击是可能的。利用这种类型的攻击,可以构造类似的算法和类似的密钥。 |
| 病毒攻击 | 如果应用加密,这种攻击是可能的算法并在易受攻击且未受保护的 PC 上运行。 |

img/493660_1_En_19_Fig1_HTML.png

图 19-1

验证和测试框架

对于充分的测试和验证过程,至少需要密码模块密码/密码分析算法 对于开发的任何加密产品(或桌面/网络软件应用),有必要执行测试并将产品提交给 CMVP 2 (加密模块验证程序),以便根据 FIPS 140-2 3 和 FIPS 140-3 进行测试。 4

一个密码模块被表示为专用软件和硬件进程的组合。利用经验证的加密和密码分析模块的最重要的优势是

  • 模块应满足最低要求。

  • 应在标准范围内通知和指导授权人员和技术人员。该标准得到了普遍认可,并且经过了测试。

  • 在开发加密模块之前,验证最终用户是否知道加密模块已经根据安全要求进行了验证和测试。

  • 为了开发类似和特定的应用,需要实现高水平的安全性可靠性。

特征 FIPS 140-2 的安全要求具有 11 个可在加密模块的设计和实现过程中使用的度量和标准。加密模块必须满足并验证每个指标。在验证过程中,从 1 到 4 的标记被分配给加密模块,与所保证的安全级别成比例。

加密模块通过验证后,应包含一组信息,如制造商名称、地址、模块名称、模块版本、模块类型(软件或硬件)、验证日期、验证级别和模块描述。

密码分析操作

设计加密系统时,应遵循以下原则。这些原则很简单,应该构成任何密码分析程序的基础:

  • 不应该低估对手。

  • 密码系统可以由密码分析人员来评估。

  • 在评估密码系统之前,对手对被评估的密码系统的了解被考虑在内。

  • 密码系统的保密性依赖于密钥。

  • 在加密系统评估过程中,必须考虑系统中的所有元素,如密钥分发、加密内容等。

Claude Shannon 认为,在进行密码系统评估时,必须考虑以下标准:

  • 一旦消息被成功解密,密码分析者将获得一份奖品。

  • 密钥长度和复杂性

  • 加密-解密过程的复杂程度

  • 根据文档大小的编码文本的大小

  • 传播错误的方式

每个密码都有一个解决方案的基本操作如下:

  • 找到并决定使用的词汇

  • 决定加密系统

  • 加密系统的单个密钥的重建或加密流系统的部分或完全重建

  • 这种方案的重构或确定完全明文

密码分析攻击的分类

本节讨论针对密码算法、密钥、认证协议、系统本身的攻击类型,以及硬件攻击。

对密码算法的攻击

表 19-2 列出了对加密算法最常见的攻击。

表 19-2

对密钥的攻击

|

对密钥的攻击类型

|
| --- |
|

攻击标题

|

攻击描述

|
| --- | --- |
| 暴力攻击 | 这种攻击需要对密钥和密码进行严格的检查,如果加密密钥的大小和加密密钥的空间很小,这种攻击就有可能发生。 |
| 智能暴力攻击 | 加密密钥的随机性程度小(熵小),这使得密码可以被识别,因为它接近于所使用的语言术语。 |
| 回溯攻击 | 这种攻击是基于实现一种回溯方法,这种方法包括在期望的方向上继续搜索的条件的存在。 |
| 贪婪攻击 | 攻击提供了最佳本地密钥(最佳全局密钥不能相同)。 |
| 字典攻击 | 这种攻击包括搜索密码或密钥,并使用字典来执行。 |
| 混合字典攻击 | 这种攻击是通过改变字典中的术语并借助字典中的单词通过暴力初始化攻击来实现的。 |
| 病毒攻击 | 如果密钥存储在未受保护的 PC 上,这种攻击是可能的。 |
| 密码哈希攻击/密钥 | 如果密码哈希设计不当或不正确,就会发生这种攻击。 |
| 替代攻击 | 原始密钥由第三方替换,并在网络中重复。这可以通过病毒来实现。 |
| 存储加密密钥 | 如果这是在没有任何物理保护措施或密码、软件或硬件的情况下以错误的方式(与加密数据一起)在明文中完成的,这可能导致对加密消息的攻击。 |
| 旧加密密钥的存储 | 该攻击将导致旧加密文档的受损版本。 |
| 关键妥协 | 如果对称密钥被泄露,那么只有分配给该键的文档将会受到威胁。如果可以发现存储在各种服务器上的公钥被泄露,那么攻击者就可以替换数据的合法所有者,从而在网络内造成不良的负面影响。 |
| 万能钥匙 | 密码系统代表不同的阶段。 |
| 密钥寿命 | 它是一个不可或缺的组成部分,排除了尚未检测到的成功攻击的可能性。 |

对密钥的攻击

表 19-2 列出了对密钥最常见的攻击。

对认证协议的攻击

认证协议受到各种形式的攻击。我们在表 19-3 中列出了最相关的,常用的。考虑到网络认证协议非常必要且至关重要,这一点非常重要。一旦遭到破坏,就有可能暴露至关重要的信息,攻击者可以获得大量信息。

表 19-3

对认证协议的攻击

|

对认证协议的攻击类型

|
| --- |
|

攻击标题

|

攻击描述

|
| --- | --- |
| 对公钥的攻击 | 协议内的攻击是为了签名而发生的。它仅适用于公钥方案。 |
| 对称算法的攻击 | 在认证协议内部,攻击必须发生在签名上。这只能通过使用对称密钥来实现。 |
| 被动攻击 | 入侵者可以拦截并跟踪接触在没有任何干扰的情况下。 |
| 使用第三人称攻击 | 两个伙伴在通信信道内的通信被第三方用户故意截取。 |
| 失败停止签名 | 这是一种加密协议,发送者可以提供他的签名是否是伪造的证据。 |

结论

这一章涵盖了适用于密码分析的最重要和最有用的指南和方法。要检查和验证加密和密码分析算法和方法的实现,您必须能够处理标准。你了解到了

  • 密码分析攻击的分类

  • 密码分析过程中涉及的操作

  • FIPS 140-2 和 FIPS 140-3 标准

  • ISO 15408 标准

  • 加密系统验证过程

文献学

  1. Adrian Atanasiu, 密码学中的数学 。美国出版社,编辑:科学宇宙,ISBN:978-973-1944-48-7。2015.

  2. Adrian Atanasiu, Information Security vol 1Cryptography) , InfoData 出版社, ISBN: 978-973-1803-18-0.2007.

  3. Adrian Atanasiu, Information Security vol 2 (Security Protocols) , InfoData 出版社, ISBN: 978-973-1803-18-0.2009.

  4. S.J. Knapskog,“安全通信协议的形式规范和验证”(第 58–73 页)。 https://link.springer.com/chapter/10.1007/BFb0030352

  5. K.Koyama,“破解公钥密码系统能力的直接演示”(第 14-21 页)。 www.iacr.org/cryptodb/data/paper.php?pubkey=279

  6. P.J. Lee ,“公共网络的安全用户访问控制”(第 46–57 页)。 www.sciencedirect.com/science/article/pii/S0140366416300652

  7. R.Lidl 和 W.B. Muller,“关于强斐波那契伪素数的注记”(第 311–317 页)。 https://link.springer.com/article/10.1007/BF01810848

  8. 阿尔弗雷德·j·梅内塞斯和斯科特·范斯通,“椭圆曲线密码系统的实现”(第 2-13 页)。 https://link.springer.com/article/10.1007/BF00203817

  9. M.J. Mihaljevi 和 J.D. Goli,“给定噪声输出序列时移位寄存器初始状态重建的快速迭代算法”(第 165–175 页)。密码学进展—AUSCRYPT '90,计算机科学讲义,第 453 卷,施普林格出版社,柏林,1990 年。

*

二十、线性和差分密码分析

在本章中,我们将介绍两种重要的密码分析类型,线性差分密码分析,介绍关于这两种密码分析如何进行和实现的基本和高级概念和技术。

关于线性和差分密码分析的研究文献在理论方法和机制方面是广泛和丰富的,但是只有少数方法可以在现实环境中实际应用。理论密码分析和应用密码分析之间的差异是巨大的,发表的许多观点(算法、方法、博弈论方面等。)在过去的 12 年里将研究人员和专业人士引入了歧途。对密码分析进行研究并增加其在实践和不同场景中应用的潜在价值需要时间、经验以及理论家和实践者之间的持续交叉合作,而这两种类型之间没有任何隔离。

它们的重要性在密码分析领域至关重要,为构造分组密码和流密码的密码分析攻击方案提供了必要的工具和机制。

差分密码分析

差分密码分析是由 E. Biham 和 A. Shamir 在 20 世纪 90 年代早期提出的。差分密码分析的目标是测试密钥中的某些位置是否比密码中的其他位置更容易被追踪到。这个测试可以在任何一级风险下进行。实际上,这个测试是一个更复杂的测试过程的二阶近似。

通过差分密码分析,我们可以暴露密码算法的弱点。下面的差分密码分析的例子是针对流密码算法 来说明的。

该算法的伪代码如下:

  1. α←读取拒绝率

  2. Build n sets of perturbed keys starting from the key K.

    $$ for\ i=1\  to\ n\  do\ {K}^{(i)}=\left({\delta}_{1i}\oplus {k}_1,\dots, {\delta}_{ni}\oplus {k}_n\right): $$

INPUT:    A base key is chosen as K = (k1, …, kn) with ki ∈ {0, 1}
OUTPUT:   The sensitive and weak points of the cryptography
          algorithm and the resistance decision for
          differential cryptanalysis

$$ {\delta}_{1i}=\left{\begin{array}{c}1,\kern0.5em if\ j\ne i,\ {}0,\kern0.5em if\ j=i.\end{array}\right., $$

对于 I,j=1,…,n,这样,第 I 个密钥是通过改变第 I 个的位从基密钥获得的。

  1. 建筑密码。我们将从基本密钥、扰动密钥和明文 M 开始构建 n + 1 个密码。我们将用C(I), i = 1,…, n + 1 来标注这个密文。作为明文,我们可以在任何地方选择文本 0。

  2. Building the correlation matrix. In this step we will build the matrix (n + 1) × (n + 1) for the corellation values C:

    $$ {c}_{ij}= corelation\left( cryptogram\ i, cryptogram\ j\right), $$

其中 correlationcij表示应用于序列的统计测试的值(密文 i密文 j )。矩阵 C 被表示为在主对角线上具有 1 的对称矩阵。

  1. The computation of significant value. It will count the values of significant correlation which are situated above the main diagonal. A value is called significant if

    $$ {c}_{i,j}\notin \left[{u}_{\frac{\alpha }{2}};{u}_{1-\frac{\alpha }{2}}\right]. $$

考虑 T 代表共循环测试中剔除数量的有效值的数量。

  1. Decision and result interpretation. If

    $$ \frac{T-\alpha \cdotp \frac{n\left(n+1\right)}{2}\ }{\sqrt{\alpha \left(1-\alpha \right)\cdotp \frac{n\left(n+1\right)}{2}}}\notin \left[{u}_{\frac{\alpha }{2}};{u}_{1-\frac{\alpha }{2}}\right], $$

一旦计算出来,我们就可以判定对差分密码分析的不抵抗性($$ {u}_{\frac{\alpha }{2}} $$$$ {u}_{1-\frac{\alpha }{2}} $$分别代表$$ \frac{\alpha }{2} $$$$ 1-\frac{\alpha }{2} $$阶正态分布的分位数,并将 ij 元素固定为nI>j≥1,其中cij有意义)。这些元素代表了算法的弱点。否则,我们将无法提及任何关于抵抗这类攻击的东西。

清单 20-1 中的 C# 源代码是上述伪代码的实现(输出见图 20-1 )。

img/493660_1_En_20_Fig1_HTML.jpg

图 20-1

差异密码分析示例

using System;

namespace DifferentialCryptanalysis
{
    class ExampleOfDifferentialCryptanalysis
    {
        //** variables
        public static int[] Known_P0 = new int[10000];
        public static int[] Known_P1 = new int[10000];
        public static int[] Known_C0 = new int[10000];
        public static int[] Known_C1 = new int[10000];
        public static int Good_P0, Good_P1, Good_C0, Good_C1;
        public static int numbers_pairs;
        public static int[] characters_data0 = new int[16];
        public static int characters_data_max = 0;
        public static int[,] characters = new int[32, 32];

        public static int[] theSBOX = new int[16] { 3, 14, 1,
                10, 4, 9, 5, 6, 8, 11, 15, 2, 13, 12, 0, 7 };
        public static int[] sbox_reviwed = { 14, 2, 11, 0, 4,
                6, 7, 15, 8, 5, 3, 9, 13, 12, 1, 10 };

        public static int round_function(int theInputData, int theKey)
        {
            return theSBOX[theKey ^ theInputData];
        }

        public static int encryption(int theInputData, int k_0, int k_1)
        {
            int x_0 = round_function(theInputData, k_0);
            return x_0 ^ k_1;
        }

        public static void find_differences()
        {
            Console.WriteLine("\nGenerating a differential
                                table structure for XOR:\n");

            Random rnd = new Random();

            int x, y;

            for (x = 0; x < 16; x++)
            {
                for (y = 0; y < 16; y++)
                {
                    characters[x ^ y, theSBOX[x] ^ theSBOX[y]]
                                                    = rnd.Next(-1, 1);
                }
            }

            for (x = 0; x < 16; x++)
            {
                for (y = 0; y < 16; y++)
                {
                    characters[x^y, theSBOX[x] ^
                                                theSBOX[y]]++;
                }
            }

            for (x = 0; x < 16; x++)
            {
                for (y = 0; y < 16; y++)
                    Console.Write("{0}",
                                            characters[x, y] + " ");
                Console.WriteLine("\n");
            }

            Console.WriteLine("\nShow the possible differentials:\n”);

            for (x = 0; x < 16; x++)
                for (y = 0; y < 16; y++)
                    if (characters[x, y] == 6)
                        Console.WriteLine("\t\t6/16: {0} to
                                                    {1}\n", x, y);
        }

        public static void genCharData(int input_differences,
                                       int output_differences)
        {
            Console.WriteLine("\nValues represented as
                        possible intermediate based on
                        differntial has been generated: ({0} to
                        {1}):\n", input_differences,
                        output_differences);

            characters_data_max = 0;
            int p;

            for (p = 0; p < 16; p++)
            {
                int theComputation = p ^ input_differences;

                if ((theSBOX[p] ^ theSBOX[theComputation]) ==
                                                    output_differences)
                {
                    Console.WriteLine("\t\tThe certain values
                    choosen are:   {0} + {1} to  {2} +
                    {3}\n", p, theComputation, theSBOX[p],
                    theSBOX[theComputation]);

                    characters_data0[characters_data_max] = p;
                    characters_data_max++;
                }
            }
        }

        public static void genPairs(int input_differences)
        {
            Random randomNumber = new Random();

            Console.WriteLine("\nGenerating {0} known pairs
                          with input differential of {1}.\n",
                          numbers_pairs, input_differences);

            //** generate randomly subkey
            int Real_K0 = randomNumber.Next() % 16;

            //** generate randomly subkey
            int Real_K1 = randomNumber.Next() % 16;

            Console.WriteLine("\t\tThe K0 Real Value is =
                                                    {0}\n", Real_K0);
            Console.WriteLine("\t\tThe K1 Real Value is =
                                                    {0}\n", Real_K1);

            int b;

            //** Generate plaintexts pairs using different
            //** XORs based on the differences
            //** that are provided as input
            for (b = 0; b < numbers_pairs; b++)
            {
                Known_P0[b] = randomNumber.Next() % 16;
                Known_P1[b] = Known_P0[b] ^ input_differences;
                Known_C0[b] = encryption(Known_P0[b], Real_K0,
                                                      Real_K1);
                Known_C1[b] = encryption(Known_P1[b], Real_K0,
                                                      Real_K1);
            }
        }

        public static void findGoodPair(int
                                             output_differences)
        {
            Console.WriteLine("\nSearching for good pair:\n");

            int c;
            for (c = 0; c < numbers_pairs; c++)
                if ((Known_C0[c] ^ Known_C1[c]) ==
                                                    output_differences)
                {
                    Good_C0 = Known_C0[c];
                    Good_C1 = Known_C1[c];
                    Good_P0 = Known_P0[c];
                    Good_P1 = Known_P1[c];
                    Console.WriteLine("\t\tA good pair has
                                been found: (P0 = {0}, P1 = {1}) to
                                (C0 = {2}, C1 = {3})\n", Good_P0,
                                Good_P1, Good_C0, Good_C1);
                    return;
                }
            Console.WriteLine("There is no pair proper
                                                        found!\n");
        }

        public static int testKey(int Test_Key_0,
                                                  int Test_Key_1)
        {
            int c;
            int someCrappyValue = 0;
            for (c = 0; c < numbers_pairs; c++)
            {
                if ((encryption(Known_P0[c], Test_Key_0,
                          Test_Key_1) != Known_C0[c]) ||
                          (encryption(Known_P1[c], Test_Key_0,
                          Test_Key_1) != Known_C1[c]))
                {
                    someCrappyValue = 1;
                    break;
                }
            }

            if (someCrappyValue == 0)
                return 1;
            else
                return 0;
        }

        public static void crack()
        {
            Console.WriteLine("\nUsing brute force to reduce
                                                    the keyspace:\n");

            for (int g = 0; g < characters_data_max; g++)
            {
                int Test_K0 = characters_data0[g] ^ Good_P0;

                int Test_K1 =
                          theSBOX[characters_data0[g]] ^ Good_C0;

                if (testKey(Test_K0, Test_K1) == 1)
                    Console.WriteLine("\t\tThe Key is! ({0},
                                      {1})\n", Test_K0, Test_K1);
                else
                    Console.WriteLine("\t\t({0}, {1})\n",
                                             Test_K0, Test_K1);
            }
        }

        static void Main(String[] args)
        {
            Console.WriteLine("DIFFERENTIAL CRYPTANALYSIS\n");
            Random randomPerRunning = new Random();
            //** generating random values per each running
            randomPerRunning.Next();

            //** identify proper differentials
            //** within the SBoxes
            find_differences();

            //** defining a numerical
            //** value for known pairs
            numbers_pairs = 8;

            //** identify data inputs that will help
            //** to lead us to specific characteristic
            genCharData(4, 7);

            //** randomly, generate pairs of
            //** chosen-plaintext
            genPairs(4);

            //** based and using the characteristic,
            //** we will choose a known pair
            findGoodPair(7);

            //** use characteristic_data0 and within
            //** the proper pair we will find it
            crack();

            while (true) { }
            Console.ReadKey();
        }
    }
}

Listing 20-1Implementation of Differential Cryptanalysis Example

线性密码分析

线性密码分析作为 DES(数据加密系统)的理论框架于 1993 年被设计和引入。目前,线性密码分析被用于分组密码中,并且代表了设计和实施复杂攻击的一个非常好的起点。

我们可以将线性密码分析定义为在密钥、明文结构和密文结构之间建立的线性关系。明文的结构用字符或位来表示。有必要将其表示为一个以异或为特征的操作链,显示为

$$ {A}_{i_1}\oplus {A}_{i_2}\dots \bigoplus {A}_{i_u}\bigoplus {B}_{j_1}\bigoplus {B}_{j_2}\bigoplus \dots \bigoplus {B}_{j_v}={Key}_{k_1}\bigoplus {Key}_{k_2}\bigoplus \dots {Key}_{k_w} $$

其中⨁表示异或运算(这是一种二进制运算), A i 表示从 i th 位置开始输入的位结构a=a1A 2 ,…】, B j 代表输出结构B=B1B 2 、…】和 k

进行线性密码分析

在大多数情况下,我们从这样的想法出发,即除了私钥之外,我们知道加密算法。在分组密码上启动线性密码分析被表示为模型/框架,描述为

  • 识别特征加密算法的非线性分量的线性近似(例如,S 盒)。

  • 执行替换盒的线性近似之间的组合,其包括在加密算法内执行的操作。对于专业人员来说,关注线性近似是非常重要的,因为它代表了一个特殊的函数,该函数包含并处理明文和密文比特以及来自私钥的比特。

  • 设计线性近似作为第一次使用的键的指南。这将非常有帮助,因为它为所有可能的键值节省了重要的计算资源。使用多重线性近似对于消除尝试所必需的关键数字是非常有用的。

在下一节中,我们将提供一些细节,以帮助确定在进行线性密码分析攻击时应该考虑哪些组件。如果不在理论层面理解以下概念,将很难实施任何攻击。

s 盒

通过 S 盒,非线性与它的操作、异或和位移一起被引入,它们也在线性表示中被发现。

S-Box 的目标是在具有特定输出的输入二进制序列之间创建一个映射。也就是说,我们将提供非线性,这将建立和呈现应用线性密码分析时获得的仿射近似。表 20-1 显示了一个 S-Box 的例子以及映射是如何工作的。它使用第 1 个和第 4 个位来查找列和中间位,第 3 个和第 4 个*。基于此,以输入 1110 将是 0101 的方式确定行。*****

**表 20-1

S-Box 的例子

|   |

11

|

10

|

01

|

00

|
| --- | --- | --- | --- | --- |
| 00 | "0000" | "0001" | "0010" | "0011" |
| 01 | "1000" | "1001" | "1111" | "1011" |
| 10 | "1100" | "1101" | "1110" | "1010" |
| 11 | "0100" | "0101" | "0010" | "0111" |

表 20-2 显示了作为输入的位和作为输出的位的例子之间的映射操作。

表 20-2

输入和输出之间的映射

|

The Input (J)

|

The Output (Q)

|
| --- | --- |
| "0000" | "0011" |
| "0001" | "0010" |
| "0010" | "1011" |
| "0011" | "1111" |
| "0100" | "1010" |
| "0101" | "1110" |
| "0110" | "0111" |
| "0111" | "0010" |
| "1000" | "0001" |
| "1001" | "0000" |
| "1010" | "1001" |
| "1011" | "1000" |
| "1100" | "1101" |
| "1101" | "1100" |
| "1110" | "0101" |
| "1111" | "0100" |

S-Box 线性近似

我们将从我们想要近似上面提出的替换框的结构的想法开始。基于这些信息,我们得到了各种线性近似的精度,这是相当高的。我们包括具有以下形式的 256 个这样的线性近似:

$$ {d}_1{J}_1\bigoplus {d}_2{I}_2\bigoplus {d}_3{J}_3\bigoplus {a}_4{I}_4={g}_1{Q}_1\bigoplus {g}_2{Q}_2\bigoplus {g}_3{Q}_3\bigoplus {g}_4{Q}_4 $$

其中 J 1Q i 代表第Ith位特征为输入( J )和输出( Q )对应于 d ig 举个例子,让我们用下面的线性近似法j2=q1q4并由 d = 0100 2g = 1001 2 给出。

线性逼近的串联

是时候进入下一步,为整个系统形成、设计和规划线性近似。为了实现这一点,我们需要两样东西:

  • 首先,我们需要已经计算出构成系统的每个组分的线性近似。

  • 第二,为了进行组合,我们简单地通过在不同的组合中使用异或来对整个方程组求和。这样,我们得到一个方程,它将消除中间变量。

组合两个变量

让我们考虑一下 B 1B 2 ,两个随机二元变量。它们之间的线性关系是b1⨁b2= 0。接下来,我们用 l 表示概率B1= 0,用 m 表示概率B2= 0。基于这两个随机自变量,我们有

$$ P\left({B}_1=a,{B}_2=b\right)=\left{\begin{array}{c}\begin{array}{c}l\cdotp m\kern7em for\ a=0,b=0\ {}l\cdotp \left(1-m\right)\kern4.25em for\ a=0,b=1\end{array}\ {}\left(1-l\right)\cdotp q\kern4.75em for\ a=1,b=0\ {}\left(1-l\right)\cdotp \left(1-m\right)\kern1.5em for\ a=1,b=1\end{array}\right. $$

继续向前,我们可以展示如下:

$$ P\left({B}_1\bigoplus {B}_2=0\right)= $$

$$ =P\left({B}_1={B}_2\right) $$

$$ =P\left({B}_1=0,{B}_2=0\right)+P\left({B}_1=1,{B}_2=1\right) $$

$$ =l\cdotp m+\left(1-l\right)\left(1-m\right) $$

下一步通过计算b1⨁b2= 0 的偏差来表示。给出为ζ1ζ2

现在是用 C# 实现线性密码分析(见图 20-2 )的时候了(见清单 20-2 ),并展示如何在实践中使用上述概念。

img/493660_1_En_20_Fig2_HTML.jpg

图 20-2

线性密码分析输出模拟程序

using System;

namespace LinearCryptanalysis
{
    class ExampleOfLinearCryptanalysis
    {
        #region Variables
        public static int[,] approximation_table =
        new int[16,16];
        public static int[] known_plaintext = new int[500];
        public static int[] known_ciphertext = new int[500];
        public static int number_known = 0;

        public static int[] theSBox =
               new int [16] {9, 11, 12, 4, 10, 1, 2, 6, 13,
                                 7, 3, 8, 15, 14, 0, 5};
        public static int[] revtheSBox = {14, 5, 6, 10, 3, 15,
                                 7, 9, 11, 0, 4, 1, 2, 8, 13, 12};
        #endregion

        //** the function will round
        //** the sbox values accordingly
        //** based on the value inputed and the sub key
        public static int RoundingFunction(int theInputValue,
                                                int theSubKey)
        {
            int index_position = theInputValue ^ theSubKey;
            return theSBox[index_position];
        }

        //** generatiing the keys
        //** and generating the known pairs
        public static void FillingTheKnowledgedOnces()
        {
            Random randomNumber = new Random();
            int theSubKey_1 = randomNumber.Next() % 16;
            int theSubKey_2 = randomNumber.Next() % 16;

            Console.WriteLine("Generating the data: Key1 =
                  {0}, Key2 = {1}\n", theSubKey_1, theSubKey_2);

            for (int h = 0; h < number_known; h++)
            {
                known_plaintext[h] = randomNumber.Next() % 16;
                known_ciphertext[h] =
                          RoundingFunction(RoundingFunction(
                          known_plaintext[h], theSubKey_1),
                                theSubKey_2);
            }

            Console.WriteLine("Generating the data: Generating
                              {0} Known Pairs\n\n", number_known);
        }

        //** show the the linear approximation
        //** note that the parameters
        //** a and b starts from 1
        public static void DisplayTheApproximation()
        {
            Console.WriteLine("Generate the linear
                                            approximation: \n");

            for (int a = 1; a < 16; a++)
            {
                for (int b = 1; b < 16; b++)
                {
                    if (approximation_table[a, b] == 14)
                        Console.WriteLine("{0} : {1} to
                                {2}\n", approximation_table[a, b],
                                a, b);
                }
            }
            Console.WriteLine("\n");
        }

        public static int ApplyingTheMask(int v, int m)
        {
            //** v - is the value
            //** m - is the mask
            int internal_value = v & m;
            int total_amount = 0;

            while (internal_value > 0)
            {
                int temporary = internal_value % 2;
                internal_value /= 2;

                if (temporary == 1)
                    total_amount = total_amount ^ 1;
            }
            return total_amount;
        }

        //** the function will validate and
        //** test the keys accordingly
        public static void ValidationAndTestingKeys(int key_1,
        int key_2)
        {
            for (int h = 0; h < number_known; h++)
            {
                if (RoundingFunction(RoundingFunction
                          (known_plaintext[h], key_1), key_2) !=
                          known_ciphertext[h])
                break;
            }
            Console.WriteLine("* ");
        }

        public static void FindingTheApproximation()
        {
            Random randomNumber = new Random();

            //** The output the mask
            for (int a = 1; a < 16; a++)
            {
                //** The input mask
                for (int b = 1; b < 16; b++)
                {
                    //** the input
                    for (int c = 0; c < 16; c++)
                    {
                        if (ApplyingTheMask(c, b) ==
                            ApplyingTheMask(theSBox[c], a))
                        {
                            approximation_table[b, a]++;
                        }
                    }
                }
            }
        }

        public static void Main(String[] args)
        {
            int[] key_score = new int[16];
            int[] theProperKeys = new int[16];
            int stateProgress = 0;
            int maximum_score = 0;
            int guessing_key_1, guessing_key_2;
            int x, y;

            Random randomNumber = new Random();

            Console.WriteLine("Linear Cryptanalysis
                                        Simulation Program\n");

            randomNumber.Next();

            FindingTheApproximation();
            DisplayTheApproximation();

            int approximationAsInput = 11;
            int approximationAsOutput = 11;

            number_known = 16;
            FillingTheKnowledgedOnces();

            Console.WriteLine("Cryptanalysis Linear Attack –
                          PHASE1\. \n\t\t Based on linear
                          approximation = {0} -> {1}\n",
                          approximationAsInput,
                          approximationAsOutput);

            for (x = 0; x < 16; x++)
            {
                key_score[x] = 0;

                for (y = 0; y < number_known; y++)
                {
                    stateProgress++;

                    //** Find Bi by guessing at K1
                    int middle_round =
                          RoundingFunction(known_plaintext[y], x);

                    if ((ApplyingTheMask(middle_round,
                              approximationAsInput) ==
                              ApplyingTheMask(known_ciphertext[y],
                              approximationAsOutput)))
                          key_score[x]++;
                    else
                        key_score[x]--;
                }
            }

            for (x = 0; x < 16; x++)
            {
                int theScore = key_score[x] * key_score[x];
                if (theScore > maximum_score)
                    maximum_score = theScore;
            }

            for (y = 0; y < 16; y++)
                theProperKeys[y] = -1;

            y = 0;

            for (x = 0; x < 16; x++)
                if ((key_score[x] * key_score[x]) ==
                                                    maximum_score)
                {
                    theProperKeys[y] = x;
                    Console.WriteLine("Cryptanalysis Linear
                                Attack - PHASE 2\. \n\t\t The
                                possible for Key 1 = {0}\n",
                                theProperKeys[x]);
                    y++;
                }

            for (y = 0; y < 16; y++)
            {
                if (theProperKeys[y] != -1)
                {
                    int testing_key_1 =
                                RoundingFunction(known_plaintext[0],
                                theProperKeys[y]) ^
                                revtheSBox[known_ciphertext[0]];

                    int g;
                    int wrong = 0;
                    for (g = 0; g < number_known; g++)
                    {
                        stateProgress += 2;
                        int testOut =
                                RoundingFunction(RoundingFunction(
                                known_plaintext[g], theProperKeys[y]), testing_key_1);

                        if (testOut != known_ciphertext[g])
                            wrong = 1;
                    }
                    if (wrong == 0)
                    {
                        Console.WriteLine("Cryptanalayis
                                      Linear Attack - PHASE 3.\n");
                        Console.WriteLine("\t\tI have found
                                      the keys! Key1 = {0}, Key2 =
                                      {1}\n", theProperKeys[y],
                                      testing_key_1);

                        guessing_key_1 = theProperKeys[y];
                        guessing_key_2 = testing_key_1;
                        Console.WriteLine("Cryptanalysis
                                      Linear Attack - PHASE 4.\n");
                        Console.WriteLine("\t\tThe number of
                                      computation until the key has
                                      been found = 0\n",
                                      stateProgress);

                        }
                }
            }

            Console.WriteLine("Cryptanalyis Linear Attack –
                                                   PHASE 5.\n");
            Console.WriteLine("The number of computation =
                                      {0}\n\n", stateProgress);

            stateProgress = 0;

            for (y = 0; y < 16; y++)
            {
                for (x = 0; x < 16; x++)
                {
                    int t;
                    int wrong = 0;
                    for (t = 0; t < number_known; t++)
                    {
                        stateProgress += 2;
                        int testOut =
                               RoundingFunction(RoundingFunction(
                                      known_plaintext[t], y), x);

                        if (testOut != known_ciphertext[t])
                            wrong = 1;
                    }
                    if (wrong == 0)
                    {
                        Console.WriteLine("Brute Force –
                                                         PHASE 1.\n");
                        Console.WriteLine("\t\tI managed to
                                      find the keys! \n\t\t
                                            Key1 = {0} \n\t\t Key2 =
                                            {1}\n", y, x);

                        Console.WriteLine("Brute Force –
                                                         PHASE 2\n");
                        Console.WriteLine("\t\tThe number of
                                      computations until the key
                                      was dound = {0}\n",
                                      stateProgress);
                    }
                }
            }

            Console.WriteLine("Brute Force - PHASE 3.\n");
            Console.WriteLine("Computations total_amount =
                                              {0}\n", stateProgress);

            while (true) { }
        }
    }
}

Listing 20-2Linear Cryptanalysis Simulation

结论

在本章中,我们讨论了差分和线性密码分析攻击,以及如何设计和实现这种攻击。我们介绍了在设计这种密码分析攻击之前必须了解的理论背景和主要基础。

在本章结束时,您现在可以

  • 从理论上确定密码分析人员应该关注的主要部分

  • 了解这些组件有多脆弱以及如何被利用

  • 实现线性和差分密码分析攻击

文献学

  1. 琼·代蒙、拉斯·努森和文森特·里门,《分组密码方阵》。快速软件加密(FSE),”计算机科学讲义第 1267 卷(第 149-165 页),以色列海法:施普林格出版社。citeserx 10 . 1 . 1 . 55 . 6109。1997.

  2. H.Heys,“线性和差分密码分析教程”,载于 Cryptologia ,第二十六卷,第 3 期(第 189-221 页),2002 年。

  3. 米(meter 的缩写))Matsui,“DES 密码的线性密码分析方法”,载于密码学进展-EUROCRYPT’93(第 386-397 页),Springer-Verlag,1994 年。

  4. E.比哈姆,《论松井的线性密码分析》在 a .德森蒂斯峰(编),计算机科学讲座笔记,第 950 卷,(第 341–355 页)。施普林格出版社,柏林,1995。

  5. A.Biryukov,C. De Cannière,M. Quisquater,“论多重线性近似”,M. Franklin(编辑),密码学进展,2004 年加密会议录,计算机科学讲义 3152 (第 1-22 页)。斯普林格出版社,2004 年。

  6. 长度 Keliher,H. Meijer 和 S.E. Tavares,“SPN 最大平均线性外壳概率上限的新方法”在 b .普菲兹曼(编), LNCS ,第 2045 卷(第 420—436 页)。施普林格出版社,柏林,2001。

  7. 长度 R. Knudsen 和 J.E. Mathiassen,“对 DES 的选择明文线性攻击”。),计算机科学讲义,1978 卷(第 262–272 页)。施普林格出版社,柏林,2001。

  8. 米(meter 的缩写))FEAL 密码已知明文攻击的新方法在 R.A. Rueppel(编),计算机科学讲义,第 658 卷(第 81-91 页)。施普林格出版社,柏林,1993。

  9. 米(meter 的缩写))数据加密标准的首次实验性密码分析。在 Y.G. Desmedt (ed),计算机科学讲义,第 839 卷(第 1-11 页)。施普林格出版社,柏林,1994。**

二十一、整数密码分析

整数密码分析代表了一种特殊的密码分析攻击,它适用于基于替换置换网络构建的分组密码。该攻击由 Lars Knudsen 设计为针对 Square [1 ]的专用攻击,因此被称为 Square 攻击。

置换网络代表了分组密码最重要的弱点之一。一旦网络可以被发现(让我们直观地说),那么对分组密码的攻击可以毁灭性地破坏整个密码系统。下一个易受攻击的点是密钥和用于置换密钥的表。一旦密钥接近真实的或相同的,那么我们就接近破解密码系统。这种密钥的一个例子是

        private byte[]PermutationTableForKey = {
            06, 30, 13, 07, 05, 35, 15, 14,
            12, 18, 03, 38, 09, 10, 22, 25,
            16, 04, 21, 08, 39, 37, 36, 02,
            24, 11, 28, 27, 29, 23, 33, 01,
            32, 17, 31, 00, 26, 34, 20, 19
        };

在接下来的几页中,我们将介绍实现分组密码所需的必要元素,并确定为了发起整体密码分析攻击而必须关注的元素,例如构建 Feistel 网络(参见清单 22-2 )和为密钥生成置换表(清单 22-4 )。一旦对这两个阶段有了清楚的理解,如何进行完整的密码分析就变得非常清楚了。

基本概念

为了实现和设计完整的密码分析攻击,首先理解基本概念是非常重要的。所以让我们把下面的概念作为主要的出发点。在此基础上,我们将设计和实施这样的攻击,目的只是为了教育。

让我们考虑( G ,+)是一个有限阿贝尔群或序 k 。下面的产品组,Gn=G×…×G,是具有形式为v=(v1,…, v n 的元素的组,其中 v i G n 内的加法被定义为组件方式。所以现在我们有u+v=w持有为 uvwGnuI+v**

我们用 B 表示向量的多重集。在 B 上定义的积分代表所有矢量的和, S 。换句话说,积分被定义为$$ \int S=\sum \limits_{v\in B}v $$,求和运算被定义为 G * n * 的分组运算。

当设计整数密码分析攻击时,知道并计算明文和密文中的字数是非常重要的。对于我们的例子,这个数字将用 n 来表示。另一个需要注意的非常重要的数字是明文和密文的数量,用 m 表示。通常, m = k (即 k = | G |),代表明文和密文的向量 vB ,以及G=GF(2BG=

前进到攻击,其中一方将试图在特定数量的加密轮次之后预测位于积分中的值。基于这一目的,区分以下三种情况是有益的:(1)在所有字都相等的情况下(例如 i ),(2)所有字都不同,(3)求和到预先预测的某个值。

让我们考虑将bgn声明为和以前一样的固定索引 i 。发生了以下三种情况:

  1. vI=c,对于所有 vB

  2. {v【I】:【v】**【b】} =

** $$ \sum \limits_{v\in B}{v}_i={c}^{\prime } $$

 *

其中 ccG*是一些事先已知并固定的值。

下面的例子代表了一种典型的情况,其中 m = k ,在 B 中找到的向量的数量等于所考虑的组中的元素的数量。通过使用拉格朗日定理,我们可以看到是否所有的字, i th 都相等,然后很清楚来自积分的Ith字将取来自 G 的中性元素的值。

以下两个定理是必要的,并且对于任何想要将整数密码分析转化为实践的实际开发者来说是必须的。

定理 21-1【1,定理 1,第 114 页】让我们考虑( G ,+) a 有限阿贝尔加群。阶为 1 或 2 的元素的子组表示为L= {GG:G+G= 0 }。我们认为写作 s ( G )是在 G 中找到的所有元素的总和$$ \sum \limits_{g\in G}g $$。接下来我们考虑s(G)=∑HHH。更多的,理解下面这个类比很重要:s(G)∈H,即s(G)+s(G)= 0。

也就是说,对于G=GF(2B)我们得到 s ( G ) = 0,对于 Z / mZ 我们得到s(Z/mZ)=m/2 当我们也有书面群的乘法情况(见定理 21-2)。

由此 21-2【1,定理 2,第 114 页】。让我们考虑( G ,∫)一个有限阿贝尔乘法群。1 阶或 2 阶元素的子群表示为H= {GG:GG= 1 }。我们认为写作 p ( G )是 G 所有元素的乘积$$ \prod \limits_{g\in G\ }g $$。接下来,我们考虑$$ p(G)=\prod \limits_{h\in H}h $$。更多,理解下面这个类比很重要:p(G)∈H,即p(G)∫p(G)= 1。

举个例子,如果我们有G=(Z/pZ)其中 p 是质数, p ( G )是 1,p(G)= 1。这是用威尔逊定理证明的。

实用方法

这一节将展示如何使用?NET 框架和 C# 编程语言。下面的方法代表了一个 Feistel 网络在 C# 中的基本实现,可以覆盖当前的块大小。我们将使用重复的序列,目的是创造一个弱点,以显示它如何被利用和制造攻击。

清单 21-1 展示了如何构建和设计整体密码分析攻击。清单 21-2 展示了 Feistel 网络是如何构建的。一旦我们有了结构,要成功攻击,重要的是要“弄清楚”Feistel 网络是如何构建的,并创建一个它的副本或至少是与原始网络相似的东西。清单 21-3 展示了如何实现块数据,清单 21-4 展示了如何生成密钥。

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

namespace IntegralCryptanalysis
{
    public class Key
    {
        //** permutation table (initial)
        //** used to permutate the key
        private byte[] initial_permutation_table_1 = {
            06, 30, 13, 07, 05, 35, 15, 14,
            12, 18, 03, 38, 09, 10, 22, 25,
            16, 04, 21, 08, 39, 37, 36, 02,
            24, 11, 28, 27, 29, 23, 33, 01,
            32, 17, 31, 00, 26, 34, 20, 19
        };

        /// <summary>
        /// The representation of the key as a byte array
        /// </summary>
        public byte[] KeyBytes
        {
            get; set;
        }

        /// <summary>
        /// Encryption and decryption key for a text
        /// </summary>
        /// <param name="keyAsAString">The key to use for this instance</param>
        public Key(string keyAsAString)
        {
            int k = 0, key_length = keyAsAString.Length;

            //** expansion of the key to a maximum of 40 bytes
            while (keyAsAString.Length < 40)
                keyAsAString += keyAsAString[k++];

            KeyBytes = System.Text.Encoding.UTF8.GetBytes(keyAsAString);

            //** permutation of the key bytes using
            //** initial_permutation_table_1
            for (k = 0; k < KeyBytes.Length; k++)
                KeyBytes[k] = KeyBytes[initial_permutation_table_1[k]];

            Debug.WriteLine("The post permutation key is: "+ System.Text.Encoding.UTF8.GetString(KeyBytes));
        }

        /// <summary>
        /// Generate the keys that are used within the round function
        /// </summary>
        /// <returns>A list with the keys that are of 64-bit. The format is ulong.</returns>
        public List<ulong> ReturnRoundKeys()
        {
            //** Rounds is defined as 64-bit
            //** keys found in the Key string
            int count_of_round = KeyBytes.Length / 8;
            List<ulong> round_keys = new List<ulong>();

            for (int k = 0; k < count_of_round; k++)
            {
                byte[] round_key_bytes = new byte[8];
                ulong round_key = 0;

                Array.Copy(KeyBytes, k * 8, round_key_bytes, 0, 8);
                Array.Reverse(round_key_bytes);

                round_key = BitConverter.ToUInt64(round_key_bytes, 0);

                round_keys.Add(round_key);
            }
            return round_keys;
        }
    }
}

Listing 21-4The Key Class

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

namespace IntegralCryptanalysis
{
    public class Block
    {
        /// <summary>
        /// Represents the data that are held by the block
        /// </summary>
        public byte[] DataStructure { get; set; }

        /// <summary>
        /// Represents the left half of the data block
        /// </summary>
        public byte[] LeftHalf
        {
            get
            {
                return DataStructure.Take(DataStructure.Length / 2).ToArray();
            }
            set
            {
                Array.Copy(value, DataStructure, DataStructure.Length / 2);
            }
        }

        /// <summary>
        /// Represents the right half of the data block
        /// </summary>
        public byte[] RightHalf
        {
            get
            {
                return DataStructure.Skip(DataStructure.Length / 2).ToArray();
            }
            set
            {
                Array.Copy(value, 0, DataStructure, DataStructure.Length / 2, DataStructure.Length / 2);
            }
        }

        /// <summary>
        /// Get and return as BitArray the left half of the block data
        /// </summary>
        public BitArray TheLeftBitsOfBlock
        {
            get
            {
                return new BitArray(LeftHalf);
            }
        }

        /// <summary>
        /// Get and return as BitArray the right half of the block data
        /// </summary>
        public BitArray RightBitsOfBlock
        {
            get
            {
                return new BitArray(RightHalf);
            }
        }

        /// <summary>
        /// Representation of the size in bytes of the Block
        /// </summary>
        public int BlockSize
        {
            get
            {
                return DataStructure.Length;
            }
        }

        /// <summary>
        /// The representation of a data block. Constructor
        /// </summary>
        /// <param name="size_of_the_block">The size value (in bytes) of the block</param>
        public Block(int size_of_the_block)
        {
            DataStructure = new byte[size_of_the_block];
        }

        /// <summary>
        /// The representation of a data block. Constructor
        /// </summary>
        /// <param name="the_data_block">the data content stored by the block</param>
        public Block(byte[] the_data_block)
        {
            DataStructure = the_data_block;
        }

        /// <summary>
        /// Swaps the halves (left and right) of the block
        /// </summary>
        public void SwapHalfes()
        {
            byte[] temporary = LeftHalf;
            LeftHalf = RightHalf;
            RightHalf = temporary;
        }

        /// <summary>
        /// Converts the Block to a UTF-8 string
        /// </summary>
        /// <returns>String representation of this block</returns>
        public override string ToString()
        {
            return System.Text.Encoding.UTF8.GetString(DataStructure);
        }
    }

}

Listing 21-3Implementing the Block Data

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

namespace IntegralCryptanalysis
{
    public class FeistelNetwork
    {
        //** represents the size in bytes
        //** for each of the block
        public int BlockSize
        {
            get;
            private set;
        }

        //** the password code for
        //** encryption and decryption
        public string PasswordCode
        {
            get;
            set;
        }

        /// <summary>
        /// The basic constructor of Feistel Class.
        /// </summary>
        /// <param name="password_code">represents the
        /// password code</param>
        public FeistelNetwork(string password_code)
        {
            this.PasswordCode = password_code;
            this.BlockSize = 16;
        }

        /// <summary>
        /// Constructs a new instance of the Feist class, with a custom blocksize
        /// </summary>
        /// <param name="password_code">Passcode used in this instance</param>
        /// <param name="the_block_size">Size of the blocks to use in this instance</param>
        public FeistelNetwork(string password_code, int the_block_size) : this(password_code)
        {
            this.BlockSize = the_block_size;
        }

        /// <summary>
        /// Encryption operation of the clear text using the password code
        /// </summary>
        /// <param name="clearText">The string to encrypt</param>
        /// <returns>The encrypted text.</returns>
        public string EncryptionOp(string clearText)
        {
            return DoCiphering(clearText, true);
        }

        /// <summary>

        /// Decryption operation of the encrypted text using the password code
        /// </summary>
        /// <param name="clearText">The string to decrypt</param>
        /// <returns>The decrypted text.</returns>
        public string DecryptionOp(string clearText)
        {
            return DoCiphering(clearText, false);
        }

        /// <summary>
        /// Do a Feistel encryption on the text
        /// </summary>
        /// <param name="sourceText">The clear text or encrypted text to encrypt/decrypt</param>
        /// <param name="isClearText">Decide if the given text represents (true) or not (false) a plaintext string</param>
        /// <returns>A string of plain or ciphered
        /// text</returns>
        private string DoCiphering(string sourceText, bool isClearText)
        {
            int pointer_block = 0;
            string cipher_text_posting = "";
            List<ulong> the_round_keys = new Key(PasswordCode).ReturnRoundKeys();

            //** Do a padding operation to
            //** the string using '\0'.
            //** The output will
            //** be a multiple of <blocksize>
            while (sourceText.Length % BlockSize != 0)
                sourceText += new char();

            //** in case of decryption, reverse

            //** the encryption keys
            if (!isClearText)
                the_round_keys.Reverse();

            byte[] the_text_bytes = Encoding.UTF8.GetBytes(sourceText);

            //** do iteration through the text
            //** moving with <blocksize> bytes per iteration
            while (pointer_block < the_text_bytes.Length)
            {
                byte[] the_block_as_bytes = new byte[BlockSize];
                Array.Copy(the_text_bytes, pointer_block, the_block_as_bytes, 0, BlockSize);

                Block text_as_block = new Block(the_block_as_bytes);

                //** if we have a ciphertext,
                //** swap it in halves
                if (!isClearText)
                    text_as_block.SwapHalfes();

                //** each round keys will be
                //** applied to the text
                foreach (ulong the_round_key in the_round_keys)
                    text_as_block = RoundOnTheBlock(text_as_block, the_round_key);

                //** build the output by appending it
                if (!isClearText) text_as_block.SwapHalfes();
                cipher_text_posting += text_as_block.ToString();

                pointer_block += BlockSize;
            }
            return cipher_text_posting.Trim('\0');
        }

        /// <summary>
        /// Do a single round encryption on the block
        /// </summary>
        /// <param name="theBlock">The block that will be encrypted or decrypted</param>
        /// <param name="theRoundKey">The round key which will be applied as the round function</param>
        /// <returns>The next block in the round sequence</returns>
        private Block RoundOnTheBlock(Block block, ulong theRoundKey)
        {
            ulong theRoundFunction = 0;

            Block roundBlock = new Block(block.BlockSize);

            BitArray keyBits = new BitArray(BitConverter.GetBytes(theRoundKey)), funcBits = block.RightBitsOfBlock.Xor(keyBits);

            roundBlock.LeftHalf = block.RightHalf;

            //** do the proper casting AND round
            //** the function bits to an int
            //** set R(i+1) as L(i) XOR f
            theRoundFunction = ToInteger64(funcBits);
            roundBlock.RightHalf = BitConverter.GetBytes(ToInteger64(block.TheLeftBitsOfBlock) ^ theRoundFunction);

            return roundBlock;
        }

        /// <summary>
        /// Helper method used for conversion of BitArray to have an integer representation
        /// </summary>
        /// <param name="theArray">BitArray that will be converted</param>
        /// <returns>A value of 64-bit integer of the array</returns>
        private ulong ToInteger64(BitArray theArray)
        {
            byte[] array_as_byte = new byte[8];
            theArray.CopyTo(array_as_byte, 0);
            return BitConverter.ToUInt64(array_as_byte, 0);
        }
    }

}

Listing 21-2Building the Feistel Network

using System;
using System.IO;

namespace BuildingIntegralCryptanalysis
{
    class Program
    {
        static void Main(string[] args)
        {
            string theKeyword = "", data_input = "",
                   data_output = "", data_file = "";

            FeistelNetwork feist_network;
            StreamReader file_stream_reader;
            StreamWriter file_stream_writer;

            //** create a help text for the user
            const string helperData =
            @"Building Integral Cryptanalysis Attack
              Usage:
               private [-option] keyword
                         input_file output_file
                          Options:
                             -enc Encrypt the file passed as
                                          input using the keyword
                             -dec Decrypt the file passed as
                                          input using the keyword";

            //** show in the console the helper
            //** if we have less than four arguments
            if(args.Length < 4)

            {
                Console.WriteLine(helperData);
                return;
            }
            else if(args[1].Length < 10 ||
                                              args[1].Length > 40)
            {
                 //** Output usage if the password
                 //** is too short/long
                 Console.Write("The length of the password is
                 invalid. The password should have between 10-40 characters.\n" + helperData);

                 return;
            }

            theKeyword = args[1];
            data_input   = args[2];
            data_output  = args[3];

            //** environment input/output configuration
            feist_network = new FeistelNetwork(theKeyword);
            file_stream_reader = new StreamReader(data_input);
            file_stream_writer = new
                                     StreamWriter(data_output);

            //** Read the data from the input file
            data_file = file_stream_reader.ReadToEnd();
            file_stream_reader.Close();

            if (args[0] == "-enc")
            {
                //** do the encryption based
                //** on the argument provided
                string ciphertext =
                                   feist_network.EncryptionOp(data_file);
                file_stream_writer.Write(ciphertext);
                Console.WriteLine("The file has been encrypted
                        with success. The file has been saved
                                               to: " + data_output);
            }
            else if(args[0] == "-dec")

            {
                //** do the decryption based on the argument
                string thePlaintext =
                                   feist_network.DecryptionOp(data_file);
                file_stream_writer.Write(thePlaintext);
                Console.WriteLine("The file has been decrypted
                             with success. The file has been saved
                             to: " + data_output);
            }
            else
            {
                //** invalid option selected
                Console.Write("The selected option is invalid.
                               Please, choose another option.\n"
                               + helperData);
            }

            file_stream_writer.Close();

            Console.ReadKey();
        }
    }
}

Listing 21-1The Main Program

结论

在这一章中,我们讨论了整体密码分析以及这种攻击是如何设计和实现的。我们提出了一种建立分组密码系统的方法,并结合脆弱点来说明如何应用整数密码分析攻击。

在本章结束时,您现在可以

  • 设计并实现一个简单的分组密码

  • 理解这种密码的两个弱点,例如 Feistel 网络和生成置换表来置换密钥

  • 了解 Feistel 网络是如何实现的

  • 使用排列表,并在密钥上使用它们

文献学

  1. 琼·代蒙、拉斯·努森和文森特·里门,《分组密码方阵》。快速软件加密(FSE) 1997,“计算机科学讲义*第 1267 卷(第 149–165 页)。以色列海法:施普林格出版社。citeserx 10 . 1 . 1 . 55 . 6109。1997.**

二十二、攻击

在这一章中,我们将分析分布式环境中可能发生的最重要的攻击(云计算 [3 或大数据),以及如何使用具有 8.0 版本功能的 C# 编程语言 [2 来利用它们。作为参考,我们将使用一个 ASP.NET MVC 5(也适用于以前版本的 MVC,比如 1.0、2.0、3.0 和 4.0)应用作为例子,它使用 C# 作为后端编程语言。

web 应用上每天发生的三种最常见的攻击是重定向攻击、SQL 注入和跨站脚本(XSS)。

这里描述的方法是道德黑客领域的一部分,错误地使用它们会给企业带来重大灾难。这些方法对红队队员非常有用,其中一些方法将用于后期开发。

端口转发和如何防止开放重定向攻击

通过端口转发,黑客无法访问您,但是可以利用路由器在 web 端口上进行配置。

通过将用户重定向到外部恶意 URL,重定向到使用基于 querystring 的请求指定的 URL 的 web 应用很容易被篡改。这种攻击被称为开放重定向攻击

要执行攻击并应对此漏洞,了解登录重定向在 ASP.NET MVC web 应用项目中的工作方式非常重要。在下面的例子中,我们可以看到,如果我们要访问一个结构中有[Authorize]属性的控制器动作,这将把未授权的用户重定向到/Account/LogOn视图。提到的到/Account/LogOn的重定向在其结构中有一个称为returning_url querystring的参数,这样用户在成功登录后将被返回到所请求的真正的 URL。

基于开放重定向的攻击是非常危险的。攻击者确切地知道我们何时开始登录某个网站。这可能使我们容易受到网络钓鱼攻击。例如,考虑一个攻击者,他向订阅了某个网站的用户发送恶意电子邮件,以获取他们的密码。

让我们考虑以下示例,其中攻击者向用户发送包含特定网页(例如 http://apressprocryptoexample.com )上的登录页面的链接,该链接在其组件中具有到伪造页面的重定向,

http://apressprocryptoexample.com/Account/LogOn?returnUrl=http://apresprocryptoexample.com/Account/LogOn 。注意返回的 URL,它指向 apresprocryptoexample.com. Note that,单词“apress”中少了一个"s"这意味着域名apresprocryptoexample.com正在被黑客控制。

如果登录正确,来自 ASP.NET MVC 的AccountController LogOn动作将把我们重定向到 querystring 参数中提到的 URL,returning_url。在这种情况下,该 URL 就是攻击者使用的 URL,即 http://apresprocryptoexample.com 。如果我们不注意,我们不会注意到浏览器的不同。攻击者是一个非常精通此道的人,他会小心翼翼地确保伪造的页面看起来与原始页面一模一样。假登录页面包含一条错误消息,提示我们需要使用我们的凭据再次登录。一旦我们重新输入我们的凭证,假登录页面将保存数据并把我们送回原来的页面,ApressProCryptoExample.com。在这一点上,ApressProCryptoExample.com已经成功地让我们登录了,这个假的认证页面能够把我们重定向到那个特定的页面。最终的结果是基于这样一个事实,即攻击者知道我们的凭证(用户名和密码),而我们,真正的用户,不知道我们已经如此容易地提供了我们的凭证 [4 ]。

让我们继续来看看我们的应用中的LoginAuthentication动作的代码(清单 22-1 )。我们定义的控制器将返回一个到returning_url的重定向。对于returning_url参数,完全没有验证。

表 22-1

SQL 注入的其他示例

|

SQL 注入的例子

|

描述

|
| --- | --- |
| ' or '' | 字符串字符作为指示符 |
| -- or # | 单行注释 |
| /*..*/ | 多行注释 |
| + | 添加、连接(也适用于 URL) |
| &#124;&#124; | 双重连接 |
| % | 通配符属性指示器 |
| ?Param1=foo&Param2=bar | URL 参数 |
| PRINT | 非事务命令 |
| @variable | 局部变量 |
| @@variable | 全局变量 |
| waitfor delay '0:0:10' | 时延 |

[HttpPost]
public ActionResult LoginAuthentication(
                                    LogOnModel model,
                                    string returning_url)
{
    if (ModelState.IsValid)
    {
        if (MembershipService.ValidateUser(model.UserName,
        model.Password))
        {
            FormsService.SignIn(model.UserName,
                                model.RememberMe);
            if (!String.IsNullOrEmpty(returning_url)) {
                return Redirect(returning_url);
            }
            else {
                return RedirectToAction("Index", "Home");
            }
        }
        else {
            ModelState.AddModelError("", "The credentials are
                                      wrong. Please, try again.");
        }
    }

    //** if we reach here it means that
    //** something went wrong
    //** we will show again the form
    return View(model);
}

Listing 22-1Login Controller

清单 22-2 展示了如何用returning_url进行验证。这是使用一个新方法IsLocalUrl()完成的,它是助手类System.Web.Mvc.Url的一部分。

[HttpPost]
public ActionResult LoginAuthentication(LogOnModel model, string returning_url)
{
    if (ModelState.IsValid)
    {
        if (MembershipService.ValidateUser(model.UserName,
                                           model.Password))
        {
            FormsService.SignIn(model.UserName,
                                            model.RememberMe);
            if (Url.IsLocalUrl(returning_url)){
                return Redirect(returning_url);
            }
            else {
                return RedirectToAction("Index", "Home");
            }
        }
        else {
            ModelState.AddModelError("",
                      "The credentials are wrong.
                       Please, try again.");
        }
    }

    //** if we reach here it means that
    //** something went wrong
    //** we will show again the form
    return View(model);
}

Listing 22-2Validation for returning_url

SQL 注入

对于许多公司来说,他们的业务是在网上和云计算、大数据等环境中进行的,因此他们的网站越来越暴露在数据被盗的可能性之下。黑客可以从他们的数据中获得重要信息,并将其传递给市场上的其他参与者。

SQL 注入是最常见的方法之一,恶意用户可以通过它使用网页输入将不同的 SQL 命令注入到 SQL 语句中。通过这样做,恶意用户可以破坏 web 应用的安全性。

在这一节中,我们将考虑下面的 web 应用,它是为此目的从头开始构建的。从头开始创建这样一个应用的原因是为了说明开发人员在开发过程中经常犯的错误。

第一步是建立一个包含用户登录数据的数据库。数据库是在 Microsoft SQL Server v.17.9 中创建的。图 22-1 显示了表的结构,清单 22-3 显示了生成表的 SQL 脚本代码。一个常见的错误是,许多 web 应用以明文形式存储密码。建议避免这种做法。最佳实践是使用密码的散列(如 MD5、SHA128、SHA256 等。).对于我们的例子,我们将使用明文密码。

img/493660_1_En_22_Fig1_HTML.jpg

图 22-1

表结构

USE [Apress_ProCrypto_SQLInjectionDB]
GO

/****** Object:  Table [dbo].[LoginUserData]
Script Date: 6/23/2020 2:51:06 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].LoginUserData NOT NULL,
     [FirstName] varchar NULL,
     [LastName] varchar NULL,
     [Email] varchar NULL,
     [Password] varchar NULL,
     [LastDateLogged] [datetime] NULL,
 CONSTRAINT [PK_LoginUserData] PRIMARY KEY CLUSTERED
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Listing 22-3Login User Data Table SQL Script

第二步是用一些数据填充表格,如图 22-2 所示。然后我们对清单 22-4 中的表执行脚本。

img/493660_1_En_22_Fig2_HTML.jpg

图 22-2

用户数据

SET NOCOUNT ON
INSERT INTO dbo.LoginUserData
              ([FirstName],[LastName],
              [Email],[Password],[LastDateLogged])
VALUES
      ('Jefferson','Nicholas','nicholas.jefferson@domain.com','password1',CONVERT(datetime,NULL,121))
      ,('Thomas','Claudio','claudio.thomas@domain.com','password2',CONVERT(datetime,NULL,121))
      ,('Steven','Paolo','steven.paolo@domain.com','password3',CONVERT(datetime,NULL,121))
      ,('Billy','Walsh','billy.walsh@domain.com','password4',CONVERT(datetime,NULL,121))

Listing 22-4Data Content

第三步是构建网络应用。为此,我们不会使用额外的花哨的 web 应用架构。这只是一个简单的 web 应用,有两个网页。到数据库的连接是使用 done 完成的。对于其他对象关系映射(ORM ),如 NHibernate、LINQ 到 SQL 或实体框架,结果是相同的。唯一的区别是处理 SQL 注入的时间长。

我们构建的第一个页面是Login.aspx。如图 22-3 所示,该页面包含一个简单的表单。代码如清单 22-5 所示。

img/493660_1_En_22_Fig3_HTML.jpg

图 22-3

Login.aspx 页面

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>

<!DOCTYPE html>

<html xmlns:="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <center>
        <form id="form1" runat="server">
        <div>
            <asp:Login ID="MyLogin" runat="server"
                   OnAuthenticate="MyLogin_Authenticate"
                   Width="331px" BackColor="#F7F6F3" BorderColor="#E6E2D8" BorderPadding="4" BorderStyle="Solid" BorderWidth="1px"
                   Font-Names="Verdana" Font-Size="0.8em" ForeColor="#333333" Height="139px">
            <InstructionTextStyle Font-Italic="True"
                                                    ForeColor="Blue" />

            <LoginButtonStyle BackColor="green"
                   BorderColor="black" BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana"
                   Font-Size="0.8em" ForeColor="#284775" />
                <TextBoxStyle Font-Size="0.8em" />
                <TitleTextStyle BackColor="green" Font-Bold="True" Font-Size="0.9em"
                   ForeColor="White" />
            </asp:Login>
        </div>
        </form>
    </center>
    <br />
    <br />
    <center>
             This example is build for illustrating SQL
             Injection. Authors: Marius Iulian MIHAILESCU and Stefania Loredana NITA
    </center>
</body>
</html>

Listing 22-5The HTML Source Code of Login.aspx

清单 22-6 显示了Login.aspx的服务器端代码。从这里你可以注意到,没有任何形式的验证密码的完整性或避免 SQL 注入攻击。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;

public partial class Login : System.Web.UI.Page
{
    DataTable data_table;
    SqlDataAdapter adapter;
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void MyLogin_Authenticate(object sender,
                                        AuthenticateEventArgs e)
    {
        SqlConnection connection = new SqlConnection(@"Data
                         Source=SERVER_NAME;Initial Catalog=Apress_ProCrypto_SQLInjectionDB;Integrated Security=True");
        string query="select * from LoginUserData where
                   Email='"+ MyLogin.UserName+"'and Password='"+
                   MyLogin.Password+"' ";
        adapter = new SqlDataAdapter(query, connection);
        data_table = new DataTable();
        adapter.Fill(data_table);
        if (data_table.Rows.Count >= 1)
        {
            Response.Redirect("index.aspx");
        }
    }
}

Listing 22-6Server Side Source Code for Login.aspx

第二个页面叫做Index.aspx,,这是一个成功登录的确认页面(参见清单 22-7 和 22-8 )。该页面没有什么特别之处。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class Index : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
}

Listing 22-8Server Side Source Code for Index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Index.aspx.cs" Inherits="Index" %>

<!DOCTYPE html>

<html xmlns:="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>APRESS-Example of SQL Injection</title>
</head>
<body>
    <form id="form1" runat="server">
    <div><center>
    <h1>Hello, Apress! <br/>
        Example for SQL Injection
    </h1></center>
    </div>
    </form>
</body>
</html>

Listing 22-7Source Code for Index.aspx

现在我们有了整个项目集,让我们继续进行 SQL 注入分析和攻击。查询如下所示:

                 select * from LoginUserData where
             Email='"+ MyLogin.UserName+"'and Password='"+
                 MyLogin.Password+"';

让我们考虑在登录控件的文本框中输入‘or 1 = 1’的情况。一旦按下登录按钮,查询将如下所示:

select  * from LoginUserData where Email=" or 1=1--'and Password="

如果攻击者知道用户名或规则 1=1,它将不再适用。他只需在文本框中写下用户名+',并对后面的所有内容进行注释,就像这样:

               select  * from LoginUserData where
          Email='nicholas.jefferson@domain.com'--and Password="

另一个危险的例子是从数据库中删除记录或删除表。以下示例显示了这种情况:

select * from LoginUserData where Email=" Delete from LoginUserData
       –'and Password="

其他类型的 SQL 注入可以在表 23-1 中看到。其中一些非常危险,不建议用于实时生产数据库。

在 ASP.NET MVC web 应用的情况下,过程与上面描述的类似。

跨站点脚本(XSS)

跨站脚本(XSS)是一个安全漏洞,攻击者在网页中插入客户端脚本,主要是 JavaScript。当用户访问 web 应用时,浏览器将加载受影响的页面,这些页面中的脚本将运行,从而为攻击者提供了获取 cookies 和会话令牌的途径。大多数情况下,当 web 应用接受用户输入并将其输出到另一个页面而不进行验证、编码或转义时,就会出现 XSS 漏洞。

根据经验,我们建议您遵循以下规则来保护您的 web 应用免受 XSS 攻击:

  • 千万不要在 HTML 页面中输入敏感数据,比如 HTML 表单输入、查询、HTTP 头等。

  • HTML 元素中的所有内容都应该被编码。

  • HTML 属性中的所有内容都应该被编码。

  • 比方说,任何需要添加到 JavaScript 中的不受信任的数据(我不喜欢这个术语,但让我们保持这种方式)应该首先输入到一个 HTML 元素中,该元素的内容应该在运行时过程中检索。

  • 在向 URL 查询字符串添加任何数据之前,请确保 URL 已编码。

例如,我们将考虑 HTML 编码如何与 MVC 中使用的 Razor 引擎一起工作,该引擎对变量的整个输出进行编码。为了避免这种情况发生,我们需要一个解决方案来防止这种情况发生。与编码相关的 HTML 属性表示 HTML 编码类型的超集,这意味着我们不需要担心应该使用 HTML 编码还是 HTML 属性编码。我们只需要确保在 HTML 上下文中使用' @ ',而不是当我们试图用 JavaScript 直接添加不可信的数据作为输入时。

让我们考虑下面清单 22-9 中的剃刀 1 视图。视图的输出将是一个untrustedInput变量。这种类型的变量在其组件中具有一些特征,这些特征可以在 XSS 攻击中被成功地利用和使用。这些角色是。在渲染输出中,编码看起来像:<&quot;testing123&quot;>

@{
       var untrustedInput = "<\"testing123\">";
   }

   @untrustedInput

Listing 22-9Example of a Razor View

有些情况下,我们希望在视图中向 JavaScript 添加要处理的值。要实现这一点,我们可以走两条路。实现这一点最安全的方法是将值放在特定标记的数据属性中,并在我们的 JavaScript 中获取它,如清单 22-10 所示。

@{
       var untrustedInput = "<\"testing123\">";
   }

   <div
       id="theDataToBeInjected"
       data-untrustedinput="@untrustedInput" />

   <script>
     var theDataToBeInjected =
            document.getElementById("theDataToBeInjected");

     //** for all the clients
     var clientWithUntrustedInput =
         theDataToBeInjected.getAttribute("data-untrustedinput");

     //** for clients that support HTML 5
     var clientWithUntrustedInputHtml5 =
         theDataToBeInjected.dataset.untrustedinput;

     document.write(clientWithUntrustedInput);
     document.write("<br />")
     document.write(clientWithUntrustedInputHtml5);
   </script>

Listing 22-10Using Razor and JavaScript Encoding

清单 22-10 中的代码将产生清单 22-11 中所示的输出。

<div
     id="theDataToBeInjected"
     data-untrustedinput="<&quot;testing123&quot;>" />

   <script>
     var theDataToBeInjected =
                   document.getElementById("theDataToBeInjected");

     var clientWithUntrustedInput =
         theDataToBeInjected.getAttribute("data-untrustedinput");;

     var clientWithUntrustedInputHtml5 =
         theDataToBeInjected.dataset.untrustedinput;

     document.write(clientSideUntrustedInputOldStyle);
     document.write("<br />")
     document.write(clientWithUntrustedInputHtml5);
   </script>

Listing 22-11The Output

一旦脚本运行,渲染结果将是

<"testing123">
   <"testing123">

同样,我们可以调用 JavaScript 编码器,如清单 22-12 所示。

@using System.Text.Encodings.Web;
   @inject JavaScriptEncoder encoder;

   @{
       var someUntrustedInput = "<\"testing123\">";
   }

   <script>
       document.write("@encoder.Encode(someUntrustedInput)");
   </script>

Listing 22-12JavaScript Encoder

一旦由浏览器呈现,我们将得到以下内容:

<script>
    document.write("\u003C\u0022testing123\u0022\u003E");
</script>

也就是说,实际上很难注意编码过程。编码发生在输出上,编码后的值不应存储在数据库或服务器上,尤其是在大数据环境中的云计算中。

结论

本章介绍了 web 应用中出现的三种常见攻击:带有重定向攻击的端口转发、SQL 注入和跨站点脚本攻击。

本章结束后,您现在可以

  • 识别 web 应用中的三种常见攻击。

  • 通过提供编程语言功能的最新支持,以专业的方式处理漏洞。

  • 理解 SQL 注入的行为方式以及如何遭遇它。

  • 通过了解可能发生的灾难,了解通过端口转发和重定向攻击暴露应用的危险。

  • 了解什么是跨站点脚本以及它们的行为方式。

文献学

  1. 使用 Razor 语法(C#)的 ASP.NET Web 编程介绍。在线可用: https://docs.microsoft.com/en-us/aspnet/web-pages/overview/getting-started/introducing-razor-syntax-c

  2. 布兰登·佩里。Gray Hat C# -创建和自动化安全工具的黑客指南。无淀粉出版社,2017。

  3. 亚当·弗里曼。Pro ASP.NET Core 3–使用 MVC Blazor 和 Razor Pages 开发云就绪型 Web 应用。2020 年出版。

  4. 斯蒂芬出没。应用密码学于 .NET 和 Azure Key Vault 。Apress,2019。

  5. 罗伯特·西斯拉。面向组织和个人的加密——当代和量子加密基础知识。2020 年出版。

二十三、文本特征

在本章中,我们将分析密码和明文分析的两个重要指标:卡方统计和模式搜索(单字母组合词、双字母组合词和三字母组合词)。当使用经典和现代密码学时,文本特征化技术是密码分析技巧包中非常重要的一部分。

卡方统计量

卡方统计是计算两个概率分布之间相似性百分比的重要度量。当卡方统计的结果等于 0 时,有两种情况:这意味着两个分布相似,如果分布非常不同,将输出更高的数字。

卡方统计由以下公式定义:

$$ {\chi}²\left(C,E\right)=\sum \limits_{i=A}^{i=Z}\frac{{\left({C}_i-{E}_i\right)}²}{E_i} $$

在清单 23-1 中,我们计算了一个卡方分布的例子。

using System

namespace ComputeChiSquaredStatistics
{
   class ComputeChiSquaredStatistics
   {
      static void Main(string[] args)
      {
         int number_of_experiments=10000;
         int number_of_stars_distribution=100;

         Random theGenerator = new Random();
            double theDistribution(6.0);

         int[] probability = new int[10];

         for (int counter=0; counter
             <number_of_experiments; ++counter)
         {
               double no =
                   theDistribution(theGenerator);
              if ((no>=0.0)&&(no<10.0))
                 ++ probability [int(no)];
        }

           Console.Writeline("The Chi-Squared
            Distribution (6.0):");

            for (int index = 0; index < 10; ++index)
            {
                Console.WriteLine("index {0} ", index, " -- {1}: ",  (index + 1), ":");
                Console.WriteLine("{0}", probability[index] * number_of_stars_distribution / number_of_experiments);
            }

            Console.ReadKey();
        }
   }
}

Listing 23-1ChiSquaredDistribution Source Code

上述实现的输出如图 23-1 所示。

img/493660_1_En_23_Fig1_HTML.jpg

图 23-1

卡氏分布输出

卡方分布的例子如何帮助我们进行密码分析和加密?

第一步是计算密文中字符的出现频率。第二步是比较用于加密的假设语言(例如英语)的频率分布,并使两个频率分布彼此相关。这样我们就可以找到加密过程中使用的移位。这个过程是一个标准和简单的过程,可用于密码,如凯撒密码。当英文字符的频率与密文的频率一致时,就会发生这种情况。我们知道英语字符出现的概率

作为一个例子,让我们考虑下面这个用凯撒密码加密的例子,它有 46 个字符(字母频率见图 23-2 )。图 23-2 中的例子使用 CrypTool1T7 计算并验证加密文本上字母频率的实现及其正确性;

     ZHOFRPHWRDSUHVVWKLVLVHQFUBSWHGZLWKFDHVDUFLSKHU

理解卡方统计是基于计数而不是概率是非常重要的。例如,如果我们有一个出现概率为 0.127 的字母 E,则预期在 100 个字符内出现 12.7 次。

img/493660_1_En_23_Fig2_HTML.jpg

图 23-2

加密文本的字母频率

为了计算预期的计数,密文的长度必须乘以概率。上面的密码共有 46 个字符。根据上面 E 的统计,我们的期望是 E 字母出现 46 0.127 = 5.842 次。

为了解开凯撒密码,我们需要使用 25 个可能的密钥中的每一个,使用字母或字母在字母表中的位置。为此,计数从 0 或 1 开始非常重要。必须为每个键计算卡方。该过程包括将一个字母的计数与如果该文本是英语,我们可以预期的计数进行比较。

为了计算我们的密文的卡方统计量,我们对每个字母进行计数,我们看到字母 H 出现了 7 次。如果使用的语言是英语,应该出现 46 0.082 = 3.772 次。根据输出,我们可以计算出:

$$ \frac{{\left(7-3.772\right)}²}{3.772}=\frac{3.228²}{3.772}=\frac{10.420}{3.772}=2.762 $$

对剩余的字母进行该程序,并在所有概率之间进行加法运算(见图 23-3 )。

一旦密文被解密,明文应该是

          WELCOMETOAPRESSTHISISENCRYPTEDWITHCAESARCIPHER

img/493660_1_En_23_Fig3_HTML.jpg

图 23-3

加密字母频率(%) 2

使用 Monogram、Bigram 和 Trigram 频率计数的密码分析

频率分析是找到密文字符出现次数的最佳实践之一,目的是破解密码。模式分析可用于将字符作为二元模型(或有向图)进行测量和计数,这是一种测量文本中出现的字符对的方法。还有三元语法频率分析,它测量由三个字母组成的组合的出现率。

在这一节中,我们将重点关注使用二元模型和三元模型的文本特征,而不是解析密码,如 Playfair。

计数字母组合

计数字母组合是替代密码中最有效的方法之一,如凯撒密码、波利比乌斯方块等。这种方法非常有效,因为英语有特定的频率分布。这也意味着它没有被替换密码隐藏。分布将如图 23-4 和列表 23-2 所示。

img/493660_1_En_23_Fig4_HTML.jpg

图 23-4

英语的字母频率

using System;
using System.IO;

class Program
{
    static void Main()
    {
        //** we use the array to store the frequencies
        int[] frequency = new int[(int)char.MaxValue];

        //** look at the content of the text file
        string s = File.ReadAllText("TheText.txt");

        //** go through each of the characters
        foreach (char t in s)
        {
            //** store the frequencies as a table
            frequency [(int)t]++;
        }

        //** write all letters that have been found
        for (int letterPos = 0; letterPos <
                                (int)char.MaxValue; letterPos++)
        {
            if (c[letterPos] > 0 &&
                char.IsLetterOrDigit((char)letterPos))
            {
                Console.WriteLine("The Letter: {0}
                                        has the frequency: {1}",
                    (char)letterPos,
                    freq [letterPos]);
            }
        }
    }
}
}

Listing 23-2Counting Monograms

输出如图 23-5 所示。

img/493660_1_En_23_Fig5_HTML.jpg

图 23-5

计数字母组合的输出

清单 23-3 显示了进行上述计数的高级示例。这是一个使用 LINQ 和λ表达式的高级例子。图 23-6 显示了输出。

img/493660_1_En_23_Fig6_HTML.jpg

图 23-6

使用 LINQ 和拉姆达表达式的字符频率

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

namespace MonogramsCounting_LINQ{
    class Program{
        static void Main(){
            var frequencies = from c in
   File.ReadAllText("TheText.txt")
              group c by c into groupCharactersFrequencies
              select groupCharactersFrequencies;

            foreach (var c in frequencies)
                Console.WriteLine($"The character: {c.Key} has
the frequency: {c.Count()} times");

            Console.ReadKey();}}}

Listing 23-3Lambda LINQ Expression for Counting Letter Frequencies

计算二元模型

计算二元模型的方法基于与计算单字母模型相同的思想。计算二元模型意味着计算成对字符的出现频率,而不是计算单个字符的出现次数。

图 23-7 列出了密码分析过程中发现的一些常见二元模型。在清单 23-4 中,我们实现了一个处理二元模型出现次数的解决方案。图 23-8 显示输出。

img/493660_1_En_23_Fig8_HTML.jpg

图 23-8

清单 23-4 的输出

img/493660_1_En_23_Fig7_HTML.jpg

图 23-7

二元模型的例子

class Program
    {
        static void Main(string[] args)
        {
            String text = "Welcome to Apress! This book is
            about cryptography and C#.";
            String bigramPattern = "to";
            Console.WriteLine("The number of occurrences of
                  \"" + bigramPattern + "\" in \"" + text +
                  "\" is: " +
                 countFrequenciesBigrams(bigramPattern,
                                       text).ToString());

        }

        static int countFrequenciesBigrams(String
                                bigramPattern, String text)
        {
            int bigramPatternLength = bigramPattern.Length;
            int textLength = text.Length;
            int occurrences = 0;

            for (int idx = 0; idx <= textLength –
                               bigramPatternLength; idx++)
            {
                int jIdx;
                for (jIdx = 0; jIdx < bigramPatternLength;
                                                   jIdx++)
                {
                    if (text[idx + jIdx] !=
                                    bigramPattern[jIdx])
                    {
                        break;
                    }
                }

                if (jIdx == bigramPatternLength)
                {
                    occurrences++;
                    jIdx = 0;
                }
            }
            return occurrences;
        }
    }

Listing 23-4Computing Bigrams

计数三元模型

三元组计数使用与二元组计数相同的原理。区别在于计算三重字符。

图 23-9 列出了密码分析过程中遇到的一些常见二元模型。在清单 23-5 中,我们实现了一个解决方案,用于查找和计算文本中三元模型的出现次数。该解决方案与清单 23-4 中的解决方案不同。输出如图 23-10 所示。

img/493660_1_En_23_Fig10_HTML.jpg

图 23-10

清单 23-5 的输出

img/493660_1_En_23_Fig9_HTML.jpg

图 23-9

三元模型的例子

class Program
    {
        static void Main(string[] args)
        {
            String fullText = "Welcome to Apress! This book is
            about cryptography and C#.";
            String trigramPattern = "Apr";
            Console.WriteLine("Full text: " + fullText);
            Console.WriteLine("Trigram: " +
                         trigramPattern + "\n");
            Console.WriteLine("The number of occurrences of
\"" + trigramPattern + "\" in \"" +
fullText + "\" is: " +
countFrequenciesTrigrams(trigramPattern,
fullText).ToString());

        }

        static int countFrequenciesTrigrams(String
                        trigramPattern, String fullText)
        {
            int trigramPatternLength = trigramPattern.Length;
            int fullTextLength = fullText.Length;
            int noOfOccurrence = 0;

            for (int index = 0; index <= fullTextLength –
                           trigramPatternLength; index++)
            {
                int jIndex;
                for (jIndex = 0; jIndex <
                          trigramPatternLength; jIndex++)
                {
                    if (fullText[index + jIndex] !=
                                  trigramPattern[jIndex])
                    {
                        break;
                    }
                }

                if (jIndex == trigramPatternLength)
                {
                    noOfOccurrence++;
                    jIndex = 0;
                }
            }
            return noOfOccurrence;
        }
    }

Listing 23-5Counting Trigrams

生成信函频率

数组wikiFrequencies存储维基百科 3 中列出的频率(字母的相对百分比)(见清单 23-6 )。提供的示例将数字解释为百分比。例如,字母“A”出现的频率为 8.167%。

图 23-11 中的程序声明了一个Random对象。字母的定义值(如“A”)是一个整数。我们将在以后使用这个约定。事件处理器Load对来自wikiFrequencies.的所有值求和

img/493660_1_En_23_Fig11_HTML.jpg

图 23-11

字母频率的生成

using System;
using System.Linq;

namespace LetterFrequency
{
    class Program
    {
        static void Main(string[] args)
        {
            string input = "";
            VerifyFreq();

            Console.Write("The number of letters: ");
            input = Console.ReadLine();

            Compute(input);
        }

        //** Store the letter frequencies.
        //** For more details and the values
        //** stored below, see the link:
        //** http://en.wikipedia.org/wiki/Letter_frequency
        private static float[] wikiFrequencies =
        {
            8.167f, 1.492f, 2.782f, 4.253f, 12.702f,
            2.228f, 2.015f, 6.094f, 6.966f, 0.153f,
            0.772f, 4.025f, 2.406f, 6.749f, 7.507f,
            1.929f, 0.095f, 5.987f, 6.327f, 9.056f,
            2.758f, 0.978f, 2.360f, 0.150f, 1.974f,
            0.074f

        };

        //** create a instance of a number
        //** generator using Random class
        private static Random randomNumber = new Random();

        //** compute the ASCII value of letter A
        private static int int_AsciiA = (int)'A';

        //** verify that the frequencies are adding up to 100
        private static void VerifyFreq()
        {
            //** compute the difference to E
            float totalAmount = wikiFrequencies.Sum();
            float differenceComputation = 100f - totalAmount;
            wikiFrequencies[(int)'E' - int_AsciiA] +=
                         differenceComputation;
        }

        //** based on the frequencies
        //** generate randomly the letters
        private static void Compute(string txtNumLetters)
        {
            //** monitor and track each letter
            //** that has been generated
            int[] countGeneratedLetters = new int[26];

            //** randomly generate the letters
            int theNumberOfLetters = int.Parse(txtNumLetters);
            string result = "";
            for (int k = 0; k < theNumberOfLetters; k++)
            {
                //** randomly generate a number
                //** between 0 and 100
                double randomlyNumber = 100.0 *
                randomNumber.NextDouble();

                //** select the letter that
                //** this will represents
                for (int numberOfLetter = 0; ;
                                    numberOfLetter++)
                {
                    //** extract the frequency of the
                    //** letter from the number

                    randomlyNumber -=
                       wikiFrequencies[numberOfLetter];

                    //** if the randomly number is
                    //** less and equal than 0
                    //** it means that we have the letter
                    if ((randomlyNumber <= 0) ||
                              (numberOfLetter == 25))
                    {
                        char character = (char)(int_AsciiA +
                                           numberOfLetter);
                        result += character.ToString() + ' ';
                      countGeneratedLetters[numberOfLetter]++;
                        break;
                    }
                }
            }

            Console.WriteLine(result + "\n");

            //** show the frequencies
            for (int i = 0; i < countGeneratedLetters.Length;
                                                  i++)
            {
                char ch = (char)(int_AsciiA + i);
                float frequency =
(float)countGeneratedLetters[i] /
theNumberOfLetters * 100;
                string str =
string.Format("{0}\t{1,6}\t{2,6}\t
{3,6} ,ch.ToString(),
frequency.ToString("0.000"),
wikiFrequencies[i].ToString("0.000"),
(frequency –
wikiFrequencies[i]).ToString("0.000"));

                Console.WriteLine(str);
            }
        }
    }
}

Listing 23-6Randomly Generating Letter Frequencies

结论

本章介绍了文本特征的概念,并展示了它在密码分析过程中的重要性。现在,您可以处理卡方统计,并使用单字母组合词、双字母组合词和三字母组合词来解密替代密文。作为总结,您了解了

  • 文本特征的概念

  • 使用字母组合、二元组合和三元组合

  • 卡方统计的实现

  • Monogram、digram 和 trigram 实现

文献学

  1. 西蒙·辛格,《密码本:从古埃及到量子密码术的秘密科学》。国际标准书号 0-385-49532-3。2000.

  2. 《密码分析:对密码及其解决方案的研究》, 1989 年。

二十四、密码分析方法的实现和实用途径

在这一章中,我们想提出一个密码分析方法的方法论,以及如何快速有效地应用它。该方法用于经典和实际(现代)密码术/密码分析算法和方法。目前不包括量子密码术。

所提出的方法(见图 24-1 )旨在让密码分析人员知道他们在工作中的位置和位置,以及他们可以相应地使用什么工具或方法。

img/493660_1_En_24_Fig1_HTML.jpg

图 24-1

密码分析方法

如果您没有关于加密方法的适当信息,实现密码分析方法是一项非常棘手的任务。也就是说,密码分析过程包括两个一般步骤。第一步包括确定应该执行哪种密码分析,第二步包括我们所知道的密码算法。基于这两个步骤,我们可以转到步骤 3* 来构建合适的攻击模型步骤 4 来选择合适的工具。*

第一步。应该进行什么样的密码分析?在这里,密码分析员决定,连同他们的商业环境,他们将扮演什么样的角色:一个合法的和授权的密码分析员(道德黑客)或一个恶意的(黑客)。一旦他们决定了自己的角色,他们就进入第二步。

第二步。如果他们是一个合法的密码分析者,在开始之前有两件事他们应该知道:?? 密码算法和 ?? 密钥。根据一些密码分析者,这不是一个必要的要求,但在某些情况下,知道这一点是非常有用的。一旦知道了这两件事,他们就可以很容易地执行密码分析方法,并测试业务的安全性。

第三步。当攻击模型或攻击类型对加密消息执行破解方法时,它们将为密码分析能够获得多少信息设置一个量化变量。最常用的攻击模型有

  • 唯密文攻击

  • 已知明文攻击

  • 选择明文攻击

  • 选择密文攻击

    • 自适应选择密文攻击

    • 无差别选择密文攻击

第四步。一旦选择了攻击模型,或者根据情况和要求创建并调整了另一个模型,下一步就是选择软件工具。有两种方法,从已经存在的工具中选择或者创建自己的工具(这很费时间,但是很好的实践)。下面是一些我们可以在密码分析过程中使用的工具的例子,这取决于我们试图“测试”什么

  • 渗透工具:Kali Linux,Parrot Security,BackBox

  • 法医:DEFT,CAINE,BlackArch,Matriux

  • 数据库:sqlmap(独立版)、Metasploit 框架(独立版)、VulDB

  • Web 和网络:Wireshark、Nmap、Nessus、Burp Suite、Nikto、OpenVas

  • 其他工具:CryptTool(非常有用和神奇的工具)

上面提到的工具只代表了那些非常强大并且能够产生预期结果的工具中的一部分。

纯密文攻击(COA)

COA 是最弱的攻击之一,因为密码分析者可以很容易地使用它,因为他只是对消息进行编码。

攻击者(密码分析员)将有权访问一组密文。如果相应的明文和密钥一起被推断出来,攻击就被认为是成功的。

在这种类型的攻击中(见图 24-2 ),攻击者/密码分析师能够观察到密文。他们所看到的是一组被打乱的无意义的字符,这些字符被表示为加密过程的输出。

img/493660_1_En_24_Fig2_HTML.jpg

图 24-2

COA 表示

已知明文攻击

这种攻击(见图 24-3 )赋予了密码分析者生成密文的能力,因为他知道密文。

img/493660_1_En_24_Fig3_HTML.jpg

图 24-3

KPA 表示法

密码分析员会选择明文,但他们会注意到由明文和密文组成的密码对。与 COA 相比,成功的机会更大。简单的密码很容易受到这种攻击。

选择明文攻击

密码分析员选择使用加密算法发送的明文,并观察密文是如何生成的。这可以看作是一种主动模型,其中密码分析者有机会选择明文并实现加密。

由于可以选择任何明文,密码分析者也可以观察到密文的细节,这给了他很大的优势来理解算法内部是如何工作的,并有机会获得密钥。

一个专业的密码分析人员将拥有一个数据库,其中包含已知的明文、密文和可能的密钥(参见清单 24-1 和图 24-5 中自动生成可能密钥的示例;这是一个说明要点的非常简单的例子),并使用它们来确定密文输入(见图 24-4 )。

img/493660_1_En_24_Fig4_HTML.jpg

图 24-4

注册会计师代表

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

namespace GeneratingKeysDatabase
{
    class Program
    {
        public static string size = Console.ReadLine();
        public static int values_based_on_length =
            Convert.ToInt32(size);
        public char first_character = 'a';
        public char last_character = 'z';
        public int string_length = values_based_on_length;

        static void Main(string[] args)
        {
            var writting_password = new Program();
            writting_password.WrittingPasswordsAndKeys(" ");
            Console.ReadLine();
        }

        //** automatically generates the
        //** passwords and create a file
        private void WrittingPasswordsAndKeys(string
                                              cryptographic_passwords)
        {
            //** location and file name that
            //** contains the passwords
            string file = "passwords_database.txt";

            //** add on each row a new password
            File.AppendAllText(file, Environment.NewLine +
                                              cryptographic_passwords);

            //** display it on the console
            Console.WriteLine(cryptographic_passwords);

            //** don't do anything if the length of the
            //** passwords is equal with the length of
            //** the string and continue with generating
            //** the passwords and keys
            if (cryptographic_passwords.Length ==
                                                 string_length)
            {

                return;
            }
            for (char c = first_character; c <=
                                                last_character; c++)
            {
                  WrittingPasswordsAndKeys(
                                           cryptographic_passwords + c);
            }
        }
    }
}

Listing 24-1Automatic Generation of Random Keys

img/493660_1_En_24_Fig5_HTML.jpg

图 24-5

生成的密钥和可能的密码。我们选择三个字符只是为了短时间处理

选择密文攻击

密码分析员有机会加密和解密信息。在这种攻击中(见图 24-6 ),他们有能力选择明文,为其提供加密,观察密文是如何生成的,并反转整个过程。在这种攻击中,密码分析人员还将试图找到用于加密的算法和密钥。

img/493660_1_En_24_Fig6_HTML.jpg

图 24-6

CCA 代表

结论

在这一章中,我们讨论了如何实现密码分析方法以及如何为密码分析者定义这样的过程。在本章结束时,您将能够

  • 对攻击模型有很好的理解

  • 遵循简单直接的方法来了解您在密码分析过程中的位置

  • 用密钥和可能的密码模拟并生成一个数据库

文献学

  1. Abu Yusuf yaqub ibn ishaq al-sabbah al-kindiwww.trincoll.edu/depts/phil/philo/phils/muslim/kindi.html

  2. 哲学家:亚库卜·伊本·伊扎克·肯尼迪日、k·金迪、阿布·优素福·亚库卜·伊本·伊沙克(草 866-73)。 www.muslimphilosophy.com/ip/kin.html

  3. Ahmad Fouad Al-Ehwany,穆斯林哲学史第 1 卷中的“Al-Kindi”。新德里:低价出版物。第 421-434 页。1961.

  4. Ismail R. Al-Faruqi 和 Lois Lamya al-Faruqi,伊斯兰文化图集,纽约:麦克米伦出版公司。第 305-306 页。1986 年大英百科全书公司(1969 年)。大英百科全书。芝加哥:威廉·本顿。

  5. J.J .奥康纳和 E.F .罗伯逊,E.F. 阿布·优素福·雅各布·伊本·伊斯哈格·萨巴·金迪。1999.

第一部分:基础主题

第二部分:密码

第三部分:密码分析

posted @ 2024-08-10 19:02  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报