0

I am having a problem in storing exact size of encrypted message inside an image.

What I want to do is that I want to encrypt a message with AES cipher and then I want to hide each byte of message in a random pixel by replacing only one component(either red or green or blue component) of pixel with the message byte.

I have done that but I am unable to store with the same size of ciphertext.

First I have done encryption of message with the help of a key using AES/CBC/PKCS5Padding using Java Cryptographic Extension.

byte[] cipherText = encrypt(message, keyBytes);

Then I had to convert cipherText to UTF-8 byte array and then store inside image. If not I will not be able to decrypt it later. If I don't encode properly, I cannot decrypt it after extraction. The problem here is that the size of messageBytes > size of cipherText. It takes more space than the encrypted message i.e cipherText. Example: if cipherText is 32 bytes, I get 148 bytes of messageBytes.

But I want to store the byte array of size cipherText and be able to extract it later.

String encryptedMessage = Arrays.toString(cipherText);
byte[] messageBytes = encryptedMessage.getBytes(StandardCharsets.UTF_8);

Now 148 bytes are stored in the image. Then I have stored each byte inside one of the component(r or g or b) of random pixel.

for(int i=0; i<messageBytes.length; i++) {
    storeInsidePixel(image, messageBytes[i], secureRandom)
}

In the extraction part, I retrieved the messageBytes back and converted to encryptedMessage to get the hidden cipherText.

String encryptedMessage = new String(byteArray, StandardCharsets.UTF_8);
String newString = encryptedMessage.substring(1, encryptedMessage.length()-1);
String[] stringArray = newString.split(", ");

byte[] cipherText = new byte[stringArray.length];
for(int i=0; i<stringArray.length; i++) {
    cipherText[i] = (byte) (Integer.parseInt(stringArray[i]));
}

Then I decrypted it using the same cipher and key.

String output = decrypt(cipherText, keyBytes);

I want to store the byte array of size cipherText and be able to extract it with same size and decrypt it later.

If I don't encode to utf-8 and store it, I don't get the same output because if the cipherText stored in image is 1040 bytes and later extract it, I will get the same byte values but the size of byte array is just 205 bytes and decryption fails.

Is there any to store exactly 1040 bytes of cipherText in image and extract the same 1040 bytes of cipherText and be able to decrypt it? Is there any way to get it like that?

  • Try `System.out.println(encryptedMessage)` and see what you get – Tim Moore Dec 27 '21 at 10:37
  • @TimMoore With that I will just get byte[] in string format. I have checked everything. Without encoding, I will not be able to decrypt it later. But by encoding, the size needed to store increases – Vinay Kumar P.V. Dec 27 '21 at 10:41
  • 1
    I'll be honest, I don't exactly understand what is going on. You use a lot of words and show very little code, and worse is you only show us function calls that we can't even know whether they are internally to blame. What I don't get is why `decrypt(encrypt(message, keyBytes), keyBytes)` doesn't reconstruct the message properly. And in the second to last paragraph you say something about hiding a secret of certain length and not extracting it all back? – Reti43 Dec 27 '21 at 10:53
  • @Reti43 Just leave those parts. If I put the codes you asked you will get more confused. The remaining part doesn't even relate to the question. The code shows what is happening and there is no need of other code. Which part of code you didn't understand? `decrypt(encrypt(message, keyBytes), keyBytes)` This part doesn't work if both operations are needed to be done independently. You need encoding to decrypt later. I want to store the cipherText inside an image and not encoded one, then be able to extract and decrypt it. – Vinay Kumar P.V. Dec 27 '21 at 11:04
  • If `decrypt(encrypt(input))` does not give back `input`, then `decode()` is not the inverse of `encode()` and the naming convention is misleading. But if `decrypt(encrypt(encode(input)))` works, then embed `cipherText` as your secret and after you extract it you can encode it and then decrypt it. By the way, I don't want you to add unncessary code. I only need to see the [necessary code](https://stackoverflow.com/help/minimal-reproducible-example) that generate your error, but to be honest I don't even get at which part in your flow the problem is. – Reti43 Dec 27 '21 at 11:12
  • @Reti43 Please read the question before commenting. The code works 100%. No errors. The question is that "Is there any way to store data inside image of size same as cipherText size and be able to decrypt after extraction?" Because encoding increases size and without encoding, I cannot decrypt from the extracted output. – Vinay Kumar P.V. Dec 27 '21 at 11:16
  • Then we don't need to see ANY of that. Your question seems to be "how can I embed a certain amount of bytes in a cover image and then be able to know how many there are so I can extract them later"? Is this one correct? – Reti43 Dec 27 '21 at 11:29

