自学内容网 自学内容网

java 实现AES的CBC、CFB、OFB加解密

1. CBC(Cipher Block Chaining,密码分组链接模式)

概述

CBC 模式是一种常见的块密码工作模式,通过将每个明文块与前一个密文块进行异或操作,再进行加密,从而增强数据的安全性。

工作原理

  1. 初始向量(IV,Initialization Vector)

    • CBC 模式需要一个随机生成的 IV 来确保相同明文在不同加密过程中的密文不同。
  2. 加密过程

    • 步骤 1:将第一个明文块与 IV 进行异或(XOR)操作。
    • 步骤 2:将异或后的结果通过块密码算法(如 AES)加密,生成第一个密文块。
    • 步骤 3:将第一个密文块与第二个明文块进行异或,再加密生成第二个密文块。
    • 步骤 4:重复上述步骤,直到所有明文块都被加密。
  3. 解密过程

    • 步骤 1:将第一个密文块解密,得到异或后的数据。
    • 步骤 2:将解密后的数据与 IV 进行异或,恢复出第一个明文块。
    • 步骤 3:将解密后的密文块与下一个密文块进行异或,恢复出下一个明文块。
    • 步骤 4:重复上述步骤,直到所有密文块都被解密。

特点

  • 依赖性:每个密文块都依赖于前一个密文块,增强了安全性。
  • 随机性:使用 IV 增加了加密的随机性,防止相同明文生成相同密文。
  • 错误传播:在解密过程中,如果某个密文块损坏,将导致该块和下一个块的解密失败。

优点

  • 增强安全性:通过链接操作,使得相同的明文块在不同的加密过程中生成不同的密文块。
  • 简单易实现:实现相对简单,广泛支持于各种加密库和标准中。

缺点

  • 无法并行处理:由于每个密文块依赖于前一个密文块,加密和解密过程无法并行化,影响性能。
  • 错误传播:解密过程中,某个密文块的错误会影响到当前块和下一个块的解密结果。

适用场景

  • 文件加密:如加密文件系统中的数据。
  • 通信加密:如 SSL/TLS 的部分加密阶段。
  • 通用数据保护:适用于需要高度安全性的场合。

2. CFB(Cipher Feedback,密码反馈模式)

概述

CFB 模式将块密码转换为一种类似流密码的工作模式,可以处理任意长度的数据流。它通过将前一个密文块反馈到加密过程中,实现对明文的加密。

工作原理

  1. 初始向量(IV)

    • CFB 模式同样需要一个随机生成的 IV 来确保加密的随机性。
  2. 加密过程

    • 步骤 1:将 IV 加密,得到一个密钥流块。
    • 步骤 2:将密钥流块与第一个明文块进行异或,生成第一个密文块。
    • 步骤 3:将第一个密文块作为下一个加密过程的输入,加密后得到下一个密钥流块。
    • 步骤 4:将第二个密钥流块与第二个明文块进行异或,生成第二个密文块。
    • 步骤 5:重复上述步骤,直到所有明文块被加密。
  3. 解密过程

    • 步骤 1:将 IV 加密,得到密钥流块。
    • 步骤 2:将密钥流块与第一个密文块进行异或,恢复出第一个明文块。
    • 步骤 3:将第一个密文块作为下一个加密过程的输入,加密后得到下一个密钥流块。
    • 步骤 4:将第二个密钥流块与第二个密文块进行异或,恢复出第二个明文块。
    • 步骤 5:重复上述步骤,直到所有密文块被解密。

特点

  • 反馈机制:密文块反馈到加密过程,增强了安全性。
  • 类似流密码:能够处理任意长度的数据,与流密码类似。

优点

  • 灵活性高:可以处理任意长度的数据,而不仅限于块大小的整数倍。
  • 无需填充:不需要像 CBC 模式那样进行填充操作,简化了实现。
  • 错误局部化:单个密文块的错误只会影响对应的明文块,错误不会传播到其他块。

