Java中自定义加解密字节流需继承FilterInputStream(解密输入)或FilterOutputStream(加密输出),重写read/write方法实现透明加解密,关键在于流方向、缓冲管理、IV一致性及异常处理。

Java 中可以通过继承 FilterInputStream(或 FilterOutputStream)编写自定义加解密字节流,核心是重写 read() 和 read(byte[], int, int) 方法,在读取原始数据后、返回给调用方前进行加密或解密处理。注意:加密流通常用于**解密输入**(即从加密源读取并透明解密),解密流对应“解密”,而“加密输出”应继承 FilterOutputStream;但常被统称为“加解密流”,关键在方向与职责清晰。
一、明确流方向:解密输入流 vs 加密输出流
FilterInputStream 的子类天然适用于“解密输入”场景:它包装一个底层 InputStream(如文件、网络流),每次 read() 返回的字节已是解密后的明文。反之,若要加密写入,应继承 FilterOutputStream,在 write() 时对明文字节加密后再写入底层流。
- ✅ 解密输入流(推荐继承
FilterInputStream):构造时传入加密的源流,read()返回解密后数据 - ❌ 不要用
FilterInputStream实现“加密读取”(读明文再加密无意义) - ⚠️ 若需“加密写入”,必须另写
EncryptingOutputStream extends FilterOutputStream
二、实现一个 AES 解密输入流(基于 FilterInputStream)
以 AES/CBC/PKCS5Padding 为例,需在构造时传入密钥、IV,并初始化 Cipher 为 DECRYPT_MODE。由于 CipherInputStream 已内置支持,这里展示**手动控制解密逻辑**的自定义写法(便于理解原理和定制):
- 使用
ByteArrayInputStream缓存已解密的明文字节,避免每次read()都触发完整块解密 - 重写
read():先检查缓存是否有可用字节;若空,则从底层流读取加密块(如 16 字节 AES 块),解密后填入缓存,再返回首字节 - 重写
read(byte[] b, int off, int len):同样优先从缓存取,不足时再触发解密填充 - 注意处理流末尾(padding 填充、最后一块可能不完整)、异常(
Cipher.init()失败、底层流 EOF)
三、关键细节与避坑点
自定义加解密流易出错,以下几点直接影响正确性与性能:
立即学习“Java免费学习笔记(深入)”;
-
缓冲区管理必须严格:不能跳过或重复解密同一段密文;建议用
ByteBuffer或字节数组 + 游标(pos/limit)管理缓存 - IV 必须与加密端一致:若加密时随机生成 IV 并前置写入,解密流需先读取前 N 字节作为 IV;否则需外部传入固定 IV
-
不要在
read()中抛出未声明异常:所有CryptoException应包装为IOException(因InputStream.read()只声明抛出IOException) -
关闭流时释放 Cipher 资源:虽
Cipher本身无 close,但可重写close()确保底层流被关闭
四、简单示例:XOR 解密输入流(轻量演示)
XOR 因其可逆性适合教学演示,无需复杂算法库:
public class XorDecryptInputStream extends FilterInputStream {
private final byte key;
private int bufferedByte = -1; // 缓存单字节解密结果
public XorDecryptInputStream(InputStream in, byte key) {
super(in);
this.key = key;
}
@Override
public int read() throws IOException {
int c = super.read();
if (c == -1) return -1;
return c ^ key;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int n = super.read(b, off, len);
if (n <= 0) return n;
for (int i = off; i < off + n; i++) {
b[i] ^= key;
}
return n;
}
}
使用:new XorDecryptInputStream(new FileInputStream("enc.dat"), (byte)0x55) —— 即可透明读取 XOR 加密的文件。


















