欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数字签名应用

程序员文章站 2024-03-19 13:09:52
...
  • 引言

    数字签名可以确保文件数据的完整性以及不可抵赖性。本次将使用Java语言来实现对文件的数字签名及验证,Java语言的JDK提供了丰富的密码学类库。本次采用了椭圆曲线ECDSA数字签名算法及SHA256散列算法,也可以通过简单的参数选取,使用SHA或其他签名算法。

  • 环境及设备

    Windows计算机一台,Java虚拟机 JDK 1.6 及以上版本。

  • 步骤

    1. 将一个计算机中的文件中的所有内容读取到字节数组bytes中,需要保证计算机中存在这个文件。

      byte[] bytes = {};
      try {
      	//获取计算机中文件名 d:\\test.txt的路径
      	Path path = Paths.get("d:\\test.txt");
      			
      	//从文件读取内容到字节数组byte[]中
      	bytes = Files.readAllBytes(path);
      }catch(Exception e){
      	System.out.println("文件读取错误" + e);
      }
      
    2. 使用椭圆曲线签名算法,需要先得到椭圆曲线签名算法 EC 的生成**类 KeyPairGenerator 的一个实例 keyPairGen, 然后初始化 keyPairGen,对于椭圆曲线算法,**长度最低为112,生成一对**,其中包括了公钥和私钥,存入对象 pair 中,并以十六进制方式输出私钥的内容。

      //使用椭圆曲线签名算法,需要得到椭圆曲线算法的**生成一个实例keyPairGen
      KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
      		
      //初始化**对生成器,对椭圆曲线算法,参数最低为112
      keyPairGen.initialize(112);
      		
      //生成一对**,其中包括公钥和私钥,存入对象pair中;
      KeyPair pair = keyPairGen.generateKeyPair();
      		
      //从pair中获取私钥
      PrivateKey privKey = pair.getPrivate();
      		
      //输出私钥内容
      byte[] bytePrivKey = privKey.getEncoded();
      String strPrivKey = DatatypeConverter.printHexBinary(bytePrivKey);
      System.out.printf("私钥:0x%s\n",strPrivKey);
      
    3. 从对象pair中获取公钥,以十六进制方式输出公钥的内容。

      //从pair中获取公钥
      PublicKey pubKey = pair.getPublic();
      //输出公钥字节内容
      byte[] bytePubKey = pubKey.getEncoded();
      String strPubKey = DatatypeConverter.printHexBinary(bytePubKey);
      System.out.printf("公钥:0x%s\n",strPubKey);
      
    4. 创建一个签名对象 sign。使用 SHA256 算法作为散列函数,椭圆曲线签名算法 ECDSA 作为签名算法。用私钥 pairKry 来初始化签名对象 sign。然后使用签名对象 sign 的 update 方法加载需要签名的字节数组的内容 bytes,再使用 sign 方法生成签名;以十六进制字节方法打印 bytes 的签名值。

      //创建一个签名对象sign,使用SHA256算法作为散列函数,椭圆曲线签名算法ECDSA作为签名算法
      Signature sign = Signature.getInstance("SHA256withECDSA");
      		
      //用私钥privKey来初始化签名对象sign
      sign.initSign(privKey);
      		
      //签名对象sign加载需要签名的字节数组内容bytes
      sign.update(bytes);
      		
      //生成签名
      byte[] signature = sign.sign();
      		
      //以十六进制字节方式打印出bytes的签名值
      String strSign = DatatypeConverter.printHexBinary(signature);
      System.out.printf("签名内容:0x%s\n",strSign);
      
    5. 使用公钥 pubKey 对签名进行验证。创建一个签名对象 veriSign ,使用 SHA256 算法作为散列函数,椭圆曲线签名算法 ECDSA 作为签名算法。使用公钥 pubKey 来初始化签名对象 VeriSign ,签名对象VeriSign 加载需要验证签名的字节数组内容 bytes。VeriSign 使用公钥 pubKey 对 bytes 进行验证。

      //使用公钥pubKey对签名进行验证
      System.out.println("正在验证,请稍等------");
      		
      //创建一个签名对象veriSign,使用SHA256算法作为散列函数,椭圆曲线签名算法ECDSA作为签名算法
      Signature veriSign = Signature.getInstance("SHA256withECDSA");
      		
      //使用公钥pubKey来初始化签名对象VeriSign
      veriSign.initVerify(pubKey);
      		
      //签名对象veriSign加载需要验证签名的字节数组的内容bytes
      veriSign.update(bytes);;
      		
      //签名对象veriSign使用公钥pubKey对bytes的签名signature进行验证
      boolean ok = veriSign.verify(signature);
      if(ok)
      	System.out.println("验证成功,签名正确!");
      else
      	System.out.println("验证失败,签名不正确!");
      
    6. 若签名验证成功,则打印 “验证成功,签名正确!”,否则打印出 “验证失败,签名不正确!”。

  • 实现

    package digitalsignature;
    
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.Signature;
    import javax.xml.bind.DatatypeConverter;
    
    /** 
    * @author renhongchang
    * @version 创建时间:2021年5月31日 下午2:57:08 
    * @blog https://rhc-rgb.github.io
    * 
    * 随计算机中的一个文件使用椭圆曲线算法生成数字签名
    * 然后对该文件的数字签名进行验证
    */
    public class DigitalSignature {
    
    	public static void main(String[] args) throws Exception {
    		byte[] bytes = {};
    		try {
    			//获取计算机中文件名 d:\\test.txt的路径
    			Path path = Paths.get("d:\\test.txt");
    			
    			//从文件读取内容到字节数组byte[]中
    			bytes = Files.readAllBytes(path);
    		}catch(Exception e){
    			System.out.println("文件读取错误" + e);
    		}
    		
    		
    		//使用椭圆曲线签名算法,需要得到椭圆曲线算法的**生成一个实例keyPairGen
    		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
    		
    		//初始化**对生成器,对椭圆曲线算法,参数最低为112
    		keyPairGen.initialize(112);
    		
    		//生成一对**,其中包括公钥和私钥,存入对象pair中;
    		KeyPair pair = keyPairGen.generateKeyPair();
    		
    		//从pair中获取私钥
    		PrivateKey privKey = pair.getPrivate();
    		
    		//输出私钥内容
    		byte[] bytePrivKey = privKey.getEncoded();
    		String strPrivKey = DatatypeConverter.printHexBinary(bytePrivKey);
    		System.out.printf("私钥:0x%s\n",strPrivKey);
    		
    		
    		//从pair中获取公钥
    		PublicKey pubKey = pair.getPublic();
    		//输出公钥字节内容
    		byte[] bytePubKey = pubKey.getEncoded();
    		String strPubKey = DatatypeConverter.printHexBinary(bytePubKey);
    		System.out.printf("公钥:0x%s\n",strPubKey);
    		
    		
    		//创建一个签名对象sign,使用SHA256算法作为散列函数,椭圆曲线签名算法ECDSA作为签名算法
    		Signature sign = Signature.getInstance("SHA256withECDSA");
    		
    		//用私钥privKey来初始化签名对象sign
    		sign.initSign(privKey);
    		
    		//签名对象sign加载需要签名的字节数组内容bytes
    		sign.update(bytes);
    		
    		//生成签名
    		byte[] signature = sign.sign();
    		
    		//以十六进制字节方式打印出bytes的签名值
    		String strSign = DatatypeConverter.printHexBinary(signature);
    		System.out.printf("签名内容:0x%s\n",strSign);
    		
    		//使用公钥pubKey对签名进行验证
    		System.out.println("正在验证,请稍等------");
    		
    		//创建一个签名对象veriSign,使用SHA256算法作为散列函数,椭圆曲线签名算法ECDSA作为签名算法
    		Signature veriSign = Signature.getInstance("SHA256withECDSA");
    		
    		//使用公钥pubKey来初始化签名对象VeriSign
    		veriSign.initVerify(pubKey);
    		
    		//签名对象veriSign加载需要验证签名的字节数组的内容bytes
    		veriSign.update(bytes);;
    		
    		//签名对象veriSign使用公钥pubKey对bytes的签名signature进行验证
    		boolean ok = veriSign.verify(signature);
    		if(ok)
    			System.out.println("验证成功,签名正确!");
    		else
    			System.out.println("验证失败,签名不正确!");
    	}
    }
    
  • 实现结果

    私钥:0x302C020100301006072A8648CE3D020106052B8104000604153013020101040EAE0831C7D9E7F76ADF8A0B08D1DF
    公钥:0x3032301006072A8648CE3D020106052B81040006031E0004050A0BCE147FF9BFDDA2FD06BD748B983B600DD605296822BD59E317
    签名内容:0x3020020E3CBFF953DC7C15041DDC01A56704020E51949B5868C2123534E39ABA2F03
    正在验证,请稍等------
    验证成功,签名正确!