Python 密码学实用指南(全)(4)

简介: Python 密码学实用指南(全)

Python 密码学实用指南(全)(3)https://developer.aliyun.com/article/1507501

填充预言攻击

在本节中,我们将看到 PKCS#7 系统中填充的工作原理,然后向您展示带有PADDING ERROR消息的系统。此外,我们还将处理填充预言攻击,这使得可能制作解码我们想要的 20 个明文的密文。

这是加密例程:


我们将有三个数据块,每个块长 16 字节。我们将使用 AES 在 CBC 模式下加密数据,因此初始化向量和密钥就会出现。你会产生三个密文块,第一个块之后的每一个块都使用前一个加密例程的输出作为初始化向量与明文进行异或。

这是 PKCS#7 填充的工作方式:

  • 如果需要一个字节的填充,使用01
  • 如果需要两个字节的填充,使用0202
  • 如果需要三个字节的填充,使用030303
  • 等等…

如果我们这里的消息只有 47 个字节长,那么我们无法填满最后一个块,所以我们必须添加一个字节的填充。你可以使用各种数字作为填充,但在这个系统中,我们使用一个二进制值 1,如果你需要一个字节的填充,如果你需要两个字节,你就用两个字节,如果你需要三个字节的填充,你就用三个字节。这意味着,如果我们解密它,我们将得到三个密文块。我们解密它,我们将得到 47 字节的消息:


这里的最后一个字节将始终是填充字节,即0-1,二进制值为1

这是一个易受攻击的系统的示例。这只是使用我们以前制作的相同技术,我们只是用 AES 和 CBC 模式加密东西,你可以保存在pador.py中,然后你可以导入它以使其易于使用和更加现实。已经有真实的系统使用了这个。所以,我们导入,加密和解密方法,以便我们可以输入一个 47 个字节的消息并对其进行加密。我们将得到一个长长的十六进制输出。

如果我们解密它,我们将得到我们原始的输入加上一个字节的01。x01 是 Python 表示法,表示二进制值为1的单个字节。如果你修改输入,保持前 47 个字节不变,并将最后一个字节更改为A65并解密它,你将得到一个填充错误。这个错误消息看起来可能无害,但实际上它可能完全颠覆加密。

让我们来看看:

  1. 打开终端并启动python
  2. 我们将输入以下命令:


  1. 我们将加密和解密例程。你可以看到我们有明文。当我们加密 47 个字节的明文时,我们得到一个长长的二进制块:
941dc2865db9204c40dd6f0898cbe0086fc6d915e288ed4ef223766a02967b81c6c431778a40f517e9e4aa86856e0a3b68297e102b1ec93713bf89750cdfa80e
  1. 当我们解密时,我们得到以下结果:


我们可以看到,它实际上在末尾添加了一个字节的填充。

现在,我们应该做变形的。如果我们将我们修改后的文本设置为原始明文,直到第 47 个字符,然后我们在末尾添加"A",当我们解密它时,我们得到'PADDING ERROR'


这是我们可以利用来颠覆系统的错误消息。所以,填充预言攻击的工作方式如下更改:

  1. 将密文[16:31]更改为任何字节
  2. 更改密文[31]直到填充有效。
  3. 中间[47]必须是1

这是 CBC 的图示:


保持密文的前 16 个字节不变。将其更改为任何你喜欢的东西,比如全 A,然后解密。会发生的是,因为你改变了第二个块中的字节,第二个块将变成随机字符,第三个块也是如此。但除非最后一个块的最后一个字节是 1,否则会出现填充错误。所以,你可以用穷举法。你将一个字节更改为所有 256 个可能的值,直到该字节变为1,当发生这种情况时,你就知道这个值是1。你知道这个值,因为它没有给你一个填充错误消息,你可以对它们进行异或运算,以确定这个中间值。因此,逐个字节向左进行,你可以确定这些中间值。如果你知道它们,你可以输入密文,使得你喜欢的任何东西出现在第三个块中。因此,即使你不知道密钥或初始化向量,你也可以打败加密。

这是执行此操作的代码:


并将得到以下输出:


我们将密文设置为原始密文的前 16 个字节,然后是 15 个A。然后,我们改变下一个字节的所有可能的256个值,并且保持第三个数据块不变。之后,我们查看何时不再出现填充错误,那将是234,因此中间值是234异或一:

  1. 现在,如果我们想要得到下一个字节,我们必须安排两个字节的填充,两者都将是2,如下所示:


