eclipse實現Schnorr數字簽名
Schnorr數字簽名,供大家參考,具體內容如下
一、實驗目的
學習Schnorr演算法在數字簽名方面的使用,掌握公鑰簽名中最基礎的簽名演算法-Schnorr數字簽名演算法的編寫。
二、實驗要求
1.熟悉Schnorr演算法的描述,已經其使用場景。
2.熟悉Schnorr數字簽名演算法。
3.掌握如何使用java語言,實現Schnorr簽名演算法。
三、開發環境/p>
JDK1.8,eclipse。
四、實驗原理
數字簽名是指訊息傳送方利用特定引數產生的一段訊息碼,該訊息碼可以用來標識訊息傳送者真實身份,同時可以標識傳送的資料的完整性,所以在一定程度防止了傳送的資料在傳送過程中被攻擊者篡改的行為。簡而言之,數字簽名就是由訊息傳送者利用身份資訊與訊息相結合產生的一個訊息摘要。
在數字簽名過程中核心的兩個步驟就是產生簽名信息和對簽名信息的驗證,產生簽名就是訊息傳送方使用特定的簽名演算法將資料產生成訊息摘要,同時使用私鑰對其摘要進行加密,最後將加密後的密文和原始的資料一起傳送。訊息接收方收到資訊後,首先用傳送者的公鑰來算資料的摘要,然後把此結果與收到的摘要對比,如果一致,則驗證通過,認為該訊息是真實有效的。反之無效,丟棄該訊息。過程如下圖1所示。
a) 數字簽名加密過程
b) 數字簽名解密驗證過程
圖1 數字簽名過程
Schnorr簽名演算法是由德國數學家、密碼學家Claus Schnorr提出,是Elgamal簽名方案的變種。
具體步驟如下:
首先是生成公鑰/私鑰對,過程如下:
a.選擇素數 和 ,其中 是 的素因子;
b.選擇一個整數 ,使得 ; 、 和 構成全域性公鑰引數,在使用者組內的每個使用者都可以使用;
c.選擇隨機整數 作為使用者的私鑰,其中 ;
d.計算公鑰 ;
對於金鑰對為 的使用者,通過以下過程產生簽名:
a.選擇隨機整數,計算 ;
b.將 附在訊息後面,一起計算Hash值 :
c.計算。
簽名對為 ,其他使用者通過以為過程來驗證簽名:
a.計算;
b.驗證等式 是否成立。
程式碼段:
SchnorrSignature
import java.io.File; import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Paths; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.List; /** * @ClassName: SchnorrSignature * @date: 2020年6月16日 下午9:25:09 * @Description:schnorr簽名 */ public class SchnorrSignature { // 路徑字首 private static final String PERFIX_PATH = GetProjectPath.getPath() + "/ra/"; // 存放公共引數 private static final String PARAM_PATH = PERFIX_PATH + "initParams.properties"; // 存放公鑰的路徑 private static final String PUBLIC_KEY_PATH = PERFIX_PATH + "publicKey.properties"; /** * @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("初始化完成!"); } } /** * @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 + " 金鑰頒發完成"); } } /** * @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,false); System.out.println(user+ "的檔案" + KeyPairOperate.getFileName(sourcefilePath) + "簽名成功 !"); } catch (Exception e1) { e1.printStackTrace(); } } /** * @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,"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(); } } }
GetProjectPath:
import java.io.File; /** * @ClassName: GetProjectPath * @date: 2020年6月16日 下午10:58:53 * @Description: 獲取專案絕對路徑 */ public class GetProjectPath { public static String getPath() { File directory = new File(""); String courseFile = null; try { courseFile = directory.getCanonicalPath().replace("\\","/"); }catch (Exception e) { e.printStackTrace(); } return courseFile; } }
KeyPairOperate
import java.io.FileInputStream; import java.io.FileWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Properties; /** * @ClassName: KeyPairOperate * @date: 2020年6月16日 下午9:53:11 * @Description: 金鑰對的管理與儲存 */ public class KeyPairOperate { public static String getFileName(String path) { int indexOf = path.lastIndexOf("\\")+1; int last = path.lastIndexOf("."); return path.substring(indexOf,last); } /** * @Description: 工具類,陣列轉集合 * @param para * @return * @Return:List<String> * @Date:上午10:24:33 */ public static List<String> transArryToLi(String[] para){ List<String> li = new ArrayList<String>(); for(int i=0; i<para.length; i++) { li.add(para[i]); } return li; } /** * @Description: 獲取配置檔案引數 * @param path : 檔案路徑 * @param para : 獲取引數的key * @Date:上午9:46:26 */ public static String getDataFromFile(String path,String key) { String para = null; try { Properties pro = new Properties(); pro.load(new FileInputStream(path)); para = (String) pro.get(key); } catch (Exception e) { e.printStackTrace(); } return para; } /** * @Description:寫公共金鑰到檔案 -- 公鑰證書列表 * @param path : 儲存的路徑 * @param param : 引數 * @param flag : 是否是追加儲存,公共引數不是追加,公鑰是追加; 追加: true, 覆蓋: flase * @Return:void * @Date:上午10:20:25 */ public static void writePublicKeyToFile(String path,List<String> param,Boolean flag) { try { PrintWriter printWriter = new PrintWriter(new FileWriter(path,flag)); for(String element : param) { printWriter.println(element); } printWriter.close(); } catch (Exception e) { e.printStackTrace(); } } }
Shtest
import org.junit.Test; public class Shtest { String pathFile ="C:\\Users\\89763\\Desktop\\www.rtf"; @Test public void initPara() { SchnorrSignature.initPara(12); } @Test public void generateKeyForUser() { SchnorrSignature.generateKeyForUser("xiaoming"); SchnorrSignature.generateKeyForUser("xiaowang"); } @Test public void makeSign() { SchnorrSignature.makeSign(pathFile,"xiaoming"); SchnorrSignature.makeSign(pathFile,"xiaowang"); } @Test public void checkSign() { SchnorrSignature.checkSign( pathFile,"xiaoming"); SchnorrSignature.checkSign( pathFile,"xiaowang"); } }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。