Reputation: 874
I need to convert encrypted image stored in byte[]
to BufferedImage
. I used the following code:
InputStream in = new ByteArrayInputStream(imageInByte);
BufferedImage bImageFromConvert = ImageIO.read(in);
But bImageFromConvert is null. I googled and found the reason that its because the image is encrypted ImageIO can't read it. The solutions suggested in stack overflow is to decrypt the image and then convert. But it is not applicable in my case because i need to embed a message in this encrypted image and for that iam using the code below:
public class EmbedMessage {
//BufferedImage sourceImage,embeddedImage;
public static BufferedImage embedMsg(BufferedImage sourceImage,String mess) {
//String mess = "hai";
BufferedImage embeddedImage = sourceImage.getSubimage(0,0,
sourceImage.getWidth(),sourceImage.getHeight());
embedMessage(embeddedImage, mess);
return embeddedImage;
}
private static void embedMessage(BufferedImage img, String mess) {
int messageLength = mess.length();
int imageWidth = img.getWidth(), imageHeight = img.getHeight(),
imageSize = imageWidth * imageHeight;
if(messageLength * 8 + 32 > imageSize) {
return;
}
embedInteger(img, messageLength, 0, 0);
byte b[] = mess.getBytes();
for(int i=0; i<b.length; i++)
embedByte(img, b[i], i*8+32, 0);
}
private static void embedInteger(BufferedImage img, int n, int start, int storageBit) {
int maxX = img.getWidth(), maxY = img.getHeight(),
startX = start/maxY, startY = start - startX*maxY, count=0;
for(int i=startX; i<maxX && count<32; i++) {
for(int j=startY; j<maxY && count<32; j++) {
int rgb = img.getRGB(i, j), bit = getBitValue(n, count);
rgb = setBitValue(rgb, storageBit, bit);
img.setRGB(i, j, rgb);
count++;
}
}
}
private static void embedByte(BufferedImage img, byte b, int start, int storageBit) {
int maxX = img.getWidth(), maxY = img.getHeight(),
startX = start/maxY, startY = start - startX*maxY, count=0;
for(int i=startX; i<maxX && count<8; i++) {
for(int j=startY; j<maxY && count<8; j++) {
int rgb = img.getRGB(i, j), bit = getBitValue(b, count);
rgb = setBitValue(rgb, storageBit, bit);
img.setRGB(i, j, rgb);
count++;
}
}
}
private static int getBitValue(int n, int location) {
int v = n & (int) Math.round(Math.pow(2, location));
return v==0?0:1;
}
private static int setBitValue(int n, int location, int bit) {
int toggle = (int) Math.pow(2, location), bv = getBitValue(n, location);
if(bv == bit)
return n;
if(bv == 0 && bit == 1)
n |= toggle;
else if(bv == 1 && bit == 0)
n ^= toggle;
return n;
}
}
Please suggest me a way to convert Encrypted image in byte array
to BufferedImage
Upvotes: 1
Views: 2489
Reputation: 874
Found a solution atlast. Thanks to http://blog.philippheckel.com/2013/03/02/java-encode-any-byte-array-stream-or-file-into-a-24-bit-bitmap-bmp/.
The encrypted image is not openable because its header was also getting encrypted. So in order to open it without decrypting, we have to either encrypt only the portion except header or encrypt the image fully and attach another header so that it becomes a new image.Using the class BitmapEncoder I have attached or converted the encrypted byte array back to image as follows:
File uploadedFile = new File(filePath);
item.write(uploadedFile);
//Convert uploaded image to byte[] for encryption
bFile = new byte[(int) uploadedFile.length()];
try {
FileInputStream fileInputStream = new FileInputStream(uploadedFile);
//convert file into array of bytes
fileInputStream.read(bFile);
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
//encrypt the byte array
Cipher cipher=Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
enFile=cipher.doFinal(bFile);
//save encrypted image to database
image.setImage(enFile);
//save this row to database
UploadService ups=new UploadService();
imgid=ups.uploadImage(image);
//Attach bitmap header to the encrypted byte array so that encrypted array can be still recognized as an image and save it to disk
BitmapEncoder.encodeToBitmap(enFile, new File(uploadFolder + File.separator +"encrypted"+fileName));
Hope this helps someone else.
Iam pasting the full BitmapEncoder.java to avoid broken link problems:
/*
* BitmapEncoder, encodes any byte array, stream or file into a 24-bit bitmap (BMP).
* Copyright (C) 2013 Philipp C. Heckel <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
/**
* Encodes any byte array, stream or file into a 24-bit bitmap (BMP).
*
* The <code>encodeToBitmap()</code>-methods can be used to transform any binary data
* into a rectangular image and stored in the widely supported 24-bit bitmap
* format. The method does not hide, encrypt or compress the source data in any
* way. It merely prepends a bitmap header and transforms the payload as
* specified by the bitmap file format.
*
* <p>The <code>decodeFromBitmap()</code>-methods retrieve the orginal payload from a
* previously encoded bitmap. A bitmap not encoded with this class cannot be read.
*
* <p><b>Example:</b>
*
* <pre>
* // Encode file "/etc/hosts" into file "/tmp/hosts.bmp", and restore it to "/tmp/hosts-restored"
* BitmapEncoder.encodeToBitmap(new File("/etc/hosts"), new File("/tmp/hosts.bmp"));
* BitmapEncoder.decodeFromBitmap(new File("/tmp/hosts.bmp"), new File("/tmp/hosts-restored"));
*
* // Encode 3 bytes into a bitmap file
* BitmapEncoder.encodeToBitmap(new byte[] { 0x01, 0x02, 0x03 }, new File("/tmp/3- bytes.bmp"));
* byte[] threebytes = BitmapEncoder.decodeFromBitmap(new File("/tmp/3-bytes.bmp"));
* </pre>
*
* @author Philipp C. Heckel <[email protected]>
* @see http://blog.philippheckel.com/2013/03/02/java-encode-any-byte-array-stream-or-file-into-a-24-bit-bitmap-bmp/
*/
public class BitmapEncoder {
private static final int BMP_SIZE_HEADER= 54; // total header length, 54 bytes
private static final int BMP_SIZE_IMAGE_WIDTH = 4; // size of image width field, 4 bytes
private static final int BMP_SIZE_PAYLOAD_LENGTH = 4; // size of 'horizontal resolution' field, here: payload length, 4 bytes
private static final int BMP_SIZE_BMPUTIL_MAGIC = 4; // size of 'vertical resolution' field, here: payload length, 4 bytes
private static final int BMP_OFFSET_FILESIZE_BYTES = 2; // offset of filesize field, 4 bytes
private static final int BMP_OFFSET_IMAGE_WIDTH = 18; // offset of image width field, 4 bytes
private static final int BMP_OFFSET_IMAGE_HEIGHT = 22; // offset of image height field, 4 bytes
private static final int BMP_OFFSET_IMAGE_DATA_BYTES = 34; // 4 bytes
private static final int BMP_OFFSET_PAYLOAD_LENGTH = 38; // 4 bytes
private static final int BMP_OFFSET_BMPUTIL_MAGIC = 42; // 4 bytes
private static final byte UDEF = 0; // undefined value in bitmap header, to be overwritten by methods
/**
* 24-bit bitmap header
* @see http://www.fastgraph.com/help/bmp_header_format.html
*/
private static final byte[] BMP_HEADER = new byte[] {
/* 00 */ 0x42, 0x4d, // signature, "BM"
/* 02 */ UDEF, UDEF, UDEF, UDEF, // size in bytes, filled dynamically
/* 06 */ 0x00, 0x00, // reserved, must be zero
/* 08 */ 0x00, 0x00, // reserved, must be zero
/* 10 */ 0x36, 0x00, 0x00, 0x00, // offset to start of image data in bytes
/* 14 */ 0x28, 0x00, 0x00, 0x00, // size of BITMAPINFOHEADER structure, must be 40 (0x28)
/* 18 */ UDEF, UDEF, UDEF, UDEF, // image width in pixels, filled dynamically
/* 22 */ UDEF, UDEF, UDEF, UDEF, // image height in pixels, filled dynamically
/* 26 */ 0x01, 0x00, // number of planes, must be 1
/* 28 */ 0x18, 0x00, // number of bits per pixel (1, 4, 8, or 24) -> 24 = 0x18
/* 30 */ 0x00, 0x00, 0x00, 0x00, // compression type (0=none, 1=RLE-8, 2=RLE-4)
/* 34 */ UDEF, UDEF, UDEF, UDEF, // size of image data in bytes (including padding)
/* 38 */ UDEF, UDEF, UDEF, UDEF, // normally: horizontal resolution in pixels per meter (unreliable)
// --> HERE: used to indicate the payload length
/* 42 */ UDEF, UDEF, UDEF, UDEF, // vertical resolution in pixels per meter (unreliable)
// --> HERE: used to mark file as encoded bitmap, see BMPUTIL_MAGIC
/* 46 */ 0x00, 0x00, 0x00, 0x00, // number of colors in image, or zero
/* 50 */ 0x00, 0x00, 0x00, 0x00, // number of important colors, or zero
};
/**
* Magic value to mark files created by this class
*/
private static final byte[] BMPUTIL_MAGIC = new byte[] {
0x30, 0x32, 0x30, 0x35 // mark used in 'vertical resolution' field to mark bmp file, "0205"
};
/**
* Encodes any <code>File</code> into a 24-bit bitmap (BMP) and outputs the image
* to another <code>File</code>.
*
* <p>The method does not hide, encrypt or compress the source data in any
* way. It merely prepends a bitmap header and transforms the payload as
* specified by the bitmap file format.
*
* @param srcFile File to be read pointing to the input data (payload)
* @param destFile File to write the 24-bit bitmap file to
* @throws IOException Thrown if the input/output stream cannot be read/written
*/
public static void encodeToBitmap(File srcFile, File destFile) throws IOException {
encodeToBitmap(new FileInputStream(srcFile), srcFile.length(), new FileOutputStream(destFile));
}
/**
* Encodes any <code>File</code> into a 24-bit bitmap (BMP) and outputs the image
* to another <code>File</code>.
*
* <p>The method does not hide, encrypt or compress the source data in any
* way. It merely prepends a bitmap header and transforms the payload as
* specified by the bitmap file format.
*
* @param srcBytes Byte array of input data (payload)
* @param destStream Stream to write the 24-bit bitmap file to
* @throws IOException Thrown if the input/output stream cannot be read/written
*/
public static void encodeToBitmap(byte[] srcBytes, File destFile) throws IOException {
encodeToBitmap(new ByteArrayInputStream(srcBytes), srcBytes.length, new FileOutputStream(destFile));
}
/**
* Encodes a byte array into a 24-bit bitmap (BMP) and outputs the image
* to an <code>OutputStream</code>.
*
* <p>The method does not hide, encrypt or compress the source data in any
* way. It merely prepends a bitmap header and transforms the payload as
* specified by the bitmap file format.
*
* @param srcBytes Byte array of input data (payload)
* @param destStream Stream to write the 24-bit bitmap file to
* @throws IOException Thrown if the input/output stream cannot be read/written
*/
public static void encodeToBitmap(byte[] srcBytes, OutputStream destStream) throws IOException {
encodeToBitmap(new ByteArrayInputStream(srcBytes), srcBytes.length, destStream);
}
/**
* Encodes an <code>InputStream</code> into a 24-bit bitmap (BMP) and outputs the image
* as byte array.
*
* <p>The method does not hide, encrypt or compress the source data in any
* way. It merely prepends a bitmap header and transforms the payload as
* specified by the bitmap file format.
*
* @param srcStream Stream of input data (payload)
* @param srcStreamLength Length of the input data stream (in bytes)
* @param destStream Stream to write the 24-bit bitmap file to
* @throws IOException Thrown if the input/output stream cannot be read/written
*/
public static void encodeToBitmap(InputStream srcStream, long srcStreamLength, OutputStream destStream) throws IOException {
if (srcStreamLength > Integer.MAX_VALUE) {
throw new IOException("File too big; max. "+Integer.MAX_VALUE+" bytes supported.");
}
// CALCULATE HEADER FIELDS
int imageWidth = (int) Math.ceil(Math.sqrt((double) srcStreamLength / 3)); / / Image width, sqrt(payload/3), divided by 3 because of RGB
int imageHeight = (int) Math.ceil((double) srcStreamLength // Image height, payload / image width / 3
/ (double) imageWidth / 3);
int rowPadding = 4 - (imageWidth*3 % 4); // Padding at row end, row length must be divisible by 4
int filesizeBytes = imageWidth*imageHeight*3 + imageHeight*rowPadding // Total bitmap size, RGB-data + padding-data + header
+ BMP_SIZE_HEADER;
int imageBytesWithPadding = filesizeBytes - BMP_SIZE_HEADER; // Image bytes without header (incl. padding), required field in header
int payloadPadding = (int) (imageWidth*imageHeight*3 - srcStreamLength); // Padding at the end of the image, to make it rectangular
// System.out.println("payloadLength = "+srcStreamLength);
// System.out.println("imageWidth = sqrt(payloadLength/3) = "+imageWidth);
// System.out.println("imageHeight = payloadLength / imageWidth / 3 = "+imageHeight);
// System.out.println("rowPadding = "+rowPadding);
// System.out.println("filesizeBytes = imageWidth*imageHeight*3 + imageHeight*rowPadding + BMP_SIZE_HEADER = "+filesizeBytes);
// System.out.println("imageBytesWithPadding = filesizeBytes - BMP_SIZE_HEADER = "+imageBytesWithPadding);
// System.out.println("payloadPadding = imageWidth*imageHeight*3 - payloadLength = "+payloadPadding);
// WRITE FIELDS TO HEADER
byte[] header = BMP_HEADER.clone(); // Clone bitmap header template, and overwrite with fields
writeIntLE(header, BMP_OFFSET_FILESIZE_BYTES, filesizeBytes);
writeIntLE(header, BMP_OFFSET_IMAGE_WIDTH, imageWidth);
writeIntLE(header, BMP_OFFSET_IMAGE_HEIGHT, imageHeight);
writeIntLE(header, BMP_OFFSET_IMAGE_DATA_BYTES, imageBytesWithPadding);
writeIntLE(header, BMP_OFFSET_PAYLOAD_LENGTH, (int) srcStreamLength);
System.arraycopy(BMPUTIL_MAGIC, 0, header, // Copy magic number to header
BMP_OFFSET_BMPUTIL_MAGIC, BMPUTIL_MAGIC.length);
// WRITE TO STREAM
// Add payload
destStream.write(header, 0, header.length);
// Write other lines (regular width)
byte[] row = new byte[imageWidth*3];
int read;
while ((read = srcStream.read(row)) != -1) {
destStream.write(row, 0, read); // Write payload
destStream.write(new byte[rowPadding]); // Write padding
}
// Write payload padding
destStream.write(new byte[payloadPadding]);
srcStream.close();
destStream.close();
}
/**
* Decodes a 24-bit bitmap previously encoded by this class from
* an <code>InputStream</code> to a byte array.
*
* <p>The method can only read bitmaps that were encoded using one of the
* <code>encodeToBitmap()</code> methods. It will throw an exception if
* any other bitmaps are read.
*
* @param srcStream Stream of input data (24-bit bitmap)
* @return The original data read from the bitmap (payload)
* @throws IOException Thrown if the input/output stream cannot be read/written
*/
public static byte[] decodeFromBitmap(InputStream srcStream) throws IOException {
ByteArrayOutputStream destStream = new ByteArrayOutputStream();
decodeFromBitmap(srcStream, destStream);
return destStream.toByteArray();
}
/**
* Decodes a 24-bit bitmap previously encoded by this class from
* a <code>File</code> to a byte array.
*
* <p>The method can only read bitmaps that were encoded using one of the
* <code>encodeToBitmap()</code> methods. It will throw an exception if
* any other bitmaps are read.
*
* @param srcFile File to be read pointing to the input data (24-bit bitmap)
* @return The original data read from the bitmap (payload)
* @throws IOException Thrown if the input/output stream cannot be read/written
*/
public static byte[] decodeFromBitmap(File srcFile) throws IOException {
ByteArrayOutputStream destStream = new ByteArrayOutputStream();
decodeFromBitmap(new FileInputStream(srcFile), destStream);
return destStream.toByteArray();
}
/**
* Decodes a 24-bit bitmap previously encoded by this class from
* a <code>File</code> to another <code>File</code>.
*
* <p>The method can only read bitmaps that were encoded using one of the
* <code>encodeToBitmap()</code> methods. It will throw an exception if
* any other bitmaps are read.
*
* @param srcFile File to be read pointing to the input data (24-bit bitmap)
* @param destFile File to be written the original data to (payload)
* @throws IOException Thrown if the input/output stream cannot be read/written
*/
public static void decodeFromBitmap(File srcFile, File destFile) throws IOException {
decodeFromBitmap(new FileInputStream(srcFile), new FileOutputStream(destFile));
}
/**
* Decodes a 24-bit bitmap previously encoded by this class from
* an <code>InputStream</code> to an <code>OutputStream</code>.
*
* <p>The method can only read bitmaps that were encoded using one of the
* <code>encodeToBitmap()</code> methods. It will throw an exception if
* any other bitmaps are read.
*
* @param srcStream Stream to be read pointing to the input data (24-bit bitmap)
* @param destStream Stream to be written the original data to (payload)
* @throws IOException Thrown if the input/output stream cannot be read/written
*/
public static void decodeFromBitmap(InputStream srcStream, OutputStream destStream) throws IOException {
// READ HEADER
long bytesRead = 0;
// Skip ahead & read 'image width' field
bytesRead += srcStream.skip(BMP_OFFSET_IMAGE_WIDTH - bytesRead); // Stores the image width, needed for row length and
byte[] imageWidthBytes = new byte[BMP_SIZE_IMAGE_WIDTH]; // row padding calculation
bytesRead += srcStream.read(imageWidthBytes);
int imageWidth = toIntLE(imageWidthBytes);
// Skip ahead & read 'horizontal resolution' field
bytesRead += srcStream.skip(BMP_OFFSET_PAYLOAD_LENGTH - bytesRead); // Stores the payload length, needed for read-loop below
byte[] payloadLengthBytes = new byte[BMP_SIZE_PAYLOAD_LENGTH];
bytesRead += srcStream.read(payloadLengthBytes);
int payloadLength = toIntLE(payloadLengthBytes);
// Skip ahead & read 'vertical resolution' field
bytesRead += srcStream.skip(BMP_OFFSET_BMPUTIL_MAGIC - bytesRead); // Stores the magic field, needed to check if the image was
byte[] magicFieldBytes = new byte[BMP_SIZE_BMPUTIL_MAGIC]; // previously encoded using this class
bytesRead += srcStream.read(magicFieldBytes);
if (!Arrays.equals(BMPUTIL_MAGIC, magicFieldBytes)) {
throw new IOException("Given bitmap does not contain encoded binary data.");
}
// Skip ahead to the actual payload
bytesRead += srcStream.skip(BMP_SIZE_HEADER - bytesRead); // Skip the rest of the header
// READ PAYLOAD
if (payloadLength > 0) {
int rowPaddingLength = 4 - (imageWidth*3 % 4); // Padding at row end, row length must be divisible by 4
int rowLength = imageWidth*3; // Row width is 3x the image width (RGB coding)
// System.out.println("payloadLength = "+payloadLength);
// System.out.println("imageWidth = "+imageWidth);
// System.out.println("rowPaddingLength = "+rowPaddingLength);
// System.out.println("rowLength = "+rowLength);
byte[] row = new byte[rowLength];
int read;
int restOfPayload = payloadLength;
while ((read = srcStream.read(row)) != -1) {
if (restOfPayload >= read) {
destStream.write(row, 0, read); // Write to output stream
srcStream.skip(rowPaddingLength); // Skip bitmap padding (if any)
restOfPayload -= read;
}
else {
destStream.write(row, 0, restOfPayload); // Write the last bytes
break;
}
}
}
srcStream.close();
destStream.close();
}
/**
* Write an integer to a byte array (as little endian) at a
* specific offset.
*/
private static void writeIntLE(byte[] bytes, int startoffset, int value) {
bytes[startoffset] = (byte)(value);
bytes[startoffset+1] = (byte)(value >>> 8);
bytes[startoffset+2] = (byte)(value >>> 16);
bytes[startoffset+3] = (byte)(value >>> 24);
}
/**
* Read an integer value from a 4-byte array (as little endian).
*/
private static int toIntLE(byte[] value) {
return ((value[3] & 0xff) << 24) |
((value[2] & 0xff) << 16) |
((value[1] & 0xff) << 8) |
(value[0] & 0xff);
}
/**
* Encode/decode a bitmap from the command line.
*
* <p><b>Syntax:</b><br />
* <tt>BitmapEncoder encode SRCFILE DESTFILE.bmp<br />
* <tt>BitmapEncoder decode SRCFILE.bmp DESTFILE
*/
public static void main(String[] args) throws IOException {
if (args.length >= 2 && "encode".equals(args[0])) {
BitmapEncoder.encodeToBitmap(new File(args[1]), new File(args[2]));
}
else if (args.length >= 2 && "decode".equals(args[0])) {
BitmapEncoder.decodeFromBitmap(new File(args[1]), new File(args[2]));
}
else {
System.out.println("Usage: BitmapEncoder encode SRCFILE DESTFILE.bmp");
System.out.println(" BitmapEncoder decode SRCFILE.bmp DESTFILE");
}
}
}
Upvotes: 2