Python3 加密解密技能详解
当前位置:以往代写 > Python教程 >Python3 加密解密技能详解
2019-06-14

Python3 加密解密技能详解

Python3 加密解密技能详解

Python 3 的尺度库中没几多用来办理加密的,不外却有用于处理惩罚哈希的库。在这里我们会对其举办一个简朴的先容,但重点会放在两个第三方的软件包:PyCrypto 和 cryptography 上。我们将进修如何利用这两个库,来加密息争密字符串。

哈希

假如需要用到安详哈希算法或是动静摘要算法,那么你可以利用尺度库中的 hashlib 模块。这个模块包括了切合 FIPS(美国联邦信息处理惩罚尺度)的安详哈希算法,包罗 SHA1,SHA224,SHA256,SHA384,SHA512 以及 RSA 的 MD5 算法。Python 也支持 adler32 以及 crc32 哈希函数,不外它们在 zlib 模块中。

哈希的一个最常见的用法是,存储暗码的哈希值而非暗码自己。虽然了,利用的哈希函数需要稳健一点,不然容易被破解。另一个常见的用法是,计较一个文件的哈希值,然后将这个文件和它的哈希值别离发送。吸收到文件的人可以计较文件的哈希值,检讨是否与接管到的哈希值相符。假如两者相符,就说明文件在传送的进程中未经改动。

让我们试着建设一个 md5 哈希:

>>> import hashlib
>>> md5 = hashlib.md5()
>>> md5.update('Python rocks!')
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    md5.update('Python rocks!')
TypeError: Unicode-objects must be encoded before hashing
>>> md5.update(b'Python rocks!')
>>> md5.digest()
b'\x14\x82\xec\x1b#d\xf6N}\x16*+[\x16\xf4w'

让我们花点时间一行一行来讲授。首先,我们导入 hashlib ,然后建设一个 md5 哈希工具的实例。接着,我们向这个实例中添加一个字符串后,却获得了报错信息。本来,计较 md5 哈希时,需要利用字节形式的字符串而非普通字符串。正确添加字符串后,我们挪用它的 digest 函数来获得哈希值。假如你想要十六进制的哈希值,也可以用以下要领:

>>> md5.hexdigest()
'1482ec1b2364f64e7d162a2b5b16f477'

实际上,有一种精简的要领来建设哈希,下面我们看一下用这种要领建设一个 sha1 哈希:

>>> sha = hashlib.sha1(b'Hello Python').hexdigest()
>>> sha
'422fbfbc67fe17c86642c5eaaa48f8b670cbed1b'

可以看到,我们可以同时建设一个哈希实例而且挪用其 digest 函数。然后,我们打印出这个哈希值看一下。这里我利用 sha1 哈希函数作为例子,但它不是出格安详,读者可以随意实验其他的哈希函数。

密钥导出

Python 的尺度库对密钥导出支持较弱。实际上,hashlib 函数库提供的独一要领就是 pbkdf2_hmac 函数。它是 PKCS#5 的基于口令的第二个密钥导出函数,并利用 HMAC 作为伪随机函数。因为它支持“加盐salt”和迭代操纵,你可以利用雷同的要领来哈希你的暗码。譬喻,假如你规划利用 SHA-256 加密要领,你将需要至少 16 个字节的“盐”,以及最少 100000 次的迭代操纵。

简朴来说,“盐”就是随机的数据,被用来插手到哈希的进程中,以加大破解的难度。这根基可以掩护你的暗码免受字典和彩虹表rainbow table的进攻。

让我们看一个简朴的例子:

>>> import binascii
>>> dk = hashlib.pbkdf2_hmac(hash_name='sha256',
        password=b'bad_password34', 
        salt=b'bad_salt', 
        iterations=100000)
>>> binascii.hexlify(dk)
b'6e97bad21f6200f9087036a71e7ca9fa01a59e1d697f7e0284cd7f9b897d7c02'

这里,我们用 SHA256 对一个暗码举办哈希,利用了一个糟糕的盐,但颠末尾 100000 次迭代操纵。虽然,SHA 实际上并不被推荐用来建设暗码的密钥。你应该利用雷同 scrypt 的算法来替代。另一个不错的选择是利用一个叫 bcrypt 的第三方库,它是被专门设计出来哈希暗码的。

PyCryptodome

