在 「Dropbox 做到資料加密又避免重複儲存的秘密」 這篇文章中,有提到使用 AES-256 對稱式加密,以及 SHA-256 雜湊演算法, 本文山姆鍋提供這兩者在 Python 的應用範例作為補充。
本文的範例使用 Python 2.7。 山姆鍋假設讀者對密碼學以及雜湊演算法已經有基本概念:至少知道什麼是對稱式加密 (Symmetric Encryption)、 什麼是雜湊函式 (Hash function) 等等。
相依套件
針對 AES-256 加密,我們需要用到 PyCrypto 這個套件,由於雜湊值常常需要輸出成字串且被使用在 URL 網址,因此山姆鍋習慣使用 Base58 的來將雜湊值編碼。
使用下列指令安裝相依套件:
1 2 pip install pycrypto pip install base58
或者安裝本文使用的特定版本:
1 2 pip install pycrypto==2.6.1 pip install base58==0.2.1
AES-256 CBC 範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 """ Routines for convergent encryption. """ from __future__ import absolute_import, division, unicode_literalsfrom Crypto.Cipher import AES_IV = 16 * '\x00' def aes_encrypt (data, key) : cryptor = AES.new(key, AES.MODE_CBC, _IV) return cryptor.encrypt(data) def aes_decrypt (data, key) : cryptor = AES.new(key, AES.MODE_CBC, _IV) return cryptor.decrypt(data)
範例提供 aes_encrypt
以及 aes_decrypt
分別作為加密跟解密的用途。其中的 IV
是 initial vector value, 通常應該是隨機值,這裡因為是用在 Convergent encryption, 且使用的金鑰 (secret key) 應該已經足夠安全,所以採用固定值。
SHA-256 範例
如果對於產生出的字串長度沒特別要求的話,山姆鍋建議使用 base58 的檢查模式,這在驗證輸入正確性上很有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from __future__ import absolute_import, division, unicode_literalsimport osimport hashlibimport base58def gen_sha_value (data) : """ Gets the Base58-encoded SHA value of the given data. :param data: :return: """ shavalue = hashlib.sha256() shavalue.update(data) return base58.b58encode_check(shavalue.digest()) def gen_binsha (data) : """ Gets the binary SHA value of the given data. :param data: :return: """ shavalue = hashlib.sha256() shavalue.update(data) return shavalue.digest()
使用 base58.b58encode_check
編碼的字串需要使用 base58.b58decode_check
來解碼。 讀者也許會納悶:為什麼要使用檢查碼而不直接查詢資料庫來確認? 在 Convergent encryption 的應用中, 雜湊值也用來作為定位器 (locator),可以簡單地解釋成資料區塊位址。使用檢查碼可以過濾掉假造的位址, 如果每次都要用資料庫檢查,會造成資料庫太大的負擔。當然最終還是需要讀取資料庫,這時也會做最後驗證。
測試程式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from __future__ import absolute_import, print_function, unicode_literalsimport osimport randomimport unittestfrom ava.vault import utils class TestUtils (unittest.TestCase) : def test_gen_binsha (self) : data = os.urandom(100 ) sha = utils.gen_binsha(data) self.assertEqual(32 , len(sha)) def test_gen_sha_value (self) : data = os.urandom(100 ) sha = utils.gen_sha_value(data) assert len(sha) == 50 or len(sha) == 49 def test_convergent_encryption (self) : plain_data = os.urandom(1024 ) key = utils.gen_binsha(plain_data) encrypted = utils.aes_encrypt(plain_data, key) decrypted = utils.aes_decrypt(encrypted, key) self.assertEqual(plain_data, decrypted) encrypted2 = utils.aes_encrypt(plain_data, key) self.assertEqual(encrypted, encrypted2)
其中,test_convergent_encryption
示範從資料產生加密金鑰然後完成加解密動作。 Note: 上述的程式片段是經過手動修改,跟山姆鍋實際的程式有所不同。
結語
密碼學是一門複雜的科學,背後理論不是多數人可以完全理解。但在網路時代,我們卻又不得不越來越重視密碼學的應用。 了解密碼學的基本概念以及如何應用,山姆鍋相信是軟體開發者所必須具備的知識跟能力。
參考資料
AES : https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
SHA-256 : https://en.wikipedia.org/wiki/SHA-2
PyCrypto : https://pypi.python.org/pypi/pycrypto
Python 常用的加解密套件。
Python base58 套件 : https://pypi.python.org/pypi/base58
Base58 : https://en.wikipedia.org/wiki/Base58
Base58 的說明。