现在越来越多的网站或者系统需要我们输入密码,通常我们会选择一种密码,这样记起来比较方便,一个密码闯天下。可是这样使用一种密码的也会出现其他的问题,比如:
- 不安全,若其中一个网站的密码被泄露了,其他网站也就不安全了;
- 有限制,有时候我们使用的密码在某个网站的限制规则下,并不能使用。比如我们常用的密码里都是小写字母和数字,可是这个网站要求我们的密码必须包含特殊字符。或者我们使用的密码是 20 位的,可是这个网站的密码要求最多只能 15 位。
- 网站太冷门,我们好长时间也访问不了一回。好不容易访问一回,结果第二次访问时忘记了密码。
考虑到上面的情况,我们需要再想一种密码,当遇到的情况多了,密码也就不好管理了。直接存储吧,怕被别人看到,不直接存储吧,也不知道放到什么地方。而现在网上也有很多的密码管理软件,只是我本人对这些东西不是很放心,因为它是把我们的密码加密后存储到他的服务器上,假如他的服务器被破解了,那我们所有的密码都会被泄露。因此我就想写一个属于自己的密码管理系统!
当然,如果这个网站的找回密码更简单一些的话,那就使用这个网站提供的找回密码
吧
本来是想做出一种列表式的,把所有密码加密后存储到数据库中。可是因为没有现成的空间和数据库,如果使用本地数据库,那么在这个电脑上存储的密码,在另一个电脑上是获取不到的。
因此就修改了一下策略,程序放在本地,只提供密码的加密和解密功能,使用邮箱、云盘等工具来存储加密后的字段。如果其他电脑要使用,那么就把该程序 clone 到本地,解密那个字段就能得到密码。
讲解一下我存储密码的思路。
需要的字段有:
- url : 要存储密码的网站,也可以是个单词或者字符,只要自己认识就行
- password : 加密该密码的密钥;
- content : 存储的密码;
加密的方法采用的是公认的 AES。这种加密方式的复杂程度是全世界公认的,解密起来非常的复杂。
AES 加密过程是在一个 4×4 的字节矩阵上运作,这个矩阵又称为“状态(state)”,其初值就是一个明文区块(矩阵中一个元素大小就是明文区块中的一个 Byte)。(Rijndael 加密法因支持更大的区块,其矩阵行数可视情况增加)加密时,各轮 AES 加密循环(除最后一轮外)均包含 4 个步骤:
- AddRoundKey — 矩阵中的每一个字节都与该次轮秘钥(round key)做 XOR 运算;每个子密钥由密钥生成方案产生。
- SubBytes — 通过个非线性的替换函数,用查找表的方式把每个字节替换成对应的字节。
- ShiftRows — 将矩阵中的每个横列进行循环式移位。
- MixColumns — 为了充分混合矩阵中各个直行的操作。这个步骤使用线性转换来混合每列的四个字节。
最后一个加密循环中省略 MixColumns 步骤,而以另一个 AddRoundKey 取代。
当然,里面的很多原理其实我们不用了解的很深,但是我么应该学会怎么使用,AES 的加密和解密都需要两个参数:encrypt($input, $key)
, decrypt($sStr, $sKey)
。第一个参数是我们要操作的字符串,第二个参数是加密和解密的 key 值,通常加密和解密需要的是同一个 key 值。
关于 AES 的更多信息,可以点击这里【AES-百度百科】【AES-维基百科】
从上面的内容我们可以看到,如果要加密的话则要两个字段:需要加密的字符串和 key 值;解密时需要的两个字段:需要解密的字符串和 key 值。因此,我的加密过程是这样的:
1. 确定 key 值:
$code = substr(md5(time()."wenzi".rand(1000, 9999)), 4, 10);// 需要记住
$key = md5($password.'wenzi'.$code);
从上面的代码可以看出,$key 值取决于我们输入的password
值和由系统生成的code
值。
**2. 需要加密的字符串:**不是把密码直接进行加密,而是$url和$content 拼接起来进行加密,等解密时需要验证输入的 url 是不是当时加密时的 url,如果不是则获取不到密码。
3. 加密:$content = encrypt($url.$content, $key);
4. 确认解密时需要的字段:$url(当时加密时的网址), $code(第一步中随机生成的字符串), $password(密钥), $content(需要解密的内容)
为了防止别人看到直接存储的 code 值,我们可以让程序按照我们的需要进行返回,我的程序里,code 值是长度为 10 的字符串, 返回时我们看到的 code 是:前 5 位是单数位,后 5 位是双数位,解密时需要交替输入。
$e1=""; $e2="";
for($i=0; $i<strlen($code); $i++) {
if($i%2==0){
$e1 .= $code[$i];
}else{
$e2 .= $code[$i];
}
}
$e = $e1.$e2;
当然你也可以使用如下的策略:前 5 位是 code 的后 5 位,后 5 位是 code 的前 5 位;code 的倒序等。别人只是看到经过程序处理过的 code,而不知道 code 的排列顺序
我们来使用程序加密一次试试: 加密成功后返回的数据是:
www.xiabingbao.com 3cf06d4ce6 TTkbT5BL30nIyXV+WymNgvnJwO2moLQ/LD0R4rOROOo=
三个字段分别表示:url,系统随机生成的 code 值,加密后的字符串
如果某一天我们忘了密码,则可以通过存储的上面的那 3 个字段和之前我们输入的密钥找回这个网站的密码。
$key = md5($password.'wenzi'.$code);
$result = $aes->decrypt($content, $key);
if(strpos($result, $url)>-1){
$msg = substr($result, strlen($url));
$s = array('status'=>0, 'msg'=>$msg);
}else{
$s = array('status'=>2, 'msg'=>"验证错误");
}
首先通过$key和需要解密的字段$content 来把$content解密出来$result,解密出来后再判断$result里是否包含$url 字段,如果没有包含$url 则解密失败,否则就把密码返回给用户。
假如有人获取到你了保存的加密串,如果他没有你的程序也是解不开的;即使加密串和程序都获取到了,他也没有你的密钥,因为密钥是存在你心里的,没有存放在其他任何的文件上。
好了,或许有些人觉得这样保存密码很麻烦。我呢,就把它当做一次练手的项目了,顺便存储些比较偏僻网站的密码。在写这个系统时,主要学习了 AES 算法和 bootsrap 的使用。
我的程序地址:github-password
【参考】: