Rsa签名算法详解

Rsa签名算法详解


签名生成规则与验证签名规则如下:

每次生成签名时该算法都会生成一对对应的公钥和私钥对,
所以在应用中一般都会采取使用同一对密钥对进行签名与验签(建议使用静态变量)保证对方验证签名时用的公钥是与你生成签名用的是同一对,否则就不能通过验证
将自己生成的秘钥对中的公钥交给对方用来验证你的签名,私钥用来生成签名
同样的对方也会选定一组密钥对将公钥给你用来验证签名,用他的私钥来生成签名
备注:一般对安全性要求比较高的企业,不仅仅限与用rsa签名来保证安全,还会对rsa签名再次加密,比如本案例就使用了base64来加密和解密,同样大家也可以采取其他的措施来增加安全性比如:aes等

注意事项:
①在获取对方的签名的时候注意是否有系统会自动生成空格(尤其是签名串在换行的时候)
②在拼装对方的加密串转换成byte类型时注意是否跟对方的字符编码保持一致



具体的实现代码如下

package com.xxx.web.util;

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.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * @Description: (签名工具类)
 * @author keguan_li 
 * @date 2016年9月22日 
 */
public class RSAUtils {
	
	/** */
	/**
	* 加密算法RSA
	*/
	public static final String KEY_ALGORITHM = "RSA";

	/** */
	/**
	* 签名算法
	*/
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";//有几种算法MD5withRSA是其中一种
	
	public static final String CHARSET = "UTF-8";

	/**
	 * initKey()生成公钥与私钥
	 * 将生成的公钥与对方互换
	 * 因为每次生成签名的时候公钥和私钥都会随机变化(一对密钥对)
	 * 所以要将自己的私钥和对方的公钥设成静态变量
	 * 保证每次生成签名用的都是同一个私钥
	 */
	
    public static final String privateKey="MIICeAIB2QcCQQCvpSVi/IElHyzvvPxRKsTYz/Z";
    
    public static final String publicKey="MIGfMA0GCSqGSvcuK3QJ3i9ZhBpOoyd4JOwIDAQAB";             
	/**
	 * 生成签名
	 * @param retSignPacket
	 * @param strPriKey
	 * @return
	 * @throws Exception
	 */
	public static String addSign(String retSignPacket) throws Exception{
		byte[] bytesKey = decryptBASE64(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(bytesKey);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(priKey);
		signature.update(retSignPacket.getBytes(CHARSET));
		String sign = encryptBASE64(signature.sign());
		return sign;
	}

	/** 
	 * 校验数字签名 
	 *  
	 * @param data 
	 *            加密数据 
	 * @param publicKey 
	 *            公钥 
	 * @param sign 
	 *            数字签名 
	 *  
	 * @return 校验成功返回true 失败返回false 
	 * @throws Exception 
	 *  
	 */
	public static boolean verify(byte[] data, String sign)
			throws Exception {

		// 解密由base64编码的公钥  
		byte[] keyBytes = decryptBASE64(publicKey);
		// 构造X509EncodedKeySpec对象  
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		// KEY_ALGORITHM 指定的加密算法  
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		// 取公钥匙对象  
	    PublicKey pubKey = keyFactory.generatePublic(keySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initVerify(pubKey);
		signature.update(data);
		// 验证签名是否正常  
		boolean ret = signature.verify(decryptBASE64(sign));
		System.out.println("验签结果:" + ret);
		return ret;
	}
	
	  public static Map<String, Object> initKey() throws Exception {  
	        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);  
	        keyPairGen.initialize(1024);  
	        KeyPair keyPair = keyPairGen.generateKeyPair();  
	        // 公钥  
	        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  
	  
	        // 私钥  
	        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();  
	  
	        Map<String, Object> keyMap = new HashMap<String, Object>(2);  
	  
	        keyMap.put("RSAPublicKey", encryptBASE64(publicKey.getEncoded()));  
	        keyMap.put("RSAPrivateKey", encryptBASE64(privateKey.getEncoded()));  
	        
	        System.out.println("私钥为(生成签名用) : ");
	        String priKey = encryptBASE64(privateKey.getEncoded());
			System.out.println(priKey);
			System.out.println("*****************");
			System.out.println("公钥为(验证签名用) : ");
			String pubKey = encryptBASE64(publicKey.getEncoded());
			System.out.println(pubKey);
	        return keyMap;  
	    }  
	

	/** 
	 * BASE64解密 
	 *  
	 * @param key 
	 * @return 
	 * @throws Exception 
	 */
	@SuppressWarnings("restriction")
	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * BASE64加密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("restriction")
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}

	
	public static void main(String[] args) throws Exception {
		initKey();//该方法生成签名,私钥本地用于本地签名,公钥交给对方用于验证你的签名
		addSign("a");//生成签名的方法,a为你要生成签名的签名串,还有用于生成签名的私钥,已写成静态常量
		verify("a".getBytes(),"b");//验证签名的方法,a为你拼接的对方的加密串(在转成byte时字符集编码要与对方的字符集编码保持一致),b为对方的签名,还有对方的公钥我已经将其写成静态常量了
	}
}

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。

