java - CipherInputStream 挂起
问题描述
我正在尝试使用加密通过使用 java 的套接字进行通信。我已经成功地与没有加密的套接字通信,但是当我尝试加密时,程序冻结了。
这是可以正常工作的父Connection
类:
public class Connection implements Runnable
{
protected Socket socket;
protected ObjectInputStream objectInputStream;
protected ObjectOutputStream objectOutputStream;
protected Thread listeningThread;
protected Thread dispatchThread;
protected boolean listen;
protected ArrayBlockingQueue<Object> readingQueue;
protected ConnectionListener connectionListener;
public Connection()
{
listen = true;
readingQueue = new ArrayBlockingQueue<Object>(10);
}
public Connection(Socket socket, ConnectionListener listener)
{
listen = true;
connectionListener = listener;
readingQueue = new ArrayBlockingQueue<Object>(10);
this.socket = socket;
try
{
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectInputStream = new ObjectInputStream(socket.getInputStream());
}
catch (IOException e)
{
e.printStackTrace();
}
startConnection();
}
这是使用加密的子类:
public class EncryptedConnection extends Connection
{
private Key key;
private Cipher cipherEncryption;
private Cipher cipherDecryption;
public EncryptedConnection(Socket socket, ConnectionListener listener, byte[] keydata)
{
super();
super.socket = socket;
super.connectionListener = listener;
try
{
key = new SecretKeySpec(keydata, "AES");
cipherEncryption = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecryption = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipherEncryption.init(Cipher.ENCRYPT_MODE, key, ivspec);
cipherDecryption.init(Cipher.DECRYPT_MODE, key, ivspec);
objectOutputStream = new ObjectOutputStream(new CipherOutputStream(socket.getOutputStream(),cipherEncryption));
objectInputStream = new ObjectInputStream(new CipherInputStream(socket.getInputStream(),cipherDecryption));
//The hanging or freezing occurs on the above line of code
}
catch(Exception e)
{
}
这是创建套接字的服务器代码:
@Override
public void run()
{
try
{
while(true)
{
Socket s = serverSocket.accept();
byte[] key = new byte[16];
for(int i=0;i<key.length;i++)
key[i] = 0x01;
EncryptedConnection c = new EncryptedConnection(s,connectionListener,key);
connections.add(c);
System.out.println("New Connection Established From"+s.getInetAddress().toString());
}
}
catch(java.net.SocketException e)
{
System.out.println("Listening thread terminated with exception.");
}
catch(IOException e)
{
e.printStackTrace();
}
}
这是创建套接字的客户端代码:
@Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == connect)
{
try
{
Socket s = new Socket(ipBox.getText(), Integer.parseInt(portBox.getText()));
byte[] key = new byte[16];
for(int i=0;i<key.length;i++)
key[i] = 0x01;
EncryptedConnection c = new EncryptedConnection(s,parent,key);
parent.connectionSuccessful(c);
}
catch (NumberFormatException e1)
{
JOptionPane.showMessageDialog(this, "Error! Port number must be a number", "Error", JOptionPane.ERROR_MESSAGE);
}
catch (UnknownHostException e1)
{
JOptionPane.showMessageDialog(this, "Error! Unable to find that host", "Error", JOptionPane.ERROR_MESSAGE);
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
我看过这篇文章,但没有发现它有帮助。 带有 CipherInputStream 的 ObjectInputStream 冻结、 挂起
这是可以正常工作的示例代码。我基本上在做同样的事情,但不是文件,而是使用套接字。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class IOTest
{
public static void main(String[] args)
{
FileOutputStream fos = null;
FileInputStream fis = null;
CipherInputStream cis = null;
CipherOutputStream cos = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
Key key = null;
Cipher cipherD = null;
Cipher cipherE = null;
byte[] keydata = new byte[16];
byte[] iv = new byte[16];
IvParameterSpec ivspect = new IvParameterSpec(iv);
try
{
key = new SecretKeySpec(keydata,"AES");
cipherE = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherD = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherE.init(Cipher.ENCRYPT_MODE, key,ivspect);
cipherD.init(Cipher.DECRYPT_MODE, key, ivspect);
fos = new FileOutputStream("hello.data");
cos = new CipherOutputStream(fos,cipherE);
oos = new ObjectOutputStream(cos);
oos.writeObject(new String("H"));
oos.flush();
oos.close();
fis = new FileInputStream("hello.data");
cis = new CipherInputStream(fis, cipherD);
ois = new ObjectInputStream(cis);
String s = ois.readObject().toString();
System.out.println(s);
ois.close();
}
catch(Exception e)
{
}
}
}
解决方案
由于AES是一种块密码(块大小为 128 位),它以 16 字节块的形式处理数据。如果没有足够的数据用于完整的加密块,则数据将仅位于输入缓冲区中,等待更多数据出席。在接收端,您只会被卡住。
只有当一个完整的块有足够的数据或者如果流关闭时,才会处理卡住的数据。在关闭流的情况下,最终数据会根据使用的填充方案(例如 PKCS5Padding)修补到完整的块大小。
推荐阅读
- github-actions - Github Action 是否支持按需自托管运行器?
- python - 如何强制第二级多索引的值?
- c# - Unity - 暂停/恢复按钮会触发我的跳跃动画 - 如何解决?
- jdbc - JBoss 池的 IdleCount 值为负
- python - 重新拟合已保存的 scikit-learn 模型,但未使用某些功能 - “ValueError: A given column is not a column of the dataframe”
- javascript - 如何在 React 中动态传递组件?
- reactjs - SyncState 与将整个状态发送给其他客户端有何不同?
- javascript - 将带有两个文件和文本的 FormData 发送到控制器
- c# - System.IO.DirectoryNotFoundException 找不到路由的一部分
- sql-server - 用于备份和保存 SQL Server 数据库的 VB.Net 代码不起作用