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

  • 《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. c/c++ 中的char* ,const char* 和 char* const 总结[转]

    文章转自:c/c++ 中的char* ,const char* 和 char* const 总结 例1: char* str="abc";//错误写法 (在.c文件中是正确的) c ...

  2. 万恶之源 - Python基础知识补充

    编码转换 编码回顾: 1. ASCII : 最早的编码. ⾥⾯有英⽂⼤写字⺟, ⼩写字⺟, 数字, ⼀些特殊字符. 没有中⽂, 8个01代码, 8个bit, 1个byte 2. GBK: 中⽂国标码, ...

  3. bootstrap模态框手动开启关闭与设置点击外部不关闭

    http://www.cnblogs.com/qlqwjy/p/7491054.html 完整的参考菜鸟教程:http://www.runoob.com/bootstrap/bootstrap-mod ...

  4. 图文解析Spark2.0核心技术(转载)

    导语 Spark2.0于2016-07-27正式发布,伴随着更简单.更快速.更智慧的新特性,spark 已经逐步替代 hadoop 在大数据中的地位,成为大数据处理的主流标准.本文主要以代码和绘图的方 ...

  5. RMAN备份与恢复实践(转)

    1   RMAN备份与恢复实践 1.1  备份 1.1.1 对数据库进行全备 使用backup database命令执行备份 RMAN> BACKUP DATABASE; 执行上述命令后将对目标 ...

  6. sklearn_Logistic Regression

    一.什么是逻辑回归? 一种名为“回归”的线性分类器,其本质是由线性回归变化而来的,一种广泛使用于分类问题中的广义回归算法 面试高危问题:Sigmoid函数的公式和性质 Sigmoid函数是一个S型的函 ...

  7. [redis] 介绍安装

    redis相关网站 官方网站:http://redis.io/ redis简介 官方介绍:http://redis.io/topics/introduction 百度百科:http://baike.b ...

  8. MVC备忘笔记

    1.增加样式 @Html.EditorFor(model => model.AssociationName, new { @class="inputtext input-220&quo ...

  9. Java编写验证码

    Java后台代码(CheckCodeServlet.java) package web; import java.awt.Color; import java.awt.Font; import jav ...

  10. C#方法参数传递-同时使用ref和out关键字

    在方法参数传递中,可以同时使用ref和out关键字,但是要注意ref和out参数传递的不同. using System;class Program{static void Main(){    Pro ...