Han
Han

Reputation: 397

In AES decryption, "Given final block not properly padded" occurred

I'm doing a simple encryption file transfer system and now stopped by a run time exception:

Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
    at javax.crypto.Cipher.doFinal(Cipher.java:2087)
    at ftpclient.FTPClient.main(FTPClient.java:82)

I tried to debug my code using a string to encrypt and decrypt with the same key and it works. However, when I tried to transfer stream from the file, this exception always comes.

Here are the codes of both sides. At first they will exchange symmetric key (AES key) via RSA and then transfer large files via AES encryption. We can focus on the last part of each code where the files are encrypted and decrypted by AES key.

Server Side:

package ftpserver;

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

import org.apache.commons.codec.binary.Base64; 
/**
 *
 * @author Han
 */
public class FTPServer {
    public static void main(String[] args) throws Exception {

        //generate symmetric key and initialize cipher for AES
        SecretKey skey = null;
        Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");

        KeyGenerator kg = KeyGenerator.getInstance("AES");
        kg.init(128);
        skey = kg.generateKey();

        //get public key of the receive side
        final String PUBLIC_KEY_PATH = "key_b.public";
        PublicKey publickey = null;
        try {
        FileInputStream fis;
        fis = new FileInputStream(PUBLIC_KEY_PATH);
        ObjectInputStream oin = new ObjectInputStream(fis);
        publickey = (PublicKey) oin.readObject();
        oin.close();
        } catch (FileNotFoundException e) {
        e.printStackTrace();
        } catch (IOException e) {
        e.printStackTrace();
        } catch (ClassNotFoundException e) {
        e.printStackTrace();
        };

        //encrypte symmetric key with own private key and send out
        Cipher rsa = Cipher.getInstance("RSA");
        rsa.init(Cipher.ENCRYPT_MODE, publickey);
        byte cipherSKey[] = rsa.doFinal(skey.getEncoded());
        //System.out.println(skey); //debug

    //create tcp server socket
        ServerSocket tcp = new ServerSocket(2000);
        Socket client = tcp.accept();

        //get input&output stream from the TCP connection
        InputStream in = client.getInputStream();
    OutputStream out = client.getOutputStream();

        //generate a file input stream to get stream from file
    File sentFile = new File("F:\\test.txt");
        FileInputStream fin = new FileInputStream(sentFile);

        //send encrypted symmetric key first
        out.write("Symmetric Key:\r\n".getBytes());
        out.write(cipherSKey);

        DataInputStream din = new DataInputStream(in);
        while(true)
        {
            if(din.readLine().equals("Received."))
            {
                System.out.println("Send key successfully.");
                break;
            }

        };

        //send files
    int count;
        byte[] bytearray = new byte[8192];
        byte[] cipherbuffer;
        while((count = fin.read(bytearray))>0)
    {
        cipherbuffer = Base64.encodeBase64(aes.doFinal(bytearray));
        out.write(cipherbuffer,0,cipherbuffer.length);
        System.out.println(count+" bytes have been sent.");
        };

        out.flush();
        out.close();

        client.close();


        }
}

Client Side:

package ftpclient;

import java.io.*;
import java.net.*;
import java.security.PrivateKey;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
/**
 *
 * @author Han
 */
public class FTPClient {

public static void main(String[] args) throws Exception 
    {
            //get the private key of this side


        final String PUBLIC_KEY_PATH = "key_b.privat";
        PrivateKey privatkey = null;
        try {
        FileInputStream fis;
        fis = new FileInputStream(PUBLIC_KEY_PATH);
        ObjectInputStream oin = new ObjectInputStream(fis);
        privatkey = (PrivateKey) oin.readObject();
        oin.close();
        } catch (FileNotFoundException e) {
        e.printStackTrace();
        } catch (IOException e) {
        e.printStackTrace();
        } catch (ClassNotFoundException e) {
        e.printStackTrace();
        };

        Cipher rsa = Cipher.getInstance("RSA");
        rsa.init(Cipher.DECRYPT_MODE, privatkey);

        //create tcp client socket
        Socket tcp = new Socket("192.168.1.185",2000);        
    InputStream in = tcp.getInputStream();
        OutputStream out = tcp.getOutputStream();
        DataInputStream din = new DataInputStream(in);

        //receive symmetric key from server
        byte keybuffer[] = new byte[128];
        SecretKey skey = null;

        while(true)
        {
            if(din.readLine().equals("Symmetric Key:"))
            {
                System.out.println("Start to receiving key...");
                in.read(keybuffer);
                byte[] skeycode = rsa.doFinal(keybuffer);
                skey = new SecretKeySpec(skeycode, 0, skeycode.length, "AES");
                out.write("Received.\r\n".getBytes());
                break;
            }
        };
        //create cipher for symmetric decryption
        Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
        aes.init(Cipher.DECRYPT_MODE, skey);
        //System.out.println(skey); //debug

        //create file stream
        FileOutputStream fos = new FileOutputStream("E:\\test_cp.txt");

    int count;
        int i = 0;
        byte[] bytearray = new byte[8192];
        byte[] buffer;
        while((count = in.read(bytearray)) > 0)
        {
            buffer = (aes.doFinal(Base64.decodeBase64(bytearray)));
                        fos.write(buffer,0,buffer.length);   
                        i +=count;
                        System.out.println(i+" bytes have been received.");
        };


        fos.flush();
        fos.close();
        in.close();

        tcp.close();
        System.out.println("File Transfer completed");

    }

}

Upvotes: 3

Views: 16824

Answers (1)

andrew cooke
andrew cooke

Reputation: 46872

you are calling doFinal multiple times. or at least trying to.

when you read data, not all data arrives or is read into the buffer at once. so you decrypt some and then read again. that is all ok.

but when you do that, you are calling doFinal each time, instead of update. this is wrong and is the cause of the error. instead, replace doFinal with update and then add an extra doFinal once you have finished reading all data (there is a doFinal() that takes no arguments for exactly this reason).

see http://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html

also see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 for why ecb mode is often not a good idea (look at the penguin pictures).

Upvotes: 3

Related Questions