因此,密文的最后两个字节4647都将是两。因此,我们将密文31设置为创建两个所需的值。现在我们知道中间值,我们可以计算它。

  1. 我们改变密文30直到填充有效,这将确定中间的下一个字节:


  1. 保持第一个块不变,并添加 14 个字节的变化下一个字节的填充。保持所选值为233的字节,这样你就知道解密输出的最后一个字节将是2,当填充错误消息消失时,你可以拿这个数字,与2异或,得到中间值的下一个值。因此,现在我们可以制作消息。我们必须重复这个过程更多次以获得更多字节,但是对于这个演示,我们将只接受一个字母长的消息。我们将制作一个以A开头,后面跟着一个二进制值为1的有效填充。这是我们的目标,为了做到这一点,我们只需要将密文3031设置为这些选择的值:
  • ciphertext[30] = ord("A") ^ 113
  • ciphertext[31] = 16 235
  1. 因为我们知道中间值是113235,我们只需要用我们想要的值异或这些中间值。
  2. 我们将创建一个解密为以A结尾和二进制1的消息的密文,让我们看看它是如何进行的。现在,这个有点复杂,所以我们选择在文本编辑器中保存一些文本,这样我们可以逐个阶段地进行:


  1. 这是我们的 Python 代码:
>>> from pador import encr, decr 
>>> prefix = c[0:16] + "A"*14
>>> for i in range(256):
...   mod = prefix + chr(i) + chr(233) + c[32:]
...   if decr(mod) != "PADDING ERROR":
...     print i, "is correctly padded"
  1. 好的,我们导入了库,我们已经有了。在这里,我们保持前 16 个字节不变,并用 15 个A填充。然后,我们有一个循环,改变下一个字节的每个可能的值,并保持第三个数据块不变。我们运行循环,直到不再出现填充错误。这告诉我们234是给我们正确填充的值:
234 is correctly padded
  1. 因此,我们将234带到1,这告诉我们中间值,所有的缩进都被切断了,所以是234异或1。这告诉我们值是235。这是中间值。对于下一个位,使用非常相似的过程,所以现在我们有 14 个字节的填充。我们将改变下一个字节,接下来的字节是233,始终选择为2。因此,当我们通过这个循环运行时,它在115处被正确填充:
...
115 is correctly padded
  1. 因此,115异或2113
>>> 115 ^ 2
113

因此,113是中间值的下一个字节。

  1. 现在我们知道这两个数字235113,我们可以控制明文的最后两个字节。现在我们将保持输入数据的第一个块不变。我们有 14 个字节的填充:
>>> prefix = c[0:16] + "A"*14 
>>> c30 = ord("A") ^ 113 
>>> c31 = 1 ^ 235 mod = prefix + chr(c30) + chr(c31) + c[32:] 
>>> decr(mod)
  1. 我们选择用两个字节235113来制作A和一个二进制1。当我们创建修改后的密文并解密它时,我们得到以下消息:
"This simple sent\xc6\x8d\x12;y.\xdc\xa2\xb4\xa9)7c\x95b\xd1I\xd0(\xbb\x1f\x8d\xebRlY'\x17\xf6wA\x01"

数据的第一个块没有被修改。第二个块和大部分第三个块已经改变为随机字符,但我们控制了最后两个字节,我们可以让它们说我们想要的任何东西。因此,我们能够创建一个解密至少部分为我们选择的两个值的密文,即使我们不知道密钥或初始化向量。

使用 RSA 进行强加密

在本节中,我们将介绍公钥加密、RSA 算法以及在 Python 中的实现。

公钥加密

在公钥加密中,我们解决了这个问题:例如,谷歌想要从用户那里接收机密数据,例如密码和信用卡号,但他们没有安全的通信渠道;他们拥有的是公共互联网,发送的任何数据都可能被任意数量的攻击者窃听。因此,没有办法交付共享的秘密密钥,对称加密算法,例如 AES,无法解决这个问题。这就是公钥加密的作用。

