(02)Python密碼庫Cryptography探究學習---深入理解Fernet

本節對Fernet進行深入介紹,使讀者能夠理解cryptographic recipes的含義,能在實踐中正確使用密碼學的相關演算法。

Fernet不僅僅是個對稱密碼演算法,它是密碼學原語的集合應用,主要有3個特點:(1)使用了符合密碼安全的隨機數密鑰。(2)提供了加密功能:使用了128位密鑰的AES加密演算法,對數據 PKCS7 填充後,以AES-CBC模式進行加密。(3)提供了認證的功能,採用Sha256的哈希函數,產生消息認證碼(HMAC)。

大家先通過github.com/pyca/cryptog,結合源代碼,看看下面的詳細介紹:

一、使用了密碼安全的隨機數密鑰

對於密碼演算法而言,密鑰應該是符合密碼學安全的隨機數。只有這樣才能保證密碼演算法的安全性,否則容易遭受攻擊。在很多應用中,密鑰並不隨機,不能符合密碼學的要求,存在著漏洞。

那麼如何得到真正的隨機數呢?真正的隨機數是通過物理過程得到的,比如拋硬幣、擲骰子,布朗運動,量子效應,放射性衰變,振蕩器採樣等。

通過計演算法的方式得到真正的隨機數,是幾乎不可能的。馮.諾依曼說過:「任何人考慮用數學的方法產生隨機數肯定是不合情理的」。一般而言,大家經常用到的隨機數生成函數,並不能真正的產生隨機數,也不能用在密碼學演算法中,比如C語言中的rand()函數,它通過如下的函數計算而來。

能密碼演算法中使用的隨機數,如何產生呢?建議採用操作系統中的隨機數產生器來產生,在Unix操作系統中使用 /dev/urandom,而在Windows操作系統中使用CryptGenRandom 生成。由於我們使用Python語言,只需要使用如下的方法,即

>>> import osn>>> salt = os.urandom(32)n

我們通過15.1. os - Miscellaneous operating system interfaces - Python 2.7.13 documentation,來查看os.urandom(n)的說明。

Return a string of n random bytes suitable for cryptographic use.

This function returns random bytes from an OS-specific randomness source. The returned data should be unpredictable enough for cryptographic applications, though its exact quality depends on the OS implementation.

使用Fernet時,有兩種方式產生密鑰:

(1)讓Fernet直接產生

這種方法調用key = Fernet.generate_key()。

def generate_key(cls):nn return base64.urlsafe_b64encode(os.urandom(32))n

通過os.urandom(32)產生符合密碼學要求的隨機數,而後進行base64編碼。

(2)自己設定一個密碼

如下是個例子:

>>> import base64n>>> import osn>>> from cryptography.fernet import Fernetn>>> from cryptography.hazmat.backends import default_backendn>>> from cryptography.hazmat.primitives import hashesn>>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMACn>>> password = b"password"n>>> salt = os.urandom(16)n>>> kdf = PBKDF2HMAC(n... algorithm=hashes.SHA256(),n... length=32,n... salt=salt,n... iterations=100000,n... backend=default_backend()n... )n>>> key = base64.urlsafe_b64encode(kdf.derive(password))n>>> f = Fernet(key)n>>> token = f.encrypt(b"Secret message!")n>>> tokenn...n>>> f.decrypt(token)nSecret message!n

設定一個password,接著使用PBKDF2HMAC。它是個密鑰推導函數,通過多次對salt進行hash運算從而產生密鑰。該方法被美國政府標準化,並得到廣泛採用。以後有時間再進行更加詳細的介紹。

通過密鑰推導函數,輸出32位隨機數,使用key = base64.urlsafe_b64encode(kdf.derive(password))產生Fernet使用的密鑰。

二、加密和認證功能

這兩個功能主要通過 encrypt和_encrypt_from_parts函數實現。

def _encrypt_from_parts(self, data, current_time, iv):n if not isinstance(data, bytes):n raise TypeError("data must be bytes.")nn padder = padding.PKCS7(algorithms.AES.block_size).padder()n padded_data = padder.update(data) + padder.finalize()n encryptor = Cipher(n algorithms.AES(self._encryption_key), modes.CBC(iv), self._backendn ).encryptor()n ciphertext = encryptor.update(padded_data) + encryptor.finalize()nn basic_parts = (n b"x80" + struct.pack(">Q", current_time) + iv + ciphertextn )nn h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)n h.update(basic_parts)n hmac = h.finalize()n return base64.urlsafe_b64encode(basic_parts + hmac)n

(1)數據填充

......n padder = padding.PKCS7(algorithms.AES.block_size).padder()n padded_data = padder.update(data) + padder.finalize()n......n

它使用Cryptography提供的padding函數,對明文數據進行填充。

明文消息的長度是隨機的,按128比特分組時,最後一組消息長度可能不足128比特。此時要填充一些數字湊夠128比特。為了讓接收者能區分正確消息與填充的無用數字,需要加上指示信息。通常數據尾部、填充字元和填充指示符作為一組明文進行加密。n

(2)使用了AES-CBC模式進行加密

......n encryptor = Cipher(n algorithms.AES(self._encryption_key), modes.CBC(iv), self._backendn ).encryptor()n ciphertext = encryptor.update(padded_data) + encryptor.finalize()n......n

在加密開始前,選擇一個初始化向量IV,先於明文分組異或,再開始加密流程;加密每一分組後,該分組的密文與下一分組的明文異或,接著繼續加密流程,並如此反覆。初始化向量(IV)沒有實際意義,在第一次計算時使用,當然在解密時同樣需要IV。該模式不容易被主動攻擊,適合傳輸長度長的報文,是SSL、IPSec的標準。

(3)產生認證碼

basic_parts = (n b"x80" + struct.pack(">Q", current_time) + iv + ciphertextn )nn h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)n h.update(basic_parts)n hmac = h.finalize()n return base64.urlsafe_b64encode(basic_parts + hmac)n

basic_parts以「x80」開頭,包含現有時間、IV和密文。接著使用SHA256()哈希函數產生HMAC認證碼。HMAC是使用hash演算法構造的含有密鑰散列函數演算法,其中哈希演算法採用了SHA256,密鑰是self._signing_key(32位key中的前16位),產生固定長度的認證碼,以防止密文在傳輸過程中被篡改。

最後將basic_parts和消息認證碼hmac一同返回。

三、小結

本文對Fernet進行了深入的介紹,希望讀者能夠理解密碼演算法中使用的隨機數是如何產生的,並理解對明文的加密,要有填充、加密的步驟,且為了防止密文傳輸過程中的篡改,要使用HMAC演算法,從而對cryptographic recipes有更加深刻的認識。

由於部分讀者對一些密碼學原語並不十分清楚,這裡知道大概的含義即可,有時間我會進行更加詳細的介紹。


推薦閱讀:

這串字元什麼意思?
如果我隨意打出亂碼,可不可以用密碼學解出有意義的信息?
用密碼學玩暗軍棋 -- 閑聊多方計算
將大於2的正整數分解成2個因數(不是質因數),並求其各結果間兩因數最小非負數差,這樣的數有研究的意義嗎?
從 Shor 演算法到格密碼學

TAG:密码学 | 信息安全和密码学 | Python |