Java8 Signature探秘

由于最近负责项目的license模块,对称加密、非对称加密、摘要加密、签名都大量用到,所以想写个系列博客探究下Java中各种加密算法的使用和注意事项,既然这样,那就先从签名开始吧!

No.1 签名是什么
  • 现实中,由于我们每个人的笔迹近似独一无二,所以一旦我们在文件中签字就无法抵赖说不是自己签的,因为对方可以做笔迹鉴定。

  • 计算机的世界更加错综复杂,A向B发送了一个文件,中途可能别拦截,然后可能被篡改或者替换,B怎么知道收到的文件一定是A发送的原件呢?

  • 为了解决这个问题,聪明的计算机大佬们发明了数字签名这么个东东,数字签名并不是说这个签名是一串数字,而是为了区分传统的手写签名。
  • A发送文件的同时也把这个文件的签名发送给B,B只要验证文件签名是正确的,就可以认为文件是A发送的而且是原件。

也就是说,签名可以让B很自信的确定收到的文件是否被 篡改替换

No.2 签名是怎么产生的
  • 我们先来看看签名是怎么生成的,简单来说有如下几步:

    1. 生成唯一的非对称加密公钥和私钥对,一般是RSA算法;
    2. 对要发送的文件计算摘要,一般是MD5算法;
    3. 使用私钥对摘要进行加密,得到签名
  • 为什么要计算摘要呢?主要是出于性能考虑,非对称加密安全性较好,但是不足之处是加密和解密过程复杂,如果要加密的字符很多多性能影响会很大。所以一般没有人会对整个问价进行加密生成签名,虽然这样也能达到签名的目的。

  • 为什么使用私钥加密?大部分情况下,我们使用公钥加密、私钥解密,但是签名却是使用私钥加密,公钥解密。这是因为如果用公钥加密,则必须把私钥发送给要验证签名的一方,不仅麻烦而且很危险;而使用私钥加密,公钥验证方可以很容易得到,不存在泄漏风险,而且由于发送的签名只是摘要,就算被拦截用公钥解密,得到的信息也只是一串字符,没有任何意义。

综上所示,签名的生成方生成签名,并把签名和公钥发送给验证方,验证方根据公钥解密得到摘要,然后对接收到的文件重新计算摘要,两个摘要一样就说明文件没有被替换和篡改。

No.3 签名 VS 篡改+替换
  • 为什么签名可以防止篡改呢?因为签名是通告摘要算法(MD5)产生的,任何一位数据的修改都会导致摘要发生变化,所以验证方只要判断签名解密后的摘要和文件计算的摘要一致,就说明文件未被篡改。

  • 为什么签名可以防止文件被整个替换呢?如果攻击者另外找一个假文件,自己生成假的签名呢?别忘了,因为签名生成方产生的公钥和私钥对是唯一的,而且公钥验证方可以很容易获取到,如果签名是伪造的,则解密会出现错误,或者解密得到的摘要和源文件摘要会大不相同。

综上所示,B通过验证签名就可以知道这个文件一定是A发送的,而且一定是A发送的原件,所以就可以无条件的信任这个文件啦~

No.4 Java签名生成与验证
import java.security.InvalidKeyException;
import java.security.KeyFactory;
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;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;  

public class SignatureUtil{  

