【问题标题】:ObjectInputStream with CipherInputStream freezing, hangingObjectInputStream 与 CipherInputStream 冻结、挂起
【发布时间】:2012-02-22 23:47:57
【问题描述】:

我正在编写基于客户端-服务器的 Java 应用程序,但遇到了一个问题,因为在客户端和服务器中构造 ObjectInputStream 时它都会挂起。

客户:

Socket socket = new Socket("localhost", 9999);

outCiph = new CipherOutputStream(socket.getOutputStream(), AES.getEncryptCipher("key"));
out = new ObjectOutputStream(outCiph);
inCiph = new CipherInputStream(socket.getInputStream(), AES.getDecryptCipher("key"));
in = new ObjectInputStream(inCiph);

try
{
String text = "test!";

out.writeObject(text);
out.flush();

if (out != null)
out.close();

if (in != null)
in.close();
}
catch (IOException ex)
{
System.err.println(ex.toString());
}

服务器:

ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();

outCiph = new CipherOutputStream(socket.getOutputStream(), AES.getEncryptCipher("key"));
out = new ObjectOutputStream(outCiph);
inCiph = new CipherInputStream(socket.getInputStream(), AES.getDecryptCipher("key"));
in = new ObjectInputStream(inCiph);

try
{
String rec = (String) in.readObject();
System.out.println("Received from client: " + rec);

if (out != null)
out.close();

if (in != null)
in.close();

}
catch (IOException ex)
{
System.err.println(ex.toString() + " in start()");
}
catch (ClassNotFoundException ex)
{
System.err.println(ex.toString());
}

AES:

// I'm not author of generateKey method so I've no idea if is it correct
private static byte[] generateKey(String pass) throws UnsupportedEncodingException, NoSuchAlgorithmException
{
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] passBytes = pass.getBytes("ASCII");
byte[] sha256Bytes = sha.digest(passBytes);


byte[] key = new byte[16];
int j = 0;
for (int i = 0; i < sha256Bytes.length; i++)
{
    if (i % 2 == 0)
    {
    key[j] = sha256Bytes[i];
    j++;
    }
}
return key;
}

public static Cipher getEncryptCipher(String pass)
{
try
{
    SecretKeySpec skeySpec = new SecretKeySpec(generateKey(pass), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    return cipher;
}
catch (Exception ex) // just for clarity
{
    Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}

public static Cipher getDecryptCipher(String pass)
{
try
{
    SecretKeySpec skeySpec = new SecretKeySpec(generateKey(pass), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    return cipher;
}
catch (Exception ex) // just for clarity
{
    Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}

当我不使用 CipherInput/OutputStream 时,一切正常,因此问题与 CipherInput/OutputStream 有关。

【问题讨论】:

  • DES.getEncryptCipher 返回什么样的密码?你能打印出返回密码的算法和状态吗?您可能需要一个执行填充的密码,否则它可能正在等待恰好 N * [块大小] 字节来完成计算。 注意DES不安全,使用AES
  • DES 只是一个例子,我正在测试另一个类。主要加密类是上面的,不是 DES。
  • 好的,但请使用 "AES/CBC/PKCS5Padding" 作为密码,因为 "AES" 默认为 "AES/ECB/PKCS5Padding",至少对于 Sun/Oracle 提供程序而言,ECB 对于非随机字节是不安全的,例如标题和对象。

标签: java encryption stream streaming jca


【解决方案1】:

您需要在发送所有信息后创建 ObjectInputStream,因为 ObjectInputStream 的构造函数会阻塞,因为它需要读取标头。

通常,所有字节都将由 ObjectOutputStream 写入,但现在 CipherOutputStream 正在等待,直到它有一个完整的 16 字节块(在 AES 的情况下),然后才发送(最后一部分)标头。也许流密码模式(CTR 或 GCM)中的 AES 在这里会更有用,因为它使用按字节加密,并且能够直接发送每个字节。

【讨论】:

  • 您可以执行一些 hack,例如在创建构造函数后直接写入 字节的字节数组,然后刷新 ObjectOutputStream。当然,你还需要在另一端读取那些虚假的 16 个字节。
  • 如果您跟进您的问题,user1227115,或者点击接受按钮,那就太好了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 2020-06-19
  • 1970-01-01
  • 2015-02-28
  • 1970-01-01
相关资源
最近更新 更多