应客户要求部分数据需要使用 “非对称加密算法传递数据”。
记得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方法,方便数据库等入库业务。

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