Java实现RSA⾮对称加密算法:⽣成密钥对、保存读取密钥、加密解密
RSA 加密算法是⼀种⾮对称加密算法,即 RSA 拥有⼀对密钥(公钥 和 私钥),公钥可公开。公钥加密的数据,只能由私钥解密;私钥加密的数据只能由公钥解密。
为了⽅便读取和保存密钥,先创建⼀个 IO ⼯具类(IOUtils.java):
package com.xiets.rsa;
import java.io.*;
/**
* IO ⼯具类, 读写⽂件
*
* @author xietansheng
*/
public class IOUtils {
public static void writeFile(String data, File file)throws IOException {
OutputStream out = null;
try{
out =new FileOutputStream(file);
out.Bytes());
out.flush();
}finally{
close(out);
}
}
public static String readFile(File file)throws IOException {
InputStream in = null;
ByteArrayOutputStream out = null;
try{
in =new FileInputStream(file);
out =new ByteArrayOutputStream();
byte[] buf =new byte[1024];
int len =-1;
while((len = in.read(buf))!=-1){
out.write(buf,0, len);
}
out.flush();
byte[] data = ByteArray();
return new String(data);
}finally{
close(in);
close(out);
}
}
public static void close(Closeable c){
if(c != null){
try{
c.close();
}catch(IOException e){
// nothing
}
}
}
}
主要两个⽅法:
把⽂本保存到⽂件(String -> File): IOUtils.writeFile(String data, File file)
读取⽂件中的⽂本(File -> String): adFile(File file)
1. ⽣成 RSA 密钥对
Java 加密安全相关的类在 JDK 的java.security.*包下,以及下⾯使⽤到的类均为 JDK 内置的类。
1.1 ⽣成密钥对
// 获取指定算法的密钥对⽣成器
KeyPairGenerator gen = Instance("RSA");
// 初始化密钥对⽣成器(指定密钥长度, 使⽤默认的安全随机数源)
gen.initialize(2048);
// 随机⽣成⼀对密钥(包含公钥和私钥)
KeyPair keyPair = ateKeyPair();
// 获取公钥和私钥
PublicKey pubKey = Public();
PrivateKey priKey = Private();
1.2 保存密钥
1.2.1 Base64 编码保存
将密钥编码转换为 Base64 ⽂本格式保存到⽂件:
// 获取公钥和私钥的编码格式(通过该编码格式可以反过来⽣成公钥和私钥对象)
byte[] pubEncBytes = Encoded();
byte[] priEncBytes = Encoded();
// 把公钥和私钥的编码格式转换为 Base64⽂本⽅便保存
String pubEncBase64 =new BASE64Encoder().encode(pubEncBytes);
String priEncBase64 =new BASE64Encoder().encode(priEncBytes);
// 保存公钥和私钥到指定⽂件
IOUtils.writeFile(pubEncBase64,new File(""));
IOUtils.writeFile(priEncBase64,new File(""));
/
* 通过该⽅法保存的密钥, 通⽤性较好, 使⽤其他编程语⾔也可以读取使⽤(推荐) */
1.2.2 对象序列化保存
PublicKey 和 PrivateKey 均实现了 java.io.Serializable 接⼝,可以直接将整个密钥对象序列化保存。通过该⽅法保存的密钥只能⽤ Java 代码读取反序列化重新⽣成对象使⽤。
// 创建对象输出流, 保存到指定的⽂件
ObjectOutputStream pubOut =new ObjectOutputStream(new FileOutputStream("pub.obj"));
ObjectOutputStream priOut =new ObjectOutputStream(new FileOutputStream("pri.obj"));
// 将公钥/私钥对象序列号写⼊对象输出流
pubOut.writeObject(pubKey);
priOut.writeObject(priKey);
java加密方式有哪些// 刷新并关闭流
pubOut.flush();
priOut.flush();
pubOut.close();
priOut.close();
1.3 读取密钥
1.3.1 读取密钥的 Base64 ⽂本⽣成密钥对象
读取公钥:
// 从公钥保存的⽂件读取公钥的Base64⽂本
String pubKeyBase64 = adFile(new File(""));
// 把公钥的Base64⽂本转换为已编码的公钥bytes
byte[] encPubKey =new BASE64Decoder().decodeBuffer(pubKeyBase64);
// 创建已编码的公钥规格
X509EncodedKeySpec encPubKeySpec =new X509EncodedKeySpec(encPubKey);
// 获取指定算法的密钥⼯⼚, 根据已编码的公钥规格, ⽣成公钥对象
PublicKey pubKey = Instance("RSA").generatePublic(encPubKeySpec);
读取私钥:
// 从私钥保存的⽂件读取私钥的base⽂本
String priKeyBase64 = adFile(new File(""));
// 把私钥的Base64⽂本转换为已编码的私钥bytes
byte[] encPriKey =new BASE64Decoder().decodeBuffer(priKeyBase64);
// 创建已编码的私钥规格
PKCS8EncodedKeySpec encPriKeySpec =new PKCS8EncodedKeySpec(encPriKey);
// 获取指定算法的密钥⼯⼚, 根据已编码的私钥规格, ⽣成私钥对象
PrivateKey priKey = Instance("RSA").generatePrivate(encPriKeySpec);
1.3.2 反序列化⽣成密钥对象
公钥和私钥对象被序列号保存后,可以通过反序列化⽣成回对象。
// 创建对象输如流, 读取保存到指定⽂件的序列化对象
ObjectInputStream pubIn =new ObjectInputStream(new FileInputStream("pub.obj"));
ObjectInputStream priIn =new ObjectInputStream(new FileInputStream("pri.obj"));
// 从读取输如流读取对象, 反序列化⽣成公钥/私钥对象
PublicKey pubKey =(PublicKey) adObject();
PrivateKey priKey =(PrivateKey) adObject();
// 关闭流
pubIn.close();
priIn.close();
2. RSA 加密/解密数据
RSA ⾮对称加密在使⽤中通常公钥公开,私钥保密,使⽤公钥加密,私钥解密。例如 客户端 给 服务端 加密发送数据:
1. 客户端从服务端获取公钥;
2. 客户端⽤公钥先加密要发送的数据,加密后发送给服务端;
3. 服务端拿到加密后的数据,⽤私钥解密得到原⽂。
公钥加密后的数据,只有⽤私钥才能解,只有服务端才有对应的私钥,因此只有服务端能解密,中途就算数据被截获,没有私钥依然不知道数据的原⽂内容,因此达到数据安全传输的⽬的。
2.1 公钥加密
// 获取指定算法的密码器
Cipher cipher = Instance("RSA");
// 初始化密码器(公钥加密模型)
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
// 加密数据, 返回加密后的密⽂
byte[] cipherData = cipher.doFinal(plainData);
2.2 私钥解密
// 获取指定算法的密码器
Cipher cipher = Instance("RSA");
// 初始化密码器(私钥解密模型)
cipher.init(Cipher.DECRYPT_MODE, priKey);
// 解密数据, 返回解密后的明⽂
byte[] plainData = cipher.doFinal(cipherData);
2.3 加密/解密完整代码实例:
package com.xiets.rsa;
pto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* @author xietansheng
*/
public class Main {
public static void main(String[] args)throws Exception {
// 随机⽣成⼀对密钥(包含公钥和私钥)
KeyPair keyPair =generateKeyPair();
// 获取公钥和私钥
PublicKey pubKey = Public();
PrivateKey priKey = Private();
// 原⽂数据
String data ="你好, World!";
// 客户端: ⽤公钥加密原⽂, 返回加密后的数据
byte[] cipherData =Bytes(), pubKey);
// 服务端: ⽤私钥解密数据, 返回原⽂
byte[] plainData =decrypt(cipherData, priKey);
// 输出查看解密后的原⽂
System.out.println(new String(plainData));// 结果打印: 你好, World!
}
/**
* 随机⽣成密钥对(包含公钥和私钥)
*/
private static KeyPair generateKeyPair()throws Exception {
// 获取指定算法的密钥对⽣成器
KeyPairGenerator gen = Instance("RSA");
// 初始化密钥对⽣成器(密钥长度要适中, 太短不安全, 太长加密/解密速度慢) gen.initialize(2048);
// 随机⽣成⼀对密钥(包含公钥和私钥)
// 随机⽣成⼀对密钥(包含公钥和私钥)
ateKeyPair();
}
/**
* 公钥加密数据
*/
private static byte[]encrypt(byte[] plainData, PublicKey pubKey)throws Exception {
// 获取指定算法的密码器
Cipher cipher = Instance("RSA");
// 初始化密码器(公钥加密模型)
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
// 加密数据, 返回加密后的密⽂
return cipher.doFinal(plainData);
}
/**
* 私钥解密数据
*/
private static byte[]decrypt(byte[] cipherData, PrivateKey priKey)throws Exception {
// 获取指定算法的密码器
Cipher cipher = Instance("RSA");
// 初始化密码器(私钥解密模型)
cipher.init(Cipher.DECRYPT_MODE, priKey);
// 解密数据, 返回解密后的明⽂
return cipher.doFinal(cipherData);
}
}
3. 封装 RSA ⼯具类
为了在实践中⽅便使⽤ RSA 加密/解密数据,把 RSA 的 ⽣成密钥对、保存密钥、读取密钥、加密/解密 等操作封装到⼀个⼯具类中,⽅便直接使⽤。
2.1 ⼯具类: RSAUtils.java
引⽤⽂章开头封装的 IO ⼯具类:IOUtils.java
RSA ⼯具类(RSAUtils.java)完整源码:
package com.xiets.rsa;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
pto.Cipher;
import java.io.File;
import java.io.IOException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA ⼯具类(⽣成/保存密钥对、加密、解密)
*
* @author xietansheng
*/
public class RSAUtils {
/** 算法名称 */
private static final String ALGORITHM ="RSA";
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论