一、引入背包问题
超市里有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;
}
}
完