PyCrypto 大概是 Python 中暗码学方面最有名的第三方软件包。惋惜的是,它的开拓事情于 2012 年就已遏制。其他人还在继承宣布最新版本的 PyCrypto,假如你不介怀利用第三方的二进制包,仍可以取得 Python 3.5 的相应版本。好比,我在 Github (https://github.com/sfbahr/PyCrypto-Wheels) 上找到了对应 Python 3.5 的 PyCrypto 二进制包。

幸运的是,有一个该项目标分支 PyCrytodome 代替了 PyCrypto 。为了在 Linux 上安装它,你可以利用以下 pip 呼吁:

pip install pycryptodome

在 Windows 系统上安装则稍有差异:

pip install pycryptodomex

假如你碰着了问题,大概是因为你没有安装正确的依赖包(LCTT 译注:如 python-devel),可能你的 Windows 系统需要一个编译器。

还值得留意的是,PyCryptodome 在 PyCrypto 最后版本的基本上有许多改造。很是值得去会见它们的主页,看看有什么新的特性。

加密字符串

#p#分页标题#e#

会见了他们的主页之后,我们可以看一些例子。在第一个例子中,我们将利用 DES 算法来加密一个字符串:

>>> from Crypto.Cipher import DES
>>> key = 'abcdefgh'
>>> def pad(text):
        while len(text) % 8 != 0:
            text += ' '
        return text
>>> des = DES.new(key, DES.MODE_ECB)
>>> text = 'Python rocks!'
>>> padded_text = pad(text)
>>> encrypted_text = des.encrypt(text)
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    encrypted_text = des.encrypt(text)
  File "C:\Programs\Python\Python35-32\lib\site-packages\Crypto\Cipher\blockalgo.py", line 244, in encrypt
    return self._cipher.encrypt(plaintext)
ValueError: Input strings must be a multiple of 8 in length
>>> encrypted_text = des.encrypt(padded_text)
>>> encrypted_text
b'>\xfc\x1f\x16x\x87\xb2\x93\x0e\xfcH\x02\xd59VQ'

这段代码稍有些巨大,让我们一点点来看。首先需要留意的是,DES 加密利用的密钥长度为 8 个字节,这也是我们将密钥变量配置为 8 个字符的原因。而我们需要加密的字符串的长度必需是 8 的倍数,所以我们建设了一个名为 pad 的函数,来给一个字符串末端填充空格,直到它的长度是 8 的倍数。然后,我们建设了一个 DES 的实例,以及我们需要加密的文本。我们还建设了一个颠末填充处理惩罚的文本。我们实验着对未经填充处理惩罚的文本举办加密,啊欧,报了一个 ValueError 错误!我们需要对颠末填充处理惩罚的文本举办加密,然后获得加密的字符串。(LCTT 译注:encrypt 函数的参数应为 byte 范例字符串,代码为:encrypted_text = des.encrypt(padded_text.encode('utf-8')))

知道了如何加密,还要知道如何解密:

>>> des.decrypt(encrypted_text)
b'Python rocks!   '

幸运的是,解密很是容易,我们只需要挪用 des 工具的 decrypt 要领就可以获得我们本来的 byte 范例字符串了。下一个任务是进修如何用 RSA 算法加密息争密一个文件。首先,我们需要建设一些 RSA 密钥。

建设 RSA 密钥

假如你但愿利用 RSA 算法加密数据,那么你需要拥有会见 RAS 公钥和私钥的权限,不然你需要生成一组本身的密钥对。在这个例子中,我们将生本钱身的密钥对。建设 RSA 密钥很是容易,所以我们将在 Python 表明器中完成。

>>> from Crypto.PublicKey import RSA
>>> code = 'nooneknows'
>>> key = RSA.generate(2048)
>>> encrypted_key = key.exportKey(passphrase=code, pkcs=8, 
        protection="scryptAndAES128-CBC")
>>> with open('/path_to_private_key/my_private_rsa_key.bin', 'wb') as f:
        f.write(encrypted_key)
>>> with open('/path_to_public_key/my_rsa_public.pem', 'wb') as f:
        f.write(key.publickey().exportKey())

首先我们从 Crypto.PublicKey 包中导入 RSA,然后建设一个傻傻的暗码。接着我们生成 2048 位的 RSA 密钥。此刻我们到了要害的部门。为了生成私钥,我们需要挪用 RSA 密钥实例的 exportKey 要领,然后传入暗码,利用的 PKCS 尺度,以及加密方案这三个参数。之后,我们把私钥写入磁盘的文件中。

#p#分页标题#e#

接下来,我们通过 RSA 密钥实例的 publickey 要领建设我们的公钥。我们利用要领链挪用 publickey 和 exportKey 要领生成公钥,同样将它写入磁盘上的文件。

加密文件

有了私钥和公钥之后,我们就可以加密一些数据,并写入文件了。这里有个较量尺度的例子:

from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
with open('/path/to/encrypted_data.bin', 'wb') as out_file:
    recipient_key = RSA.import_key(
        open('/path_to_public_key/my_rsa_public.pem').read())
    session_key = get_random_bytes(16)
    cipher_rsa = PKCS1_OAEP.new(recipient_key)
    out_file.write(cipher_rsa.encrypt(session_key))
    cipher_aes = AES.new(session_key, AES.MODE_EAX)
    data = b'blah blah blah Python blah blah'
    ciphertext, tag = cipher_aes.encrypt_and_digest(data)
    out_file.write(cipher_aes.nonce)
    out_file.write(tag)
    out_file.write(ciphertext)

代码的前三行导入 PyCryptodome 包。然后我们打开一个文件用于写入数据。接着我们导入公钥赋给一个变量,建设一个 16 字节的会话密钥。在这个例子中,我们将利用殽杂加密要领,即 PKCS#1 OAEP ,也就是最优非对称加密填充。这答允我们向文件中写入任意长度的数据。接着我们建设 AES 加密,要加密的数据,然后加密数据。我们将获得加密的文本和动静认证码。最后,我们将随机数,动静认证码和加密的文本写入文件。

顺便提一下,随机数凡是是真随机或伪随机数,只是用来举办暗码通信的。对付 AES 加密,其密钥长度最少是 16 个字节。随意用一个你喜欢的编辑器试着打开这个被加密的文件,你应该只能看到乱码。

此刻让我们进修如何解密我们的数据。

from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP
code = 'nooneknows'
with open('/path/to/encrypted_data.bin', 'rb') as fobj:
    private_key = RSA.import_key(
        open('/path_to_private_key/my_rsa_key.pem').read(),
        passphrase=code)
    enc_session_key, nonce, tag, ciphertext = [ fobj.read(x) 
                                                for x in (private_key.size_in_bytes(), 
                                                16, 16, -1) ]
    cipher_rsa = PKCS1_OAEP.new(private_key)
    session_key = cipher_rsa.decrypt(enc_session_key)
    cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce)
    data = cipher_aes.decrypt_and_verify(ciphertext, tag)
