Java ChaCha20-Poly1305因ShortBufferException输出缓冲区太小而失败

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.

我正在为大文件编写一个文件加密基准测试,并测试了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.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();
    }
}