注意:本节内容主要参考自

  • 《Java加密与解密的艺术(第2版)》第9章“带密钥的消息摘要算法--数字签名算法”
  • 《大型分布式网站架构(设计与实践)》第3章“互联网安全架构”

14.1、数字签名算法

特点:

  • 非对称加密算法+消息摘要算法的结合体
  • 抗否认性、认证数据来源、防止数据被篡改(具体意思与做法查看下边的过程与类比部分)
  • 私钥加密(签名)、公钥解密(验证)

过程:

1)消息发送者产生一个密钥对(私钥+公钥),然后将公钥发送给消息接收者

2)消息发送者使用消息摘要算法对原文进行加密(加密后的密文称作摘要)

3)消息发送者将上述的摘要使用私钥加密得到密文--这个过程就被称作签名处理,得到的密文就被称作签名(注意,这个签名是名词)

4)消息发送者将原文与密文发给消息接收者

5)消息接收者使用公钥对密文(即签名)进行解密,得到摘要值content1

6)消息接收者使用与消息发送者相同的消息摘要算法对原文进行加密,得到摘要值content2

7)比较content1是不是与content2相等,若相等,则说明消息没有被篡改(消息完整性),也说明消息却是来源于上述的消息发送方(因为其他人是无法伪造签名的,这就完成了“抗否认性”和“认证消息来源”)

类比:

手写签名:

  • 张三在合同上签了自己的名字,这样张三在后来想否认自己的这个合同内容时,就不行了(抗否认性)
  • 在张三签过名之后,若合同内容又发生了变化,这个时候签名就可以看做无效了
  • 当然,我们通过合同上张三的签名,我们就可以知道签名的来源是张三(认证数据来源)

常见的数字签名算法:

  • RSA(数字签名算法的经典,也是最常用的数字签名算法)
  • DSA(是后续数字签名算法的基础)
  • ECDSA(ECC+DSA的结合体,相较于其他数字签名算法,速度快,强度高,签名短,但是使用还没有RSA广泛)

14.2、RSA

常见算法:

  • MD5WithRSA(JDK6)
  • SHA1WithRSA(JDK6)
  • SHA256WithRSA(>=JDK1.6.45,Bouncy Castle-->简称BC)

注意:在《Java加密与解密(第二版)》一书中,说JDK6不支持SHA256WithRSA,但是经过我自己测试1.6.45是支持的。

实现方式:(参考上边三行)

  • JDK
  • BC

14.2.1、基于JDK6实现的MD5WithRSA或SHA1WithRSA或SHA256WithRSA算法

package com.uti.rsa.digital;

import java.io.UnsupportedEncodingException;
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.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.commons.codec.binary.Base64;

/**
 * 基于BC的RSA数字签名算法
 */
public class RSACoderBC {
    private static final String ENCODING = "UTF-8";
    private static final String KEY_ALGORITHM = "RSA";//非对称加密密钥算法
    private static final String SIGNATURE_ALGORITHM = "MD5withRSA";//指定数字签名算法(可以换成SHA1withRSA或SHA256withRSA)
    private static final int KEY_SIZE = 512;//非对称密钥长度(512~1024之间的64的整数倍)

