在没有解密的情况下,将byte []中的加密图像转换为java中的BufferedImage

时间:2014-02-21 06:56:41

标签: java encryption

我需要将byte[]中存储的加密图像转换为BufferedImage。我使用了以下代码:

InputStream in = new ByteArrayInputStream(imageInByte);
BufferedImage bImageFromConvert = ImageIO.read(in);

但是bImageFromConvert为null。我用谷歌搜索并找到了它的原因,因为图像是加密的,ImageIO无法读取它。堆栈溢出中建议的解决方案是解密图像然后转换。但它不适用于我的情况,因为我需要在这个加密的图像中嵌入一条消息,并使用下面的代码进行搜索:

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;
        }
    }

请为我推荐一种方法,将byte array中的加密图片转换为BufferedImage

1 个答案:

答案 0 :(得分:2)

找到解决方案。感谢http://blog.philippheckel.com/2013/03/02/java-encode-any-byte-array-stream-or-file-into-a-24-bit-bitmap-bmp/

加密图像无法打开,因为其标题也已加密。因此,为了在不解密的情况下打开它,我们必须加密除头部之外的部分或完全加密图像并附加另一个头部以使其成为新图像。使用类BitmapEncoder我已经附加或转换了加密的字节数组如下图像:

 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));

希望这有助于其他人。

我粘贴完整的BitmapEncoder.java以避免链路问题:

    /*
  * BitmapEncoder, encodes any byte array, stream or file into a 24-bit bitmap (BMP).
  * Copyright (C) 2013 Philipp C. Heckel <philipp.heckel@gmail.com> 
  *
  * 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 <philipp.heckel@gmail.com>
   * @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");
                }
                    }
                    }