Java 实现 RSA 非对称加密
非对称加密算法:用两个密钥(公钥/私钥)对数据进行加密和解密。
一、原理
非对称加密算法实现机密信息交换的基本过程是:
1)甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;
2)得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;
3)甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。
二、编码
1.依赖
前端(乙方):jsencrypt.min.js
后端(甲方):Java原生API
2.后端加密/解密工具类编码
import java.security.Key;
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.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 javax.crypto.Cipher;
import org.apache.tomcat.util.codec.binary.Base64;
public class RsaUtil {
/**
* 定义加密方式
*/
private final static String KEY_RSA = "RSA";
/**
* 定义签名算法
*/
private final static String KEY_RSA_SIGNATURE = "MD5withRSA";
/**
* 定义公钥算法
*/
private final static String KEY_RSA_PUBLICKEY = "RSAPublicKey";
/**
* 定义私钥算法
*/
private final static String KEY_RSA_PRIVATEKEY = "RSAPrivateKey";
private RsaUtil() {}
/**
* 创建密钥
* @return
*/
public static Map<String, Object> generateKey() {
Map<String, Object> map = null;
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_RSA);
generator.initialize(1024);
KeyPair keyPair = generator.generateKeyPair();
// 公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 将密钥封装为map
map = new HashMap<>(2);
map.put(KEY_RSA_PUBLICKEY, publicKey);
map.put(KEY_RSA_PRIVATEKEY, privateKey);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return map;
}
/**
* 用私钥对信息生成数字签名
* @param data 加密数据
* @param privateKey 私钥
* @return
*/
public static String sign(String privateKey, byte[] data) {
String str = "";
try {
// 解密由base64编码的私钥
byte[] bytes = decryptBase64(privateKey);
// 构造PKCS8EncodedKeySpec对象
PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(bytes);
// 指定的加密算法
KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
// 取私钥对象
PrivateKey key = factory.generatePrivate(pkcs);
// 用私钥对信息生成数字签名
Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
signature.initSign(key);
signature.update(data);
str = encryptBase64(signature.sign());
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
/**
* 校验数字签名
* @param data 加密数据
* @param publicKey 公钥
* @param sign 数字签名
* @return 校验成功返回true,失败返回false
*/
public static boolean verify(String publicKey, byte[] data, String sign) {
boolean flag = false;
try {
// 解密由base64编码的公钥
byte[] bytes = decryptBase64(publicKey);
// 构造X509EncodedKeySpec对象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
// 指定的加密算法
KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
// 取公钥对象
PublicKey key = factory.generatePublic(keySpec);
// 用公钥验证数字签名
Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
signature.initVerify(key);
signature.update(data);
flag = signature.verify(decryptBase64(sign));
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* 公钥加密
* @param key 公钥
* @param data 待加密数据
* @return
*/
public static byte[] encryptByPublicKey(String key, String data) {
byte[] result = null;
try {
// 获取公钥字符串时,进行了encryptBase64操作,因此此处需对公钥钥解密
byte[] bytes = decryptBase64(key);
// 取得公钥
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
PublicKey publicKey = factory.generatePublic(keySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
result = cipher.doFinal(data.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 私钥解密
* @param data 加密数据
* @param key 私钥
* @return
*/
public static byte[] decryptByPrivateKey(String key, byte[] data) {
byte[] result = null;
try {
// 获取私钥字符串时,进行了encryptBase64操作,因此此处需对私钥解密
byte[] bytes = decryptBase64(key);
// 取得私钥
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
PrivateKey privateKey = factory.generatePrivate(keySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
result = cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 私钥加密
* @param data 待加密数据
* @param key 私钥
* @return
*/
public static byte[] encryptByPrivateKey(String key, byte[] data) {
byte[] result = null;
try {
byte[] bytes = decryptBase64(key);
// 取得私钥
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
PrivateKey privateKey = factory.generatePrivate(keySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
result = cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 公钥钥解密
* @param key 公钥
* @param data 加密数据
* @return
*/
public static byte[] decryptByPublicKey(String key, byte[] data) {
byte[] result = null;
try {
// 对公钥解密
byte[] bytes = decryptBase64(key);
// 取得公钥
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
PublicKey publicKey = factory.generatePublic(keySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
result = cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 获取公钥
* @param map
* @return
*/
public static String getPublicKey(Map<String, Object> map) {
String str = "";
try {
Key key = (Key) map.get(KEY_RSA_PUBLICKEY);
str = encryptBase64(key.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
/**
* 获取私钥
* @param map
* @return
*/
public static String getPrivateKey(Map<String, Object> map) {
String str = "";
try {
Key key = (Key) map.get(KEY_RSA_PRIVATEKEY);
str = encryptBase64(key.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
/**
* BASE64 解密
* @param key 需要解密的字符串
* @return 字节数组
* @throws Exception
*/
public static byte[] decryptBase64(String key) throws Exception {
return Base64.decodeBase64(key);
}
/**
* BASE64 加密
* @param key 需要加密的字节数组
* @return 字符串
* @throws Exception
*/
public static String encryptBase64(byte[] key) throws Exception {
return Base64.encodeBase64String(key);
}
}
3.接口编码
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.light.utils.RsaUtil;
import com.light.vo.Result;
@RestController
public class RsaController {
/**
* 获取公钥
* @param session
* @return
* @throws Exception
*/
@GetMapping("rsa/publicKey")
public Result randomNum(HttpSession session) throws Exception {
Map<String, Object> map = RsaUtil.generateKey();
String publicKey = RsaUtil.getPublicKey(map);
String privateKey = RsaUtil.getPrivateKey(map);
session.setAttribute("privateKey", privateKey);
return Result.success(publicKey);
}
/**
* 解密
* @param msg
* @param session
* @return
*/
@RequestMapping("rsa/decrypt")
public Result decrypt(String msg, HttpSession session) {
String rtStr = "";
try {
String privateKey = (String) session.getAttribute("privateKey");
session.removeAttribute("privateKey");
//下边需要解密的字符串需要使用RsaUtils.decryptBase64转成byte[]
rtStr = new String(RsaUtil.decryptByPrivateKey(privateKey, RsaUtil.decryptBase64(msg)));
// 下边的写法会报错:javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
// rtStr = new String(RsaUtils.decryptByPrivateKey(privateKey, msg.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return Result.success(rtStr);
}
}
4.前端编码
//1.通过ajax获取公钥
//2.加密数据
var encrypt = new JSEncrypt();//来源于jsencrypt.min.js
encrypt.setPublicKey("公钥");
var content = encrypt.encrypt("待加密数据");//content为加密后的密文
//3.通过ajax提交加密数据
三、总结
如果使用对称加密,http请求下通过抓包工具可以获取用户提交的参数数据,暴露加密数据和密钥。由于前端js加密源码是可见的,黑客可以很容易的对加密数据进行破解。
但是,当使用非对称加密时,由于加密和破解使用的不同的密钥,因此即使抓包获取到参数(公钥),黑客也很难破解数据。