缺点

  • 无法并行处理:与 CBC 模式类似,加密和解密过程需要串行进行,影响性能。
  • 安全性依赖于反馈:需要确保反馈机制的安全性,否则可能导致攻击风险。

适用场景

  • 流式通信:如实时数据传输、网络通信加密。
  • 无线通信:适用于需要快速响应和低延迟的场合。
  • 即时消息传输:如聊天应用中的数据加密。

3. OFB(Output Feedback,输出反馈模式)

概述

OFB 模式也是将块密码转换为一种流密码的模式,通过不断生成密钥流来与明文进行异或,从而实现加密。与 CFB 不同的是,OFB 模式使用块密码的输出作为密钥流的生成源,而不依赖于密文。

工作原理

  1. 初始向量(IV)

    • 与 CBC 和 CFB 模式类似,OFB 也需要一个随机的 IV。
  2. 密钥流生成

    • 步骤 1:将 IV 输入块密码算法,得到第一个密钥流块。
    • 步骤 2:将第一个密钥流块再次输入块密码算法,生成第二个密钥流块。
    • 步骤 3:重复上述步骤,持续生成密钥流块,直到覆盖所有明文数据。
  3. 加密过程

    • 步骤 1:将生成的密钥流块与明文数据进行异或(XOR)操作,生成密文块。
    • 步骤 2:重复以上步骤,直到所有数据块都被加密。
  4. 解密过程

    • 由于 XOR 操作的对称性,解密过程与加密过程相同,将密钥流块与密文块进行异或,恢复出明文。

特点

  • 独立性:密钥流的生成独立于明文和密文,只依赖于初始向量和密钥。
  • 同步流密码:可以预先生成密钥流,适用于需要同步流密码的场合。

优点

  • 并行性高:密钥流可以提前生成或并行处理,提高加密和解密性能。
  • 无错误传播:单个密文块的错误只会影响对应的明文块,错误不会传播到其他块。
  • 无需填充:无需像 CBC 模式那样进行填充操作,简化了实现。

缺点

  • 无内建认证:OFB 模式仅提供加密功能,不提供数据完整性和认证保障,容易受到重放攻击等威胁。
  • IV 重复风险:如果 IV 被重复使用,可能导致密钥流重复,破坏安全性。因此,必须确保 IV 的唯一性。

适用场景

  • 广播通信:如加密广播数据流。
  • 实时系统:需要快速生成密钥流并进行加密的场合。
  • 高性能应用:如视频流、音频流加密。

CBC、CFB 和 OFB 模式的比较

特性CBCCFBOFB
加密类型块加密模式混合块加密和流加密流加密模式
初始向量(IV)需要需要需要
并行处理不支持加密,支持解密的部分并行不支持并行支持加密和解密的并行
错误传播解密时错误会影响当前和下一个块错误只影响对应的块错误只影响对应的块
适用场景文件加密、通信加密流式传输、实时通信高性能流加密、广播通信
安全性高,但依赖于 IV 的唯一性和安全性高,但依赖于 IV 的唯一性和安全性高,但需要防止 IV 重复使用
需要填充需要不需要不需要

总结

  • CBC 模式:适用于需要高度安全性但对性能要求不高的场景,如文件加密和常规通信加密。其串行依赖性限制了并行处理能力,且错误传播可能影响到多个密文块。

  • CFB 模式:适用于流式数据加密,如实时通信和无线通信。虽然提供了类似流密码的灵活性,但同样存在无法并行处理的限制。

  • OFB 模式:适用于需要高性能和并行处理的场景,如视频流和音频流加密。其密钥流的独立性使得加密和解密过程可以并行进行,但缺乏内建的认证机制,需要额外的手段来保证数据完整性。

在实际应用中,选择哪种加密模式取决于具体的需求,包括数据类型、性能要求、安全性需求以及系统的特性。对于需要同时保证数据机密性和完整性的场景,可能需要结合使用认证机制(如 HMAC)或选择带认证功能的加密模式(如 GCM)。


