前言:BlowFish對稱算法學習筆記
參考文章:https://bbs.pediy.com/thread-256209.htm
什么是BlowFish對稱加密算法
BlowFish算法是一個64位分組及可變密鑰長度的對稱密鑰分組密碼算法,可用來加密64比特長度的字符串。32位處理器誕生后,BlowFish算法因其在加密速度上超越了DES而引起人們的關注。Blowfish算法具有加密速度快、緊湊、密鑰長度可變、可免費使用等特點,已被廣泛使用於眾多加密軟件。
BlowFish對稱加密算法是一個要求的密鑰長度需要為32-448位,其分組長度:64位,16輪循環的Feistel結構
對於BlowFish對稱加密算法由兩部分組成,分別是密鑰拓展部分和數據加密部分。密鑰拓展部分將最長為448位的密鑰轉換為4168字節的子密鑰數組,每一輪由一個密鑰相關置換和一個密鑰與數據相關的替換組成。
這兩個部分跟IDEA對稱算法加密有點相像,因為在IDEA對稱算法加密中也是有這兩個部分組成,一個是子密鑰的生成(密鑰拓展部分),一部分是進行加密過程(數據加密部分)。
BlowFish的圖解過程如下所示
BlowFish的結構體
首先要代碼中對於BlowFish結構體都定義為如下,其中有s-box和p-box成員
關於s-box和p-box的描述是ORIG_P(p-box)與ORIG_S(s-box)取自圓周率的小數位,每4個字節賦值給其中的一個元素。
typedef struct {
unsigned long P[16 + 2];
unsigned long S[4][256];
} BLOWFISH_CTX;
對BLOWFISH_CTX* ctx中S-Box進行初始化,直接將ORIG_S中的每個元素逐一賦值給S-Box
這里的ORIG_S定義如下圖所示
BlowFish密鑰拓展部分
初始化可以稱作為Blowfish_Init函數,該函數用來初始化S-Box與P-Box,傳遞參數中的key即密鑰,keyLen是密鑰長度。
第一步:對BLOWFISH_CTX *ctx中S-Box進行初始化,直接將ORIG_S中的每個元素逐一賦值給S-Box,也就是上面中的圖所示,直接將ORIG_S[4][256]賦值給s-box中
第二步:對BLOWFISH_CTX *ctx中P-Box進行初始化,具體過程如下:
-
data=0x00000000;
-
如果參數中的字符數組key長度不足4,則循環使用key中字符(當使用到key中最后一個字符時,下一個字符是key中第一個字符)與data << 8進行或運算
注意:上面的過程總結起來就是將參數中的字符數組key轉換為ASCII碼形式(e.g.:key[3]="abc"---->>0x61626361並存儲於data中)
- 將ORIG_P中的每個元素與data作異或運算后逐一賦值給P-Box
第三步:初始化兩個變量detal和datar
datal=0x00000000;
datar=0x00000000;
第四步:將上面經過變換后的ctx,datal與datar傳遞給Blowfish_Encrypt
第五步:將加密后的datal與datar賦值給P-Box中的元素
重復9次步驟4-5
與步驟4類似,不過這次傳遞的是上面過程中已經加密后的datal與datar
將加密后的datal與datar賦值給S-Box中的元素
重復512次步驟7-8
步驟5、8中提到的賦值過程是這樣的(以步驟5來舉例):
第一次 P[0]=datal,P[1]=datar
第二次 P[2]=datal,P[3]=datar
......
其代碼定義如下所示
// BlowFish進行初始化操作
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;
}
}
}
BlowFish數據加密部分
加密過程如下圖所示
下面說的這4個步驟是一輪循環的工作過程,Feistel Structure是進行了16輪循環才完成一次加密。
1、將原數據分成左右兩部分
2、原數據的右側不變,直接變成下次循環的左側
3、將原數據的右側與子密鑰傳遞給輪函數F
4、輪函數F的返回值與原數據左側進行異或運算,變成下次循環的右側
具體的函數F為如下定義,其中傳入的x變量
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);
// 將原數據的右側與子密鑰傳遞給輪函數F
y = ctx->S[0][a] + ctx->S[1][b];
y = y ^ ctx->S[2][c];
y = y + ctx->S[3][d];
return y; // 輪函數F的返回值與原數據左側進行異或運算,變成下次循環的右側
}
需要說明一點,在最后一輪循環中左右數據不對調。解密過程是加密過程的反向。
解密代碼如下所示
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;
}