表单提交时,前端用js加密,后端用Java(struts2)解密
前一篇博文提到,对称密钥系统双方使用相同的密码加密和解密,相对而言比较快,但是,如果双方之前没有约定,仅仅靠对称密钥系统无法进行加密和解密。RSA公开密钥加密系统采用公钥加密,私钥解密,能够安全传输需要加密的文本。但是,由于其需要进行大量的指数运算,速度慢,成本高,常用于加密少量的文本。
这里,我们对于表单提交时的密码采用公开密钥系统进行加密和解密。前端用JS进行公钥加密,后端用struts2(Java)私钥解密。
前端jsp表单提交的代码
<form action="login" id="loginForm" method="post">
<table>
<tr>
<td>用户名</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password" id="password" /></td>
</tr>
<tr>
<!--这里加上验证码是因为我们在密码框失去焦点时采用异步的方式从后端得到公钥。如果没有输入验证码的时间来等待查询,可能在提交之前得到不加密。另一种方式,在页面加载时如果发现表单项,则请求;或者用户框得到焦点时,就请求,则有足够的时间了。-->
<!--验证码这里直接是数字,不是动态的-->
<td>验证码</td>
<td><input type="text" name="verify" id="verify" /></td>
<td><p>这是验证码 <i>520520</i></p></td>
</tr>
<tr>
<td><input type="submit" value="提交"/></td>
<td><input type="reset" value="重置"/></td>
</tr>
</table>
</form>
JS代码
添加两个事件处理程序。第一个,密码框失去焦点时,触发获取公钥的函数。第二个,提交时,触发加密的操作并提交。
<script type="text/javascript">
//获取public key
var publicKey = null;
function getPublicKey() {
//新建一个XHR对象,只支持IE7以上的浏览器,或者其他浏览器,我用的是谷歌浏览器
var xhr = new XMLHttpRequest();
//需要访问的链接
var urlString = "/Encryption/passwordEncrypt.action";
xhr.open("post", urlString, true);
xhr.send(null);
//检查响应的状态
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
if(xhr.responseText != null) {
publicKey = xhr.responseText;
}
}
}
};
}
function encryptPassword(event) {
//对密码进行加密
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
var password = document.getElementById("password").value;
document.getElementById("password").value = encrypt.encrypt(password);
//提交之前,检查是否已经加密。假设用户的密码不超过20位,加密后的密码不小于20位
var password = document.getElementById("password").value;
if(password.length < 20) {
//实际提示,可以换一种说法
alert("encryption failed");
//若没有加密,阻止提交
event.preventDefault();
}
}
var password = document.getElementById("password");
//添加事件处理程序:密码框失去焦点时,请求publicKey
password.addEventListener("blur", getPublicKey, false);
var loginForm = document.getElementById("loginForm");
//提交时,进行加密
loginForm.addEventListener("submit", encryptPassword, false);
</script>
用于提供加密和解密功能的工具类
这里除了struts2自身的JAR包以外,还需要依赖bcprov和commons-codec这两个JAR包。bcprov提供用于加密和解密的provider,它实现了算法和密钥的生成等。codec大致提供了编码解码功能。这个工具类很大程度上参考了链接,自己加了注释。
package com.chris.encryption;
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 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 is used to generate pairs of public and private keys,
//which is constructed using the getInstance() factory methods.
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() {
PublicKey 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.getBytes())));
}
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/ECB/PKCS1Padding", provider);
PrivateKey privateKey = keyPair.getPrivate();
//初始化
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//doFinal(): 加密或者解密数据
byte[] plainText = cipher.doFinal(byteArray);
return plainText;
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
产生公钥的action类
package com.chris.encryption;
import java.io.PrintWriter;
import javax.servlet.ServletResponse;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class PasswordEncrypt extends ActionSupport {
@Override
public String execute() throws Exception {
ServletResponse response = ServletActionContext.getResponse();
PrintWriter writer = response.getWriter();
String publicKey = RSAUtils.generateBase64PublicKey();
writer.write(publicKey);
System.out.println(publicKey);
//由于我们这里不需要返回页面,故这里直接返回null
return null;
}
}
处理登陆请求的action
package com.chris.encryption;
import com.opensymphony.xwork2.ActionSupport;
public class Login extends ActionSupport {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String execute() throws Exception {
System.out.println(username);
System.out.println(password);
String decipher = RSAUtils.decryptBase64(password);
System.out.println(decipher);
return null;
}
}
struts.xml配置action
<?xml version="1.0" encoding="gb2312" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.i18n.encoding" value="gb2312"/>
<package name="chris" extends="struts-default">
<!-- 配置login -->
<action name="passwordEncrypt"
class="com.chris.encryption.PasswordEncrypt">
</action>
<action name="login"
class="com.chris.encryption.Login">
</action>
</package>
</struts>
参考链接:
[1]http://blog.csdn.net/sinat_29508201/article/details/51648572