第十四章 数字签名算法--RSA
注意:本节内容主要参考自
- 《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的更多相关文章
- perl 第十四章 Perl5的包和模块
第十四章 Perl5的包和模块 by flamephoenix 一.require函数 1.require函数和子程序库 2.用require指定Perl版本二.包 1.包的定义 2.在包间切 ...
- 第十四章——循环神经网络(Recurrent Neural Networks)(第二部分)
本章共两部分,这是第二部分: 第十四章--循环神经网络(Recurrent Neural Networks)(第一部分) 第十四章--循环神经网络(Recurrent Neural Networks) ...
- HTML第十四章总结 HTML forms
第十四章主要讲了 html forms,通过 forms,我们可以得到 customers' feedback,使得网页能够 interactive,本章的内容分为三个部分: forms 的 elem ...
- 进击的Python【第十四章】:Web前端基础之Javascript
进击的Python[第十四章]:Web前端基础之Javascript 一.javascript是什么 JavaScript 是一种轻量级的编程语言. JavaScript 是可插入 HTML 页面的编 ...
- 20190827 On Java8 第十四章 流式编程
第十四章 流式编程 流的一个核心好处是,它使得程序更加短小并且更易理解.当 Lambda 表达式和方法引用(method references)和流一起使用的时候会让人感觉自成一体.流使得 Java ...
- 《Linux命令行与shell脚本编程大全》 第十四章 学习笔记
第十四章:呈现数据 理解输入与输出 标准文件描述符 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 1.STDIN 代表标准输入.对于终端界面 ...
- Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
- C和指针 (pointers on C)——第十四章:预处理器
第十四章 预处理器 我跳过了先进的指针主题的章节. 太多的技巧,太学科不适合今天的我.但我真的读,读懂.假设谁读了私下能够交流一下.有的小技巧还是非常有意思. 预处理器这一章的内容.大家肯定都用过.什 ...
- CSS3秘笈复习:十三章&十四章&十五章&十六章&十七章
第十三章 1.在使用浮动时,源代码的顺序非常重要.浮动元素的HTML必须处在要包围它的元素的HTML之前. 2.清楚浮动: (1).在外围div的底部添加一个清除元素:clear属性可以防止元素包围浮 ...
随机推荐
- 【F12】谷歌浏览器F12前端调试工具 Console
谷歌浏览器F12前端调试工具 Console 前言 先上图:不知道有多少人发现,在浏览器开发工具的“Console”上的百度首页的关于百度招聘的信息: 今天要给大家介绍的就是是Web前端调试工具中的C ...
- 前端 HTML文档 详解
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- CSS 基础知识
CSS 实例(CSS声明总是以分号(;)结束,声明组以大括号({})括起来:) CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明: 选择器通常是您需要改变样式的 HTML 元素. 每条声明 ...
- switch语句语法
switch case语句适用于从一组互斥的分支中选择一个执行分支. int day = 0;switch (day) { : dayName = "Sunday"; break ...
- ES6(简)
一. let.const 和 var let和const只在当前块级作用域中有效const用来声明常量var是全局作用域有效的 constants.js 模块export const A = 1;ex ...
- SQL工具类
package com.ebizwindow.crm.utils; import java.util.List; import com.ebizwindow.crm.constants.SqlCons ...
- 谷歌浏览器使用IE内核
经常使用谷歌浏览器去到处溜达,可是,居然还不知道谷歌浏览的还有很多新鲜的设置,昨天遇到了调试一个脚本没有出现效果,而在IE中就可以显示所谓的效果: 查询了下,看到谷歌浏览器确实对脚本的部分样式要求还是 ...
- ASP.NET控件--DropDownList
设置默认值:DropDownList1.Items[i].Selected=true;绑定:DropDownList1.DataSource = dataSet.Tables["Tabl ...
- python 命令执行文件传递参数
import os,sys for root,dirs,files in os.walk(sys.argv[1]): for name in files: print(os.path.join(roo ...
- checkbox的readonly不起作用的解决方案
checkbox的readonly不起作用的解决方案 <input type="checkbox" readonly /> checkbox没有readOnly属性,r ...