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() 方法的行为方式。它只会将数据刷新到完整的密码块边界。如果您发送的消息长度不是密码块长度的精确倍数,则某些数据将保留在流的缓冲区中,直到您写入更多数据或关闭流。关闭流填充最终输入,使其与块边界对齐。
这一限制是合理的,因为分组密码一次只对一个数据块进行运算。
我的套接字需要加密的 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() 方法的行为方式。它只会将数据刷新到完整的密码块边界。如果您发送的消息长度不是密码块长度的精确倍数,则某些数据将保留在流的缓冲区中,直到您写入更多数据或关闭流。关闭流填充最终输入,使其与块边界对齐。
这一限制是合理的,因为分组密码一次只对一个数据块进行运算。