    /**
     * 生成发送方密钥对
     */
    public static KeyPair initKey() throws NoSuchAlgorithmException{
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);//密钥对生成器
        keyPairGenerator.initialize(KEY_SIZE);//指定密钥长度
        KeyPair keyPair = keyPairGenerator.generateKeyPair();//生成密钥对
        return keyPair;
    }

    /**
     * 还原公钥
     * @param pubKey 二进制公钥
     */
    public static PublicKey toPublicKey(byte[] pubKey) throws NoSuchAlgorithmException,
                                                              InvalidKeySpecException{
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂
        return keyFactory.generatePublic(new X509EncodedKeySpec(pubKey));//还原公钥
    }

    /**
     * 还原私钥
     * @param priKey 二进制私钥
     */
    public static PrivateKey toPrivateKey(byte[] priKey) throws NoSuchAlgorithmException,
                                                                InvalidKeySpecException{
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(priKey));//还原私钥
    }

    /**
     * 私钥加密(签名)
     * @param data     待加密数据
     * @param keyByte  私钥
     */
    public static byte[] encryptPriKey(String data, byte[] keyByte) throws NoSuchAlgorithmException,
                                                                           InvalidKeySpecException,
                                                                           InvalidKeyException,
                                                                           SignatureException,
                                                                           UnsupportedEncodingException {
        PrivateKey priKey = toPrivateKey(keyByte);//还原私钥

        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data.getBytes(ENCODING));

        return signature.sign();
    }

    /**
     * 公钥解密(验证)
     * @param data        原文(待加密数据,也成为“待校验数据”)
     * @param keyByte    公钥
     * @param sign        密文(也称作“签名”)
     */
    public static boolean decryptPubKey(String data, byte[] keyByte, byte[] sign) throws NoSuchAlgorithmException,
                                                                                         InvalidKeySpecException,
                                                                                         InvalidKeyException,
                                                                                         SignatureException,
                                                                                         UnsupportedEncodingException {
        PublicKey pubKey = toPublicKey(keyByte);//还原公钥

        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data.getBytes(ENCODING));

        return signature.verify(sign);
    }

    /**
     * 获取公钥
     */
    public static byte[] getPublicKey(KeyPair keyPair){
        return keyPair.getPublic().getEncoded();
    }

    /**
     * 获取私钥
     */
    public static byte[] getPrivateKey(KeyPair keyPair){
        return keyPair.getPrivate().getEncoded();
    }

    /**
     * 测试
     */
    public static void main(String[] args) throws NoSuchAlgorithmException,
                                                  InvalidKeyException,
                                                  InvalidKeySpecException,
                                                  SignatureException,
                                                  UnsupportedEncodingException {
        byte[] pubKey1;//甲方公钥
        byte[] priKey1;//甲方私钥

        /*********************测试是否可以正确生成以上2个key*********************/
        KeyPair keyPair1 = RSACoderBC.initKey();//生成甲方密钥对
        pubKey1 = RSACoderBC.getPublicKey(keyPair1);
        priKey1 = RSACoderBC.getPrivateKey(keyPair1);

        System.out.println("甲方公钥pubKey1-->"+Base64.encodeBase64String(pubKey1)+"@@pubKey1.length-->"+pubKey1.length);
        System.out.println("甲方私钥priKey1-->"+Base64.encodeBase64String(priKey1)+"@@priKey1.length-->"+priKey1.length);

        /*********************测试甲方使用私钥加密数据向乙方发送,乙方使用公钥解密数据*********************/
        System.out.println("甲方-->乙方");
        String data = "找一个好姑娘啊!你好吗,孩子";
        byte[] encodeStr = RSACoderBC.encryptPriKey(data, priKey1);//加密(签名)
        System.out.println("甲方加密后的数据-->"+Base64.encodeBase64String(encodeStr)+"@@encodeStr.length-->"+encodeStr.length);
        boolean decodeStr = RSACoderBC.decryptPubKey(data, pubKey1, encodeStr);
        System.out.println("乙方检验结果-->"+decodeStr);
    }
}

注意点:

  • 代码与RSA非对称加密算法(查看第十二章)基本一样,只是将其中的私钥加密与公钥解密部分的加解密算法改为签名和验证算法,当然没有“公钥加密,私钥解密”

疑问:在《Java加密与解密的艺术(第二版)》一书中,作者说:“RSA数字签名算法的签名值与密钥长度相同”,但是在我的测试中,结果不一致:

1)在我配置非对称密钥为512的时候,测出来的公钥长度是96位,私钥长度是345位,与512差很远,那这里的512到底是怎么计算的?

2)消息摘要的结果长度是64(摘要值的二进制自己数组长度),不知道到底是怎么与密钥长度相同的?

以上两个问题,有知道的朋友请指点一下,谢谢!

