​ 趁放假把下学期的创新项目实践预判完成一点,下学期多腾出时间备考。

LSB算法

简介

​ LSB算法:全称Least Significant Bit(最低有效位),指将秘密信息嵌入到载体图像像素值的最低有效位,也称最不显著位,改变这一位置对载体图像的品质影响最小。

​ LSB属于空域算法中的一种,是将信息嵌入到图像点中像素位的最低位,以保证嵌入的信息是不可见的,但是由于使用了图像不重要的像素位,算法的鲁棒性差,水印信息很容易为滤波、图像量化、几何变形的操作破坏。

​ 本篇主要讲简单实现在图片中注入隐含信息。

算法分析

​ 以.png图片为例,每一个像素点显示的颜色由r、g、b三个元素分量的数值决定(取值范围为0~255),将其化为二进制的形式,如Color(255,102,102),不足8位的首部补0,可得到如下排列:

1
2
3
1111111 1
1110011 0
1110011 0

​ 末尾排列为100,将其置0,我们可以将二进制编码写在其中,对应需要隐藏的信息。由于最低有效位的原理,在复杂的图片中人眼基本感受不到颜色的变化,这样就实现了加密;解密只需将其取出组合,并翻译成对应的文本即可。

算法步骤

1 将原始载体图像的空域像素值由十进制转换成二进制;

2 用二进制秘密信息中的每一比特信息替换与之相对应的载体数据的最低有效位

3 将得到的含秘密信息的二进制数据转换为十进制像素值,从而获得含秘密信息的图像。

简单的java代码实现

加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class encryption {
public static BufferedImage encryptionLSB(BufferedImage secretImg, String data){
List<StringBuffer> rgbList= new ArrayList<StringBuffer>();
StringToBin stringToBin=new StringToBin();

//密文内容转二进制,并分割0和1元素
String dataBin= stringToBin.StrToBinstr(data);
char[] databinArray=dataBin.toCharArray();

int width = secretImg.getWidth(), height = secretImg.getHeight();

String r8bit=null;
String g8bit=null;
String b8bit=null;
int a,r = 0,g=0,b=0;
for (int w = 0; w < width; w++) {
for (int h = 0; h < height; h++) {
int val=secretImg.getRGB(w,h);
a = (0xff000000 & val) >>> 24;
r = (0x00ff0000 & val) >> 16;
g = (0x0000ff00 & val) >> 8;
b = (0x000000ff & val);
double power = Math.pow(2.0, 4.0);

//转换为二进制
r8bit = Integer.toBinaryString((int)(r));
g8bit = Integer.toBinaryString((int)(g));
b8bit = Integer.toBinaryString((int)(b));

//不足8位的在首部补齐0
int rDifference = 8 - r8bit.length();
int gDifference = 8 - g8bit.length();
int bDifference = 8 - b8bit.length();
for (int i = rDifference; i > 0; i--){
r8bit="0"+r8bit;}
for (int i = gDifference; i > 0; i--){
g8bit = "0"+g8bit;}
for (int i = bDifference; i > 0; i--){
b8bit = "0"+b8bit;}

//将末尾置0,便于进行编码隐藏
//StringBuffer便于修改字符串内容
StringBuffer rbuff=new StringBuffer(r8bit);
StringBuffer gbuff=new StringBuffer(g8bit);
StringBuffer bbuff=new StringBuffer(b8bit);
rbuff.setCharAt(7,'0');
gbuff.setCharAt(7,'0');
bbuff.setCharAt(7,'0');

rgbList.add(rbuff);
rgbList.add(gbuff);
rgbList.add(bbuff);
}
}
//生成加密后的rgb数组,并将其元素从二进制转换为十进制
int[] rgbArray=new int[rgbList.size()];
for(int i=0;i<rgbList.size();i++){
if(i<databinArray.length-1) {
rgbList.get(i).setCharAt(7, databinArray[i]);
}
rgbArray[i] = Integer.parseInt(rgbList.get(i).toString(), 2);
}

//生成加密图片
int i=0;
for (int w = 0; w < width; w++) {
for (int h = 0; h < height; h++) {
secretImg.setRGB(w, h,new Color(rgbArray[i],rgbArray[i+1],rgbArray[i+2]).getRGB());
i=i+3;
}
}
return secretImg;
}

public static void main(String[] args) throws IOException {
//设置原始图片路径
BufferedImage secretImg = ImageIO.read(new File("src/main/img/kitokawa.png"));
//设置密文内容
String data="这是一道密文!";
secretImg=encryptionLSB(secretImg,data);
ImageIO.write(secretImg, "png", new File("secret.png"));//在项目路径输出得到的图片
}
}

