1. 基本原理
1.1 對(duì)稱加密
基本概念
加密解密都使用同一個(gè)密鑰。
缺點(diǎn)
- 安全問(wèn)題:一旦密鑰泄露钥勋,傳輸數(shù)據(jù)將不會(huì)安全
- 保管問(wèn)題:每一類的數(shù)據(jù)加密需要重新生成一個(gè)密鑰沼溜,一旦需要傳輸?shù)臄?shù)據(jù)類型多了趟佃,密鑰管理就成了一個(gè)負(fù)擔(dān)
1.2 非對(duì)稱加密
基本概念
數(shù)據(jù)傳輸方和接收方都擁有自己的公鑰和私鑰例书,他們將公鑰暴露出來(lái)提供給對(duì)方加密/解密數(shù)據(jù),自己使用自己的私鑰對(duì)數(shù)據(jù)進(jìn)行加密/解密攀隔。保證加密和解密使用的是不同的密鑰皂贩,所以叫做非對(duì)稱加密
數(shù)據(jù)傳輸過(guò)程
- 傳輸雙方AB都需要生成自己的一對(duì)私鑰和公鑰
- A將數(shù)據(jù)用B提供的公鑰將數(shù)據(jù)進(jìn)行加密,傳輸給B
- B將數(shù)據(jù)拿到昆汹,用自己的私鑰對(duì)數(shù)據(jù)進(jìn)行解密明刷。反過(guò)來(lái)B傳數(shù)據(jù)給A也是一樣
這個(gè)過(guò)程保證了解密使用的私鑰是自己私有的,對(duì)數(shù)據(jù)解密只能使用自己知道的私鑰進(jìn)行解密筹煮,這個(gè)過(guò)程就避免了密鑰泄露的問(wèn)題遮精。
1.3 非對(duì)稱加密實(shí)例(RSA算法)
這里用登錄校驗(yàn)功能展開(kāi)。
- 前端通過(guò)調(diào)用后端提供的接口獲取公鑰败潦,在前端對(duì)密碼進(jìn)行加密后傳輸給后端
- 后端通過(guò)自己的私鑰對(duì)數(shù)據(jù)進(jìn)行解密本冲,在數(shù)據(jù)庫(kù)進(jìn)行校驗(yàn)后保存登錄用戶信息
需要引用的包
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
前端JS
$('#loginButton').click(function(e){
//調(diào)用后端接口獲取公鑰
$.ajax({
type:'post',
url:'/getPublicKey.do',
data:{},
success:function(publicKey){
//對(duì)密碼進(jìn)行加密
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
that.val(encrypt.encrypt(password));
$('#passwd').submit();
}
})
})
RSA工具類
public class RSAUtils {
private static final KeyPair keyPair = initKey();
/**
* 初始化key pair
*
* @return KeyPair
*/
private static KeyPair initKey() {
try {
// 添加provider
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
// 隨機(jī)數(shù)用于安全加密
SecureRandom random = new SecureRandom();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
generator.initialize(1024, random);
return generator.generateKeyPair();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 產(chǎn)生公鑰
*
* @return 公鑰字符串
*/
public static String generateBase64PublicKey() {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 使用base64算法對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行編碼,返回key的原始編碼形式
return new String(Base64.encodeBase64(publicKey.getEncoded()));
}
/**
* 解密數(shù)據(jù)
*
* @param arg 需要解密的字符串
* @return 解密后的字符串
*/
public static String decryptBase64(String arg) {
try {
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
// Cipher 提供加密和解密功能
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", provider);
PrivateKey privateKey = keyPair.getPrivate();
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// doFinal(): 加密或者解密數(shù)據(jù)
byte[] plainText = cipher.doFinal(Base64.decodeBase64(arg));
return new String(plainText);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
獲取公鑰
@RequestMapping("/getPublicKey.do")
public String getPublicKey(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
try {
PrintWriter writer = httpServletResponse.getWriter();
String publicKey = RSAUtils.generateBase64PublicKey();
writer.write(publicKey);
return publicKey;
} catch (Exception e) {
return null;
}
}
解密處理
@RequestMapping("/login.do")
public String login(HttpServletRequest request, HttpServletResponse response) {
String account = request.getParameter("usename");
String passwd = request.getParameter("passwd");
try {
//解密
passwd = RSAUtils.decryptBase64(passwd);
} catch (Exception e) {
e.printStackTrace();
}
if (account != "" && passwd != "") {
//校驗(yàn)賬號(hào)密碼,成功則保存到session
User user = UserDao.check(account, passwd);
if (user != null) {
session.setAttribute("loginUser", user);
}
}
return "";
}