密碼學系列之:bcrypt

簡介

今天要給大家介紹的一種加密算法叫做bcrypt, bcrypt是由Niels Provos和David Mazières設計的密碼哈希函數(shù)掀亥,他是基于Blowfish密碼而來的,并于1999年在USENIX上提出托享。

除了加鹽來抵御rainbow table 攻擊之外舶得,bcrypt的一個非常重要的特征就是自適應性,可以保證加密的速度在一個特定的范圍內(nèi)痒钝,即使計算機的運算能力非常高婉支,可以通過增加迭代次數(shù)的方式鸯隅,使得加密速度變慢,從而可以抵御暴力搜索攻擊向挖。

bcrypt函數(shù)是OpenBSD和其他系統(tǒng)包括一些Linux發(fā)行版(如SUSE Linux)的默認密碼哈希算法蝌以。

bcrypt的工作原理

我們先回顧一下Blowfish的加密原理。 blowfish首先需要生成用于加密使用的K數(shù)組和S-box, blowfish在生成最終的K數(shù)組和S-box需要耗費一定的時間何之,每個新的密鑰都需要進行大概4 KB文本的預處理饼灿,和其他分組密碼算法相比,這個會很慢帝美。但是一旦生成完畢碍彭,或者說密鑰不變的情況下,blowfish還是很快速的一種分組加密方法悼潭。

那么慢有沒有好處呢庇忌?

當然有,因為對于一個正常應用來說舰褪,是不會經(jīng)常更換密鑰的皆疹。所以預處理只會生成一次。在后面使用的時候就會很快了占拍。

而對于惡意攻擊者來說略就,每次嘗試新的密鑰都需要進行漫長的預處理捎迫,所以對攻擊者來說要破解blowfish算法是非常不劃算的。所以blowfish是可以抵御字典攻擊的表牢。

Provos和Mazières利用了這一點窄绒,并將其進一步發(fā)展。他們?yōu)锽lowfish開發(fā)了一種新的密鑰設置算法崔兴,將由此產(chǎn)生的密碼稱為 "Eksblowfish"("expensive key schedule Blowfish")彰导。這是對Blowfish的改進算法,在bcrypt的初始密鑰設置中敲茄,salt 和 password 都被用來設置子密鑰位谋。然后經(jīng)過一輪輪的標準Blowfish算法,通過交替使用salt 和 password作為key堰燎,每一輪都依賴上一輪子密鑰的狀態(tài)掏父。雖然從理論上來說,bcrypt算法的強度并不比blowfish更好秆剪,但是因為在bcrpyt中重置key的輪數(shù)是可以配置的损同,所以可以通過增加輪數(shù)來更好的抵御暴力攻擊。

bcrypt算法實現(xiàn)

簡單點說bcrypt算法就是對字符串OrpheanBeholderScryDoubt 進行64次blowfish加密得到的結(jié)果鸟款。有朋友會問了,bcrypt不是用來對密碼進行加密的嗎茂卦?怎么加密的是一個字符串何什?

別急,bcrpyt是將密碼作為對該字符串加密的因子等龙,同樣也得到了加密的效果处渣。我們看下bcrypt的基本算法實現(xiàn):

Function bcrypt
   Input:
      cost:     Number (4..31)                      log2(Iterations). e.g. 12 ==> 212 = 4,096 iterations
      salt:     array of Bytes (16 bytes)           random salt
      password: array of Bytes (1..72 bytes)        UTF-8 encoded password
   Output: 
      hash:     array of Bytes (24 bytes)

   //Initialize Blowfish state with expensive key setup algorithm
   //P: array of 18 subkeys (UInt32[18])
   //S: Four substitution boxes (S-boxes), S0...S3. Each S-box is 1,024 bytes (UInt32[256])
   P, S <- EksBlowfishSetup(cost, salt, password)   

   //Repeatedly encrypt the text "OrpheanBeholderScryDoubt" 64 times
   ctext <- "OrpheanBeholderScryDoubt"  //24 bytes ==> three 64-bit blocks
   repeat (64)
      ctext <-  EncryptECB(P, S, ctext) //encrypt using standard Blowfish in ECB mode

   //24-byte ctext is resulting password hash
   return Concatenate(cost, salt, ctext)

上述函數(shù)bcrypt 有3個輸入和1個輸出。

