Java的CipherInputStream使用AES/GCM加密不会读取直到关闭。

huangapple go评论81阅读模式
英文:

Java CipherInputStream using AES/GCM encryption wont read until close

问题

我需要一个用于套接字的加密输入/输出流。我已经尝试了几乎所有的方法来修复这个问题,但都没有成功。我知道一旦套接字或输出流被关闭,套接字将会接收数据。然而,这对我来说没有任何用处。我会很乐意听取任何关于这个问题的意见。

"这不是重复问题,虽然有关于这个主题的其他问题,但是没有一个能提供答案。"

对于任何遇到相同问题的人,我创建了一个自定义的加密流,并在其中添加了DataInputStream / DataOutputStream。在对GCM进行了一些小的调整后,它可以与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;
}
英文:

I need an encrypted in/output stream for my sockets. I have tried just about everything out there to fix this with no luck. I know the socket will receive the data once the socket or output stream are closed. However this is not useful what so ever. I would love any input on this.

"This is not a duplicate, there are other questions about this subject, however none provide an answer to this."

For anyone that is struggling with this same issue I made a custom cipher stream and added DataInputStream / DataOutputStream to it. It will work fine with GCM with some minor tweaks.

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;
}

答案1

得分: 1

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

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

英文:

CipherOutputStream is not suitable for this kind of use, because of how its flush() method behaves. It only flushes data up to a complete cipher block boundary. If the length of the message you're sending is not an exact multiple of the cipher block length, some of the data is going to stay in the stream's buffer until you write more data or close the stream. Closing the stream pads the final input so it aligns to a block boundary.

This limitation is reasonable, since block ciphers operate on one block of data at a time.

huangapple
  • 本文由 发表于 2020年7月23日 22:35:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/63056792.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定