1 Answers1

1

Then I had to convert cipherText to UTF-8 byte array and then store inside image. If not I will not be able to decrypt it later. If I don't encode properly, I cannot decrypt it after extraction.

No, here is where you go wrong. Your cipherText should be binary already (as shown by byte[] cipherText = encrypt(message, keyBytes);) , and image formats are binary as well. So you should never have to encode it again. Just directly use cipherText.

The problem here is that the size of messageBytes > size of cipherText. It takes more space than the encrypted message i.e cipherText. Example: if cipherText is 32 bytes, I get 148 bytes of messageBytes. ... String encryptedMessage = Arrays.toString(cipherText);

Yeah, well, duh. If you do Arrays.toString then you will get a textual representation of the array, including separators and whatnot, all which you can do without.

If I don't encode to utf-8 and store it, I don't get the same output because if the cipherText stored in image is 1040 bytes and later extract it, I will get the same byte values but the size of byte array is just 205 bytes and decryption fails.

We cannot see why you get a smaller value back here, but this is what needs to be solved - and it should not be solved by encoding / decoding. Feel free to post a follow-up question with a minimal reproducible example.

Is there any to store exactly 1040 bytes of cipherText in image and extract the same 1040 bytes of cipherText and be able to decrypt it? Is there any way to get it like that?

There are two problems here: length indication -> you need some way to indicate the size of the ciphertext. Furthermore, if you use the same key for multiple messages then you would need to store the IV as well.

For detecting the size: you can e.g. prefix a 16 bit (short) length indicator if your ciphertext doesn't exceed 65536 bytes. Or, for CBC, you could use it to count the number of blocks as the unpadding will then reveal the exact size. This would let you store 16 times larger ciphertext.

With a 2 byte size indicator and 16 byte IV you'd grow your ciphertext by 18 bytes. Since your ciphertext is already up to 16 bytes larger than the plaintext this may be a small enough size increase for you to handle.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • I am using SecureRandom to generate IV and AES key by giving the key given by the user as seed. – Vinay Kumar P.V. Dec 27 '21 at 11:23
  • 1
    That's very bad practice, as you generally don't know the internals of the secure random number generator. It can (and has) failed, see all the issues with `getRawKey` in Java and Android, e.g. on this site. Besides that, key / IV pairs should not be reused, so the problem now shifts to reusing the same seed. You could use a password based key derivation function such as PBKDF2 instead, but in that case you need to store a salt (at the very minimum), assuming that you allow reuse of the password. – Maarten Bodewes Dec 27 '21 at 11:34
  • Thanks. You are right and you are also right about this - "We cannot see why you get a smaller value back here, but this is what needs to be solved - and it should not be solved by encoding / decoding." I am getting result for smaller messages. But when I go for messages like 1 kb, the extraction fails. Thanks @MaartenBodewes – Vinay Kumar P.V. Dec 27 '21 at 11:44
  • I'm currently downvoting all answers that indicate that you can use strings to store binary data that do not have a specified encoding - and upvoting those that indicate base64 and hexadecimal encoding. If questions are *protected* or indicated as *highly active* then you have to be very sure of your solution before you decide to answer (this goes for me as well of course). It's a bit strange that you've decided to answer while you are still having issues with the encoding / decoding as displayed in this question (I didn't downvote this question, by the way). – Maarten Bodewes Dec 27 '21 at 15:04
  • Not that question. This one [https://stackoverflow.com/a/70486387/17364272] – Vinay Kumar P.V. Dec 27 '21 at 15:08