第十四章 数字签名算法--RSA的更多相关文章

  1. perl 第十四章 Perl5的包和模块

    第十四章 Perl5的包和模块 by flamephoenix 一.require函数  1.require函数和子程序库  2.用require指定Perl版本二.包  1.包的定义  2.在包间切 ...

  2. 第十四章——循环神经网络(Recurrent Neural Networks)(第二部分)

    本章共两部分,这是第二部分: 第十四章--循环神经网络(Recurrent Neural Networks)(第一部分) 第十四章--循环神经网络(Recurrent Neural Networks) ...

  3. HTML第十四章总结 HTML forms

    第十四章主要讲了 html forms,通过 forms,我们可以得到 customers' feedback,使得网页能够 interactive,本章的内容分为三个部分: forms 的 elem ...

  4. 进击的Python【第十四章】:Web前端基础之Javascript

    进击的Python[第十四章]:Web前端基础之Javascript 一.javascript是什么 JavaScript 是一种轻量级的编程语言. JavaScript 是可插入 HTML 页面的编 ...

  5. 20190827 On Java8 第十四章 流式编程

    第十四章 流式编程 流的一个核心好处是,它使得程序更加短小并且更易理解.当 Lambda 表达式和方法引用(method references)和流一起使用的时候会让人感觉自成一体.流使得 Java ...

  6. 《Linux命令行与shell脚本编程大全》 第十四章 学习笔记

    第十四章:呈现数据 理解输入与输出 标准文件描述符 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 1.STDIN 代表标准输入.对于终端界面 ...

  7. Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  8. C和指针 (pointers on C)——第十四章:预处理器

    第十四章 预处理器 我跳过了先进的指针主题的章节. 太多的技巧,太学科不适合今天的我.但我真的读,读懂.假设谁读了私下能够交流一下.有的小技巧还是非常有意思. 预处理器这一章的内容.大家肯定都用过.什 ...

  9. CSS3秘笈复习:十三章&十四章&十五章&十六章&十七章

    第十三章 1.在使用浮动时,源代码的顺序非常重要.浮动元素的HTML必须处在要包围它的元素的HTML之前. 2.清楚浮动: (1).在外围div的底部添加一个清除元素:clear属性可以防止元素包围浮 ...

随机推荐

  1. django时间的时区问题

    在用django1.8版本做项目的时候遇到时间的存储与读取不一致的问题,网上找了很多帖子,但都没有讲明白.本文将在项目中遇到的问题及如何解决的尽可能详细的记录下来,当然本文参考了网上大量相关文章. 在 ...

  2. Python之Pandas使用教程(转)

    https://blog.csdn.net/XiaoYi_Eric/article/details/79506660

  3. 009-java中常用的单个键值对

    1.Java 6提供AbstractMap.SimpleEntry<K,V>和AbstractMap.SimpleImmutableEntry<K,V> Map.Entry&l ...

  4. 知道椭圆长轴,短轴长度,ab直线的长度知道且垂直于长轴。求ab的弧长。

    1:知道椭圆长轴,短轴长度,ab直线的长度知道且垂直于长轴.求ab的弦长.: https://jingyan.baidu.com/article/a378c960a5af27b3282830e6.ht ...

  5. [py][mx]django自定义认证类-实现邮箱作为用户名登录

    创建自定义验证用户名密码类CustomBackend users/views.py from django.contrib.auth import authenticate, login from d ...

  6. easyUI中datebox的格式显示

    使用datebox的问题: 1.需要YYYY-MM-dd这种时间格式: 2.月份显示的是中文. 上述两个问题只要引入国际化的js文件即可. 注:下图为easyUI使用时需要引入的文件,红框就可以解决上 ...

  7. Andrew Ng-ML-第十三章-支持向量机

    1.从代价函数谈起SVM 图一 根据将y=0||y=1,得到逻辑回归的代价函数,那么SVM和其代价函数是相似的,只不过是引入了cost0与cost1,并且自变量使用了theta_T*x(i),并且由于 ...

  8. svn 常见问题记录

    One or more files are in a conflicted state 情景:A组员新增文件并提交,B组员更新出现如下图情况. 解决方案:直接拷贝到B组员工作区.

  9. MFC CFile类读写文件详解

    CFile类提供了对文件进行打开,关闭,读,写,删除,重命名以及获取文件信息等文件操作的基本功能,足以处理任意类型的文件操作. 一个读写文件的例子: 文件I/O 虽然使用CArchive类内建的序列化 ...

  10. 填格子3*N的方框使用2*1的矩形进行填充

    考虑每个位置的前一个状态 可以发现有 我们分别给他们编号 假设 现在填充到了i+1行,我们可以发现从i行可以通过填充转到i+1行的状态 第i行第j列表示 可以 从上一个转态 j 可以到达这个状态的j ...