超递增背包加密算法原理和javaDemo

一、引入背包问题

 

超市里有N件物品,第一件物品的重量是m1,价值是v1,第二件物品的重量是m2,价值是v2…..第N件物品的重量是mN,价值是vN。现在有一个背包Bag,最多能装下的重量为M,请问在不超过背包容量的前提下,怎样使得装在背包里的商品价值总和最大。

 背包加密算法是非对称密钥算法的一种。

二、背包加密过程

(一)  生成公钥和私钥

 

                        i.             构造超递增背包

要求:前面元素的和总是小于等于第三个元素。

我们取一个简单的超递增背包:{2,5,7,13}

 

该背包就是私钥

 

                      ii.             获取乘数e和模数m

乘数和模数用于计算公钥中的每个元素。乘数e和模数m要满足:

 

1)、e和m互质;

2)、m大于私钥背包所有元素之和.

 

我们令 e = 3 , m = 50

 

求e对于m的模反元素d:e*d≡1(mod m)

 

e*d / m = k 余 1

这是一个二元一次方程

3d  = 50k +1;

使用穷举法发现,k=1时,d = 17

 

故(d,k) = (17,1)

 

                     iii.             利用私钥,乘数e和模数m生成公钥:k = e * I mod m

k是公钥中的元素,i是与k对应的私钥中的元素

k1 = e * i1 mod m = 3 * 2 mod 50 =  6

k2,k3,k4也用同样方法计算

 

最后得出公钥为:{6,15,21,39}

        

(二)  加密

背包算法加密和解密都涉及到进制的转换。一段明文必须先转成双方事先约定好的进制(例如常见的二进制,八进制和不常见的七进制等),过长的信息必须分割成与密钥长度相等信息片段,然后分段加密。

 

         假设我们的明文是mc = 18 , 使用二进制进行加密

mc用二进制表示 : 00010010

密钥长度为4,所以,对明文的二进制进行分段

mc1 = 0001, mc2 = 0010

 

加密:明文和密文和公钥的关系是

         二进制片段的每一位,都与公钥数字对齐,如果二进制位为1,则用公钥数字对应位置的数字乘以1,如果二进制位为0,则公钥数字对应位置数字乘以0.最后将所得结果相加,就是密文片段的密码。

公钥数组:{6,15,21,39}

         mc1 =  0 0 0  1

                            mc1= 6*0+15*0+21*0+39*0 = 39

                   同理

                   mc2= 21

 

         所以mc的密文c 就是{39 ,  21}

 

(三)  解密

 

密文明文和私钥的关系为:c*d mod m = mc

 

mc1 = 39 * 17 % 50 = 13

mc2 = 21 * 17 % 50 = 7

 

将解密后的明文片段分解成几个加数相加的形式:这几个加数必须都包含在私钥中

(由于本例的特殊情况,明文片段本身就包含在私钥数组)。

 

将明文片段的所有加数按从小到大排序,然后对应私钥转换进制。就是如果私钥背包中的某项元素位于mc1片段的加数内,则该位为1,反之为0

私钥: {2,5,7,13}

mc1= 0 0  0 1   由于mc1的加数只有13一个,所以只有13这位为1

mc2= 0 0  1 0  

最后拼接二进制:

         mc的二进制就是: 00010010

         转换成十进制就是: 16 + 2 = 18。

 

三、关于密文解密成明文

关于密文解密成明文过程中,即:已经求得mc = 13时候,使用13和私钥得到加密时传入的13的二进制形式的数字,用到了如下思路:

 

         例如:一个私钥 prikey = {2,3,6,10 }和解密后得到的解密后密文 = 8,可以这样计算

        

1.        设一个长度与私钥长度相等的数组arr[]令k = mc ,用k和私钥最后一位10比较:k = 8 < 10 ,

                   :在arr[]最后一位写入0 ,表示mc的和数里没有10

2.        然后用k和私钥中的倒数第二位6进行比较:k = 10 > 6 :

1)        :在arr[]倒数第二位写入1,表示在mc的和数里有6

