Java 使用 AES/GCM 加密的 CipherInputStream 在关闭之前不会读取

Java CipherInputStream using AES/GCM encryption wont read until close

我的套接字需要加密的 in/output 流。我已经尝试了几乎所有的方法来解决这个问题,但没有运气。我知道套接字或输出流关闭后套接字将接收数据。然而,这从来都没有用。我希望对此有任何意见。

"这不是重复的,关于这个主题还有其他问题,但是 none 提供了一个答案。"

对于遇到同样问题的任何人,我制作了一个自定义密码流并向其中添加了 DataInputStream / DataOutputStream。通过一些小的调整,它将与 GCM 一起正常工作。

https://gist.github.com/DrBrad/0148f54cb1e5f5b04414c7756b97996a

public static void testC()throws Exception {

    String password = "PASSWORD";

    Socket socket = new Socket("127.0.0.1", 7000);

    CipherInputStream in = new CipherInputStream(socket.getInputStream(), getEN(password, true));
    CipherOutputStream out = new CipherOutputStream(socket.getOutputStream(), getEN(password, false));

    out.write("HELLO WORLD".getBytes());
    out.flush();
    out.close();
}

public static void testS(){
    new Thread(new Runnable() {
        private Socket socket;
        @Override
        public void run() {
            try {
                ServerSocket serverSocket = new ServerSocket(7000);
                String password = "PASSWORD";

                System.out.println("STARTED");

                while((socket = serverSocket.accept()) != null){
                    CipherInputStream in = new CipherInputStream(socket.getInputStream(), getEN(password, true));
                    CipherOutputStream out = new CipherOutputStream(socket.getOutputStream(), getEN(password, false));

                    byte[] buffer = new byte[4096];

                    int length = in.read(buffer);

                    System.out.println(new String(buffer, 0, length));

                    socket.close();
                }
            }catch (Exception e){

            }
        }
    }).start();
}


public static Cipher getEN(String password, boolean t)throws Exception {
    byte[] salt = new byte[8], iv = new byte[16];

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
    KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
    SecretKey secretKey = factory.generateSecret(keySpec);
    SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec spec = new GCMParameterSpec(128, iv);
    if(t){
        cipher.init(Cipher.DECRYPT_MODE, secret, spec);
    }else{
        cipher.init(Cipher.ENCRYPT_MODE, secret, spec);
    }

    return cipher;
}

CipherOutputStream 不适合这种用途,因为其 flush() 方法的行为方式。它只会将数据刷新到完整的密码块边界。如果您发送的消息长度不是密码块长度的精确倍数,则某些数据将保留在流的缓冲区中,直到您写入更多数据或关闭流。关闭流填充最终输入,使其与块边界对齐。

这一限制是合理的,因为分组密码一次只对一个数据块进行运算。