Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erläuterung der Verschlüsselung und Entschlüsselung in Python3

Detaillierte Erläuterung der Verschlüsselung und Entschlüsselung in Python3

巴扎黑
巴扎黑Original
2017-08-21 14:42:5113662Durchsuche

In der Standardbibliothek von Python 3 gibt es nicht viel zum Lösen der Verschlüsselung, aber es gibt eine Bibliothek zum Umgang mit Hashes. Hier geben wir eine kurze Einführung, der Schwerpunkt liegt jedoch auf zwei Softwarepaketen von Drittanbietern: PyCrypto und Kryptographie. Wir werden lernen, wie man diese beiden Bibliotheken zum Verschlüsseln und Entschlüsseln von Zeichenfolgen verwendet.

Hash

Wenn Sie einen sicheren Hash-Algorithmus oder Message-Digest-Algorithmus verwenden müssen, können Sie das Hashlib-Modul in der Standardbibliothek verwenden. Dieses Modul enthält FIPS-konforme (Federal Information Processing Standard) sichere Hashing-Algorithmen, einschließlich SHA1, SHA224, SHA256, SHA384, SHA512 und den MD5-Algorithmus von RSA. Python unterstützt auch die Hash-Funktionen adler32 und crc32, diese befinden sich jedoch im zlib-Modul.

Eine der häufigsten Verwendungsmöglichkeiten von Hashes besteht darin, den Hash eines Passworts statt des Passworts selbst zu speichern. Natürlich muss die verwendete Hash-Funktion robuster sein, sonst kann sie leicht geknackt werden. Eine weitere häufige Verwendung besteht darin, den Hash einer Datei zu berechnen und dann die Datei und ihren Hash separat zu senden. Der Empfänger der Datei kann den Hashwert der Datei berechnen und prüfen, ob dieser mit dem empfangenen Hashwert übereinstimmt. Wenn beide übereinstimmen, bedeutet dies, dass die Datei während der Übertragung nicht manipuliert wurde.

Versuchen wir, einen MD5-Hash zu erstellen:

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

Nehmen wir uns einen Moment Zeit, um es Zeile für Zeile zu erklären. Zuerst importieren wir Hashlib und erstellen dann eine Instanz des MD5-Hash-Objekts. Nachdem wir dieser Instanz eine Zeichenfolge hinzugefügt hatten, erhielten wir eine Fehlermeldung. Es stellt sich heraus, dass Sie bei der Berechnung des MD5-Hashs eine Zeichenfolge in Byteform anstelle einer gewöhnlichen Zeichenfolge verwenden müssen. Nachdem wir die Zeichenfolge korrekt hinzugefügt haben, rufen wir ihre Digest-Funktion auf, um den Hash-Wert zu erhalten. Wenn Sie einen hexadezimalen Hash-Wert wünschen, können Sie auch die folgende Methode verwenden:

>>> md5.hexdigest()
&#39;1482ec1b2364f64e7d162a2b5b16f477&#39;

Tatsächlich gibt es eine optimierte Möglichkeit, einen Hash zu erstellen. Schauen wir uns die Erstellung mit dieser Methode an: Ein sha1-Hash :