2)        :重新给k赋值,k = k - 6 = 8 – 6 = 2

 

3.        使用k和私钥的倒数第三位比较:k = 2 < 3

在arr[]的倒数第三位写入0,表示mc的和数里没有3

4.        使用k和私钥的倒数第四位比较:k =2

1)        :在arr[]的倒数第四位里写入1,表示mc的和数里有2

2)        重新给k赋值,k = k – 2 = 2 – 2 = 0  结束

        

这时看arr[]中的数据为:1010,这个才是真正的明文。即:明文m = 5

 

 

package com.joe.main;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description:超递增加法背包加密Demo, 匆忙写成,加密内容不能超过Integer.MAX_VALUE。只能加密数字.
 * @company XXX通信技术(北京)有限公司
 * @author Joe
 * @date 2013-9-10 下午4:47:36
 */
public class BagEncript {

	private static int[] privateKeys;// 私钥数组
	private static int sn; // 私钥数组所有元素和
	private static int[] publicKeys;// 公钥数组
	private static int m = 1; // 模数m
	private static int e = 1; // 乘数e
	private static int d = 1; // e对m的模反元素d,满足e*d % m == 1恒成立
	private static String lastEightBinaryString; // 明文二进制数的后八位
													// ,明文的二进制每一位都和密钥数组是一一对应的,所以必须将过长的明文分割成与密钥等长的子数组
	private static char[] charLBS;// 明文二进制数的后八位 对应的字符数组
	private static List<Integer> listOfCipherPieces; // 存放密文片段的集合
	private static List<char[]> listOfCharLBS; // listOfCharLBS用于存放明文片段的二进制数组
	private static String resultPlaintext = "";// 解密密文后得到的二进制结果

	public static void main(String[] args) {
		// 1、生成超递增背包私钥
		createPriKey();
		// 2、取乘数e和模数m,他们的模反 d
		getEAndM();
		// 3、生成公钥
		createPubKey();
		// 4、加密,解密
		encript(Integer.MAX_VALUE);

	}

	// 生成私钥4位超递增背包
	private static void createPriKey() {

		privateKeys = new int[8];
		int num1 = 1;
		int num2 = 1;
		do {
			num1 = (int) (Math.random() * 5);
			num2 = (int) (Math.random() * 5);
		} while (num1 == num2 || num1 == 0 || num2 == 0);
		if (num1 > num2) {
			int temp = num1;
			num1 = num2;
			num2 = temp;
		}
		privateKeys[0] = num1;
		privateKeys[1] = num2;
		int num3 = 1;
		for (int i = 2; i < privateKeys.length; i++) {
			do {
				num3 = (int) (Math.random() * Math.pow(3, i));
			} while (num3 <= (privateKeys[i - 2] + privateKeys[i - 1]));
			privateKeys[i] = num3;
		}
		for (int i = 0; i < privateKeys.length; i++) {
			sn += privateKeys[i];
			System.out.println("privateKeys[" + i + "] = " + privateKeys[i]);
		}
	}

	// 2、取乘数e和模数m
	private static void getEAndM() {

		do {
			m = (int) (Math.random() * 10000);
		} while (sn >= m);

		do {
			e = (int) (Math.random() * 10000);
		} while (gcd(e, m) != 1 || e > 10000);
		loop: for (int i = 0;; i++) {
			if ((i * m + 1) % e == 0) {
				d = (i * m + 1) / e;
				break loop;
			}
		}
		System.out.println("m = " + m + " , e = " + e + " ,d = " + d);
	}

	// 3、生成公钥
	private static void createPubKey() {
		publicKeys = new int[privateKeys.length];
		for (int i = 0; i < publicKeys.length; i++) {
			publicKeys[i] = (e * privateKeys[i]) % m;
			System.out.println("publicKeys[" + i + "] = " + publicKeys[i]);
		}
	}

