RSA签名算法 - Java加密与安全

RSA签名算法

在非对称加密中我们可以看到,甲乙双方要进行通信,用publicKey进行加密,用priavteKey解密,这个时候会出现一个问题,

如果黑客用你的publicKey对消息进行加密,然后冒充甲发送给乙,乙怎么确定这个消息是甲发送,还是有人伪造甲发送的呢,

所以我们就需要数字签证算法,发送加密消息的时候同时需要发送签名,而这个签名是需要甲的privateKey计算的,而乙要

验证这个签名,是否是合法的,它会用甲的publicKey进行验证,如果验证成功,证明这个消息确实是甲发送的

数字签名就是发送方用自己的私钥对消息进行签名,然后接受方用发送方的私钥来验证签名是否有效,我们可以把数字签名

混入了公钥和私钥的一种摘要,publicKey和原始签名和message着三个中的任何一个被修改了,签名都是无效的




数字签名的目的是:

1. 确认信息是某个发送方发送的,因为只有他用自己的privateKey签名,其他人才能用它的publicKey去验证这个签名

2. 发送方还不能抵赖他发送了消息,因为用谁的publicKey成功验证的签名,就一定是谁用privateKey签名的,所以privateKey

就相当于用户的身份

3. 最后数字签名保证在传输过程中没有被修改


常用的数字签名算法有:

1. MD5WithRSA

2. SHA1WithRSA

3. SHA256WithRSA
package com.learn.securl;

import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * 使用RSA签名算法的时候
 * 我们同样要生成privateKey和publicKey
 * @author Leon.Sun
 *
 */
public class SecRSASignature {
	PrivateKey sk;
	PublicKey pk;
	/**
	 * 生成公钥/私钥对
	 * @throws GeneralSecurityException
	 */
	public SecRSASignature() throws GeneralSecurityException{
		/**
		 * 生成KeyPair的方法和使用RSA加密是一样的
		 */
		KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
		kpGen.initialize(1024);
		KeyPair kp = kpGen.generateKeyPair();
		this.sk = kp.getPrivate();
		this.pk = kp.getPublic();
	}
	/**
	 * 从以保存的字节中(例如:读取文件)恢复公钥/私钥
	 * @param pk
	 * @param sk
	 * @throws GeneralSecurityException
	 */
	public SecRSASignature(byte[] pk, byte[] sk) throws GeneralSecurityException{
		KeyFactory kf = KeyFactory.getInstance("RSA");
		X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pk);
		this.pk = kf.generatePublic(pkSpec);
		PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(sk);
		this.sk = kf.generatePrivate(skSpec);
	}
	/**
	 * 把私钥导出为字节
	 * @return
	 */
	public byte[] getProvateKey(){
		return this.sk.getEncoded();
	}
	/**
	 * 把公钥导出为字节
	 * @return
	 */
	public byte[] getPublicKey(){
		return this.pk.getEncoded();
	}
	/**
	 * sign by sk
	 * 然后我们定义一个sign方法
	 * 用于对数据进行签名
	 * 当我们使用签名算法的时候
	 * @param message
	 * @return
	 * @throws GeneralSecurityException
	 */
	public byte[] sign(byte[] message) throws GeneralSecurityException{
		/**
		 * 我们首先通过Signature.getInstance
		 * 然后传入签名算法的算法名称
		 * 这里我们使用SHA1withRSA
		 * 我们就得到一个Signature对象
		 */
		Signature signature = Signature.getInstance("SHA1withRSA");
		/**
		 * 然后我们调用initSign
		 * 传入我们的privateKey
		 * 初始化一个签名
		 */
		signature.initSign(this.sk);
		/**
		 * 紧接着我们用update方法传入一个消息
		 * 表示这个消息进行签名
		 */
		signature.update(message);
		/**
		 * 接着我们用signature的sign方法
		 * 就可以获得签名以后的字节
		 */
		return signature.sign();
	}
	/**
	 * sign by pk
	 * 我们再定义一个verify方法来验证签名
	 * 验证签名的时候
	 * 我们需要传入原始信息和签名信息
	 * @param message
	 * @param sign
	 * @return
	 * @throws GeneralSecurityException
	 */
	public boolean verify(byte[] message, byte[] sign) throws GeneralSecurityException{
		/**
		 * 同样我们调用Signature.getInstance
		 * 然后传入SHA1withRSA
		 * 获得一个Signature对象
		 */
		Signature signature = Signature.getInstance("SHA1withRSA");
		/**
		 * 这个时候我们要调用initVerify
		 * 然后传入publicKey
		 * 表示我们要验证的签名
		 * 验证签名只能使用publicKey
		 */
		signature.initVerify(this.pk);
		/**
		 * 然后我们传入原始的信息
		 */
		signature.update(message);
		/**
		 * 最后我们调用verigy并传入签名信息
		 * 表示对签名进行验证
		 * 签名的结果为true或者false
		 */
		return signature.verify(sign);
	}
	
	/**
	 * 现在我们用main来对RSA签名进行测试
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception{
		byte[] message = "Hello, 使用SHA1withRSA算法进行数字签名!".getBytes();
		SecRSASignature rsas = new SecRSASignature();
		/**
		 * 我们首先通过sign方法对信息进行签名
		 */
		byte[] sign = rsas.sign(message);
		/**
		 * 然后打印出签名的结果
		 */
		System.out.println("sign: " + Base64.getEncoder().encodeToString(sign));
		/**
		 * 然后我们通过verify方法对信息和签名进行验证
		 */
		boolean verified = rsas.verify(message, sign);
		/**
		 * 然后打印出签名的结果
		 * 
		 * verify: true
		 * 我们用原始的公钥进行的签名结果为true,
		 */
		System.out.println("verify: " + verified);
		// 用另一个公钥验证:
		/**
		 * 如果我们创建另一个Signature对象
		 * 然后用另一个公钥去验证
		 */
		boolean verified2 = new SecRSASignature().verify(message, sign);
		/**
		 * 我们可以看到验证的结果为false
		 * 
		 * verify with another public key: false
		 * 我们用另一个publicKey进行的签名结果为false
		 */
		System.out.println("verify with another public key: " + verified2);
		// 修改原始信息:
		/**
		 * 当我们把原始信息修改为一个字节的时候
		 */
		message[0] = 100;
		/**
		 * 我们再对信息和签名进行验证
		 * 我们可以看到改正过的信息验证的结果也应该为false
		 * 
		 * verify changed message: false
		 * 我们对原始信息进行的改动结果为false
		 */
		boolean verified3 = rsas.verify(message, sign);
		System.out.println("verify changed message: " + verified3);
	}
}

现在我们总结一下:

1. 数字前面就是用发送方的私钥对原始数据进行签名

2. 只有用发送方的公钥才能通过签名验证,这样做的目的是为了防止伪造发送方,防止抵赖发送过信息,

防止发送信息发送过程中被修改

3. 常用的算法有:MD5withRSA,SHA1withRSA,SHA256withRSA

猜你喜欢

转载自blog.csdn.net/Leon_Jinhai_Sun/article/details/89944564