視頻演示鏈接:用python做的密碼管理器
1.前言
自從迷上各種網站以後,各種註冊壓根停不下來,密碼老是記不住是接觸互聯網的人都會遇到的問題。
有的人不管是什麼密碼,都統一用相同的密碼,省去了不必要的麻煩,但是如果某天隨意一個賬號密碼泄露,壞人來入侵你簡直易如反掌。(只要知道你手機號和一個密碼,就可以去嘗試登陸不同的網站,並不是什麼平臺都會有短信驗證碼這種安全操作的)
有的人會使用網上的密碼管理器,看似安全,但是假如不法分子在軟件上故意留了後門,那豈不是很危險。大數據時代,防人之心不可無啊!
這幾天在學習python語言,倒不如來練練手,自己寫一個簡易的密碼管理器,密碼的規則只有你知道,設置不同符號就可以擋住暴力破解,每一個賬號的密碼都不相同,都不簡單。
2.需求
① 輸入不同賬戶名,就可以生成不同的可設置長度的密碼;
② 也能夠自己修改密碼,並且更新密碼文件;
③ 不想記密碼,要有保存密碼的文件,以後可以查看;
④ 密碼文件不輕易被別人看到,能夠加密、自動備份、隱藏。
(大神請繞路,不要嘗試找我的漏洞,因爲你一定找得到~小小百姓能夠使用就可以啦,本文旨在分享學習心得)
3.問題思路
把賬戶名用哈希計算,得到固定長度的數據,比如SHA-512算法得到512位長度的數據,此過程不可逆。然後自行設置規則 —— 提取該數據的某一段、什麼字符替換什麼字符等,這個規則自行設定且要牢記。得到某一長度的密碼後,連同賬戶名一起寫入到文件中,這裏可以使用python的字典來保存賬號密碼,對於後續多賬號的管理操作會比較簡單。然後使用RSA非對稱加密來加密密碼文件裏的信息,在需要讀取文件時,再用私鑰去解密即可讀取出文件內容。
關鍵點1:如果擔心規則記不住,可以在提示信息里加以備註;
關鍵點2:RSA加解密中必須考慮到的密鑰長度、明文長度和密文長度問題,對於1024長度的密鑰,128字節(1024bits)-減去11字節正好是117字節,可以先對數據進行bas64加密, 再對加密後的內容使用rsa加密, 最後對rsa解密後的內容進行bas64解密。
關鍵點3:關於公鑰密鑰的保存,可以在代碼裏嵌入在線獲取公鑰密鑰的方法(有點危險),也可以先把公鑰密鑰保存爲pem文件放在本地,在代碼裏調用出公鑰,只有在自己修改查看密碼時將私鑰文件放進特定路徑再使用(安全係數更高)。
關鍵點4:每一次對密碼文件的讀寫,都要經過加密、解密、備份的過程。加密過程是先讀取文件裏所有文本,經過加密再保存到文件裏;解密過程是先讀取文件的密文,經過解密後再將信息保存到文件裏,對於文件的隱藏和備份,使用os.system()執行cmd命令。
4.開始辦事
環境:WIN7 32位 python3.8.1
需要用到的包:hashlib,rsa,base64,os,re(注意有些自帶了)
可以使用鏡像源下載:pip install -i http://pypi.douban.com/simple --trusted-host pypi.douban.com hashlib(需要下載哪個包,就把黃色名字改成包名)
查看是否有某個工具包的方法:進入cmd,先輸入python,再輸入import+包名,如果有這個工具包,則不會報錯。
文件操作 |
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True)
參數 | 用途 |
---|---|
file | 必需,文件路徑(相對或者絕對路徑) |
mode | 可選,文件打開模式 |
模式 | 描述 |
---|---|
r | 以只讀方式打開文件,這是默認模式 |
w | 打開一個文件只用於寫入,如果該文件已存在則打開文件,並從開頭開始編輯,即原有內容會被刪除。如果該文件不存在,創建新文件。 |
2、file.read()方法
read() 方法用於從文件讀取指定的字節數,如果未給定或爲負則讀取所有
3、file.readlines()方法
readlines() 方法用於讀取所有行(直到結束符 EOF)並返回列表,該列表可以由 Python 的 for… in … 結構進行處理。 如果碰到結束符 EOF 則返回空字符串。
4、file.write()方法
write() 方法用於向文件中寫入指定字符串。如果文件打開模式帶 b,那寫入文件內容時,str (參數)要用 encode 方法轉爲 bytes 形式,否則報錯:TypeError: a bytes-like object is required, not ‘str’。
示例:
在F:\MyPwd\b.txt下,輸入如下內容:
123
456
789
{123}
新建一個.py文件,然後運行代碼
file_path = "F:\\MyPwd\\b.txt" #使用絕對路徑
f = open(file_path,'r')
read_data = f.read()
readlines_data = f.readlines()
f.close() #記得一定要關了文件
print('read_data:\n{}\ntype: {}\n\n'.format(read_data,type(read_data)))
print('readlines_data:\n{}\ntype: {}'.format(readlines_data,type(readlines_data)))
f = open(file_path,'w')
f.write('abcd')
f.close()
運行結果爲:
readlines_data: #使用readlines讀取文件,返回列表
['123\n', '456\n', '789\n', '{123}']
type: <class 'list'>
read_data: #使用read讀取文件,返回字符串
123
456
789
{123}
type: <class 'str'>
abcd #使用write寫文件,將刪除原有內容,再寫入
鑑於此,先對密碼文件的儲存格式規劃,在代碼裏,希望通過字典的方式來保存、調用信息,鍵值爲賬號名稱,其值裏再包含密碼長度、密碼內容,示例如下:
data = {
'張三': {'len': '13', 'value': '1aswedwxdsaaa'},
'李四': {'len': '12', 'value': '1aswedwxdsaa'},
'小五': {'len': '11', 'value': '1aswedwxdsa'}
}
使用write()函數時,傳入參數不能直接將字典傳進去,否則報錯:TypeError: write() argument must be str, not dict,所以可以規定,將鍵值存放在單數行,值存放在雙數行
f = open(file_path,'w') #file_path和data在前文已定義
for k,v in data.items():#data.items()返回字典的某一項內容
#k爲鍵值,寫入在文件的單數行,如'張三'
#v爲value,如{'len': '13', 'value': '1aswedwxdsaaa'}
f.write(str(k)+'\n') #主鍵值寫完,回車
#x爲鍵值,如'len' y爲該鍵值對應的value,如'13'
for x,y in v.items(): #同理操作
f.write(str(x)+' ') #以空格隔開,方便後續分離
f.write(str(y)+' ')
f.write('\n') #次鍵值寫完,回車
f.close()
運行結果如下,在txt文件可以看到:
張三 #主鍵值
len 13 value 1aswedwxdsaaa #以空格分開
李四
len 12 value 1aswedwxdsaa
小五
len 11 value 1aswedwxdsa
#最後一行是回車
現在已經完成將字典內容寫入txt文件,內容是以字符串形式儲存的,接下來是如何讀取出txt文件的字符串,再轉成字典格式呢?回憶上文的readlines()函數,使用它讀取文件返回的是list類型數據
f = open(file_path,'r')
data = f.readlines()
print(data)
f.close()
返回的結果如下
['張三\n', 'len 13 value 1aswedwxdsaaa \n', '李四\n', 'len 12 value 1aswedwxdsaa \n', '小五\n', 'len 11 value 1aswedwxdsa \n']
返回的是列表,列表的每一項內容是字符串,因此可以接着對這個返回結果操作,將有用信息提取出來
print('原字典信息:\n{}\n\n'.format(data))#file_path和data在前文已定義
f = open(file_path,'r')
information = f.readlines()
name,lenth,value = [],[],[] #分別存放姓名、密碼長度、密碼
data = {} #新建空白字典
for i in range(len(information)): #遍歷所有信息條
if i % 2 == 0: # for循環從0開始,因此對應的是文件的第一行,即鍵值
s1 = information[i].replace('\n', '')#將字符串中的'\n'替換,即刪除
name.append(s1) #添加到name列表
else: #對應的是主鍵值的value,如'len 13 value 1aswedwxdsaaa \n'
s2 = information[i].split() #將該字符串以空格分開組成新的列表,如['len', '13', 'value', '1aswedwxdsaaa']
lenth.append(s2[1]) #index=1對應長度的數值
value.append(s2[3]) #index=3對應密碼的數值
for j in range(len(name)): #在所有賬號中遍歷
#新建字典,格式如下:
temp = {name[j]:{'len':lenth[j],'value':value[j]}}
data.update(temp) #在data字典裏增加新的內容
f.close()
print('讀取出的信息:\n{}\n\n'.format(data))
運行結果如下:
原字典信息:
{'張三': {'len': '13', 'value': '1aswedwxdsaaa'}, '李四': {'len': '12', 'value': '1aswedwxdsaa'}, '小五': {'len': '11', 'value': '1aswedwxdsa'}}
讀取出的信息:
{'張三': {'len': '13', 'value': '1aswedwxdsaaa'}, '李四': {'len': '12', 'value': '1aswedwxdsaa'}, '小五': {'len': '11', 'value': '1aswedwxdsa'}}
到這裏就可以實現,將字典寫入文件,並且能從文件讀出數據組成字典。值得注意的是,每次寫入字典,都會覆蓋原有內容,因此要使用dict.update()形式來增加字典內容,保證數據的完整。
加密操作 |
簡單的理解就是,使用公鑰加密的數據,只有私鑰才能解開,公鑰可以給需要傳信息給你的人,讓他加密,你再用私鑰解開,加密的密文不是固定的,但是用私鑰解開,得到的數據是固定的。
加密密鑰(即公開密鑰)PK是公開信息,而解密密鑰(即祕
密密鑰)SK是需要保密的。RSA密鑰至少爲500位長,一般推薦使用1024位。RSA密鑰長度隨着保密級別提高,增加很快。由於RSA的特性,一個1024位的密鑰只能加密117位字節數據,當數據量超過117位字節的時候,程序就會拋出異常。
1、生成公私鑰對方法
import rsa
# 1、接收者(A)生成512位公私鑰對
# a. lemon_pub爲PublicKey對象, lemon_priv爲PrivateKey對象
# b. 512爲祕鑰的位數, 可以自定義指定, 例如: 128、256、512、1024、2048等
pubkey, privkey = rsa.newkeys(1024)
print('公鑰:\n{}'.format(pubkey))
print('私鑰:\n{}'.format(privkey))
運行結果爲:
公鑰:
PublicKey(136043793246195131376598853070437698306114833972959095011027114725856352489960372449126256960893386324011025727709760353355161658861524305075813113012558114176160175699651050556689236540303816483879189644670406301771660213339983982106027499836359428406193843275085402340802395917935876461206122900474103550081, 65537)
私鑰:
PrivateKey(136043793246195131376598853070437698306114833972959095011027114725856352489960372449126256960893386324011025727709760353355161658861524305075813113012558114176160175699651050556689236540303816483879189644670406301771660213339983982106027499836359428406193843275085402340802395917935876461206122900474103550081, 65537, 67111644348222967139256312003406484676391848609880945751354297863602787372025250488735399660431255319213214852325503947754281954178450047806598354045723899698083205408652913843867050341620368862896794412934853121165578244016362213359581410238054965306729656494042087397877482874513921173331474467024976125353, 49941274253535477245884116206745790020259530246041428907030691911368756042123890501630166746194845032347468161709234004166916134481594454244519551020710967997946663, 2724075332069930615036147089954740192436418597964199394724831470277180639582666198461658504095140678009881992486078909443511818268222744093220887)
2、發送者加密方法
# 2、發送者(B)使用接收者(A)的公鑰去加密消息
# rsa只能處理字節類型, 故字符串類型需要轉化爲字節類型
love_talk = "little girl, I love you very much!".encode("utf-8")
cryto_info = rsa.encrypt(love_talk, pubkey) # 使用接收者(A)的公鑰加密
print(cryto_info)
運行結果爲:(bytes型數據)
b"1\x06\xa6\x89\x8b\xd7\xfb\xc7=\xe6\x0b\x7f\x03\xceqp\x19$l\x86\x9e\x1e\xee\xd2\xb0b\xedK\xb9\xd9\xc8\x83\xf2\x8c~\xe0\xebP\xd0\x04\xbb\x9f\x1f\xb9O\x95\x1c\xf7\xfbK\xf29\xff\x7f\xbb\x0e\xb2\x99\x89\xed*\xdf\xd2\x84K#\xdb\x14r$'\x03d!{&z\xf4\x0cQcc\xcb\x96\xa7\xcb\x010\x90^O`\xe4\xb9\x0fY&L8i\x8d\x8b\xcf\x86\x00\x80\xbb\t\xee8\xae1\xb4\xa0O\x12_-\xda\xf1\x05\x0b\xf7\xd5Y\xed\xf6c"
3、接收者解密方法
# 3. 接收者(A)使用自己的私鑰去解密消息
talk_real = rsa.decrypt(cryto_info, privkey)
talk_real2 = talk_real.decode("utf-8")
print(talk_real2)
解密結果爲:
little girl, I love you very much!
4、保存密鑰方法
with open('F:\\MyPwd\\rsa\\public.pem' ,'w+') as f:
f.write(pubkey.save_pkcs1().decode())
with open('F:\\MyPwd\\rsa\\private.pem' ,'w+') as f:
f.write(privkey.save_pkcs1().decode())
打開private.pem文件可以看到的結果如下,和上面直接打印出的密鑰有所不同,這是因爲寫入文件時,需要將數據進行解碼
-----BEGIN RSA PRIVATE KEY-----
MIICYAIBAAKBgQCD051f1t6GplBhOIMfx8uGpzq3A73vqx76oxJ14MROTqcunatD
EzmRLPgELWDufrt8Hv0ZQ3GYC3g4Ozlk0ukhWVQFaFbtNyu4HhnzEnnZ4FQAerjG
CsbSaPtJNPq1Dgql1RQiIC0uSEuYvvFckI7eyH6hOpvqySCtXKRdRdnQ0QIDAQAB
AoGAAIXiZfLwRxB52SjkPEgKoqofLYKySjUfllb3R8hwfu8I8sJlX4q/+7d19G5J
qCiQjdmBn4wI81V4UKDLhMeYzsKoaWzLUaURCFkv7nH7p4nv0nU/fIdx2KymzBYt
iOz/mQ1NPLc4vZ0PJ0ncPQrVbCavYs2H4d/8o5cDvrI8eAECRQC0dsNc875HK8y4
acebaOjwHGX4Ap+bLYKL5P8zfgHLLV+putVcx5N4AnRyrLNjfzYS0FZHXhAFgCOz
QmR/KkorcXNIoQI9ALsBOQgiZiWO3GEnMQqnM6qhmZg9727rWLd+/AvhxFs7Yy4Q
e1Tpa4U66R4FljYBOcuCfGIDNvIcL86qMQJECKTDmLkn/PqxFIgkgmIU/iMuEyH1
CRa18QNn4cyAQ34J3fRP8eCxRIdBkpiJAxP9wArwhvyPYeQQUa61Z43b/ZaygeEC
PECUI4XTm0LNGv3R8vWi2AzM0aXpfY3oaDK1/4R66rw2vgFiX7TrBt5zgZ2EgGMV
+Ud2QE34njjt0vSjgQJFAI3XEB2gqY/g4LbgCQpbaWlaNA/w8EPvNEe2SFlsKJBU
uWe6l78I/sbvdRLfJ12XIN4I3Jnl8C3gwMI9iLaUz2MxuiUe
-----END RSA PRIVATE KEY-----
5、導出密鑰方法
f = open('F:\\MyPwd\\rsa\\public.pem','r')
pubkey = f.read().replace('-----BEGIN RSA PUBLIC KEY-----\n','')
pubkey = pubkey.replace('-----END RSA PUBLIC KEY-----\n','')
f.close()
f = open('F:\\MyPwd\\rsa\\private.pem','r')
privkey = f.read().replace('-----BEGIN RSA PRIVATE KEY-----\n','')
privkey = privkey.replace('-----END RSA PRIVATE KEY-----\n','')
f.close()
print('pubkey:\n{}'.format(pubkey))
print('privkey:\n{}'.format(privkey))
導出結果爲:可以發現導出的結果和在文件裏看到的是一樣的
pubkey:
MIGJAoGBAIPTnV/W3oamUGE4gx/Hy4anOrcDve+rHvqjEnXgxE5Opy6dq0MTOZEs
+AQtYO5+u3we/RlDcZgLeDg7OWTS6SFZVAVoVu03K7geGfMSedngVAB6uMYKxtJo
+0k0+rUOCqXVFCIgLS5IS5i+8VyQjt7IfqE6m+rJIK1cpF1F2dDRAgMBAAE=
privkey:
MIICYAIBAAKBgQCD051f1t6GplBhOIMfx8uGpzq3A73vqx76oxJ14MROTqcunatD
EzmRLPgELWDufrt8Hv0ZQ3GYC3g4Ozlk0ukhWVQFaFbtNyu4HhnzEnnZ4FQAerjG
CsbSaPtJNPq1Dgql1RQiIC0uSEuYvvFckI7eyH6hOpvqySCtXKRdRdnQ0QIDAQAB
AoGAAIXiZfLwRxB52SjkPEgKoqofLYKySjUfllb3R8hwfu8I8sJlX4q/+7d19G5J
qCiQjdmBn4wI81V4UKDLhMeYzsKoaWzLUaURCFkv7nH7p4nv0nU/fIdx2KymzBYt
iOz/mQ1NPLc4vZ0PJ0ncPQrVbCavYs2H4d/8o5cDvrI8eAECRQC0dsNc875HK8y4
acebaOjwHGX4Ap+bLYKL5P8zfgHLLV+putVcx5N4AnRyrLNjfzYS0FZHXhAFgCOz
QmR/KkorcXNIoQI9ALsBOQgiZiWO3GEnMQqnM6qhmZg9727rWLd+/AvhxFs7Yy4Q
e1Tpa4U66R4FljYBOcuCfGIDNvIcL86qMQJECKTDmLkn/PqxFIgkgmIU/iMuEyH1
CRa18QNn4cyAQ34J3fRP8eCxRIdBkpiJAxP9wArwhvyPYeQQUa61Z43b/ZaygeEC
PECUI4XTm0LNGv3R8vWi2AzM0aXpfY3oaDK1/4R66rw2vgFiX7TrBt5zgZ2EgGMV
+Ud2QE34njjt0vSjgQJFAI3XEB2gqY/g4LbgCQpbaWlaNA/w8EPvNEe2SFlsKJBU
uWe6l78I/sbvdRLfJ12XIN4I3Jnl8C3gwMI9iLaUz2MxuiUe
6、加密長字符串方法
加密的字段長短規則如下:
加密的 plaintext 最大長度是 證書key位數/8 - 11, 例如1024 bit的證書,被加密的串最長 1024/8 - 11=117,那麼對於 2048bit的證書,被加密的長度最長2048/8 - 11 =245,解決辦法是 分塊 加密,然後分塊解密就行了,因爲 證書key固定的情況下,加密出來的串長度是固定的。也就是說,如果使用2048bit的證書,並且被加密的字符段是小於245個,那麼被加密出來的字符長度是344個,以此類推,被加密的字符串可以是688個,1032個等。
import rsa
pubkey, privkey = rsa.newkeys(1024)
data = ‘little girl, I love you very much!’*100
love_talk = data.encode(“utf-8”)
cryto_info = rsa.encrypt(love_talk, pubkey)
報錯信息:
raise OverflowError(’%i bytes needed for message, but there is only’
OverflowError: 3400 bytes needed for message, but there is only space for 117
解決方法如下,可以加密足夠長的字符串:
import rsa
import base64
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
#密鑰一定要經過解碼,否則無法直接使用,即bytes轉str
pubkey = '''
MIGJAoGBAIPTnV/W3oamUGE4gx/Hy4anOrcDve+rHvqjEnXgxE5Opy6dq0MTOZEs
+AQtYO5+u3we/RlDcZgLeDg7OWTS6SFZVAVoVu03K7geGfMSedngVAB6uMYKxtJo
+0k0+rUOCqXVFCIgLS5IS5i+8VyQjt7IfqE6m+rJIK1cpF1F2dDRAgMBAAE=
'''
privkey = '''
MIICYAIBAAKBgQCD051f1t6GplBhOIMfx8uGpzq3A73vqx76oxJ14MROTqcunatD
EzmRLPgELWDufrt8Hv0ZQ3GYC3g4Ozlk0ukhWVQFaFbtNyu4HhnzEnnZ4FQAerjG
CsbSaPtJNPq1Dgql1RQiIC0uSEuYvvFckI7eyH6hOpvqySCtXKRdRdnQ0QIDAQAB
AoGAAIXiZfLwRxB52SjkPEgKoqofLYKySjUfllb3R8hwfu8I8sJlX4q/+7d19G5J
qCiQjdmBn4wI81V4UKDLhMeYzsKoaWzLUaURCFkv7nH7p4nv0nU/fIdx2KymzBYt
iOz/mQ1NPLc4vZ0PJ0ncPQrVbCavYs2H4d/8o5cDvrI8eAECRQC0dsNc875HK8y4
acebaOjwHGX4Ap+bLYKL5P8zfgHLLV+putVcx5N4AnRyrLNjfzYS0FZHXhAFgCOz
QmR/KkorcXNIoQI9ALsBOQgiZiWO3GEnMQqnM6qhmZg9727rWLd+/AvhxFs7Yy4Q
e1Tpa4U66R4FljYBOcuCfGIDNvIcL86qMQJECKTDmLkn/PqxFIgkgmIU/iMuEyH1
CRa18QNn4cyAQ34J3fRP8eCxRIdBkpiJAxP9wArwhvyPYeQQUa61Z43b/ZaygeEC
PECUI4XTm0LNGv3R8vWi2AzM0aXpfY3oaDK1/4R66rw2vgFiX7TrBt5zgZ2EgGMV
+Ud2QE34njjt0vSjgQJFAI3XEB2gqY/g4LbgCQpbaWlaNA/w8EPvNEe2SFlsKJBU
uWe6l78I/sbvdRLfJ12XIN4I3Jnl8C3gwMI9iLaUz2MxuiUe
'''
def public_long_encrypt(data, charset='utf-8'):
global pubkey
# base64.b64decode()解碼一個字符串
pub_key = RSA.importKey(base64.b64decode(pubkey))# 導入公鑰
pub_key_obj = Cipher_pkcs1_v1_5.new(pub_key)
data = data.encode(charset) #將數據進行編碼
length = len(data)
default_length = 117
res = []
for i in range(0, length, default_length):
res.append(pub_key_obj.encrypt(data[i:i + default_length]))
byte_data = b''.join(res)
return base64.b64encode(byte_data)
def private_long_decrypt(data, sentinel=b'decrypt error'):
global privkey
pri_key = RSA.importKey(base64.b64decode(privkey))
pri_key_obj = Cipher_pkcs1_v1_5.new(pri_key)
data = base64.b64decode(data) # 將數據進行解碼
length = len(data)
default_length = 128
res = []
for i in range(0, length, default_length):
res.append(pri_key_obj.decrypt(data[i:i + default_length], sentinel))
return str(b''.join(res), encoding = "utf-8")
data = 'little girl, I love you very much!'*100
cryto_info = public_long_encrypt(data)
print('加密信息:\n{}'.format(cryto_info))
decrypt_info = private_long_decrypt(cryto_info)
print('解密信息:\n{}'.format(decrypt_info))
運行結果如下:
到這裏,就可以實現對文本進行加密解密了,主要思路就是通過讀取文件得到字符串,對長字符串進行rsa加密,再將密文寫入迴文件裏,以後即使比人看到文件,也無法知道其內容是什麼,直到我們想用的時候再解密即可。
5.全部代碼
'''
By Afeng
date:2020/02/28
virsion:1.0
'''
import os
import hashlib
import rsa
import base64
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
import re
initval = {
'測試1': {'len': '3', 'value': '1'},
'2': {'len': '2', 'value': '2'},
'1': {'len': '1', 'value': '3'}
}
#如果有關於路徑的錯誤,這裏最好用絕對路徑
public_path = 'F:\\MyPwd\\public.pem'
private_path = 'F:\\MyPwd\\private.pem'
file_path = 'F:\\MyPwd\\a'
path = 'F:\\MyPwd'
target_path = 'G:\\MyPwd'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
加密相關
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
# 函數功能:將賬號進行哈希運算,得到lenth長度
# 入口參數:賬戶名、密碼長度
# 返回值:經過密碼規則處理後的密碼
def hash_pwd(value,lenth):
m = hashlib.sha512()
m.update(value.encode('utf-8'))#直接加密字符串會報錯
pwd = m.hexdigest() #得到512位數據
pwd = pwd.replace('c','&') #將字符串裏的'c'符號替換成'&'
pwd = str(pwd)[3:lenth+3] #取lenth長度的數據
return pwd
# 函數功能:生成新的公鑰密鑰並且保存到本地
# 入口參數:無
# 返回值:無
def new_keys():
global public_path,private_path
pub_key, priv_key = rsa.newkeys(1024)
with open(public_path ,'w+') as f:
f.write(pub_key.save_pkcs1().decode())
with open(private_path ,'w+') as f:
f.write(priv_key.save_pkcs1().decode())
# 函數功能:從本地文件導入密鑰
# 入口參數:無
# 返回值:無
def import_keys():
global public_path,private_path,pubkey,privkey
f = open(public_path,'r')
pubkey = f.read().replace('-----BEGIN RSA PUBLIC KEY-----\n','')#去掉前面後面不必要的數據
pubkey = pubkey.replace('-----END RSA PUBLIC KEY-----\n','')
f.close()
f = open(private_path,'r')
privkey = f.read().replace('-----BEGIN RSA PRIVATE KEY-----\n','')
privkey = privkey.replace('-----END RSA PRIVATE KEY-----\n','')
f.close()
# 函數功能:加密長字符串
# 入口參數:需要加密的字符串
# 返回值:返回b64編碼的數據,即密文
def public_long_encrypt(data, charset='utf-8'):
global pubkey
pub_key = RSA.importKey(base64.b64decode(pubkey))
pub_key_obj = Cipher_pkcs1_v1_5.new(pub_key)
data = data.encode(charset)
length = len(data)
default_length = 117
res = []
for i in range(0, length, default_length):
res.append(pub_key_obj.encrypt(data[i:i + default_length]))
byte_data = b''.join(res)
return base64.b64encode(byte_data)
# 函數功能:解密長字符串
# 入口參數:需要解密的b64編碼數據
# 返回值:返字符串
def private_long_decrypt(data, sentinel=b'decrypt error'):
global privkey
pri_key = RSA.importKey(base64.b64decode(privkey))
pri_key_obj = Cipher_pkcs1_v1_5.new(pri_key)
data = base64.b64decode(data)
length = len(data)
default_length = 128
res = []
for i in range(0, length, default_length):
res.append(pri_key_obj.decrypt(data[i:i + default_length], sentinel))
return str(b''.join(res), encoding = "utf-8")
# 函數功能:加密本地某個文件
# 入口參數:無
# 返回值:無
def lock_data():
global path,target_path
import_keys() #獲取密鑰
data = read_data() #讀取文件中的數據 —— 字典
encrypt = public_long_encrypt(data) #加密該數據
bs = str(encrypt, encoding = "utf8") #將密文轉成str型
save_data(bs) #函數在後面,保存數據到本地
os.system("attrib +H +R +S %s"%path) #將文件設置爲隱藏、只讀、系統文件
# 函數功能:解密本地某個文件
# 入口參數:無
# 返回值:無
def unlock_data():
global path
os.system("attrib +H -R -S %s"%path) #將文件設置爲隱藏、可改、非系統文件
import_keys() #獲取密鑰
bs = read_data() #讀取文件中的數據 —— 密文
sb = bytes(bs, encoding = "utf8") #將字符串數據轉成bytes型
decrypt = private_long_decrypt(sb) #解密該數據
save_data(decrypt) #函數在後面,保存數據到本地
# 函數功能:驗證是否是本人
# 入口參數:無
# 返回值:True/False
def check_is_me():
permit = input('請輸入密鑰:')
data = read_file() #讀取文件中的數據
count_cipher = list(data.keys())[0] #找到數據的第一項裏的name
if permit == data[count_cipher]['value']: #如果是管理員,返回True
return True
print('密鑰錯誤!')
print('%s'%data['提示']['value'])
return False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
文件相關
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
# 函數功能:保存數據到本地文件
# 入口參數:字符串型數據
# 返回值:無
def save_data(data):
global file_path
f = open(file_path,'w')
f.write(data)
f.close()
# 函數功能:讀取本地文件數據
# 入口參數:無
# 返回值:字符串型數據
def read_data():
global file_path
f = open(file_path,'r')
data = f.read()
f.close()
return data
# 函數功能:保存數據到本地文件
# 入口參數:字典型數據
# 返回值:無
def write_file(data):
global file_path,path,target_path
unlock_data() #先解密本地文件,得到正常數據
f = open(file_path,'w')
for k,v in data.items(): #本函數具體講解參照上文
f.write(str(k)+'\n')
for x,y in v.items():
f.write(str(x)+' ')
f.write(str(y)+' ')
f.write('\n')
f.close()
os.system("ROBOCOPY %s %s /E /MT:10"%(path,target_path)) #複製密碼文件至特定路徑
lock_data() #寫入數據完成後,加密密碼文件
# 函數功能:讀取本地文件數據
# 入口參數:無
# 返回值:字典型數據
def read_file():
global file_path
unlock_data() #先解密本地文件,得到正常數據
f = open(file_path,'r')
information = f.readlines()#本函數具體講解參照上文
name,lenth,value = [],[],[]
data = {}
if len(information) < 2:
return 0
for i in range(len(information)):
if i % 2 == 0:
s1 = information[i].replace('\n', '')
name.append(s1)
else:
s2 = information[i].split()
lenth.append(s2[1])
value.append(s2[3])
for j in range(len(name)):
temp = {name[j]:{'len':lenth[j],'value':value[j]}}
data.update(temp)
f.close()
lock_data() #讀取數據完成後,加密密碼文件
return data
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
功能相關
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
# 函數功能:格式化打印字典數據
# 入口參數:字典型數據
# 返回值:無
def report_info(data):
for key,value in data.items():
print(key+": "+str(value))
# 函數功能:新建密鑰者信息
# 入口參數:無
# 返回值:無
def new_cipher():
data = {} #創建空字典
count_cipher = input('重新生成密鑰,請輸入密文:')
tip_info = (input('請輸入提示信息:')+'(3-,我最喜歡的<數字>和&)')
new_tip = {'提示':{'len':0,'value':tip_info}}
len_cipher = input('請設置密鑰長度:')
cipher = hash_pwd(count_cipher,int(len_cipher)) #設置長度
new_cipher = {count_cipher:{'len':len_cipher,'value':cipher}}
data.update(new_cipher)
data.update(new_tip)
write_file(data) #將包含管理員信息、提示信息寫入密碼文件
print('新密鑰已生成!%s'%cipher)
# 函數功能:生成新的賬戶密碼
# 入口參數:賬戶名、字典數據
# 返回值:無
def new_pwd(count,data):
lenth = int(input('請設置密碼長度:'))
pwd = hash_pwd(count,lenth) #經過哈希得到密碼
new_count = {count:{'len':lenth,'value':pwd}}
data.update(new_count) #將新的賬戶信息添加到原有信息上
write_file(data) #保存到本地
print('新賬戶密碼保存成功!')
print(count,': ',data[count])
# 函數功能:更改密碼
# 入口參數:賬戶名、字典數據
# 返回值:無
def change_pwd(count,data):
new_pwd = input('請輸入新的密碼:')
lenth = len(new_pwd)
new_count = {count:{'len':lenth,'value':new_pwd}}
data.update(new_count)
write_file(data)
print('新賬戶密碼保存成功!')
# 函數功能:初始化密碼
# 入口參數:賬戶名、字典數據
# 返回值:無
def init_pwd(count,data):
lenth = 10 #默認生成的密碼長度爲10
new_count = {count:{'len':lenth,'value':(hash_pwd(count,lenth))}}
data.update(new_count)
write_file(data)
print('密碼初始化成功!(默認10位)')
# 函數功能:刪除某個賬戶密碼
# 入口參數:字典數據
# 返回值:無
def del_count(data):
report_info(data) #顯示密碼文件的所有信息
key = input('請輸入需要刪除的賬戶名:')
if key in data:
data.pop(key) #刪除字典中的某個數據
write_file(data) #重新保存文件
print('已刪除 %s 的賬戶密碼!'%key)
else:
print('請輸入正確的賬戶名!')
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
主函數
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#功能描述:
#第一次操作時,需要生成新的管理員信息
#依靠管理員的密鑰對密碼文件進行操作,該密鑰非常重要
while True:
count = input('請輸入要查詢的賬號:')
data = read_file()
if data:
if (check_is_me()): #檢驗是否爲管理員
if count in data.keys(): #尋找該賬戶是否存在
print(count,': ',data[count])
if (input('是否需要更改密碼?(Y/N):')) == 'Y':
if (input('是否需要初始化密碼?(Y/N):')) == 'Y':
init_pwd(count,data) #初始化密碼,默認10位的長度
else:
change_pwd(count,data) #更改密碼
elif count == 'ls':
print(list(data.keys())[2:]) #查看密碼文件中的所有賬戶名
if (input('是否需要刪除某個密碼?(Y/N):')) == 'Y':
if (check_is_me()):
del_count(data) #顯示密碼文件所有信息,並且刪除某個賬戶
else:
if (input('是否需要保存新賬號?(Y/N):')) == 'Y':
new_pwd(count,data) #創建新的賬戶密碼
else: #如果讀取的文件爲空
write_file(initval) #寫入一個初始數據
print('文件空白!')
if (input('是否重新生成新密鑰?(Y/N):')=='Y'):
new_cipher() #生成新的管理員信息
6.結束了
初次學習,請多指教!
本代碼旨在學習python的相關知識,對於安全性還需要非常大的優化改進,優化思路:
- 公鑰放在代碼裏,將密鑰加密成某一段密碼(是否可以爲中文?其長度取決於你的加密方式)
- 代碼裏嵌入關於解密的功能,運行py文件時,第一步先要求輸入“密文”來解密,解密後的信息(即密鑰)直接用去解密公鑰所加密的數據,如果能夠解開,則說明是管理員,否則退出!因此該“密文”尤爲重要!
- 這樣的好處就是,私鑰只有自己知道,並且並不直接是私鑰;所剩下的文件就只有密碼文件和py可執行文件了,即使最終被別人發現了文件,由於沒有私鑰,文件將無法解密。