其实本来想起的题目是“Manage your password the hard way” —— 题注
背景
首先恭喜好莱坞一众女星(We saw your boobs )。嘛...虽然到底是怎么弄的貌似还没有定论,不过看上去已经引起了一些对密码安全的重视。
密码安全往往都会强调两点,健壮性和密码复用。强密码可以提高密码安全性,但是相对增加记忆负担,因为强密码往往是没有规律的。另外一方面,即使是强密码也会因为各种各样的情况丢失。例如暴力破解和站点被攻击从后台获得密码。如果每个账户以及站点使用不同的密码(即避免密码复用),就可以相对减少损失,不过这同样会增加记忆负担。
拿小本本记密码实在是麻烦又不安全,并且逼格一点都不高。能不能有个高端点的方法来实现?
思路
通过某种加密算法可以将一个简单的字符串变成另外一个随机字符串,这样生成出来的随机字符串是强密码。
通过两个输入(用户输入的主密码(mainkey)和基于站点的输入)决定一个输出可以避免密码复用。
主密码的长度太短可以用随机字符补长 长度太长可以通过某种映射缩短
加密算法的结果如果不全在密码可用字符范围内的话可以建立一个字符集,通过某种映射将密文转换成实际要用的密码
实际上这个东西在之前一直都在考虑。昨天微博上的一位朋友@神無转转子问到这个问题:如果说想一个简单密码再将其取hash作为密码,密码安全性会怎样?于是我说了一下我的想法
光说不练也没意思 于是我就花了半天时间用Python实现这个密码管理。
需求
便携:用户不可能只在一个终端上登陆,于是要求只要用户记住自己的主密码,无论在任何终端上,只要有这个应用,输入主密码和网站名就能获得网站密码
保存:在用户的常用终端,可以保存常用网站的设置
稳定:相同的输入(主密码和站点名)一定要是相同的输出 (要不然怎么登的进去啊喂)
多变:通过少量的修改可以生成完全不一样的一套密码
加密算法的选择
以我半吊子的数学水平,密码学是根本不想看懂的。各种加密算法我也只是当他们是黑盒子,搞清楚怎么调用就行了
简单的进行MD5、SHA等显然是不合适的,因为弱密码的碰撞很多(很多人都是用同一个弱密码),这样这些信息摘要也很有可能碰撞很多,安全性下降。
试了一下Python的RSA库,发现相同的两个输入会出现不同的输出。仔细查看源代码之后发现他库里的加密函数中间有随机值。(给跪)
其他的非对称加密算法我表示完全不了解,打算使用非对称加密的想法破灭了,于是选了一个对称加密DES。
(为啥是DES?我是随便选的)
实现
基本思路就是上图中说的那样
首先要安装DES库,pyDes这个库是纯Python实现的,只要pip一下就能安装了
pip install pyDes easy_install pyDes
这是我的KeyManager.py文件
from pyDes import * import random as r import string, pickle # Any Initial Value you want, must be 8 bytes initialvalue = "babybear" # Any salt you want salt = "CuGBabyBeaR" # Any char set you want, it should be intersection of usable char sets of all sites charset = string.ascii_letters + string.digits + "/[email protected]#$%^&*()_+-={}|:\"<>?,./;'[]\\" sites = [] # Generate 24 byte key by mainkey and implement triple DES object def init(mainkey): global d r.seed(salt) l = len(mainkey) if l < 24: mainkey += ''.join([chr(r.randint(0,255))for x in xrange(24-l)]) elif l > 24: mainkey = fence(mainkey, 24) mainkey = map(lambda a:chr(a%256), mainkey) d = triple_des(mainkey, CBC, initialvalue, pad=None, padmode=PAD_PKCS5) # A mapping method, transform data to integer list def fence(data,n): res = [0 for x in xrange(n)] nd, rd = divmod(len(data), n) for i in xrange(nd): for j in xrange(n): res[j] += ord(data[i*n + j]) for i in xrange(rd): res[i] += ord(data[i-rd]) return res # Calculate key by site name def getKey(name, n = 16, _charset = None): if not _charset: _charset = charset encrypted = d.encrypt(salt + "@" + name) encrypted = fence(encrypted, n) key = map(lambda a : _charset[a % len(_charset)], encrypted) return ''.join(key) # Calculate key in saved settings def getSiteKey(i): site = sites[i] return getKey(site['name'],site['n'],site['charset']) pass # Load sites settings def load(): global sites f = open('settings','r') sites = pickle.load(f) pass # Save sites settings def save(): f = open('settings','w+') pickle.dump(sites, f) pass # Add a site setting def addSite(name, n, _charset = None): if not isinstance(name,basestring): raise TypeError("Sitename must be sting") if not isinstance(n, int): raise TypeError("Keylength must be integer") if _charset and not isinstance(name,basestring): raise TypeError("Charset must be sting") sites.append({'name':name,'n':n,'charset':charset}) pass if __name__ == '__main__': mainkey = raw_input("Input your mainkey: ") init(mainkey) while True: name = raw_input("Input site name (or \"exit\"): ") if name == 'exit': break else: print getKey(name, 16) pass
这只是一个基本应用,保存站点信息的功能就不在此演示了。
只要修改全局变量salt就可以完全改变生成的密码