    /**得到产生的私钥/公钥对 
     * @return KeyPair
     */  
    public static KeyPair getKeypair(){  
      //产生RSA密钥对(myKeyPair)  
        KeyPairGenerator myKeyGen = null;  
        try {  
            myKeyGen = KeyPairGenerator.getInstance("RSA");  
            myKeyGen.initialize(1024);              
        } catch (NoSuchAlgorithmException e) {             
            e.printStackTrace();  
        }  
        KeyPair myKeyPair = myKeyGen.generateKeyPair();  
        return myKeyPair;  
    }  
    /**根据私钥和信息生成签名 
     * @param privateKey 
     * @param data 
     * @return 签名的Base64编码
     */  
    public static String getSignature(PrivateKey privateKey,String data){  
        Signature sign; 
        String res = "";
        try {
            sign = Signature.getInstance("MD5WithRSA");
            sign.initSign(privateKey);
            sign.update(data.getBytes());
            byte[] signSequ = sign.sign();
            res = Base64.getEncoder().encodeToString(signSequ);
        }catch(NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return res;  
    }  

    /**验证签名 
     * @param publicKey 公钥的Base64编码 
     * @param sign 签名的Base64编码 
     * @param data 生成签名的原数据
     * @return 
     */  
    public static boolean verify(String publicKey, String sign, String data){  
        boolean res = true;
        try {
            byte[] keyBytes = Base64.getDecoder().decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicK = keyFactory.generatePublic(keySpec);

            Signature signature = Signature.getInstance("MD5withRSA");
            signature.initVerify(publicK);
            signature.update(data.getBytes());
            res = signature.verify(Base64.getDecoder().decode(sign));
        }catch(NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  
        return res;  
    }  


    public static void main(String[] args) {
        String data = "给我签名吧!";
        /*(1)生成公钥和私钥对*/
        KeyPair keyPair = getKeypair();
        String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
        String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
        System.out.println("公钥:" + publicKey);
        System.out.println("私钥:" + privateKey);
        /*(2)用私钥生成签名*/
        PrivateKey pk = keyPair.getPrivate();
        String signStr = getSignature(pk,data);  
        System.out.println("签名是:" + signStr);
        /*(3)验证签名*/
        System.out.println("验证签名的结果是:" + verify(publicKey,signStr,data));  
    }  
} 

运行上面代码得到如下结果:

公钥:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDElEXn3KJdoIAGhM3lDwnxm0a1w+G6WQpZrDkeBSJtarWEMXZ6+dF5aL+yGeXtHyCmo0X97mgfEwoe9ElK4u0iDb4zmPB9K8IC5+3K2+XJcQKdtSVO9AOp1SDxr5AT3kHHe+HBaknQdnqkb4dNc5nAUAXAK1FRTP0vgRAb9R+iSwIDAQAB
私钥:MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMSURefcol2ggAaEzeUPCfGbRrXD4bpZClmsOR4FIm1qtYQxdnr50Xlov7IZ5e0fIKajRf3uaB8TCh70SUri7SINvjOY8H0rwgLn7crb5clxAp21JU70A6nVIPGvkBPeQcd74cFqSdB2eqRvh01zmcBQBcArUVFM/S+BEBv1H6JLAgMBAAECgYAIQrrVTX49NPtsSrRkRceDMaU9Cig4Lnmy3vvfeRPDSVKrZXC3JjxZP7+eelwhJMe4eO/+BcC2XZR1TIqv7O1OCBjuI42hyEnqntCZ1PeSMtp3UQ3SmTEwo6X16qp4OPz4GQrlMGwnWhARGDZsXJdwWiUHAWwmC/DUYRiowkZQoQJBAPbwTVGowwqibEB2n1SDu0DyNSFxlJPhBAv+qgxWDbkSprDr/NY6nLf+0tOmXnTNP4MFuuFYkdkn4i60I+nZhekCQQDLyucpvdeyWSLzBJanalAll11H7iLzBmedBGN3UM763xXSrQ4Tp9s66ZW4LpdWMhvjvSvqrYDKwh+YKlgSy+ITAkBBrrliNxlqArn4i5TlzgRIyiQHuUZj7z48Uoi4r0sHJ0bfWGXwNbbp2gYJ9f654r46A5QpzH0+3bTz50aGNS3BAkEAxA2HJZkFEQa/oJshdB3KzN85ViG6baITu/Kk3fxXovFKxUrG6BHrzlk5N99aqAm82vL6dOJFrMnkKzdRU4PhEQJBANAiS9e2nU0r8m203zlti8Ee8Y+SqVzxhPDF3HjL1Zf9SxF0CNe67AABMk5VB9SnOGiuz5X862ZAiCtKJMi+ASs=
签名是:l55XNEvRwTo24mcEtpggsK6IPWOo11ar9wa7ZuKCbBen06wj3tOkBgZvzW5Rmw+bybr1lByhV1SC4X+LuFMVewf5aNOLseKlMBd7ERv2nUyfrxk7ymL9NtdJy/gKOreB/IK0joULHa/m6crjVCiiyls3MbNPfomvmDqH0VbmKYs=
验证签名的结果是:true
  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值