(注:本文仅供学习,转载或者拷贝引起的一切后果自负,本文部分内容翻译并参考自:《An Introduction to Mathematical Cryptography》(Jeffrey Hoffstein, Jill Pipher, Joseph H. Silverman))
本文将介绍一些基于格问题上的困难问题的公钥密码体制。回顾下实数域R上的向量空间V,线性空间上的两个向量可以相加或者乘以实数。格与向量空间非常相似,不同之处在于,运算是在格上。这个很小的差别引出了一系列很有意思的问题。在介绍格密码之前,首先介绍2个密码体制,同余公钥体制(A congruential public key cryptosystem)和背包公钥密码体制(Subset-sum problems and knapsack cryptosystems)。
一、同余公钥密码体制
(1)Alice选择一个大整数p(公共参数,大家都知晓)和两个秘密整数f,g,f,g满足:,然后计算下面等式:
,
。
这里需要注意的是,f,g都小于q。Alice的私钥是f,g。公钥是大整数h,q。
(2)Bob为了发送消息m给Alice,选择一个随机的整数r,明文m和r需要满足下面的等式。
,并且
接下来Bob计算密文
,0<e<q。e就是密文。
(3)Alice接收到密文m后进行解密。
Alice计算下面的公式
,0<a<q。
再计算
,0<b<g。
(注意,是f模g的逆)。
下面给出正确性证明:
只需要证明b==m即可。
根据加解密过程,我们有:
由于f,g,r的大小都有范围限制,所以有:
因此,当Alice计算,她得到的是一个准确值:
这是该算法的关键之处,最后Alice计算
,因为
,所以我们得到b==m。
下面给出该密钥体制的JAVA实现。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Random;
public class CongruentialPublicKey {
private static BigInteger Q, H, F, G;
public BigInteger[] generatorQandH() {
int alpha = 50;
BigInteger g;
Random r = new Random();
BigInteger q = null;
while (true) {
q = BigInteger.probablePrime(alpha, r);
if (q.bitLength() != alpha)
continue;
if (q.isProbablePrime(10)) // if q is prime ,contiune
{
Q = q.multiply(new BigInteger("2")).add(BigInteger.ONE);
if (Q.isProbablePrime(10)) // if p is prime,then break
break;
}
}
while (true) {
g = BigInteger.probablePrime(Q.bitLength() - 1, r);
if (!g.modPow(BigInteger.ONE, Q).equals(BigInteger.ONE)
&& !g.modPow(q, Q).equals(BigInteger.ONE)) {
break;
}
}
// Q = new BigInteger("122430513841");
// 选择随机的F和G作为密钥
BigInteger max = Q.divide(new BigInteger("2"));
boolean flag_exit = true;
F = (new BigDecimal(Math.random() + "").multiply(new BigDecimal(max)))
.toBigInteger();
// F = new BigInteger("231231");
while (flag_exit) {
if ((F.pow(2).subtract(max)).signum() < 0) {
flag_exit = false;
} else {
F = (new BigDecimal(Math.random() + "")
.multiply(new BigDecimal(F))).toBigInteger();
}
}
BigInteger min = Q.divide(new BigInteger("4"));
flag_exit = true;
G = (new BigDecimal(Math.random() + "").multiply(new BigDecimal(Q)))
.toBigInteger();
// G = new BigInteger("195698");
while (flag_exit) {
if (G.pow(2).subtract(max).signum() < 0
&& G.pow(2).subtract(min).signum() > 0
&& F.gcd(G).equals(BigInteger.ONE)) {
flag_exit = false;
} else if (G.pow(2).subtract(max).signum() > 0)// only 满足这个
{
G = (new BigDecimal(G)).multiply(
new BigDecimal(Math.random() + "")).toBigInteger();
} else if (G.pow(2).subtract(min).signum() < 0)// 仅仅满足这个
{
G = (new BigDecimal(G)).divide(
new BigDecimal(Math.random() + ""), 0).toBigInteger();
}else if(!F.gcd(G).equals(BigInteger.ONE))//保证F,G互素
{
G = (new BigDecimal(G)).multiply(
new BigDecimal(Math.random() + "")).toBigInteger();
}
}
// 计算公钥H
H = F.modInverse(Q).multiply(G).mod(Q);
BigInteger[] temp = new BigInteger[2];
temp[0] = Q;
temp[1] = H;
return temp;
}
public BigInteger encrypt(BigInteger m) {
BigInteger e;
BigInteger r = (new BigDecimal(F)).multiply(
new BigDecimal(Math.random() + "")).toBigInteger();
// r = new BigInteger("101010");
e = r.multiply(H).add(m).mod(Q);
return e;
}
public BigInteger decrypt(BigInteger e) {
BigInteger a = F.multiply(e).mod(Q);
// System.out.println(F.gcd(G));
BigInteger b = (F.modInverse(G).multiply(a)).mod(G);
return b;
}
public static void main(String args[]) {
CongruentialPublicKey cpk = new CongruentialPublicKey();
BigInteger[] PublicKey = cpk.generatorQandH();
System.out.println("Alice:随机产生的公钥(q,h):(" + PublicKey[0] + ","
+ PublicKey[1] + ")");
// Bob发送消息
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Bob:请输入要发送的明文:");
try {
String str;
BigInteger m = null;
while ((str = br.readLine()) != null) {
m = new BigInteger(str);
if (m.pow(2).subtract(PublicKey[0].divide(new BigInteger("2")))
.signum() < 0) {
break;
} else {
System.out.println("您输入的明文过大,请重新输入!");
}
}
System.out.println(cpk.decrypt(cpk.encrypt(m)));
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码运行结果如下:
(注明:本代码只针对一定范围内的大整数,对于较大的字符串,可以分段处理,笔者在这里不再讨论。有兴趣的读者可以自己尝试下实现,难度不大。)
下一篇,笔者将介绍另一个有趣的问题--背包公钥密码体制。
笔者水平有限,难免存在不足,欢迎批评交流!