天天看點

探索 Python 3 加密技術

<b>本文講的是探索 Python 3 加密技術,</b>

<b></b>

Python 3 沒有太多用于處理加密的标準庫,通常以哈希庫作為替代。在本章節中我們将簡單了解一下 <code>hashlib</code>,但重點還是集中在兩個第三方庫:PyCrypto 和 cryptography。我們将會學習如何使用這些庫對字元串進行加密和解密。

如果你需要安全哈希或密文資訊的算法,那麼可以使用 <code>hashlib</code> 子產品所提供的 Python 标準庫,它提供 SHA1,SHA224,SHA256,SHA384,SHA512 等 FIPS 安全雜湊演算法以及 RSA MD5 算法。Python 也支援 adler32 和 crc32 哈希函數,不過它們由 <code>zlib</code> 子產品提供。

雜湊演算法最常用于對密碼進行加密,進而儲存密碼的哈希值而非明文密碼。當然,雜湊演算法必須足夠好否則就會被破譯。雜湊演算法的另一用途是擷取檔案的哈希值并将其與檔案分開傳遞,接收者就可以據此判斷收到的檔案與哈希值是否比對,如果比對這說明在傳遞過程中檔案沒有被人篡改。

讓我們試着生成一個 md5 哈希值:

讓我們花點時間分解一下這個過程。首先,我們引入了 <code>hashlib</code> 子產品并建立了一個 md5 HASH 對象。接下來我們向這個對象添加了一些文本字元,但是卻得到錯誤資訊。要使用 md5 雜湊演算法,你必須傳遞位元組串而不是普通的字元串。于是我們改用位元組串并調用 <code>digest</code> 方法擷取我們想要的哈希值。如果你想要十六進制的密文,我們也可以做到:

實際上有更便捷的方法産生哈希值,在建立 sha512 哈希值的時候我們可以這樣做:

正如你所見,我們可以在建立哈希對象執行個體的同時調用密文算法,然後将其結果列印出來。我選擇 sha1 雜湊演算法是因為它的結果更短能夠更好地适應頁面。但同樣也更不安全,你也可以随便嘗試一下上面列舉的其它算法。

Python 内置标準庫對密鑰派生的支援非常有限。實際上 <code>hashlib</code> 提供的唯一方法是 <code>pbkdf2_hmac</code>,實作的是 PKCS#5 基于密碼的密鑰派生函數2(PBKDF2 譯者注:這裡作者原意是pbkdf2_hmac是基于PBKDF2的實作,詳細細節可以參看PKCS#5标準,即RFC6070标準 ),它利用 HMAC 作為僞随機函數。你也可以用自己的算法來完成對密碼的哈希加密,隻要支援 <code>salt</code>和疊代。例如,如果你用 SHA-256 你可能需要一個長度至少為16個位元組的 <code>salt</code> 和至少 100,000 次疊代。

簡單解釋一下,<code>salt</code> 是一個随機資料,用來和密碼加到一起進行哈希加密,進而使得更難從哈希值“解哈希”得到密碼。基本上可以保護你的密碼不受字典攻擊和預計算彩虹表攻擊(譯者注:彩虹表是通過實作計算一系列字元集的hash值的資料,然後通過hash後的資料,反查原文的攻擊方式)。

讓我們來看一個簡單的例子:

這裡我們使用一個簡單的 salt 資料,但經過 100,000 次疊代來對密碼建立一個 SHA256 哈希對象。。當然,實際上并不推薦 SHA 作為密碼的密鑰派生算法,而是應該使用 <code>scrypt</code> 之類的方法。另外一個很好的選擇是使用第三方包:<code>bcrypt</code>,它的設計初衷就是為了專門應對密碼哈希加密的。

幸運的是有一個名為 <code>PyCrytodome</code> 的 fork 項目可以替代 <code>PyCrypto</code>。在 Linux 上可以用 <code>pip</code> 指令安裝:

<code>pip install pycryptodome</code>

Windows 系統上有點不同:

<code>pip install pycryptodomex</code>

另外值得一提的是 <code>PyCryptodome</code> 有許多針對最後一版 <code>PyCryto</code> 的增強版本,值得你花時間去通路它的首頁檢視一下有哪些新的特性。

看完他們的首頁之後,我們可以繼續看幾個例子。首先我們用 DES 來加密一個字元串:

這段代碼看起來可能有點繞,我們花點時間了将它分解一下。首先,需要注意 DES 加密所需要的密鑰長度應為8位元組,是以我們設定變量 <code>key</code> 為這一長度的字元串。被加密的字元串長度必須是 8 的倍數,是以我們建立一個 <code>pad</code> 方法來将字元串用空格填充直至長度為8的倍數。接下來我們建立一個 DES 執行個體和一個填充過的字元串。我們試一下對原始字元串進行加密結果會導緻 <code>ValueError</code> ,我們已經知道将填充過的字元串傳遞給加密算法,正如你所見,我們可以對字元串進行加密了!

