RSA與AES加密與解密
1.什么是對稱加密
對稱加密算法是應用較早的加密算法,技術成熟。在對稱加密算法中,數據發信方將明文(原始數據)和加密密鑰(mi yao)一起經過特殊加密算法處理后,使其變成復雜的加密密文發送出去。收信方收到密文后,若想解讀原文,則需要使用加密用過的密鑰及相同算法的逆算法對密文進行解密,才能使其恢復成可讀明文。在對稱加密算法中,使用的密鑰只有一個,發收信雙方都使用這個密鑰對數據進行加密和解密,這就要求解密方事先必須知道加密密鑰。
1. 特點
對稱加密算法的特點是算法公開、計算量小、加密速度快、加密效率高。
不足之處是,交易雙方都使用同樣鑰匙,安全性得不到保證。此外,每對用戶每次使用對稱加密算法時,都需要使用其他人不知道的惟一鑰匙,這會使得發收信雙方所擁有的鑰匙數量呈幾何級數增長,密鑰管理成為用戶的負擔。對稱加密算法在分布式網絡系統上使用較為困難,主要是因為密鑰管理困難,使用成本較高。而與公開密鑰加密算法比起來,對稱加密算法能夠提供加密和認證卻缺乏了簽名功能,使得使用范圍有所縮小。在計算機專網系統中廣泛使用的對稱加密算法有DES和IDEA等。美國國家標准局倡導的AES即將作為新標准取代DES。
2.實現步驟
代碼實現流程:
- 首先在頁面加載完成時向后台發送ajax請求,后台隨機生成一個16位的字符串key返回給前端
- 前端使用CryptoJS.js和后台生成的key對password進行加密
- 點擊“登錄”按鈕后提交到后台,后台獲取到加密后的密碼進行解密后,把解密后的密碼返回給前端展示出來。
導入依賴
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
后端controller
package com.sean.jsencrypt.controller;
import cn.hutool.core.util.RandomUtil;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import utils.AesUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Controller
public class AesController {
@GetMapping("/key")
@ResponseBody
private String create16String()
{
return RandomUtil.randomString(16);
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public Object login(HttpServletRequest request) throws Exception {
String userName = request.getParameter("userName");
String encryptedPassword = request.getParameter("password");
String key = request.getParameter("key");
String password = AesUtils.decrypt(encryptedPassword,key);
return password;
}
}
后端代碼:AesUtils工具類
package utils;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AesUtils {
private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
public static String encrypt(String content, String key) {
try {
//獲得密碼的字節數組
byte[] raw = key.getBytes();
//根據密碼生成AES密鑰
SecretKeySpec skey = new SecretKeySpec(raw, "AES");
//根據指定算法ALGORITHM自成密碼器
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
//初始化密碼器,第一個參數為加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作,第二個參數為生成的AES密鑰
cipher.init(Cipher.ENCRYPT_MODE, skey);
//獲取加密內容的字節數組(設置為utf-8)不然內容中如果有中文和英文混合中文就會解密為亂碼
byte [] byte_content = content.getBytes("utf-8");
//密碼器加密數據
byte [] encode_content = cipher.doFinal(byte_content);
//將加密后的數據轉換為字符串返回
return Base64.encodeBase64String(encode_content);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String decrypt(String encryptStr, String decryptKey) {
try {
//獲得密碼的字節數組
byte[] raw = decryptKey.getBytes();
//根據密碼生成AES密鑰
SecretKeySpec skey = new SecretKeySpec(raw, "AES");
//根據指定算法ALGORITHM自成密碼器
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
//初始化密碼器,第一個參數為加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作,第二個參數為生成的AES密鑰
cipher.init(Cipher.DECRYPT_MODE, skey);
//把密文字符串轉回密文字節數組
byte [] encode_content = Base64.decodeBase64(encryptStr);
//密碼器解密數據
byte [] byte_content = cipher.doFinal(encode_content);
//將解密后的數據轉換為字符串返回
return new String(byte_content,"utf-8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
前端代碼:
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.w3.org/1999/xhtml"
>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<script src="/js/jquery-3.3.1.min.js" ></script>
<script src="/js/bootstrap.min.js" ></script>
<script src="https://cdn.bootcss.com/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<title>AES</title>
<style>
.bgColor {
background-color: rgba(243, 66, 111, 0.15)
}
.divBorder {
border: solid 1px rgba(12, 24, 255, 0.15);
padding: 10px;
margin-top: 10px;
border-radius: 10px;
text-align: center;
vertical-align: middle;
}
.h4font {
margin-top: 0px;
font-family: 微軟雅黑;
font-weight: 500;
}
.center {
padding: 20% 0;
}
.verifyInput {
vertical-align: middle;
font-size: 14px;
font-weight: normal;
line-height: 1;
/*border:1px solid #999;*/
float: left;
width: 180px;
height: 30px;
}
.verifyImage {
vertical-align: middle;
float: right;
height: 30px;
}
</style>
</head>
<body>
<div class="container">
<div class="row center">
<div class="divBorder col-sm-offset-4 col-sm-4">
<h3 class="panel panel-heading h4font">
用戶登錄
</h3>
<h4 name="msg" th:text="${msg}"></h4>
<input type="hidden" name="key" value="">
<form class="form-horizontal" method="post">
<div class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-user" aria-hidden="true"></i></span>
<input type="text" class="form-control" name="userName" placeholder="請輸入用戶名稱"
th:value="${userName}"/>
</div>
<br>
<div class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<input type="text" class="form-control" name="password" th:value="${password}"
placeholder="請輸入密碼"/>
</div>
<br/>
<br>
<input type="button" name="btnLogin" class="btn btn-lg btn-block btn-info" value="登 錄">
</form>
</div>
</div>
</div>
<script th:inline="javascript">
$(function () {
$.ajax({
type: "get",
url: "/key",
success: function (result) {
console.log(result);
$('input[name="key"]').val(result)
}
})
$('input[name="btnLogin"]').click(function () {
var $key = $('input[name="key"]').val();
var $userName = $('input[name="userName"]').val();
var $password = $('input[name="password"]').val();
var key = CryptoJS.enc.Utf8.parse($key);
console.log("key:" + key + ",$key:" + $key);
var password = CryptoJS.enc.Utf8.parse($password);
var encrypted = CryptoJS.AES.encrypt(password, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7});
var encryptedPwd = encrypted.toString();
console.log("encrypted:" + encrypted);
console.log("encryptedPwd:" + encryptedPwd);
var decrypt = CryptoJS.AES.decrypt(encryptedPwd, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
var testDecryptStr = CryptoJS.enc.Utf8.stringify(decrypt).toString();
console.log("decrypt:" + decrypt);
console.log("testDecryptStr:" + testDecryptStr);
$.ajax({
type: "post",
url: "/login",
data: {userName: $userName, password: encryptedPwd, key: $key },
dataType: "json",
success: function (result) {
alert("后端解密后的密碼:"+result);
}
});
})
})
</script>
</body>
</html>
AES是通過后台隨機生成的key進行密碼的加密和解密的
2.什么是非對稱加密
非對稱加密算法需要兩個密鑰:公開密鑰(publickey:簡稱公鑰)和私有密鑰(privatekey:簡稱私鑰)。公鑰與私鑰是一對,如果用公鑰對數據進行加密,只有用對應的私鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。
RAS則是一種非對稱加密算法
代碼實現流程:
實現步驟
生成公鑰和私鑰
首先對於非對稱加密的話必須要有公鑰和私鑰,產生公鑰和私鑰的方式有很多種,我使用支付寶開放平台開發助手工具生成,也可以使用在線生成
把生成的公鑰和私鑰保存起來,前端頁面用公鑰加密,后端通過私鑰解密
導入依賴
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
前端代碼
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Login</title>
<script src="https://blog.yoodb.com/app/default/js/jquery-2.1.4.min.js"></script>
<script src="http://passport.cnblogs.com/scripts/jsencrypt.min.js"></script>
<script type="text/javascript">
$(function () {
$("#btn").click(function () {
let inputData = $('#data').val();
let encrypt = new JSEncrypt();
encrypt.setPublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXQHkn4dX3oUppqr2Vj7pBEDoe5Y+wJuTFqVImFU7xyDHCzwVY40q8/2OCmoMnrnjORL6Yh7UZl9GQ/FOvJHAnXrClU2mMRDzhTG9tw4mvsfDk/EHpJ+oCDUmlSqp1rG78mc89rjXqoaRzSsdqYB9bnpBHVjG8r1gmcRqKvCpuvQIDAQAB");
let data = encrypt.encrypt(inputData);
$("#encrypt").val(data);
$.ajax({
type: "GET",
url: "/rsaDecrypt",
data:{
data:data
},
success: function (result) {
console.log(result);
$("#decrypt").val(result);
}
})
})
});
</script>
</head>
<body>
<div>
<div>
<label> 請輸入要加密的字符串:</label>
<input type="text" id="data" value="" />
<button id = btn >發送</button>
</div>
<div style=" margin-top: 20px">
<label>前端加密后:</label>
<textarea id="encrypt"></textarea>
</div>
<div style=" margin-top: 20px">
<label>后端解密后:</label>
<textarea id="decrypt"></textarea>
</div>
</div>
</body>
</html>
后端代碼:RSAUtils工具類
package utils;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @author : sean
* @version V1.0
* @Project: jsencrypt
* @Package utils
* @date Date : 2021年09月23日 21:36
* @Description: RSA加解密工具類,實現公鑰加密私鑰解密和私鑰解密公鑰解密
*/
public class RsaUtils {
/**
* 公鑰加密私鑰解密
*/
private static void test1(RSAKeyPair keyPair, String source) throws Exception {
System.out.println("***************** 公鑰加密私鑰解密開始 *****************");
String text1 = encryptByPublicKey(keyPair.getPublicKey(), source);
String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);
System.out.println("加密前:" + source);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (source.equals(text2)) {
System.out.println("解密字符串和原始字符串一致,解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失敗");
}
System.out.println("***************** 公鑰加密私鑰解密結束 *****************");
}
/**
* 私鑰加密公鑰解密
*
* @throws Exception
*/
private static void test2(RSAKeyPair keyPair, String source) throws Exception {
System.out.println("***************** 私鑰加密公鑰解密開始 *****************");
String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), source);
String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1);
System.out.println("加密前:" + source);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (source.equals(text2)) {
System.out.println("解密字符串和原始字符串一致,解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失敗");
}
System.out.println("***************** 私鑰加密公鑰解密結束 *****************");
}
/**
* 公鑰解密
*
* @param publicKeyText
* @param text
* @return
* @throws Exception
*/
public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 私鑰加密
*
* @param privateKeyText
* @param text
* @return
* @throws Exception
*/
public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 私鑰解密
*
* @param privateKeyText
* @param text
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 公鑰加密
*
* @param publicKeyText
* @param text
* @return
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 構建RSA密鑰對
*
* @return
* @throws NoSuchAlgorithmException
*/
public static RSAKeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
RSAKeyPair rsaKeyPair = new RSAKeyPair(publicKeyString, privateKeyString);
return rsaKeyPair;
}
/**
* RSA密鑰對對象
*/
public static class RSAKeyPair {
private String publicKey;
private String privateKey;
public RSAKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
}
}
后端代碼:RSAController
@RestController
public class RsaController {
private static final String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANdAeSfh1fehSmmqvZWPukEQOh7lj7Am5MWpUiYVTvHIMcLPBVjjSrz/Y4KagyeueM5EvpiHtRmX0ZD8U68kcCdesKVTaYxEPOFMb23Dia+x8OT8Qekn6gINSaVKqnWsbvyZzz2uNeqhpHNKx2pgH1uekEdWMbyvWCZxGoq8Km69AgMBAAECgYEAj+d0mCstRv43p2kUkfaiyHi2wo0qgNOfwl2uo/M+8fmq+tg4dPKPtsbumhP+dvr3nL7sxUCE3HVZD5sBv2eW6iFCW8TRm9z3OHmcBPWPoCoc9BtWwXcm6MA29J1KJqvsGNLwGUcRtoMcYE/OWed1hCrp4kn9AI6LhgPt75rj8qkCQQDuxSJrc1x2TIiQfkAYFiw5U6SldDNEM0PRy2qZv2+cSHCuMrDmr0b2nBPj0RR6Q9yvQbYVUfR4LGCD6Cd1l5q3AkEA5sjhY+qLMghjMyJNUTXZceBxc99o5H6d8CUe16zeCfbdQkeI5JUnzk6Mjg8Wcf8XxTboi/uRUYWcKjJg+v4eKwJAM5ezLUabFxDIfXhaPxojaiuxqvKl1TnCkMWEfj5ITpu0hV98rAv5qHXnMlXON/EL8W6gepDf40urezUhuZ4NlwJBAOKFLWVq4zE6tlOMSaN6XXG+wNzg3g3YkaESblF3JYFWQxo5KI5kMGv5AVC2UmuV3HkASgSL6bjAkeWBCVuSbX0CQC5xBG/83h0Q1Rnozv1hfj0pQL9QDBsxYKRl0XpyjUbCBF/F0oAv1DfUEOwHbCkgDlBl5U8jSHXvkvOOd8Qe18U=";
@GetMapping("/rsaDecrypt")
public String DecryptAction(String data) throws Exception {
System.out.println(data);
String decrypt = RsaUtils.decryptByPrivateKey(privateKey, data);
return decrypt;
}
}