BlowFish 加密算法Bcrypt

Blowfish是1993年布魯斯·施奈爾(Bruce Schneier)開發的對稱密鑰區塊加密算法,區塊長爲64位,密鑰爲1至448位的可變長度。與DES等算法相比,其處理速度較快。由於其無須受權便可使用,做爲一種自由受權的加密方式在SSH、文件加密軟件等被普遍地使用。php

關於此算法的發明者:html

布魯斯·施奈爾 (Bruce Schneier,1963年1月15日-)是一位美國的密碼學學者、信息安全專家與做家。他撰寫了數本信息安全與密碼學相關的書籍,而且創辦了BT公司並擔任其首席技術官(CTO)。算法

分組密碼(Block cypher)

分組密碼(Block cypher,又稱分塊密碼),是一種對稱密鑰密碼。它的特色是將明文分紅多個等長的組,並用相同的密碼算法和密鑰對每組分別進行加密和解密。其中典型的如DES和AES做爲美國政府覈定的標準加密算法,應用領域從電子郵件加密到銀行交易轉賬,很是普遍。
blowfish屬於分組密碼中的一種。安全

要了解blowfish加密算法,也許咱們應該先了解一下Feistel 密碼,由於blowfish加密算法也是feistel密碼算法中的一種:
假設F是輪函數,而後K0到Kn做爲0到n輪的sub-keys,
基本的操做以下:dom

把明文分爲相等的兩塊:L0和R0,
對於每一輪i=0,1,2,…,n 進行以下運算:
Li+1 = Ri
Ri+1 = Li ^ F(Ri,Ki)
最後獲得密文(Rn+1,Ln+1)
解密(Rn+1,Ln+1)是經過以下步驟來完成的:
令i=n,n-1,…,0
Ri = Li+1
Li = Ri+1 ^ F(Li+1,Ki)
最後又獲得原來的(L0,R0)了函數

這麼說可能很差理解,看下圖(來自wikipedia)就清楚了:

簡單地說:
就是一種分組密碼,通常分爲64位一組,一組分左右部分,進行通常爲16輪的迭代運算,每次迭代完後交換左右位置,能夠本身進行設計的有:
分組大小
密鑰長度
輪次數
子密鑰生成
輪函數加密

而後應該瞭解一下塊密碼的工做模式
from wikipedia:url

密碼學中,塊密碼的工做模式容許使用同一個塊密碼密鑰對多於一塊的數據進行加密,並保證其安全性。[1][2] 塊密碼自身只能加密長度等於密碼塊長度的單塊數據,若要加密變長數據,則數據必須先被劃分爲一些單獨的密碼塊。一般而言,最後一塊數據也須要使用合適填充方式將數據擴展到符合密碼塊大小的長度。一種工做模式描述了加密每一數據塊的過程,並經常使用基於一個一般稱爲初始化向量的附加輸入值以進行隨機化,以保證安全[1]。spa

工做模式主要用來進行加密和認證。[1][3] 對加密模式的研究曾經包含數據的完整性保護,即在某些數據被修改後的狀況下密碼的偏差傳播特性。後來的研究則將完整性保護做爲另外一個徹底不一樣的,與加密無關的密碼學目標。部分現代的工做模式用有效的方法將加密和認證結合起來,稱爲認證加密模式。[2]設計

雖然工做模式一般應用於對稱加密[2],它亦能夠應用於公鑰加密,例如在原理上對RSA進行處理,但在實用中,公鑰密碼學一般不用於加密較長的信息,而是使用混合加密方案

通常來講經常使用的加密模式有這麼幾種:

電子密碼本(ECB)
密碼塊連接(CBC):在CBC模式中,每一個平文塊先與前一個密文塊進行異或後,再進行加密。在這種方法中,每一個密文塊都依賴於它前面的全部平文塊。同時,爲了保證每條消息的惟一性,在第一個塊中須要使用初始化向量。
填充密碼塊連接(PCBC)
密文反饋(CFB)
輸出反饋(OFB)
計數器模式(CTR)

初始化向量(IV)

初始化向量(IV,Initialization Vector)是許多工做模式中用於隨機化加密的一塊數據,所以能夠由相同的明文,相同的密鑰產生不一樣的密文,而無需從新產生密鑰,避免了一般至關複雜的這一過程。

