前言:
关于bmp图片的格式分析:BMP
用java读写24位bmp格式图片的一篇博客:关于Java读取和编写BMP文件的总结,
正文:
乱序和移位加密都属于古典加密方法,容易被破解,本文将两种加密方式结合,
再进行多轮加密,保密性能稍微增强一点。
乱序加密:
这里只简单介绍一下列乱序加密:
设明文 m=m1 m2 ... ms,共 s 个字符,现规定每行有 n 个字符(n<s),
设 t= [s / n] , 如果n不整除s , 则明文按通用格式输出,共形成 t+1 行的
一个明文矩阵,第 t+1 行仅有 s-nt 个字符,有 ( t+1 ) * n - s 个空格。
密匙 k 是一个 Zn = { 1 2 ... n } 上的置换θ,把 1 2 ... n 置换成
θ(1) θ(2) ... θ(n)。现在以 θ^ 作为θ的逆变换。
加密时对明文长方阵先按第θ^(1)列读出字符(自第 1 行读至第t+1 行,t+1行如果是空格则不读)
然后以行顺序填入一个空的矩阵该矩阵和明文矩阵一样大小,
再按第θ^(2)列读出字符...,最后按第θ^(n)列读出,然后就算加密完毕。
举个简单的例子:
现在加密以下明文:
春种一粒粟, 秋收万颗子。
四海无闲田, 农夫犹饿死。
锄禾日当午, 汗滴禾下土。
谁知盘中餐, 粒粒皆辛苦。
加密时忽略标点符号,字符数s = 40,列数n = 10,则t = [ 40 / 10 ] = 4,
这里恰好是可以整除。
设变换 θ: θ(1) = 5,θ(2) = 1,θ(3) = 8,θ(4) = 7,θ(5) = 2,
θ(6) = 10,θ(7) = 3,θ(8) = 9,θ(9) = 6,θ(10) = 4。
相应的逆变换也就是密钥: θ^(1) = 2,θ^(2) = 5,θ^(3) = 7,θ^(4) = 10,θ^(5) = 1,
θ^(6) = 9,θ^(7) = 4,θ^(8) = 3,θ^(9) = 8,θ^(10) = 6。
明文矩阵:
春种一粒粟秋收万颗子
四海无闲田农夫犹饿死
锄禾日当午汗滴禾下土
谁知盘中餐粒粒皆辛苦
根据逆变换首先读取θ^(1)列,就是明文第2列,按行顺序填入空矩阵:
种海禾知
然后读取θ^(2)列,就是明文第5列,按行顺序填入空矩阵:
种海禾知粟田午餐
然后读取θ^(3)列,就是明文第7列,按行顺序填入空矩阵:
种海禾知粟田午餐收夫
滴粒
按照以上步骤。
加密后:
种海禾知粟田午餐收夫
滴粒子死土苦春四锄谁
颗饿下辛粒闲当中一无
日盘万犹禾皆秋农汗粒
解密时,由于是整除的情况明文矩阵每一列的行数相等都是4,所以每次按行读取密文矩阵
4个元素,然后按照密钥即逆变换放到相应的明文矩阵的列,
比如θ^(1) = 2,所以将“种海禾知”放到明文的第2列,θ^(2) = 5,
将“粟田午餐”放到明文的第5列,
如此类推根据密钥就可以解出明文。
乱序加密bmp图片:
加密bmp图片只是对bmp格式图片的图像数据部分进行加密,然后按照bmp图片的格式先将信息头写入文件,
最后再写入加密的数据,这样就完成了对bmp图片内容的加密,加密后还是bmp格式图片。
加密过程简述:
主要还是按列乱序加密,不过在加密时,当当前列的序号能被2整除,
则读取时从上往下读,若列序号不能被2整除则从下往上读取明文,解密时
也按照相应的顺序即可,然后可以加密多轮,加密轮次由由命令行参数决定,
最后将密钥和加密轮次写入加密后的bmp图片的尾部,这样解密程序只需要读入
加密图片就可以解密了,每一轮加密的结果都会输出。
乱序加密例子:
原图 加密一次 加密两次
原图
加密一次
加密二次
我们可以看到,加密两次后由于颜色没有被加密,还是会多少透露了一些信息,
所以还要加上移位加密对颜色rgb进行移位加密。
移位加密bmp图片:
程序每次加密前随机生成一个整数n, 0 <= n <= 255,然后对于
red通道: red = (red + n) % 256
green通道:green = (green + n + 64) % 256
blue通道: blue = (blue + n + 128) % 256
每一轮加密都对颜色进行一次移位加密。
效果:
原图 加密一次 加密两次
原图
加密一次
加密两次
乱序+移位加密程序代码:
(注:只能加密宽度能被4整除的24位bmp图片,这应该个bug):
import java.io._
object EncryptBmp24 {
def main(args: Array[String]): Unit = {
if(args.length < 2){
System.out.println("usage: programe bmpImagePath encryptTimes");
}else{
Encrypt(args(0), Integer.parseInt(args(1)))
}
}
def Encrypt(bmpPath: String, times: Int) = {
//获取图片的长宽和rgb数据
val (height, width, red, green, blue) = init(bmpPath)
val P = getRandomP(width) //随机生成变换
val Q = new Array[Int](width)
val R = height //每一列的行数
//生成密钥
for(i <- 0 to width - 1)
Q(P(i)) = i
val encry_red = Array.ofDim[Int](height, width)
val encry_green = Array.ofDim[Int](height, width)
val encry_blue = Array.ofDim[Int](height, width)
import java.util.Random
//随机生成移位加密密钥
val shift = new Random(System.currentTimeMillis).nextInt(256)
//总共加密times次
for(t <- 0 until times){
var vi = 0
var vj = 0
for(j <- 0 until width){
//取变换对应的一列的元素按行填入加密矩阵
for(k <- 0 until R){
if(vj == width){
vi += 1
vj = 0
}
if(Q(j) % 2 == 0){
encry_red(vi)(vj) = red(k)(Q(j))
encry_green(vi)(vj) = green(k)(Q(j))
encry_blue(vi)(vj) = blue(k)(Q(j))
}else{
encry_red(vi)(vj) = red(R - 1 - k)(Q(j))
encry_green(vi)(vj) = green(R - 1 - k)(Q(j))
encry_blue(vi)(vj) = blue(R - 1 - k)(Q(j))
}
vj += 1
}
}
for(i <- 0 until height)
for(j <- 0 until width){
red(i)(j) = encry_red(i)(j)
green(i)(j) = encry_green(i)(j)
blue(i)(j) = encry_blue(i)(j)
}
//加密rgb
encryptColor(red, green, blue, shift)
val parentPath = new File(bmpPath).getParent
Bmp24Writer.writeEncryptedBmp(s"$parentPath/encrypt$t.bmp",
Q, shift, t + 1, red, green, blue)
}
}
def encryptColor(red: Array[Array[Int]],
green: Array[Array[Int]],
blue: Array[Array[Int]], shift: Int) = {
val height = red.length
val width = red(0).length
for(i <- 0 until height)
for(j <- 0 until width){
red(i)(j) = (red(i)(j) + shift) % 256
green(i)(j) = (green(i)(j) + shift + 64) % 256
blue(i)(j) = (blue(i)(j) + shift + 128) % 256
}
}
def getRandomP(length: Int): Array[Int] = {
import java.util.ArrayList
import java.util.Random
val temp = new Array[Int](length)
val list = new ArrayList[Int]
for(i <- 0 until length)
list add i
val random = new Random(System.currentTimeMillis)
for(t <- length until 0 by -1)
temp(length - t) = list remove random.nextInt(t)
temp
}
def init(bmpPath: String) = {
val fin = new FileInputStream(bmpPath)
val bis = new BufferedInputStream(fin)
// 获得文件头和信息头的数据
//分别是14字节和40字节
val array1 = new Array[Byte](14)
bis.read(array1, 0, 14)
val array2 = new Array[Byte](40)
bis.read(array2, 0, 40)
// 解析信息头的数据得到位图数据的宽和高
val width = Bmp24Writer.ChangeInt(array2, 7)
val height = Bmp24Writer.ChangeInt(array2, 11)
println(s"width: $width, height: $height")
//获取位图的数据,返回rgb二维整型数组
val (red, green, blue) = Bmp24Writer.getInf(bis, height, width)
fin.close
bis.close
(height, width, red, green, blue)
}
}
解密代码:
import java.io._
object DecryptBmp24 {
def main(args: Array[String]): Unit = {
if(args.length < 1){
System.out.println("usage: programe bmpImagePath");
}else{
Decrypt(args(0))
}
}
def Decrypt(bmpPath: String) = {
//从已经加密的图片中读出宽高,
//加密的数据,密钥,加密轮次和移位密钥
val (height, width, red, green, blue, keys, times, shift) = init(bmpPath)
val R = height
val decry_red = Array.ofDim[Int](height, width)
val decry_green = Array.ofDim[Int](height, width)
val decry_blue = Array.ofDim[Int](height, width)
for(t <- 0 until times){
var vi = 0
var vj = 0
//每一轮都对rgb进行一次移位解密
decryptColor(red, green, blue, shift)
for(j <- 0 until width){
for(k <- 0 until R){
if(vj == width){
vi += 1
vj = 0
}
if(keys(j) % 2 == 0){
decry_red(k)(keys(j)) = red(vi)(vj)
decry_green(k)(keys(j)) = green(vi)(vj)
decry_blue(k)(keys(j)) = blue(vi)(vj)
}else{
decry_red(R - 1 - k)(keys(j)) = red(vi)(vj)
decry_green(R - 1 - k)(keys(j)) = green(vi)(vj)
decry_blue(R - 1 - k)(keys(j)) = blue(vi)(vj)
}
vj += 1
}
}
for(i <- 0 until height)
for(j <- 0 until width){
red(i)(j) = decry_red(i)(j)
green(i)(j) = decry_green(i)(j)
blue(i)(j) = decry_blue(i)(j)
}
}
val parentPath = new File(bmpPath).getParent
Bmp24Writer.writeNormalBmp(s"$parentPath/decrypt.bmp",
red, green, blue)
}
//移位解密就是加密的逆变换
def decryptColor(red: Array[Array[Int]],
green: Array[Array[Int]],
blue: Array[Array[Int]], shift: Int) = {
val height = red.length
val width = red(0).length
for(i <- 0 until height)
for(j <- 0 until width){
red(i)(j) = (red(i)(j) - shift + 256) % 256
green(i)(j) = (green(i)(j) - shift - 64 + 256) % 256
blue(i)(j) = (blue(i)(j) - shift - 128 + 256) % 256
}
}
def init(bmpPath: String) = {
val fin = new FileInputStream(bmpPath)
val bis = new BufferedInputStream(fin)
val array1 = new Array[Byte](14)
bis read(array1, 0, 14)
val array2 = new Array[Byte](40)
bis read(array2, 0, 40)
val width = Bmp24Writer.ChangeInt(array2, 7)
val height = Bmp24Writer.ChangeInt(array2, 11)
println(s"width: $width, height: $height")
//首先读出加密的数据
val (red, green, blue) = Bmp24Writer.getInf(bis, height, width)
val keys = new Array[Int](width)
//然后读取列乱序密钥
val temp = new Array[Byte](4)
for(i <- 0 until width){
bis read(temp, 0, 4)
keys(i) = Bmp24Writer.ChangeInt(temp, 3)
}
//然后读取加密轮次
bis read(temp, 0, 4)
val times = Bmp24Writer.ChangeInt(temp, 3)
//读取移位密钥
bis read(temp, 0, 4)
val shift = Bmp24Writer.ChangeInt(temp, 3)
for(i <- 0 to width - 1)
print(s"${keys(i)} ")
println(s"\nkey length: ${keys.length}")
println(s"encrypted times: $times, color shift: $shift")
fin.close
bis.close
(height, width, red, green, blue, keys, times, shift)
}
}
Bmp24Writer:
由于超出了文章限制的字符数,所以将这部分代码放到了 Bmp24Writer那篇文章中