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;
}
posted @ 2022-09-19 22:46  zpchcbd  阅读(1181)  评论(0编辑  收藏  举报