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

密码学研究-数字签名

程序员文章站 2022-04-06 23:03:51
引入: 提到签名,大家都不陌生,大家知道,重大的文件一般都要领导签名,来确保这个文件的真实有效。而一些比较重要的合同,比如买房的购房合同,都要盖“骑缝章”...
引入:

提到签名,大家都不陌生,大家知道,重大的文件一般都要领导签名,来确保这个文件的真实有效。而一些比较重要的合同,比如买房的购房合同,都要盖“骑缝章”,这个骑缝章,就是盖在2页纸中间的印章,它也代表了签名,它用来保证你合同的完整性。所以说,签名在日常生活中非常重要,它主要用来保证了信息的完整性。同样,计算机世界也对这个签名过程进行了模拟,数字签名的概念由此而生。

 

数字签名的过程:

一般数字签名的过程分为2个,一个是签名过程,一个是验证过程。

 

数字签名的基本过程如下:

密码学研究-数字签名

(1)发送方用指定的散列函数作用于原始报文,计算出原始报文的原始摘要。

(2)发送方选用非对称的密钥中的私钥对原始摘要进行加密,得到加密后的原始摘要

(3)发送方构造一个报文(SignedObject对象),将原始报文添加在其中

(4)发送方将第(2)步产生的加密后的原始摘要也添加到第(3)步构建的报文中。

这个报文通过不可信任的网络传输到接收端。

(5)接受端从报文中取出原始报文,用约定的散列函数计算出摘要值,记为D1。

(6)接受端从报文中取出加密后的原始摘要,用公钥解密,还原出摘要值,记为D2。

(7)接收端比较D1和D2的值,如果相同,那么认为这个原始报文是可靠的。

 

 

实践:

我们java.security包提供了一组API 来表示数字签名的过程,这个核心类就是Signature类,它提供了一组方法来签名和验证,我们这里就给出这个类的一般用法:

 

我们先建立一个工具类,这个类是个单例,它主要是提供了一些封装方法来封装签名和验证的过程,因为签名和验证都需要公钥-私钥对,所以它包含了这对钥匙的产生逻辑。

 

package com.charles.signaturestudy;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.SignedObject;
/**
 *
 * Description: 这个工具类提供了一组方法来操作Signature类,它主要可以对签名进行一些操作
 *
 * @author charles.wang
 * @created Oct 28, 2013 11:11:52 AM
 *
 */
public class SignatureUtil {
    private static SignatureUtil instance = null;
    //公钥私钥对
    private KeyPair keyPair = null;
    //数字签名类
    private Signature signature = null;
    /**
     * 私有构造器,用指定的算法来初始化Signature类
     * @param algorithm
     */
    private SignatureUtil(String algorithm) {
        try {
            // 实例化KeyPairGenerator对象,并且指定算法为DSA
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
            // 初始化 KeyPairGenerator对象
            keyPairGenerator.initialize(1024);
            // 生成Keypair对象
            keyPair = keyPairGenerator.generateKeyPair();
            // 实例化Signature对象,这个对象提供一组动作方法类操作签名
            signature = Signature.getInstance(keyPairGenerator.getAlgorithm());
        } catch (NoSuchAlgorithmException ex) {
            keyPair = null;
            signature = null;
        }
    }
    /**
     * 单例的工厂方法,用于创建SignatureUtil的实例
     *
     * @return
     */
    public static SignatureUtil getInstance(String algorithm) {
        if (instance == null)
            instance = new SignatureUtil(algorithm);
        return instance;
    }
    /**
     * 用私钥对指定的原始数据进行签名
     * @param data 被签名的数据
     * @return
     */
    public byte[] signWithPrivateKey(byte[] data) {
        try {
            // 私钥完成签名,所以这里用私钥初始化用于签名的Signature
            signature.initSign(keyPair.getPrivate());
            // 更新要签名的原始数据
            signature.update(data);
            // 返回签名的内容
            return signature.sign();
        } catch (InvalidKeyException ie) {
            ie.printStackTrace();
            return null;
        } catch (SignatureException se) {
            se.printStackTrace();
            return null;
        }
    }
    /**
     * 用公钥对指定的原始数据和签名进行验证
     * @param data
     * @param sign
     * @return
     */
    public boolean verifySignedObjectWithPublicKey(byte[] data, byte[] sign) {
        try {
            // 公钥完成验证,所以这里用公钥初始化用于验证的Signature
            signature.initVerify(keyPair.getPublic());
            // 更新要验证的原始数据
            signature.update(data);
            // 验证签名,获得验证结果
            return signature.verify(sign);
        } catch (InvalidKeyException ie) {
            ie.printStackTrace();
            return false;
        } catch (SignatureException se) {
            se.printStackTrace();
            return false;
        }
    }
                                                                                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                     
}

