Java ChaCha20-Poly1305因ShortBufferException输出缓冲区太小而失败
我正在为大文件编写一个文件加密基准测试,并测试了ChaCha20-Poly1305,但在解密部分收到一个错误:Java ChaCha20-Poly1305因ShortBufferException输出缓冲区太小而失败,java,encryption,encryption-symmetric,Java,Encryption,Encryption Symmetric,我正在为大文件编写一个文件加密基准测试,并测试了ChaCha20-Poly1305,但在解密部分收到一个错误: java.lang.RuntimeException: javax.crypto.ShortBufferException: Output buffer too small at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:703) at java.
java.lang.RuntimeException: javax.crypto.ShortBufferException: Output buffer too small
at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:703)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2085)
at ChaCha20.ChaCha20Poly1305Jre.main(ChaCha20Poly1305Jre.java:73)
Caused by: javax.crypto.ShortBufferException: Output buffer too small
at java.base/com.sun.crypto.provider.ChaCha20Cipher$EngineAEADDec.doFinal(ChaCha20Cipher.java:1360)
at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:701)
这在我的程序中不是一个错误,但在我正在使用的OpenJava11中应该得到修复(2019年就知道了,请参阅)。
即使使用最新的“早期采用者”版本(OpenJDK11U-jdk_x64_windows_11.0.7_9_ea),错误仍然会发生。此测试使用java版本11.0.6+8-b520.43运行
我的问题是:对于大型文件,有没有其他方法可以使用ChaCha20-Poly1305和本机JCE执行文件加密?
我不想使用BouncyCastle(因为我正在使用BC allready进行对应的基准测试)或将明文完全读入内存(在我的源代码中)
testfile仅1.024字节大,但基准测试将测试多达1GB的文件)。我也不想使用ChaCha20,因为它不提供任何身份验证。
您可以在我的Github Repo中找到ChaCha20Poly1305Jce.java、ChaCha20Poly1305JceNoStream.java和ChaCha20Jce.java的源代码
导入javax.crypto.Cipher;
导入javax.crypto.NoSuchPaddingException;
导入javax.crypto.spec.IvParameterSpec;
导入javax.crypto.spec.SecretKeySpec;
导入java.io.*;
导入java.nio.file.Files;
导入java.nio.file.path;
导入java.nio.file.StandardOpenOption;
导入java.security.*;
导入java.security.spec.AlgorithmParameterSpec;
导入java.util.array;
公共类ChaCha20Poly1305Jce{
公共静态void main(字符串[]args)抛出IOException、NoSuchAlgorithmException、NoSuchPaddingException、InvalidAlgorithmParameterException、InvalidKeyException{
System.out.println(“使用ChaCha20-Poly1305 JCE进行文件En-/解密”);
System.out.println(“参见:https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small");
System.out.println(“\njava版本:“+Runtime.version()”);
字符串filenameplane=“test1024.txt”;
字符串filenameEnc=“test1024enc.txt”;
字符串filenameDec=“test1024dec.txt”;
Files.deleteIfExists(新文件(filenameplane.toPath());
GeneratorDomainFile(filenamePlain,1024);
//设置chacha20-poly1305-cipher
SecureRandom sr=SecureRandom.getInstanceStrong();
字节[]键=新字节[32];//32表示256位键或16表示128位键
字节[]nonce=新字节[12];//nonce=96位
高级nextBytes(钥匙);
高级次字节(nonce);
//获取密码实例
Cipher cipherE=Cipher.getInstance(“ChaCha20-Poly1305/None/NoPadding”);
//创建参数规范
AlgorithmParameterSpec AlgorithmParameterSpec=新的IvParameterSpec(nonce);
//创建SecretKeySpec
SecretKeySpec keySpec=新的SecretKeySpec(键,“ChaCha20”);
System.out.println(“keySpec:+keySpec.getAlgorithm()+”+keySpec.getFormat());
System.out.println(“密码算法:+cipherE.getAlgorithm());
//初始化密码以进行加密
init(Cipher.ENCRYPT_模式、密钥规范、算法参数规范);
//加密
System.out.println(“启动加密”);
字节inE[]=新字节[8192];
字节outE[]=新字节[8192];
try(InputStream=newfileinputstream(newfile(filenameplane));
OutputStream os=新文件OutputStream(新文件(filenameEnc))){
int len=0;
而(-1!=(len=is.read(inE))){
cipherE.update(inE,0,len,outE,0);
写操作(outE,0,len);
}
字节[]outEf=cipherE.doFinal();
写操作(outEf,0,outEf.length);
}捕获(例外e){
e、 printStackTrace();
}
//解密
System.out.println(“开始解密”);
Cipher Cipher=Cipher.getInstance(“ChaCha20-Poly1305/None/NoPadding”);
//初始化密码进行解密
cipherD.init(Cipher.DECRYPT_模式、密钥规范、算法参数规范);
字节inD[]=新字节[8192];
字节outD[]=新字节[8192];
try(InputStream=newfileinputstream(newfile(filenameEnc));
OutputStream os=新文件OutputStream(新文件(filenameDec))){
int len=0;
而(-1!=(len=is.read(inD))){
密码更新(inD,0,len,outD,0);
os.write(out,0,len);
}
字节[]outDf=cipherD.doFinal();
写操作(outDf,0,outDf.length);
}捕获(例外e){
e、 printStackTrace();
}
//文件比较
System.out.println(“比较普通dec:+Arrays.equals(sha256(filenameplane)、sha256(filenameDec));
}
公共静态void generateRandomFile(字符串文件名,int size)抛出IOException、NoSuchAlgorithmException{
SecureRandom sr=SecureRandom.getInstanceStrong();
字节[]数据=新字节[大小];
高级下字节(数据);
文件.write(路径.get(文件名)、数据、StandardOpenOption.CREATE);
}
公共静态字节[]sha256(字符串filenameString)引发IOException、NoSuchAlgorithmException{
字节[]缓冲区=新字节[8192];
整数计数;
MessageDigest md=MessageDigest.getInstance(“SHA-256”);
BufferedInputStream bis=新的BufferedInputStream(新的FileInputStream(filenameString));
而((计数=双读(缓冲区))>0){
md.update(缓冲区,0,计数);
}
二、关闭();
返回md.digest();
}
}
使用OpenJDK 11.0.8的早期访问版本(在此处获取:)
我能够运行(编辑过的)测试程序(现在我使用的是CipherInput/OutputStreams)
使用ChaCha20-Poly1305 JCE对文件进行加密/解密
见:https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small
java版本:11.0.8-ea+8
开始加密
按键:ChaCha20原始
密码a
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
public class ChaCha20Poly1305Jce {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
System.out.println("File En-/Decryption with ChaCha20-Poly1305 JCE");
System.out.println("see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small");
System.out.println("\njava version: " + Runtime.version());
String filenamePlain = "test1024.txt";
String filenameEnc = "test1024enc.txt";
String filenameDec = "test1024dec.txt";
Files.deleteIfExists(new File(filenamePlain).toPath());
generateRandomFile(filenamePlain, 1024);
// setup chacha20-poly1305-cipher
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] key = new byte[32]; // 32 for 256 bit key or 16 for 128 bit
byte[] nonce = new byte[12]; // nonce = 96 bit
sr.nextBytes(key);
sr.nextBytes(nonce);
// Get Cipher Instance
Cipher cipherE = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
// Create parameterSpec
AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key, "ChaCha20");
System.out.println("keySpec: " + keySpec.getAlgorithm() + " " + keySpec.getFormat());
System.out.println("cipher algorithm: " + cipherE.getAlgorithm());
// initialize the cipher for encryption
cipherE.init(Cipher.ENCRYPT_MODE, keySpec, algorithmParameterSpec);
// encryption
System.out.println("start encryption");
byte inE[] = new byte[8192];
byte outE[] = new byte[8192];
try (InputStream is = new FileInputStream(new File(filenamePlain));
OutputStream os = new FileOutputStream(new File(filenameEnc))) {
int len = 0;
while (-1 != (len = is.read(inE))) {
cipherE.update(inE, 0, len, outE, 0);
os.write(outE, 0, len);
}
byte[] outEf = cipherE.doFinal();
os.write(outEf, 0, outEf.length);
} catch (Exception e) {
e.printStackTrace();
}
// decryption
System.out.println("start decryption");
Cipher cipherD = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
// initialize the cipher for decryption
cipherD.init(Cipher.DECRYPT_MODE, keySpec, algorithmParameterSpec);
byte inD[] = new byte[8192];
byte outD[] = new byte[8192];
try (InputStream is = new FileInputStream(new File(filenameEnc));
OutputStream os = new FileOutputStream(new File(filenameDec))) {
int len = 0;
while (-1 != (len = is.read(inD))) {
cipherD.update(inD, 0, len, outD, 0);
os.write(outD, 0, len);
}
byte[] outDf = cipherD.doFinal();
os.write(outDf, 0, outDf.length);
} catch (Exception e) {
e.printStackTrace();
}
// file compare
System.out.println("compare plain <-> dec: " + Arrays.equals(sha256(filenamePlain), sha256(filenameDec)));
}
public static void generateRandomFile(String filename, int size) throws IOException, NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] data = new byte[size];
sr.nextBytes(data);
Files.write(Paths.get(filename), data, StandardOpenOption.CREATE);
}
public static byte[] sha256(String filenameString) throws IOException, NoSuchAlgorithmException {
byte[] buffer = new byte[8192];
int count;
MessageDigest md = MessageDigest.getInstance("SHA-256");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filenameString));
while ((count = bis.read(buffer)) > 0) {
md.update(buffer, 0, count);
}
bis.close();
return md.digest();
}
}
File En-/Decryption with ChaCha20-Poly1305 JCE
see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small
java version: 11.0.8-ea+8
start encryption
keySpec: ChaCha20 RAW
cipher algorithm: ChaCha20-Poly1305/None/NoPadding
start decryption
compare plain <-> dec: true
java version: 11.0.8+10
start encryption
keySpec: ChaCha20 RAW
cipher algorithm: ChaCha20-Poly1305/None/NoPadding
start decryption
compare plain <-> dec: true
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.*;
import java.util.Arrays;
public class ChaCha20Poly1305JceCis {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
System.out.println("File En-/Decryption with ChaCha20-Poly1305 JCE");
System.out.println("see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small");
System.out.println("\njava version: " + Runtime.version());
String filenamePlain = "test1024.txt";
String filenameEnc = "test1024enc.txt";
String filenameDec = "test1024dec.txt";
Files.deleteIfExists(new File(filenamePlain).toPath());
generateRandomFile(filenamePlain, 1024);
// setup chacha20-poly1305-cipher
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] key = new byte[32]; // 32 for 256 bit key or 16 for 128 bit
byte[] nonce = new byte[12]; // nonce = 96 bit
sr.nextBytes(key);
sr.nextBytes(nonce);
System.out.println("start encryption");
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
try (FileInputStream in = new FileInputStream(filenamePlain);
FileOutputStream out = new FileOutputStream(filenameEnc);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "ChaCha20");
System.out.println("keySpec: " + secretKeySpec.getAlgorithm() + " " + secretKeySpec.getFormat());
System.out.println("cipher algorithm: " + cipher.getAlgorithm());
//AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(nonce));
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
// decryption
System.out.println("start decryption");
Cipher cipherD = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
try (FileInputStream in = new FileInputStream(filenameEnc); // i don't care about the path as all is lokal
CipherInputStream cipherInputStream = new CipherInputStream(in, cipherD);
FileOutputStream out = new FileOutputStream(filenameDec)) // i don't care about the path as all is lokal
{
byte[] buffer = new byte[8192];
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "ChaCha20");
//AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
cipherD.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(nonce));
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
// file compare
System.out.println("compare plain <-> dec: " + Arrays.equals(sha256(filenamePlain), sha256(filenameDec)));
}
public static void generateRandomFile(String filename, int size) throws IOException, NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] data = new byte[size];
sr.nextBytes(data);
Files.write(Paths.get(filename), data, StandardOpenOption.CREATE);
}
public static byte[] sha256(String filenameString) throws IOException, NoSuchAlgorithmException {
byte[] buffer = new byte[8192];
int count;
MessageDigest md = MessageDigest.getInstance("SHA-256");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filenameString));
while ((count = bis.read(buffer)) > 0) {
md.update(buffer, 0, count);
}
bis.close();
return md.digest();
}
}