0

I see this "Input length must be multiple of 16 when decrypting with padded cipher" error when I run the program

RealEchoServer.java

import java.io.*;
import java.net.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class RealEchoServer {

public static void main(String[] args) {
    int i = 1;
    try {
        ServerSocket s = new ServerSocket(9003);

        for (;;) {
            Socket incoming = s.accept();
            System.out.println("Spawning " + i);
            new RealEchoHandler(incoming, i).start();
            i++;
        }
    } catch (Exception e) {
        System.out.println(e);
    }
 }
}

class RealEchoHandler extends Thread {

DataInputStream in;
DataOutputStream out;
private Socket incoming;
private int counter;

public RealEchoHandler(Socket i, int c) {
    incoming = i;
    counter = c;
}

public void run() {
    try {

        String key1 = "1234567812345678";
        byte[] key2 = key1.getBytes();
        SecretKeySpec secret = new SecretKeySpec(key2, "AES");
        String msg = "Singapore Malaysia Japan India Indonesia HongKong Taiwan China England";
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        byte[] encrypted = cipher.doFinal(msg.getBytes());

        in = new DataInputStream(incoming.getInputStream());
        out = new DataOutputStream(incoming.getOutputStream());

        boolean done = false;
        String str = "";
        out.writeUTF("Connected!\n");
        out.flush();
        while (!done) {
            out.writeUTF(">");
            out.flush();
            str = in.readUTF();
            System.out.println(in + ":" + str);
            if (str == null) {
                done = true;
            } else {
                System.out.println("Sending Ciphertext : " + new String(encrypted));
                out.writeUTF(new String(encrypted));
                out.flush();
            }
        }
        incoming.close();
    } catch (Exception e) {
        System.out.println(e);
    }
 }
}

RealSocketTest.java

   import java.io.*;
   import java.net.*;
   import java.security.*;
   import javax.crypto.*;
   import javax.crypto.spec.*;
   import java.util.*;

 class RealSocketTest {
 public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

    String str = "";
    String str2 = "";
    DataOutputStream out;
    DataInputStream in;

    try {
        Socket t = new Socket("127.0.0.1", 9003);
        in = new DataInputStream(t.getInputStream());
        out = new DataOutputStream(t.getOutputStream());
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        boolean more = true;
        System.out.println(in.readUTF());

        while (more) {
            str = in.readUTF();
            System.out.print(str);
            str2 = br.readLine();
            out.writeUTF(str2);
            out.flush();
            str = in.readUTF();

            System.out.println("Encrypted Info: " + str);

            try {

                String key1 = "1234567812345678";
                byte[] key2 = key1.getBytes();
                SecretKeySpec secret = new SecretKeySpec(key2, "AES");
                Cipher cipher = Cipher.getInstance("AES");
                cipher.init(Cipher.DECRYPT_MODE, secret);
                byte[] decrypted = cipher.doFinal(str.getBytes());
                System.out.println("Decrypted Info: " + new String(decrypted));
            } catch (BadPaddingException e) {
                System.out.println("Wrong Key!");
            } catch (InvalidKeyException f) {
                System.out.println("Invalid Key!");
            }
        }
    } catch (IOException e) {
        System.out.println("Error");
    }
  }
}

I've read a similar problem here Illegal Block Size Exception Input length must be multiple of 16 when decrypting with padded cipher , but I don't understand how I could change mine, because it looks very different from mine.

So what should be added/changed to get it decrypted?

Community
  • 1
  • 1

1 Answers1

1

The problem is that ciphertexts may contain every possible byte value. Many byte values on the other hand are not printable and therefore not a valid UTF-8 encoding. When you make a String out of it new String(encrypted), it will silently drop some bytes and you won't be able to successfully decrypt the ciphertext.

Two possible fixes:

  • Encode the ciphertext as Base64 or Hex to be used in a text-based protocol.
  • Use the DataOutputStream::write() method to make it a binary protocol without encoding it.

Other security stuff:

  • Always specify the complete String for the expected Cipher instance. Different providers might have different defaults and it may happen that client and server don't use the same method. Example: AES/ECB/PKCS5Padding.

  • Never use ECB mode. It is not semantically secure. At least use CBC with a random IV (prepend the IV in front of the ciphertex or write it to the stream in order).

  • Check your ciphertext for manipulation. This is easily done, by employing an authenticated mode like GCM (AES/GCM/NoPadding with GCMParameters). If you don't want that, then at least try to implement an encrypt-then-MAC scheme where you HMAC the ciphertext (with a different key) and check it before decryption.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • that really made sense, and the security stuff was deep(good). i'm just a starter , i probably got the code from the internet so could you post the code precisely for either of the possible fixes ? i appreciate it very much – pavanleawesome Apr 20 '15 at 20:13
  • 1
    Here are [some ways](http://stackoverflow.com/questions/13109588/base64-encoding-in-java) you can use Base64 encoders. Encode `encrypted` before writing it to the socket and decode `str = in.readUTF();` before passing it to `cipher.doFinal()`. I'm not really sure whether this will fix all your problems, because the protocol seems strange. – Artjom B. Apr 20 '15 at 20:38