示例代码(Java)

以下是使用 Java 实现 CBC、CFB 和 OFB 模式的示例代码

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 *
 * 支持 CBC CFB OFB
 */

public class AES {

    private static Logger log = LoggerFactory.getLogger(AES.class);
    //默认模式
    private static String DEFAULT_MODE = "AES/CBC/PKCS5Padding";
    //默认key
    private static final String DEFAULT_KEY = "hqsywqVnGKaEYka1";
    //默认初始向量
    private static final String DEFAULT_IV = "TlP5B1wg7zMnmQHT";

    public static Build build(String key, String mode) {
        return new Build(key, mode);
    }

    public static Build build() {
        return new Build();
    }

    public static class Build {

        private Cipher cipher;
        private String key = DEFAULT_KEY;
        private String mode = DEFAULT_MODE;
        private String result;
        private String iv = DEFAULT_IV;
        private String padding;

        public Build() {
        }

        public Build(String key, String mode) {
            if (StringUtils.isNotBlank(key)) {
                this.key = key;
            }
            if (StringUtils.isNotBlank(mode)) {
                this.mode = mode;
            }
        }

        /**
         * 初始化
         * @return
         */
        public Build init() {
            try {
                cipher = Cipher.getInstance(mode);
            } catch (Exception e) {
                log.error("初始化aes失败:", e);
            }
            return this;
        }

        /**
         * 加密
         * @param enc
         * @return
         */
        public Build encrypt(String enc) {
            if (cipher == null) {
                init();
            }
            try {
                cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key), getIvParameterSpec(iv));
                byte[] bytes = cipher.doFinal(enc.getBytes(StandardCharsets.UTF_8));
                result = Base64.getEncoder().encodeToString(bytes);
            } catch (Exception e) {
                log.error("aes加密失败:", e);
            }
            return this;
        }

        /**
         * 解密
         * @param dec
         * @return
         */
        public Build decrypt(String dec) {
            if (cipher == null) {
                init();
            }
            try {
                cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key), getIvParameterSpec(iv));
                byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(dec));
                result = new String(bytes);
            } catch (Exception e) {
                log.error("aes解密失败:", e);
            }
            return this;
        }

        public String build() {
            return result;
        }
    }

    // 将字符串转换为初始化向量
    public static IvParameterSpec getIvParameterSpec(String ivStr) {
        return new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 生成密钥
     * @param key
     * @return
     */
    public static SecretKey getSecretKey(String key) {
        byte[] bytes = key.getBytes(StandardCharsets.UTF_8);
        return new SecretKeySpec(bytes, 0, key.length(), "AES");
    }

    public static void main(String[] args) {
        //支持 CBC CFB OFB
        Build init = AES.build("1232QWE131ASD2Q2","AES/CBC/PKCS5Padding").init();
        String enc = init.encrypt("123").build();
        System.out.println(enc);
        String dec = init.decrypt(enc).build();
        System.out.println(dec);
    }
}

注意事项

  1. 密钥管理

    • 密钥必须安全地存储和管理,避免泄露。可以使用 Java 的 KeyStore 来管理密钥。
    • 不建议在代码中硬编码密钥。
  2. 初始化向量(IV)

    • 每次加密操作应使用不同且随机生成的 IV,以确保密文的唯一性。
    • IV 本身不需要保密,但必须与密文一起存储或传输,以便解密时使用。
  3. 填充方式

    • 在 CBC 模式下,需要填充(如 PKCS5Padding)以确保明文长度是块大小的整数倍。
  4. 安全性

    • 确保使用最新的 Java 版本和安全库,以防止已知的安全漏洞。
    • 考虑结合使用认证机制(如 HMAC)以增强数据完整性和防御主动攻击。
  5. 异常处理

    • 加密和解密过程中可能会抛出各种异常,应妥善处理,确保系统的稳定性和安全性。

原文地址:https://blog.csdn.net/wenlai123456/article/details/142485474

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!