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, 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");
 }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2020-06-22

Java数字签名算法DSA实例详解

本文实例讲述了Java数字签名算法DSA.分享给大家供大家参考,具体如下: 一.介绍 DSS:Digital Signature Standard 数字签名标准 DSA:Digital Signature Algorithm 数字签名算法 DSA仅包含数字签名 二.参数说明 三.代码实现 package com.imooc.security.dsa; import java.security.KeyFactory; import java.security.KeyPair; import jav

解决在eclipse中将android项目生成apk并且给apk签名的实现方法详解

生成apk最懒惰的方法是:只要你运行过android项目,到工作目录的bin文件夹下就能找到与项目同名的apk文件,这种apk默认是已经使用debug用户签名的.如果想要自己给apk签名:1.签名的意义为了保证每个应用程序开发商合法ID,防止部分开放商可能通过使用相同的Package Name来混淆替换已经安装的程序,我们需要对我们发布的APK文件进行唯一签名,保证我们每次发布的版本的一致性(如自动更新不会因为版本不一致而无法安装).2.签名的步骤a.创建keyb.使用步骤a中产生的key对ap

常用数字签名算法RSA与DSA的Java程序内实现示例

RSA加密算法 我们来回顾一下RSA的加密算法.我们从公钥加密算法和签名算法的定义出发,用比较规范的语言来描述这一算法. RSA公钥加密体制包含如下3个算法:KeyGen(密钥生成算法),Encrypt(加密算法)以及Decrypt(解密算法). 密钥生成算法以安全常数作为输入,输出一个公钥PK,和一个私钥SK.安全常数用于确定这个加密算法的安全性有多高,一般以加密算法使用的质数p的大小有关.越大,质数p一般越大,保证体制有更高的安全性.在RSA中,密钥生成算法如下:算法首先随机产生两个不同大质

使用数字签名实现数据库记录防篡改(Java实现)

本文大纲 一.提出问题 二.数字签名 三.实现步骤 四.参考代码 五.后记 六.参考资料 一.提出问题 最近在做一个项目,需要对一个现成的产品的数据库进行操作,增加额外的功能.为此,需要对该产品对数据库有什么操作进行研究(至于怎么监控一个产品的操作会引发什么数据库操作,以后会详细解说).本来已经对数据库的操作了如指掌的,无意中发现数据库表里的每条记录都会有这样一个字段: 这感觉不妙了,字段名叫signature,顾名思义,就是签名的意思呀.难道数据库表中的每条记录都会有签名?也就是说如果我不能正

Android APK使用Debug签名重新打包 Eclipse更改默认Debug签名

对已有的apk文件进行重新打包,前面 Android签名机制:生成keystore.签名.查看签名信息 已经介绍了.本文介绍另外两种需求. 使用默认的Debug签名打包 如果直接使用Eclipse开发新程序,默认就会被打上Debug的签名,这个是地球人皆知,但是如果你是想Repack别人并且想使用Debug签名该怎么办? 首先需要找到你本机的默认签名的,一般位于C:\Users\用户名\.android\debug.keystore下,拷出来,然后打包,打包命令: 复制代码 代码如下: jars

Java2下Applet数字签名

我的项目是使用APPLET制作一个实时消息队列监控程序,由于涉及到了本地资源,对APPLET一定要进行数字签名和认证.我使用的环境是WINDOWS2000,应用服务器是WEBLOGIC6.0,开发环境是JBUILDER4.0.之前我提醒大家一定要注意服务器端和客户端的概念.那些文件应该在服务器端,那些文件应该在客户端. 首先在客户端使用JRE1.3.0_01(JAVA运行环境1.3.0.1版本)以取代IE的JVM(JAVA虚拟机),可以到WWW.JAVA.SUN.COM网站上去下载,下载好了先在

Eclipse下配置Ant脚本并自动打包带签名的Android apk

eclipse虽然用的比较少,但是在一些古老的项目上还会用到滴,其中最麻烦的事情就是打带签名包的问题,不太方便,下面通过本文给大家记录下配置ant,自动打包带签名apk的过程,作为备份.(PC环境为MAC) 1,第一步得安ant,下载对应安装包,解压后配置环境变量: export ANT_HOME="/Users/yanzi/work/apache-ant-1.9.4" export PATH=${PATH}:${ANT_HOME}/bin 通过which ant检查是否安装成功. 2

Java加密和数字签名编程

本文主要谈一下密码学中的加密和数字签名,以及其在java中如何进行使用.对密码学有兴趣的伙伴,推荐看Bruce Schneier的著作:Applied Crypotography.在jdk1.5的发行版本中安全性方面有了很大的改进,也提供了对RSA算法的直接支持,现在我们从实例入手解决问题(本文仅是作为简单介绍): 一.密码学上常用的概念 1)消息摘要: 这是一种与消息认证码结合使用以确保消息完整性的技术.主要使用单向散列函数算法,可用于检验消息的完整性,和通过散列密码直接以文本形式保存等,目前

Java实现的数字签名算法RSA完整示例