初始化向量與密鑰相比有不一樣的安全性需求,所以IV一般無須保密,然而在大多數狀況中,不該當在使用同一密鑰的狀況下兩次使用同一個IV。對於CBC和CFB,重用IV會致使泄露平文首個塊的某些信息,亦包括兩個不一樣消息中相同的前綴。對於OFB和CTR而言,重用IV會致使徹底失去安全性。另外,在CBC模式中,IV在加密時必須是沒法預測的;特別的,在許多實現中使用的產生IV的方法,例如SSL2.0使用的,即採用上一個消息的最後一塊密文做爲下一個消息的IV,是不安全的[12]。

填充

塊密碼只能對肯定長度的數據塊進行處理,而消息的長度一般是可變的。所以部分模式(即ECB和CBC)須要最後一塊在加密前進行填充。有數種填充方法,其中最簡單的一種是在平文的最後填充空字符以使其長度爲塊長度的整數倍,但必須保證能夠恢復平文的原始長度;例如,若平文是C語言風格的字符串,則只有串尾會有空字符。稍微複雜一點的方法則是原始的DES使用的方法,即在數據後添加一個1位,再添加足夠的0位直到知足塊長度的要求;若消息長度恰好符合塊長度,則添加一個填充塊。最複雜的則是針對CBC的方法,例如密文竊取,殘塊終結等,不會產生額外的密文,但會增長一些複雜度。布魯斯·施奈爾和尼爾斯·弗格森提出了兩種簡單的可能性:添加一個值爲128的字節(十六進制的80),再以0字節填滿最後一個塊;或向最後一個塊填充n個值均爲n的字節[13]。

CFB,OFB和CTR模式不須要對長度不爲密碼塊大小整數倍的消息進行特別的處理。由於這些模式是經過對塊密碼的輸出與平文進行異或工做的。最後一個平文塊(多是不完整的)與密鑰流塊的前幾個字節異或後,產生了與該平文塊大小相同的密文塊。流密碼的這個特性使得它們能夠應用在須要密文和平文數據長度嚴格相等的場合,也能夠應用在以流形式傳輸數據而不便於進行填充的場合。

另外在mcrypt的man文檔裏也找到了加密模式的介紹:

加密模式

modes of encryption:
ECB: The Electronic CodeBook mode. It is the simplest mode to use with a block cipher. Encrypts each block independently.

CBC: The Cipher Block Chaining mode. It is better than ECB since the plaintext is XOR’ed with the previous ciphertext. A random block is placed as the first block so the same block or messages always encrypt to something different. (This is the default mode)

CFB: The Cipher-Feedback Mode (in 8bit). This is a self-synchronizing stream cipher implemented from a block cipher.

OFB: The Output-Feedback Mode (in 8bit). This is a synchronous stream cipher implemented from a block cipher. It is intended for use in noisy lines, because corrupted ciphertext blocks do not corrupt the plaintext blocks that follow. Insecure (because used in 8bit mode) so I recommend against using it. Added just for completeness.

nOFB: The Output-Feedback Mode (in nbit). n Is the size of the block of the algorithm. This is a synchronous stream cipher implemented from a block cipher. It is intended for use in noisy lines, because corrupted ciphertext blocks do not corrupt the plaintext blocks that follow.

via http://mcrypt.hellug.gr/mcrypt/mcrypt.1.html
BTW,The author of mcrypt and libmcrypt is Nikos Mavroyanopoulos.

分塊大小

Block size

現代加密算法中,對稱密鑰算法一般被分爲stream ciphers和block ciphers。分塊密碼處理固定長度的字符串位數(bit)。這個比特字符串的長度必須和分塊大小同樣長。輸入(明文)和輸出(密文)都是一樣長度的。輸出不能比輸入短——這是遵循Pigeonhole principle(鴿巢原理,又名狄利克雷抽屜原理、鴿籠原理)和密碼必須是可逆的事實——然而,輸出比輸入更長又是不可取的。

鴿巢原理:

其中一種簡單的表述法爲:
如有n個籠子和n+1只鴿子,全部的鴿子都被關在鴿籠裏,那麼至少有一個籠子有至少2只鴿子。
另外一種爲:
如有n個籠子和kn+1只鴿子,全部的鴿子都被關在鴿籠裏,那麼至少有一個籠子有至少k+1只鴿子。
拉姆齊定理是此原理的推廣。

通俗點說:
10只鴿子放進9個鴿籠,那麼必定有一個鴿籠放進了至少兩隻鴿子。

