Java安全之数字签名

Java安全之数字签名

数字签名是一种安全措施,分为:消息摘要和消息签名。

概念:

1、消息摘要:是一种算法,分为MD5/SHA算法,无论原始数据多长,消息摘要的结果都是固定长度的;原始数据任意bit位的变化,都会导致消息摘要的结果有很大的不同,且根据结果推算出原始数据的概率极低。消息摘要可以看作原始数据的指纹,指纹不同则原始数据不同。主要作用用来防止消息在传递途中被“第三者”篡改了。

2、消息签名:其基础是公钥和私钥的非对称加密,发送者使用私钥加密消息摘要,接收者使用公钥解密消息摘要以验证签名是否是某个人的。主要作用是验证发消息者的身份,确保消息来源的可靠性。

图解:

1、鲍勃有两把钥匙,一把是公钥,另一把是私钥。

这里写图片描述

2、鲍勃把公钥送给他的朋友们—-帕蒂、道格、苏珊—-每人一把。

这里写图片描述

3、鲍勃给苏珊写信,决定采用 “数字签名”。他写完后先用Hash函数,生成信件的摘要(digest)

这里写图片描述
这个过程用到的哈希算法主要有MD5算法和SHA算法。

4、然后,鲍勃使用私钥,对这个摘要加密,生成”消息签名”(signature)。

这里写图片描述

5、鲍勃将这个签名,附在信件下面,一起发给苏珊。

这里写图片描述

6、苏珊收信后,取下消息签名,用鲍勃的公钥解密,得到信件的摘要。由此证明,这封信确实是鲍勃发出的。

这里写图片描述

7、苏珊再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。

这里写图片描述

这样一个完整过程就完成了对消息来源者身份的确定和对消息内容安全性的验证。

但是,这样并不能保证绝对安全,假如,道格想欺骗苏珊,他偷偷使用了苏珊的电脑,用自己的公钥换走了鲍勃的公钥。此时,苏珊实际拥有的是道格的公钥,但是还以为这是鲍勃的公钥。因此,道格就可以冒充鲍勃,用自己的私钥做成”数字签名”,写信给苏珊,让苏珊用假的鲍勃公钥进行解密。解决这个问题就涉及到认证,这里我们先不做谈论。

示例代码:

1、生成消息摘要的代码:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Digest {

    private static String[] arg={"D:\\英文\\java\\JavaInterview\\阿房宫赋.txt","SHA-1","MD5"};
    public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
        // TODO Auto-generated method stub
        String sha=arg.length>=2?arg[1]:"SHA-1";
        String md5=arg.length>=3?arg[2]:"MD5";

        MessageDigest alg1=MessageDigest.getInstance(sha);
        MessageDigest alg2=MessageDigest.getInstance(md5);

        byte[] input=Files.readAllBytes(Paths.get(arg[0]));

        byte[] hash1=alg1.digest(input);
        byte[] hash2=alg2.digest(input);

        String d1="";
        for(int i=0;i<hash1.length;i++){
            int v=hash1[i]&0xFF;
            if(v<16)  d1+="0";
            d1+=Integer.toString(v,16).toUpperCase()+"";
        }
        String d2="";
        for(int i=0;i<hash2.length;i++){
            int v=hash2[i]&0xFF;
            if(v<16)  d2+="0";
            d2+=Integer.toString(v,16).toUpperCase()+"";
        }
        System.out.println("SHA-1算法生成的消息摘要:  "+d1);
        System.out.println("MD5算法生成的消息摘要:  "+d2);
    }

}

运行结果:

SHA-1算法生成的消息摘要:  42109004CF53C57A5667D94420063D1D5B762DB1
MD5算法生成的消息摘要:  FD0B8405534503F587F22FD73038E03B

从结果可以看出,SHA-1算法生成的消息摘要要稍微长一些,安全新也自然高一些,而且目前MD5算法被找到了一些规律,可靠性更差了,一般推荐使用SHA-1算法。

2、消息签名的代码:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;

