专栏首页Ldpe2G的个人博客乱序+移位加密24位bmp格式图片 scala实现

乱序+移位加密24位bmp格式图片 scala实现

前言:

关于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那篇文章中

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • MXNet Scala 学习笔记 二 ---- 创建新的 Operator

    Ldpe2G
  • MXNet Scala 学习笔记 二 ---- 创建新的 Operator

    (比如Python、Scala)采用现有的操作子来组合,比如实现 Selu 激活函数。

    Ldpe2G
  • ScalaMP ---- 模仿 OpenMp 的一个简单并行计算框架

    这个项目是一次课程作业,要求是写一个并行计算框架,本人本身对openmp比较熟,

    Ldpe2G
  • 【leetcode刷题】T154-二叉搜索树中的插入操作

    https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/

    木又AI帮
  • mysql基础操作实例

    之前写过一篇mysql基础指令集合,感觉看指令在一些新手朋友来说有点不爽很容易理解,正好现在自己开始自学mysq就把这里基础的指令使用代码展示一下,希望能帮...

    十月梦想
  • Java软件开发者,如何学习大数据?

    这是正常学习大数据必须要做到的三个步骤,如果有了java基础再去学习基本上已经成功了一半,起码不用为了基础语言的学习而恼火了。

    程序员互动联盟
  • 从运营角度看数据安全

    早在笔者刚入行的那个时期,安全岗位基本只有两种,WEB安全工程师和Android安全工程师,回忆一下前几年企业出现的风险事件、大多是安全工程师参围绕应用安全漏洞...

    FB客服
  • 一篇文章告诉你怎么做性能测试

    突然有一天,领导说:“小王,今天把996福报系统压一下,下班前把压测报告发我邮箱。”

    测试小兵
  • 2015伦敦深度学习峰会笔记:来自DeepMind、Clarifai等大神的分享

    上周,我有机会参加在伦敦举行的第三届深度学习峰会,上两届分别在旧金山和波士顿举行。 深度学习峰会由 RE.WORK主办,汇集了从工业领域到学术领域不同背景的专业...

    CSDN技术头条
  • 微信小程序发话:玩一个小游戏才是正经事

    “第一张微信身份证”“如果问了也是白问,不如微信搜一搜”“第一流量入口开放”等等话题,占据了各大媒体平台。这些被写烂的热度,小渔儿在此就不多费口舌,不如坐下来,...

    企鹅号小编

扫码关注云+社区

领取腾讯云代金券