文档章节

RSA加密解密及数字签名Java实现

立志尚早
 立志尚早
发布于 2013/10/25 11:26
字数 1997
阅读 36491
收藏 44

    RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

    RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密算法

    RSA算法是一种非对称密码算法,所谓非对称,就是指该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密。

    关于RSA算法的原理,这里就不再详加介绍,网上各种资源一大堆。下面就开始介绍RSA加密解密JAVA类的具体实现。

import java.security.MessageDigest;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Coder {
	
	public static final String KEY_SHA="SHA";
	public static final String KEY_MD5="MD5";
	
	/**
	 * BASE64解密
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String key) throws Exception{
		return (new BASE64Decoder()).decodeBuffer(key);
	}
	
	/**
	 * BASE64加密
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptBASE64(byte[] key)throws Exception{
		return (new BASE64Encoder()).encodeBuffer(key);
	}
	
	/**
	 * MD5加密
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptMD5(byte[] data)throws Exception{
		MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
		md5.update(data);
		return md5.digest();
	}
	
	/**
	 * SHA加密
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptSHA(byte[] data)throws Exception{
		MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
		sha.update(data);
		return sha.digest();
	}
}

     先提供Coder编码类,该类封装了基本的Base64、md5和SHA加密解密算法。Java对这些算法的实现提供了很好的API封装,开发人员只需调用这些API就可很简单方便的实现数据的加密与解密。

    下面提供RSA加密解密类,该类为Coder类子类,因为其中对RSA公私密钥的保存进行了一层Base64加密处理。

    RSA加密解密类静态常量

       public static final String KEY_ALGORTHM="RSA";//
	public static final String SIGNATURE_ALGORITHM="MD5withRSA";
	
	public static final String PUBLIC_KEY = "RSAPublicKey";//公钥
	public static final String PRIVATE_KEY = "RSAPrivateKey";//私钥



    RSA加密解密的实现,需要有一对公私密钥,公私密钥的初始化如下:

/**
	 * 初始化密钥
	 * @return
	 * @throws Exception
	 */
	public static Map<String,Object> initKey()throws Exception{
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORTHM);
		keyPairGenerator.initialize(1024);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		
		//公钥
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		//私钥
		RSAPrivateKey privateKey =  (RSAPrivateKey) keyPair.getPrivate();
		
		Map<String,Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		
		return keyMap;
	}

      从代码中可以看出密钥的初始化长度为1024位,密钥的长度越长,安全性就越好,但是加密解密所用的时间就会越多。而一次能加密的密文长度也与密钥的长度成正比。一次能加密的密文长度为:密钥的长度/8-11。所以1024bit长度的密钥一次可以加密的密文为1024/8-11=117bit。所以非对称加密一般都用于加密对称加密算法的密钥,而不是直接加密内容。对于小文件可以使用RSA加密,但加密过程仍可能会使用分段加密。

    从map中获取公钥、私钥

/**
	 * 取得公钥,并转化为String类型
	 * @param keyMap
	 * @return
	 * @throws Exception
	 */
	public static String getPublicKey(Map<String, Object> keyMap)throws Exception{
		Key key = (Key) keyMap.get(PUBLIC_KEY);  
		return encryptBASE64(key.getEncoded());     
	}

	/**
	 * 取得私钥,并转化为String类型
	 * @param keyMap
	 * @return
	 * @throws Exception
	 */
	public static String getPrivateKey(Map<String, Object> keyMap) throws Exception{
		Key key = (Key) keyMap.get(PRIVATE_KEY);  
		return encryptBASE64(key.getEncoded());     
	}


    对于RSA产生的公钥、私钥,我们可以有两种方式可以对信息进行加密解密。私钥加密-公钥解密 和 公钥加密-私钥解密。
 

    私钥加密

/**
	 * 用私钥加密
	 * @param data	加密数据
	 * @param key	密钥
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPrivateKey(byte[] data,String key)throws Exception{
		//解密密钥
		byte[] keyBytes = decryptBASE64(key);
		//取私钥
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		
		//对数据加密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		
		return cipher.doFinal(data);
	}

    私钥解密

/**
	 * 用私钥解密 * @param data 	加密数据
	 * @param key	密钥
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPrivateKey(byte[] data,String key)throws Exception{
		//对私钥解密
		byte[] keyBytes = decryptBASE64(key);
		
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		//对数据解密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		
		return cipher.doFinal(data);
	}

    公钥加密

/**
	 * 用公钥加密
	 * @param data	加密数据
	 * @param key	密钥
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data,String key)throws Exception{
		//对公钥解密
		byte[] keyBytes = decryptBASE64(key);
		//取公钥
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
		
		//对数据解密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		
		return cipher.doFinal(data);
	}

    私钥加密

/**
	 * 用公钥解密
	 * @param data	加密数据
	 * @param key	密钥
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPublicKey(byte[] data,String key)throws Exception{
		//对私钥解密
		byte[] keyBytes = decryptBASE64(key);
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
		
		//对数据解密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		
		return cipher.doFinal(data);
	}

    关于数字签名,先了解下何为数字签名。数字签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。数字签名是非对称密钥加密技术与数字摘要技术的应用。简单地说,所谓数字签名就是附加在数据单元上的一些数据,或是对数据单元所作的密码变换。这种数据或变换允许数据单元的接收者用以确认数据单元的来源和数据单元的完整性并保护数据,防止被人(例如接收者)进行伪造。

    数字签名的主要功能如下:
 

    保证信息传输的完整性、发送者的身份认证、防止交易中的抵赖发生。

    数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。

    数字签名是个加密的过程,数字签名验证是个解密的过程。

     数字签名算法依靠公钥加密技术来实现的。在公钥加密技术里,每一个使用者有一对密钥:一把公钥和一把私钥。公钥可以自由发布,但私钥则秘密保存;还有一个要求就是要让通过公钥推算出私钥的做法不可能实现。

    普通的数字签名算法包括三种算法:

    1.密码生成算法;

    2.标记算法;

   3.验证算法

    通过RSA加密解密算法,我们可以实现数字签名的功能。我们可以用私钥对信息生成数字签名,再用公钥来校验数字签名,当然也可以反过来公钥签名,私钥校验。

    私钥签名
 

/**
	 *	用私钥对信息生成数字签名
	 * @param data	//加密数据
	 * @param privateKey	//私钥
	 * @return
	 * @throws Exception
	 */
	public static String sign(byte[] data,String privateKey)throws Exception{
		//解密私钥
		byte[] keyBytes = decryptBASE64(privateKey);
		//构造PKCS8EncodedKeySpec对象
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
		//指定加密算法
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		//取私钥匙对象
		PrivateKey privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		//用私钥对信息生成数字签名
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(privateKey2);
		signature.update(data);
		
		return encryptBASE64(signature.sign());
	}

    公钥校验