S-box (Substitution-box,替換盒)
在分塊密碼中,S-box一般被用來掩蓋密鑰和密文之間的關係。

In general, an S-Box takes some number of input bits, m, and transforms them into some number of output bits, n: an m×n S-Box can be implemented as a lookup table with 2m words of n bits each. Fixed tables are normally used, as in the Data Encryption Standard (DES), but in some ciphers the tables are generated dynamically from the key; e.g. the Blowfish and the Twofish encryption algorithms. Bruce Schneier describes IDEA’s modular multiplication step as a key-dependent S-Box.

IDEA(International Data Encryption Algorithm)

Rijndael S-box

算法說明:
BlowFish 使用了兩個box,除了著名的S-box,還有一個p-box
pbox有18個unsigned long元素
sbox有4×256個unsigned long元素
BlowFish算法中,有一個核心加密函數,該函數輸入64位信息,運算後, 以64位密文的形式輸出。 用BlowFish算法加密信息,須要兩個過程:
這裏我看的源碼是Paul Kocher的C語言版本。
源碼下載地址:http://www.schneier.com/blowfish-download.html

1.密鑰預處理
2.信息加密

結構體定義:

typedef struct {
  unsigned long P[16 + 2];
  unsigned long S[4][256];
} BLOWFISH_CTX;

1.密鑰預處理
BlowFish算法的源密鑰——pbox和sbox是固定的。此pbox和sbox的值來自PI的十六進制數字值,使用這些數的緣由是這些數看不出有什麼明顯的規律。(PI)
這裏摘取1-500位:

3.243F6A8885 A308D31319 8A2E037073 44A4093822 299F31D008
  2EFA98EC4E 6C89452821 E638D01377 BE5466CF34 E90C6CC0AC
  29B7C97C50 DD3F84D5B5 B547091792 16D5D98979 FB1BD1310B
  A698DFB5AC 2FFD72DBD0 1ADFB7B8E1 AFED6A267E 96BA7C9045
  F12C7F9924 A19947B391 6CF70801F2 E2858EFC16 636920D871
  574E69A458 FEA3F4933D 7E0D95748F 728EB65871 8BCD588215
  4AEE7B54A4 1DC25A59B5 9C30D5392A F26013C5D1 B023286085
  F0CA417918 B8DB38EF8E 79DCB0603A 180E6C9E0E 8BB01E8A3E
  D71577C1BD 314B2778AF 2FDA55605C 60E65525F3 AA55AB9457
  48986263E8 144055CA39 6A2AAB10B6 B4CC5C3411 41E8CEA154
void Blowfish_Init(BLOWFISH_CTX *ctx, unsigned char *key, int keyLen) {
  int i, j, k;
  unsigned long data, datal, datar;

  for (i = 0; i < 4; i++) {
    for (j = 0; j < 256; j++)
      ctx->S[i][j] = ORIG_S[i][j];
  }

  j = 0;
  for (i = 0; i < N + 2; ++i) {
    data = 0x00000000;
    for (k = 0; k < 4; ++k) {
      data = (data << 8) | key[j];
      j = j + 1;
      if (j >= keyLen)
        j = 0;
    }
    ctx->P[i] = ORIG_P[i] ^ data;
  }

  datal = 0x00000000;
  datar = 0x00000000;

  for (i = 0; i < N + 2; i += 2) {
    Blowfish_Encrypt(ctx, &datal, &datar);
    ctx->P[i] = datal;
    ctx->P[i + 1] = datar;
  }

  for (i = 0; i < 4; ++i) {
    for (j = 0; j < 256; j += 2) {
      Blowfish_Encrypt(ctx, &datal, &datar);
      ctx->S[i][j] = datal;
      ctx->S[i][j + 1] = datar;
    }
  }
}

咱們要加密一個信息,
須要本身選擇一個key,用這個key對pbox和sbox進行變換,獲得下一步信息加密
所要用的pbox和sbox。
具體的變化算法以下:
用原sbox: ORIG_S 填充 sbox

而後,每次取key與data進行運算,運算後的結果送給pbox
運算過程是這樣的:
進行N+2次運算(N=16),
令 32位無符號data爲0,因爲Key是unsigned char類型的,每次對data左移8位(一個字節)以後與
相應的key相或(即相加),當key長度小於4時,循環使用Key。

