RSA是当前最流行的非对称加密方式,使用公钥加密使用密钥解密,如何妥善的保管密钥就成了关键。
动态生成密钥
工具类
package com.yitong.utils;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
/**
* RSA工具类
*/
public class RSAUtil {
/**
* @description: 随机生成RSA密钥对(密钥默认长度为1024)
* @params:
* @return:
*/
public static KeyPair getRSAKeyPair() {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
return kpg.genKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* @description: 随机生成RSA密钥对
* @params:
* @return:
*/
public static KeyPair getRSAKeyPair(int length) {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(length);
return kpg.genKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* @description: 用公钥加密
* @params:
* @return: String
*/
public static String encryptData(String data, PublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return HexUtil.encode(cipher.doFinal(data.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @description: 用私钥解密
* @params:
* @return: String
*/
public static String decryptData(String encryptedData, PrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(HexUtil.decode(encryptedData)));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @description: 字符串还原公钥
* @params:
* @return: PublicKey
*/
public static PublicKey getPublicKey(String key) {
try {
byte[] keyBytes;
keyBytes = Base64Util.decode(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = null;
keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @description: 字符串还原公钥Base64
* @params:
* @return: String
*/
public static String getPublicKey(PublicKey publicKey) {
return Base64Util.encode(publicKey.getEncoded());
}
/**
* @description: 字符串还原私钥
* @params:
* @return: PrivateKey
*/
public static PrivateKey getPrivateKey(String key) {
try {
byte[] keyBytes;
keyBytes = Base64Util.decode(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = null;
keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @description: 字符串还原私钥Base64
* @params:
* @return: String
*/
public static String getPrivateKey(PrivateKey privateKey) {
return Base64Util.encode(privateKey.getEncoded());
}
}
使用
static String plainText = "123456789123456789";// 明文
static String cipherText;// 密文
static PublicKey publicKey;
static PrivateKey privateKey;
private static void RSAEncrypt() {
if (null == publicKey) {
KeyPair aKey = RSAUtil.getRSAKeyPair();
publicKey = aKey.getPublic();
privateKey = aKey.getPrivate();
String pString = RSAUtil.getPublicKey(publicKey);
String pString2 = RSAUtil.getPrivateKey(privateKey);
System.out.println("公钥:" + pString + "\n私钥:" + pString2);
}
for (int i = 0; i < 5; i++) {
cipherText = RSAUtil.encryptData(plainText, publicKey);
System.out.println("加密密文:" + cipherText);
try {
System.out.println("解密明文:" + RSAUtil.decryptData(cipherText, privateKey));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这个是动态生成一对密钥的用法,这里存在一个问题,自己生成的一对密钥别人不知道,除了自己任何人都没有办法解密,我们一般都是自己加密给别人解密,这样就得想办法把私钥传给别人,但存在泄露的风险。
利用证书和keystore方式加解密
这种方式是为了解决传递密钥存在泄露风险的问题,keystore拥有完整的一对密钥,既包含公钥也包含私钥,证书只包含公钥,只能利用证书进行加密。
利用keytool生成证书
以win系统为例,打开cmd,输入:keytool -genkey -alias 你的证书别名 -keyalg 密钥算法 -keystore 证书库文件保存的位置和文件名 -keysize 密钥长度 -validity 证书有效期天数
示例:keytool -genkey -alias mykey -keyalg RSA -keystore D:/mk.keystore -keysize 1024 -validity 36500
然后按照提示输入密钥库的相关信息:
输入密钥库口令:
再次输入新口令:
您的名字与姓氏是什么?
[Unknown]: zhang
您的组织单位名称是什么?
[Unknown]: pc
您的组织名称是什么?
[Unknown]: pc
您所在的城市或区域名称是什么?
[Unknown]: shanghai
您所在的省/市/自治区名称是什么?
[Unknown]: shanghai
该单位的双字母国家/地区代码是什么?
[Unknown]: 86
CN=zhang, OU=yitong, O=pc, L=shanghai, ST=shanghai, C=86是否正确?
[否]: 是
输入 <mykey> 的密钥口令
(如果和密钥库口令相同, 按回车):
再次输入新口令:
Warning:
JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore D:/mk.keystore -destkeystore D:/mk.keystore -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。
这样就在D盘根目录下生成一个密钥库,这个密钥库相当重要一定要妥善保管,接下来就需要生成证书了:
keytool -export -alias mykey -keystore D:/mk.keystore -file D:/mk.cer
这样就能再D盘根目录下看到证书文件了,我们可以根据需要在原有的密钥库添加新的密钥。
加解密
直接上代码:
static String plainText = "123456789123456789";// 明文
static String cipherText;// 密文
static PublicKey publicKey;
static PrivateKey privateKey;
static String certificatePath = "D:/mk.cer";
static String keystorePath = "D:/mk.keystore";
private static void CerEncrypt() throws Exception {
// 加密
CertificateFactory cff = CertificateFactory.getInstance("X.509");
InputStream in = new FileInputStream(certificatePath);
Certificate cf = cff.generateCertificate(in);
PublicKey pString = cf.getPublicKey(); // 得到证书文件携带的公钥
System.out.println("公钥:" + Base64Util.encode(pString.getEncoded()));
cipherText = RSAUtil.encryptData(plainText, pString);
System.out.println("加密密文:" + cipherText);
// 解密
FileInputStream inputStream = new FileInputStream(keystorePath);
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(inputStream, "000000".toCharArray());
inputStream.close();
String alias = "mykey";
String pswd = "000000";
Certificate cert = ks.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, pswd.toCharArray());
System.out.println("私钥:" + RSAUtil.getPrivateKey(privateKey) + "\n公钥:" + RSAUtil.getPublicKey(publicKey));
System.out.println(RSAUtil.decryptData(cipherText, privateKey));
}
应用
当我们的报文需要RSA加密的时候就可以将证书放在客户端,keystore放在服务端,这样客户端发送的报文就能自行加密,在服务端利用keystone文件获取私钥进行解密。