简介
吹水时间开始了,是这样的,公司自研开发一个app,因为要运营和上架需要办理安全评估,办理中说到公司app有可能会泄露用户信息,对用户信息没有加密,遇到抓包的可能会导致用户信息泄露,这时我想到了RSA加密数据来保护用户信息,所以自己使用好像有点效果。
一、简单使用RSA加密解密
RSA是jdk自带的,不需要依赖
public class RSA{private static Map<String, String> map = new HashMap<>();public static void main(String[] args) throws Exception {String content = "我喜欢你";genKeyPair();//加密String publicKey = Encryption(content, map.get("publicKey"));System.out.println("加密后:"+publicKey);//解密System.out.println("解密后:"+Decryption(publicKey,map.get("privateKey")));}private static String Encryption(String content,String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {byte[] decodeBase64 = Base64.decodeBase64(publicKey);PublicKey rsa = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decodeBase64));Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.ENCRYPT_MODE,rsa);String outStr = Base64.encodeBase64String(cipher.doFinal(content.getBytes()));return outStr;}private static String Decryption(String publicKey, String privateKey)throws Exception{byte[] publicKeyByte = Base64.decodeBase64(publicKey.getBytes("UTF-8"));byte[] privateKeyByte = Base64.decodeBase64(privateKey.getBytes("UTF-8"));RSAPrivateKey generatePublic = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyByte));Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE, generatePublic);return new String(cipher.doFinal(publicKeyByte));}/*** 随机生成密钥对* @throws NoSuchAlgorithmException*/public static void genKeyPair() throws NoSuchAlgorithmException {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");keyPairGenerator.initialize(1024);KeyPair keyPair = keyPairGenerator.generateKeyPair();//私钥PrivateKey privateK = keyPair.getPrivate();//公钥PublicKey publicK = keyPair.getPublic();//PrivateKey2StringString privateKey = new String(Base64.encodeBase64(privateK.getEncoded()));//PublicKey2StringString publicKey = new String(Base64.encodeBase64(publicK.getEncoded()));System.out.println("公钥:"+publicKey);System.out.println("私钥:"+privateKey);map.put("publicKey",publicKey);map.put("privateKey",privateKey);}
}
结果:
二、Spring Boot 整合 RSA
可以自己生成一个公钥和私钥,也可以在网站中在线生成:http://www.metools.info/code/c80.html
application.yml
#swagger
lanys:swagger:title: 牟某公司description: 牟某公司termsOfServiceUrl: https://eurasia.plus/swagger-ui.htmlContactName: xxxContactUrl: https://eurasia.plus/swagger-ui.htmlContactEmail: 1090613735@qq.comversion: 1.0public:kay: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJVBeO0vImDyMvmRV7bgQJxhzOkLtAAUv6UJSMBP3YtWj6/5DNjaD5Ezt7OXqQ/wz9b+FWTNECsCRC7Nc/kvqiIfHZp4yZYtlzXUbRJk0iu4Y1lv6oKHOXjjnxCjmZwSgXuuDOChNMwqv9GhOQSo5X4HNmrUdTSbAUxfJMfu6WMwIDAQABprivate:kay: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMlUF47S8iYPIy+ZFXtuBAnGHM6Qu0ABS/pQlIwE/di1aPr/kM2NoPkTO3s5epD/DP1v4VZM0QKwJELs1z+S+qIh8dmnjJli2XNdRtEmTSK7hjWW/qgoc5eOOfEKOZnBKBe64M4KE0zCq/0aE5BKjlfgc2atR1NJsBTF8kx+7pYzAgMBAAECgYEAnwoh7f5vsWcF2aTplm6rG/l3aipk8UPU/DFJl26eBOIRwy29+tMl1Xy+4THfw8jnPxFxhZX7Ck+Q5Zlo1FzMRX3Z6ltqCy5H58n4KI7L85MI+dO5l8tjCtxFgNd7NY1gwRjKCjWUsdKqOHzs/+vaeZR1kp2feljTpMBzGGPqS4ECQQD3kU7Er79XuHpk6qK56ihiLT6O8ZnZzhBPRCHq89MIDmt2s13RnSfxEeLZpKlmKLC7VYbZeypoiLxKOdRq1X9BAkEA0C+Y6wUM6h3VC+HzrH5/x++6siHRFwwnDp4U9VmPeesa2a84krNaqptMZtPcm4wr0heDzr5PLksfrSUoYJdscwJAIrBiqB60IfeAdumFuaU82Vzbmi3yT9mW4XR7iC94D3XtyukhKUDrGtVVwwdWkTD8apN2XLzsWU9nisMFp56YwQJAFqFV6hY+dSSRCB2js1h842r3zG1IoUy84iXk+Vam9gXMgWU+rRO6A1mmUHcvP+lYyhmsRkkBqnKYGuYzIWzUaQJBAII0gYs7yRJ4FckmnehD1bMdpgsivlO03qimh7V+fCePG8MMZSq5NFNDP0rogNyo/HLxk5V+tPT05bitJJmHTGM=
注意:公钥和私钥要放为一行,不然会报错。
SecurityParameter
/*** @author lanys* @Description:* @date 22/7/2021 上午11:37*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
@Documented
public @interface SecurityParameter {/*** 入参是否解密,默认是解密*/boolean inDecode() default true;/*** 返回数据是否加密,默认加密*/boolean outEncode() default true;
}
RsaDecodeRequestBodyAdvice
请求我们接口前做数据处理
/*** @author lanys* @Description:* @date 22/7/2021 上午11:26*/
@ControllerAdvice("com.example.materialboot.modules.usercontroller.conterller")
@Slf4j
public class RsaDecodeRequestBodyAdvice implements RequestBodyAdvice {@Value("${lanys.private.kay}")public String privateKay;@Overridepublic boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {return true;}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {boolean encode = false;if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {SecurityParameter methodAnnotation = methodParameter.getMethodAnnotation(SecurityParameter.class);encode = methodAnnotation.inDecode();}if (encode) {log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密");return new MyHttpInputMessage(httpInputMessage);} else {return httpInputMessage;}}@Overridepublic Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {return body;}@Overridepublic Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {return body;}class MyHttpInputMessage implements HttpInputMessage {private HttpHeaders httpHeaders;private InputStream inputStream;public MyHttpInputMessage(HttpInputMessage httpInputMessage) throws IOException {this.httpHeaders = httpInputMessage.getHeaders();String easpString = easpString(IOUtils.toString(httpInputMessage.getBody(), "UTF-8"));this.inputStream = IOUtils.toInputStream(RSAUtils.decryptDataOnJava(easpString,privateKay));}@Overridepublic InputStream getBody() throws IOException {return this.inputStream;}@Overridepublic HttpHeaders getHeaders() {return this.httpHeaders;}/**** @param requestData* @return*/public String easpString(String requestData){if (requestData != null && !requestData.isEmpty()){String s = "{\"requestData\":";if (!requestData.startsWith(s)){throw new RuntimeException("参数【requestData】缺失异常!");}int closeLen = requestData.length()-1;int openLen = "{\"requestData\":".length();String substring = StringUtils.substring(requestData, openLen, closeLen);return substring;}return null;}}
}
EncodeResponseBodyAdvice
获取数据响应时做处理
/*** @author lanys* @Description: 返回数据加密* @date 22/7/2021 上午11:26*/
@Component
@ControllerAdvice("com.example.materialboot.modules.usercontroller.conterller")
@Slf4j
public class EncodeResponseBodyAdvice implements ResponseBodyAdvice {@Autowiredprivate RsaUtil rsaUtil;@Value("${lanys.public.kay}")public String publicKay;@Value("${lanys.private.kay}")public String privateKay;/*** 是否支持这个类* @param methodParameter* @param aClass* @return*/@Overridepublic boolean supports(MethodParameter methodParameter, Class aClass) {//默认是不使用,改为truereturn true;}/*** 返回前处理* @param body* @param methodParameter* @param mediaType* @param aClass* @param serverHttpRequest* @param serverHttpResponse* @return*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {log.info("------响应数据加密-------");boolean encode = false;if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {//获取注解配置的包含和去除字段SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);//出参是否需要加密encode = serializedField.outEncode();}if (encode) {log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");ObjectMapper objectMapper = new ObjectMapper();try {//获取内容String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);// 生成一个解数据的秘钥String aseKey = getRandomString(16);// 将公钥和(生成一个解数据的秘钥)进行加密String encrypted = RSAUtils.encryptedDataOnJava(aseKey, publicKay);// 将响应数据和(生成一个解数据的秘钥)加密String requestData = AesEncryptUtils.encrypt(result, aseKey);System.out.println("解密:"+AesEncryptUtils.decrypt(requestData,aseKey));Map<String, String> map = new HashMap<>();map.put("encrypted", encrypted);map.put("requestData", requestData);return map;} catch (Exception e) {e.printStackTrace();log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());}}return body;}/*** 创建指定位数的随机字符串* @param length 表示生成字符串的长度* @return 字符串*/public static String getRandomString(int length) {String base = "abcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}
}
RSAUtils
public class RSAUtils {/*** 加密算法RSA*/public static final String KEY_ALGORITHM = "RSA";/** *//*** RSA最大加密明文大小*/private static final int MAX_ENCRYPT_BLOCK = 117;/** *//*** RSA最大解密密文大小*/private static final int MAX_DECRYPT_BLOCK = 128;/*** 私钥解密* @param encryptedData* @param privateKey* @return* @throws Exception*/public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {byte[] keyBytes = Base64.decodeBase64(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;}/*** java端私钥解密*/public static String decryptDataOnJava(String data, String PRIVATEKEY) {String temp = "";try {byte[] rs = Base64.decodeBase64(data);temp = new String(RSAUtils.decryptByPrivateKey(rs, PRIVATEKEY),"UTF-8");} catch (Exception e) {e.printStackTrace();}return temp;}/*** 公钥加密* @param data* @param publicKey* @return* @throws Exception*/public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {byte[] keyBytes = Base64.decodeBase64(publicKey);Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(keyBytes));// 对数据加密Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);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;}/*** java端公钥加密*/public static String encryptedDataOnJava(String data, String PUBLICKEY) {try {data = Base64.encodeBase64String(encryptByPublicKey(data.getBytes(), PUBLICKEY));} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return data;}}
AesEncryptUtils
前端工具:前端传过来的数据也是加密的,我们也会解密
public class AesEncryptUtils {//参数分别代表 算法名称/加密模式/数据填充方式private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";/*** 加密* @param content 加密的字符串* @param encryptKey key值* @return* @throws Exception*/public static String encrypt(String content, String encryptKey) throws Exception {KeyGenerator kgen = KeyGenerator.getInstance("AES");kgen.init(128);Cipher cipher = Cipher.getInstance(ALGORITHMSTR);cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));byte[] b = cipher.doFinal(content.getBytes("utf-8"));// 采用base64算法进行转码,避免出现中文乱码return Base64.encodeBase64String(b);}@Value("${lanys.public.kay}")public String publicKay;@Value("${lanys.private.kay}")public String privateKay;/*** 解密* @param encryptStr 解密的字符串* @param decryptKey 解密的key值* @return* @throws Exception*/public static String decrypt(String encryptStr, String decryptKey) throws Exception {KeyGenerator kgen = KeyGenerator.getInstance("AES");kgen.init(128);Cipher cipher = Cipher.getInstance(ALGORITHMSTR);cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));// 采用base64算法进行转码,避免出现中文乱码byte[] encryptBytes = Base64.decodeBase64(encryptStr);System.out.println(encryptBytes.length);byte[] decryptBytes = cipher.doFinal(encryptBytes);return new String(decryptBytes);}
}
2.读入数据
代码如下(示例):
data = pd.read_csv('https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())
UserController
/*** @ClassName UserController* @Author lanys* @Data 1/4/2021 下午4:56*/
@Api(tags = "用户管理")
@RestController
@RequestMapping("v1/user")
public class UserController {@Autowiredprivate SysUserService userService;@SecurityParameter(inDecode = false,outEncode = true)@GetMapping("list")@RequiresPermissions("sys:user:save,sys:role:select")@ApiImplicitParams({ @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "token", required = true, paramType = "header")})public Result UserList(){return Result.success().put(userService.userList());}}
这时响应加密:
encrypted:是加密后的公钥,只需要前端用私钥进行解密获取生成的解密数据秘钥,在根据获取的秘钥对数据进行解密就能获取所响应的数据类型
requestData:指的是加密后的数据
解密测试
/*** 解密*/@ApiOperation(value = "解密测试")@PostMapping("/decryption")@ApiImplicitParam(name = "data",value = "数据",paramType = "query")@ResponseBodypublic Result decryption(String key,String data) throws Exception {String decryptDataOnJava = RSAUtils.decryptDataOnJava(key, privateKay);String decrypt = AesEncryptUtils.decrypt(data, decryptDataOnJava);JSONObject jsonObject = JSON.parseObject(decrypt);String jsonData = jsonObject.getString("data");List<SysUser> dataArr = JSONArray.parseArray(jsonData,SysUser.class);System.out.println(decrypt);return Result.success().put(dataArr);}
总结
最后解密后是String类型的数据,输出是JSON的样子,需要JSON对数据进行转换就可以转换自己原有的数据类型,我是参考网上的案例进行使用,其中也有自己改的代码,吹水结束。