视频演示链接:用python做的密码管理器
自从迷上各种网站以后,各种注册压根停不下来,密码老是记不住是接触互联网的人都会遇到的问题。
有的人不管是什么密码,都统一用相同的密码,省去了不必要的麻烦,但是如果某天随意一个账号密码泄露,坏人来入侵你简直易如反掌。(只要知道你手机号和一个密码,就可以去尝试登陆不同的网站,并不是什么平台都会有短信验证码这种安全操作的)
有的人会使用网上的密码管理器,看似安全,但是假如不法分子在软件上故意留了后门,那岂不是很危险。大数据时代,防人之心不可无啊!
这几天在学习python语言,倒不如来练练手,自己写一个简易的密码管理器,密码的规则只有你知道,设置不同符号就可以挡住暴力破解,每一个账号的密码都不相同,都不简单。
① 输入不同账户名,就可以生成不同的可设置长度的密码;
② 也能够自己修改密码,并且更新密码文件;
③ 不想记密码,要有保存密码的文件,以后可以查看;
④ 密码文件不轻易被别人看到,能够加密、自动备份、隐藏。
(大神请绕路,不要尝试找我的漏洞,因为你一定找得到~小小百姓能够使用就可以啦,本文旨在分享学习心得)
把账户名用哈希计算,得到固定长度的数据,比如SHA-512算法得到512位长度的数据,此过程不可逆。然后自行设置规则 —— 提取该数据的某一段、什么字符替换什么字符等,这个规则自行设定且要牢记。得到某一长度的密码后,连同账户名一起写入到文件中,这里可以使用python的字典来保存账号密码,对于后续多账号的管理操作会比较简单。然后使用RSA非对称加密来加密密码文件里的信息,在需要读取文件时,再用私钥去解密即可读取出文件内容。
关键点1:如果担心规则记不住,可以在提示信息里加以备注;
关键点2:RSA加解密中必须考虑到的密钥长度、明文长度和密文长度问题,对于1024长度的密钥,128字节(1024bits)-减去11字节正好是117字节,可以先对数据进行bas64加密, 再对加密后的内容使用rsa加密, 最后对rsa解密后的内容进行bas64解密。
关键点3:关于公钥密钥的保存,可以在代码里嵌入在线获取公钥密钥的方法(有点危险),也可以先把公钥密钥保存为pem文件放在本地,在代码里调用出公钥,只有在自己修改查看密码时将私钥文件放进特定路径再使用(安全系数更高)。
关键点4:每一次对密码文件的读写,都要经过加密、解密、备份的过程。加密过程是先读取文件里所有文本,经过加密再保存到文件里;解密过程是先读取文件的密文,经过解密后再将信息保存到文件里,对于文件的隐藏和备份,使用os.system()执行cmd命令。
环境: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加密,再将密文写入回文件里,以后即使比人看到文件,也无法知道其内容是什么,直到我们想用的时候再解密即可。
'''
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() #生成新的管理员信息
初次学习,请多指教!
本代码旨在学习python的相关知识,对于安全性还需要非常大的优化改进,优化思路: