概述
Feistel网络
是由IBM密码学家Horst Feistel在20世纪70年代提出的对称加密结构,已成为现代分组密码的核心框架。DES
、Blowfish
、RC5
等经典加密算法均基于此结构。其核心思想是将输入明文分组分成左右两半,通过多轮迭代操作实现加密,每轮使用不同的子密钥和轮函数处理数据。Feistel结构具有加解密过程对称的特性,只需反转子密钥顺序即可实现解密,极大简化了实现复杂度。
关键特点
-
雪崩效应:
- 定义:输入微小变化(如1比特翻转)导致输出产生显著变化
- 实现机制:轮函数扩散特性 + 左右数据交叉混合
- 重要性:增强密码算法抵抗差分密码分析的能力
-
结构对称性:
- 加密与解密使用相同结构
- 仅需反转子密钥顺序即可解密
-
可证明安全性:
- 轮数足够多时可逼近理想密码模型
- 安全性依赖轮函数设计质量
加解密过程
加密流程(n轮迭代):
1. 输入:明文分组M = (L0, R0)
2. 对于每轮i (1到n):
Li = Ri-1
Ri = Li-1 ⊕ F(Ri-1, Ki) # Ki为第i轮子密钥
3. 输出密文:C = (Rn, Ln) # 注意左右交换

