RSA数据加解密,解决项目验收扫漏风险
在实际开发中,大家基本都不会对表单数据进行加密处理,但在进行项目实际验收过程中,第三方会进行系统的漏洞和风险点扫描,若发现数据传输为明文传输则会要求整改。小编今天便遇到了,便采用RSA来解决该类风险。
如这样的表单提交就将用户的账号信息铭文传输了:
提示:本文只描述如何实现加解密操作和提供实现的简易代码,感兴趣的可以进行参考。
文章目录
1、RSA是什么?
RSA公开密钥密码体制是一种使用不同的加密密钥与解密密钥,“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制 [2] 。
在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK [2] 。百度可见。
2、封装RSA加解密工具类
1.引入相关依赖库
其中有用到Base64进行字符操作,所以需要引入该依赖。
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
2.RSAUtil工具类
RSA加解密的主要思路为,根据自定义字符串进行公钥和私钥的生成,该公钥和私钥是一对匹配使用才能正确解密。在使用中服务端生成公钥和私钥,只提供给客户端一种密钥的值即可,客户端采用该(公)密钥进行数据加密,服务端使用对应的(私)密钥解密。完成数据的加密传输。
直接上这边实现的工具类代码:
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA加解密工具类,实现公钥加密私钥解密和私钥解密公钥解密
*/
public class RSAUtils {
private static final String yourText = "最孤独的海洋";
public static void main(String[] args) throws Exception {
RSAKeyPair keyPair = generateKeyPair();
System.out.println("公钥:" + keyPair.getPublicKey());
System.out.println("私钥:" + keyPair.getPrivateKey());
test1(keyPair.getPublicKey(),keyPair.getPrivateKey(), yourText);
test2(keyPair.getPublicKey(),keyPair.getPrivateKey(), yourText);
}
/**
* 公钥加密私钥解密
*/
private static void test1(String publicKey, String privateKey,String source) throws Exception {
System.out.println("***************** 公钥加密私钥解密开始 *****************");
System.out.println("加密前:" + source);
String text1 = encryptByPublicKey(publicKey, source);
System.out.println("加密后:" + text1);
String text2 = decryptByPrivateKey(privateKey, text1);
System.out.println("解密后:" + text2);
}
/**
* 私钥加密公钥解密
*
* @throws Exception
*/
private static void test2(String publicKey, String privateKey, String source) throws Exception {
System.out.println("***************** 私钥加密公钥解密开始 *****************");
System.out.println("加密前:" + source);
String text1 = encryptByPrivateKey(privateKey, source);
System.out.println("加密后:" + text1);
String text2 = decryptByPublicKey(publicKey, text1);
System.out.println("解密后:" + text2);
}
/**
* 公钥解密
*
* @param publicKeyText
* @param text
* @return
* @throws Exception
*/
public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 私钥加密
*
* @param privateKeyText
* @param text
* @return
* @throws Exception
*/
public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 私钥解密
*
* @param privateKeyText
* @param text
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 公钥加密
*
* @param publicKeyText
* @param text
* @return
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 构建RSA密钥对
*
* @return
* @throws NoSuchAlgorithmException
*/
public static RSAKeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
RSAKeyPair rsaKeyPair = new RSAKeyPair(publicKeyString, privateKeyString);
return rsaKeyPair;
}
/**
* RSA密钥对对象
*/
public static class RSAKeyPair {
private String publicKey;
private String privateKey;
public RSAKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
}
}
直接运行本工具类即可完成字符的加解密操作如图:
至此RSA的后端操作工具就已经写好了,下面是进行前端交互操作了。
3、准备前端资料
3.1.一个简易的login.html界面
用模拟登录的操作来演示数据加密操作
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>一个简单的登录网页</title>
</head>
<style>
#center {
width:800px;
height:500px;
background-image:url(../static/1.jpg);
background-repeat:no-repeat;
margin:15px 0px 0px 0px;
}
#center .login {
float: left;
height: 150px;
width: 230px;
margin: 150px 0px 0px 100px;
}
#center .login_line {
width:204px;
height:30px;
margin-top: 5px;
color: #FFFFFF;
}
</style>
<body >
<!--align用于设置子内容的居中-->
<div align="center">
<h2>登录例子</h2>
<div id="center">
<!--登录输入框-->
<div class="login" align="left">
<div class="login_line" style="margin-top:35px">
<div style="float:left;margin-left:10px">账号:</div>
<input type="text" id="userName" style="width:140px;float:left;margin-left:10px"/>
</div>
<div class="login_line">
<div style="float:left;margin-left:10px">密码:</div>
<input type="password" id="pwd" style="width:140px;float:left;margin-left:10px"/>
</div>
<div class="login_line">
<input type="button" id="loginBut" value="登录" style="width:60px;float:left;margin-left:10px"/>
<input type="button" value="重置" style="width:60px;float:left;margin-left:10px"/>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript" src="../static/jquery-1.8.0.js"></script>
<script type="text/javascript" src="../static/jsencrypt.min.js"></script>
<script type="text/javascript">
$(function () {
//前端使用RSA加密数据,需要结合已有的密钥
var encrypt = new JSEncrypt();
var publicKey='[[${publicKey}]]';
encrypt.setPublicKey(publicKey);
$("#loginBut").click(function () {
var userName = encrypt.encrypt($("#userName").val());
var pwd = encrypt.encrypt($("#pwd").val());
$.ajax({
headers:{'Authorization':getToken()},
url: 'http://localhost:8082/esb/login',
data: {"userName":userName ,"pwd":pwd},
type: 'post',
success: function (msg) {
alert(msg.message);
}
});
});
});
/**
* 服务调用Token权限验证
*/
function getToken(){
$.ajax({
headers:{'Authorization':'genrMyToken'},
url: 'http://localhost:8082/AuthorizeToken',
data: {"userName":"admin" ,"pwd":"admin"},
type: 'post',
success: function (msg) {
return msg.responseData;
}
});
}
</script>
</html>
其中的资源路径大家根据自己项目的实际路径来配置,请求的根据后的服务的实际情况进行处理。需要注意的是在html中获取后端model中传输的值需要以这样的[[${publicKey}]]方式获取。
3.2.jQuery-1.8.0.js
自行下载,版本可以更高,但不推荐使用比该版本还低的jQuery。(可以直接在网站上打开该js在复制到本地引入)
3.3.jsencrypt.min.js
自行下载(可以直接在网站上打开该js在复制到本地引入)
3.4.后端控制层代码
代码如下(示例):一个跳转到前端界面的方法,一个接收表单数据提交的方法即可。代码中相关swagger的注解可以忽略。
import com.zpg.fun.entity.Result;
import com.zpg.fun.utils.RSA.RSAUtils;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.security.NoSuchAlgorithmException;
/**
* @Author: zp
* @Description: TODO
* @Date: 2021/5/26 16:46
* @Version: 1.0
*/
@Controller
@RequestMapping("/esb")
public class SayHello {
@GetMapping("/toIndex")
@ApiOperation(value="打开简易的登录界面")
public String toIndex(Model model, HttpServletRequest request) throws NoSuchAlgorithmException {
RSAUtils.RSAKeyPair keyPair = RSAUtils.generateKeyPair();
String publicKey= keyPair.getPublicKey();
String privateKey= keyPair.getPrivateKey();
model.addAttribute("publicKey",publicKey);//提供公钥加密
//将公钥和私钥存入session中[开发中会单独的进行公私钥的保存]
HttpSession session = request.getSession();
session.setAttribute("publicKey", publicKey);
session.setAttribute("privateKey", privateKey);
return "/index";
}
@PostMapping("/login")
@ApiOperation(value="登录操作")
@ResponseBody
public Result toIndex(String userName, String pwd, HttpServletRequest request) throws Exception {
System.out.println("userName:" + userName);
System.out.println("pwd:" + pwd);
HttpSession session = request.getSession();
String privateKey= (String)session.getAttribute("privateKey");
System.out.println("私钥解密后="+RSAUtils.decryptByPrivateKey(privateKey,userName));
System.out.println("私钥解密后="+RSAUtils.decryptByPrivateKey(privateKey,pwd));
System.out.println("获取到用户账号,可以随意发挥操作了!");
return Result.success("登录成功");
}
}
4、效果呈现
运行该项目,访问打开前端的接口进行表单数据提交,如图:
填入账号密码,看后端获取表单数据的方法是否可以获取到加密后的数据和正常解密。(前端进行数据加密的代码已经在前面html中js里面贴出来了,就不在赘述了。)
点击提交前端响应如图:
后端接收到的数据为:
现在第三方扫漏时看到的表单数据就是已经被加密后的数据了,如图:
总结
:以上就是今天要分享的内容,本文将前端表单数据加密和后端解密操作的完整流程进行了讲解,也希望大家在编码过程中留意一下数据安全。不许勿喷。