随机产生和为S的N个正整数 - 2016-09-23 14:09:17

如果给你一个问题:“ 随机产生和为S的N个正整数 ”, 你会如何做呢?   针对该问题,解决的方法有很多种。在这篇文章中,我将为大家给出两种比较好理解的解决方法:一个是 “尺子法” ;另外一个是 “锯木头法” 。 ( 名字随便取的,主要是方便理解用 )。   方法一:尺子法   思想: 将给定值S看成一个尺子的长度,那么,生成N个和为S的正整数的问题就变成在尺子中 寻找出N-1个不同的刻度 , 加上最小刻度0和最大刻度S , 一共有N+1个刻度 。 然后,从小到大,计算出相邻刻度的长度,这些长度就可以认为
JVMjava 一、常见配置 1、堆设置 -Xms:初始堆大小 -Xmx:最大堆大小 -XX:NewSize=n:设置年轻代大小 -XX:NewRatio=n:设置年轻代和年老代的比值。如:为5,表示年轻代与年老代比值为1:5,年轻代占整个年轻代年老代和的1/6 -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:2,一个Survivor区占整个年轻代的1/10 -XX:MaxPermSize=n

Java为什么能够跨平台? - 2016-09-23 14:09:05

  这也许是个萌新的问题,但我也想把它记录下来,作为Java学习的开始。   在这个大型企业软件横行的时代,Java的使用率越来越高,当然依旧不少的人不喜欢Java,因为它的设计过于笨重,但其开发效率高也是不可否认的一个优点。   言归正传,今天我们讨论下为什么Java是跨平台的语言,我们知道汇编语言的优点及其显著,就是运行速度极快,因为其就是机器指令的助记符。但缺点也是很突出的,除了读不懂以外(本人弱渣),就是汇编是体系结构依赖的语言。如有8086指令集,也有MIPS指令集等等,而像C,C++就会先翻译成
方法一:     import  java.awt.*;   import  java.awt.image.*;   import  java.io.*;   import  javax.swing.*;   import  com.sun.image.codec.jpeg.*;   public   class  WaterSet {       /** */ /**        * 给图片添加水印        *         * @param filePath        *        
    最近项目上要调用其他系统的WebService(Axis搭建),由于接收数据较大耗时4分钟左右,所以要设置超时时间,而且系统中已有jar包的原因,导致许多方法都不能用,最终用HttpClient。 方法一:使用JaxWsDynamicClientFactory调用WebService public static Object call(String wsdl,String method,String requestStr){JaxWsDynamicClientFactory factory = J

深刻理解IdentityHashMap - 2016-09-23 14:09:05

新建POJO package test;public class Cat {private String name;private Integer age;public Cat(String name, Integer age) {super();this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public In

成为java高手的八大条件 - 2016-09-23 14:09:05

成为java高手的八大条件 世界上并没有成为高手的捷径,但一些基本原则是可以遵循的。兄弟连JavaEE培训导师,告诉你学习java有这样的素质才能成为高手! 1、扎实的基础 数据结构、离散数学、编译原理,这些是所有计算机科学的基础,如果不掌握它们,很难写出高水平的程序。程序人人都会写,但当你发现写到一定程度很难再提高的时候,就应该想想是不是要回过头来学学这些最基本的理论。不要一开始就去学OOP,即使你再精通OOP,遇到一些基本算法的时候可能也会束手无策。因此多读一些计算机基础理论方面的书籍是非常有必要的。

MySQL助理配置 - 2016-09-23 14:09:05

基本配置   你需要经常察看以下3个配置项。不然,可能很快就会出问题。 innodb_buffer_pool_size:这是你安装完InnoDB后第一个应该设置的选项。缓冲池是数据和索引缓存的地方:这个值越大越好,这能保证你在大多数的读取操作时使用的是内存而不是硬盘。典型的值是5-6GB(8GB内存),20-25GB(32GB内存),100-120GB(128GB内存)。 innodb_log_file_size:这是redo日志的大小。redo日志被用于确保写操作快速而可靠并且在崩溃时恢复。一直到MyS
9个基于Java的搜索引擎框架   1.Java全文搜索引擎框架Lucene   毫 无疑问,Lucene是目前最受欢迎的Java全文搜索框架,准确地说,它是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引 擎。Lucene为开发人员提供了相当完整的工具包,可以非常方便地实现强大的全文检索功能。下面有几款搜索引擎框架也是基于Lucene实现的。 官方网站: http://lucene.apache.org/   2.开源Java搜索引擎Nutch   Nutch 是一个开源Java实

Quartz定时任务的实现及详解 - 2016-09-23 04:09:09

Quartz定时任务的实现及详解 平常工作中经常遇到写定时任务的,这次做一次记录和解析分享给大家,共同学习进步,遇到写的不对的地方也请大家指教 用例:比如每天凌晨我们要让系统定时执行一些耗费服务器的事情,或者其他的一些定时任务 相关的jar包在附件,如有需要自行下载 1、 首先怎么启动这个Quartz呢? 在项目启动的时候,让web.xml读取我们的定时器配置文件,如下所示 param-namecontextConfigLocation/param-nameparam-value/WEB-INF/conf