一. 原理
非对称加密算法是一种密钥的保密方法。
非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。 非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将公钥公开,需要向甲方发送信息的其他角色(乙方)使用该密钥(甲方的公钥)对机密信息进行加密后再发送给甲方;甲方再用自己私钥对加密后的信息进行解密。甲方想要回复乙方时正好相反,使用乙方的公钥对数据进行加密,同理,乙方使用自己的私钥来进行解密。
二. 场景
多用于银行等安全级别较高系统
三. 实现原理
整体过程描述:首先通过RSA生成工具生成公玥密钥,分发给各系统,各系统在调接口时,利用私约生成签名,公玥加密数据,被调用方利用公玥验签,私约解密数据。
四.java案例
package com.atguigu.springcloud.util.interrup;import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.net.URLDecoder;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;public class RSAUtil04 {/*** 加密算法RSA*/public static final String KEY_ALGORITHM = "RSA";/*** 签名算法*/public static final String SIGNATURE_ALGORITHM = "MD5withRSA";/*** 获取公钥的key*/private static final String PUBLIC_KEY = "RSAPublicKey";/*** 获取私钥的key*/private static final String PRIVATE_KEY = "RSAPrivateKey";/*** RSA最大加密明文大小*/private static final int MAX_ENCRYPT_BLOCK = 117;/*** RSA最大解密密文大小*/private static final int MAX_DECRYPT_BLOCK = 128;/*** 生成密钥对(公钥和私钥)** @return* @throws Exception*/public static Map<String, Object> genKeyPair() throws Exception {KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);keyPairGen.initialize(1024);KeyPair keyPair = keyPairGen.generateKeyPair();RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();Map<String, Object> keyMap = new HashMap<String, Object>(2);keyMap.put(PUBLIC_KEY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}/*** 获取公钥字符串** @param keyMap 密钥对* @return 公钥字符串* @throws Exception 异常*/public static String getPublicKeyStr(Map<String, Object> keyMap) throws Exception {//获得map中的公钥对象 转为key对象Key key = (Key) keyMap.get(PUBLIC_KEY);//编码返回字符串return encryptBASE64(key.getEncoded());}/*** 获取私钥字符串** @param keyMap 密钥对* @return 私钥字符串* @throws Exception 异常*/public static String getPrivateKeyStr(Map<String, Object> keyMap) throws Exception {//获得map中的私钥对象 转为key对象Key key = (Key) keyMap.get(PRIVATE_KEY);//编码返回字符串return encryptBASE64(key.getEncoded());}/*** 将byte[]进行Base64编码** @param key 待编码的byte[]* @return 编码后的字符串*/public static String encryptBASE64(byte[] key) {return Base64.getMimeEncoder().encodeToString(key);}/*** 用私钥对信息生成数字签名** @param data 已加密数据* @param privateKey 私钥(BASE64编码)** @return* @throws Exception*/public static String sign(byte[] data, String privateKey) throws Exception {byte[] keyBytes = Base64Utils.decode(privateKey);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);signature.initSign(privateK);signature.update(data);return Base64Utils.encode(signature.sign());}/*** 校验数字签名** @param data 已加密数据* @param publicKey 公钥(BASE64编码)* @param sign 数字签名** @return* @throws Exception**/public static boolean verify(byte[] data, String publicKey, String sign)throws Exception {byte[] keyBytes = Base64Utils.decode(publicKey);X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);PublicKey publicK = keyFactory.generatePublic(keySpec);Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);signature.initVerify(publicK);signature.update(data);return signature.verify(Base64Utils.decode(sign));}/*** 私钥解密** @param encryptedData 已加密数据* @param privateKey 私钥(BASE64编码)* @return* @throws Exception*/public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)throws Exception {byte[] keyBytes = Base64Utils.decode(privateKey);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, privateK);int inputLen = encryptedData.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段解密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_DECRYPT_BLOCK) {cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);} else {cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return decryptedData;}/*** 公钥解密** @param encryptedData 已加密数据* @param publicKey 公钥(BASE64编码)* @return* @throws Exception*/public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)throws Exception {byte[] keyBytes = Base64Utils.decode(publicKey);X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key publicK = keyFactory.generatePublic(x509KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, publicK);int inputLen = encryptedData.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段解密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_DECRYPT_BLOCK) {cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);} else {cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return decryptedData;}/*** 公钥加密** @param data 源数据* @param publicKey 公钥(BASE64编码)* @return* @throws Exception*/public static byte[] encryptByPublicKey(byte[] data, String publicKey)throws Exception {byte[] keyBytes = Base64Utils.decode(publicKey);X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key publicK = keyFactory.generatePublic(x509KeySpec);// 对数据加密Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, publicK);int inputLen = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段加密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);} else {cache = cipher.doFinal(data, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_ENCRYPT_BLOCK;}byte[] encryptedData = out.toByteArray();out.close();return encryptedData;}/*** 私钥加密** @param data 源数据* @param privateKey 私钥(BASE64编码)* @return* @throws Exception*/public static byte[] encryptByPrivateKey(byte[] data, String privateKey)throws Exception {byte[] keyBytes = Base64Utils.decode(privateKey);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, privateK);int inputLen = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段加密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);} else {cache = cipher.doFinal(data, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_ENCRYPT_BLOCK;}byte[] encryptedData = out.toByteArray();out.close();return encryptedData;}/*** 获取私钥** @param keyMap 密钥对* @return* @throws Exception*/public static String getPrivateKey(Map<String, Object> keyMap)throws Exception {Key key = (Key) keyMap.get(PRIVATE_KEY);return Base64Utils.encode(key.getEncoded());}/*** 获取公钥** @param keyMap 密钥对* @return* @throws Exception*/public static String getPublicKey(Map<String, Object> keyMap)throws Exception {Key key = (Key) keyMap.get(PUBLIC_KEY);return Base64Utils.encode(key.getEncoded());}public static void main(String[] args) {try {String json = "{\n" +" \"successful\": false,\n" +" \"code\": 1010,\n" +" \"message\": \"参数【_identificationId】没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限没有该字段查询权限\",\n" +" \"timestamp\": 1695881640402,\n" +" \"requestId\": \"4d8a793df32141d1bf6fb0a2bd527fa8\"\n" +"}";byte[] data = json.getBytes();Map<String, Object> stringObjectMap = genKeyPair();String pubK = getPublicKeyStr(stringObjectMap);String priK = getPrivateKeyStr(stringObjectMap);//System.out.println("---------------------------------公钥加密私钥解密-----------------------------------");// 获取签名String sign = RSAUtil04.sign(data, priK);boolean flag = RSAUtil04.verify(data, pubK, sign);byte[] sendByte = RSAUtil04.encryptByPublicKey(data, pubK);// 把加密数据转string// 这是实际接口中要发送的数据String sendStr = Base64Utils.encode(sendByte);System.out.println("encode1="+sendStr);// string转二进制byte[] sendBytes = Base64Utils.decode(sendStr);// 数据解密byte[] resultByte = new byte[0];resultByte = RSAUtil04.decryptByPrivateKey(sendBytes, priK);String resultStr = new String(resultByte);System.out.println("resultStr1="+resultStr);System.out.println("---------------------------------私钥加密公钥解密-----------------------------------");byte[] sendByte2 = RSAUtil04.encryptByPrivateKey(data, priK);// 把加密数据转string// 这是实际接口中要发送的数据String sendStr2 = Base64Utils.encode(sendByte2);System.out.println("encode2="+sendStr2);// string转二进制byte[] sendBytes2 = Base64Utils.decode(sendStr2);// 数据解密byte[] resultByte2 = new byte[0];resultByte2 = RSAUtil04.decryptByPublicKey(sendBytes2, pubK);String resultStr2 = new String(resultByte2);System.out.println("resultStr2="+resultStr2);System.out.println("--------------------------用别人返回的密文公钥解密------------------------------");//密文String data2 = "HMoBWOOr2z7yTEl0O4lwkNzJM8ntzuSNfViIQfbM8vCoeaVCvVub41n51QMsPgklD2cMa17dFp3SpkUGwRz2f7c7ckA85RYZm0UQtUB7yjYGNwKdiyGnHOu4WVmL3SoOFC2gYJWV0gznsiGOS02WJ3FRLNzneW+tCk1RA7jCDx+WL1f+K7AFklOFRAoJAqFq7VkPTaRNyjDFY4VYZwUGR+KxDPwvGJctaCRwvcZ0DmhSTYoKrqCbrUlV62WcUO4WPjVMphil8V8rLZ+E5xL675a75MTLhpw/QEUMfXOtpDz3YTMWvYrfzV/un/TPnuxVseEmQDjamJ1sOD4VPesY34whHS+82n2ds5NQZG7IzgQ3maPMbDjWHk4CNFj00LWFAia4dlIs/tbiPQBMkQx33jwmcc5uI3PUEhVg+lJIuJKEbr7R9oKBUGbimhmE8C6XlK7kKw97cisWX5tJQsnHpMKwr+tZy7pY+lZX/AT8Ek1624aHx4jDU7MHdGbv1zuJS95AziLce7GEUExeRvs93Mj3KHZgD6rVO+KcU5JlfE2Wj176iuaWRjAtib0neZpMRqTmH4FZdFPZRfP1YpWrZrguwyeRhWjEs+h6Khf6QBQRRxdcOf6OrJz1ykP7Eq7+OoTjqSViq5ARfhqjWbwyUwVZ/JMWa3DAxCR9m+xYlmI=";byte[] sendBytes4 = Base64Utils.decode(data2);// 数据解密byte[] resultByte4 = new byte[0];String pubk2="***公钥***";resultByte4 = RSAUtil04.decryptByPublicKey(sendBytes4, pubk2);String resultStr4 = new String(resultByte4);//加上下面这行解密就不会出现乱码原因:分段解密的时候汉字 字节代表3个字节,在解密最大长度分界如果3个字节被分隔成了两段转成string就会乱码了resultStr4 = URLDecoder.decode(resultStr4);System.out.println("resultStr4="+resultStr4);} catch (Exception e) {e.printStackTrace();}}
}
五.发现的问题:对方返回的密文在本地使用公钥解密时乱码
效果:
返回值:
分段解密的时候汉字 字节代表3个字节,在解密最大长度分界如果3个字节被分隔成了两段转成string就会乱码了
加上下面这样代码就OK
resultStr4 = URLDecoder.decode(resultStr4);