javax.crypto.BadPaddingException:数据必须从零开始,客户端无法从服务器解密

时间:2014-04-01 10:33:08

标签: java encryption cryptography rsa public-key-encryption

运行代码时出现编译错误。

Error: javax.crypto.BadPaddingException: Data must start with zero

此行testClient.java

byte[] newPlainText = cipher.doFinal(cipherTextFromServer);

客户端无法解密从服务器加密的消息。 我的代码有什么问题吗?

// TestServer.java

public class TestServer {

public static void main(String[] args) throws Exception{
    //set variables for port number, RSA key size
    final int port = 3344;
    final int RSAKeySize = 1024;
    final String newline = "\n";
    //set public key, sockets, server text, plain text
    PublicKey pubKey = null;
    PrivateKey priKey = null;
    ServerSocket server = null;
    Socket client = null;
    String serverText = "Hello Client! This is an authentication message from server";
    byte[] plainText = serverText.getBytes("UTF8");
    //initialize RSA
    try {
        System.out.println("Start generating RSA key");
        KeyPairGenerator RSAKeyGen = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        RSAKeyGen.initialize(RSAKeySize,random);
        KeyPair pair = RSAKeyGen.generateKeyPair();
        System.out.println("Finish generating RSA key");
        pubKey = pair.getPublic();
        priKey = pair.getPrivate();
    }catch (GeneralSecurityException e){
        System.out.println(e.getLocalizedMessage() + newline);
        System.out.println("Error initialising encryption. Exiting.\n");
        System.exit(0);
    }
    //initialize cryptography, set cipherText
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    System.out.println("RSA cipher object and provider"+cipher.getProvider().getInfo());
    System.out.println("Start Encryption for plainText");
    cipher.init(Cipher.ENCRYPT_MODE, pubKey);
    byte[] cipherText = cipher.doFinal(plainText);
    System.out.println("Finish Encryption to cipherText: ");
    BASE64Encoder base64 = new BASE64Encoder();
    String encryptedValue = base64.encode(cipherText);
    //String encryptedValue = new sun.misc.BASE64Encoder().encode(cipherText);
    System.out.println(new String(cipherText,"UTF8"));
    System.out.println("Base64");
    System.out.println(encryptedValue);
    //initialize socket connection
    try{
        server = new ServerSocket(port);
        client = server.accept();
    }catch(IOException e){
        System.out.println("Error initialising I/O.\n");
        System.exit(0);
    }
    //send server private key
    try{
        System.out.println("Send private key out");
        System.out.println(DatatypeConverter.printHexBinary(priKey.getEncoded()));
        ByteBuffer bb = ByteBuffer.allocate(4);
        bb.putInt(priKey.getEncoded().length);
        client.getOutputStream().write(bb.array());
        client.getOutputStream().write(pubKey.getEncoded());
        client.getOutputStream().flush();
    }catch (IOException e){
        System.out.println("I/O Error");
        System.exit(0);
    }
    //send cipherText
    ObjectOutputStream obOut = new ObjectOutputStream(client.getOutputStream());
    obOut.writeObject(encryptedValue);
    obOut.flush();
    client.close();
}

}

// TestClient.java

public class TestClient {

public static void main(String[] args){ 
    //throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, ClassNotFoundException, 
    //InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    //set variable for port, socket, server public key
    final int port = 3344;
    Socket sock = null;
    Key serverPubKey = null;
    final int RSAKeySize = 1024;
    final String newline = "\n";
    Key priKey = null;
    //setup connection by creating socket
    try{
        sock = new Socket(InetAddress.getLocalHost(),port);
    }catch(UnknownHostException e){
        System.out.println("Unknown host.");
        System.exit(1);
    }catch(IOException e){
        System.out.println("No I/O");
        System.exit(1);
    }
    //get public key from server
    try{
        byte[] lenb = new byte[4];
        sock.getInputStream().read(lenb,0,4);
        ByteBuffer bb = ByteBuffer.wrap(lenb);
        int len = bb.getInt();
        System.out.println(len);
        byte[] servPubKeyBytes = new byte[len];
        sock.getInputStream().read(servPubKeyBytes);
        System.out.println(DatatypeConverter.printHexBinary(servPubKeyBytes));
        X509EncodedKeySpec ks = new X509EncodedKeySpec(servPubKeyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        serverPubKey = kf.generatePublic(ks);
        System.out.println(DatatypeConverter.printHexBinary(serverPubKey.getEncoded()));
        //PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));

    }catch (IOException e) {
        System.out.println("Error obtaining server public key 1.");
        System.exit(0);
    } catch (NoSuchAlgorithmException e) {
        System.out.println("Error obtaining server public key 2.");
        System.exit(0);
    } catch (InvalidKeySpecException e) {
        System.out.println("Error obtaining server public key 3.");
        System.exit(0);
    }
    try {
        System.out.println("Start generating RSA key");
        KeyPairGenerator RSAKeyGen = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        RSAKeyGen.initialize(RSAKeySize, random);
        KeyPair pair = RSAKeyGen.generateKeyPair();
        System.out.println("Finish generating RSA key");
        priKey = pair.getPrivate();
    }catch (GeneralSecurityException e){
        System.out.println(e.getLocalizedMessage() + newline);
        System.out.println("Error initialising encryption. Exiting.\n");
        System.exit(0);
    }
    try{
    //Decrypt message from server
        BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        String message = in.readLine();
        //ObjectInputStream obIn = new ObjectInputStream(sock.getInputStream());
        //Object obj = obIn.readObject();
        System.out.println(message);
        byte[] cipherTextFromServer = new BASE64Decoder().decodeBuffer(message);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        System.out.println("Start decryption");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        byte[] newPlainText = cipher.doFinal(cipherTextFromServer);
        System.out.println("Finish decryption: ");
        System.out.println(new String(newPlainText,"UTF8"));
        sock.close();
    }catch(Exception e){
        e.printStackTrace();
    }   
}

}

2 个答案:

答案 0 :(得分:3)

您的代码看起来完全坏了。您使用服务器上生成的公钥加密数据,然后使用客户端上生成的私钥对其进行解密。这将永远不会奏效,因为密钥对不会匹配。

为了进一步混淆问题,似乎你试图在服务器和客户端之间发送密钥,但这看起来也很糟糕,例如。

ByteBuffer bb = ByteBuffer.allocate(4);
bb.putInt(priKey.getEncoded().length);
client.getOutputStream().write(bb.array());
client.getOutputStream().write(pubKey.getEncoded()); // pubKey?!!

你应该完全重新考虑你的设计。私钥通常不是集中生成的,而是向外分发的。相反,请考虑让您的客户端生成密钥对并将其公钥发送到服务器,服务器可以使用它来加密数据。

答案 1 :(得分:3)

您忽略了InputStream.read()方法的返回值。

您正在使用ObjectOutputStream在服务器端编写编码值,并使用InputStreamReader在客户端读取它。

最终结果是servPubKeyBytescipherTextFromServer包含一些垃圾,当你将它们传递给密码时,它可以预测会中断。

首先,通过测试客户端是否正确读取所有数据来修复您的I / O代码。

其次,检查加密和解密代码是否可以在同一个应用程序中使用它,而不需要任何I / O.

第三,将工作I / O与工作的enc / dec代码结合起来。