>>> sha = hashlib.sha1(b&#39;Hello Python&#39;).hexdigest()
>>> sha
&#39;422fbfbc67fe17c86642c5eaaa48f8b670cbed1b&#39;

Wie Sie sehen, können wir eine Hash-Instanz erstellen und gleichzeitig ihre Digest-Funktion aufrufen. Dann drucken wir den Hashwert aus und schauen ihn uns an. Hier verwende ich die Hash-Funktion sha1 als Beispiel, sie ist jedoch nicht besonders sicher und es steht den Lesern frei, andere Hash-Funktionen auszuprobieren.

Schlüsselexport

Pythons Standardbibliothek unterstützt den Schlüsselexport nur schwach. Tatsächlich ist die einzige von der Hashlib-Bibliothek bereitgestellte Methode die Funktion pbkdf2_hmac. Es handelt sich um die zweite passwortbasierte Schlüsselableitungsfunktion von PKCS#5 und verwendet HMAC als Pseudozufallsfunktion. Da es „Salting“- und iterative Vorgänge unterstützt, können Sie ähnliche Methoden zum Hashen Ihrer Passwörter verwenden. Wenn Sie beispielsweise die Verschlüsselungsmethode SHA-256 verwenden möchten, benötigen Sie einen „Salt“ von mindestens 16 Bytes und mindestens 100.000 Iterationen.

Einfach ausgedrückt handelt es sich bei „Salt“ um zufällige Daten, die dem Hashing-Prozess hinzugefügt werden, um die Schwierigkeit des Knackens zu erhöhen. Dies schützt Ihre Passwörter grundsätzlich vor Wörterbuch- und Rainbow-Table-Angriffen.

Sehen wir uns ein einfaches Beispiel an:

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

Hier hashen wir ein Passwort mit SHA256, verwenden ein schlechtes Salt, aber über 100.000 Iterationen. Natürlich wird SHA nicht für die Erstellung von Passwortschlüsseln empfohlen. Sie sollten stattdessen einen Algorithmus wie scrypt verwenden. Eine weitere gute Option ist die Verwendung einer Drittanbieter-Bibliothek namens bcrypt, die speziell für das Hashen von Passwörtern entwickelt wurde.

PyCryptodome

PyCrypto ist wahrscheinlich das bekannteste Drittanbieterpaket für Kryptographie in Python. Leider wurde die Entwicklung im Jahr 2012 eingestellt. Andere veröffentlichen weiterhin die neuesten Versionen von PyCrypto, und wenn es Ihnen nichts ausmacht, Binärpakete von Drittanbietern zu verwenden, können Sie immer noch die entsprechende Version für Python 3.5 erhalten. Beispielsweise habe ich das PyCrypto-Binärpaket, das Python 3.5 entspricht, auf Github gefunden (https://github.com/sfbahr/PyCrypto-Wheels).

Glücklicherweise gibt es einen Fork des Projekts namens PyCrytodome, der PyCrypto ersetzt. Um es unter Linux zu installieren, können Sie den folgenden pip-Befehl verwenden:

pip install pycryptodome

Die Installation auf einem Windows-System ist etwas anders:

pip install pycryptodomex

Wenn Sie auf Probleme stoßen, Dies kann daran liegen, dass Sie nicht das richtige Abhängigkeitspaket installiert haben (LCTT-Anmerkung: z. B. python-devel) oder dass Ihr Windows-System einen Compiler erfordert.

Es ist auch erwähnenswert, dass PyCryptodome gegenüber der letzten Version von PyCrypto viele Verbesserungen aufweist. Es lohnt sich, die Homepage zu besuchen, um zu sehen, welche neuen Funktionen verfügbar sind.

Verschlüsselte Zeichenfolge

Nachdem wir ihre Homepage besucht haben, können wir uns einige Beispiele ansehen. Im ersten Beispiel verwenden wir den DES-Algorithmus, um eine Zeichenfolge zu verschlüsseln:

>>> from Crypto.Cipher import DES
>>> key = &#39;abcdefgh&#39;
>>> def pad(text):
        while len(text) % 8 != 0:
            text += &#39; &#39;
        return text
>>> des = DES.new(key, DES.MODE_ECB)
>>> text = &#39;Python rocks!&#39;
>>> 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&#39;>\xfc\x1f\x16x\x87\xb2\x93\x0e\xfcH\x02\xd59VQ&#39;

这段代码稍有些复杂,让我们一点点来看。首先需要注意的是,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&#39;Python rocks!   &#39;

幸运的是,解密非常容易,我们只需要调用 des 对象的 decrypt 方法就可以得到我们原来的 byte 类型字符串了。下一个任务是学习如何用 RSA 算法加密和解密一个文件。首先,我们需要创建一些 RSA 密钥。

创建 RSA 密钥

如果你希望使用 RSA 算法加密数据,那么你需要拥有访问 RAS 公钥和私钥的权限,否则你需要生成一组自己的密钥对。在这个例子中,我们将生成自己的密钥对。创建 RSA 密钥非常容易,所以我们将在 Python 解释器中完成。

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

首先我们从 Crypto.PublicKey 包中导入 RSA,然后创建一个傻傻的密码。接着我们生成 2048 位的 RSA 密钥。现在我们到了关键的部分。为了生成私钥,我们需要调用 RSA 密钥实例的 exportKey 方法,然后传入密码,使用的 PKCS 标准,以及加密方案这三个参数。之后,我们把私钥写入磁盘的文件中。

接下来,我们通过 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(&#39;/path/to/encrypted_data.bin&#39;, &#39;wb&#39;) as out_file:
    recipient_key = RSA.import_key(
        open(&#39;/path_to_public_key/my_rsa_public.pem&#39;).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&#39;blah blah blah Python blah blah&#39;
    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 = &#39;nooneknows&#39;
with open(&#39;/path/to/encrypted_data.bin&#39;, &#39;rb&#39;) as fobj:
    private_key = RSA.import_key(
        open(&#39;/path_to_private_key/my_rsa_key.pem&#39;).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

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

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

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

现在我们有了用来加密和解密消息的密码。下一步是创建一个需要加密的消息,然后使用 encrypt 方法对它加密。我打印出加密的文本,然后你可以看到你再也读不懂它了。为了解密出我们的秘密消息,我们只需调用 decrypt 方法,并传入加密的文本作为参数。结果就是我们得到了消息字节串形式的纯文本。

小结

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

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Verschlüsselung und Entschlüsselung in Python3. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn