数字签名在数据的交互中一直都占据着很重要的地位,因此,这篇文章对其原理进行整理总结一下。最后再给出代码的实现。

一、简单认识

相信我们都写过信,在写信的时候落款处总是要留下自己的名字,用来表示写信的人是谁。我们签的这个字就是生活中的签名:

图片

而数字签名呢?其实也是同样的道理,他的含义是:在网络中传输数据时候,给数据添加一个数字签名,表示是谁发的数据,而且还能证明数据没有被篡改。

OK,数字签名的主要作用就是保证了数据的有效性(验证是谁发的)和完整性(证明信息没有被篡改)。下面我们就来好好的看一下他的底层实现原理是什么样子的。

二、基本原理

为了理解的清楚,我们通过案例一步一步来讲解。话说张三有俩好哥们A、B。由于工作原因,张三和AB写邮件的时候为了安全都需要加密。于是张三想到了数字签名:

整个思路是这个样子的:

第一步:加密采用非对称加密,张三有三把钥匙,两把公钥,送给朋友。一把私钥留给自己。

第二步:A或者B写邮件给张三:A先用公钥对邮件加密,然后张三收到邮件之后使用私钥解密。

第三步:张三写邮件给A或者B:

(1)张三写完邮件,先用hash函数生成邮件的摘要,附着在文章上面,这就完成了数字签名,然后张三再使用私钥加密。就可以把邮件发出去了。

(2)A或者是B收到邮件之后,先把数字签名取下来,然后使用自己的公钥解密即可。这时候取下来的数字签名中的摘要若和张三的一致,那就认为是张三发来的,再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。

上面的流程我们使用一张图来演示一下:

首先把公钥送给朋友A和B:

图片

然后呢,就是朋友A或者B给张三发邮件:

图片

还有就是最后一个比较麻烦的,张三给A或者B发邮件:

图片

OK,上面的这几张图想必你应该能够理解清楚了,其实还有一些很复杂的情况,因为上面的数字签名是在理想状态下完成的,但是如果遇到了公钥错误,摘要不正确该如何处理呢?这里就涉及到数字证书了,我们来分析一下。

三、数字证书

上面提到我们对签名进行验证时,需要用到公钥。如果公钥是伪造的,那我们无法验证数字签名了,也就根本不可能从数字签名确定对方的合法性了。这时候证书就闪亮登场了。我们可能都有考各种证书的经历,比如说普通话证书,四六级证书等等,但是归根结底,到任何场合我们都能拿出我们的证书来证明自己确实已经考过了普通话,考过了四六级。这里的证书也是同样的道理。

如果不理解证书的作用,我们可以举一个例子,比如说我们的毕业证书,任何公司都会承认。为什么会承认?因为那是国家发的,大家都信任国家。也就是说只要是国家的认证机构,我们都信任它是合法的。

那么这个证书是如何生成的呢?我们再来看一张图:

图片

此时即使张三的朋友A把公钥弄错了,张三也可以通过这个证书验证。

四、代码验证

常用的数字签名算法有:RSA、DSA、ECDSA。这里的代码参考了慕课网。下面给出三种方式的代码实现:

1、RSA

        public static void jdkRSA() {
        try {
            //1.初始化密钥
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(512);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();

            //2.执行签名
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = 
                new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Signature signature = Signature.getInstance("MD5withRSA");
            signature.initSign(privateKey);
            signature.update(src.getBytes());
            byte[] result = signature.sign();

            //3.验证签名
            X509EncodedKeySpec x509EncodedKeySpec = 
                new X509EncodedKeySpec(rsaPublicKey.getEncoded());
            keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            signature = Signature.getInstance("MD5withRSA");
            signature.initVerify(publicKey);
            signature.update(src.getBytes());
            boolean bool = signature.verify(result);
            System.out.println("jdk rsa verify : " + bool);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2、DSA

    public static void jdkDSA() {
        try {
            //1.初始化密钥
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
            keyPairGenerator.initialize(512);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            DSAPublicKey dsaPublicKey = (DSAPublicKey) keyPair.getPublic();
            DSAPrivateKey dsaPrivateKey = (DSAPrivateKey)keyPair.getPrivate();

            //2.执行签名
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = 
                new PKCS8EncodedKeySpec(dsaPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("DSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Signature signature = Signature.getInstance("SHA1withDSA");
            signature.initSign(privateKey);
            signature.update(src.getBytes());
            byte[] result = signature.sign();
            System.out.println("jdk dsa sign : " + Hex.encodeHexString(result));

            //3.验证签名
            X509EncodedKeySpec x509EncodedKeySpec = 
                new X509EncodedKeySpec(dsaPublicKey.getEncoded());
            keyFactory = KeyFactory.getInstance("DSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            signature = Signature.getInstance("SHA1withDSA");
            signature.initVerify(publicKey);
            signature.update(src.getBytes());
            boolean bool = signature.verify(result);
            System.out.println("jdk dsa verify : " + bool);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3、ECDSA

    public static void jdkECDSA() {
        try {
            //1.初始化密钥
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
            keyPairGenerator.initialize(256);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            ECPublicKey ecPublicKey = (ECPublicKey)keyPair.getPublic();
            ECPrivateKey ecPrivateKey = (ECPrivateKey)keyPair.getPrivate();

            //2.执行签名
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = 
                new PKCS8EncodedKeySpec(ecPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Signature signature = Signature.getInstance("SHA1withECDSA");
            signature.initSign(privateKey);
            signature.update(src.getBytes());
            byte[] result = signature.sign();
            System.out.println("jdk ecdsa sign : " + Hex.encodeHexString(result));

            //3.验证签名
            X509EncodedKeySpec x509EncodedKeySpec = 
                new X509EncodedKeySpec(ecPublicKey.getEncoded());
            keyFactory = KeyFactory.getInstance("EC");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            signature = Signature.getInstance("SHA1withECDSA");
            signature.initVerify(publicKey);
            signature.update(src.getBytes());
            boolean bool = signature.verify(result);
            System.out.println("jdk ecdsa verify : " + bool);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

代码已经在这里了,可以自己去实现一遍即可,


更多相关文章

  1. 为什么jQuery的电子邮件验证regex如此简单?
  2. 如何用Cdont+ASP发送带附件的html格式邮件
  3. Outlook Express 无法读取Html邮件的解决办法
  4. css字体大小在苹果邮件中比gmail (iphone)要小
  5. php实现邮件自动发送之PHPMailer
  6. 使用pdf附件发送PHP电子邮件
  7. PHP邮件脚本占用了大量资源
  8. PHP将邮件发送到多个电子邮件地址
  9. 如何在发送之前更改SOAP请求中的名称空间前缀(皇家邮件 - 发送请

随机推荐

  1. 诊断 Java.lang.OutOfMemoryError(OOM)
  2. 不想用 Swagger?可以试试这个神器!
  3. 测试领域,小白问题大集合(适合未入门和入
  4. Scala 与 Java 的交互操作
  5. Swagger(丝袜哥)3.0 官方 Starter 终于出了
  6. 到底要不要考研?
  7. Java 线程面试题 Top 50
  8. 推荐一款神仙颜值的 ZooKeeper 客户端工
  9. Hibernate 和 UUID 标示符
  10. 内推工作福利