公钥加密初学笔记

公钥加密

公钥加密算法(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,....,ap1modp
能够产生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计算公钥YAXA mod q ,同样的,YBXB mod q。
4. A和B互换公钥。
5. A计算共享密钥K=(YBXA mod q ,B计算共享密钥K=(YAXB 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=(YArmod p。
4.将M加密成密文对(C1,C2),其中C1=gr mod p,C2=KM mod p。

解密过程

1.计算K=(C1XAmod 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中是临时的,所以它没有受到这个限制。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值