解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class decrypt {//解密
public static String decryptLSB(BufferedImage secretImg,int limit) {//防止图片尺寸过大,循环过长时间,添加限制次数默认为10000,可适度增大
int width = secretImg.getWidth(), height = secretImg.getHeight();
String dataBin = "";
String data = "";
String r8bit = null;
String g8bit = null;
String b8bit = null;
int a,x=0, r = 0, g = 0, b = 0;
for (int w = 0; w < width; w++) {
for (int h = 0; h < height; h++) {
int val = secretImg.getRGB(w, h);
a = (0xff000000 & val) >>> 24;
r = (0x00ff0000 & val) >> 16;
g = (0x0000ff00 & val) >> 8;
b = (0x000000ff & val);
double power = Math.pow(2.0, 4.0);

//转换二进制
r8bit = Integer.toBinaryString((int) (r));
g8bit = Integer.toBinaryString((int) (g));
b8bit = Integer.toBinaryString((int) (b));

//不足8位的在首部补齐0
int rDifference = 8 - r8bit.length();
int gDifference = 8 - g8bit.length();
int bDifference = 8 - b8bit.length();
for (int i = rDifference; i > 0; i--) {
r8bit = "0" + r8bit;
}
for (int i = gDifference; i > 0; i--) {
g8bit = "0" + g8bit;
}
for (int i = bDifference; i > 0; i--) {
b8bit = "0" + b8bit;
}

//将末尾置0,便于进行编码隐藏
StringBuffer rbuff = new StringBuffer(r8bit);
StringBuffer gbuff = new StringBuffer(g8bit);
StringBuffer bbuff = new StringBuffer(b8bit);
dataBin+=rbuff.charAt(7);
dataBin+=gbuff.charAt(7);
dataBin+=bbuff.charAt(7);

if(x>limit)break;
x++;
}
if(x>limit)break;
}
//每个字母、汉字、符号的分隔符为'%#'
String X=Integer.toBinaryString('%');
String Y=Integer.toBinaryString('#');
String[] datas=dataBin.split(X+Y);
for(int i=0;i<datas.length-1;i++){
data+= BinToString.toString(datas[i]);
}
return data;
}

public static void main(String[] args) throws IOException {
int limit=10000;
BufferedImage secretImg = ImageIO.read(new File("secret.png"));
System.out.println(decryptLSB(secretImg,limit));
File f=new File("a.txt");//解密内容输出txt
FileOutputStream fos1=new FileOutputStream(f);
OutputStreamWriter dos1=new OutputStreamWriter(fos1);
dos1.write(decryptLSB(secretImg,limit));
dos1.close();
}
}

使用到的其他方法类

加工整合二进制编码字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StringToBin {
//加工整合二进制编码字符串
public String StrToBinstr(String str) {
//分隔符为'%#'
String x=Integer.toBinaryString('%');
String y=Integer.toBinaryString('#');
char[] strChar = str.toCharArray();
String result = "";
for (int i = 0; i < strChar.length; i++) {
result += Integer.toBinaryString(strChar[i]) + x+y;
}
return result;
}
}

二进制编码翻译成String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class BinToString {//加密
//将二进制翻译成String文本
public static String toString(String binary) {
//分隔符为'%#'
String x=Integer.toBinaryString('%');
String y=Integer.toBinaryString('#');
String[] tempStr=binary.split(x+y);
char[] tempChar=new char[tempStr.length];
for(int i=0;i<tempStr.length;i++) {
tempChar[i]=BinstrToChar(tempStr[i]);
}
return String.valueOf(tempChar);
}

//将二进制字符串转换成int数组
public static int[] BinstrToIntArray(String binStr) {
char[] temp=binStr.toCharArray();
int[] result=new int[temp.length];
for(int i=0;i<temp.length;i++) {
result[i]=temp[i]-48;
}
return result;
}

//将二进制转换成字符
public static char BinstrToChar(String binStr){
int[] temp=BinstrToIntArray(binStr);
int sum=0;
for(int i=0; i<temp.length;i++){
sum +=temp[temp.length-1-i]<<i;
}
return (char)sum;
}
}

运行测试

加密生成secret.png:

avatar

解密生成文本:

avatar

附录

参考链接:

原理详解 百度百科 基于图像的 LSB 隐写术科普