PUB:20160518 Python 3 - An Intro to Encryption

@Cathon 翻译的不错,很用心。
This commit is contained in:
wxy 2016-08-12 08:57:38 +08:00
parent d5f08e4b62
commit 33f340f5ee

View File

@ -1,15 +1,13 @@
Python 3: 加密简介
===================================
Python 3 的标准库中没什么用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包: PyCrypto 和 cryptography 上。我们将学习如何使用这两个库,来加密和解密字符串。
---
Python 3 的标准库中没多少用来解决加密的不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍但重点会放在两个第三方的软件包PyCrypto 和 cryptography 上。我们将学习如何使用这两个库,来加密和解密字符串。
### 哈希
如果需要用到安全哈希算法或是消息摘要算法,那么你可以使用标准库中的 **hashlib** 模块。这个模块包含了标准的安全哈希算法,包括 SHA1SHA224SHA256SHA384SHA512 以及 RSA 的 MD5 算法。Python **zlib** 模块也提供 adler32 以及 crc32 哈希函数
如果需要用到安全哈希算法或是消息摘要算法,那么你可以使用标准库中的 **hashlib** 模块。这个模块包含了符合 FIPS美国联邦信息处理标准的安全哈希算法,包括 SHA1SHA224SHA256SHA384SHA512 以及 RSA 的 MD5 算法。Python 也支持 adler32 以及 crc32 哈希函数,不过它们在 **zlib** 模块中
一个哈希最常见的用法是,存储密码的哈希值而非密码本身。当然了,使用的哈希函数需要稳健一点,否则容易被破解。另一个常见的用法是,计算一个文件的哈希值,然后将这个文件和它的哈希值分别发送。接到文件的人可以计算文件的哈希值,检验是否与接受到的哈希值相符。如果两者相符,就说明文件在传送的过程中未经篡改。
哈希的一个最常见的用法是,存储密码的哈希值而非密码本身。当然了,使用的哈希函数需要稳健一点,否则容易被破解。另一个常见的用法是,计算一个文件的哈希值,然后将这个文件和它的哈希值分别发送。接到文件的人可以计算文件的哈希值,检验是否与接受到的哈希值相符。如果两者相符,就说明文件在传送的过程中未经篡改。
让我们试着创建一个 md5 哈希:
@ -26,8 +24,6 @@ TypeError: Unicode-objects must be encoded before hashing
b'\x14\x82\xec\x1b#d\xf6N}\x16*+[\x16\xf4w'
```
Lets take a moment to break this down a bit. First off, we import **hashlib** and then we create an instance of an md5 HASH object. Next we add some text to the hash object and we get a traceback. It turns out that to use the md5 hash, you have to pass it a byte string instead of a regular string. So we try that and then call its **digest** method to get our hash. If you prefer the hex digest, we can do that too:
让我们花点时间一行一行来讲解。首先,我们导入 **hashlib** ,然后创建一个 md5 哈希对象的实例。接着,我们向这个实例中添加一个字符串后,却得到了报错信息。原来,计算 md5 哈希时,需要使用字节形式的字符串而非普通字符串。正确添加字符串后,我们调用它的 **digest** 函数来得到哈希值。如果你想要十六进制的哈希值,也可以用以下方法:
```
@ -35,7 +31,7 @@ Lets take a moment to break this down a bit. First off, we import **hashlib**
'1482ec1b2364f64e7d162a2b5b16f477'
```
实际上,有一种精简的方法来创建哈希,下面我们看一下用这种方法创建一个 sha512 哈希:
实际上,有一种精简的方法来创建哈希,下面我们看一下用这种方法创建一个 sha1 哈希:
```
>>> sha = hashlib.sha1(b'Hello Python').hexdigest()
@ -45,14 +41,11 @@ Lets take a moment to break this down a bit. First off, we import **hashlib**
可以看到,我们可以同时创建一个哈希实例并且调用其 digest 函数。然后,我们打印出这个哈希值看一下。这里我使用 sha1 哈希函数作为例子,但它不是特别安全,读者可以随意尝试其他的哈希函数。
---
### 密钥导出
Python 的标准库对密钥导出支持较弱。实际上hashlib 函数库提供的唯一方法就是 **pbkdf2_hmac** 函数。它是基于口令的密钥导出函数 PKCS#5 ,并使用 HMAC 作为伪随机函数。因为它支持加盐和迭代操作,你可以使用类似的方法来哈希你的密码。例如,如果你打算使用 SHA-256 加密方法,你将需要至少 16 个字节的盐,以及最少 100000 次的迭代操作。
Python 的标准库对密钥导出支持较弱。实际上hashlib 函数库提供的唯一方法就是 **pbkdf2_hmac** 函数。它是 PKCS#5 的基于口令的第二个密钥导出函数,并使用 HMAC 作为伪随机函数。因为它支持“加盐salt”和迭代操作你可以使用类似的方法来哈希你的密码。例如如果你打算使用 SHA-256 加密方法,你将需要至少 16 个字节的“盐”,以及最少 100000 次的迭代操作。
简单来说,盐就是随机的数据,被用来加入到哈希的过程中,以加大破解的难度。这基本可以保护你的密码免受字典和彩虹表的攻击。
简单来说,就是随机的数据,被用来加入到哈希的过程中,以加大破解的难度。这基本可以保护你的密码免受字典和彩虹表rainbow table的攻击。
让我们看一个简单的例子:
@ -66,9 +59,7 @@ Python 的标准库对密钥导出支持较弱。实际上hashlib 函数库
b'6e97bad21f6200f9087036a71e7ca9fa01a59e1d697f7e0284cd7f9b897d7c02'
```
这里,我们用 SHA256 对一个密码进行哈希,使用了一个糟糕的盐,但经过了 100000 次迭代操作。当然SHA 实际上并不被推荐用来创建密码的密钥。你应该使用类似 **scrypt** 的算法来替代。另一个不错的选择是使用一个叫 **bcrypt** 的第三方库。它是被专门设计出来哈希密码的。
---
这里,我们用 SHA256 对一个密码进行哈希,使用了一个糟糕的盐,但经过了 100000 次迭代操作。当然SHA 实际上并不被推荐用来创建密码的密钥。你应该使用类似 **scrypt** 的算法来替代。另一个不错的选择是使用一个叫 **bcrypt** 的第三方库,它是被专门设计出来哈希密码的。
### PyCryptodome
@ -86,11 +77,11 @@ pip install pycryptodome
pip install pycryptodomex
```
如果你遇到了问题,可能是因为你没有安装正确的依赖包(译注:如 python-devel或者你的 Windows 系统需要一个编译器。如果你需要安装上的帮助或技术支持,可以访问 PyCryptodome 的[网站][1]。
如果你遇到了问题,可能是因为你没有安装正确的依赖包(LCTT 译注:如 python-devel或者你的 Windows 系统需要一个编译器。如果你需要安装上的帮助或技术支持,可以访问 PyCryptodome 的[网站][1]。
还值得注意的是PyCryptodome 在 PyCrypto 最后版本的基础上有很多改进。非常值得去访问它们的主页,看看有什么新的特性。
### 加密字符串
#### 加密字符串
访问了他们的主页之后,我们可以看一些例子。在第一个例子中,我们将使用 DES 算法来加密一个字符串:
@ -116,8 +107,7 @@ ValueError: Input strings must be a multiple of 8 in length
b'>\xfc\x1f\x16x\x87\xb2\x93\x0e\xfcH\x02\xd59VQ'
```
这段代码稍有些复杂让我们一点点来看。首先需要注意的是DES 加密使用的密钥长度为 8 个字节,这也是我们将密钥变量设置为 8 个字符的原因。而我们需要加密的字符串的长度必须是 8 的倍数,所以我们创建了一个名为 **pad** 的函数,来给一个字符串末尾添加空格,直到它的长度是 8 的倍数。然后,我们创建了一个 DES 的实例,以及我们需要加密的文本。我们还创建了一个经过填充处理的文本。我们尝试这对未经填充处理的文本进行加密,啊欧,报错了!我们需要对经过填充处理的文本进行加密,然后得到加密的字符串。
译者注encrypt 函数的参数应为 byte 类型字符串,代码为:`encrypted_text = des.encrypt(padded_textpadded_text.encode('uf-8'))`
这段代码稍有些复杂让我们一点点来看。首先需要注意的是DES 加密使用的密钥长度为 8 个字节,这也是我们将密钥变量设置为 8 个字符的原因。而我们需要加密的字符串的长度必须是 8 的倍数,所以我们创建了一个名为 **pad** 的函数,来给一个字符串末尾填充空格,直到它的长度是 8 的倍数。然后,我们创建了一个 DES 的实例,以及我们需要加密的文本。我们还创建了一个经过填充处理的文本。我们尝试着对未经填充处理的文本进行加密,啊欧,报了一个 ValueError 错误我们需要对经过填充处理的文本进行加密然后得到加密的字符串。LCTT 译注encrypt 函数的参数应为 byte 类型字符串,代码为:`encrypted_text = des.encrypt(padded_text.encode('utf-8'))`
知道了如何加密,还要知道如何解密:
@ -128,7 +118,7 @@ b'Python rocks! '
幸运的是,解密非常容易,我们只需要调用 des 对象的 **decrypt** 方法就可以得到我们原来的 byte 类型字符串了。下一个任务是学习如何用 RSA 算法加密和解密一个文件。首先,我们需要创建一些 RSA 密钥。
### 创建 RSA 密钥
#### 创建 RSA 密钥
如果你希望使用 RSA 算法加密数据,那么你需要拥有访问 RAS 公钥和私钥的权限,否则你需要生成一组自己的密钥对。在这个例子中,我们将生成自己的密钥对。创建 RSA 密钥非常容易,所以我们将在 Python 解释器中完成。
@ -148,9 +138,9 @@ b'Python rocks! '
接下来,我们通过 RSA 密钥实例的 **publickey** 方法创建我们的公钥。我们使用方法链调用 publickey 和 exportKey 方法生成公钥,同样将它写入磁盘上的文件。
### 加密文件
#### 加密文件
有了私钥和公钥之后,我们就可以加密一些数据,并写入文件了。这有个比较标准的例子:
有了私钥和公钥之后,我们就可以加密一些数据,并写入文件了。这有个比较标准的例子:
```
from Crypto.PublicKey import RSA
@ -204,17 +194,15 @@ with open('/path/to/encrypted_data.bin', 'rb') as fobj:
print(data)
```
如果你认真看了上一个例子,这段代码应该很容易解析。在这里,我们先读取二进制的加密文件,然后导入私钥。注意,当你导入私钥时,需要提供一个密码,否则会出现错误。然后,我们文件中读取数据,首先是加密的会话密钥,然后是 16 字节的随机数和 16 字节的消息认证码,最后是剩下的加密的数据。
如果你认真看了上一个例子,这段代码应该很容易解析。在这里,我们先以二进制模式读取我们的加密文件,然后导入私钥。注意,当你导入私钥时,需要提供一个密码,否则会出现错误。然后,我们文件中读取数据,首先是加密的会话密钥,然后是 16 字节的随机数和 16 字节的消息认证码,最后是剩下的加密的数据。
接下来我们需要解密出会话密钥,重新创建 AES 密钥,然后解密出数据。
你还可以用 PyCryptodome 库做更多的事。不过我们要接着讨论在 Python 中还可以用什么来满足我们加密解密的需求。
---
### cryptography 包
**cryptography** 的目标是成为人类易于使用的密码学包,就像 **requests** 是人类易于使用的 HTTP 库一样。这个想法使你能够创建简单安全易于使用的加密方案。如果有需要的话,你也可以使用一些底层的密码学基元,但这也需要你知道更多的细节,否则创建的东西将是不安全的。
**cryptography** 的目标是成为人类易于使用的密码学包cryptography for humans,就像 **requests**人类易于使用的 HTTP 库HTTP for Humans一样。这个想法使你能够创建简单安全易于使用的加密方案。如果有需要的话,你也可以使用一些底层的密码学基元,但这也需要你知道更多的细节,否则创建的东西将是不安全的。
如果你使用的 Python 版本是 3.5, 你可以使用 pip 安装,如下:
@ -222,7 +210,7 @@ print(data)
pip install cryptography
```
你会看到 cryptography 包还安装了一些依赖包(译注:如 libopenssl-devel。如果安装都顺利我们就可以试着加密一些文本了。让我们使用 **Fernet** 对称加密算法它保证了你加密的任何信息在不知道密码的情况下不能被篡改或读取。Fernet 还通过 **MultiFernet** 支持密钥轮换。下面让我们看一个简单的例子:
你会看到 cryptography 包还安装了一些依赖包(LCTT 译注:如 libopenssl-devel。如果安装都顺利我们就可以试着加密一些文本了。让我们使用 **Fernet** 对称加密算法它保证了你加密的任何信息在不知道密码的情况下不能被篡改或读取。Fernet 还通过 **MultiFernet** 支持密钥轮换。下面让我们看一个简单的例子:
```
>>> from cryptography.fernet import Fernet
@ -242,9 +230,8 @@ b'My super secret message'
首先我们需要导入 Fernet然后生成一个密钥。我们输出密钥看看它是什么样儿。如你所见它是一个随机的字节串。如果你愿意的话可以试着多运行 **generate_key** 方法几次,生成的密钥会是不同的。然后我们使用这个密钥生成 Fernet 密码实例。
现在我们有了用来加密和解密消息的密码。下一步是创建一个需要加密的消息,然后使用 **encrypt** 方法对它加密。我打印出加密的文本,然后你可以看到你不再能够读懂它。为了**解密**出我们的秘密消息,我们只需调用 decrypt 方法,并传入加密的文本作为参数。结果就是我们得到了消息字节串形式的纯文本。
现在我们有了用来加密和解密消息的密码。下一步是创建一个需要加密的消息,然后使用 **encrypt** 方法对它加密。我打印出加密的文本,然后你可以看到你再也读不懂它了。为了解密出我们的秘密消息,我们只需调用 **decrypt** 方法,并传入加密的文本作为参数。结果就是我们得到了消息字节串形式的纯文本。
---
### 小结
@ -268,7 +255,7 @@ via: http://www.blog.pythonlibrary.org/2016/05/18/python-3-an-intro-to-encryptio
作者:[Mike][a]
译者:[Cathon](https://github.com/Cathon)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出