	// 4、加密
	private static void encript(int msg) {
		System.out.println("要加密的明文 : " + msg);
		// 初始化listOfCharLBS,用于存放明文片段的二进制数组
		listOfCharLBS = new ArrayList<char[]>();
		// 初始化 listOfCipherPieces,是明文片段加密后对应的密文片段的存放数组
		listOfCipherPieces = new ArrayList<Integer>();
		String binaryString = Integer.toBinaryString(msg);
		// 判断二进制数组是否是8位或8的整数倍
		int leftZero = binaryString.length() % 8;
		// 将不足8位或8位整数倍的左边二进制位补0
		for (int i = 0; i < 8 - leftZero; i++) {
			binaryString = "0" + binaryString;
		}
		System.out.println("明文的二进制数组  :  " + binaryString);
		// 计算明文二进制字符串按照8位一组可以分机组
		int k = binaryString.length() / 8;
		for (int i = 0; i < k; i++) {
			// 从二进制数组的右边开始向左,每8位一组
			lastEightBinaryString = binaryString.substring(
					binaryString.length() - 8, binaryString.length());
			// 将二进制数组重新赋值,新数组中只要原数组的前 n - 8位(其中n代表原数组长度)
			binaryString = binaryString.substring(0, binaryString.length() - 8);
			// 将二进制片段存入list中
			charLBS = lastEightBinaryString.toCharArray();
			listOfCharLBS.add(0, charLBS);
		}
		System.out.println("binaryString.length() =  " + binaryString.length());
		System.out.println("binaryString =  " + binaryString);
		for (int j = 0; j < listOfCharLBS.size(); j++) {
			char[] eightBinary = (char[]) listOfCharLBS.get(j);
			System.out.println("listOfCharLBS中的第" + j + "个char 数组 = "
					+ String.copyValueOf(eightBinary));
			int cipherPart = 0; // 密文片段
			// 加密过程 每个二进制位上的数乘以公钥对应位置的元素,之后求和,就是当前明文片段对应的密文
			for (int i = 0; i < eightBinary.length; i++) {
				cipherPart += publicKeys[i]
						* Integer.parseInt(eightBinary[i] + "");
			}
			// 密文片段放入密文数组
			listOfCipherPieces.add(0, cipherPart);
		}
		// 输出查看密文数组
		for (int i = 0; i < listOfCipherPieces.size(); i++) {
			Integer cipherPart = listOfCipherPieces.get(i);
			System.out.println("密文片段 " + i + " = " + cipherPart);
		}
		// 解密
		System.out.println("密文数组长度 = " + listOfCipherPieces.size());

		for (int i = 0; i < listOfCipherPieces.size(); i++) {
			int cipherPart = (Integer) listOfCipherPieces.get(i);
			// 解密方法
			decript(cipherPart);
		}
		// 打印解密后的明文
		System.out.println("解密后的明文二进制数组 : " + resultPlaintext);

		// for (int i = 0; i < listOfPlaintext.size(); i++) {
		// System.out.print(listOfPlaintext.get(i));
		// }
		// 将二进制结果转成10进制输出
		getPlainText();
		resultPlaintext = null;
	}

	// 解密
	public static void decript(int cipherPart) {

		int plainText = cipherPart * d % m; // 密文片段解密后得到明文片段
		int cha = plainText; 

		for (int i = privateKeys.length - 1; i >= 0; i--) {
			
			if (cha >= privateKeys[i]) {
				// listOfPlaintext.add(0, 1);
				resultPlaintext = "1" + resultPlaintext;
				cha = cha - privateKeys[i];
			} else {
				// listOfPlaintext.add(0, 0);
				resultPlaintext = "0" + resultPlaintext;
			}
		}
	}

	// 将二进制结果转成10进制输出
	public static void getPlainText() {
		BigInteger bi = new BigInteger(resultPlaintext, 2);
		System.out.println(bi.toString(10));
	}

	// 求最大公约数
	public static int gcd(int a, int b) {
		int gcd;
		if (b == 0)
			gcd = a;
		else
			gcd = gcd(b, a % b);
		return gcd;

	}
}

 

 

 

 

 

点击此处下载免积分Demo

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值