public class SignatureTest {

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, ClassNotFoundException, IOException {
        // 通用处理方式
        KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance("RSA");

        KeyPair keyPair=keyPairGenerator.generateKeyPair();

        PrivateKey privateKey=keyPair.getPrivate();
        PublicKey publicKey=keyPair.getPublic();

        Signature signature=Signature.getInstance("MD5withRSA");

        signature.initSign(privateKey);

        byte[] src=new byte[]{1,2,3,4,5,6,7};

        signature.update(src);

        byte[] signed=signature.sign();

        signature.initVerify(publicKey);

        signature.update(src);

        System.out.println(signature.verify(signed));

        // 保存原始数据,签名,公钥,秘钥
        FileOutputStream foutsrc=new FileOutputStream("src.txt");
        FileOutputStream foutsigned=new FileOutputStream("signed.dat");
        foutsrc.write(src);
        foutsigned.write(signed);
        foutsigned.close();
        foutsrc.close();

        FileOutputStream outPublicKey=new FileOutputStream("public.key");
        ObjectOutputStream objOut=new ObjectOutputStream(outPublicKey);
        objOut.writeObject(publicKey);
        objOut.close();
        outPublicKey.close();

        FileOutputStream outprivateKey=new FileOutputStream("private.key");
        ObjectOutputStream objOut1=new ObjectOutputStream(outprivateKey);
        objOut1.writeObject(privateKey);
        objOut1.close();
        outprivateKey.close();


        // 流数据的特殊处理
        sign();
        verify();

    }

    private static void verify() throws IOException, ClassNotFoundException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {

        FileInputStream fisPublicKey=new FileInputStream("public.key");
        ObjectInputStream oisPublicKey=new ObjectInputStream(fisPublicKey);
        PublicKey publicKey=(PublicKey) oisPublicKey.readObject();
        oisPublicKey.close();
        fisPublicKey.close();

        Signature signature=Signature.getInstance("MD5withRSA");
        signature.initVerify(publicKey);
        FileInputStream fis=new FileInputStream("src.txt");
        byte[] src=new byte[1024];
        int len=0;
        while((len=fis.read(src))!=-1){
            signature.update(src,0,len);
        }
        fis.close();

        FileInputStream fisSigned=new FileInputStream("signed.dat");
        byte[] signed=new byte[fisSigned.available()];
        int readed=0;
        int total=0;
        System.out.println(signed.length);

        while(total<signed.length){
            // 此方法的第三个参数 len 长度不能大于 b.length-off,并且 len 等于 0 时 直接返回,所以不能用 while(readed!=-1)进行判断
            readed=fisSigned.read(signed,total,signed.length-total);
            total+=readed;
        }

        fisSigned.close();
        System.out.println(signature.verify(signed));

    }

    private static void sign() throws IOException, ClassNotFoundException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {

        FileInputStream fisPrivateKey=new FileInputStream("private.key");
        ObjectInputStream oisPrivateKey=new ObjectInputStream(fisPrivateKey);
        PrivateKey privateKey=(PrivateKey) oisPrivateKey.readObject();
        oisPrivateKey.close();
        fisPrivateKey.close();

        Signature signature=Signature.getInstance("MD5withRSA");
        signature.initSign(privateKey);
        FileInputStream fis=new FileInputStream("src.txt");
        byte[] src=new byte[1024];
        int len=0;
        while((len=fis.read(src))!=-1){
            signature.update(src,0,len);
        }
        fis.close();

        byte[] signed=signature.sign();
        System.out.println(signed.length);
        FileOutputStream fos=new FileOutputStream("signed.dat");
        fos.write(signed);
        fos.close();

    }

}

运行结果:

true
128
128
true

本示例代码中,展现了两种方式的签名,第一种是通用方式:直接对已知的内容进行加密;第二种方式是通过数据流的方式,对读取的文件中的内容进行加密,加密完成之后再存入文件,然后进行文件传输,接收端又读取接收文件,再进行解密验证。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值