非对称加密算法是一种密钥的保密方法。
非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。 非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。
RSA公开密钥加密系统采用公钥加密,私钥解密,能够安全传输需要加密的文本。但是,由于其需要进行大量的指数运算,速度慢,成本高,常用于加密少量的文本。
我们对于表单提交时的密码采用公开密钥系统进行加密和解密。前端用JS进行公钥加密,后端用Java私钥解密。
1.bcprov-jdk16-146.jar.
2.commons-codec-1.10.jar.
3.jsencrypt.min.js.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>注册</title>
<script src="/js/jquery.min.js"></script>
<script src="/js/jsencrypt.min.js"></script>
</head>
<body>
<form id="erlingyiba" class="form1" action="/toregist.do" method="post">
<div class="one">
<div class="user stle">
<span>
<i>*</i>
用户名
</span>
<input class="border-box admin" type="text" name="username" placeholder="请输入您的用户名" />
<label>请输入用户名</label>
</div>
<div class="password stle">
<span>
<i>*</i>
登录密码
</span>
<input class="passwordone border-box" type="password" name="password" placeholder="请输入密码" />
<label>请输入密码</label>
</div>
<div class="password stle">
<span>
<i>*</i>
确认密码
</span>
<input class="passwordtwo border-box" type="password" placeholder="请输入密码" />
<label>请再次输入密码</label>
</div>
</div>
<div class="sublimeButton stle">
<span>
<i></i>
</span>
<input id="registbutton" class="border-box" type="submit" value="注 册" />
<input id="registreg" name="registreg" type="hidden" value="1" show="true" />
</div>
</div>
</form>
</body>
</html>
添加提交按钮的点击事件,点击后,通过接口,到后台获取公钥,对密码加密,然后提交表单。
$('#registbutton').click(function(e){
//注销默认事件
e.preventDefault();
var that = $('.passwordone');
//获取公钥
$.ajax({
type:'post',
url:'/getPasswprdKey.do',
data:{},
success:function(date){
var publicKey = date;
//对密码进行加密
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
var password = that.val();
that.val(encrypt.encrypt(password));
$('#erlingyiba').submit();
}
})
})
这里除了常用的JAR包以外,还需要依赖bcprov-jdk16-146.jar和commons-codec-1.10.jar这两个JAR包。bcprov-jdk16-146.jar提供用于加密和解密的provider,它实现了算法和密钥的生成等。commons-codec-1.10.jar提供编码解码功能。
package com.jzsoft.util;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.RSAPublicKey;
import java.util.Scanner;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
public class RSAUtils {
// KeyPair is a simple holder for a key pair.
private static final KeyPair keyPair = initKey();
/**
* 初始化方法,产生key pair,提供provider和random
* @return KeyPair instance
*/
private static KeyPair initKey() {
try {
// 添加provider
Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
Security.addProvider(provider);
// 产生用于安全加密的随机数
SecureRandom random = new SecureRandom();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
generator.initialize(1024, random);
return generator.generateKeyPair();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 产生public key
* @return public key字符串
*/
public static String generateBase64PublicKey() {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// encodeBase64(): Encodes binary data using the base64
// algorithm but does not chunk the output.
// getEncoded():返回key的原始编码形式
return new String(Base64.encodeBase64(publicKey.getEncoded()));
}
/**
* 解密数据
* @param string 需要解密的字符串
* @return 破解之后的字符串
*/
public static String decryptBase64(String string) {
// decodeBase64():将Base64数据解码为"八位字节”数据
return new String(decrypt(Base64.decodeBase64(string)));
}
private static byte[] decrypt(byte[] byteArray) {
try {
Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
Security.addProvider(provider);
// Cipher: 提供加密和解密功能的实例
// transformation: "algorithm/mode/padding"
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", provider);
PrivateKey privateKey = keyPair.getPrivate();
// 初始化
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// doFinal(): 加密或者解密数据
byte[] plainText = cipher.doFinal(byteArray);
return plainText;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
@RequestMapping("/getPasswprdKey.do")
public String getPasswprdKey(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
try {
PrintWriter writer = httpServletResponse.getWriter();
String publickey = RSAUtils.generateBase64PublicKey();
writer.write(publickey);
System.out.println(publickey);
return null;
} catch (Exception e) {
// TODO: handle exception
return null;
}
}
@RequestMapping("/login.do")
public String login(HttpServletRequest request, HttpServletResponse response, HttpSession session, ModelMap mm) {
IUserDao UserDao = ServiceFactory.getBean(IUserDao.class);
String retUrl = ParamUtils.getParameter(request, "retUrl", "");
String account = ParamUtils.getParameter(request, "usename", "");
try {
//处理乱码
account = new String(account.getBytes("ISO-8859-1"), "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取密码,密码是加密后的密码
String passwd = ParamUtils.getParameter(request, "password", "");
try {
//解密
passwd = RSAUtils.decryptBase64(passwd);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
if (account != "" && passwd != "") {
User user = UserDao.login(account, passwd);
if (user != null) {
mm.addAttribute("user", user);
session.setAttribute(Constant.USER_FRONT_SESSION_KEY, user);
return "forward:" + retUrl;
} else {
mm.addAttribute("msgCode", "用户名或密码错误!");
return "forward:/tologin.do";
}
} else {
mm.addAttribute("msgCode", "用户名或密码不能为空!");
return "forward:/tologin.do";
}
}