这几天一直做安全登录,网上查了好多资料,不尽如意。
具体实现思路如下:
1。服务端生成公钥与私钥,保存。
2。客户端在请求到登录页面后,随机生成一字符串。
3。后此随机字符串作为密钥加密密码,再用从服务端获取到的公钥加密生成的随机字符串。
4。将此两段密文传入服务端,服务端用私钥解出随机字符串,再用此私钥解出加密的密文。
这其中有一个关键是解决服务端的公钥,传入客户端,客户端用此公钥加密字符串后,后又能在服务端用私钥解出。
此文即为实现此步而作。
加密算法为RSA:
1。服务端的RSA java实现。
-
/**
-
*
-
*/
-
package
com.sunsoft.struts.util; -
import
java.io.ByteArrayOutputStream; -
import
java.io.FileInputStream; -
import
java.io.FileOutputStream; -
import
java.io.ObjectInputStream; -
import
java.io.ObjectOutputStream; -
import
java.math.BigInteger; -
import
java.security.KeyFactory; -
import
java.security.KeyPair; -
import
java.security.KeyPairGenerator; -
import
java.security.NoSuchAlgorithmException; -
import
java.security.PrivateKey; -
import
java.security.PublicKey; -
import
java.security.SecureRandom; -
import
java.security.interfaces.RSAPrivateKey; -
import
java.security.interfaces.RSAPublicKey; -
import
java.security.spec.InvalidKeySpecException; -
import
java.security.spec.RSAPrivateKeySpec; -
import
java.security.spec.RSAPublicKeySpec; -
import
javax.crypto.Cipher; -
/**
-
* RSA 工具类。提供加密,解密,生成密钥对等方法。
-
* 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
-
*
-
*/
-
public
class
RSAUtil { -
/**
-
* * 生成密钥对 *
-
*
-
* @return KeyPair *
-
* @throws EncryptException
-
*/
-
public
static
KeyPair generateKeyPair()
throws
Exception { -
try
{ -
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(
“RSA”
, -
new
org.bouncycastle.jce.provider.BouncyCastleProvider()); -
final
int
KEY_SIZE =
1024
;
// 没什么好说的了,这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低
-
keyPairGen.initialize(KEY_SIZE,
new
SecureRandom()); - KeyPair keyPair = keyPairGen.generateKeyPair();
- saveKeyPair(keyPair);
-
return
keyPair; -
}
catch
(Exception e) { -
throw
new
Exception(e.getMessage()); - }
- }
-
public
static
KeyPair getKeyPair()
throws
Exception{ -
FileInputStream fis =
new
FileInputStream(
“C:/RSAKey.txt”
); -
ObjectInputStream oos =
new
ObjectInputStream(fis); - KeyPair kp= (KeyPair) oos.readObject();
- oos.close();
- fis.close();
-
return
kp; - }
-
public
static
void
saveKeyPair(KeyPair kp)
throws
Exception{ -
FileOutputStream fos =
new
FileOutputStream(
“C:/RSAKey.txt”
); -
ObjectOutputStream oos =
new
ObjectOutputStream(fos); -
//生成密钥
- oos.writeObject(kp);
- oos.close();
- fos.close();
- }
-
/**
-
* * 生成公钥 *
-
*
-
* @param modulus *
-
* @param publicExponent *
-
* @return RSAPublicKey *
-
* @throws Exception
-
*/
-
public
static
RSAPublicKey generateRSAPublicKey(
byte
[] modulus, -
byte
[] publicExponent)
throws
Exception { -
KeyFactory keyFac =
null
; -
try
{ -
keyFac = KeyFactory.getInstance(
“RSA”
, -
new
org.bouncycastle.jce.provider.BouncyCastleProvider()); -
}
catch
(NoSuchAlgorithmException ex) { -
throw
new
Exception(ex.getMessage()); - }
-
RSAPublicKeySpec pubKeySpec =
new
RSAPublicKeySpec(
new
BigInteger( -
modulus),
new
BigInteger(publicExponent)); -
try
{ -
return
(RSAPublicKey) keyFac.generatePublic(pubKeySpec); -
}
catch
(InvalidKeySpecException ex) { -
throw
new
Exception(ex.getMessage()); - }
- }
-
/**
-
* * 生成私钥 *
-
*
-
* @param modulus *
-
* @param privateExponent *
-
* @return RSAPrivateKey *
-
* @throws Exception
-
*/
-
public
static
RSAPrivateKey generateRSAPrivateKey(
byte
[] modulus, -
byte
[] privateExponent)
throws
Exception { -
KeyFactory keyFac =
null
; -
try
{ -
keyFac = KeyFactory.getInstance(
“RSA”
, -
new
org.bouncycastle.jce.provider.BouncyCastleProvider()); -
}
catch
(NoSuchAlgorithmException ex) { -
throw
new
Exception(ex.getMessage()); - }
-
RSAPrivateKeySpec priKeySpec =
new
RSAPrivateKeySpec(
new
BigInteger( -
modulus),
new
BigInteger(privateExponent)); -
try
{ -
return
(RSAPrivateKey) keyFac.generatePrivate(priKeySpec); -
}
catch
(InvalidKeySpecException ex) { -
throw
new
Exception(ex.getMessage()); - }
- }
-
/**
-
* * 加密 *
-
*
-
* @param key
-
* 加密的密钥 *
-
* @param data
-
* 待加密的明文数据 *
-
* @return 加密后的数据 *
-
* @throws Exception
-
*/
-
public
static
byte
[] encrypt(PublicKey pk,
byte
[] data)
throws
Exception { -
try
{ -
Cipher cipher = Cipher.getInstance(
“RSA”
, -
new
org.bouncycastle.jce.provider.BouncyCastleProvider()); - cipher.init(Cipher.ENCRYPT_MODE, pk);
-
int
blockSize = cipher.getBlockSize();
// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
-
// 加密块大小为127
-
// byte,加密后为128个byte;因此共有2个加密块,第一个127
-
// byte第二个为1个byte
-
int
outputSize = cipher.getOutputSize(data.length);
// 获得加密块加密后块大小
-
int
leavedSize = data.length % blockSize; -
int
blocksSize = leavedSize !=
0
? data.length / blockSize +
1
- : data.length / blockSize;
-
byte
[] raw =
new
byte
[outputSize * blocksSize]; -
int
i =
0
; -
while
(data.length – i * blockSize >
0
) { -
if
(data.length – i * blockSize > blockSize) - cipher.doFinal(data, i * blockSize, blockSize, raw, i
- * outputSize);
-
else
- cipher.doFinal(data, i * blockSize, data.length – i
- * blockSize, raw, i * outputSize);
-
// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
-
// ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了
-
// OutputSize所以只好用dofinal方法。
- i++;
- }
-
return
raw; -
}
catch
(Exception e) { -
throw
new
Exception(e.getMessage()); - }
- }
-
/**
-
* * 解密 *
-
*
-
* @param key
-
* 解密的密钥 *
-
* @param raw
-
* 已经加密的数据 *
-
* @return 解密后的明文 *
-
* @throws Exception
-
*/
-
public
static
byte
[] decrypt(PrivateKey pk,
byte
[] raw)
throws
Exception { -
try
{ -
Cipher cipher = Cipher.getInstance(
“RSA”
, -
new
org.bouncycastle.jce.provider.BouncyCastleProvider()); - cipher.init(cipher.DECRYPT_MODE, pk);
-
int
blockSize = cipher.getBlockSize(); -
ByteArrayOutputStream bout =
new
ByteArrayOutputStream(
64
); -
int
j =
0
; -
while
(raw.length – j * blockSize >
0
) { - bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
- j++;
- }
-
return
bout.toByteArray(); -
}
catch
(Exception e) { -
throw
new
Exception(e.getMessage()); - }
- }
-
/**
-
* * *
-
*
-
* @param args *
-
* @throws Exception
-
*/
-
public
static
void
main(String[] args)
throws
Exception { - RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair().getPublic();
-
String test =
“hello world”
; -
byte
[] en_test = encrypt(getKeyPair().getPublic(),test.getBytes()); -
byte
[] de_test = decrypt(getKeyPair().getPrivate(),en_test); -
System.out.println(
new
String(de_test)); - }
- }
2.测试页面:
IndexAction.java
-
/*
-
* Generated by MyEclipse Struts
-
* Template path: templates/java/JavaClass.vtl
-
*/
-
package
com.sunsoft.struts.action; -
import
java.security.interfaces.RSAPrivateKey; -
import
java.security.interfaces.RSAPublicKey; -
import
javax.servlet.http.HttpServletRequest; -
import
javax.servlet.http.HttpServletResponse; -
import
org.apache.struts.action.Action; -
import
org.apache.struts.action.ActionForm; -
import
org.apache.struts.action.ActionForward; -
import
org.apache.struts.action.ActionMapping; -
import
com.sunsoft.struts.util.RSAUtil; -
/**
-
* MyEclipse Struts
-
* Creation date: 06-28-2008
-
*
-
* XDoclet definition:
-
* @struts.action validate=”true”
-
*/
-
public
class
IndexAction
extends
Action { -
/*
-
* Generated Methods
-
*/
-
/**
-
* Method execute
-
* @param mapping
-
* @param form
-
* @param request
-
* @param response
-
* @return ActionForward
-
*/
-
public
ActionForward execute(ActionMapping mapping, ActionForm form, -
HttpServletRequest request, HttpServletResponse response)
throws
Exception { - RSAPublicKey rsap = (RSAPublicKey) RSAUtil.getKeyPair().getPublic();
-
String module = rsap.getModulus().toString(
16
); -
String empoent = rsap.getPublicExponent().toString(
16
); -
System.out.println(
“module”
); - System.out.println(module);
-
System.out.println(
“empoent”
); - System.out.println(empoent);
-
request.setAttribute(
“m”
, module); -
request.setAttribute(
“e”
, empoent); -
return
mapping.findForward(
“login”
); - }
- }
通过此action进入登录页面,并传入公钥的 Modulus 与PublicExponent的hex编码形式。
3。登录页面 login.jsp
-
<
%@ page
language
=
“java”
pageEncoding
=
“GBK”
%
>
-
<
%@ taglib
uri
=
“http://struts.apache.org/tags-bean”
prefix
=
“bean”
%
>
-
<
%@ taglib
uri
=
“http://struts.apache.org/tags-html”
prefix
=
“html”
%
>
-
<
%@ taglib
uri
=
“http://struts.apache.org/tags-logic”
prefix
=
“logic”
%
>
-
<
%@ taglib
uri
=
“http://struts.apache.org/tags-tiles”
prefix
=
“tiles”
%
>
-
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”
>
-
<
html:html
lang
=
“true”
>
-
<
head
>
-
<
html:base
/>
-
<
title
>
login
</
title
>
-
<
meta
http-equiv
=
“pragma”
content
=
“no-cache”
>
-
<
meta
http-equiv
=
“cache-control”
content
=
“no-cache”
>
-
<
meta
http-equiv
=
“expires”
content
=
“0”
>
-
<
meta
http-equiv
=
“keywords”
content
=
“keyword1,keyword2,keyword3”
>
-
<
meta
http-equiv
=
“description”
content
=
“This is my page”
>
-
<!–
-
<link rel=”stylesheet” type=”text/css” href=”styles.css”>
-
–>
-
<
script
type
=
“text/javascript”
src
=
“js/RSA.js”
>
</
script
>
-
<
script
type
=
“text/javascript”
src
=
“js/BigInt.js”
>
</
script
>
-
<
script
type
=
“text/javascript”
src
=
“js/Barrett.js”
>
</
script
>
-
<
script
type
=
“text/javascript”
>
- function rsalogin()
- {
- bodyRSA();
-
var
result
=
encryptedString
(key, document.getElementById(“pwd”).value); - //alert(result);
-
loginForm.action
=
“login.do?result=”
+result; - loginForm.submit();
- }
- var key ;
- function bodyRSA()
- {
- setMaxDigits(130);
-
key
=
new
RSAKeyPair(“10001″,””,”8c1cd09a04ed01aafe70dc84c5f32ae23a16fe8fc8898aba6797c5a9c708720de4f08dbf086af429fc51c0636208f56de20a8ab5686affd9bdfb643ae1e90d5617155c4867eef06b0884ba8ecd187907c7069ae3eed4f0155eeca6573411864035ae803ad8fd91a0cc479f27e41b19c13465ab30f3cfbfd14de56f49cbd09481″); - }
-
</
script
>
-
</
head
>
-
<
body
>
-
<
html:form
action
=
“login”
method
=
“post”
focus
=
“username”
>
-
<
table
border
=
“0”
>
-
<
tr
>
-
<
td
>
Login:
</
td
>
-
<
td
>
<
html:text
property
=
“username”
/>
</
td
>
-
</
tr
>
-
<
tr
>
-
<
td
>
Password:
</
td
>
-
<
td
>
<
html:password
property
=
“password”
styleId
=
“pwd”
/>
</
td
>
-
</
tr
>
-
<
tr
>
-
<
td
colspan
=
“2”
align
=
“center”
>
<
input
type
=
“button”
value
=
“SUBMIT”
onclick
=
“rsalogin();”
/>
</
td
>
-
</
tr
>
-
</
table
>
-
</
html:form
>
-
</
body
>
-
</
html:html
>
3.点击登录后,调用LoginAction.java
-
/*
-
* Generated by MyEclipse Struts
-
* Template path: templates/java/JavaClass.vtl
-
*/
-
package
com.sunsoft.struts.action; -
import
java.math.BigInteger; -
import
javax.servlet.http.HttpServletRequest; -
import
javax.servlet.http.HttpServletResponse; -
import
org.apache.struts.action.Action; -
import
org.apache.struts.action.ActionForm; -
import
org.apache.struts.action.ActionForward; -
import
org.apache.struts.action.ActionMapping; -
import
com.sunsoft.struts.util.RSAUtil; -
/**
-
* MyEclipse Struts
-
* Creation date: 06-28-2008
-
*
-
* XDoclet definition:
-
* @struts.action path=”/login” name=”loginForm” input=”/login.jsp” scope=”request” validate=”true”
-
* @struts.action-forward name=”error” path=”/error.jsp”
-
* @struts.action-forward name=”success” path=”/success.jsp”
-
*/
-
public
class
LoginAction
extends
Action { -
/*
-
* Generated Methods
-
*/
-
/**
-
* Method execute
-
* @param mapping
-
* @param form
-
* @param request
-
* @param response
-
* @return ActionForward
-
*/
-
public
ActionForward execute(ActionMapping mapping, ActionForm form, -
HttpServletRequest request, HttpServletResponse response)
throws
Exception{ -
//LoginForm loginForm = (LoginForm) form;
-
String result = request.getParameter(
“result”
); -
System.out.println(
“原文加密后为:”
); - System.out.println(result);
-
byte
[] en_result =
new
BigInteger(result,
16
).toByteArray(); -
System.out.println(
“转成byte[]”
+
new
String(en_result)); -
byte
[] de_result = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(),en_result); -
System.out.println(
“还原密文:”
); -
System.out.println(
new
String(de_result)); -
StringBuffer sb =
new
StringBuffer(); -
sb.append(
new
String(de_result)); - System.out.println(sb.reverse().toString());
-
return
mapping.findForward(
“success”
); - }
- }
因为发现解出的明文是倒序的,后面就用StringBuffer的reverse()来转换了一下。
4。login.jsp所调用的js
-
js.rar
(6.4 KB) - 描述: login.jsp所调用的javascript,有:RSA.jsBigInt.jsBarrett.js
原文地址:http://sunxboy.iteye.com/blog/209156