You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CipherInputStream/OutputStream传输RSA加密数据时遇BadPaddingException

问题:使用CipherOutputStream/CipherInputStream网络传输加密数据时出现BadPaddingException

尝试用CipherOutputStream/CipherInputStream通过网络传输加密数据,一直报BadPaddingException。双方使用相同的RSA密钥,手动加解密完全正常,公私钥模数也匹配。


手动加解密可行示例代码

public static void main(String[] args) {
    PrintStream out = System.out;
    try {
        PrivateKey pri_key = Util.readPrivateKey("data/private.der");
        PublicKey pub_key = Util.readPublicKey("data/public.der");

        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");

        String hi = "Hello World";
        byte[] data = hi.getBytes("UTF8");
        out.println("data[" + data.length + "]");

        cipher.init(Cipher.ENCRYPT_MODE, pub_key);
        byte[] encdata = cipher.doFinal(data);
        out.println("encdata[" + encdata.length + "]");

        cipher.init(Cipher.DECRYPT_MODE, pri_key);
        byte[] decdata = cipher.doFinal(encdata);
        out.println("decdata[" + decdata.length + "]");
        out.println(new String(decdata, "UTF8"));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

网络收发加密代码

static class ByteEncComm extends Comm {
    public ByteEncComm(Socket sock, PrivateKey priv_key, PublicKey pub_key) {
        super(sock, priv_key, pub_key);
    }

    public void send(Bird b) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, public_key);

            byte[] data = "Hello World".getBytes("UTF8");
            CipherOutputStream cos = new CipherOutputStream(sock.getOutputStream(), cipher);
            cos.write(data, 0, data.length);
            cos.flush();
            out.println("Sent data[" + data.length + "]");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Bird receive() {
        PrintStream out = System.out;
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
            cipher.init(Cipher.DECRYPT_MODE, private_key);

            CipherInputStream cis = new CipherInputStream(sock.getInputStream(), cipher);
            out.println("Avail " + cis.available());
            byte[] data = cis.readAllBytes();
            out.println("Got data[" + data.length + "]");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

客户端代码

public class Client {
    public static void main(String[] args) {
        PrintStream out = System.out;
        try {
            PrivateKey pri_key = Util.readPrivateKey("data/private.der");
            PublicKey pub_key = Util.readPublicKey("data/public.der");

            Socket sock = new Socket("127.0.0.1", 5555);

            Comm comm = new Comm.ByteEncComm(sock, pri_key, pub_key);
            comm.send(new Bird("Robin"));

            sock.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务端代码

public class Server extends Thread {
    public static void main(String[] args) {
        PrintStream out = System.out;
        try {
            PrivateKey pri_key = Util.readPrivateKey("data/private.der");
            PublicKey pub_key = Util.readPublicKey("data/public.der");

            ServerSocket serversock = new ServerSocket(5555);
            Socket sock = serversock.accept();

            Comm comm = new Comm.ByteEncComm(sock, pri_key, pub_key);
            comm.receive();

            sock.close();
            serversock.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

报错信息

java.io.IOException: javax.crypto.BadPaddingException: Decryption error
    at java.base/javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:148)
    at java.base/javax.crypto.CipherInputStream.read(CipherInputStream.java:261)
    at java.base/java.io.InputStream.readNBytes(InputStream.java:409)
    at java.base/java.io.InputStream.readAllBytes(InputStream.java:346)
    at foo.enc.Comm$ByteEncComm.receive(Comm.java:139)
    at foo.enc.Server.main(Server.java:21)
Caused by: javax.crypto.BadPaddingException: Decryption error
    at java.base/sun.security.rsa.RSAPadding.unpadOAEP(RSAPadding.java:488)
    at java.base/sun.security.rsa.RSAPadding.unpad(RSAPadding.java:284)
    at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:372)
    at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:418)
    at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2152)
    at java.base/javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:145)
    ... 5 more

问题原因与解决方案

核心问题1:RSA流式加密的误用

RSA是块加密算法,单块长度受密钥长度限制(比如2048位RSA,OAEP padding下单块最大明文长度为214字节)。CipherOutputStream会按块加密数据,但仅调用flush()不会触发doFinal()——RSA加密需要该方法完成最终块的padding操作。客户端没有关闭流,导致服务端收到的加密数据不完整,解密时padding校验失败。

核心问题2:密钥使用逻辑错误

客户端发送数据时应该用服务端的公钥加密,服务端用自己的私钥解密;但当前代码中客户端和服务端都使用本地的公私钥对,相当于客户端用自己的公钥加密,服务端用自己的私钥解密,密钥不匹配必然解密失败。

修复步骤:

  1. 修正密钥使用逻辑

    • 客户端加载服务端的公钥进行加密;
    • 服务端加载自身私钥进行解密。
  2. 正确关闭CipherOutputStream
    在客户端send方法中,写完数据后必须调用cos.close(),触发doFinal()完成加密:

    public void send(Bird b) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
            // 替换为服务端公钥
            cipher.init(Cipher.ENCRYPT_MODE, server_public_key);
    
            byte[] data = "Hello World".getBytes("UTF8");
            CipherOutputStream cos = new CipherOutputStream(sock.getOutputStream(), cipher);
            cos.write(data, 0, data.length);
            cos.close(); // 必须关闭流以完成最终块加密
            out.println("Sent data[" + data.length + "]");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
  3. 优化RSA使用场景
    RSA不适合直接加密大体积数据,常规方案是:

    • 生成随机对称密钥(如AES);
    • 用对称密钥加密实际数据;
    • 用RSA加密对称密钥,与加密数据一同发送;
    • 接收方先用RSA解密出对称密钥,再解密数据。

内容的提问来源于stack exchange,提问作者trungdok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 12:25:03