将文件内容隐藏到png图片中

来源:互联网 发布:finebi商业智能软件 编辑:程序博客网 时间:2024/05/22 08:21

要想更好的实现该功能,你可以阅读将文件内容隐藏到bmp图片中

要实现这个功能,你得了解png文件的格式,详情:http://www.w3.org/TR/PNG/

实现原理:
png文件格式包括固定的文件头部+ 必要的数据块和辅助数据块
每一个数据块包括四个字节的数据块数据长度,4个字节的类型码,可变长度的数据块数据,4个字节的CRC
其中的IEND数据块为最后一块数据块,且默认情况下是不存储数据的,除非人为加入,我们就可以将自己的数据
写入到IEND的数据块数据区域


隐藏实现思路:
1、解析png文件格式(为了验证后期的数据的正确性,也为了学习png格式)
2、获取隐藏文件的大小
3、修改IEND数据块的数据长度信息
4、写入png文件的其它信息,直到IEND数据块的数据区域
5、写入隐藏文件的内容
6、写入IEND数据块的CRC信息(该处记录要隐藏文件的大小,方便恢复数据时使用)


恢复隐藏数据实现思路:
1、定位到png格式中IEND数据块的CRC,获取其值
2、根据获取到的值,定位到写入隐藏文件数据的开始位置
3、读取隐藏文件的数据,直到遇到IEND数据块的CRC


下面给出关键代码:

package com.pan.utils;import java.io.BufferedInputStream;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 com.pan.entity.CommonBlock;import com.pan.entity.DataBlock;import com.pan.entity.Png;import com.pan.entity.PngHeader;import com.pan.factory.BlockFactory;/** * @author yp2 * @date 2015-11-19 * @decription 隐藏文件内容到png格式图片中 */public class PngUtil {/** * 读取指定png文件的信息 * @param pngFileName * @return * @throws IOException  */private static Png readPng(String pngFileName) throws IOException {Png png = new Png();File pngFile = new File(pngFileName);InputStream pngIn = null;//记录输入流读取位置(字节为单位)long pos = 0;try {pngIn = new FileInputStream(pngFile);//读取头部信息PngHeader pngHeader = new PngHeader();pngIn.read(pngHeader.getFlag());png.setPngHeader(pngHeader);pos += pngHeader.getFlag().length;while(pos < pngFile.length()) {DataBlock realDataBlock = null;//读取数据块DataBlock dataBlock = new CommonBlock();//先读取长度,4个字节pngIn.read(dataBlock.getLength());pos += dataBlock.getLength().length;//再读取类型码,4个字节pngIn.read(dataBlock.getChunkTypeCode());pos += dataBlock.getChunkTypeCode().length;//如果有数据再读取数据//读取数据realDataBlock = BlockFactory.readBlock(pngIn, png, dataBlock);pos += ByteUtil.highByteToInt(dataBlock.getLength());//读取crc,4个字节pngIn.read(realDataBlock.getCrc());//添加读取到的数据块png.getDataBlocks().add(realDataBlock);pos += realDataBlock.getCrc().length;dataBlock = null;}} catch (IOException e) {e.printStackTrace();throw e;} finally {try {if(pngIn != null) {pngIn.close();}} catch (IOException e) {e.printStackTrace();throw e;}}return png;}/** * 将读取到的文件信息写入到指定png的文件中,并指定输出文件 * @param pngPng信息对象 * @param pngFileNamepng文件名 * @param inputFileName要隐藏的文件名 * @param outFileName输出文件名,内容包括png数据和要隐藏文件的信息 * @throws IOException  */private static void wirteFileToPng(Png png, String pngFileName, String inputFileName, String outFileName) throws IOException {File pngFile = new File(pngFileName);File inputFile = new File(inputFileName);File outFile = new File(outFileName);InputStream pngIn = null;InputStream inputIn = null;OutputStream out = null;int len = -1;byte[] buf = new byte[1024];try {if(!outFile.exists()) {outFile.createNewFile();}pngIn = new FileInputStream(pngFile);inputIn = new FileInputStream(inputFile);out = new FileOutputStream(outFile);//获取最后一个数据块,即IEND数据块DataBlock iendBlock = png.getDataBlocks().get(png.getDataBlocks().size() - 1);//修改IEND数据块数据长度:原来的长度+要隐藏文件的长度long iendLength = ByteUtil.highByteToLong(iendBlock.getLength());iendLength += inputFile.length();iendBlock.setLength(ByteUtil.longToHighByte(iendLength, iendBlock.getLength().length));//修改IEND crc信息:保存隐藏文件的大小(字节),方便后面读取png时找到文件内容的位置,并读取iendBlock.setCrc(ByteUtil.longToHighByte(inputFile.length(), iendBlock.getCrc().length));//写入文件头部信息out.write(png.getPngHeader().getFlag());//写入数据块信息String hexCode = null;for(int i = 0; i < png.getDataBlocks().size(); i++) {DataBlock dataBlock = png.getDataBlocks().get(i);hexCode = ByteUtil.byteToHex(dataBlock.getChunkTypeCode(), 0, dataBlock.getChunkTypeCode().length);hexCode = hexCode.toUpperCase();out.write(dataBlock.getLength());out.write(dataBlock.getChunkTypeCode());//写数据块数据if(BlockUtil.isIEND(hexCode)) {//写原来IEND数据块的数据if(dataBlock.getData() != null) {out.write(dataBlock.getData());}//如果是IEND数据块,那么将文件内容写入IEND数据块的数据中去len = -1;while((len = inputIn.read(buf)) > 0) {out.write(buf, 0, len);}} else {out.write(dataBlock.getData());}out.write(dataBlock.getCrc());}} catch (Exception e) {e.printStackTrace();throw e;} finally {try {if(pngIn != null) {pngIn.close();}if(inputIn != null) {inputIn.close();}if(out != null) {out.close();}} catch (IOException e) {e.printStackTrace();throw e;}}}/** * 将指定的文件信息写入到png文件中,并输出到指定的文件中 * @param pngFileNamepng文件名 * @param inputFileName要隐藏的文件名 * @param outFileName输出文件名 * @throws IOException  */public static void writeFileToPng(String pngFileName, String inputFileName, String outFileName) throws IOException {Png png = readPng(pngFileName);wirteFileToPng(png, pngFileName, inputFileName, outFileName);}/** * 读取png文件中存储的信息,并写入到指定指定输出文件中 * @param pngFileNamepng文件名 * @param outFileName指定输出文件名 * @throws IOException  */public static void readFileFromPng(String pngFileName, String outFileName) throws IOException {File pngFile = new File(pngFileName);File outFile = new File(outFileName);InputStream pngIn = null;OutputStream out = null;//记录输入流读取位置long pos = 0;int len = -1;byte[] buf = new byte[1024];try {if(!outFile.exists()) {outFile.createNewFile();}pngIn = new BufferedInputStream(new FileInputStream(pngFile));out = new FileOutputStream(outFile);DataBlock dataBlock = new CommonBlock();//获取crc的长度信息,因为不能写死,所以额外获取一下int crcLength = dataBlock.getCrc().length;byte[] fileLengthByte = new byte[crcLength];pngIn.mark(0);//定位到IEND数据块的crc信息位置,因为写入的时候我们往crc写入的是隐藏文件的大小信息pngIn.skip(pngFile.length() - crcLength);//读取crc信息pngIn.read(fileLengthByte);//获取到隐藏文件的大小(字节)int fileLength = ByteUtil.highByteToInt(fileLengthByte);//重新定位到开始部分 pngIn.reset();//定位到隐藏文件的第一个字节pngIn.skip(pngFile.length() - fileLength - crcLength);pos = pngFile.length() - fileLength - crcLength;//读取隐藏文件数据while((len = pngIn.read(buf)) > 0) {if( (pos + len) > (pngFile.length() - crcLength) ) {out.write(buf, 0, (int) (pngFile.length() - crcLength - pos));break;} else {out.write(buf, 0, len);}pos += len;}} catch (IOException e) {e.printStackTrace();throw e;} finally {try {if(pngIn != null) {pngIn.close();}if(out != null) {out.close();}} catch (IOException e) {e.printStackTrace();throw e;}}}public static void main(String[] args) throws IOException {String filePath = PngUtil.class.getClassLoader().getResource("resource/sound_wav.png").getPath();Png png = readPng(filePath);wirteFileToPng(png, filePath, PngUtil.class.getClassLoader().getResource("resource/").getPath() + "screct.txt",PngUtil.class.getClassLoader().getResource("resource/").getPath() + "sound_wavout.png");readFileFromPng(PngUtil.class.getClassLoader().getResource("resource/").getPath() + "sound_wavout.png",PngUtil.class.getClassLoader().getResource("resource/").getPath() + "sound_wavscrect.txt");System.out.println(ByteUtil.byteToHexforPrint(png.getPngHeader().getFlag(), 0, png.getPngHeader().getFlag().length));for(DataBlock dataBlock : png.getDataBlocks()) {System.out.println(ByteUtil.byteToHexforPrint(dataBlock.getLength(), 0, dataBlock.getLength().length));System.out.println(ByteUtil.byteToHexforPrint(dataBlock.getChunkTypeCode(), 0, dataBlock.getChunkTypeCode().length));if(dataBlock.getData() != null) {System.out.println(ByteUtil.byteToHexforPrint(dataBlock.getData(), 0, dataBlock.getData().length));}System.out.println(ByteUtil.byteToHexforPrint(dataBlock.getCrc(), 0, dataBlock.getCrc().length));}System.out.println();}}


package com.pan.factory;import java.io.IOException;import java.io.InputStream;import com.pan.entity.DataBlock;import com.pan.entity.IDATBlock;import com.pan.entity.IENDBlock;import com.pan.entity.IHDRBlock;import com.pan.entity.PHYSBlock;import com.pan.entity.PLTEBlock;import com.pan.entity.Png;import com.pan.entity.SRGBBlock;import com.pan.entity.TEXTBlock;import com.pan.entity.TRNSBlock;import com.pan.utils.BlockUtil;import com.pan.utils.ByteUtil;/** * @author yp2 * @date 2015-11-19 * @description 数据块工厂 */public class BlockFactory {/** * 读取输入流中的数据块的数据 * @param in 输入流 * @param pngpng对象 * @param dataBlock数据块 * @return具体细节的数据块 * @throws IOException */public static DataBlock readBlock(InputStream in, Png png, DataBlock dataBlock) throws IOException {String hexCode = ByteUtil.byteToHex(dataBlock.getChunkTypeCode(), 0, dataBlock.getChunkTypeCode().length);hexCode = hexCode.toUpperCase();DataBlock realDataBlock = null;if(BlockUtil.isIHDR(hexCode)) {//IHDR数据块realDataBlock = new IHDRBlock();} else if(BlockUtil.isPLTE(hexCode)) {//PLTE数据块realDataBlock = new PLTEBlock();} else if(BlockUtil.isIDAT(hexCode)) {//IDAT数据块realDataBlock = new IDATBlock();} else if(BlockUtil.isIEND(hexCode)) {//IEND数据块realDataBlock = new IENDBlock();} else if(BlockUtil.isSRGB(hexCode)) {//sRGB数据块realDataBlock = new SRGBBlock();} else if(BlockUtil.istEXt(hexCode)) {//tEXt数据块realDataBlock = new TEXTBlock();} else if(BlockUtil.isPHYS(hexCode)) {//pHYs数据块realDataBlock = new PHYSBlock();} else if(BlockUtil.istRNS(hexCode)) {//tRNS数据块realDataBlock = new TRNSBlock();} else {//其它数据块realDataBlock = dataBlock;}realDataBlock.setLength(dataBlock.getLength());realDataBlock.setChunkTypeCode(dataBlock.getChunkTypeCode());//读取数据,这里的测试版做法是: 把所有数据读取进内存来int len = -1;byte[] data = new byte[8096];len = in.read(data, 0, ByteUtil.highByteToInt(dataBlock.getLength()));realDataBlock.setData(ByteUtil.cutByte(data, 0, len));return realDataBlock;}}

源码下载:源码


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝上发的快递没有了怎么办 天猫购物半个月不发货怎么办 京东网同一产品购买多规格的怎么办 天猫商城购买的家具要退换货怎么办 亚马逊美国站会员日前没销量怎么办 淘宝买的鞋子把脚磨坏了怎么办 拼多多下单 没货 没法发货怎么办 闲鱼退货物流弄坏了卖家拒收怎么办 客户说物流太慢了 要退货怎么办 京东退货物流系统不更新怎么办 把货交给物流但是物流丢货了怎么办 货还在物流就申请退款怎么办 荣耀4x返回键不管用怎么办 华为手机关不了机也开不了机怎么办 荣耀畅玩5x手机密码忘了怎么办 华为短信验证码失败其他错误怎么办 红米3x手机卡顿反应慢怎么办 华为手机不停的自动重启怎么办 华为荣耀4x卡在开机界面怎么办 华为荣耀8青春版密码忘了怎么办 华为手机内存满了开不起来怎么办 华为荣耀畅玩平板2比较卡怎么办 红米4x太卡了怎么办 魅蓝e移动卡只有2g网络怎么办 联通关闭2g网络双卡手机怎么办 小米6x手机插耳机音量大怎么办 音量键和亮度键盘拆了怎么办 荣耀v10锁屏密码忘了怎么办 荣耀9锁屏密码忘记了怎么办 华为荣耀v9锁屏密码忘记了怎么办 荣耀手机密码忘了怎么办数字锁 华为畅享5忘了开机密码怎么办 华为p9连接热点忘了密码怎么办 苹果手机用联通卡信号不好怎么办 魅族手机充电口松了怎么办 华为手机刷机失败开不了机怎么办 华为刷机失败开不了机怎么办 银行卡信息被盗密码被改该怎么办 全民k歌手机话筒有杂音怎么办 手机刷机清除数据需要密码怎么办 oppo手机屏锁密码忘了怎么办