接下來,用bf_encypt加密一個全0的64位信息(分爲datal和datar,各32位)
用輸出的結果datal和datar分別替換pbox[0]和pbox[1]
而後,繼續加密datal和datar,用輸出結果替換pbox[2]和pbox[3]
……
這樣循環18次,把pbox所有替換完成。

接下來是對sbox的替換了。
此次總共是循環4×256次,每次循環的過程與上面的同樣。
不過這裏用的datal和datar就是上面運算事後的datal和datar.

2.信息加密
接下來看這個加密函數,上面的初始化過程已經用到它了。

void Blowfish_Encrypt(BLOWFISH_CTX *ctx, unsigned long *xl, unsigned long *xr){
  unsigned long  Xl;
  unsigned long  Xr;
  unsigned long  temp;
  short       i;

  Xl = *xl;
  Xr = *xr;

  for (i = 0; i < N; ++i) {
    Xl = Xl ^ ctx->P[i];
    Xr = F(ctx, Xl) ^ Xr;

    temp = Xl;
    Xl = Xr;
    Xr = temp;
  }

  temp = Xl;
  Xl = Xr;
  Xr = temp;

  Xr = Xr ^ ctx->P[N];
  Xl = Xl ^ ctx->P[N + 1];

  *xl = Xl;
  *xr = Xr;
}

與上面說的Feistel 密碼的運算過程是同樣的:
把64位的明文分爲l和r
進行16輪運算:
令i=0,1,2,3,…,N-1
Xl = Xl ^ ctx->P[i];
Xr = F(ctx, Xl) ^ Xr;
若i<N,交換Xl和Xr的值繼續作上述運算。
16輪運算完了以後,再作一次Xl與Xr的交換,由於第16次運算完了以後,實際上
沒有再作運算了,可是Xl與Xr仍是交換了,所以,得換回來。

而後,Xr = Xr ^ ctx->P[N];
Xl = Xl ^ ctx->P[N + 1];
如今的Xl和Xr就是加密後的數據了。
加密過程圖解:

這加密過程當中用到了F變換:

static unsigned long F(BLOWFISH_CTX *ctx, unsigned long x) {
   unsigned short a, b, c, d;
   unsigned long  y;

   d = (unsigned short)(x & 0xFF);
   x >>= 8;
   c = (unsigned short)(x & 0xFF);
   x >>= 8;
   b = (unsigned short)(x & 0xFF);
   x >>= 8;
   a = (unsigned short)(x & 0xFF);
   y = ctx->S[0][a] + ctx->S[1][b];
   y = y ^ ctx->S[2][c];
   y = y + ctx->S[3][d];

   return y;
}

這個變換是這樣的:
它接收一個32位的數據,而後從高位到低位分爲四段,每段8位,依次給
a,b,c,d
取sbox[0][a]+sbox[1][b],相加後的結果再與sbox[2][c]作異或運算,
得出的結果再加上sbox[3][d]
因爲sbox的元素都是32位的,所以,F變換後的輸出也是32位的。
輪函數圖解:

解密函數:
能夠看到解密函數與加密函數很是的類似,只是把p0,p1,…,p17 逆序使用:

void Blowfish_Decrypt(BLOWFISH_CTX *ctx, unsigned long *xl, unsigned long *xr){
  unsigned long  Xl;
  unsigned long  Xr;
  unsigned long  temp;
  short       i;

  Xl = *xl;
  Xr = *xr;

  for (i = N + 1; i > 1; --i) {
    Xl = Xl ^ ctx->P[i];
    Xr = F(ctx, Xl) ^ Xr;

    /* Exchange Xl and Xr */
    temp = Xl;
    Xl = Xr;
    Xr = temp;
  }

  /* Exchange Xl and Xr */
  temp = Xl;
  Xl = Xr;
  Xr = temp;

  Xr = Xr ^ ctx->P[1];
  Xl = Xl ^ ctx->P[0];

  *xl = Xl;
  *xr = Xr;
}

關於BlowFish 密碼,我的認爲能夠把它理解爲一種複雜的異或密碼,異或運算的規律:

A ^ 0 = A
A ^ A = 0
(A ^ B) ^ C = A ^ (B ^ C)
(B ^ A) ^ A = B ^ 0 = B

按這種邏輯,文本序列的每一個字符能夠經過與給定的密鑰進行按位異或運算來加密。若是要解密,只須要將加密後的結果與密鑰再次進行按位異或運算便可。

相關文章
相關標籤/搜索