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
Java 中,“signature”有不同层面的含义和用途: ### 方法签名(Method Signature) 方法签名是指方法的名称和参数列表(参数的类型、顺序和数量),不包含返回类型和访问修饰符。方法签名用于唯一标识类中的一个方法,在 Java 里,同一个类中不能有两个方法签名完全相同的方法。 ```java public class Example { // 方法签名: add(int, int) public int add(int a, int b) { return a + b; } // 方法签名: add(double, double) public double add(double a, double b) { return a + b; } } ``` 方法签名的用途主要在于方法重载(Overloading),通过定义多个具有相同名称但不同参数列表的方法,提高代码的可读性和可维护性。 ### 类签名(Class Signature) 类签名在泛型类和泛型方法的场景中使用,它描述了类或方法的泛型类型信息。类签名包含了泛型类型参数、边界等信息,编译器利用这些信息进行类型检查和类型擦除后的类型转换。 ```java // 类签名包含泛型类型参数 T public class GenericClass<T> { private T value; public GenericClass(T value) { this.value = value; } public T getValue() { return value; } } ``` 类签名的用途是支持泛型编程,使得代码可以处理不同类型的数据,同时保证类型安全。 ### 签名对象(Signature Class) 在 Java 的安全框架中,`java.security.Signature` 类用于数字签名和验证操作。数字签名是一种用于验证数据完整性、认证数据来源和防止数据被篡改的技术。 ```java import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.Signature; public class SignatureExample { public static void main(String[] args) throws Exception { // 生成密钥对 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); keyGen.initialize(2048); KeyPair keyPair = keyGen.generateKeyPair(); // 创建 Signature 对象 Signature dsa = Signature.getInstance("SHA256withDSA"); // 初始化签名 dsa.initSign(keyPair.getPrivate()); // 更新要签名的数据 String data = "Hello, World!"; dsa.update(data.getBytes()); // 生成签名 byte[] sig = dsa.sign(); // 初始化验证 dsa.initVerify(keyPair.getPublic()); dsa.update(data.getBytes()); // 验证签名 boolean verified = dsa.verify(sig); System.out.println("Signature verified: " + verified); } } ``` `Signature` 类的用途是实现数据的数字签名和验证,确保数据在传输和存储过程中的安全性和完整性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值