谷歌创建了一对密钥。他们保持私钥保密,不告诉任何人,并且公开公钥,以便任何人都可以知道。想要向谷歌发送秘密信息的人可以用公钥加密它们,然后通过不安全的渠道发送,因为唯一能解密的是谷歌,谷歌拥有私钥。邮箱的工作原理就是这样。任何人都可以去邮箱把信放在顶部槽里,但底部的门是锁着的,只有拥有私钥的邮递员才能把信拿出来。私钥和公钥必须有关联,但它们必须通过单向函数相关联,以便从私钥轻松计算出公钥,这是谷歌在首次设置密钥对时必须做的。但是从公钥计算出私钥必须非常困难,因此公开公钥是安全的,没有人会找到私钥。

RSA 算法

有各种单向函数可以用于此目的,但在 RSA 中,该函数是分解一个大数:

  • 私钥d由两个大素数pq组成
  • 公钥是n = p * q的乘积,以及任意值e
  • 如果pq很大,将n分解为pq是非常困难的

如果将两个素数pq相乘以创建它们的乘积n,那么将n分解为pq是一个众所周知的困难问题。如果pq足够大,这几乎是不可能的。这就是单向函数。你可以轻松地将pq相乘以创建公钥n,但是公钥的知识不能用于实际确定pq

  • 公钥:这是两个数字(n,e)
  • e可以是任何素数,通常是65537
  • 加密y = x^(e)mod n
  • 解密x = y^d mod n
  • x是明文,y是密文

因此,公钥是n,它是两个素数的乘积和另一个任意数e,通常只是这个值65,537。任何希望秘密发送明文x的人,将其提升到e的幂,模n,并将其加密的内容发送到不安全的渠道,例如互联网,给接收者。接收者有私钥,因此可以找到解密密钥d,并将密文取模n,然后变成解密的消息。解密密钥是这样计算的:

  • phin = (p-1) * (q-1)
  • d*e = 1 mod phin

由于 Google 知道pq的秘密,他们可以计算出这个数字phin,即p - 1乘以q - 1,然后他们选择一个解密密钥,使得d乘以ePhin取模等于1。其他人无法进行这种计算,因为他们不知道pq的值。因此,在 Python 中,您可以导入RSA模块,然后生成任意长度的密钥。在这个例子中,我们使用了2048位,这是当前国家标准研究所的推荐。然后,他们有一个公钥。有一条要加密的消息,你加密它,结果是这个非常长的密文,长度为2048位。密文很长,计算速度很慢,所以你通常不会用这种方法发送长消息。在 RSA 中,你只需要发送一个秘密密钥,然后你使用 AES 来加密之后的所有内容,以加快计算速度。本章介绍了一种称为教科书 RSA 的东西,其中包含许多基本要素,但实际上并不足够安全,因为你必须添加一个在 RFC 8017 中指定的填充。这会向消息添加哈希值、掩码和填充,并保护密钥免受一些攻击。让我们在 Python 中看一下这个。

Python 中的实现

这是我们如何在 Python 中实现我们所讨论的内容:

  1. 我们启动python,然后添加以下代码:


  1. 所示的最后一步大约需要 2 到 4 秒钟才能生成密钥;这是因为它必须找到两个大素数,而这些是非常困难的计算:


  1. 它必须猜一个数字并测试它,通常情况下,它必须为每个大素数尝试超过一百次猜测,因此这个过程非常耗时。但是,这是自动发生的,现在我们可以用密钥加密消息,生成这个非常长的密文:


  1. 现在,我们可以测试一下,看看我们是否改变了消息的一个比特,或者将明文的最后一个字母改为f。如果我们加密这个,结果将类似于以下内容:
