CipherInputStream 在读取数据时挂起
CipherInputStream hangs while reading data
我正在尝试 encrypt/decrypt 一些文件,我将 reading/writing 使用 FileIn/OutputStream
通过 CipherIn/OutputStream
传输的文件。概念上相当简单,我已经使用原始字节数组和 Cipher.doFinal
实现了它。所以我知道我的加密参数(位大小、iv 大小等)是正确的。 (或者至少是功能性的?)
我可以通过 CipherOutputStream
写入数据。但是,当我尝试通过 CipherInputStream
读回该数据时,它会无限期地挂起。
我发现的唯一 related problem 仍然没有答案,并且可能与我的问题根本不同,因为我的问题将始终在磁盘上提供所有可用数据,而不是相关问题依赖于 Sockets
.
我尝试了多种解决方案,最明显的一种是更改缓冲区大小 (data = new byte[4096];
)。我尝试了很多值,包括明文的大小和加密数据的大小。 None 这些值有效。我找到的唯一解决方案是完全避免使用 CipherInputStream
,而是依赖 Cipher.doFinal
和 Cipher.update
.
我错过了什么吗?如果能够使用 CipherInputStream
,而不是必须使用 Cipher.update
.
重新发明轮子,那就太好了
SSCCE:
private static final String AES_ALG = "aes_256/gcm/nopadding";
private static final int GCM_TAG_SIZE = 128;
private static void doEncryptionTest() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, FileNotFoundException, IOException
{
File f = new File("encrypted_random_data.dat");
// 12-byte long iv
byte[] iv = new byte[] {0x27, 0x51, 0x34, 0x14, -0x65, 0x4d, -0x67, 0x35, -0x63, 0x11, -0x02, -0x05};
// 256-bit long key
byte[] keyBytes = new byte[] {0x55, -0x7f, -0x17, -0x29, -0x68, 0x25, 0x29, 0x5f, -0x27, -0x2d, -0x4d, 0x1b,
0x25, 0x74, 0x57, 0x35, -0x23, -0x1b, 0x12, 0x7c, 0x1, -0xf, -0x60, -0x42, 0x1c, 0x61, 0x3e, -0x5,
-0x13, 0x31, -0x48, -0x6e};
SecretKey key = new SecretKeySpec(keyBytes, "AES");
OutputStream os = encryptStream(key, iv, f);
System.out.println("generating random data...");
// 24MB of random data
byte[] data = new byte[25165824];
new Random().nextBytes(data);
System.out.println("encrypting and writing data...");
os.write(data);
os.close();
InputStream is = decryptStream(key, iv, f);
System.out.println("reading and decrypting data...");
// read the data in 4096 byte packets
int n;
data = new byte[4096];
while ((n = is.read(data)) > 0)
{
System.out.println("read " + n + " bytes.");
}
is.close();
}
private static OutputStream encryptStream(SecretKey key, byte[] iv, File f) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, FileNotFoundException
{
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE, iv);
Cipher enc = Cipher.getInstance(AES_ALG);
enc.init(Cipher.ENCRYPT_MODE, key, spec);
OutputStream os = new CipherOutputStream(new FileOutputStream(f), enc);
return os;
}
private static InputStream decryptStream(SecretKey key, byte[] iv, File f) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, FileNotFoundException
{
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE, iv);
Cipher dec = Cipher.getInstance(AES_ALG);
dec.init(Cipher.DECRYPT_MODE, key, spec);
InputStream is = new CipherInputStream(new FileInputStream(f), dec);
return is;
}
它没有挂起,只是非常慢。 CipherInputStream
有一个大小为 512 的固定输入缓冲区,这意味着它一次调用最多 512 个字节的 Cipher#update(byte[], int, int)
方法。使用更大的缓冲区大小手动解密使其速度更快。
原因是使用 512 字节调用 update
50 000 次比使用 65 KB 调用 400 次花费的时间要长得多。我不确定具体原因,但似乎每次调用 update
都需要支付一定的开销,无论您传递的数据量是多少。
另外请注意,您不能使用 AES GCM 来解密大文件。按照设计,Sun 的密码实现在解密之前将整个密文缓冲在内存中。您必须将明文拆分为足够小的块并单独加密每个块。
另见 https://crypto.stackexchange.com/questions/20333/encryption-of-big-files-in-java-with-aes-gcm and How come putting the GCM authentication tag at the end of a cipher stream require internal buffering during decryption?。
我正在尝试 encrypt/decrypt 一些文件,我将 reading/writing 使用 FileIn/OutputStream
通过 CipherIn/OutputStream
传输的文件。概念上相当简单,我已经使用原始字节数组和 Cipher.doFinal
实现了它。所以我知道我的加密参数(位大小、iv 大小等)是正确的。 (或者至少是功能性的?)
我可以通过 CipherOutputStream
写入数据。但是,当我尝试通过 CipherInputStream
读回该数据时,它会无限期地挂起。
我发现的唯一 related problem 仍然没有答案,并且可能与我的问题根本不同,因为我的问题将始终在磁盘上提供所有可用数据,而不是相关问题依赖于 Sockets
.
我尝试了多种解决方案,最明显的一种是更改缓冲区大小 (data = new byte[4096];
)。我尝试了很多值,包括明文的大小和加密数据的大小。 None 这些值有效。我找到的唯一解决方案是完全避免使用 CipherInputStream
,而是依赖 Cipher.doFinal
和 Cipher.update
.
我错过了什么吗?如果能够使用 CipherInputStream
,而不是必须使用 Cipher.update
.
SSCCE:
private static final String AES_ALG = "aes_256/gcm/nopadding";
private static final int GCM_TAG_SIZE = 128;
private static void doEncryptionTest() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, FileNotFoundException, IOException
{
File f = new File("encrypted_random_data.dat");
// 12-byte long iv
byte[] iv = new byte[] {0x27, 0x51, 0x34, 0x14, -0x65, 0x4d, -0x67, 0x35, -0x63, 0x11, -0x02, -0x05};
// 256-bit long key
byte[] keyBytes = new byte[] {0x55, -0x7f, -0x17, -0x29, -0x68, 0x25, 0x29, 0x5f, -0x27, -0x2d, -0x4d, 0x1b,
0x25, 0x74, 0x57, 0x35, -0x23, -0x1b, 0x12, 0x7c, 0x1, -0xf, -0x60, -0x42, 0x1c, 0x61, 0x3e, -0x5,
-0x13, 0x31, -0x48, -0x6e};
SecretKey key = new SecretKeySpec(keyBytes, "AES");
OutputStream os = encryptStream(key, iv, f);
System.out.println("generating random data...");
// 24MB of random data
byte[] data = new byte[25165824];
new Random().nextBytes(data);
System.out.println("encrypting and writing data...");
os.write(data);
os.close();
InputStream is = decryptStream(key, iv, f);
System.out.println("reading and decrypting data...");
// read the data in 4096 byte packets
int n;
data = new byte[4096];
while ((n = is.read(data)) > 0)
{
System.out.println("read " + n + " bytes.");
}
is.close();
}
private static OutputStream encryptStream(SecretKey key, byte[] iv, File f) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, FileNotFoundException
{
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE, iv);
Cipher enc = Cipher.getInstance(AES_ALG);
enc.init(Cipher.ENCRYPT_MODE, key, spec);
OutputStream os = new CipherOutputStream(new FileOutputStream(f), enc);
return os;
}
private static InputStream decryptStream(SecretKey key, byte[] iv, File f) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, FileNotFoundException
{
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE, iv);
Cipher dec = Cipher.getInstance(AES_ALG);
dec.init(Cipher.DECRYPT_MODE, key, spec);
InputStream is = new CipherInputStream(new FileInputStream(f), dec);
return is;
}
它没有挂起,只是非常慢。 CipherInputStream
有一个大小为 512 的固定输入缓冲区,这意味着它一次调用最多 512 个字节的 Cipher#update(byte[], int, int)
方法。使用更大的缓冲区大小手动解密使其速度更快。
原因是使用 512 字节调用 update
50 000 次比使用 65 KB 调用 400 次花费的时间要长得多。我不确定具体原因,但似乎每次调用 update
都需要支付一定的开销,无论您传递的数据量是多少。
另外请注意,您不能使用 AES GCM 来解密大文件。按照设计,Sun 的密码实现在解密之前将整个密文缓冲在内存中。您必须将明文拆分为足够小的块并单独加密每个块。
另见 https://crypto.stackexchange.com/questions/20333/encryption-of-big-files-in-java-with-aes-gcm and How come putting the GCM authentication tag at the end of a cipher stream require internal buffering during decryption?。