1 背景
在登录页面存在明文传输的漏洞,为了解决这个问题百度到了两种方案,第一种是使用https协议,许多人都推荐这种方案。第二种是对传输的信息使用加密算法进行加密然后传输。
最后选择了第二种方案,原因是第一种的证书需要申请并且需要花钱,有免费证书有很多但大多有效期是一年。但网上评价https确实好,个人感觉https还是首选,借用网友一句话“没有https的都是在裸奔”。
本文并不涉及https的相关内容,只是前端使用了jsencrypt.js,结合后端代码做了RSA加密。
2 代码
2.1准备jar和js
接下来的RSAUtils工具类需要导入commons-codec-1.10.jar,前端需要jsencrypt.js。以上传到SCDN下载,不需要积分,编写本章时在审核。百度云链接稍后发出。
2.2 工具类
建立一个工具类RSAUtils,这个类借鉴了网友的代码,出处我忘记保存了。
使用这个类的initKey()方法生成密钥对,getPublicKey()方法可以得到公钥字符串,将其传输到前端中用于信息加密。
public class RSAUtils {
private static final String KEYALGORITHM = "RSA";
private static final String PUBLICKEY = "RSAPublicKey";
private static final String PRIVATEKEY = "RSAPrivateKey";
//rsa私钥 或者可从配置文件读取。
// public static final String DECRYPTPRIVATEKEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIMzJa4oZpQcPhRDTIaWnF4olSaeGt5oV0XFwoeeSK+FZ3lc4N34523tdfasgba";
private RSAUtils(){super();}
public static byte[] decryptBASE64(String key) {
Base64 base64 = new Base64();
return base64.decode(key);
}
public static String encryptBASE64(byte[] bytes) {
Base64 base64 = new Base64();
return base64.encodeToString(bytes);
}
public static byte[] decryptByPrivateKey(byte[] data, String key){
try {
byte[] keyBytes = decryptBASE64(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEYALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}catch (Exception e){
// log.error("RSAUtilsPrivateKeyDecryptError");
e.printStackTrace();
return new byte[0];
}
}
/**
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(String data, String key){
return decryptByPrivateKey(decryptBASE64(data), key);
}
/**
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String key){
try {
byte[] keyBytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEYALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}catch (Exception e){
// log.error("RSAUtilsPublicKeyDecryptError");
e.printStackTrace();
return new byte[0];
}
}
/**
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(String data, String key) {
try {
byte[] keyBytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEYALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data.getBytes());
}catch (Exception e){
// log.error("RSAUtilsPublicKeyEncryptError");
e.printStackTrace();
return new byte[0];
}
}
/**
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String key){
try {
byte[] keyBytes = decryptBASE64(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEYALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}catch (Exception e){
// log.error("RSAUtilsPrivateKeyEncryptError");
e.printStackTrace();
return new byte[0];
}
}
/**
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPrivateKey(Map<String, Key> keyMap){
if(keyMap != null){
Key key = keyMap.get(PRIVATEKEY);
return encryptBASE64(key.getEncoded());
}else{
return "";
}
}
/**
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPublicKey(Map<String, Key> keyMap){
if(keyMap != null){
Key key = keyMap.get(PUBLICKEY);
return encryptBASE64(key.getEncoded());
}else {
return "";
}
}
/**
*
* @return
* @throws Exception
*/
public static Map<String, Key> initKey(){
try {
KeyPairGenerator keyPairGen = KeyPairGenerator
.getInstance(KEYALGORITHM);
keyPairGen.initialize(2048);
KeyPair keyPair = keyPairGen.generateKeyPair();
Map<String, Key> keyMap = new HashMap(2);
keyMap.put(PUBLICKEY, keyPair.getPublic());
keyMap.put(PRIVATEKEY, keyPair.getPrivate());
return keyMap;
} catch (NoSuchAlgorithmException e) {
// log.error("RSAUtilsInitKeyError");
e.printStackTrace();
return null;
}
}
}
2.3 前端请求公钥
2.3.1 后端java代码
这里采用了最简单的session存储密钥对。每次回话都生成新的密钥对,加强对信息的保护。将公钥暴露给前端。
PrintWriter out = response.getWriter();
String generateKeypair = request.getParameter("generateKeypair");
HttpSession session = request.getSession();
Map<String, Key> keyMap = (Map<String, Key>) session.getAttribute("keyMap");
if (keyMap == null) {
keyMap = RSAUtils.initKey();
session.setAttribute("keyMap", keyMap);
}
String puk = RSAUtils.getPublicKey(keyMap);
out.println(puk);
2.3.2 前端js代码
先引入jsencrypt.js,在js中得到公钥,然后对数据进行加密。下面代码中contentEncrypted 变量就是加密之后的字符串。
$.ajax({
type : 'post',
url : 'url',
data : {
"generateKeypair" : true
},
success : function(result) {
//验证成功
PUBLIC_KEY = result
var encrypt = new JSEncrypt();
encrypt.setPublicKey(PUBLIC_KEY);
var contentEncrypted = encrypt.encrypt(content)
}
});
2.4 后端代码解密。
前端将加密之后的信息传递到后端进行解密。附上后端代码,其中decryptContent 就是解密之后的字符串。
Map<String, Key> keyMap = (Map<String, Key>) session.getAttribute("keyMap");
content = URLDecoder.decode(content,"UTF-8").replace(' ', '+');
String DECRYPTPRIVATEKEY = RSAUtils.getPrivateKey(keyMap);
byte[] decryptData = RSAUtils.decryptByPrivateKey(content, DECRYPTPRIVATEKEY);
String decryptContent = new String(decryptData);
这段代码有一点问题,中文解密出来会乱码,会不会和第二行有关,前端的pageEncoding="UTF-8",有没有大佬知道原因。
3 总结
私以为,上面的文字已经说得很清楚了,讲述了一点在java web中使用RSA进行数据加密的过程,其他的可以根据业务需求进行变化,本身还有很大的提升空间,所以本文的题目最后加了个“(一)”,在以后的学习中如果能再进一步,争取写个"(二)",欢迎各位指出不足。