向BMP圖片中隱藏一段文字並保存,從保存的圖片中提取文字.
Java代碼:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Enumeration;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.FontUIResource;
/**
*
* @ClassName: 信息隱藏_LSB技術_於BMP位圖
* @Description: 基於24位BMP圖片,運用LSB技術對文本信息進行隱藏
* @author:
* @date: 2018年4月23日 下午8:11:52
*/
public class 信息隱藏_LSB技術_於BMP位圖 extends JFrame{
private static final long serialVersionUID = 1L;
int map[][];//保存像素顏色的數組
MyPanel center;//繪圖面板
File selectFile;//讀取的文件
int width;//圖像寬度
int height;//圖像高度
byte temp1[];//讀取BMP文件的前18個字節信息
byte temp2[];//讀取BMP文件的後28個字節信息
JScrollPane scrollpane;//滾動面板
JTextArea infoJt;//信息文本區
JMenuItem open;
JMenuItem save;
JMenuItem hide;
JMenuItem show;
int disWidth=1000;
int disHeight=700;
public 信息隱藏_LSB技術_於BMP位圖() {
setUIFont(new FontUIResource("微軟雅黑", 0, 20));
this.setLayout(new BorderLayout());//最好先設置佈局再添加組件
//初始化畫圖面板
center=new MyPanel();
center.setBackground(Color.WHITE);
center.setBackground(Color.GRAY);
// center.setPreferredSize(new Dimension(200, 300));
scrollpane=new JScrollPane(center);//用center初始化滾動面板
// scrollpane.setPreferredSize(new Dimension(200,100));
infoJt=new JTextArea();
infoJt.setLineWrap(true);
infoJt.setWrapStyleWord(true);
JScrollPane infoJs=new JScrollPane(infoJt);
infoJs.setPreferredSize(new Dimension(disWidth, 300));
MyListener lis=new MyListener();
//初始化菜單欄
JMenuBar menuBar=new JMenuBar();
JMenu fileMenu=new JMenu("file");
open=new JMenuItem("open");
save=new JMenuItem("save");
JMenu LSBMenu=new JMenu("LSB");
hide=new JMenuItem("hide");
show=new JMenuItem("show");
open.addActionListener(lis);
save.addActionListener(lis);
hide.addActionListener(lis);
show.addActionListener(lis);
fileMenu.add(open);
fileMenu.add(save);
menuBar.add(fileMenu);
LSBMenu.add(hide);
LSBMenu.add(show);
menuBar.add(LSBMenu);
this.setJMenuBar(menuBar);
this.add(scrollpane,BorderLayout.CENTER);//加入的是滾動面板
this.add(infoJs,BorderLayout.SOUTH);
this.setTitle("LSB隱藏器");
this.setSize(disWidth, disHeight);
this.setLocationRelativeTo(null);//設置窗體出現在屏幕中間
this.setDefaultCloseOperation(3);
this.setVisible(true);
}
/**
* 讀取bmp文件
*/
public void readBMP()
{
try {
FileInputStream fis=new FileInputStream(selectFile);
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] wb=new byte[4];//讀取寬度的字節數組
byte[] hb=new byte[4];//讀取高度的字節數組
temp1=new byte[18];
bis.read(temp1);//bis.skip(18);//跳過前18個byte
bis.read(wb);//讀取寬度
bis.read(hb);//讀取高度
width=byteToint(wb);
height=byteToint(hb);
map=new int[height][width];
int skip=4-width*3%4;//得到每行要跳過的數字(與windows 系統機制有關)
temp2=new byte[28];
bis.read(temp2);//bis.skip(28);
if(temp1[10]==55)//偏移量爲55,此位圖已經隱入了信息數據
bis.read();//跳過1字節
for(int i=height-1;i>0;i--)
{
for(int j=0;j<width;j++)
{
int blue=bis.read();
int green=bis.read();
int red=bis.read();
Color c=new Color(red,green,blue);
map[i][j]=c.getRGB();
}
if(skip!=4)
bis.skip(skip);
}
bis.close();
center.setPreferredSize(new Dimension(width,height));
javax.swing.SwingUtilities.updateComponentTreeUI(center);//這裏必須要用updateComponentTreeUI(center)函數
//不能用repaint()函數
} catch (Exception e) {
e.printStackTrace();
}
}
public void writeBMP()
{
try {
FileOutputStream fos=new FileOutputStream(selectFile);
BufferedOutputStream bos=new BufferedOutputStream(fos);
bos.write(temp1);//
bos.write(intTobyte(width,4));//寫入寬度
bos.write(intTobyte(height,4));//寫入高度
bos.write(temp2);
int skip=0;
if(width*3/4!=0)
{
skip=4-width*3%4;
}
if(temp1[10]==55)
bos.write(new byte[1]);
for(int i=height-1;i>=0;i--)
{
for(int j=0;j<width;j++)
{
Color c=new Color(map[i][j]);
int blue=c.getBlue();
int green=c.getGreen();
int red=c.getRed();
bos.write(blue);
bos.write(green);
bos.write(red);
}
if(skip!=0)
bos.write(new byte[skip]);
}
bos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//字節轉int
public static int byteToint(byte b[])
{
int t1=(b[3]&0xff)<<24;
int t2=(b[2]&0xff)<<16;
int t3=(b[1]&0xff)<<8;
int t4=b[0]&0xff;
//System.out.println(b[1]&0xff);//輸出的是一個整形數據
//在java中,設計int和比int位數來的小的類型b,如byte,char等,都是先把小類型擴展成int再來運算,
//return( t1<<24)+(t2<<16)+(t3<<8)+t4;//必須加括號
return t1+t2+t3+t4;
}
//int 轉byte
public static byte[] intTobyte(int a,int len)
{
byte []t=new byte[len];
t[0]=(byte) ((a&0xff));
if(len>1)
t[1]=(byte)((a&0xff00)>>8);
if(len>2)
t[2]=(byte)((a&0xff0000)>>16);
if(len>3)
t[3]=(byte)((a&0xff000000)>>24);
return t;
}
private static void setUIFont(javax.swing.plaf.FontUIResource f) {
Enumeration<Object> keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get(key);
if (value instanceof javax.swing.plaf.FontUIResource) {
UIManager.put(key, f);
}
}
}
/**
* LSB處理
* @author
* @date 2018年4月22日
*/
class LSB{
public boolean hide() {
temp1[10]+=1;//將位圖數據偏移量增1,作爲是否隱入數據的標識,此時temp1[10]=55;
String info=infoJt.getText();
char[] infoChs=info.toCharArray();
int index=0,endIndex=0;
String str;
byte[] infoBins=new byte[infoChs.length*16];
char[] wordBins=new char[16];
for(int i=0;i<infoChs.length;i++) {
str=intToWordBinaryString((int)infoChs[i]);
str=new StringBuilder(str).reverse().toString();
wordBins=str.toCharArray();
for(int j=0;j<16;j++) {
infoBins[index++]=(byte)(wordBins[j]-'0');
}
}
index=0;
for(int i=height-1;i>0;i--)
{
for(int j=0;j<width;j++)
{
if(index<infoBins.length) {
map[i][j]=map[i][j]&0xfffffffe;
if(infoBins[index]==1) {
map[i][j]+=1;
}
index++;
}
else
{
map[i][j]=map[i][j]&0xfffffffe;//結束標記,一個字的最低位爲0
endIndex++;
if(endIndex>=16)
return true;
}
}
}
return false;
}
public String show() {
String info="";
byte[] wordBins=new byte[16];
int index=0;
String wordStr="";
int wordInt;
outer:for(int i=height-1;i>=0;i--) {
for(int j=0;j<width;j++) {
wordBins[index++]=(byte)(map[i][j]&0x00000001);
if(index>=16) {
for(int k=15;k>=0;k--) {
wordStr+=wordBins[k];
}
wordInt=Integer.parseInt(wordStr,2);
if(wordInt!=0) {
info+=(char)wordInt;
}else {
break outer;
}
index=0;wordStr="";
}
}
}
return info;
}
private String intToWordBinaryString(int i) {
StringBuilder sb = new StringBuilder(Integer.toBinaryString(i));
while (sb.length() < 16) {
sb.insert(0, "0");
}
return sb.toString();
}
}
/**
* 繪圖面板
*
*
*/
class MyPanel extends JPanel{
private static final long serialVersionUID = 1L;
public void paint(Graphics g) {
super.paint(g);
if(map!=null)
{
for(int i=0;i<map.length;i++)
{
for(int j=0;j<map[i].length;j++)
{
g.setColor(new Color(map[i][j]));
g.drawLine(j+200, i, j+200, i);
}
}
}
}
}
class MyListener implements ActionListener{
JFileChooser fileChosser;
MyListener()
{
FileNameExtensionFilter filter=new FileNameExtensionFilter("24位位圖(*.bmp)", "bmp");
fileChosser=new JFileChooser();
fileChosser.setFileFilter(filter);
fileChosser.setCurrentDirectory(new File("\\"));
}
public void actionPerformed(ActionEvent e) {
JMenuItem jmi = (JMenuItem) e.getSource();
if(jmi==open)//選擇的是打開
{
int choose=fileChosser.showOpenDialog(null);
if(choose==JFileChooser.APPROVE_OPTION)//點擊的是確定按鈕
{
selectFile=fileChosser.getSelectedFile();
readBMP();
infoJt.setText("圖片打開成功!\n");
if(temp1[10]==54) {
infoJt.append("最多可嵌入"+height*width/8+"個字節的信息!\n");
}else if(temp1[10]==55) {
infoJt.append("此圖片已經隱入有信息!");
}
}
}
else if(jmi==save)//選擇的是保存
{
int choose=fileChosser.showSaveDialog(null);
if(choose==JFileChooser.APPROVE_OPTION)
{
selectFile=fileChosser.getSelectedFile();
writeBMP();
infoJt.setText("保存成功!");
}
}
else if(jmi==hide) {
if(new LSB().hide()) {
infoJt.setText("信息隱入成功!");
}else{
infoJt.setText("信息隱入出現不確定問題!");
}
}
else if(jmi==show) {
String info=new LSB().show();
infoJt.setText("隱入的信息內容:\n");
infoJt.append(info);
}
}
}
public static void main(String[] args) {
new 信息隱藏_LSB技術_於BMP位圖();
}
}
效果圖:
程序操作步驟:
file-->open,LSB-->hide,file-->save,file-->open,LSB-->show.