应客户要求部分数据需要使用 “非对称加密算法传递数据”。
记得JF里有个加密工具类AesKit,但是它是对称加密。
该客户对第三方包要求比较严格,会进行审核,比较麻烦。决定模仿AesKit手撸一个RsaKit。
什么是非对称加密? 答: 非对称加密算法需要两个密钥:公开密钥(public key)和 私有密钥(private key)。 公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。 非对称加密的过程: A要向B发送信息,A和B都要产生一对用于加密和解密的公钥和私钥。 A的私钥保密,A的公钥告诉B;B的私钥保密,B的公钥告诉A。 A要给B发送信息时,A用B的公钥加密信息,因为A知道B的公钥。 A将这个消息发给B(已经用B的公钥加密消息)。 B收到这个消息后,B用自己的私钥解密A的消息,其他所有收到这个报文的人都无法解密,因为只有B才有B的私钥。 反过来,B向A发送消息也是一样。 常见的非对称加密算法: RSA,ECC,DSA... 摘自:https://www.bbsmax.com/A/n2d9QXywdD/
想使用ECC加密来着,但是这个需要导包。最后还是选择RSA算法了,JDK有自带。
废话不多说了,上石马!
import com.jfinal.kit.Base64Kit;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA加密方法
*/
public class RsaKit {
private final static String ALGORITHM = "RSA";
/**
* 直接生成公钥、私钥对象
*/
public static KeyPair getKeyPair() {
return getKeyPair(2048);
}
public static KeyPair getKeyPair(int keySize) {
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM);
generator.initialize(keySize);
return generator.generateKeyPair();
}catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
/**
* 公钥加密
*/
public static String encrypt(RSAPublicKey key, String data){
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
int maxSingleSize = key.getModulus().bitLength() / 8 - 11;
byte[][] dataArray = splitArray(data.getBytes(), maxSingleSize);
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (byte[] s : dataArray) {
out.write(cipher.doFinal(s));
}
return Base64Kit.encode(out.toByteArray());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String encrypt(KeyPair key, String data){
return encrypt((RSAPublicKey) key.getPublic(), data);
}
public static String encrypt(String key, String data){
return encrypt((RSAPublicKey) toKey(key, true), data);
}
/**
* 私钥解密
*/
public static String decrypt(RSAPrivateKey keyPair, String data){
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keyPair);
int modulusSize = keyPair.getModulus().bitLength() / 8;
byte[] decodeData = Base64Kit.decode(data);
byte[][] splitArrays = splitArray(decodeData, modulusSize);
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (byte[] arr : splitArrays) {
out.write(cipher.doFinal(arr));
}
return new String(out.toByteArray());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public static String decrypt(KeyPair key, String data){
return decrypt((RSAPrivateKey) key.getPrivate(), data);
}
public static String decrypt(String key, String data){
return decrypt((RSAPrivateKey) toKey(key, false), data);
}
/**
* 钥对象 转换为 字符串 方便数据库存储
*/
public static String toStr(Key key){
return Base64Kit.encode(key.getEncoded());
}
public static String toStrByPublic(KeyPair key){
return toStr(key.getPublic());
}
public static String toStrByPrivate(KeyPair key){
return toStr(key.getPrivate());
}
/**
* 字符串钥 转换为 钥对象 方便使用
*/
public static <T> T toKey(String key, boolean isPublic){
try {
KeyFactory factory = KeyFactory.getInstance(ALGORITHM);
byte[] bytes = Base64Kit.decode(key);
if (isPublic){
return (T) factory.generatePublic(new X509EncodedKeySpec(bytes));
}
return (T) factory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException(e);
}
}
private static byte[][] splitArray(byte[] data, int len){
int dataLen = data.length;
if (dataLen <= len) {
return new byte[][]{data};
}
byte[][] result = new byte[(dataLen-1)/len + 1][];
int resultLen = result.length;
for (int i = 0; i < resultLen; i++) {
if (i == resultLen - 1) {
int length = dataLen - len * i;
byte[] single = new byte[length];
System.arraycopy(data, len * i, single, 0, length);
result[i] = single;
break;
}
byte[] single = new byte[len];
System.arraycopy(data, len * i, single, 0, len);
result[i] = single;
}
return result;
}
}测试:
public static void main(String[] args) {
KeyPair key = RsaKit.getKeyPair();
String s = "Abc 中文 @#";
System.out.println(s);
//加密
String str = RsaKit.encrypt(key, s);
//解密
String s1 = RsaKit.decrypt(key, str);
System.out.println(str);
System.out.println(s1);
System.out.println("-----------");
//公钥字符串
String publicStr = RsaKit.toStrByPublic(key);
//私钥字符串
String privateStr = RsaKit.toStrByPrivate(key);
System.out.println(publicStr);
System.out.println(privateStr);
System.out.println("-----------");
//加密
str = RsaKit.encrypt(publicStr, s);
//解密
s1 = RsaKit.decrypt(privateStr, str);
System.out.println(str);
System.out.println(s1);
System.out.println("-----------");
}结果:

