Schnorr签名java实现

Schnorr签名(模指数)的实现java

和ElGama数字签名一样,Schnorr数字签名方案也是基于离散对数。

Schnorr数字签名主要工作不依赖于消息,生成签名过程与消息相关的部分需要进行2n位长度的整数与n位长度的整数相乘。

1、算法描述

算法参数分析

该方案基于素数模p,且p-1包含大素数因子q,即 p-1≡0(mod q), p一般大约取 p=2^1024, q一般大约取 q=2^160,

p是1024位整数,q是160位整数,正好等于SHA-1中Hash值的长度。

具体算法流程

第一步生成公钥和私钥对,如下:

选择素数p和q,使得q是p-1的素因子
选择整数a,使 α^q=1 mod p, 使a,p,q 构成全局公钥参数,在用户组内的每个用户都可以取此值
选择随机整数s,0<s<q ,作为用户的私钥
计算 v=a^-s mod p, 作为用户的公钥 

签名

选择随机整数r,0<r<q, 并计算 x=a^r mod p, 该过程和待签名消息M无关
将x附在消息后面一起计算Hash值e, e=H(M||x)
计算 y=(r+ s*e) mod q , 签名包括(e,y)对

其他用户验证签名:

计算 x'= a^y * v^e mod p
验证是否 e=H(M||x')

对于该验证过程有:

x’≡a^y * v^e ≡a^y *a^-se ≡a^ y-se ≡ a^r ≡x mod p

所以 H(M||x’)=H(M||x)

schnorr

2、算法的实现(java)

/**
 * @author: mulming
 * @ClassName: SchnorrSignature
 * @date: 2020年6月16日 下午9:25:09
 * @Description:schnorr签名
 */
public class SchnorrSignature {
	/**
	 * @Author: mulming
	 * @Description: 系统初始化阶段,把初始化的密码公共参数存放到文件中去
	 * @param blq:选择的q的bit长度
	 * @Date:下午9:28:20
	 */
	public static void initPara(int blq) {
		File  file = new File(PARAM_PATH);
		if(file.exists()) {
			System.out.println("已经存在初始化参数,为不影响已经颁发的证书,如果你强制要重新产生参数,请备份所有文件到其他路径下,并重新生产密钥对并重新签名");
		}else {
			System.out.println("系统初始化中,生产公共参数... ...");
			BigInteger one = new BigInteger("1");
			BigInteger two = new BigInteger("2");
			BigInteger q, qp, p, a, g;
			int certainty = 100;
			SecureRandom sr = new SecureRandom();
			// blq长度的q, q是p-1的素因子 
			//生成BigInteger伪随机数,它可能是(概率不小于1 - 1/2certainty)一个具有指定 bitLength 的素数
			q = new BigInteger(blq, certainty, sr);
			qp = BigInteger.ONE;
			do { // 选择一个素数 p 
				p = q.multiply(qp).multiply(two).add(one);
				if(p.isProbablePrime(certainty))
					break;
				qp = qp.add(BigInteger.ONE);
			} while(true);
			
			while(true) {
				a = (two.add(new BigInteger(blq, 100, sr))).mod(p);// (2+x) mod p
				BigInteger ga = (p.subtract(BigInteger.ONE)).divide(q);// (p-1)/q
				g = a.modPow(ga, p); // a^ga mod p = 1 
				if(g.compareTo(BigInteger.ONE) != 0) // g!=1
					break;
			}
			// 存放公共参数
			List<String> transArryToLi = KeyPairOperate.transArryToLi(new String[] {"blq=" + blq,"q=" + q, "p=" + p, "g=" + g});
			KeyPairOperate.writePublicKeyToFile(PARAM_PATH, transArryToLi, false);
			System.out.println("...");
			System.out.println("初始化完成!");
		}
	}
	
	/**
	 * @Author: mulming
	 * @Description: 为用户生成公私钥对
	 * @param user:传入用户的身份
	 * @Return:void
	 * @Date:上午9:32:18
	 */
	public static void generateKeyForUser(String user) {
		File  file = new File(PERFIX_PATH + user + ".properties");
		if(file.exists()) {
			System.out.println(user + "已经颁发了密钥,请备份所有文件到其他路径下,并重新签名");
		}else {
			System.out.println("密钥颁发中,请稍后");
			System.out.println("... ...");
			BigInteger sk,pk;// 私钥和公钥
			int blq = Integer.parseInt(KeyPairOperate.getDataFromFile(PARAM_PATH, "blq"));
			SecureRandom sr = new SecureRandom();
			// 随机数作为私钥
			sk = new BigInteger(blq, sr);
			
			// 私钥的话名字命名
			List<String> toLiSK = KeyPairOperate.transArryToLi(new String[] {"sk=" + sk});
			KeyPairOperate.writePublicKeyToFile(PERFIX_PATH + user + ".properties", toLiSK, false);
			
			BigInteger g = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "g"));
			BigInteger p = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "p"));
			// 公钥
			pk = g.modPow(sk, p);// g^w mod p  -- 注意这儿是正,所以下面验证的时候是用的负
			List<String> toLiPK = KeyPairOperate.transArryToLi(new String[] {user + "=" + pk});
			KeyPairOperate.writePublicKeyToFile(PUBLIC_KEY_PATH, toLiPK, true);
			System.out.println(user + " 密钥颁发完成");
		}
	}
	
	/**
	 * @Author: mulming
	 * @Description: 产生签名
	 * @param sourcefilePath : 待签名文件路径
	 * @param user:用户身份
	 * @Date:下午10:41:37
	 */
	public static void makeSign(String sourcefilePath, String user) {
		System.out.println(user+ "的文件" + KeyPairOperate.getFileName(sourcefilePath) + " 签名开始");
		System.out.println("... ...");
		BigInteger q = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "q")); // 素数 q
		BigInteger p = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "p")); // 素数 p
		BigInteger g = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "g")); // q的原根 a
		
		// 私钥
		BigInteger sk = new BigInteger(KeyPairOperate.getDataFromFile(PERFIX_PATH + user + ".properties", "sk")); // 私钥
		
		SecureRandom sr = new SecureRandom();
		BigInteger r, x, e, y; 
		r = new BigInteger(q.bitLength(), sr); // 随机数
		x = g.modPow(r, p); // g^r mod p
		// e=H(M||x)
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			md5.update(Files.readAllBytes(Paths.get(sourcefilePath)));
			md5.update(x.toString().getBytes());
			byte[] digest = md5.digest();
			// e 将BigInteger的符号大小表示法转换成一个BigInteger值
			e = new BigInteger(1, digest); 
			// y s2 = r
			y = (r.subtract(sk.multiply(e))).mod(q);
			List<String> transArryToLi = KeyPairOperate.transArryToLi(new String[] {"e="+e,"y="+y});
			String fileName =PERFIX_PATH + user + "_sign_" + KeyPairOperate.getFileName(sourcefilePath) + ".properties";
			KeyPairOperate.writePublicKeyToFile(fileName, transArryToLi, false);
			System.out.println(user+ "的文件" + KeyPairOperate.getFileName(sourcefilePath) + "签名成功 !");
		} catch (Exception e1) {
			e1.printStackTrace();
		}
	}
	
	/**
	 * @Author: mulming
	 * @Description: 验证签名
	 * @param sourcePath : 原文件路径
	 * @param user : 用户名
	 * @Return:void
	 * @Date:上午11:07:04
	 */
	public static void checkSign(String sourcefilePath, String user) {
		System.out.println("验证签名");
		
		BigInteger p = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "p")); // 素数 p
		BigInteger g = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "g")); // q的原根 a
		
		BigInteger pk = new BigInteger(KeyPairOperate.getDataFromFile(PUBLIC_KEY_PATH, user));//  公钥
		
		String fileName =PERFIX_PATH + user + "_sign_" + KeyPairOperate.getFileName(sourcefilePath) + ".properties"; 
		
		BigInteger e = new BigInteger(KeyPairOperate.getDataFromFile(fileName, "e")); // e 签名信息1: 产生的签名信息
		BigInteger y = new BigInteger(KeyPairOperate.getDataFromFile(fileName, "y"));; // y 签名信息2: 加密后的消息
		
		// 计算的 x'
		BigInteger x1 = g.modPow(y, p); // g^y mod p -- y
		BigInteger x2 = (pk.modPow(e, p)).mod(p); // pk^e mod p 
		BigInteger x = x1.multiply(x2).mod(p); // x1*x2 mod p = (g^y)*(pk^e)mod p
		
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			md5.update(Files.readAllBytes(Paths.get(sourcefilePath)));
			md5.update(x.toString().getBytes());
			byte[] digest = md5.digest();
			BigInteger h = new BigInteger(1, digest);
			System.out.println("... ...");
			if(e.equals(h))
				System.out.println(user+ "的文件" + KeyPairOperate.getFileName(sourcefilePath) + "验证通过 !");
			else
				System.out.println(user+ "的文件" + KeyPairOperate.getFileName(sourcefilePath) + "验证失败 !");
		} catch (Exception e1) {
			e1.printStackTrace();
		}
	}
}

** 注:如果需要完整一点的代码,可以在下面留下邮箱,我发送。**

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章