然后我们提供了一个演示类,它的过程如下:

先给出原始数据,再用我们的API对其进行数字签名(sign),并且打印出数字签名的内容,然后我们用我们的API来验证(verify)数字签名的有效性。

 

package com.charles.signaturestudy;
/**
 *
 * Description: 这个类用来演示数字签名的使用方法
 *
 * @author charles.wang
 * @created Oct 28, 2013 10:37:32 AM
 *
 */
public class SignatureDemo {
    public static void main(String [] args) throws Exception{
                                                                                                                                                                                                                                                                        
        //SignatureUtil是我们开发的一组动作类,它对于Signature类进行了进一步的封装
        //它提供了我们可以对数字签名完成的一组动作
        SignatureUtil sigUtil = SignatureUtil.getInstance("DSA");
                                                                                                                                                                                                                                                                        
        //原始数据对象
        String content="被测试的原始数据对象";
                                                                                                                                                                                                                                                                        
        //打印出原始数据
        System.out.println("原始数据为:"+content);
                                                                                                                                                                                                                                                                        
        //将原始数据对象转为字节数组
        byte[] rawData = content.getBytes();
                                                                                                                                                                                                                                                                        
        System.out.println("\n开始对原始数据签名...");
        //进行签名,返回签名的内容
        byte[] sign = sigUtil.signWithPrivateKey(rawData);
        System.out.println("签名内容(16进制)为:"+byte2hex(sign));
                                                                                                                                                                                                                                                                            
        System.out.println("\n开始对签名内容验证...");
        //进行验证,对验证结果进行分析
        boolean status = sigUtil.verifySignedObjectWithPublicKey(rawData, sign);
                                                                                                                                                                                                                                                                        
        if(status==true){
            System.out.println("验证结果,此签名是有效的");
        }else{
            System.out.println("验证结果,此签名是无效的");
        }
                                                                                                                                                                                                                                                                        
    }
                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                    
    /**
     * 将二进制转为字符串的形式
     *
     * @param b
     * @return
     */
    protected static String byte2hex(byte[] b) // 二行制转字符串
    {
        // 最终要转化的16进制字符串
        StringBuilder hexString = new StringBuilder();
        // 处理每个转化的当前字符串
        String tmpStr = "";
        for (int n = 0; n < b.length; n++) {
            // 将二进制转为16进制
            tmpStr = (Integer.toHexString(b[n] & 0XFF));
            // 如果当前转成的字符串只有一位长度的话,则前面补0,然后加上当前转换值
            if (tmpStr.length() == 1) {
                hexString.append("0").append(tmpStr);
            }
            // 否则,,则直接将当前转换值tmpStr附加在hexString后面
            else
                hexString.append(tmpStr);
            // 如果没有到byte[]的尾部,则中间用冒号分开,最后一个后面不用加冒号
            if (n < b.length - 1)
                hexString.append(":");
        }
        return hexString.toString().toUpperCase();
    }
}

 

 

最后,我们运行这个实例程序,然后可以清晰的看出以上的过程:

密码学研究-数字签名