利用SHA-1算法和RSA秘钥进行签名验签(带注释)
背景介绍
1、SHA
安全散列算法SHA (Secure Hash Algorithm)是美国国家标准和技术局发布的国家标准FIPS PUB 180-1,一般称为SHA-1。其对长度不超过264二进制位的消息产生160位的消息摘要输出,按512比特块处理其输入。
SHA是一种数据加密算法,该算法经过加密专家多年来的发展和改进已日益完善,现在已成为公认的最安全的散列算法之一,并被广泛使用。
该算法的思想是接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文,也可以简单的理解为取一串输入码(称为预映射或信息),并把它们转化为长度较短、位数固定的输出序列即散列值(也称为信息摘要或信息认证代码)的过程。散列函数值可以说时对明文的一种“指纹”或是“摘要”所以对散列值的数字签名就可以视为对此明文的数字签名。
2、消息摘要
定义:
消息摘要(Message Digest)又称为数字摘要(Digital Digest)。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生。如果消息在途中改变了,则接收者通过对收到消息的新产生的摘要与原摘要比较,就可知道消息是否被改变了。因此消息摘要保证了消息的完整性。
消息摘要采用单向Hash函数将需加密的明文"摘要"成一串128bit的密文,这一串密文亦称为数字指纹(Finger Print),它有固定的长度,且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。这样这串摘要便可成为验证明文是否是"真身"的"指纹"了。
类型:
摘要:GOST3411
,Keccak
,MD2
,MD4
,MD5
,RIPEMD128
,RIPEMD160
,RIPEMD256
,RIPEMD320
,SHA-1
,SHA-224
,SHA-256
,SHA-384
,SHA-512
,SHA3
,Tiger
和Whirlpool
。
3、公钥和私钥:
定义:
公钥和私钥就是俗称的不对称加密方式,是从以前的对称加密(使用用户名与密码)方式的提高。
下面用电子邮件的方式说明一下原理:
使用公钥与私钥的目的就是实现安全的电子邮件,必须实现如下目的:
- 1、我发送给你的内容必须加密,在邮件的传输过程中不能被别人看到。
- 2、必须保证是我发送的邮件,不是别人冒充我的。
要达到这样的目标必须发送邮件的两人都有公钥和私钥。 - 公钥: 就是给大家用的,你可以通过电子邮件发布,可以通过网站让别人下载,公钥其实是用来加密/验章用的。
- 私钥: 就是自己的,必须非常小心保存,最好加上密码,私钥是用来解密/签章,首先就Key的所有权来说,私钥只有个人拥有。
- 公钥与私钥的作用是: 用公钥加密的内容只能用私钥解密,用私钥加密的内容只能用公钥解密。
- 举例:
比如说,我要给你发送一个加密的邮件。首先,我必须拥有你的公钥,你也必须拥有我的公钥。
首先,我用你的公钥给这个邮件加密,这样就保证这个邮件不被别人看到,而且保证这个邮件在传送过程中没有被修改。你收到邮件后,用你的私钥就可以解密,就能看到内容。
其次我用我的私钥给这个邮件加密,发送到你手里后,你可以用我的公钥解密。因为私钥只有我手里有,这样就保证了这个邮件是我发送的。
当A->B资料时,A会使用B的公钥加密,这样才能确保只有B能解开,否则普罗大众都能解开加密的讯息,就是去了资料的保密性。验证方面则是使用签验章的机制,A传资料给大家时,会以自己的私钥做签章,如此所有收到讯息的人都可以用A的公钥进行验章,便可确认讯息是由A发出来的了。
类型:
- 对称密钥算法:
AES
,Blowfish
,Camellia
,CAST5
,CAST6
,ChaCha
,DES
,DESede
,GOST28147
,HC-128
,HC-256
,IDEA
,ISAAC
,Noekeon
,RC2
,RC4
,RC5-32
,RC5-64
,RC6
,Rijndael
,Salsa20
,SEED
,Serpent
,Skipjack
,TEA/XTEA
,Threefish
,Tnepres
,Twofish
,VMPC
andXSalsa20
. - 对称密钥模式:
CBC
,CFB
,CTS
,GOFB
,OFB
,OpenPGPCFB
和SIC(或CTR)
。 - 对称密钥填充:
ISO10126d2
,ISO7816d4
,PKCS-5/7
,TBC
,X.923
, andZero Byte
. - 非对称密钥算法:
ElGamal
,DSA
,ECDSA
,NaccacheStern
andRSA (with blinding)
. - 非对称密钥填充/编码:
ISO9796d1
,OAEP
, andPKCS-1
.
4、数字签名
电子商务中数据传输的几个安全性需求
- 1、数据的保密性:用于防止非法用户进入系统及合法用户对系统资源的非法使用;通过对一些敏感的数据文件进行加密来保护系统之间的数据交换,防止除接收方之外的第三方截获数据及即使获取文件也无法得到其内容。如在电子交易中,避免遭到黑客的袭击使信用卡信息丢失的问题。
- 2、数据的完整性:防止非法用户对进行交换的数据进行无意或恶意的修改、插入,防止交换的数据丢失等。
- 3、数据的不可否认性:对数据和信息的来源进行验证,以确保数据由合法的用户发出;防止数据发送方在发出数据后又加以否认;同时防止接收方在收到数据后又否认曾收到过此数据及篡改数据。
注: 上述需求对应于防火墙、加密、数字签名、身份认证等技术,但其关键在于数字签名技术。
数字签名的含义
数字签名是通过一个单向函数对要传送的报文进行处理得到的用以认证报文来源并核实报文是否发生变化的一个字母数字串。
数字签名的实现方法
实现数字签名有很多方法,目前数字签名采用较多的是公钥加密技术,如基于RSA Date Security 公司的PKCS( Public Key Cryptography Standards )、Digital Signature Algorithm、x.509、PGP(Pretty Good Privacy)。
1994年美国标准与技术协会公布了数字签名标准(DSS)而使公钥加密技术广泛应用。公钥加密系统采用的是非对称加密算法。
由SignerUtilities支持的签名算法
MD2withRSA
, MD4withRSA
,MD5withRSA
, RIPEMD128withRSA
, RIPEMD160withECDSA
, RIPEMD160withRSA
, RIPEMD256withRSA
, SHA-1withRSA
, SHA-224withRSA
, SHA-256withRSAandMGF1
, SHA-384withRSAandMGF1
, SHA-512withRSAandMGF1
, SHA-1withDSA
, and SHA-1withECDSA
使用范例:(带注释)
SHA-1:
对于长度小于2^64位的消息,SHA1会产生一个160位(40个字符)的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。
SHA-1有如下特性:
- 不可以从消息摘要中复原信息;
- 两个不同的消息不会产生同样的消息摘要,(但会有1x10 ^ 48分之一的机率出现相同的消息摘要,一般使用时忽略)。
利用SHA-1算法和RSA秘钥进行签名验签:
代码如下:
import javax.crypto.Cipher;
import java.io.*;
import java.security.*;
import java.util.Base64; /**
* @author: mmzsit
* @date: 2018年10月24日
* @Description:
* 博客地址:https://blog.mmzsblog.cn
* @version V1.0
*/
public class EncryptUtil { public static void main(String[] args) {
ObjectInputStream inputStream = null; //参数字符串
String userName="测试test0->1";
String orderId="测试ID123456";
String price="666";
//构建用于签名和传输的字符串
StringBuffer bufferStr =new StringBuffer();
bufferStr.append("userName=").append(userName)
.append("&orderId=").append(orderId)
.append("&price=").append(price);
//将构建的字符串转化为String类型
String localStr =bufferStr.toString(); //签名算法加密
try {
//随机生成秘钥对
// 检查是否存在这对密钥,否则生成这些密钥
if (!areKeysPresent()) {
// 使用RSA算法生成一对密钥,并存储在指定路径的指定文件中
generateKey();
} //服务端数字签名开始
//第一步:用SHA-1算出原文的摘要
byte[] shaDigest = shaEncrypt(localStr);
System.out.println("原文本内容:\n"+localStr);
String shaStr = new String(shaDigest,"UTF-8");
System.out.println("原文本内容SHA-1算法后:\n"+shaStr); //第二步:使用私钥对原文进行加密
//读取文件中的私钥
inputStream = new ObjectInputStream(new FileInputStream(PRIVATE_KEY_FILE));
final PrivateKey privateKey = (PrivateKey) inputStream.readObject();
//使用私钥加密
byte[] rsaBytes = rsaEncrypt(shaDigest,privateKey); //第三步:对密文进行BASE64编码
byte[] base64Str = Base64.getEncoder().encode(rsaBytes);
String base64enCode=new String(base64Str,"UTF-8");
System.out.println("加密后的内容:\n"+base64enCode); //签名加密完成数据传输到客户端 //客户端验证签名开始
//获取原文
String receiveStr=localStr;
//第一步:使用Base64解码密文
byte[] bese64Decoded =Base64.getDecoder().decode(base64enCode.getBytes("UTF-8")); //第二步:使用公钥对密文进行解码
//读取文件中的公钥
inputStream = new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE));
final PublicKey publicKey = (PublicKey) inputStream.readObject();
//使用公钥解密
byte[] rsaDecode = rsaDecrypt(bese64Decoded,publicKey);
//公钥解密后的结果
String base64denCode=new String(rsaDecode,"utf-8");
System.out.println("公钥解密后的结果:\n"+base64denCode); //第三步:验签
//读取解密后的摘要
String sha1=Base64.getEncoder().encodeToString(rsaDecode);
//使用Sha-1对原文计算摘要
MessageDigest md =MessageDigest.getInstance("SHA-1");
String sha2=Base64.getEncoder().encodeToString(md.digest(receiveStr.getBytes("utf-8")));
//用Sha-1对原文计算摘要结果和解密后的明文比对
if(sha1.equals(sha2)) {
System.out.println("验签成功");
} else {
System.out.println("验签失败");
} System.out.println("字符串sha1:\n"+sha1);
System.out.println("字符串sha2:\n"+sha2); }catch (Exception e) {
e.printStackTrace();
} } /**
* 用于保存加密算法名称的字符串
*/
public static final String ALGORITHM = "RSA"; /**
*
* 用于保存加密填充名称的字符串
* 如果不填写,那么RSA/NONE/NoPadding就是Bouncy Castle 的默认 RSA 实现
* 备用:
*/
public static final String PADDING = "RSA/ECB/PKCS1Padding"; /**
* 用于保存安全提供程序名称的字符串
*/
// public static final String PROVIDER = "BC"; /**
* 用于保存私钥文件名称的字符串
*/
public static final String PRIVATE_KEY_FILE = "d:/Temp/private.key"; /**
* 用于保存公钥文件名称的字符串
*/
public static final String PUBLIC_KEY_FILE = "d:/Temp/public.key"; /**
* 假设最高安全性(即4096位RSA密钥或更大)是非常安全
* 使用1024字节生成包含一对私钥和公钥的密钥。
* 将该组密钥存储在Prvate.key和Public.key文件中。
*
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws FileNotFoundException
*/
public static void generateKey() {
try { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
//final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER);
//密钥位数
keyGen.initialize(1024);
//密钥对
final KeyPair key = keyGen.generateKeyPair(); File privateKeyFile = new File(PRIVATE_KEY_FILE);
File publicKeyFile = new File(PUBLIC_KEY_FILE); // 创建文件夹存储私钥
if (privateKeyFile.getParentFile() != null) {
privateKeyFile.getParentFile().mkdirs();
}
privateKeyFile.createNewFile();
// 创建文件夹存储公钥
if (publicKeyFile.getParentFile() != null) {
publicKeyFile.getParentFile().mkdirs();
}
publicKeyFile.createNewFile(); // 创建文件夹保存公钥
ObjectOutputStream publicKeyOS = new ObjectOutputStream(
new FileOutputStream(publicKeyFile));
publicKeyOS.writeObject(key.getPublic());
publicKeyOS.close(); // 创建文件夹保存私钥
ObjectOutputStream privateKeyOS = new ObjectOutputStream(
new FileOutputStream(privateKeyFile));
privateKeyOS.writeObject(key.getPrivate());
privateKeyOS.close();
} catch (Exception e) {
e.printStackTrace();
} } /**
* 检查是否已生成一对公钥和私钥
*
* @return boolean 返回是否生成秘钥对的标识
*/
public static boolean areKeysPresent() { File privateKey = new File(PRIVATE_KEY_FILE);
File publicKey = new File(PUBLIC_KEY_FILE); if (privateKey.exists() && publicKey.exists()) {
return true;
}
return false;
} /**
* 使用公钥解密数据
*
* @param text 待解密文本
* @param key 公钥
* @return 解密文本
* @throws java.lang.Exception
*/
public static byte[] rsaDecrypt(byte[] text, PublicKey key) {
byte[] cipherText = null;
try {
// 获取RSA密码对象并打印提供程序
// Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance(PADDING);
//final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER); // 使用公钥,ENCRYPT_MODE表示为解密模式
cipher.init(Cipher.DECRYPT_MODE, key);
cipherText = cipher.doFinal(text);
} catch (Exception e) {
e.printStackTrace();
}
return cipherText;
} /**
* 使用私钥加密数据
*
* @param text 待加密文本
* @param key 私钥
* @return 加密后的数据
* @throws java.lang.Exception
*/
public static byte[] rsaEncrypt(byte[] text, PrivateKey key) {
byte[] dectyptedText = null;
try {
// //获取RSA密码对象并打印提供程序
// Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance(PADDING);
//final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER); // 使用私钥加密文本
cipher.init(Cipher.ENCRYPT_MODE, key);
dectyptedText = cipher.doFinal(text); } catch (Exception ex) {
ex.printStackTrace();
} return dectyptedText;
} /**
* 使用sha-1对摘要进行加密
* @param text 签名的原始文本
*/
public static byte[] shaEncrypt(String text) {
//创建消息摘要算法的类
MessageDigest md = null;
//由于接收加密后的摘要的字节数组
byte[] shaDigest = null;
try {
//使用getInstance("算法")来获得消息摘要
md = MessageDigest.getInstance("SHA-1");
//将摘要转化为UTF-8格式的字节数组
byte[] plainText = text.getBytes("UTF-8");
//使用指定的 byte 数组更新摘要
md.update(plainText);
//得出SHA-1算法加密后的结果
shaDigest=md.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return shaDigest;
} }
参考文章:
- https://my.oschina.net/u/242764/blog/481503
- https://blog.csdn.net/WuLex/article/details/72862140
- https://juejin.im/post/5aa22ee9518825558358d30c
利用SHA-1算法和RSA秘钥进行签名验签(带注释)的更多相关文章
- 一台电脑存放多个git账户的多个rsa秘钥
未命名.html div.oembedall-githubrepos{border:1px solid #DDD;border-radius:4px;list-style-type:none;marg ...
- 多个git账户生成多份rsa秘钥实现多个账户同时使用配置
下文分享一个多个git账户生成多份rsa秘钥实现多个账户同时使用配置例子了,这个例子非常的好用对于有多个git的朋友有不小的帮助. 使用过git的童鞋应该对id_rsa秘钥不陌生,总得用github吧 ...
- 一台电脑存放多个git账户的多个rsa秘钥(转)
如何在一个电脑上存储多个git账户生成的多份rsa秘钥,实现多个账户同时使用配置的情况?今天,不幸又再次遇到这个问题. 问题描述 公司最近在开发一款开源产品,项目被托管在github上,但是公司内部一 ...
- js rsa sign使用笔记(加密,解密,签名,验签)
你将会收获: js如何加密, 解密 js如何签名, 验签 js和Java交互如何相互解密, 验签(重点) 通过谷歌, 发现jsrsasign库使用者较多. 查看api发现这个库功能很健全. 本文使用方 ...
- [Python3] RSA的加解密和签名/验签实现 -- 使用pycrytodome
Crypto 包介绍: pycrypto,pycrytodome 和 crypto 是一个东西,crypto 在 python 上面的名字是 pycrypto 它是一个第三方库,但是已经停止更新,所以 ...
- RSA签名验签
import android.util.Base64; import java.security.KeyFactory; import java.security.PrivateKey; import ...
- RSA密钥生成、加密解密、签名验签
RSA 非对称加密公钥加密,私钥解密 私钥签名,公钥验签 下面是生成随机密钥对: //随机生成密钥对 KeyPairGenerator keyPairGen = null; try { keyPair ...
- 数据安全管理:RSA加密算法,签名验签流程详解
本文源码:GitHub·点这里 || GitEE·点这里 一.RSA算法简介 1.加密解密 RSA加密是一种非对称加密,在公开密钥加密和电子商业中RSA被广泛使用.可以在不直接传递密钥的情况下,完成加 ...
- 开源工具 DotnetRSA 快速生成和转换RSA秘钥
一.简介 DotnetRSA 是一个利用 .NET Core 2.1 开发的 .NET Global Tool,是可以想npm全局安装一样,安装在你的系统中,只需敲一行命令便可以快速生成RSA加密算法 ...
随机推荐
- HrbustOJ 1564 螺旋矩阵
Description 对于给定的一个数n,要你打印n*n的螺旋矩阵. 比如n=3时,输出: 1 2 3 8 9 4 7 6 5 Input 多组测试数据,每个测试数据包含一个整数n(1<=n& ...
- SpringBoot(二)_项目属性配置
修改端口 在main/resources/application.properties修改端口 server.port=8088 此时启动访问localhost:8088/hello 就会看到 Hel ...
- 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...
- 关于socket.io的使用
这段时间学习了socket.io,用它写了小项目,在此总结下它的基本使用方式和一些要点. socket.io是基于Node.js和WebSocket协议的实时通信开源框架,它包括客户端的JavaScr ...
- 阿里微服务架构下分布式事务解决方案-GTS
虽然微服务现在如火如荼,但对其实践其实仍处于初级阶段.即使互联网巨头的实践也大多是试验层面,鲜有核心业务系统微服务化的案例.GTS是目前业界第一款,也是唯一的一款通用的解决微服务分布式事务问题的中间件 ...
- MySQL 数据库字符集 utf8 和 utf8mb4 的区别
参考于今日头条上Java芋道源码的-----记住:永远不要在 MySQL 中使用 UTF-8 字符集选择 MySQL 的 utf8 实际上不是真正的 UTF-8.utf8 只支持每个字符最多三个字节, ...
- Docker最全教程之使用TeamCity来完成内部CI、CD流程(十六)
本篇教程主要讲解基于容器服务搭建TeamCity服务,并且完成内部项目的CI流程配置.教程中也分享了一个简单的CI.CD流程,仅作探讨.不过由于篇幅有限,完整的DevOps,我们后续独立探讨. 为了降 ...
- asp.net core 系列之并发冲突
本文介绍如何处理多个用户并发更新同一实体(同时)时出现的冲突 . 主要是两种:一种,检查属性并发冲突,使用 [ConcurrencyCheck] ;另一种,检测行的并发冲突,使用 rowversion ...
- C# 接口的使用(工厂模式)
接口(interface)与抽象类(abstract)的区别: 相同点: 1.都不能被直接实例化,都可以通过继承实现其抽象方法. 2.都是面向抽象编程的技术基础,实现诸多模式 不同点: 1.接口可以多 ...
- I/O基础之概念
1:I/O流就是常说的输入/输出流,用于数据在内存与存储设备(硬盘,文件等)之间的数据传输. 2 : 分类 根据操作分:输入流与输出流 输入流:将外部设备中的数据(包括网络数据)读入内存 ...