应客户要求部分数据需要使用 “非对称加密算法传递数据”。
记得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方法,方便数据库等入库业务。
好了,有参考价值就点个赞呗~ 有特殊业务自行改造吧~