公钥加密
公钥加密算法(Public-Key Cryptography)也称非对称加密算法(Asymetric Cryptography)。通常需要两个密钥,一个公开密钥和一个私有密钥。在密文加密过程中,利用公开密码对明文加密,只有用户利用私有密钥才能解密得到明文。针对公钥加密特性,一般公钥加密体制的应用主要分为三类:加密/解密、数字签名、密钥交换。
RSA 学习
RSA是由Ron Rivest、Adi Shamir、Leonard Adleman三人于1977年提出,并以三人名字的首字母命名。
RSA是一种非对称加密。
RSA思想及实现
Diffie-Hellman
Diffie-Hellman算法通常称为Diffie-Hellman密钥交换,其主要目的是使用户在没有预先消息的条件下能安全的交换密钥,以保证随后的通信的安全性。
本原根
在介绍Diffiie-Hellman之前,首先需要介绍本原根的概念。
定义1: 假设gcd(a,n)=1,若m是使得a^m≡1 mod n成立的最小正整数,则称m是a对模n的指数,记作Ordna。
定义2: 若Ordna=Φ(n),则称a是模n的本原根,也称作生成元。
定理: 模m存在本原根的必要条件是m=2,4,pa,2pa其中,p为奇素数。
素数p的本原根是一个整数,则可以使得
a
m
o
d
p
,
a
2
m
o
d
p
,
.
.
.
.
,
a
p
−
1
m
o
d
p
a mod p,a^2modp,....,a^{p-1}modp
amodp,a2modp,....,ap−1modp
能够产生1-(p-1)之间的所有整数,也就是说,他是一种将1-(p-1)中所有整数的置换。
算法概述
在Diffie-Hellman算法中,假设A和B两用户建立通信。
1.A和B共享一个素数q,α(α为q的本原根)。
2.A选择一个密钥XA,B选择一个私钥XB,其中,XA,XB<q。
3.A计算公钥YA=αXA mod q ,同样的,YB=αXB mod q。
4. A和B互换公钥。
5. A计算共享密钥K=(YB)XA mod q ,B计算共享密钥K=(YA)XB mod q
总结: 该算法通信过程中,公开了参数q,α,公钥YA,YB。
如果攻击者想要利用以上参数求知共享密钥,必须求得一个用户的私钥,如:
X
A
=
d
l
o
g
(
a
,
q
)
(
Y
A
)
X_A=dlog_{(a,q)}(Y_A)
XA=dlog(a,q)(YA)
由此可见,Diffie-Hellman的安全性建立在素数幂运算相对容易,而求离散对数十分困难的情况下。
ElGamal密码体制
EIGamal密码体制与Diffie-Hellman密钥分配体制密切相关,且主要应用于数据加密与数字签名中。
算法概述
密钥产生
1.系统用户A任选一个大素数p,使得p-1有大素数因子,g是模p的一个本原根,公开p和g。
2.A在任选一个私钥XA,x∈[0,p-1]。
3.计算YA=gXA mod p。
所以 A用户的私钥为 XA,公钥为{p,g,YA}
加密过程
B想要通过A的公钥向A发送信息
1.对于明文M,将M分为若干个密码块,其中每一个分块m的长度不大于整数p。
2.选取一个任意的整数r,r∈[0,p-1]
3.计算一次密钥K=(YA)rmod p。
4.将M加密成密文对(C1,C2),其中C1=gr mod p,C2=KM mod p。
解密过程
1.计算K=(C1)XAmod p恢复密钥。
2.利用密钥K解密密文,获得M=(C2K-1)mod p。
总结: EIGamal密码体制中密文由明文和随机数k来决定,因而是非确定性加密,对于同一明文不同时刻所选随机数k不同,便会产生不同的密文,其代价是数据扩展一倍。其安全性基础同样建立在离散对数求解。
EIGamal实现
public class EIGamal {
/*
* #### 密钥产生
1.系统用户A任选一个大素数p,使得p-1有大素数因子,g是模p的一个本原根,公开p和g。
2.A在任选一个私钥XA,x∈[0,p-1]。
3.计算YA=gXA mod p。
所以 A用户的私钥为 XA,公钥为{p,g,YA}
#### 加密过程
B想要通过A的公钥向A发送信息
1.对于明文M,将M分为若干个密码块,其中每一个分块m的长度不大于整数p。
2.选取一个任意的整数r,r∈[0,p-1]
3.计算一次密钥K=(YA)kmod p。
4.将M加密成密文对(C1,C2),其中C1=gx mod p,C2=KM mod p。
#### 解密过程
1.计算K=(C1)XAmod q恢复密钥。
2.利用密钥K解密密文,获得M=(C2K-1)mod p。
*
*/
static BigInteger p=null,g=null,Ya=null,q=null;
static BigInteger one = new BigInteger ("1");
static BigInteger two = new BigInteger ("2");
//First Step is to find a big prime num
public static void init() {
while(true) {
q= BigInteger.probablePrime(512, new Random());
p = q.multiply(two).add(one);
if(p.isProbablePrime(20))
break;
}
g=GetPrimordialRoot(p);
}
private static BigInteger GetPrimordialRoot(BigInteger p) {
BigInteger PrimordialRoot = new BigInteger("2");
int i = 1;
while(PrimordialRoot.compareTo(p.subtract(one))==-1) {
//前面已经设定 p = 2 * q+1,其中q为512位的素数,q也为素数。所以如果是模p的本原根,那么PrimordialRoot^(2||q)=1modp
if(PrimordialRoot.modPow(two, p).equals(one)||PrimordialRoot.modPow(q, p).equals(one))
PrimordialRoot=PrimordialRoot.add(one);
else
break;
}
if(PrimordialRoot.compareTo(p.subtract(one))==-1)
return PrimordialRoot;
else
return null;
}
public static void KeyGenerator() throws Exception {
StringBuffer key = new StringBuffer();
for(int i=0;i<256;i++) {
Random ran=new Random();
int x=ran.nextInt(10);
if(x>5)
key=key.append(1);
else
key=key.append(0);
}
//构造私钥
SecurityKey privateKey=new SecurityKey(new BigInteger(key.toString(),2),p,g);
//构造公钥对
SecurityKey publicKey=new SecurityKey(g.modPow(privateKey.getKey(), p),p,g);
Ya=g.modPow(new BigInteger(key.toString(),2), p);
String path=new File("").getCanonicalPath();
out(path+"\\publicKey.key",publicKey.getKey()+","+publicKey.getP()+","+publicKey.getG());
out(path+"\\privateKey.key",privateKey.getKey()+","+privateKey.getP()+","+privateKey.getG());
System.out.println("密钥存放在:"+path);
}
public static SecurityKey readKey(String path) throws Exception {
SecurityKey sk = null;
File f=new File(path);
FileReader fr= new FileReader(f);
BufferedReader br = new BufferedReader(fr);
String line = null;
//StringBuffer sb =new StringBuffer();
while((line=br.readLine())!=null) {
byte[] b = line.getBytes();
String[] key = new String(b,"utf-8").split(",");
//System.out.println(key.length);
if(key.length<3)
throw new Exception("文件错误");
sk=new SecurityKey(new BigInteger(key[0]),new BigInteger(key[1]),new BigInteger(key[2]));
br.close();
return sk;
}
return null;
}
//Encapsulated out stream
// Input: path(Filepath), val(the value)
public static void out (String path,String val) {
try {
FileWriter fw=new FileWriter(path);
BufferedWriter bw=new BufferedWriter(fw);
PrintWriter outs=new PrintWriter(bw);
outs.println(val);
outs.flush();
outs.close();
}
catch(Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
init();
KeyGenerator();
System.out.println("请输入明文:");
Scanner sc=new Scanner(System.in);
String str ="";
sc.useDelimiter("\n");
SecurityKey publicKey = readKey("G:\\WorkSpace\\WorkStation\\Encode\\Test\\publicKey.key");
if(sc.hasNext())
{
str=sc.next();
}
String c=encrypt(publicKey,str);
System.out.println("密文为:"+c);
System.out.println("***************************");
SecurityKey privateKey = readKey("G:\\WorkSpace\\WorkStation\\Encode\\Test\\privateKey.key");
String m = decrypt(privateKey,c);
System.out.println("明文为:"+m);
}
private static String decrypt(SecurityKey Privatekey, String data) throws UnsupportedEncodingException {
String ciphertext= data;
//分解密文
String[] CS=ciphertext.split(",");
String C=CS[0];
//获得每块的C1和C2
BigInteger C1=new BigInteger(C.split(":")[0]);
BigInteger C2=new BigInteger(C.split(":")[1]);
//在这里不能简单的用除法,而是乘以逆元
BigInteger m= C2.multiply(C1.modPow(Privatekey.getKey().negate(), Privatekey.getP())).mod(Privatekey.getP());
//System.out.println(m.toString(2));
byte[] c = m.toByteArray();
byte c2[] = new byte[c.length -1];
if(c[0]==0)
{
System.arraycopy(c, 1, c2, 0, c.length - 1);
}
return new String(c2);
}
private static String encrypt(SecurityKey Publickey,String str) throws Exception {
//System.out.println(str);
BigInteger M=ChangeBase.encode(str);
//System.out.println("M="+M);
StringBuffer C=new StringBuffer();
Random ran=new Random();
//k<p-1.我们这里取他的子集
int k=ran.nextInt(1000000000);
//计算c1
BigInteger C1=Publickey.getG().modPow(new BigInteger(k+""), Publickey.getP());
//计算c2
BigInteger C2=Publickey.getKey().modPow(new BigInteger(k+""), Publickey.getP()).multiply(M).mod(Publickey.getP());
C.append(C1+":"+C2+",");
return C.toString();
}
ElGamal 安全性
前面说到,ElGamal加密的难度取决于离散对数求解问题,目前,世界上已经有了一部分离散对数求解的重要研究成果。
小步大步算法
小步大步算法的思想主要来源于生日攻击。假定给定了一个素数p阶循环群G= <g>和h∈G,我们目标是在时间复杂度O(p1/2)找到a使得h=ga成立。
算法介绍:
1.令r=p1/2向下取整,并令a=a0+ra1(a0,a1∈[0,r])
2.所以h=ga可以化作hg-a0=ga-a0=gra1
3.列两列序列,一列为hg-a0,一列为gra1。
(1)小步序列:hg-a0,a0从1到r取遍。
(2)大步序列:gra1,a1从1到r取遍。
冷知识:序列类似g的等比数列,而公比g<gr,所以,叫做小步大步算法。
只要找到两个序列中相等的元素,h便可以轻松求解。
Diffie-Hellman与EIGamal的区别
唯一实质性的区别是,在ElGamal中,发送者使用临时密钥,而在Diffie-Hellman中,该密钥必须经过身份验证。
在Diffie-Hellman中,发送方和所有接收方必须在同一组中拥有密钥才能进行通信。在ElGamal中,密钥可以在任何组中,发送方不需要证书。
Diffie-Hellman中,发送方的身份必须是已知的,并且必须是正确的(避免中间人攻击),这样才能正确地打开密钥K。即便K可以被验证为正确的。然而,这并不意味着用K加密的消息是完整的;它可以通过多种方式进行修改。因此,它不能代替签名。但是密钥在ElGamal中是临时的,所以它没有受到这个限制。