1.原理
RSA是一种非对称加密算法。通过生成密钥对,用公钥加密,用私钥解密。对于前后端分离的项目,让前端获取到公钥对敏感数据加密,发送到后端,后端用私钥对加密后的数据进行解密即可。
2.实现
RSA工具类:提供秘钥对生成、公钥获取、私钥解密的方法。
public class RSAUtil {
private final static Logger logger = LoggerFactory.getLogger(RSAUtil.class);
private static final String RSA = "RSA";
private static final int KEY_SIZE = 2048;
private static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// Bouncy Castle加密库提供的一个类,用于为Java提供加密服务
private static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();
// 密钥对生成
static void createKey() throws Exception{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA, BOUNCY_CASTLE_PROVIDER);
keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(keyPairGenerator.generateKeyPair());
objectOutputStream.close();
}
// 公钥获取
public static RSAPublicKey getPublicKey() throws Exception{
return (RSAPublicKey) getKeyPair().getPublic();
}
// 读取,有密钥对则获取公钥,无密钥对则创建密钥对再获取公钥
static KeyPair getKeyPair()throws Exception{
if(byteArrayOutputStream.size() == 0){
synchronized (byteArrayOutputStream) {
createKey();
}
}
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
KeyPair keyPair= (KeyPair) objectInputStream.readObject();
objectInputStream.close();
byteArrayInputStream.close();
return keyPair;
}
static byte[] decryptByPrivateKey(byte[] encrypttext){
try {
Cipher ci = Cipher.getInstance(RSA, BOUNCY_CASTLE_PROVIDER);
ci.init(Cipher.DECRYPT_MODE, getKeyPair().getPrivate());
return ci.doFinal(encrypttext);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return null;
}
// 接收一个String类型,私钥解密
public static String decodeString(String string){
try {
byte[] input = Base64.getDecoder().decode(string); // 解码Base64字符串为字节数组
byte[] raw = decryptByPrivateKey(input); // 使用私钥解密数据
return new String(raw, StandardCharsets.UTF_8); // 直接将字节数组转换为UTF-8字符串
} catch (Exception e) {
logger.error("解析失败", e);
return "";
}
}
}
为前端提供一个获取公钥的接口
@GetMapping("getRSAPublickey")
public Result<?> getRSAPublickey() {
Map key = new HashMap();
try {
// 通过提供模数和公共指数,可以间接地提供公钥,同时保持安全性。
String modulus = RSAUtil.getPublicKey().getModulus().toString(16);
String publicExponent = RSAUtil.getPublicKey().getPublicExponent().toString(16);
key.put("m", modulus);
key.put("e", publicExponent);
} catch (Exception e) {
}
return Result.ok(key);
}
前端需要导入一个适合前端使用的RSA加密库。利用获取到的模数和公共指数创建RSA公钥对象,对敏感数据加密。
<html>
<script src="rsa.js"></script>
<body>
<script>
window.encryptStr = function (m, e, str) {
var rsa = new RSAKey()
rsa.setPublic(m, e)
return rsa.encrypt(str)
}
</script>
</body>
</html>
后端接收到前端利用RSA加密后的字符串后,直接调用RSAUtil进行解密,得到原文。
String password = RSAUtil.decodeString(pram.getPassword());
需要注意的是,前端加密只是数据传输过程中的一部分安全措施。为了确保数据的安全性,还需要在后端服务器进行相应的安全措施,例如验证用户身份、使用HTTPS协议进行通信等。