大家好,又见面了,我是你们的朋友全栈君。
原文链接 https://yq.aliyun.com/articles/72267
背景
随着手机app越来越多,对于App信息安全面临的挑战越来越大,像接口传递的验证信息这些相对保密的信息如果直接放在app中明文,那么毫无疑问,很容易就被激活成功教程出来,想干嘛就干嘛。因为为了对部分本地信息加密,相处过无数的办法,本次讨论的重点,无水印信息图片加密。
原理
无水印信息图片加密,基本原理,就是将信息负载在图片上,然后程序通过特定的算法将信息再度拿出来,而图片一看下去也是正常显示的,肉眼看不出任何的猫腻。那么到底怎么附加法?
1.追加信息法
利用不同格式图片的特性,例如BMP文件头标记了图片文件大小,后面信息不读取,或JPG文件拥有FFD9标志结束符,因此就算将再多信息附加上去,也不会影响原来图片查看。
2.颜色特征法
根据颜色的特点,因为颜色的最后一个位含有的信息量就算改变也不会改变大局,所以颜色的最后一个bit作为信息记录点。
3.颜色特征法Ex
颜色特征,按一定的算法,获取约定的图片特征,例如,都获取RGB中G的整张图的波形,通过某种滤波器,分析出来某段,然后加上校验码进行校验信息是否有效,而且多端,含冗余,分布图片各个地方,即使压缩,或者截图后,信息也有可能被获取到。
各自优缺点
追加信息法:
优点:
加密后图片正常显示,无信息长度限制,可以无限追加信息。我们都不明白为啥某个”正常”图片竟然有1,2G那么大,到底后面附加了什么?
缺点:
无限追加,也是致命缺点,你会傻到真的认为阿强那张1,2G的图片真的只是单纯的图片那么简单吧?
颜色特征法:
优点:
加密后图片正常显示,信息保密度更强,不会增加图片本身的大小,当然转格式例外,而且根据算法,整体的保密性更强。
缺点:
能加密的信息长度受图片size限制,如果对图片进行过压缩,信息将会损失的一塌糊涂。
颜色特征法Ex
优点:
经过压缩后,信息仍有机会提取出来,耐操,加密后不会改变图片大小,有冗余信息,激活成功教程难度大。
缺点:
图片容易显示不正常,当然搞成类似白噪点也是个技术活,能加密的信息的长度受图片size限制。
颜色特征法原理剖析
这里重点解释下颜色特征法是怎么实现的
颜色原理
说之前,必须要说下颜色的组成。大家都知道平时开发中我们使用的颜色值例如白色 #FFFFFFFF黑色#00000000 这些数字代表什么呢?
他们以2位16进制数字为一个单位分别代表A,R,G,B。记得在保存的时候别忘记了A,透明度,否则出来的都是黑色一片哦。
这里讨论R,G,B,他们代表红,绿,蓝,三原色。
而2位16进制的数字联合代表256个色值,换算2进制就是8位。因为主要决定颜色的信息其实都存储在这里,而前面的值表示颜色的变化越大,而最后以为相对改变的话,对颜色本身的影响是非常小的,255和254是相差很小的颜色变化。因为只要我们改变三原色随机一个或者几个的最后一位,其实对颜色变化影响微乎其微。肉眼根本不能看出变化。
int rgb = image.getPixel(curX, curY);
r = (rgb & 0x00ff0000) >> 16;
g = (rgb & 0x0000ff00) >> 8;
b = (rgb & 0x000000ff);
al = (rgb & 0xff000000) >> 24;
if (bitLength >= 0) {
switch (iRGB) {
case 0:
r = (r & 0x000000FE);
r |= value;
break;
case 1:
g = (g & 0x000000FE);
g |= value;
break;
case 2:
b = (b & 0x000000FE);
b |= value;
break;
}
}
rgb = al << 24 | (r << 16) | (g << 8) | b;
图片格式原理
如果你以为只是改个颜色值,就大功告成,呵呵,那你马上哭着发现,压根你加密的信息从来就没正确拿出来过。因为图片是含有头部信息的,而且不同格式的图片头信息肯定也不一致的,相对固定的头部是BMP图片的,因此俺们这次也是采用输出BMP图片作为加密后的结果图片。首先我们看看BMP文件头组成:
bmp文件头
变量名 | 大小 | 作用 |
---|---|---|
bfType | 2bytes | 默认直接写死 424d 说明文件类型的 |
bfSize | 4bytes | 图片总大小,包括头信息 |
bfReserved1 | 2bytes | 保留,必须设置为0 |
bfReserved2 | 2bytes | 保留,必须设置为0 |
bfOffBits | 4bytes | 说明文件头开始到实际图片数据之间的偏移量,其实也是相对恒定的 |
位图信息头
变量名 | 大小 | 作用 |
---|---|---|
biSize | 4bytes | BitmapInfoHeader结构需要的字数,固定的40 |
biWidth | 4bytes | 图像的宽度,用像素为单位 |
biHeight | 4bytes | 图像的高度,用像素为单位。还有个作用,标志图片是正向还是倒向的。如果该值是正数,说明图像是倒向的,如果该数是负数,那么图像是正向的 |
biPlanes | 2bytes | 为目标设备说明颜色的平面数,他的值总是设为1 |
biBitCount | 2bytes | 说明比特数/像数,其值为1、4、8、16、24、32,现在通常用24位 |
biCompression | 4bytes | 说明图像数据压缩的类型。 0 表示不压缩 1 表示8比特编码,只用于8位图 |
biSizeImage | 4bytes | 图像大小,单位为字节 |
biXpelsPerMeter | 4bytes | 说明水平分辨率,像素/米 表示 |
biYPelsPerMeter | 4bytes | 说明垂直分辨率,像素/米 表示 |
biClrUsed | 4bytes | 说明位图实际使用的彩色表中的颜色索引数 |
biClrImportant | 4bytes | 说明对图像显示有重要影响的颜色索引的数目如果是0,表示都很重要 |
so,在修改完图片信息后,需要将这些信息补上头信息,再将颜色信息附上,关键代码如下补充头信息:
FileOutputStream fileos = new FileOutputStream(filename);
// bmp文件头 int bfType = 0x4d42;
long bfSize = 14 + 40 + bufferSize; int bfReserved1 = 0;
int bfReserved2 = 0;
long bfOffBits = 14 + 40;
// 保存bmp文件头 writeWord(fileos, bfType); writeDword(fileos, bfSize); writeWord(fileos, bfReserved1); writeWord(fileos, bfReserved2); writeDword(fileos, bfOffBits); // bmp信息头 long biSize = 40L;
long biWidth = nBmpWidth;
long biHeight = nBmpHeight;
int biPlanes = 1;
int biBitCount = 24;
long biCompression = 0L;
long biSizeImage = 0L;
long biXpelsPerMeter = 0L;
long biYPelsPerMeter = 0L;
long biClrUsed = 0L;
long biClrImportant = 0L;
// 保存bmp信息头 writeDword(fileos, biSize); writeLong(fileos, biWidth); writeLong(fileos, biHeight); writeWord(fileos, biPlanes); writeWord(fileos, biBitCount); writeDword(fileos, biCompression); writeDword(fileos, biSizeImage); writeLong(fileos, biXpelsPerMeter); writeLong(fileos, biYPelsPerMeter); writeDword(fileos, biClrUsed); writeDword(fileos, biClrImportant);
最后把图像信息也附上去。
for (int nCol = 0, nRealCol = nBmpHeight - 1; nCol < nBmpHeight; ++nCol, --nRealCol)
for (int wRow = 0, wByteIdex = 0; wRow < nBmpWidth; wRow++, wByteIdex += 3) {
int clr = bitmap.getPixel(wRow, nCol);
bmpData[nRealCol * wWidth + wByteIdex] = (byte) Color.blue(clr); bmpData[nRealCol * wWidth + wByteIdex + 1] = (byte) Color.green(clr);
bmpData[nRealCol * wWidth + wByteIdex + 2] = (byte) Color.red(clr);
}
效果:
后续:
这只是相对最简单的图像加密,图像并压缩后容易出现损失,因此,后面要加上特征值作为验证,还有应该有一定冗余,还需要部分对其做成类似噪点的研究。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/151998.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...