在輸入部分蛛砰,cost 表示的是輪循的次數(shù)罐栈,這個我們可以自己指定,輪循次數(shù)多加密就慢泥畅。

salt 是加密用鹽荠诬,用來混淆密碼使用。

password 就是我們要加密的密碼了位仁。

最后的輸出是加密后的結(jié)果hash柑贞。

有了3個輸入,我們會調(diào)用EksBlowfishSetup函數(shù)去初始化18個subkeys和4個1K大小的S-boxes聂抢,從而達到最終的P和S钧嘶。

然后使用P和S對"OrpheanBeholderScryDoubt" 進行64次blowfish運算,最終得到結(jié)果琳疏。

接下來看下 EksBlowfishSetup方法的算法實現(xiàn):

Function EksBlowfishSetup
   Input:
      password: array of Bytes (1..72 bytes)   UTF-8 encoded password
      salt:     array of Bytes (16 bytes)      random salt
      cost:     Number (4..31)                 log2(Iterations). e.g. 12 ==> 212 = 4,096 iterations
   Output: 
      P:        array of UInt32                array of 18 per-round subkeys
      S1..S4:   array of UInt32                array of four SBoxes; each SBox is 256 UInt32 (i.e. 1024 KB)

   //Initialize P (Subkeys), and S (Substitution boxes) with the hex digits of pi 
   P, S  <- InitialState() 
 
   //Permutate P and S based on the password and salt     
   P, S  <- ExpandKey(P, S, salt, password)

   //This is the "Expensive" part of the "Expensive Key Setup".
   //Otherwise the key setup is identical to Blowfish.
   repeat (2cost)
      P, S  <-  ExpandKey(P, S, 0, password)
      P, S  <- ExpandKey(P, S, 0, salt)

   return P, S

代碼很簡單有决,EksBlowfishSetup 接收上面我們的3個參數(shù)闸拿,返回最終的包含18個子key的P和4個1k大小的Sbox。

首先初始化书幕,得到最初的P和S新荤。

然后調(diào)用ExpandKey,傳入salt和password按咒,生成第一輪的P和S迟隅。

然后循環(huán)2的cost方次,輪流使用password和salt作為參數(shù)去生成P和S励七,最后返回智袭。

最后看一下ExpandKey的實現(xiàn):

Function ExpandKey
   Input:
      password: array of Bytes (1..72 bytes)  UTF-8 encoded password
      salt:     Byte[16]                      random salt
      P:        array of UInt32               Array of 18 subkeys
      S1..S4:   UInt32[1024]                  Four 1 KB SBoxes
   Output: 
      P:        array of UInt32               Array of 18 per-round subkeys
      S1..S4:   UInt32[1024]                  Four 1 KB SBoxes       
 
   //Mix password into the P subkeys array
   for n   <- 1 to 18 do
      Pn   <-  Pn xor password[32(n-1)..32n-1] //treat the password as cyclic
 
   //Treat the 128-bit salt as two 64-bit halves (the Blowfish block size).
   saltHalf[0]   <-  salt[0..63]  //Lower 64-bits of salt
   saltHalf[1]   <-  salt[64..127]  //Upper 64-bits of salt

   //Initialize an 8-byte (64-bit) buffer with all zeros.
   block   <-  0

   //Mix internal state into P-boxes   
   for n   <-  1 to 9 do
      //xor 64-bit block with a 64-bit salt half
      block   <-  block xor saltHalf[(n-1) mod 2] //each iteration alternating between saltHalf[0], and saltHalf[1]

      //encrypt block using current key schedule
      block   <-  Encrypt(P, S, block) 
      P2n   <-  block[0..31]      //lower 32-bits of block
      P2n+1   <- block[32..63]  //upper 32-bits block

   //Mix encrypted state into the internal S-boxes of state
   for i   <- 1 to 4 do
      for n   <- 0 to 127 do
         block   <- Encrypt(state, block xor salt[64(n-1)..64n-1]) //as above
         Si[2n]     <- block[0..31]  //lower 32-bits
         Si[2n+1]   <-  block[32..63]  //upper 32-bits
    return state

ExpandKey主要用來生成P和S,算法的生成比較復雜掠抬,大家感興趣的可以詳細研究一下吼野。

bcrypt hash的結(jié)構(gòu)

我們可以使用bcrypt來加密密碼,最終以bcrypt hash的形式保存到系統(tǒng)中两波,一個bcrypt hash的格式如下:

$2b$[cost]$[22 character salt][31 character hash]