解密流程:
1. 输入密文:C = (Rn, Ln)
2. 对于每轮i (n到1):
Ri-1 = Li
Li-1 = Ri ⊕ F(Li, Ki) # 使用与加密相同的子密钥序列
3. 输出明文:M = (L0, R0)
- 与
SP网络结构
相比,Feistel网络结构
的一个优点是,它的轮函数F不必可逆
Java实现
java
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.spec.KeySpec;
import java.util.Arrays;
public class FeistelCipher {
// 使用DES的分组大小(64位/8字节)
private static final int BLOCK_SIZE = 8; // DES分组大小
private static final int HALF_BLOCK = BLOCK_SIZE / 2; // 4字节
// 默认轮数
private static final int DEFAULT_ROUNDS = 16;
/**
* PKCS#7填充 - 加密时使用
* @param data 需要填充的数据
* @return 填充后的数据
*/
public static byte[] padPKCS7(byte[] data) {
int paddingLength = BLOCK_SIZE - (data.length % BLOCK_SIZE);
byte[] padded = new byte[data.length + paddingLength];
System.arraycopy(data, 0, padded, 0, data.length);
// 填充字节的值等于填充长度
Arrays.fill(padded, data.length, padded.length, (byte) paddingLength);
return padded;
}
/**
* PKCS#7去除填充 - 解密时使用
* @param data 带填充的数据
* @return 去除填充后的原始数据
* @throws IllegalArgumentException 如果填充无效
*/
public static byte[] unpadPKCS7(byte[] data) {
// 检查填充长度是否有效
int paddingLength = data[data.length - 1] & 0xFF;
if (paddingLength < 1 || paddingLength > BLOCK_SIZE) {
throw new IllegalArgumentException("无效的PKCS#7填充");
}
// 验证所有填充字节是否正确
for (int i = data.length - paddingLength; i < data.length; i++) {
if (data[i] != paddingLength) {
throw new IllegalArgumentException("无效的PKCS#7填充");
}
}
return Arrays.copyOfRange(data, 0, data.length - paddingLength);
}
/**
* DES轮函数实现
* @param data 输入数据(半块大小,4字节)
* @param key 轮密钥(8字节)
* @return 轮函数处理结果(4字节)
*/
private static byte[] desFFunction(byte[] data, byte[] key) throws Exception {
// 确保输入大小正确
if (data.length != HALF_BLOCK) {
throw new IllegalArgumentException("轮函数输入大小必须为" + HALF_BLOCK + "字节");
}
// 将4字节数据扩展为8字节(DES输入大小)
byte[] expanded = new byte[BLOCK_SIZE];
System.arraycopy(data, 0, expanded, 0, HALF_BLOCK);
System.arraycopy(data, 0, expanded, HALF_BLOCK, HALF_BLOCK);
// 使用DES ECB模式
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
// 创建DES密钥(仅使用前56位有效位)
KeySpec keySpec = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(keySpec);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypted = cipher.doFinal(expanded);
// 取结果的前4字节作为输出
return Arrays.copyOf(encrypted, HALF_BLOCK);
}
/**
* 从主密钥派生轮密钥
* @param masterKey 主密钥
* @param rounds 轮数
* @return 轮密钥数组(每个8字节)
*/
public static byte[][] deriveRoundKeys(byte[] masterKey, int rounds) throws Exception {
byte[][] roundKeys = new byte[rounds][8];
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
for (int i = 0; i < rounds; i++) {
// 使用不同的盐派生每轮密钥
sha256.update(masterKey);
sha256.update((byte) i);
byte[] hash = sha256.digest();
// 取前8字节作为轮密钥
System.arraycopy(hash, 0, roundKeys[i], 0, 8);
}
return roundKeys;
}
/**
* Feistel网络加密
* @param plaintext 明文
* @param roundKeys 轮密钥
* @return 密文
*/
public static byte[] encrypt(byte[] plaintext, byte[][] roundKeys) throws Exception {
// 应用PKCS#7填充
byte[] padded = padPKCS7(plaintext);
byte[] ciphertext = new byte[padded.length];
// 分块处理
for (int block = 0; block < padded.length; block += BLOCK_SIZE) {
// 分割左右块
byte[] left = new byte[HALF_BLOCK];
byte[] right = new byte[HALF_BLOCK];
System.arraycopy(padded, block, left, 0, HALF_BLOCK);
System.arraycopy(padded, block + HALF_BLOCK, right, 0, HALF_BLOCK);
// 多轮迭代
for (int round = 0; round < roundKeys.length; round++) {
byte[] temp = right.clone();
byte[] fResult = desFFunction(right, roundKeys[round]);
// 左块与轮函数结果异或
for (int i = 0; i < HALF_BLOCK; i++) {
right[i] = (byte) (left[i] ^ fResult[i]);
}
left = temp;
}
// 合并结果(最后一轮不交换)
System.arraycopy(left, 0, ciphertext, block, HALF_BLOCK);
System.arraycopy(right, 0, ciphertext, block + HALF_BLOCK, HALF_BLOCK);
}
return ciphertext;
}
/**
* Feistel网络解密
* @param ciphertext 密文
* @param roundKeys 轮密钥
* @return 解密后的数据
*/
public static byte[] decrypt(byte[] ciphertext, byte[][] roundKeys) throws Exception {
if (ciphertext.length % BLOCK_SIZE != 0) {
throw new IllegalArgumentException("密文长度必须是块大小的倍数");
}
byte[] plaintext = new byte[ciphertext.length];
for (int block = 0; block < ciphertext.length; block += BLOCK_SIZE) {
byte[] left = new byte[HALF_BLOCK];
byte[] right = new byte[HALF_BLOCK];
System.arraycopy(ciphertext, block, left, 0, HALF_BLOCK);
System.arraycopy(ciphertext, block + HALF_BLOCK, right, 0, HALF_BLOCK);
// 反向使用子密钥
for (int round = roundKeys.length - 1; round >= 0; round--) {
byte[] temp = left.clone();
byte[] fResult = desFFunction(left, roundKeys[round]);
// 右轮与轮函数结果异或
for (int i = 0; i < HALF_BLOCK; i++) {
left[i] = (byte) (right[i] ^ fResult[i]);
}
right = temp;
}
System.arraycopy(left, 0, plaintext, block, HALF_BLOCK);
System.arraycopy(right, 0, plaintext, block + HALF_BLOCK, HALF_BLOCK);
}
// 去除填充的数据
plaintext = unpadPKCS7(plaintext);
// 返回解密数据
return plaintext;
}
public static void main(String[] args) throws Exception {
// 示例主密钥
byte[] masterKey = "MySecretKey".getBytes(StandardCharsets.UTF_8);
// 派生轮密钥
int rounds = DEFAULT_ROUNDS;
byte[][] roundKeys = deriveRoundKeys(masterKey, rounds);
// 测试短文本(长度不足块大小)
String shortText = "Short";
System.out.println("测试短文本加密(长度不足块大小):");
testEncryptionDecryption(shortText, roundKeys);
// 测试长文本
String longText = "This is a longer text that will require multiple blocks for encryption.";
System.out.println("\n测试长文本加密:");
testEncryptionDecryption(longText, roundKeys);
}
private static void testEncryptionDecryption(String text, byte[][] roundKeys) throws Exception {
byte[] plaintext = text.getBytes(StandardCharsets.UTF_8);
System.out.println("原始文本: " + text);
System.out.println("原始长度: " + plaintext.length + " 字节");
// 加密
byte[] ciphertext = encrypt(plaintext, roundKeys);
System.out.println("加密后长度: " + ciphertext.length + " 字节");
// 解密
byte[] decrypted = decrypt(ciphertext, roundKeys);
System.out.println("解密后文本: " + new String(decrypted, StandardCharsets.UTF_8));
System.out.println("解密后长度: " + decrypted.length + " 字节");
// 验证
if (Arrays.equals(plaintext, decrypted)) {
System.out.println("验证成功: 原始文本与解密文本匹配");
} else {
System.out.println("验证失败: 原始文本与解密文本不匹配");
}
}
}
- 关键设计要素
-
轮函数(F函数):
- 决定密码安全强度的核心组件
- 需满足非线性、混淆和扩散特性
-
密钥调度:
- 主密钥扩展为多轮子密钥的过程
- 设计目标:消除密钥相关性,抵抗相关密钥攻击
-
轮数选择:
- 权衡安全性与性能的关键参数
- 典型值:DES使用16轮,AES-128使用10轮
总结
Feistel网络通过其优雅的对称结构和可证明安全性,奠定了现代密码学的工程基础。其核心设计哲学------通过简单操作的迭代实现复杂安全目标,至今仍在新型密码设计中焕发生机。随着量子计算的发展,基于Feistel结构的后量子密码研究也展现出新的可能性。