2

I've tried to gather all possible information about Encryption/Decryption from here. Tinkered with it, some success and failures.
But now I've applied the code and its hit and a miss too. Some files (exe or msi's) are working but they still gives error about BadPaddingException. Moreover, some other media files like (mp4, mkv etc) are stuck at 99% and doesn't go beyond that, although they r fully received (just some minor bytes difference but Size on Disk always matches).

I just want some help to get rid of these both problems. The files r transfering from 1 PC to another via socket programming.
Server: (Edited)

    DataInputStream dis = new DataInputStream(msock.getInputStream());
    DataOutputStream dos = new DataOutputStream(msock.getOutputStream());

    String file2dl = dis.readLine(); //2
    File file = new File(sharedDirectory.toString() + "\\" + file2dl);
    dos.writeLong(file.length()); //3+

    //Get file name without extension.
    String fileName = Files.getNameWithoutExtension(file2dl);

    //AES-128 bit key initialization.
    byte[] keyvalue = "AES128BitPasswd".getBytes();
    SecretKey key = new SecretKeySpec(keyvalue, "AES");

    //Initialize the Cipher.
    Cipher encCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    encCipher.init(Cipher.ENCRYPT_MODE, key);

    //Get the IV from cipher.
    IvParameterSpec spec = null;
    try {
        spec = encCipher.getParameters().getParameterSpec(IvParameterSpec.class);
    } catch (InvalidParameterSpecException ex) {
        Logger.getLogger(PeersController.class.getName()).log(Level.SEVERE, null, ex);
    }

    byte[] iv = spec.getIV();

    dos.write(iv, 0, iv.length);
    File tempDir = new File(tempDirectory.toString());
    //Encryption Mechanism.
        try (FileInputStream fis = new FileInputStream(file)) {
            try (CipherOutputStream cos = new CipherOutputStream(dos, encCipher);
                    FileInputStream stream = new FileInputStream(tempDir + "\\" + fileName + ".encr")) {
                int read, r;
                byte[] buffer = new byte[1024 * 1024];
                while ((read = fis.read(buffer)) != -1) {
                    cos.write(buffer, 0, read);
                }
        }
    }
 }


Client:

 long len;
 int count = 0;
 int dflag = 0;
 String size;
 dos.writeBytes("Download\r\n"); //1+
 dos.writeBytes(filename + "\r\n"); //2+
 System.out.println("File to fetch: -> " + filename);
 len = dis.readLong(); //3
 System.out.println("Size of file: -> " + len);

//Get file name without Extension.
String fileName = Files.getNameWithoutExtension(filename);

//Get Initialization Vector from Encryption Cypher.
byte[] iv = new byte[16];
int j = dis.read(iv, 0, iv.length);

final File encrypted = new File(sharedDirectory.toString() + "\\" + fileName + ".encr");
final File decrypted = new File(sharedDirectory.toString() + "\\" + filename);
try (FileOutputStream fos = new FileOutputStream(encrypted)) {
    byte[] b = new byte[1024 * 1024];
    while (fetching) {
        int r = dis.read(b, 0, b.length); //4
        count = count + r;
        double p = (double) count / len;
        double per = new BigDecimal(p).setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue();
        fos.write(b, 0, r);
        System.out.println("Size Appending: -> " + count);
        System.out.println("Percentage: ->" + per);
        Platform.runLater(() -> {
            pBar.setProgress(per);
        });
        if (count >= len) {
         dflag = 1;
         break;
        }
    }
}


If encrypted data is fully received