比如:

$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
\__/\/ \____________________/\_____________________________/
 Alg Cost      Salt                        Hash

上面例子中瞳步,$2a$ 表示的hash算法的唯一標志。這里表示的是bcrypt算法腰奋。

10 表示的是代價因子单起,這里是2的10次方,也就是1024輪劣坊。

N9qo8uLOickgx2ZMRZoMye 是16個字節(jié)(128bits)的salt經(jīng)過base64編碼得到的22長度的字符嘀倒。

最后的IjZAgcfl7p92ldGxad68LJZdL17lhWy是24個字節(jié)(192bits)的hash,經(jīng)過bash64的編碼得到的31長度的字符局冰。

hash的歷史

這種hash格式是遵循的是OpenBSD密碼文件中存儲密碼時使用的Modular Crypt Format格式测蘑。最開始的時候格式定義是下面的:

  • $1$: MD5-based crypt ('md5crypt')
  • $2$: Blowfish-based crypt ('bcrypt')
  • $sha1$: SHA-1-based crypt ('sha1crypt')
  • $5$: SHA-256-based crypt ('sha256crypt')
  • $6$: SHA-512-based crypt ('sha512crypt')

但是最初的規(guī)范沒有定義如何處理非ASCII字符,也沒有定義如何處理null終止符康二。修訂后的規(guī)范規(guī)定碳胳,在hash字符串時:

  • String 必須是UTF-8編碼
  • 必須包含null終止符

因為包含了這些改動,所以bcrypt的版本號被修改成了 $2a$沫勿。

但是在2011年6月挨约,因為PHP對bcypt的實現(xiàn) crypt_blowfish 中的一個bug,他們建議系統(tǒng)管理員更新他們現(xiàn)有的密碼數(shù)據(jù)庫产雹,用$2x$代替$2a$烫罩,以表明這些哈希值是壞的(需要使用舊的算法)。他們還建議讓crypt_blowfish對新算法生成的哈希值使用頭$2y$洽故。 當然這個改動只限于PHP的crypt_blowfish贝攒。

然后在2014年2月,在OpenBSD的bcrypt實現(xiàn)中也發(fā)現(xiàn)了一個bug时甚,他們將字符串的長度存儲在無符號char中(即8位Byte)隘弊。如果密碼的長度超過255個字符哈踱,就會溢出來。

因為bcrypt是為OpenBSD創(chuàng)建的梨熙。所以當他們的庫中出現(xiàn)了一個bug時, 他們決定將版本號升級到$2b$开镣。

本文已收錄于 http://www.flydean.com/37-bcrypt/

最通俗的解讀,最深刻的干貨咽扇,最簡潔的教程邪财,眾多你不知道的小技巧等你來發(fā)現(xiàn)!

歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù)质欲,更懂你树埠!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘶伟,隨后出現(xiàn)的幾起案子怎憋,更是在濱河造成了極大的恐慌,老刑警劉巖九昧,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绊袋,死亡現(xiàn)場離奇詭異,居然都是意外死亡铸鹰,警方通過查閱死者的電腦和手機癌别,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹋笼,“玉大人展姐,你說我怎么就攤上這事⌒战ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵缤苫,是天一觀的道長速兔。 經(jīng)常有香客問我,道長活玲,這世上最難降的妖魔是什么涣狗? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮舒憾,結(jié)果婚禮上镀钓,老公的妹妹穿的比我還像新娘。我一直安慰自己镀迂,他們只是感情好丁溅,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著探遵,像睡著了一般窟赏。 火紅的嫁衣襯著肌膚如雪妓柜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天涯穷,我揣著相機與錄音棍掐,去河邊找鬼。 笑死拷况,一個胖子當著我的面吹牛作煌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赚瘦,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼粟誓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蚤告?” 一聲冷哼從身側(cè)響起努酸,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杜恰,沒想到半個月后获诈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡心褐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年舔涎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逗爹。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡亡嫌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掘而,到底是詐尸還是另有隱情挟冠,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布袍睡,位于F島的核電站知染,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏斑胜。R本人自食惡果不足惜控淡,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望止潘。 院中可真熱鬧掺炭,春花似錦、人聲如沸凭戴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勋篓,卻和暖如春吧享,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背譬嚣。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工钢颂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拜银。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓殊鞭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親尼桶。 傳聞我的和親對象是個殘疾皇子操灿,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容