简述:
RSA非对称加密算法, 简单理解就是: 准备两把钥匙-公钥, 私钥.
加密的时候使用公钥进行加密, 解密的时候使用私钥进行解密, 这两把钥匙是不同的.
js里面包含了解密和加密方法, 我们需要做的就是使用方法的时候传递 钥匙和数据进去.
RSA工具类里面也包含 解密js端传递来的密文方法, 以及在后台进行数据的加密解密方法.
直接开始:
1. 准备前端需要的js
security.js,已上传
2. jsp或者html页面, 控制层传密钥
controller层将公钥系统和公钥指数传递到前端
RSAPublicKey publicKey = RSAUtils.getDefaultPublicKey(); //获取公钥对象--注意:前端需要用到公钥系数和指数
//公钥-系数(n)
model.addAttribute("modulus",new String(Hex.encode(publicKey.getModulus().toByteArray())));
//公钥-指数(e1)
model.addAttribute("exponent",new String(Hex.encode(publicKey.getPublicExponent().toByteArray())));
js或者html文件:
<script type="text/javascript" th:src="@{/js/security.js}"></script>
//登录验证之前需要加密用户名密码
$("#loginUp").on('click', function (event) {
var userAccount = $("#userAccount").val();
var password = $("#inputPassword").val();
var validCode = $("#validCode").val();
if (userAccount.trim() === "") {
$("#enterUserName").show();
return false;
}
if (password.trim() === "") {
$("#enterPassword").show();
return false;
}
if (validCode.trim() === "") {
$("#enterCode").show();
return false;
}
//获取公钥系数
var modulus = [[${modulus}]];
//获取公钥指数
var exponent = [[${exponent}]];
//获取最终公钥
var key = RSAUtils.getKeyPair(exponent, '', modulus);
//进行数据加密
//var ap = RSAUtils.encryptedString(key, encodeURI(word));
$.get(ctx + "oauth/loginValidate", {"userAccount": RSAUtils.encryptedString(key,encodeURI(userAccount)), "password": RSAUtils.encryptedString(key,encodeURI(password)), "validCode": validCode, "codeNumber": codeNumber}, function (data) {
if (data.errorCode === 1) {
$("#spanText").text(data.errorMsg).show();
refreshCode();
}
if (data.code == 506) {
$("#spanText").text(data.msg).show();
refreshCode();
}
if (data.errorCode === 0) {
submitForm();
}
});
});
3. java端需要的RSA工具类.
注意点:
该工具类中需要引入org.bouncycastle..包依赖,如下 ,其余RSA需要的jar包都是java自带的
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.69</version>
</dependency>
RSAUtils工具类
package com.prenatalscreen.util;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import javax.crypto.Cipher;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
/**
* RSA算法加密/解密工具类。
*/
public class RSAUtils {
/**
* 算法名称
*/
private static final String ALGORITHOM = "RSA";
/**
* 密钥大小
*/
private static final int KEY_SIZE = 1024;
/**
* 默认的安全服务提供者
*/
private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
private static KeyPairGenerator keyPairGen = null;
private static KeyFactory keyFactory = null;
/**
* 缓存的密钥对。
*/
private static KeyPair oneKeyPair = null;
//密文种子, 当想更换RSA钥匙的时候,只需要修改密文种子,即可更换
private static final String radamKey = "wuxiaomin";
//类加载后进行初始化数据
static {
try {
keyPairGen = KeyPairGenerator.getInstance(ALGORITHOM,
DEFAULT_PROVIDER);
keyFactory = KeyFactory.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
}
/**
* 根据指定的密文种子,生成并返回RSA密钥对。
*/
private static synchronized KeyPair generateKeyPair() {
try {
keyPairGen.initialize(KEY_SIZE,
new SecureRandom(radamKey.getBytes()));
oneKeyPair = keyPairGen.generateKeyPair();
return oneKeyPair;
} catch (InvalidParameterException ex) {
ex.printStackTrace();
} catch (NullPointerException ex) {
ex.printStackTrace();
}
return null;
}
/**
* 返回初始化时默认的公钥。
*/
public static RSAPublicKey getDefaultPublicKey() {
KeyPair keyPair = generateKeyPair();
if (keyPair != null) {
return (RSAPublicKey) keyPair.getPublic();
}
return null;
}
/**
* 使用指定的私钥解密数据。
*
* @param privateKey 给定的私钥。
* @param data 要解密的数据。
* @return 原数据。
*/
public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception {
Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
ci.init(Cipher.DECRYPT_MODE, privateKey);
return ci.doFinal(data);
}
/**
* 使用默认的私钥解密给定的字符串。
* @param encryptText 密文。
* @return 原文字符串。
*/
public static String decryptString(String encryptText) {
if (StringUtils.isBlank(encryptText)) {
return null;
}
KeyPair keyPair = generateKeyPair();
try {
byte[] en_data = Hex.decode(encryptText);
byte[] data = decrypt((RSAPrivateKey) keyPair.getPrivate(), en_data);
return new String(data);
} catch (NullPointerException ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* 使用秘钥 - 对js端传递过来密文进行解密
*
* @param encryptText 密文。
* @return {@code encryptText} 的原文字符串。
*/
public static String decryptStringByJs(String encryptText) {
String text = decryptString(encryptText);
if (text == null) {
return null;
}
String reverse = StringUtils.reverse(text);
String decode = null;
try {
//这里需要进行编码转换.注:在前端js对明文加密前需要先进行转码-"编码转换"
decode = URLDecoder.decode(reverse, "UTF-8");
System.out.println("解密后文字:" + decode);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return decode;
}
//java端 - 使用公钥进行加密
public static byte[] encrypt(String plaintext) throws Exception {
// 获取公钥及参数e,n
RSAPublicKey publicKey = RSAUtils.getDefaultPublicKey();
//获取公钥指数 e
BigInteger e = publicKey.getPublicExponent();
//获取公钥系数 n
BigInteger n = publicKey.getModulus();
//先将明文进行编码
String encode = URLEncoder.encode(plaintext);
// 获取明文字节数组 m
BigInteger m = new BigInteger(encode.getBytes());
// 进行明文加密 c
BigInteger c = m.modPow(e, n);
//返回密文字节数组
return c.toByteArray();
}
//java端 - 使用私钥进行解密
public static String decrypt(byte[] cipherText) throws Exception {
// 读取私钥
KeyPair keyPair = generateKeyPair();
RSAPrivateKey prk = (RSAPrivateKey) keyPair.getPrivate();
// 获取私钥参数-指数/系数
BigInteger d = prk.getPrivateExponent();
BigInteger n = prk.getModulus();
// 读取密文
BigInteger c = new BigInteger(cipherText);
// 进行解密
BigInteger m = c.modPow(d, n);
// 解密结果-字节数组
byte[] mt = m.toByteArray();
//转成String,此时是乱码
String en = new String(mt);
//再进行编码
String result = java.net.URLDecoder.decode(en, "UTF-8");
//最后返回解密后得到的明文
return result;
}
public static void main(String[] args) {
/*解密js端传递过来的密文*/
//获取公钥对象--注意:前端那边需要用到公钥系数和指数
RSAPublicKey publicKey = RSAUtils.getDefaultPublicKey();
//公钥-系数(n)
System.out.println("public key modulus:" + new String(Hex.encode(publicKey.getModulus().toByteArray())));
//公钥-指数(e1)
System.out.println("public key exponent:" + new String(Hex.encode(publicKey.getPublicExponent().toByteArray())));
//JS加密后的字符串
String param = "8c49dfb92a0c8d15b0187b1eabc343a80f340962ebecc497205f83f6a4792141d4e711b6d439388ecf691df4eda2cae1e6431573b61e3f564ffe1c757e32f3e846b5983e5939ddeb28c9570001d7f208ffbaa069677d363cc73e4c78c0d508e8b0b9f6205473269bbfbc22e7fb9413be4c449520eb6cfb4fbbff4e7e189a005a";
//解密后的字符串
String param1 = RSAUtils.decryptStringByJs(param);
System.out.println(param1);
// /*后端的加密和解密-在加密的方法中已经使用了公钥指数和系数*/
// try {
// //加密
// byte[] param3 = encrypt("你好> @# !!# #");
// //解密
// String decrypt = RSAUtils.decrypt(param3);
// System.out.println(decrypt);
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
4. 验证用户名密码是否正确之前先将用户名密码解密
@ResponseBody
@RequestMapping("/oauth/loginValidate")
public ResMessage loginValidate(String userAccount, String password, String validCode, String codeNumber) {
//解密后的字符串
userAccount = RSAUtils.decryptStringByJs(userAccount);
password = RSAUtils.decryptStringByJs(password);
ResMessage result = new ResMessage();
SysUser user = sysUserServices.queryUserAccount(userAccount);
user = sysUserServices.queryUserAccount(userAccount);
String codeSession = RedisUtil.getCodeNumber(codeNumber);
}