print(data)

假如你当真看了上一个例子,这段代码应该很容易理会。在这里,我们先以二进制模式读取我们的加密文件,然后导入私钥。留意,当你导入私钥时,需要提供一个暗码,不然会呈现错误。然后,我们文件中读取数据,首先是加密的会话密钥,然后是 16 字节的随机数和 16 字节的动静认证码,最后是剩下的加密的数据。

接下来我们需要解密出会话密钥,从头建设 AES 密钥,然后解密出数据。

你还可以用 PyCryptodome 库做更多的事。不外我们要接着接头在 Python 中还可以用什么来满意我们加密解密的需求。

cryptography 包

cryptography 的方针是成为“人类易于利用的暗码学包cryptography for humans”,就像 requests 是“人类易于利用的 HTTP 库HTTP for Humans”一样。这个想法使你可以或许建设简朴安详、易于利用的加密方案。假如有需要的话,你也可以利用一些底层的暗码学基元,但这也需要你知道更多的细节,不然建设的对象将是不安详的。

假如你利用的 Python 版本是 3.5, 你可以利用 pip 安装,如下:

pip install cryptography

#p#分页标题#e#

你会看到 cryptography 包还安装了一些依赖包(LCTT 译注:如 libopenssl-devel)。假如安装都顺利,我们就可以试着加密一些文本了。让我们利用 Fernet 对称加密算法,它担保了你加密的任何信息在不知道暗码的环境下不能被改动或读取。Fernet 还通过 MultiFernet 支持密钥轮换。下面让我们看一个简朴的例子:

>>> from cryptography.fernet import Fernet
>>> cipher_key = Fernet.generate_key()
>>> cipher_key
b'APM1JDVgT8WDGOWBgQv6EIhvxl4vDYvUnVdg-Vjdt0o='
>>> cipher = Fernet(cipher_key)
>>> text = b'My super secret message'
>>> encrypted_text = cipher.encrypt(text)
>>> encrypted_text
(b'gAAAAABXOnV86aeUGADA6mTe9xEL92y_m0_TlC9vcqaF6NzHqRKkjEqh4d21PInEP3C9HuiUkS9f'
 b'6bdHsSlRiCNWbSkPuRd_62zfEv3eaZjJvLAm3omnya8=')
>>> decrypted_text = cipher.decrypt(encrypted_text)
>>> decrypted_text
b'My super secret message'

首先我们需要导入 Fernet,然后生成一个密钥。我们输出密钥看看它是什么样儿。如你所见,它是一个随机的字节串。假如你愿意的话,可以试着多运行 generate_key 要领屡次,生成的密钥会是差异的。然后我们利用这个密钥生成 Fernet 暗码实例。

此刻我们有了用来加密息争密动静的暗码。下一步是建设一个需要加密的动静,然后利用 encrypt 要领对它加密。我打印出加密的文本,然后你可以看到你再也读不懂它了。为相识密出我们的奥秘动静,我们只需挪用 decrypt 要领,并传入加密的文本作为参数。功效就是我们获得了动静字节串形式的纯文本。

小结

这一章仅仅浅显地先容了 PyCryptodome 和 cryptography 这两个包的利用。不外这也确实给了你一个关于如何加密解密字符串和文件的简述。请务必阅读文档,做做尝试,看看还能做些什么!

    关键字:

在线提交作业