TDEA算法
简介
Blowfish是由Bruce Schneier在1993年发明的对称密钥分组加密算法,类似的DES和AES都是分组加密算法,Blowfish是用来替代DES算法出现的,并且Blowfish是没有商用限制的,任何人都可以自由使用。
对比而言,虽然AES也是一种密码强度很高的对称密码算法,但是如果需要商用的话要向NIST支付授权费用
加密流程
Blowfish算法流程目录
一、密钥扩展
初始化静态参数
- P数组(18个元素)
- S盒(4×256元素)
融合用户密钥
- 密钥循环填充 → 异或修改P数组
动态生成参数
- 加密全零块 → 更新P数组
- 重复加密 → 更新4个S盒
二、加密流程
拆分明文
- 64位分组 → 左32位(L) + 右32位(R)
16轮Feistel迭代
- 每轮操作:
- L异或P值 → 输入F函数
- R异或F结果 → 交换L/R
- 每轮操作:
最终输出
- 末轮不交换 → 异或P[16]/P[17]
- 合并为64位密文
循环流程
F函数内部流程
三、核心模块
F函数
- 拆分4字节 → 查4个S盒
- 加法+异或混合运算
动态S盒
- 每个密钥生成唯一S盒
- 通过迭代加密随机化
实现过程
一、密钥扩展过程(密钥→P数组+S盒)
输入:用户密钥(4~56字节)
输出:初始化后的P数组(18个32位字)和4个S盒(每个256个32位字)
步骤1:初始化P数组和S盒
- P数组:初始包含18个固定32位值(来自圆周率π的小数部分)
1
2
3
4
5
6
7P = [
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
0x9216D5D9, 0x8979FB1B # 注意共18个元素
] - S盒:4个盒,每个盒256个固定32位值(同样来自π)
步骤2:处理用户密钥
- 示例密钥:假设用户密钥为
b"SecretKey"
(8字节) - 密钥转换:将密钥拆分为32位字数组
1
2
3
4
5key_bytes = b"S(0x53) e(0x65) c(0x63) r(0x72)..."
key_words = [
0x53656372, # 前4字节 "Secr"
0x65744B65 # 后4字节 "etKe"
]
步骤3:异或P数组
- 循环扩展密钥:将密钥循环重复至18个元素
1
expanded_key = [0x53656372, 0x65744B65, 0x53656372, 0x65744B65, ...] (共18个)
- 逐元素异或:
P[i] = P[i] XOR expanded_key[i]
1
2
3P[0] = 0x243F6A88 ^ 0x53656372 → 0x775A09FA
P[1] = 0x85A308D3 ^ 0x65744B65 → 0xE0D743B6
...
步骤4:初始化P数组和S盒
使用全0数据块加密生成最终P和S:
在上面操作,我们生成了初始化的P数组和S-box。
blowfish认为这样还不够安全,不够随机。
我们还需要进行一些操作来生成最终的K数组。
首先我们取一个全为0的64bits,然后P数组和S-box,应用blowfish算法,生成一个64bits。
将这个64bits分成两部分,分别作为新的P1 和 P2。
然后将这个64bits作为输入,再次调用blowfish算法,作为新的P3 和 P4。
依次类推,最终生成所有P数组中的元素。
4个S-box的数组也按照上面的流程来进行生成。从而得到最终的S-box。
初始化加密器:
- 左半部分
L = 0x00000000
- 右半部分
R = 0x00000000
- 左半部分
加密过程:
- 用当前P数组加密
(L, R)
- 结果更新P数组前两个元素
1
2
3
4
5
6
7
8# 第一次加密
new_L, new_R = encrypt(L, R)
P[0], P[1] = new_L, new_R
# 第二次加密
new_L, new_R = encrypt(new_L, new_R)
P[2], P[3] = new_L, new_R
...
- 用当前P数组加密
循环处理:
- 重复这个过程直到更新完所有18个P元素
- 接着用相同方式更新4个S盒(每个S盒256元素)
UnIIS
二、加密全流程详解(64位分组)
示例明文:0x0123456789ABCDEF
步骤1:初始拆分
- 拆分为左右两部分(各32位):
1
2L = 0x01234567 # 高32位
R = 0x89ABCDEF # 低32位
步骤2:16轮Feistel循环
循环流程
F函数内部流程
输入处理与轮操作
- 输入为一个64位数据块 ( X_m ),将其均分为高32位 ( X_L ) 和低32位 ( X_R )。
- 在每一轮中:
- 将 ( XL ) 与当前轮的轮密钥 ( P_r ) 进行异或操作,得到中间结果 ( X{L1} )。
- 将 ( X{L1} ) 输入 F函数 计算,输出结果与 ( X_R ) 进行异或操作,得到中间结果 ( X{R1} )。
- 交换 ( X{L1} ) 和 ( X{R1} ) 的位置,作为下一轮的输入 ( X_L ) 和 ( X_R )。
最终轮处理
- 完成16轮迭代后,不再交换 ( X_L ) 和 ( X_R ) 的位置。
- 将 ( XL ) 与 ( P{18} ) 异或,( XR ) 与 ( P{17} ) 异或。
- 将异或后的结果按顺序拼接(高32位为处理后的 ( X_L ),低32位为处理后的 ( X_R )),生成最终的64位加密数据块 ( X_c )。
伪代码
每一轮操作(i从0到15):1
2
3
4
5
6
7
8
9
10for i in 0..15:
# (1) 左半部分异或当前P值
L = L ^ P[i]
# (2) 右半部分异或F函数结果
R = R ^ F(L)
# (3) 交换左右部分(最后一轮不交换)
if i != 15:
L, R = R, L
第1轮示例(i=0):
L = 0x01234567 ^ P[0]
- 假设
P[0] = 0x775A09FA
- →
L = 0x01234567 ^ 0x775A09FA = 0x76794C9D
- 假设
计算
F(L)
:- 将
0x76794C9D
拆分为4字节:0x76, 0x79, 0x4C, 0x9D
- 查S盒:
S0[0x76]
→ 假设为0xA1B2C3D4
S1[0x79]
→0xE5F6A7B8
S2[0x4C]
→0x9E8D7C6B
S3[0x9D]
→0x5A4B3C2D
- 计算:
1
2
3
4F = (S0[0x76] + S1[0x79]) ^ S2[0x4C] + S3[0x9D]
= (0xA1B2C3D4 + 0xE5F6A7B8) → 0x187A96B8C (取后32位: 0x87A96B8C)
^ 0x9E8D7C6B → 0x192417E7
+ 0x5A4B3C2D → 0x736F5414
- 将
R = 0x89ABCDEF ^ F(L)
R = 0x89ABCDEF ^ 0x736F5414 = 0xFAE499FB
交换L和R:
新L = 0xFAE499FB
新R = 0x76794C9D
后续轮次:重复上述过程,使用P[1]到P[15]
步骤3:最终处理
交换最后一次结果(补偿第16轮未交换):
1
L, R = R, L # 交换回来
异或最后两个P值:
1
2L = L ^ P[17]
R = R ^ P[16]合并为密文:
1
密文 = (L << 32) | R
三、完整流程示意图
1 | 初始L/R |
四、为什么这样设计?
密钥扩展:
- 通过多次加密生成P/S,使每个密钥的S盒不同
- 密钥参与修改P数组,增强密钥相关性
Feistel结构:
- 每轮只加密一半数据,解密结构相同(只需逆序P数组)
- 交换操作确保充分扩散
F函数设计:
- S盒提供非线性,加法/异或提供线性混合
- 4次查表+2次加法+1次异或,平衡安全与效率
五、实战示例
假设某轮计算后:
P[0] = 0x243F6A88
L = 0x00000000
R = 0x00000000
加密过程:
L ^= P[0]
→0x243F6A88
- 计算
F(L)
:- 拆分:0x24, 0x3F, 0x6A, 0x88
S0[0x24] = 0x85A308D3
S1[0x3F] = 0x13198A2E
S2[0x6A] = 0x03707344
S3[0x88] = 0xA4093822
- 计算:
1
2
3h = (0x85A308D3 + 0x13198A2E) & 0xFFFFFFFF → 0x98BC9301
h ^= 0x03707344 → 0x9BCCE045
h = (h + 0xA4093822) & 0xFFFFFFFF → 0x3FD61867
R ^= h
→0x00000000 ^ 0x3FD61867 = 0x3FD61867
- 交换 →
L=0x3FD61867
,R=0x243F6A88
六、常见疑问解答
Q1:为什么需要18个P值?
A:16轮加密每轮用1个P值(P[0]~P[15]),最后交换后用P[16]和P[17]
Q2:S盒如何保证安全性?
A:S盒通过π生成,具有高度非线性,且每个密钥生成不同的S盒
Q3:如果密钥全为0会怎样?
A:P数组初始异或后不变,但后续通过加密生成新的P/S,仍具备安全性