Schnorr签名(模指数)的实现java
- 1、算法描述
- 2、算法的实现(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)
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 pBigInteger ga = (p.subtract(BigInteger.ONE)).divide(q);// (p-1)/qg = a.modPow(ga, p); // a^ga mod p = 1 if(g.compareTo(BigInteger.ONE) != 0) // g!=1break;}// 存放公共参数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")); // 素数 qBigInteger p = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "p")); // 素数 pBigInteger 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 = ry = (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")); // 素数 pBigInteger g = new BigInteger(KeyPairOperate.getDataFromFile(PARAM_PATH, "g")); // q的原根 aBigInteger 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 -- yBigInteger 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 ptry {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) + "验证通过 !");elseSystem.out.println(user+ "的文件" + KeyPairOperate.getFileName(sourcefilePath) + "验证失败 !");} catch (Exception e1) {e1.printStackTrace();}}
}
** 注:如果需要完整一点的代码,可以在下面留下邮箱,我发送。**