密码学:解析Feistel网络结构及实现代码

概述

Feistel网络是由IBM密码学家Horst Feistel在20世纪70年代提出的对称加密结构,已成为现代分组密码的核心框架。DESBlowfishRC5等经典加密算法均基于此结构。其核心思想是将输入明文分组分成左右两半,通过多轮迭代操作实现加密,每轮使用不同的子密钥和轮函数处理数据。Feistel结构具有加解密过程对称的特性,只需反转子密钥顺序即可实现解密,极大简化了实现复杂度。


关键特点

  1. 雪崩效应

    • 定义:输入微小变化(如1比特翻转)导致输出产生显著变化
    • 实现机制:轮函数扩散特性 + 左右数据交叉混合
    • 重要性:增强密码算法抵抗差分密码分析的能力
  2. 结构对称性

    • 加密与解密使用相同结构
    • 仅需反转子密钥顺序即可解密
  3. 可证明安全性

    • 轮数足够多时可逼近理想密码模型
    • 安全性依赖轮函数设计质量

加解密过程

加密流程(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("验证失败: 原始文本与解密文本不匹配");
        }
    }

}
  • 关键设计要素
  1. 轮函数(F函数)

    • 决定密码安全强度的核心组件
    • 需满足非线性、混淆和扩散特性
  2. 密钥调度

    • 主密钥扩展为多轮子密钥的过程
    • 设计目标:消除密钥相关性,抵抗相关密钥攻击
  3. 轮数选择

    • 权衡安全性与性能的关键参数
    • 典型值:DES使用16轮,AES-128使用10轮

总结

Feistel网络通过其优雅的对称结构和可证明安全性,奠定了现代密码学的工程基础。其核心设计哲学------通过简单操作的迭代实现复杂安全目标,至今仍在新型密码设计中焕发生机。随着量子计算的发展,基于Feistel结构的后量子密码研究也展现出新的可能性。

相关推荐
ademen几秒前
spring4第7-8课-AOP的5种通知类型+切点定义详解+执行顺序
java·spring
快乐肚皮15 分钟前
EasyExcel高级特性和技术选型
java
寒士obj22 分钟前
Java对象创建过程
java·开发语言
Java知识库31 分钟前
「深度拆解」Spring Boot如何用DeepSeek重构MCP通信层?从线程模型到分布式推理的架构进化
java·开发语言·spring boot·程序员·编程
进阶的DW44 分钟前
新手小白使用VMware创建虚拟机安装Linux
java·linux·运维
oioihoii1 小时前
C++11 尾随返回类型:从入门到精通
java·开发语言·c++
伍六星1 小时前
更新Java的环境变量后VScode/cursor里面还是之前的环境变量
java·开发语言·vscode
风象南1 小时前
SpringBoot实现简易直播
java·spring boot·后端
万能程序员-传康Kk1 小时前
智能教育个性化学习平台-java
java·开发语言·学习
落笔画忧愁e1 小时前
扣子Coze飞书多维表插件-列出全部数据表
java·服务器·飞书