1. 什么叫密码
生活中我们经常要用到密码,尤其是在这个信息爆炸的时代,例如银行卡的密码由六位数字组成,支付宝的密码也是由若干字符组成,然而这些所谓的密码是否就是“真正”的密码呢?或者说它们的本质究竟是什么?其实这些数字和字母符号等只是作为个人取款的一个凭证,它本质上是一种身份验证机制,只有你输对了密码才能取钱,输错了就不能取钱,对错仅仅是这一串字符的差别,所以,它们并不是真正意义的密码,严格的叫法应该叫做“口令”,对应的英文单词为Password、Passcode和Pin等。
所谓口令,就是一些过关的证据,由它来决定个人能否通过某个检测,例如过去抗战时期的暗语和暗号等等。口令并不真正具有机密性,除了你之外,银行本身也知道你卡的密码。还有就是,口令是可以在不知道口令的情况下绕过的,比如某个黑客或者电脑高手可以侵入一个企业的内部,获取一些有价值有机密的信息等,理论上是完全可行的;或者我们可以把口令比作一把宝库的钥匙,只有拿到了钥匙,你才能打开宝库的门,然而就算我们没有钥匙,我们也能够利用工具把门撬开或切开,也照样能拿到里面的珠宝。现在看来,口令其实只是通往目的地的一道门。
现在来说一下密码。密码究竟是什么呢?密码对应的英文为“cryptography”,意思是一种复杂而又庞大的信息处理系统。通俗地讲,密码就是传递信息的方法。现在让我们举一些简单的例子来说明什么是密码。
首先看下面这些密码:
1.Windows系统登录密码,
2.WinRAR压缩密码,
3.路由器管理登录密码,
4.无线网络密码,
5.Word文档打开权限密码,
6.网站邮箱密码,
7.QQ密码。
对于以上这些密码,哪些是密码(cryptography)?哪些是口令(password)?正确答案为:1367是口令,245是密码。首先看Windows登录密码,它是一种进入系统的验证方式,它虽然是经过加密存放的,但是却受win系统登录进程的控制,当你输入密码时,lsass和winlogon进程会负责将你输入的密码和解密后的原系统密码作对比,如果相同则允许进入系统,否则便不允许进入系统。在这个环节,系统进程是知道用户所设的密码的,它知道如何加密以及如何解密或还原。
同样的,这个密码也是可以绕过的,比如用U盘进入PE系统,在另一个操作系统下来清除原系统的密码文件(SAM)的数据。所以,Windows系统登录密码其实是口令。
再看RAR密码,当你要解压一个带有密码的RAR或ZIP文件时,WinRAR会提示你输入密码,这时如果你输入了一个错误的密码,RAR接下来会做些什么呢?它会不会判断你输入的密码是正确的还是错误的呢?答案是不会,因为WinRAR根本不知道你输入的密码的正确的还是错误的,因为世界上除了设置密码的人之外,理论上没有任何人知道此文件的密码。RAR收到密码以后根本不管它,它会一如继往地解压,直到解压后还原了明文,它才开始检验明文的CRC32,如果和解压前WinRAR所保存的原文件的CRC不同,说明有可能是密码错误引起的,这时它会提示一个“校验失败,文件损坏或密码错误”的消息,因为解压失败不一定是密码错误导致的,也有可能是数据本身不完整或丢失部分内容导致的。
这么说来,就算你采用了跟踪的方法绕过了RAR的密码验证机制,比如将不相等跳转指令改为相等跳转指令,欺骗WinRAR让它认为你输入的密码是正确的,这样的结果不过是WinRAR不再提示密码错误的消息,然而解压出来的数据已经完全不是原来的数据,而是一堆垃圾字符,文件里是没有任何有意义的信息的。WinRAR的密码相当于一种编码方式,它将原文按照某种方式编码,这种方式由算法和密码共同决定,从而转换成一种人们看不懂的没有意义的信息,所以你密码输错的话,还原的方式就会是错的,自然还原的数据就必错无疑了。
这就是密码和口令的区别,密码并不是一扇门,而是珠宝本身,只有当密码正确时,珠宝才是珠宝,密码错误的话,珠宝便不是珠宝,而是破铜烂铁。口令可以移去,而密码只能暴力破解,暴力破解需要的是时间,理论上任何密码总有一天会被破解,除了一次性密码本之外,这个下面会提到。
由此,我们对接下来的密码应该就清晰可见了,例如路由器登录密码,密码输错它就会提示错误,这是口令的特征,口令输错一般都会提示,而密码一般不提示,当然也不绝对,例如前面的WinRAR就会提示,或者我自己设计的密码钥匙工具(KeyManager),密码输错同样会提示,这并不代表它是口令,之所以提示是因为还原后的明文的单向散列值和原明文的散列值不一致,所以才会提示密码错误,事实上不一定是密码错误,也可能是文件损坏,而且密码钥匙这个工具用的加密方法是所谓的一次性密码本技术,理论上永远无法破解,因为即使有人声称破解了该文件,但是他怎么知道他破解出来的就是最初的数据呢?或者是完全错乱的数据?他永远不知道他破解成功了还是破解失败了?这是个悖论。一次性密码技术使用的方法是Xor(异或)运算,原理很简单,却永远不能被破解,不过由于该方法安全性虽强但不灵活,所以没有被广泛使用。
而无线网密码和路由器登录密码完全不同,你在输无线网密码时即使输错了也照样能联上路由器,但是却上不了网。因为无线网加密的是信号,无论你输对输错它都还原,只不过倘若你输错了它就无法还原到真实的网络数据,所以你联接的其实是一条死胡同。
Office密码我们遇到的比较多,网有有一些移除office密码的软件,几秒钟快速去除密码,试了下还真可以,这是什么原理呢?其实“移除”这个词用得不确切,应该用破解这个词更加贴切,Word默认加密方式的密码范围是256^5,也就是5个字节。有人将5个字节的数据全部遍历,生成共几个TB大小的文件,放在服务器上,然后破解时程序先从本地获取doc或xls等文件的几个字节数据,然后发送到服务器上查找密钥,找到后返回用该密钥解开文件。
注意这个找到的密钥并非一定是文件原来的密码,然而它同样可以打开加密的文件,因为密码有可能是重复的,两个密码可能指向同一个密钥。无论你密码设置得再长,密码的密钥总会在这256^5中找出来。最后两个邮箱密码和QQ密码就不用多说了,它们都是口令。
2. 初始密码英文
初始密码的英文是”password”,简写为”pw”。
3. password是锁屏密码吗
password一词在中文中被误译为密码,实际上它代表的是口令。
人们往往将口令与密码混为一谈,认为二者无异。然而,事实并非如此。口令是一种只有你知晓而他人未知的秘密信息,它本身并未经过加密处理。例如,你设置的银行卡口令,如123456,仅是你个人知悉的信息,他人无法得知。相比之下,密码则不同,它需要经过加密处理。如果你的银行卡仅限于你自己使用,那么仅依赖口令就足够了。但如果你还希望使用网上银行服务,银行则需要对你的口令进行加密,以保证数据安全。
4. 密码学系列之:bcrypt加密算法详解
简介今天要给大家介绍的一种加密算法叫做bcrypt,bcrypt是由NielsProvos和DavidMazières设计的密码哈希函数,他是基于Blowfish密码而来的,并于1999年在USENIX上提出。
除了加盐来抵御rainbowtable攻击之外,bcrypt的一个非常重要的特征就是自适应性,可以保证加密的速度在一个特定的范围内,即使计算机的运算能力非常高,可以通过增加迭代次数的方式,使得加密速度变慢,从而可以抵御暴力搜索攻击。
bcrypt函数是OpenBSD和其他系统包括一些Linux发行版(如SUSELinux)的默认密码哈希算法。
bcrypt的工作原理我们先回顾一下Blowfish的加密原理。blowfish首先需要生成用于加密使用的K数组和S-box,blowfish在生成最终的K数组和S-box需要耗费一定的时间,每个新的密钥都需要进行大概4KB文本的预处理,和其他分组密码算法相比,这个会很慢。但是一旦生成完毕,或者说密钥不变的情况下,blowfish还是很快速的一种分组加密方法。
那么慢有没有好处呢?
当然有,因为对于一个正常应用来说,是不会经常更换密钥的。所以预处理只会生成一次。在后面使用的时候就会很快了。
而对于恶意攻击者来说,每次尝试新的密钥都需要进行漫长的预处理,所以对攻击者来说要破解blowfish算法是非常不划算的。所以blowfish是可以抵御字典攻击的。
Provos和Mazières利用了这一点,并将其进一步发展。他们为Blowfish开发了一种新的密钥设置算法,将由此产生的密码称为"Eksblowfish"("expensivekeyscheleBlowfish")。这是对Blowfish的改进算法,在bcrypt的初始密钥设置中,salt和password都被用来设置子密钥。然后经过一轮轮的标准Blowfish算法,通过交替使用salt和password作为key,每一轮都依赖上一轮子密钥的状态。虽然从理论上来说,bcrypt算法的强度并不比blowfish更好,但是因为在bcrpyt中重置key的轮数是可以配置的,所以可以通过增加轮数来更好的抵御暴力攻击。
bcrypt算法实现简单点说bcrypt算法就是对字符串OrpheanBeholderScryDoubt进行64次blowfish加密得到的结果。有朋友会问了,bcrypt不是用来对密码进行加密的吗?怎么加密的是一个字符串?
别急,bcrpyt是将密码作为对该字符串加密的因子,同样也得到了加密的效果。我们看下bcrypt的基本算法实现:
FunctionbcryptInput:cost:Number(4..31)log2(Iterations).e.g.12==>212=4,096iterationssalt:arrayofBytes(16bytes)randomsaltpassword:arrayofBytes(1..72bytes)UTF-8encodedpasswordOutput:hash:arrayofBytes(24bytes)////P:arrayof18subkeys(UInt32[18])//S:Foursubstitutionboxes(S-boxes),S0...S3.EachS-boxis1,024bytes(UInt32[256])P,S<-EksBlowfishSetup(cost,salt,password)//Repeatedlyencryptthetext"OrpheanBeholderScryDoubt"64timesctext<-"OrpheanBeholderScryDoubt"//24bytes==>three64-bitblocksrepeat(64)ctext<-EncryptECB(P,S,ctext)////24-(cost,salt,ctext)上述函数bcrypt有3个输入和1个输出。
在输入部分,cost表示的是轮循的次数,这个我们可以自己指定,轮循次数多加密就慢。
salt是加密用盐,用来混淆密码使用。
password就是我们要加密的密码了。
最后的输出是加密后的结果hash。
有了3个输入,我们会调用EksBlowfishSetup函数去初始化18个subkeys和4个1K大小的S-boxes,从而达到最终的P和S。
然后使用P和S对"OrpheanBeholderScryDoubt"进行64次blowfish运算,最终得到结果。
接下来看下EksBlowfishSetup方法的算法实现:
FunctionEksBlowfishSetupInput:password:arrayofBytes(1..72bytes)UTF-8encodedpasswordsalt:arrayofBytes(16bytes)randomsaltcost:Number(4..31)log2(Iterations).e.g.12==>212=4,096iterationsOutput:P:arrayofUInt32arrayof18per-roundsubkeysS1..S4:;eachSBoxis256UInt32(i.e.1024KB)//InitializeP(Subkeys),andS(Substitutionboxes)withthehexdigitsofpiP,S<-InitialState()//,S<-ExpandKey(P,S,salt,password)//Thisisthe"Expensive"partofthe"ExpensiveKeySetup".//.repeat(2cost)P,S<-ExpandKey(P,S,0,password)P,S<-ExpandKey(P,S,0,salt)returnP,S代码很简单,EksBlowfishSetup接收上面我们的3个参数,返回最终的包含18个子key的P和4个1k大小的Sbox。
首先初始化,得到最初的P和S。
然后调用ExpandKey,传入salt和password,生成第一轮的P和S。
然后循环2的cost方次,轮流使用password和salt作为参数去生成P和S,最后返回。
最后看一下ExpandKey的实现:
FunctionExpandKeyInput:password:arrayofBytes(1..72bytes)UTF-8encodedpasswordsalt:Byte[16]randomsaltP:..S4:UInt32[1024]Four1KBSBoxesOutput:P:arrayofUInt32Arrayof18per-roundsubkeysS1..S4:UInt32[1024]Four1KBSBoxes//<-1to18doPn<-Pnxorpassword[32(n-1)..32n-1]//treatthepasswordascyclic//Treatthe128-bitsaltastwo64-bithalves(theBlowfishblocksize).saltHalf[0]<-salt[0..63]//Lower64-bitsofsaltsaltHalf[1]<-salt[64..127]//Upper64-bitsofsalt//Initializean8-byte(64-bit)bufferwithallzeros.block<-0//MixinternalstateintoP-boxesforn<-1to9do//xor64-bitblockwitha64-bitsalthalfblock<-blockxorsaltHalf[(n-1)mod2]//[0],andsaltHalf[1]//<-Encrypt(P,S,block)P2n<-block[0..31]//lower32-bitsofblockP2n+1<-block[32..63]//upper32-bitsblock//-boxesofstatefori<-1to4doforn<-0to127doblock<-Encrypt(state,blockxorsalt[64(n-1)..64n-1])//asaboveSi[2n]<-block[0..31]//lower32-bitsSi[2n+1]<-block[32..63]//upper32-bitsreturnstateExpandKey主要用来生成P和S,算法的生成比较复杂,大家感兴趣的可以详细研究一下。
bcrypthash的结构我们可以使用bcrypt来加密密码,最终以bcrypthash的形式保存到系统中,一个bcrypthash的格式如下:
$2b$[cost]$[22charactersalt][31characterhash]比如:
$2a$10$\__//\____________________/\_____________________________/AlgCostSaltHash上面例子中,$2a$表示的hash算法的唯一标志。这里表示的是bcrypt算法。
10表示的是代价因子,这里是2的10次方,也就是1024轮。
N9qo8uLOickgx2ZMRZoMye是16个字节(128bits)的salt经过base64编码得到的22长度的字符。
最后的是24个字节(192bits)的hash,经过bash64的编码得到的31长度的字符。
hash的历史这种hash格式是遵循的是OpenBSD密码文件中存储密码时使用的MolarCryptFormat格式。最开始的时候格式定义是下面的:
$1$:MD5-basedcrypt('md5crypt')
$2$:Blowfish-basedcrypt('bcrypt')
$sha1$:SHA-1-basedcrypt('sha1crypt')
$5$:SHA-256-basedcrypt('sha256crypt')
$6$:SHA-512-basedcrypt('sha512crypt')
但是最初的规范没有定义如何处理非ASCII字符,也没有定义如何处理null终止符。修订后的规范规定,在hash字符串时:
String必须是UTF-8编码
必须包含null终止符
因为包含了这些改动,所以bcrypt的版本号被修改成了$2a$。
但是在2011年6月,因为PHP对bcypt的实现crypt_blowfish中的一个bug,他们建议系统管理员更新他们现有的密码数据库,用$2x$代替$2a$,以表明这些哈希值是坏的(需要使用旧的算法)。他们还建议让crypt_blowfish对新算法生成的哈希值使用头$2y$。当然这个改动只限于PHP的crypt_blowfish。
然后在2014年2月,在OpenBSD的bcrypt实现中也发现了一个bug,他们将字符串的长度存储在无符号char中(即8位Byte)。如果密码的长度超过255个字符,就会溢出来。
因为bcrypt是为OpenBSD创建的。所以当他们的库中出现了一个bug时,他们决定将版本号升级到$2b$。
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:“程序那些事”,懂技术,更懂你!