/**
	 * 校验数字签名
	 * @param data	加密数据
	 * @param publicKey	公钥
	 * @param sign	数字签名
	 * @return
	 * @throws Exception
	 */
	public static boolean verify(byte[] data,String publicKey,String sign)throws Exception{
		//解密公钥
		byte[] keyBytes = decryptBASE64(publicKey);
		//构造X509EncodedKeySpec对象
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
		//指定加密算法
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		//取公钥匙对象
		PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);
		
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initVerify(publicKey2);
		signature.update(data);
		//验证签名是否正常
		return signature.verify(decryptBASE64(sign));
		
	}

       对于RSA如何加密文件、图片等信息,加密的信息又如何保存,怎样保存解密后的信息,以及操作过程中遇到的错误将如何处理,将在后面的文章中介绍给大家。 






    

© 著作权归作者所有

立志尚早

立志尚早

粉丝 25
博文 37
码字总数 23433
作品 0
浦东
程序员
私信 提问
加载中

评论(5)

追逐什么
追逐什么
公钥签名、私钥校验签名怎么写呢?
cuizhaojin
cuizhaojin
感谢楼主细心讲解
姜建林
传值的时候私钥key应该是什么呢 比如我有一个私钥(4294967297,612609463)
is_true
is_true
怎么会是乱码呢
Android外部文件加解密及应用实践

有这样的应用场景,当我们把一些重要文件放到asset文件夹中时,把.apk解压是可以直接拿到这个文件的,一些涉及到重要信息的文件我们并不想被反编译拿去,这个时候需要先对文件进行加密,然后...

C6C
2018/05/08
0
0
浅析RSA公钥密码以及使用Java自带API实现RSA的密钥生成和加解密

RSA是目前最流行的非对称密码,目前广泛应用在数字签名,数字证书上。 那么什么是非对称密码呢?就是给明文加密的密钥和给密文解密的密钥是不一样的。其中,对外暴露的是公钥,自己保留的是私...

Lunqi
2015/07/15
1K
1
Java结合keytool实现非对称加密和解密

原文出处:王洁 参考:Java结合keytool实现非对称签名与验证 那一篇讲签名,这一篇将加密解密。在Java安全体系中,签名属于JAAS模块,加解密属于JCE模块。 keytool的使用 keytool是JDK自带的...

王洁
2018/08/08
0
0
Java学习手册:Java网络编程面试问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/MaybeForever/article/details/95471329 1、Java学习手册:Java基础知...

浩比浩比
08/27
0
0
JAVA和IOS对接RSA加密问题

公司开发IOS APP,跟JAVA后台对接。在加密方面,使用的是RSA。在JAVA端生成的公钥和私钥,通过BASE64转成字符串。然后在IOS端,如果直接拿着这个转化的字符串密钥(通过BASE64解密后)去做RSA...

安静的敲代码
2016/03/31
654
1

没有更多内容

加载失败,请刷新页面

加载更多

交换两数(函数)

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> void Exchange(int* x, int* y){ int tmp = 0; tmp = *x; *x = *y; *y = tmp; } int main(){ int a; int b; scanf......

Lxxxxx256
59分钟前
3
0
给 K8s API “做减法”:阿里巴巴云原生应用管理的挑战和实践

作者 | 孙健波(天元) 阿里巴巴技术专家 本文整理自 11 月 21 日社群分享,每月 2 场高质量分享,点击加入社群。 早在 2011 年,阿里巴巴内部便开始了应用容器化,当时最开始是基于 LXC 技术...

阿里巴巴云原生
今天
7
0
数据平面

3.1数据平面的任务 解析数据包头 转发数据包到某些端口 通过查询由控制平面所生成的转发表 传统网络数据平面 数据包--输入端口---拆封和解析,转发策略匹配,转发调度---输出端口(协议相关,...

Firefly-
昨天
6
0
如何高效的阅读uni-app框架?(建议收藏)

作者 | Jeskson 来源 | 达达前端小酒馆 uni-app的框架,配置:page.json,manifest.json,package.json,vue.config.js。脚本,应用程序,main.js。日志打印,定时器,生命周期,页面,页面通...

达达前端小酒馆
昨天
8
0
实现原理专题--存储器的实现(三)

计算机实现原理专题--存储器的实现(二)中描述了一种电平触发器,但是某些应用需要在保持位从0到1变化的过程中对数据端进行保存。这种触发器叫边沿触发器。 一开始Q为0,时钟信号为0。当数据...

FAT_mt
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部