if(dflag == 1) {
     //AES-128 bit key initialization.
     System.out.println("File completely received");
     byte[] keyvalue = "AES128PeerBuLLet".getBytes();
     Key key = new SecretKeySpec(keyvalue, "AES");

     //Initialization Vector initialized
     IvParameterSpec ivParameterSpec = null;

     //Cipher Initialization.
     Cipher decCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      try {
           decCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
     } catch (InvalidAlgorithmParameterException ex) {
                        Logger.getLogger(PeersController.class.getName()).log(Level.SEVERE, null, ex);
      }
      System.out.println(decCipher.getProvider().getInfo());

      //Decryption Mechanism.
      try (FileOutputStream stream = new FileOutputStream(decrypted)) {
             try (FileInputStream fis = new FileInputStream(encrypted)) {
                    try (CipherInputStream cis = new CipherInputStream(fis, decCipher)) {
                           int read, i = 0;
                           byte[] buffer = new byte[(1024 * 1024) + 16];
                           while ((read = cis.read(buffer)) != -1) {
                                    stream.write(buffer, 0, read);
                                    i = i + read;
                                    double d = (double) i / len;
                                    double progress = new BigDecimal(d).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue();
                                    Platform.runLater(() -> {
                                        pBar.setProgress(progress);
                                        progressText.setText("Decrypting..");
                                    });
                                }
                            } catch (Exception e) {
                                System.out.println(e.getMessage());
                            }
                        }
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
        }

Any input is highly appreciated. Thank You.

Edit 1: Added the link to the sizes of encrypted and decrypted file received via stream. Dropbox Link

Edit 2: So finally the problem is solved with the help of three members who participated in helping me to the fullest. I was reviewing other solutions to my problem and I encounter with this solution which helped me to think deep bout the actual scenario happening in the background. Thanks to Artjom B. for his referral solution and @zaph & @jtahlborn for clearing my false assumptions bout padding and Input/Output streams.

Community
  • 1
  • 1
androizer
  • 77
  • 9
  • If you are using CBC mode never return a padding error to the user, if you do the an attacker can most likely obtain the unencrypted data without the encryption key. – zaph Feb 26 '16 at 21:56
  • 1
    I don't know what you mean by *stuck at 99%*, but can you check how the original and decrypted file differ? – Artjom B. Feb 26 '16 at 21:57
  • @ArtjomB. Stuck at 99 means that the decrypted file being download never goes past the 99%. Means, it never enters the `if(count >= len)` where len is the total size of the file by using `file.length();`. Other files like (exe, msi) totally works and even decrypt (fully working), but they gave error bout BadPaddingException in the console. – androizer Feb 26 '16 at 22:14
  • @zaph I didn't get what u saying. Sorry, but can u be pls more clear bout what I made mistake in the code. I'm sorry, but em actually noob in crypto. :p – androizer Feb 26 '16 at 22:17
  • How do you know is the decrypted data is correct? Do you check the padding to determine if there is an error. What do you do if there is an error? Or DuckDuckGo "padding oracle". – zaph Feb 26 '16 at 22:34
  • @zaph I didn't check the data explicitly, but opened the file and its executing (so i thought its fully functional). And the thing about checking the padding, I don't know how to do that.! – androizer Feb 26 '16 at 22:47
  • @zaph Thank you for faq(s). I wasn't aware of those actually. – androizer Feb 28 '16 at 17:31

2 Answers2

2

When using padding, PKCS#5 or PKCS#7, the encrypted output will be larger, up to and including one block size. See PKCS#7. The padding is removed after decryption.

The encrypted data will be longer so that must be accounted for. How depends on how the output is being handled. If it is going to a pre-allocated area such as a memory buffer the buffer must be allocated one block-size (16-bytes for AES) larger. If streaming usually just make sure all encrypted bytes are sent, n=it just the length of the input. All of this is per-implementation and system/language dependent.

The padding bytes are dynamically created by the encryption method so the input does not need to be altered. This assumes that the encryption method is adding the padding and the decryption method is removing the padding.

Example 1: If you have 1024-bytes of data the encrypted output will be 1040-bytes. On decryption the input data will be 1040-bytes and the output decrypted data will be 1024-bytes.

Example 2: If you have 1020-bytes of data the encrypted output will be 1024-bytes. On decryption the input data will be 1024-bytes and the output decrypted data will be 1020-bytes.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • I totally understand what you're referring to, and by example, it make me more clear bout the padding concept. But isn't padding meant to cover the block size data if leftover, like exactly in the 2nd eg. Why to pad the data with extra 16 bytes when the data is already the multiple of block size (16 bytes). ? And `If it is going to a pre-allocated area such as a memory buffer the buffer must be allocated one block-size (16-bytes for AES) larger`, I couldn't get it tho. – androizer Feb 27 '16 at 15:57
  • How will you know if there is padding or not? The only way is to always add it. If you had data that was an exact block size multiple and the last byte was 0x01 how would know if there was one byte of padding or the last byte was part of the data? The answer is to always add padding. Or one could just trust that the creators of PKCS#5 and PKCS#7 padding knew what they were doing. In any event it is a common error to remove the last block of full padding. – zaph Feb 27 '16 at 16:22
  • Okay. Now this makes more sense since decrypting is going to `remove the padding`. But if we take the consideration of Eg 1, then how could decrypt cipher get to know that last 4 padded bytes r actually padded and not the part of actual data.? Does the extra 4 byte padded data have some specific notations which decrypt cipher is going to know about it and will discard it (that its not the part of actual data, its just the padded region to match with the multiple of block size).! – androizer Feb 27 '16 at 17:16
  • Both the encryption and decryption have to agree to add padding. The same as agreeing on the key, encryption algorithm, mode and the way the iv is handled. – zaph Feb 27 '16 at 18:36
1

You can't use FileInputStream to read a file which you are currently writing. It's not built to read an in-progress file.

It looks like you are trying to write the encrypted stream to dos (you don't include it's definition in the server code). If so, you should be using that as the underlying stream for the CipherOutputStream.

Likewise, in the client, you try the same thing. If you want to write the file to disk first, then write the entire file to disk, then decrypt. if you want to stream decrypt, then wrap the CipherInputStream around the socket InputStream (presumably dis?).

Additionally, you don't show where you get len from on the client, but i'm assuming it is the length of the original data? if so, then your progress computation is incorrect because the length of the encrypted data will usually be different from the length of the original data.

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
  • You sure? cuz its partially working. I mean, see the updated OP for Dropbox link, and see the (exe) is correctly working. If u have another way out to save time for first encrypting and then sending kinda way, can u pls share it? – androizer Feb 26 '16 at 22:33
  • 1
    @androizer - you are arguing that it is correct because it partially works? i can give you a lot of partially working solutions which are not correct... – jtahlborn Feb 26 '16 at 22:34
  • I thought the encrypted and original data are of same size. And moreover, I've verified them (see DB link). How can I wrap CIS? Any snippet or eg is appreciated! Thank You. – androizer Feb 26 '16 at 23:00
  • @androizer - in the server, use `CipherOutputStream(dos)`. on the client use `CipherInputStream(dis)`. – jtahlborn Feb 26 '16 at 23:33
  • 1
    When using padding, PKCS#5 or PKCS#7 the output will be larger, up to and including one block size. See [PKCS#7](https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7). The padding is removed after decryption. – zaph Feb 27 '16 at 04:33
  • @jtahlborn I agreed with that CipherOutputStream(dos) to be perfect applicable for sending the cipher stream direct via dos. So it means, that I won't have to use fos stream for the encrypted file to send it via dos? Am I right? – androizer Feb 27 '16 at 10:50
  • @zaph So it means if my buffer size while encrypting is 1024, then a 16 byte greater buffer is required while decrypting the same data i.e 1040 byte buffer? – androizer Feb 27 '16 at 10:54
  • @androizer - i'm not sure i understand what you are asking about padding? yes, an encrypted file is usually bigger than the original (because of padding and other stuff). however, if you are referring to the buffers used to copy the data, the underlying encryption block size is irrelevant (you can use whatever size you need in order to copy the streams). – jtahlborn Feb 27 '16 at 15:03
  • @jtahlborn If buffer used is irrelevant for the encryption block size (16 bytes for AES), then how bout the buffer used when decrypting the encrypted file received over from stream (dos/dis)? – androizer Feb 27 '16 at 16:06
  • @androizer - neither one matters. all that is handled for you inside the Cipher Stream implementations. the buffers you are creating are merely for shuffling the data around. – jtahlborn Feb 28 '16 at 02:56
  • @jtahlborn I investigated into the encrypted data and the size difference is exactly what zaph told me. If the size ain't multiple of block size, padding is done to it hence making it integral multiple of block size and discarding it while decrypting. And if the length of data is already the multiple of block size, 1 extra block size is padded into the encrypted data and hence discarded while decrypting. This makes perfect sense acc. to zaph that padding will always occur whether size is to take into consideration or not. So the maximum padded data size in any case is 16 bytes. – androizer Feb 28 '16 at 09:12
  • @androizer - yes, i understand how encryption padding works. – jtahlborn Feb 28 '16 at 22:18