今天要给大家介绍的一种加密算法叫做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$。
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:“程序那些事”,懂技术,更懂你!
2. Linux入门之sha256sum指令详解
在Linux系统中,文件的完整性和数据的安全性是用户和系统管理员非常关心的问题。为了验证文件的完整性,Linux提供了一个强大的工具——sha256sum命令。这个命令可以生成一个文件的SHA-256哈希值,从而帮助用户确认文件是否被篡改过。本文将详细介绍sha256sum命令的使用方法和一些实用的场景。
什么是SHA-256哈希函数?SHA-256(Secure Hash Algorithm 256-bit)是一种加密哈希函数,属于SHA-2家族。它将任何字符串转换成一个固定长度(256位)的哈希值。SHA-256的特点是,即使是微小的数据变化也会导致生成的哈希值发生巨大的变化,这使得它非常适合用于数据完整性校验。
如何使用sha256sum命令?sha256sum命令的基本语法如下:
这里,[options]是可选的参数,[file...]是要计算哈希值的一个或多个文件。
基本使用:要计算一个文件的SHA-256哈希值,可以使用以下命令:
将filename替换为你想要计算哈希值的文件名。例如,如果有一个名为example.txt的文件,可以使用以下命令:
执行后,系统会输出example.txt文件的SHA-256哈希值。
计算多个文件的哈希值:如果要同时计算多个文件的哈希值,可以将文件名依次列出:
输出哈希值到文件:有时,我们可能需要将哈希值保存到一个文件中,而不是直接显示在终端上。可以使用重定向操作符>>来实现:
这样,file1.txt和file2.txt的哈希值就会被写入hash_values.txt文件中。
校验哈希值:除了生成哈希值,sha256sum还可以用于校验文件的哈希值是否与已知的哈希值相匹配。这可以通过在命令后添加--check(或-c)选项来实现:
如果hash_values.txt中列出的哈希值与实际文件的哈希值相匹配,命令会显示OK;如果有不匹配的,会显示相应的错误信息。
从哈希值文件中校验文件:如果有一个包含哈希值的文件(通常称为哈希值清单),可以使用以下命令来校验文件:
--status选项会让sha256sum在每个文件校验后显示OK或ERROR,而不是在所有文件校验完成后一次性显示。
显示帮助信息:如果需要获取更多关于sha256sum命令的信息,可以使用--help选项:
常见问题解答:Q: 如何加快sha256sum命令的执行速度?答案:可以通过--parallel选项来指定同时运行的线程数,从而加快计算速度。例如:
这会让sha256sum使用4个线程来计算哈希值。
Q: 如果文件很大,sha256sum命令会消耗很多资源吗?答案:sha256sum命令在计算哈希值时,其资源消耗相对较小。但是,如果文件非常大,可能会占用较多的CPU和内存资源。可以通过调整--parallel选项来优化资源使用。
Q:sha256sum命令可以用于加密文件吗?答案:不可以。sha256sum命令用于生成文件的哈希值,而不是加密文件。哈希函数是单向的,不能用来解密或还原原始数据。
结语:sha256sum命令是Linux系统中一个非常实用的工具,它可以帮助用户验证文件的完整性和一致性。通过本文的介绍,初学者应该能够快速掌握这个命令的使用方法,并在实际工作中有效地利用它来保护数据安全。记住,数据的完整性和安全性是至关重要的,而sha256sum正是维护这一关键要素的强大工具。