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