测试结果方法没问题可以加密解密。
再搭配前端JS使用,我选择jsencrypt.min.js 因为够简单:)
JS下载地址:https://www.bootcdn.cn/jsencrypt/
比如Controller渲染页面的时候,把公钥放在页面中,供JS调取使用:
<script src="assets/jsencrypt/jsencrypt.min.js"></script>
<script>
let jse = new JSEncrypt();
jse.setPublicKey("#(publicKey)");
var str = jse.encrypt("Abc 中文 @#");
console.log("加密数据:" + str)
</script>浏览器控制台打印出:

复制密文到Java这边再跑一下(相当于js提交到Controller接收参数后去解密一样,这里方便测试):
public static void main(String[] args) {
//私钥
String privateStr = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAINpw8mqG2xmf9rpWMd/hNG/s4NDsdx/3UubjTPtrjif9srBtIGxKg+OGPMjmm9wcHzUC+EKTn3SicOGt/GrLKBTdQjFrNPd+CwurWcJkrm3yf3krAbFl0e5ABWQ/ugMm3KaOA+4E9jPXD6lQqO2BcUu/K7nGSe2z9EXL3DkcFEpAgMBAAECgYBJbfPk35RrQer3W6Qr9Wk1+rRICK990kTJVqXT+l97bIbuTMacIW6rOm9ejOpikqWIWsQ4fxXU4KvyAXkPJOLN9lR2jXxLQ4GP8zVZUYSe4kdfydubWpMt/62E4iF3OmKIM7OJ+9f8iSmGM+Nb+Jzhr39hbej2plbbjSsbaOMXwQJBALoS/P+TvQQMhM+JMnweqm/qE9HI9G4gb9xjp93WZiJnHdSefJh3wdtaKLA8fzhVHP0Z07/WnL6FibSbVaTvGhUCQQC0zC/VEPJjmpff9Q5kI37xtI614wHbNSBgoIj+eUheSz4bOVe8E30Jm+n2MAEBnOYvlfOjyi/Q2htJltCWVwPFAkBxzflFC4P4fPuhvyTMeyj85+qVTVYKIPapkZ3y9RVkhzLcfs5vphc/5KWsHGQm/Q+M0YYL9+PINv5hIvw/syl9AkAt3+V/JaarOU2yCOcW557NS6guZKRS778AZZt9Hl8LdgITPFCTq0o9xu7tha6rrxkFGAJTG/lYAA+Oc5MHopqVAkAfokP2BKDtRjSXzRotJcwvrOJgXTocSrPCBB74BrfChE8KDFDkY0aIFGOQ8UKdhI4tEcETu0jiWULQ9QY126EH";
//密文
String str = "NAGhtVY+wbETv5J159R14v1zxTGu0CKQmUJbC8XV3NfuFlKYTrd/WQ+/QpMbLX2YKCkCO7yPfD5srPG7oNUppzYW/G1mQNOxlSkia+M0Im5iFa4IAHwXfpxW2WrmlULJ9fKWN2JRKOWMAfzB4Pkx6S9qie8/cazM06yTIwbQA/0=";
//解密
String s1 = RsaKit.decrypt(privateStr, str);
System.out.println(s1);
System.out.println("-----------");
}
OK!能还原内容。莫马达!
如果是单机使用,可以直接static 秘钥方便使用:
private static final KeyPair keyPair = RsaKit.getKeyPair();
KeyPair支持序列化,所以Redis和EhCache等都可以直接使用。
并且RsaKit加了toStr方法,方便数据库等入库业务。

好了,有参考价值就点个赞呗~ 有特殊业务自行改造吧~