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();
		}
	}
}

** 注:如果需要完整一點的代碼,可以在下面留下郵箱,我發送。**

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