Skip to content

Python 的 SHA-256 以及 AES-256 CBC 程式範例

Published: 7 分鐘

「Dropbox 做到資料加密又避免重複儲存的秘密」 這篇文章中,有提到使用 AES-256 對稱式加密,以及 SHA-256 雜湊演算法, 本文山姆鍋提供這兩者在 Python 的應用範例作為補充。

本文的範例使用 Python 2.7。 山姆鍋假設讀者對密碼學以及雜湊演算法已經有基本概念:至少知道什麼是對稱式加密(Symmetric Encryption)、 什麼是雜湊函式(Hash function)等等。

相依套件

針對 AES-256 加密,我們需要用到 PyCrypto 這個套件,由於雜湊值常常需要輸出成字串且被使用在 URL 網址,因此山姆鍋習慣使用 Base58 的來將雜湊值編碼。

使用下列指令安裝相依套件:

pip install pycrypto
pip install base58

或者安裝本文使用的特定版本:

pip install pycrypto==2.6.1
pip install base58==0.2.1

AES-256 CBC 範例

# -*- coding: utf-8 -*-
"""
Routines for convergent encryption.
"""
from __future__ import absolute_import, division, unicode_literals

from 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 的檢查模式,這在驗證輸入正確性上很有用。

# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals

import os
import hashlib
import base58

def 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),可以簡單地解釋成資料區塊位址。使用檢查碼可以過濾掉假造的位址, 如果每次都要用資料庫檢查,會造成資料庫太大的負擔。當然最終還是需要讀取資料庫,這時也會做最後驗證。

測試程式

# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

import os
import random
import unittest
from ava.vault import utils     # 包含 SHA-256 以及 AES-256 的公用函式。


class TestUtils(unittest.TestCase):

    def test_gen_binsha(self):
        data = os.urandom(100)
        sha = utils.gen_binsha(data)
        self.assertEqual(32, len(sha))      # SHA-256 的長度是 256 bits = 32 bytes

    def test_gen_sha_value(self):
        data = os.urandom(100)
        sha = utils.gen_sha_value(data)
        assert len(sha) == 50 or len(sha) == 49  # 使用 Base58 編碼,長度有兩種可能

    def test_convergent_encryption(self):
        plain_data = os.urandom(1024)
        key = utils.gen_binsha(plain_data)      # 加密的 key 使用 bytes,需要是256-bits
        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)  # 確認相同的資料跟 key, 編碼必須相同。

其中,test_convergent_encryption 示範從資料產生加密金鑰然後完成加解密動作。 Note: 上述的程式片段是經過手動修改,跟山姆鍋實際的程式有所不同。

結語

密碼學是一門複雜的科學,背後理論不是多數人可以完全理解。但在網路時代,我們卻又不得不越來越重視密碼學的應用。 了解密碼學的基本概念以及如何應用,山姆鍋相信是軟體開發者所必須具備的知識跟能力。

參考資料

  1. AES: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard

  2. SHA-256: https://en.wikipedia.org/wiki/SHA-2

  3. PyCrypto: https://pypi.python.org/pypi/pycrypto : Python 常用的加解密套件。

  4. Python base58 套件: https://pypi.python.org/pypi/base58

  5. Base58: https://en.wikipedia.org/wiki/Base58 : Base58 的說明。

郭信義 (Sam Kuo)

奔騰網路科技技術長,專長分散式系統、Web 應用與雲端服務架構、設計、開發、部署與維運。工作之餘,喜歡關注自由軟體的發展與應用,偶爾寫一下部落格文章。

你可能會有興趣的文章