文、專任講師 小州
因為教課需要,所以上課時候會簡單提到 linux 系統上密碼的加密方式,裡面會簡單帶到 DES, MD5, SHA-256 與 SHA-512 這類 hash 的密碼加密格式。其中系統預設都會有安裝 openssl 套件,所以有一個 openssl passwd 命令可以產生 DES 與 MD5 hash 格式的密碼。
kendlee@ubuntu:~$ openssl passwd -crypt "passwd"
eS3Et.uzGxtTQ
kendlee@ubuntu:~$ openssl passwd -1 "passwd"
$1$3PjWGBih$24Y7LLSh6UICtTH6f39Lw1
這幾年開始的 linux 系統也開始引入 SHA-256 與 SHA-512,不過查了一下 openssl 該命令似乎一直沒有支援後續的加密演算法格式,所以得求助其他工具來產生所需要的加密內容。在目前的 linux 於 glibc 內其實 crypt() 函數也不再單純只有支援傳統 DES 的加密格式,而是依據傳入不同的 salt 會提供最終 MD5/SHA-256/SHA-512 的密碼的編碼格式。
NOTES
Glibc Notes
The glibc2 version of this function supports additional
encryption algorithms.
If salt is a character string starting with the characters
"$id$" followed by a string terminated by "$":
$id$salt$encrypted
then instead of using the DES machine,id identifies the
encryption method used and this then determines how the rest of
the password string is interpreted. The following values of id
are supported:
ID | Method
─────────────────────────────────────────────────────────
1 | MD5
2a | Blowfish (not in mainline glibc; added in some
| Linux distributions)
5 | SHA-256 (since glibc 2.7)
6 | SHA-512 (since glibc 2.7)
So $5$salt$encrypted is an SHA-256 encoded password and
$6$salt$encrypted is an SHA-512 encoded one.
"salt" stands for the up to 16 characters following "$id$" in
the salt. The encrypted part of the password string is the
actual computed password. The size of this string is fixed:
MD5 | 22 characters
SHA-256 | 43 characters
SHA-512 | 86 characters
The characters in "salt" and "encrypted" are drawn from
the set [a–zA–Z0–9./]. In the MD5 and SHA implementations the
entire key is significant (instead of only the first 8 bytes in
DES).
看一下 python library 剛好有提供 crypt 的 module 可以呼叫使用,在 linux 系統環境最後底層最後會呼叫 glibc 的 crypt() 函數,所以就簡單寫了一段 python code 達成該需求。
# *-* coding: utf-8 *-*
"""
version: 0.3
author: kenduest - kenduest@gmail.com
"""
import crypt,random
####################################################################
class kenduest_crypt:
pw_chars_array = list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
salt_chars_array = list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,/')
crypt_id = { 'DES' : { "salt_prefix" : "" , "salt_length" : 2 } ,
'MD5' : { "salt_prefix" : "$1$" , "salt_length" : 16 } ,
'SHA-256' : { "salt_prefix" : "$5$" , "salt_length" : 16 } ,
'SHA-512' : { "salt_prefix" : "$6$" , "salt_length" : 16 }
}
####################################################################
def genrandompassword(self,length=100):
pw_result = []
pw_result = [ random.choice(self.pw_chars_array) for i in range(0,length) ]
return "".join(pw_result)
####################################################################
def hashpw(self, password, crypt_type) :
return crypt.crypt(password, self.gensalt(crypt_type))
####################################################################
def get_crypt_method(self):
#return self.crypt_method
return [ x for x in sorted(self.crypt_id) ]
####################################################################
def gensalt(self, ctype_type) :
supported_crypt_method = self.get_crypt_method()
self.xtype = str(ctype_type).upper()
if self.xtype not in supported_crypt_method :
raise NotImplementedError
salt_result = [ random.choice(self.salt_chars_array) for i in range(0,self.crypt_id[self.xtype]["salt_length"]) ]
result = self.crypt_id[self.xtype]["salt_prefix"] + "".join(salt_result)
return result
# end of class code
####################################################################
# test code
####################################################################
def test1() :
if len(sys.argv) != 3 :
return False
pw = kenduest_crypt();
password = sys.argv[1]
t = sys.argv[2]
try :
print(pw.hashpw(password, t))
except(NotImplementedError) :
print("Sorry, unspoorted type: %s" % t)
return True
####################################################################
def test2() :
pw = kenduest_crypt();
if len(sys.argv) == 1 :
password = pw.genrandompassword(10)
else :
password = sys.argv[1]
print("Password is %s" % password)
try :
for t in pw.get_crypt_method() :
print("using %s, result: %s" % (t, pw.hashpw(password, t)))
except(NotImplementedError) :
print("Sorry, unspoorted type: %s" % t)
return False
return True
####################################################################
# main test code entry
if __name__ == "__main__" :
import sys
if not test1() :
test2()
測試一下執行結果...。
kendlee@linux:~$ python test2.py
Password is usI3vg8DDv
using DES, result: /rgsM0QlJ/j5s
using MD5, result: $1$1QvD6tSu$zwCGapWJH7Z3YAn4Cmddp1
using SHA-256, result: $5$rCsixkEzHat,HyD3$hdpnwh.LXY1gPyMhfOi5b3LXDZtTWs2R9yLSGnrunu8
using SHA-512, result: $6$tfNEaWcMBtgBhos5$7NoS6amd94Uw35yQkE49PajSfZimYDiQ96daf4SdwGATb6G1kuQMHSa0e5fcgEqy9MzQcYbsDoKe5yv9hufZ1/
單獨指定密碼與格式執行結果..。
kendlee@linux:~$ python test2.py "123456" "md5"
$1$E1Y1p3s0$s2cc96v94Uwh0vqG3oTY21
有把產生的結果帶入到 useradd 命令內的 "-p" 參數,有確認可以正常登入成功,所以程式碼大體上應該沒有錯誤....由於 python 本身是要求版面依據要求正常縮排才可以跑的程式語言,所以網頁上顯示剪貼複製容易有錯誤,所以上面的程式碼版本可以在這個網址下載:http://expert.lccnet.com.tw/~kendlee/python/kenduest_crypt.py
這程式碼最大缺點就是還是依賴系統的 glibc 的 crypt() 呼叫,下次預期的目標是改版成為獨立計算雜湊編碼的版本,好相容於其他平台上。另外若是有一篇文章程式碼可以參考,基於 FreeBSD 上 crypt.c 的 source code 寫的 python code 版本
http://code.activestate.com/recipes/325204-passwd-file-compatible-1-md5-crypt/
留言列表