使用 ObjectOutputStream 发送 public 密钥的字节数组,加密一些内容,然后将加密版本发回

Using ObjectOutputStream to send a byte array of a public key, encrypt something, and send the encrypted version back

所以我有服务器端public密钥和私钥,我的目的是向客户端发送public密钥,客户端将使用密钥加密一个字符串,然后发送字节通过流,服务器将解密字节数组。

异常:

javax.crypto.BadPaddingException: Decryption error

代码:

正在发送编码密钥。

                    handler.getOos().writeObject(publicKey.getEncoded());
                    handler.getOos().flush();

接收(编码密钥的)字节数组:

                        Object o = ois.readObject();
                        if (o instanceof byte[]) {
                            JChat.get().setServerPublicKey(KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec((byte[]) o)));
                            JChat.get().go();
                        }

go()方法(这里我使用DataOutputStream来发送字节数组):

public void go() {
    String text = "hello darkness my old friend";
    byte[] encrypted = encrypt(text, serverPublicKey);
    try {
        handler.getDos().write(encrypted);
        handler.getDos().flush();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

读取字节数组,在服务器端:

                        int count = dis.available();
                        byte[] in = new byte[count];
                        dis.readFully(in);
                        System.out.println(Server.decrypt(in, Server.get().getPrivateKey()));

解密方法抛出异常:

javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.archiepking.Server.decrypt(Server.java:97)
at com.archiepking.net.ClientHandler.run(ClientHandler.java:44)
at java.lang.Thread.run(Thread.java:745)

关于我做错了什么有什么建议吗?请注意:

Dos = DataOutputStream Dis = DataInputStream Oos = ObjectOutputStream Ois = ObjectInputStream

我正在使用两种不同的套接字,一种用于发送对象,一种用于数据类型(因为我的聊天应用程序需要两者)。

我该如何解决这个错误?

更多信息: 生成密钥:

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        FileOutputStream fosPublic = new FileOutputStream("public");
        fosPublic.write(publicKeyBytes);
        fosPublic.close();

        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        FileOutputStream fosPrivate = new FileOutputStream("private");
        fosPrivate.write(privateKeyBytes);
        fosPrivate.close();

        publicKey = keyPair.getPublic();
        privateKey = keyPair.getPrivate();

问题是您正在使用 DataInputStream.available() 来确定要读取多少字节。该方法 并没有像您认为的那样做

来自该方法的 Javadoc:

Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking by the next caller of a method for this input stream. The next caller might be the same thread or another thread. A single read or skip of this many bytes will not block, but may read or skip fewer bytes.

它只是 returns 可以读取的字节数 没有阻塞 ,这可能远远少于您发送的实际字节数,尤其是如果您正在使用网络套接字 send/receive 该数据。

解决方案:

  • 在写入字节之前,使用包含您正在写入的字节数的 writeInt 方法写入 int
  • 在读取字节之前,调用readInt读取后面的字节数,并根据该数字构造一个正确长度的字节数组。

如果您使用的是 ObjectOutputStream,为什么还要使用 getEncoded 将 public 键转换为字节数组?您可以直接序列化对象。例如 handler.getOos().writeObject(publicKey); 或者,如果您必须使用编码版本,则删除 ObjectOutputStream 并改用 ByteArrayOutputStream。