本文实例讲述了Java实现的数字签名算法RSA.分享给大家供大家参考,具体如下: 一 背景介绍 数字签名:带有密钥(公钥.私钥)的消息摘要算法. 验证数据完整性.认证数据来源.抗否认. 私钥签名.公钥验证. 常用算法:RSA.DSA.ECDSA 二 RSA介绍 包括MD和SHA两类 三 Java代码实现 package com.imooc.security.rsa2; import java.security.KeyFactory; import java.security.KeyPair; i

Java加密解密和数字签名完整代码示例

常见的加密算法 基本的单向加密算法: BASE64严格地说,属于编码格式,而非加密算法 MD5(MessageDigestalgorithm5,信息摘要算法) SHA(SecureHashAlgorithm,安全散列算法) HMAC(HashMessageAuthenticationCode,散列消息鉴别码) 复杂的对称加密(DES.PBE).非对称加密算法: DES(DataEncryptionStandard,数据加密算法) PBE(Password-basedencryption,基于密码

java加解密RSA使用方法代码示例

最近为了分析一段请求流,不得不去研究一下RSA加密. 首先,强调一点:密钥的"钥"读"yue",不是"yao",额... 网上关于RSA的原理一抓一大把的,这里只是简单说说我的理解: 1. 两个足够大的互质数p, q: 2. 用于模运算的模 n=p*q: 3. 公钥KU(e, n)中的e满足 1<e< (p-1)(q-1),且与(p-1)(q-1)互质: 4. 密钥KR(d, n)中的d满足  d*e % (p-1)(q-1)= 1,

java算法实现红黑树完整代码示例

红黑树 定义 红黑树(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组. 红黑树的另一种定义是含有红黑链接并满足下列条件的二叉查找树: 红链接均为左链接:没有任何一个结点同时和两条红链接相连:该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同. 满足这样定义的红黑树和相应的2-3树是一一对应的. 旋转 旋转又分为左旋和右旋.通常左旋操作用于将一个向右倾斜的红色链接旋转为向左链接.对比操作前后,可以看出,该操作

java定时器timer的使用方法代码示例

1.首先肯定是容器一启动就要启动定时器,所以我们可以选择把定时器写在一个监听器里,容器一启动所以监听器也就跟着启动,然后定时器就可以工作了. 第一步,把自己写的监听器加到web.xml中: 第二步,写一个监听器,实现ServletContextListener接口: 第三步,写一个定时器,继承TimerTask,在复写的run()方法里写具体的业务逻辑. 第四步,在自己的监听器里复写的 public void contextInitialized(ServletContextEvent arg0

Java逃逸分析详解及代码示例

概念引入 我们都知道,Java 创建的对象都是被分配到堆内存上,但是事实并不是这么绝对,通过对Java对象分配的过程分析,可以知道有两个地方会导致Java中创建出来的对象并一定分别在所认为的堆上.这两个点分别是Java中的逃逸分析和TLAB(Thread Local Allocation Buffer)线程私有的缓存区. 基本概念介绍 逃逸分析,是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法.通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的

Java实现生成Excel树形表头完整代码示例

本文主要分享了Java实现生成Excel树形表头完整代码示例,没有什么好解释的,直接看看代码过程. 源数据格式: String[] targetNames = { "指标名称", "单位", "xx_yy1", "xx_yy2_zz1", "xx_yy2_zz2", "2017年5月_主营业务收入_累计", "2017年5月_主营业务收入_同比", "201

java通过JFrame做一个登录系统的界面完整代码示例

在java的JFrame内通过创建匿名对象的方式做登录界面 package com.sxt; import java.awt.Container; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.J

Java网络编程之TCP通信完整代码示例

一.概述 Socket类是Java执行客户端TCP操作的基础类,这个类本身使用代码通过主机操作系统的本地TCP栈进行通信.Socket类的方法会建立和销毁连接,设置各种Socket选项. ServerSocket类是Java执行服务器端操作的基础类,该类运行于服务器,监听入站TCP连接,每个socket服务器监听服务器的某个端口,当远程主机的客户端尝试连接此端口时,服务器就被唤醒,并返回一个表示两台主机之间socket的正常Socket对象. 二.什么是TCP? TCP是一种面向连接的.可靠的.

Java中filter用法完整代码示例

本文研究的主要是Java中filter过滤器的相关用法,具体实现代码如下. filter过滤器主要使用于前台向后台传递数据是的过滤操作.程度很简单就不说明了,直接给几个已经写好的代码: 一.使浏览器不缓存页面的过滤器 import javax.servlet.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 用于的使 Browser 不缓存页面的过滤器 */ public cla

Java语言实现Blowfish加密算法完整代码分享

前几天网上突然出现流言:某东发生数据泄露12G,最终某东在一篇声明中没有否认,还算是勉强承认了吧,这件事对于一般人有什么影响.应该怎么做已经有一堆人说了,所以就不凑热闹了,咱来点对程序猿来说实际点的,说一个个人认为目前比较安全的加密算法:Blowfish. 上代码之前,先说几点Blowfish加密算法的特点: 1. 对称加密,即加密的密钥和解密的密钥是相同的: 2. 每次加密之后的结果是不同的(这也是老夫比较欣赏的一点): 3. 可逆的,和老夫之前的文章介绍的md5等摘要算法不一样,他是可逆的: