Schnorr数字签名,供大家参考,具体内容如下
一、实验目的
学习Schnorr算法在数字签名方面的使用,掌握公钥签名中最基础的签名算法-Schnorr数字签名算法的编写。
二、实验要求
1. 熟悉Schnorr算法的描述,已经其使用场景。
2. 熟悉Schnorr数字签名算法。
3. 掌握如何使用java语言,实现Schnorr签名算法。
三、开发环境
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, transArryToLi, 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, "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(); } } }
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"); } }
以上代码均来自https://blog.csdn.net/qq_27731689/article/details/106828368 作者:黎明小书生
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持易盾网络。