BlowFish对称加密算法
前言: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;
}