當然這個例子還沒結束,我們需要知道如何解密:

幸運的是,解密方法非常簡單,我們隻需要調用 <code>des</code> 對象的 <code>decrypt</code> 方法就可以得到原始的位元組串。我們接下來的任務是學習如何利用 <code>PyCrypto</code> 的 RSA 算法對檔案進行加密和解密,首先我們需要生成 RSA 密鑰!

如果你想要通過 RSA 算法加密你的資料,那麼你将需要一對 RSA 公/私鑰組合或者自己生成一對。在這個例子中,我們将會自己生成一對。由于非常簡單,我們直接在 Python 解釋器中完成:

首先我們引入 從 <code>Crypto.PublicKey</code> 引入 RSA ,然後建立一個密碼。接下來我們生成一個 2048 位的 RSA 對象的執行個體。為了生成私鑰,我們需要調用 RSA 執行個體的 <code>exportKey</code> 方法并傳遞給它剛剛建立的密碼,PKCS 标準算法将會用它保護我們的私鑰。接下來我們将生成的私鑰寫入檔案。

下一步我們通過 RSA 執行個體的 <code>publickey</code> 方法生成公鑰,我們在這段代碼中用了簡寫的方式将 <code>publickey</code> 和 <code>exportKey</code>方法串接起來,最後也将結果寫入檔案。

現在我們有了一對私鑰和公鑰,我們可以對我們的資料進行加密并寫入檔案。下面是一個非常标準的例子:

前三行完成對 <code>PyCryptodome</code> 的引入,接下來打開将要寫入的檔案。然後我們将公鑰讀入變量并建立一個16位元組長的 session key。在這個例子中我們用了混合加密方法,是以我們使用最優非對稱加密填充的 PKCS#1 OAEP。這讓我們可以将任意長度的資料寫入檔案。接下來我們建立 AES 密文,建立一些資料并進行加密,這一方法會傳回加密後的文本和 MAC 值。最終我們将<code>nonce</code>,<code>MAC</code>(或<code>tag</code>)以及加密後的文本寫入檔案。

說明一下,<code>nonce</code> 是一個任意數字,僅用于密文通信。它們通常是随機或僞随機數。對于 AES 來說,它的長度至少要是16位。你可以用你的文本編輯器打開加密後的檔案看一下,隻能看到一堆亂碼。

現在讓我們學一下如何解密資料:

如果前面的例子你都跟上了,那這段代碼應該很容易讀懂了。在這個例子中,我們以二進制模式打開加密檔案,然後導入私鑰。要注意在導入私鑰的時候,必須給出你的密碼,否則将會出錯。接下來我們讀取加密檔案,需要注意的是先讀取私鑰,然後是16位長的 <code>nonce</code>,接下來是另外 16 位長的标簽,最後剩下的才是我們的資料。

接下來我們需要解密 <code>session key</code>,重新生成 AES key 并解密資料。

你可以用 <code>PyCryptodome</code> 來做更多的事,但是在這裡我們需要繼續看看 Python 中解決加密問題還有别的什麼方法可用。

<code>cryptography</code> 包的目的是”給人類使用的加密工具”,就像**<code>requests</code>**是“給人類使用的HTTP”工具包一樣。其理念是讓你可以用簡單的方法建立安全、易用的加密方案。如果你需要,你也可以深入到底層加密原理,這就要求你必須知道你在做什麼,而且很有可能最終得到一些并不那麼安全的結果。

如果你正在用 Python 3.5,你可以像這樣使用 <code>pip</code> 安裝:

<code>pip install cryptography</code>

你會發現 <code>cryptography</code> 自己安裝了幾個依賴包。假設這些都成功安裝完成,我們可以試着來加密一些文本。讓我們用一下<code>Fernet</code> 對稱加密算法。Fernet 算法保證你加密的任何消息除非有你自己定義的密鑰都無法修改或讀取。Fernet 同時也支援通過 <code>MultiFernet</code> 方法對密鑰進行旋轉。讓我們來看一個簡單的例子:

首先我們需要引入 Fernet,然後生成一個密鑰。這裡将密鑰列印出來看看它是什麼。如你所見,它是一個随機的位元組串。你也可以多運作幾次 <code>generate_key</code> 方法,每次的結果應該都不相同。接下來我們基于這一密鑰建立 Fernet 密文執行個體。

有了密文之後我們可以用來加密和解密我們的消息,可以用**<code>encrypt</code>**方法實作對必要消息的加密。接下來我講加密後的結果列印出來可以看出來已經無法正常閱讀了。調用 <code>decrypt</code> 方法可以對消息進行解密,結果可以得到最初的原始消息。

本章隻簡單介紹了 <code>PyCryptodome</code> 和 <code>cryptography</code> 包最基本的用法,可以讓你對如何通過 Python 對字元和檔案進行加密、解密有整體的了解。請一定要閱讀文檔并親自實驗嘗試!

<b>原文釋出時間為:2016年06月15日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>