>>> plain = 'encrypt this messagf'
>>> ciphertext = publicKey.encrypt(plain, 0) [0
... ciphertext = publicKey.encrypt(plain, 0) [0
keyboardInterrupt
>>> ciphertext = publicKey.encrypt(plain, 0) [0]
>>> print ciphertext.encode ("hex")
  1. 现在,我们打印结果:


正如您所看到的,所有的4ac都变成了1dc,然后结束于578633。这是强加密的理想特性。输入的任何更改都会改变所有输出,剪辑大约一半的位数。

挑战-用类似的因子破解 RSA

在本节中,我们将涵盖诸如大整数-在 Python 和decimal库中的主题。我们还将看一个大数因式分解的例子,然后为您提供两个挑战来解决。

Python 中的大整数

Python 可以进行乘法和除法-并且可以完全精确地进行任意大的整数的乘法和除法:


如果我们有1001,然后计算1001的平方,我们当然会得到正确的答案;即使我们取一个像10**100 + 1这样的数字,它也能正确地得到这个数字的一百位数,每一端都是1。现在,如果我们再对这个数字求平方,它也能正确地得到它的一百位数,每一端都是1

因此,对于简单的整数运算,Python 的精度是无限的。但是,如果我们想要平方根,我们需要导入math库:


正如您在前面的代码中所看到的,math库不保留任意数量的位数。如果我们取10 **100 + 1并对其求平方,然后取平方根,我们得到的不是10 **100 + 1。我们得到的是10 ** 100,这意味着它舍入到了少于100位数的一些数字,对于许多目的来说这是可以接受的。但是,对于我们想要做的事情来说不够,我们想要因式分解大整数。

为了做到这一点,您使用decimal库,并按照所示导入它:


正如你所看到的,我们已经导入了decimal库,并将a的值设置为10 **100+ 1。这里b等于 a 的平方,然后不是使用math库计算b的平方根,而是使用decimal库计算b的十进制值。使用它的平方根方法,这会再次给出错误的答案,因为默认情况下,decimal库会四舍五入。但是如果将精度设置得更高,你将得到完全正确的答案,这就是为什么decimal库对我们的目的更好。这个getcontext().prec命令让我们设置它保留足够的位数,以便我们想要的精度。

好的,所以,在一般情况下,你无法分解一个大数,这就是 RSA 安全的原因。但是,如果在使用数字时出现错误,并且以某种方式可以预测,那么 RSA 就可以被破解:


这里的错误是使用两个彼此接近的质数因子,而不是为这两个质数因子选择独立的随机数。因此,这个大数字是两个质数因子的乘积,因此你可以将其分解。因此,如果我们将该数字放入一个名为n的值中,我们将精度设置为50位并计算平方根。我们发现平方根是1后面跟着许多个零,然后以83结束+一个分数。

现在,如果这个数字是两个质数的乘积,并且这两个质数彼此接近,一个数字必须小于平方根,另一个数字必须大于平方根。

因此,如果我们从平方根开始,每次向后跳两个数字,尝试接近平方根的数字,我们最终会找到质数因子,我们找到了:


当然,我们可以向后跳两步,因为偶数肯定不是质数,所以我们不需要测试偶数。

正如我们所看到的,现在我们找到了一个数字,其中n模这个数字的结果为零,因此这是一个质数因子。

我们可以通过将n除以第一个质数来得到另一个质数因子:


因此,这里是原始数字n,它是两个质数的乘积,我们有其中一个质数;qn除以p,你可以看到。为了测试它,如果我们计算p*q,我们会再次得到原始数字。因此,我们已经将一个大数字分解为pq,这就足够破解 RSA 了。

所以,让我们在 Python 中尝试一下。转到终端并运行python


所以,我们有n等于所示的大数字。我们将这个数字导入decimal库,并将位置设置为50位。现在,如果我们取平方根,我们得到1后面跟着许多个零,然后是83,然后是一个分数。然后,我们复制平方根的整数部分:


现在我们将p设置在该数字的范围内,如下所示:

>>> for p in range(100000000000000000083, 100000000000000000030, -2):

这开始了一个循环,我们所要做的就是打印:

...  print p, n%p
...

它将计算np,结果将为零。如果这是一个整数倍数,按两次Enter运行循环:


所以,我们可以看到这个数字是p

100000000000000000039 0

如果我们复制那个数字,我们可以将p设置为那个数字,然后将q设置为n除以p

>>> p = 100000000000000000039
>>> q = n/p

如果我们打印,我们将得到以下结果:


你可以看到np*q匹配。所以,我们现在已经将那个长数字分解为了它的互补质数。

这是第一个挑战:


这是第二个挑战:


在这两种情况下,你都能够将它们分解。

接下来呢?

物联网IoT)有着光明的未来,很快将连接数十亿的设备。对于物联网,安全一直是一个主要关注点。但好消息是,加密为保护物联网免受黑客攻击提供了各种选择;因此,这是物联网即将到来的时代的关键。

物联网中的加密

当我们谈论在物联网中使用加密时,我们谈论的是在通信堆栈的许多层上使用加密。如果我们看一下 OSI 模型,我们可以看到加密在第 2 层及以上使用,链接在第 2 层操作,网络在第 3 层操作,传输在第 4 层操作:


在应用层,加密也用于通过认证和加密来保护通信。在我们开始描述物联网协议的特定加密方法之前,让我们先谈谈现有无线协议的利用工具的现成性。随着物联网的成熟,要记住有许多工具可用于利用物联网无线通信协议,这些工具将继续迅速跟上支持物联网引入的新技术。

例如,看看 1989 年推出的 Wi-Fi 802.11,2004 年推出的 AirCrack 工具至今仍然是一款受欢迎且得到良好支持的工具。还有许多工具可用于利用蓝牙通信和蜂窝通信。

除此之外,驱动加密的密钥必须在设备(模块)级别以及整个企业范围内得到安全管理。让我们来探讨其中一些。

ZigBee 加密密钥

ZigBee 使用许多密钥进行加密操作:

  • 链路密钥:这是基于制造商预先配置的主密钥建立的。链路密钥提供了两个 ZigBee 节点之间的点对点安全连接。链路密钥还用于建立派生密钥,包括数据密钥、密钥传输密钥和密钥装载密钥
  • 密钥传输密钥:这个密钥是在使用链路密钥和 1 字节字符串 0x00 作为输入字符串执行专门的密钥散列函数的结果

ZigBee 密钥管理的复杂性

如前所述,密钥管理是具有挑战性的。让我们来看看密钥管理有多具有挑战性。例如,以 ZigBee 协议为例。在 ZigBee 网络中可以使用三种主要类型的密钥。主密钥通常由供应商预先安装,并保护两个 ZigBee 节点之间的交换,因为它们生成链路密钥。链路密钥支持节点之间的通信,网络密钥支持广播通信。

密钥管理功能可能内置于实用程序的媒体管理软件中,例如,也可能作为独立软件提供。然而,所有这些密钥在它们的整个生命周期中都需要得到充分的安全保护。

蓝牙-LE

蓝牙低功耗协议采用加密技术来配对设备以建立未来的关系。蓝牙-LE 在这些加密过程中使用各种密钥,包括长期密钥(LTK),用于生成链路层加密的 128 位密钥,以及连接签名解析密钥(CSRK),用于在 ATT 层对数据进行数字签名。

通过这一切,我们来到了本书的结尾。加密应用应该根据威胁环境进行定制。加密是基于强大、精心设计的算法,并与通信堆栈的所有层相关联。它无处不在,对物联网系统的安全至关重要。

总结

在本章中,我们介绍了 AES,这是当今常用的最强大的私钥系统,以及它的两种模式,ECB 和 CBC。我们还介绍了针对 CBC 的填充预言攻击,这是可能的,当错误消息给予攻击者比他们应该获得的更多有关加密过程的信息时。

最后,我们介绍了 RSA,这是当今用于通过互联网发送秘密的主要公钥算法,我们还研究了一个挑战,即在两个素数相似而不是独立和随机选择的情况下我们是如何破解 RSA 的。我们还研究了加密技术的未来以及它如何帮助保护物联网设备。

相关实践学习
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
相关文章
|
存储 安全 网络安全
Python网络安全与密码学
【5月更文挑战第23天】 探索Python在网络安全与密码学的实践,从加密算法(如AES、RSA)和哈希函数(MD5、SHA-256)到网络安全工具(Scapy、Socket)的应用。了解如何使用PyCryptodome和hashlib进行加密解密及哈希计算,通过Scapy进行网络数据包操作和端口扫描,利用Socket实现TCP通信。深入密码学,学习RSA加密和数字签名,以及使用Django和Flask实现安全Web开发。此外,掌握高级网络安全技术,如Wireshark流量分析、Bro/Zeek入侵检测,以及自动化安全运维(Ansible)和安全数据分析(Pandas、Matplotlib)。
|
算法 Linux 数据安全/隐私保护
Python 密码学实用指南(全)(3)
Python 密码学实用指南(全)
111 1
|
Linux 数据安全/隐私保护 iOS开发
Python 密码学实用指南(全)(1)
Python 密码学实用指南(全)
265 1
|
算法 安全 数据安全/隐私保护
Python 密码学实用指南(全)(2)
Python 密码学实用指南(全)
118 0
|
数据安全/隐私保护 Python
Python——实现密码学中的模逆运算
Python——实现密码学中的模逆运算
528 0
|
Python
Python——验证密码学常见运算
Python——验证密码学常见运算
198 0
|
安全 算法 数据安全/隐私保护

推荐镜像

更多