[密碼學]格密碼學(2)-揹包公鑰密碼體制
(注:本文僅供學習,轉載或者拷貝引起的一切後果自負,本文部分內容翻譯並參考自:《An Introduction to Mathematical Cryptography》(Jeffrey Hoffstein, Jill Pipher, Joseph H. Silverman))
上一篇介紹了同餘公鑰密碼體制,本篇筆者將介紹另一個經典的金鑰體制--揹包公鑰密碼體制。在介紹該問題之前,先介紹下子集和問題。
1、子集和問題
假設在整數域上有集合S={a,b,c,d,e,f.....}和一個整數sum。那麼找到集合S的一個子集SubS,該子集滿足:該子集中的所有元素相加恰好為sum。比如S={1,2,3,4,5,6,7,8},sum=15,那麼我們可以找到其子集SubS={7,8}或者{1,6,8}等等,這樣的一個問題就是子集和問題。
子集合問題是NP完備問題(NP-complete problem),其求解是非常困難的(剛剛筆者所給的例子是一種比較簡單的情況,讀者請不要誤以為該問題很好解決)。
我們考慮,子集和問題是否有所謂的特殊情況,而且這種特殊情況我們和容易去求解呢?答案是肯定的。
這裡我們將討論下超遞增序列(superincreasing sequence)。即集合S是超遞增的,那麼什麼是超遞增序列呢?首先我們給出超遞增序列的定義。超遞增序列指的是這樣的一個集合,其中。
超遞增序列具有什麼好處呢?由其定義,我們可以知道它具有這樣的一個性質:。這個性質對於求解子集和問題是多有幫助呢?筆者下面通過一個例子來給大家演示下超遞增序列的子集和問題的解決辦法。
例1:對於超遞增序列M={3,11,24,50,115},和Sum=142。求解該子集和問題。
Step 1:142-115=27>0,故115被選中;
Step 2:27-50<0,故50不選;
Step 3:27-24=3>0,故24選中;
Step 4:3-11<0,故11不選;
Step 5:3-3=0,故3選中,演算法結束,找到子集。
綜上過程,得到的子集為SubS={3,24,115}。驗證正確。
那麼該如何將這個問題變成一個可用的密碼體制?我們只需要將一個超遞增序列通過一些運算得到一個非超遞增序列(簡單的說,就是將超遞增序列偽裝成非遞增序列)。下面我們來介紹由這個問題產生的揹包公鑰密碼體制。
2、揹包公鑰密碼體制
通過上面的介紹,我們對於子集和問題有了一定的認識。那麼該如何偽裝?
和以前一樣,我們先通過一個例子來大概瞭解下。
例2:有超遞增序列r={3,11,24,50,115},Alice選擇A=113,B=250。然後Alice通過下面的運算來偽裝該超遞增序列r。
M=113r (mod 250)={89,243,212,150,245}。顯然M不是一個超遞增序列了。M就是公鑰。
現在Bob要傳送一個訊息給Alice,這裡我們假設訊息內容x={1,0,1,0,1}(是一個二元字串)。然後S=xM=1*89+0*243+1*212+0*150+1*245=546。S即為密文。
Alice接收到Bob傳送的密文S後,解密過程如下:。再通過超遞增序列,我們即可解密得到明文x了。
下面給出該金鑰體制過程的描述。
(1)Alice:選擇一個超遞增序列,選擇A和B,其中且gcd(A,B)=1。下面計算,。得到公鑰。
(2)Bob:二元對字串明文x,用Alice的公鑰計算S=x*M。S是密文。
(3)Alice解密:計算,再用超遞增序列的子集和問題解法求解,得到一個解x,該解即是明文。
給出該公鑰密碼體制的JAVA實現。
import java.math.BigDecimal;
import java.math.BigInteger;
public class KnapsackCryptosystems {
private final int MAX = 10;
BigInteger[] SuperSet = new BigInteger[MAX];
BigInteger[] PublicKeySet = new BigInteger[MAX];// 公鑰
BigInteger A, B;
public void GenerateSetAndPublicKey() {
SuperSet[0] = (new BigDecimal("10").multiply(new BigDecimal(Math
.random() + "")).add(BigDecimal.ONE)).toBigInteger();
for (int i = 1; i < MAX; i++) {
SuperSet[i] = (SuperSet[i - 1].multiply(new BigInteger("2"))
.add(new BigDecimal("10")
.multiply(new BigDecimal(Math.random() + ""))
.add(BigDecimal.ONE).toBigInteger()));
}
B = (SuperSet[MAX - 1].multiply(new BigInteger("2"))
.add(new BigDecimal("10")
.multiply(new BigDecimal(Math.random() + ""))
.add(BigDecimal.ONE).toBigInteger()));
A = SuperSet[MAX - 1];
while (true) {
A = A.subtract(BigInteger.ONE);
if (B.gcd(A).equals(BigInteger.ONE)) {
break;
}
}
for (int i = 0; i < MAX; i++) {
PublicKeySet[i] = A.multiply(SuperSet[i]).mod(B);
}
}
public BigInteger encrypt(String x) {
BigInteger S = BigInteger.ZERO;
for (int i = x.length(); i < MAX; i++) {
x = x.concat("0");
}
for (int i = 0; i < MAX; i++) {
if (x.charAt(i) == '1')
S = S.add(PublicKeySet[i]);
}
return S;
}
public String decrypt(BigInteger S) {
BigInteger S1;
String x = "";
S1 = A.modInverse(B).multiply(S).mod(B);
int num = MAX;
while (true) {
if (S1.subtract(SuperSet[num - 1]).signum() > 0) {
S1 = S1.subtract(SuperSet[num - 1]);
x = "1".concat(x);
} else if (S1.subtract(SuperSet[num - 1]).signum() == 0) {
x = "1".concat(x);
break;
} else {
x = "0".concat(x);
}
num--;
}
if (num != 0) {
for (int i = 0; i < num - 1; i++)
x = "0".concat(x);
}
return x;
}
public static void main(String args[]) {
KnapsackCryptosystems kc = new KnapsackCryptosystems();
kc.GenerateSetAndPublicKey();
System.out.println("隨機產生的公鑰:");
System.out.print("(");
for (int i = 0; i < kc.MAX - 1; i++)
System.out.print(kc.PublicKeySet[i] + ",");
System.out.println(kc.PublicKeySet[kc.MAX - 1] + ")");
// 隨機產生5組訊息
for (int j = 0; j < 5; j++) {
String x = "";
for (int i = 0; i < kc.MAX; i++) {
if (Math.random() < 0.5) {
x = x.concat("1");
} else
x = x.concat("0");
}
// 加密x資訊,得到密文S
System.out.println("第" + (j + 1) + "組隨機產生的明文: " + x);
String S = kc.encrypt(x).toString();
System.out.println("加密得到的密文:" + S);
System.out.println("解密得到的明文" + kc.decrypt(new BigInteger(S)));
}
}
}
注:上述程式碼用了隨機的明文進行測試,且程式碼只針對二元字串進行加密處理,將文字用Huffman編碼轉換成二進位制後分段處理,這裡筆者不再實現,本程式僅供參考。
上述程式碼的執行結果如下:
筆者水平有限,難免存在不足,歡迎批評交流!