-2

I'm trying to encrypt and decrypt image in Java using DESede algorithm. My approach to do that is by getting the pixels byte from BufferedImage and encrypt them, and then set the data element of WriteableRaster from the encrypted byte, finally save it to file. With the same approach at decrypting byte, I'm getting error because when I set data element of the raster, the encrypted image still in same size/height with first plain image. These are my codes:

    public byte[] encrypt(byte[] plainByte) {
    byte[] encryptedByte = null;
    try {
        cipher.init(Cipher.ENCRYPT_MODE, key);
        encryptedByte = cipher.doFinal(plainByte);
    } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
        System.err.println(e);
        JOptionPane.showMessageDialog(null, e.getMessage());
    }
    return encryptedByte;
}

Code for decrypting byte:

    public byte[] decrypt(byte[] encryptedByte) {
    byte[] decryptedByte = null;
    try {
        cipher.init(Cipher.DECRYPT_MODE, key);
        decryptedByte = cipher.doFinal(encryptedByte);
    } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
        System.err.println(e);
        JOptionPane.showMessageDialog(null, e.getMessage());
    }
    return decryptedByte;
}

and this is my implementation at processing image:

String password = "12345";//will be hashed with MD5
    triDes.setPassword(password);
    //proses enkripsi
    BufferedImage image = ImageIO.read(new File("tes.jpg"));
    byte[] pixels = (byte[]) image.getRaster().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);
    byte[] encrypt = triDes.encrypt(pixels);
    System.out.println(encrypt.length + " - " + pixels.length);
    WritableRaster raster = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, image.getWidth(), image.getHeight(), 3, new Point(0, 0));
    raster.setDataElements(0, 0, image.getWidth(), image.getHeight(), encrypt);
    image.setData(raster);
    File outputfile = new File("enkripsi.jpg");
    ImageIO.write(image, "jpg", outputfile);

    //proses dekripsi
    image = ImageIO.read(new File("enkripsi.jpg"));
    pixels = (byte[]) image.getRaster().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);
    System.out.println(pixels.length);
    byte[] decrypt = triDes.decrypt(pixels);
    raster = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, image.getWidth(), image.getHeight(), 3, new Point(0, 0));
    raster.setDataElements(0, 0, image.getWidth(), image.getHeight(), decrypt);
    image.setData(raster);
    outputfile = new File("dekripsi.jpg");
    ImageIO.write(image, "jpg", outputfile);

I'm sorry for my bad English. On my codes the, the plain pixel bytes length and the encrypted bytes length is not in the same size. When I read encrypted image's bytes has the same length with the plain's bytes. I suspect the mistake at saving process, maybe the encrypted bytes is trimmed when BufferedImage saved. If my supposition is correct, How to setDataElements of WritableRaster without specifying witdth and height of the image?


Putra Ardiansyah
  • 5,249
  • 4
  • 25
  • 37
  • Your question isn't clear. Are you concerned because the image dimensions have not been encrypted along with the raster? You've gone to great trouble to encrypt *only* the pixels; if you want to encrypt everything, simply encrypt the entire file (use no image APIs). – erickson Sep 22 '16 at 19:59
  • I'm sorry for my bad English. On my codes the, the plain pixel bytes length and the encrypted bytes length is not in the same size. When I read encrypted image's bytes has the same length with the plain's bytes. I suspect the mistake at saving process, maybe the encrypted bytes is trimmed when BufferedImage saved. – Putra Ardiansyah Sep 22 '16 at 20:07
  • @PutraArdiansyah Which error? – Kayaman Sep 22 '16 at 20:10
  • @Kayaman The error appears when decrypting the encrypted image. _javax.crypto.BadPaddingException: Given final block not properly padded_. Because when I checked the length of encrypted bytes is not the same size with bytes of saved image – Putra Ardiansyah Sep 22 '16 at 20:14
  • As @erickson says, you need to simply encrypt a file and then decrypt it to get back the original bytes. Do any image manipulation you need either before encrypting or after decrypting. Encryption is intended to treat files as a single piece without worrying about what is inside the file. – rossum Sep 22 '16 at 20:16
  • Search for "Bad Padding Exception". There are many answers which will help you find and fix the problem. – rossum Sep 22 '16 at 20:18
  • @rossum Yes, you are right, but my purpose is visualizing the cipher of 3Des when it comes to image files. – Putra Ardiansyah Sep 22 '16 at 20:22
  • DES and 3DES should not be used for new work, they are no longer considered secure, AES is the current Advanced Encryption Standard. – zaph Sep 22 '16 at 22:56

2 Answers2

3

What you're doing is

  1. get image data
  2. encrypt image data
  3. compress encrypted image (lossy JPEG operation)
  4. decompress encrypted image
  5. try to decrypt encrypted image
  6. display image

In this case, the data after step 2 and before step 5 are actually not equal, which means that the ciphertext was corrupted on the way.

JPEG is a lossy format. You can't encrypt pixels, store the random-looking pixels in an image buffer and compress that image buffer with JPEG. This will mangle almost all the pixels and you won't be able to decrypt the image anymore, because the encryption and decryption are very precise algorithms. If even one bit is wrong a whole block (64 bit in 3DES-ECB or 3DES-CBC) will look completely different and the following blocks may be affected.

A padding error happens when the last block doesn't look as expected (the padding cannot be removed).

If you use a lossless image format such as PNG or BMP, then the answer of erickson would apply.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
2

If you simply want to perform your own version of the "ECB penguin", choose your image dimensions so that the original raster is a multiple of the cipher block size, then specify "DESede/ECB/NoPadding" as the cipher transform. This might require cropping or padding the original image.

You don't specify what cipher mode you are using, but many modes (like ECB and CBC) require the plain text length to be evenly divisible by the cipher's block size, and output a multiple of the block size. Generally the input will be padded with at least one byte, and as many more as needed to fill the final input block.

This means that your encrypted image will always be larger than your original image raster.

When you set (or save) the encrypted data as the raster, the image library trims what it considers to be excess data—all or part of the final block, and that final block is lost.

When decrypting, the cipher finds an incomplete block, or fails to find the padding in the final block, and throws an exception.

Some modes, like CTR, OFB, or CFB, can make a block cipher act like a stream cipher, encrypting one byte at a time, without the need to form complete blocks by padding. The cipher text will be the same size as the plain text—sort of.

The problem with any secure mode is that an initialization vector or nonce is required. This is a random number that ensures that a given plain text is encrypted to a different cipher text each time it is encountered. This random number needs to be stored somewhere to enable decryption.

You could store it in another file that is bundled with the image file, or perhaps there's some way to add custom data to the image meta data so that you can keep it in the same file and recover it later. I am not familiar with EXIF or the APIs in Java that might allow you to do this though.

Community
  • 1
  • 1
erickson
  • 265,237
  • 58
  • 395
  • 493
  • Thank you for answering. Perhaps add padding byte to exif is the most effective way. – Putra Ardiansyah Sep 22 '16 at 20:39
  • @PutraArdiansyah It depends on what you are trying to do. If you want to create a visual depiction of ECB's weakness, cropping the image and disabling padding is the best. If you are actually trying to encrypt images, EXIF is worth exploring. I have used [this excellent third-party library for *reading* EXIF](https://www.drewnoakes.com/code/exif/) before, but I bet it has writing capabilities too. – erickson Sep 22 '16 at 20:49