Python 計算 md5 hash 雜湊值

本篇介紹如何用 Python 來計算 md5 hash 雜湊值,以下內容將會包含使用 python 對字串作 md5 hash 的計算和對檔案內容作 md5 hash 的計算,
md5 是一種被廣泛使用的密碼雜湊函式,可以產生出一個長度為 128 bit 位元的 hash 雜湊值,用於確保資訊傳輸完整一致。
hash 的演算法有很多種,例如:SHA-1, SHA-2, MD5 等等,安全強度各有不同,依照不同情況來使用,
由於 hash 雜湊演算法所計算出來的 hash 雜湊值具有不可逆與低碰撞的特性,因此延伸出許多應用,
hash 的應用有很多種,我們今天將介紹其中兩種,第一種「儲存使用者密碼與判斷使用輸入的密碼是否相同」,第二種是「比較檔案內容是否相同」,

在 python 中要使用 md5 計算 hash 雜湊值的話,需要先 import hashlib 模組,這個 hashlib 模組是內建的,所以你不需要安裝額外的模組就可以進行以下的內容,

計算字串的 md5 hash 雜湊值

因為 hash 雜湊演算法的不可逆特性,所以常被應用在資料庫中使用者密碼的存儲,
以一個 Hello world 字串當作密碼為例,
python 3 對這個字串作 md5 hash 計算的範例如下,

1
2
3
4
5
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import hashlib

print(hashlib.md5('Hello world'.encode('utf-8')).hexdigest())

輸出結果如下所示,針對同一個字串每次計算的結果會相同,不同電腦也是,

1
3e25960a79dbc69b674cd4ec67a72c62

我們只需在資料庫存放 hash 密碼後的雜湊值,之後使用者在輸入密碼驗證時,
比對 hash 輸入密碼後的雜湊值是否與資料庫存放的雜湊值相同即可,比對結果相同表示使用者極高地可能是輸入同一組密碼,
為什麼說「極高地可能」呢?取決於hash演算法的碰撞機率,這先暫時忽略不在本篇所要討論範圍內,
如此一來我們就不必冒著安全風險將使用者密碼的明碼存在資料庫內了,

Python 2 與 Python 3 的 hashlib.md5 用法略有不同,python 2 的用法如下,

1
2
3
4
5
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hashlib

print(hashlib.md5('Hello world').hexdigest())

不同的原因是因為 hashlib.md5() 在 python 3 是接受一個 byte 變數類型,在 python 2 是接受一個 str 變數類型,
如果在 python 3 中沒有將 str 轉為 byte 的話將會看到下列錯誤訊息

1
TypeError: Unicode-objects must be encoded before hashing

知道 hashlib.md5() 在 python 2/3 差異後,以下內容將示範 python3 的寫法,而且官方已經宣佈終止 python2 的支援了,
所以寫成函式的話就會像下列這樣使用,

python3-md5.py
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import hashlib

def hash(s):
ret = hashlib.md5(s.encode())
return ret.hexdigest()

str2hash = 'Hello world'
print('md5 hash: ' + hash(str2hash))

計算檔案內容的 md5 hash 雜湊值

用 md5 計算檔案內容的 hash 雜湊值算是蠻常見的應用,舉例來說,網路上常有一些分享的程式或檔案,
但也有可能這些檔案被駭客給修改過了,甚至植入了病毒,那麼我們如果知道原始檔案的md5值,
就能夠將這個下載的檔案驗證一下,是不是計算出來的md5值也是如同原始檔的一樣,
利用這個原理,我們也能夠去計算兩個不同檔案名稱的檔案內容是否為一樣,

那麼接下來我們就來示範一下如何計算檔案內容的 md5 hash 雜湊值
hashlib.md5() 時就帶入資料

python3-md5-2.py
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import hashlib

def hashfile(filename):
with open(filename, "rb") as f:
buf = f.read()
m = hashlib.md5(buf)
return m.hexdigest()

print('md5 hash: ' + hashfile('lena.jpg'))

另一種是分批讀取檔案內容,先初始化 hashlib.md5() 再使用 update() 來更新新的資料,
分批讀取跟上面的方法比不一定比較快,但有些特殊情形會這樣使用,

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import hashlib

def hashfile(filename):
m = hashlib.md5()
with open(filename, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b''):
m.update(chunk)
return m.hexdigest()

print('md5 hash: ' + hashfile('lena.jpg'))

計算出來的結果應該與 md5sum 指令計算出來的結果相同,
不熟悉 md5sum 指令的話可以回去看我之前的 md5sum 指令用法 這篇文章,

參考
How to get MD5 sum of a string using python? - Stack Overflow
https://stackoverflow.com/questions/5297448/how-to-get-md5-sum-of-a-string-using-python
Python 計算 MD5 與 SHA 雜湊教學與範例 - G. T. Wang
https://blog.gtwang.org/programming/python-md5-sha-hash-functions-tutorial-examples/
MD5 hash in Python - GeeksforGeeks
https://www.geeksforgeeks.org/md5-hash-python/
nicolimo86/python_hash_file.py - github gist
https://gist.github.com/nicolimo86/7536613

延伸閱讀
一次搞懂密碼學中的三兄弟— Encode、Encrypt 跟Hash
https://medium.com/starbugs/what-are-encoding-encrypt-and-hashing-4b03d40e7b0c

其它相關文章推薦
Python 讀取 txt 文字檔,一篇搞懂!
Python OpenCV 彩色轉灰階(RGB/BGR to GRAY)
Python OpenCV 彩色轉HSV(RGB/BGR to HSV)
Python OpenCV 灰階轉彩色(Gray to RGB/BGR)
Python OpenCV 影像邊緣偵測 Canny Edge Detection
Python OpenCV resize 圖片縮放