vue对密码对称加密和非对称加密处理
之前博客请求参数与数据库存储都是明文密码,可以说毫无安全性。所以近期对密码进行加密处理。我首先选用的是对称加密方式处理密码。
个人小站:什么是快乐
AES加密
AES加密是对称加密,前端加密:
1、首先npm安装
//安装
npm install crypto-js --save-dev
2、第二步:在src目录下的公用js文件夹中,再建一个AES.js文件
import CryptoJS from 'crypto-js/crypto-js'
// 默认的 KEY 与 iv 如果没有给
const KEY = CryptoJS.enc.Utf8.parse("1234567890123456");//秘钥
const IV = CryptoJS.enc.Utf8.parse('1234567890123456');//偏移量
/**
\* AES加密 :字符串 key iv 返回base64
*/
export function Encrypt(word, keyStr, ivStr) {
let key = KEY;
let iv = IV;
if (keyStr) {
key = CryptoJS.enc.Utf8.parse(keyStr);
iv = CryptoJS.enc.Utf8.parse(ivStr);
}
let srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}
/**
\* AES 解密 :字符串 key iv 返回base64
*
\* @return {string}
*/
export function Decrypt(word, keyStr, ivStr) {
let key = KEY;
let iv = IV;
if (keyStr) {
key = CryptoJS.enc.Utf8.parse(keyStr);
iv = CryptoJS.enc.Utf8.parse(ivStr);
}
let base64 = CryptoJS.enc.Base64.parse(word);
let src = CryptoJS.enc.Base64.stringify(base64);
let decrypt = CryptoJS.AES.decrypt(src, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
return decryptedStr.toString();
}
3、在vue中使用
引入上面的js方法
import {Encrypt,Decrypt} from "../../assets/js/secret"
testAes: function () {
var s = '什么是快乐'
var s1=Encrypt(s)
console.log('加密后的字符串:'+s1);
var s2=Decrypt(s1)
console.log('解密后的字符串:'+s2);
}
运行截图
前端加密后使用ajax传入后端,后端处理接收的加密解密
1、maven添加依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
2、java 方法
public class Encrypt {
//--------------AES---------------
private static final String KEY = "1234567890123456"; // 密匙,必须16位
private static final String ENCODING = "UTF-8"; // 编码
private static final String ALGORITHM = "AES"; //算法
private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding"; // 默认的加密算法,ECB模式
/**
\* AES加密
\* @param data
\* @return String
*/
public static String AESencrypt(String data) throws Exception
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE,new SecretKeySpec(KEY.getBytes(),"AES"));
byte[] b = cipher.doFinal(data.getBytes("utf-8"));
//采用base64算法进行转码,避免出现中文乱码
return Base64.encodeBase64String(b);
}
/**
\* AES解密
\* @param data
\* @return String
*/
public static String AESdecrypt(String data) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(KEY.getBytes(), "AES"));
byte[] b = cipher.doFinal(Base64.decodeBase64(data));
//采用base64算法进行转码,避免出现中文乱码
return new String(b);
}
public static void main(String[] args) throws Exception {
String mi = Encrypt.AESencrypt("111111");
System.out.println(mi);
}
}
这个需要注意的是:前端和后段的对加密的填充方式必需保持一致。java本身只支持NoPadding和PKCS5Padding填充。而crypto-js提供的padding没有PKCS5Padding。这里的解决方法参考:https://blog.csdn.net/fei565789229/article/details/87487250
在使用AES加密的过程中,我发现我的密码是在前端加密的,并且密钥啥的是存在JS里。在浏览器的开发者选项中都能直接查到。那就等于直接给别人看密码了,所以感觉这个加密方式并不适合我。继续学习后发现了:非对称加密算法
RSA加密
RSA加密是一个非对称加密,它包括了一对私钥和公钥,公钥是公开出去的,私钥是保密的。公钥加密的内容需要用私钥解密。基于这个特性对于密码加密有了新的思路:vue对密码进行公钥加密,通过ajax请求将密文传入后端,后端使用私钥解密。
公钥密钥有工具网站可以生成:第三方生成RSA密钥工具
由于并不放心第三方网站自动生成的密钥,所以打算后端随机生成一对公私钥。公钥传给前端加密,将私钥上传redis。用户注册或者登录将公钥加密后的密文传入后端,后端获取redis存储的私钥解密。在进行业务处理。数据库也不能存储明文密码,在保存数据库时在做一次加密。
生成公钥私钥
创建一个RSAEncrypt.java文件
package com.lijie.ljblog.util;
import org.apache.tomcat.util.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;
import java.util.HashMap;
import java.util.Map;
/**
* @Title: ljblog_springboot
* @Description: ()
* @Author:lijie
* @Version:1.1.0
* @since 2022/1/11
*/
public class RSAEncrypt {
/**
* 随机生成密钥对
* @return map存公钥秘钥;key:0 -公钥 ;key:1 -私钥
* @throws NoSuchAlgorithmException
*/
public static Map<Integer, String> genKeyPair() throws NoSuchAlgorithmException {
Map<Integer, String> result=new HashMap<>();
// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密钥对生成器,密钥大小为96-1024位
keyPairGen.initialize(1024,new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥
String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
// 得到私钥字符串
String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
// 将公钥和私钥保存到Map
result.put(0,publicKeyString); //0表示公钥
result.put(1,privateKeyString); //1表示私钥
return result;
}
/**
* RSA公钥加密
*
* @param str
* 加密字符串
* @param publicKey
* 公钥
* @return 密文
* @throws Exception
* 加密过程中的异常信息
*/
public static String encrypt( String str, String publicKey ) throws Exception{
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
return outStr;
}
/**
* RSA私钥解密
*
* @param str
* 加密字符串
* @param privateKey
* 私钥
* @return 铭文
* @throws Exception
* 解密过程中的异常信息
*/
public static String decrypt(String str, String privateKey) throws Exception{
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
/**
* RSA私钥加密
*
* @param str
* 加密字符串
* @param privateKey
* 私钥
* @return 密文
* @throws Exception
* 加密过程中的异常信息
*/
public static String encryptBypri( String str, String privateKey ) throws Exception{
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey pubKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
// String outStr = java.util.Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8")));
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
return outStr;
}
/**
* RSA公钥解密
*
* @param str
* 加密字符串
* @param publicKey
* 公钥
* @return 密文
* @throws Exception
* 加密过程中的异常信息
*/
public static String decryptBypub( String str, String publicKey ) throws Exception{
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey priKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
public static void main(String[] args) throws Exception {
//生成公钥和私钥
Map<Integer, String> keyMap = genKeyPair();
//加密字符串
String message = "什么是快乐";
System.out.println("随机生成的公钥为:" + keyMap.get(0));
System.out.println("随机生成的私钥为:" + keyMap.get(1));
String messageEn = encrypt(message,keyMap.get(0));
String messageEn2 = decrypt(messageEn,keyMap.get(1));
System.out.println(messageEn);
System.out.println(messageEn2);
}
}
测试运行截图
随机生成的公钥为:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9dbooWM72qimN1WjxnkXTR2FNMX+EUDIA3BTPQQX2bn61bntD9hps+DOhNX4Wgb3l6wsqOEy/MhLXLojI0lBWfzf6Ch5MuAj9vnIINZsAEgcZw+d8F1R73czZIGk3f4OWCLYV3ZcXlxJChSGuTRKLOfuq1eMrJbfY9l0F1QZp7wIDAQAB
随机生成的私钥为:MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL11uihYzvaqKY3VaPGeRdNHYU0xf4RQMgDcFM9BBfZufrVue0P2Gmz4M6E1fhaBveXrCyo4TL8yEtcuiMjSUFZ/N/oKHky4CP2+cgg1mwASBxnD53wXVHvdzNkgaTd/g5YIthXdlxeXEkKFIa5NEos5+6rV4yslt9j2XQXVBmnvAgMBAAECgYBvJ4zqcwAp87ehgR2q9ELfLDfIBExeInbTzktfNTT8WeN5KrKjMtZaLFyOLkYlikURiY7pXFKashQfryuNNubuczHvb3n+iyGpWySETx7QYgKeTIDy3/zYbcHOI2Y7yixfBQ29IrSv1SWhhG5UEQqIVJrs2QEWL6+FOsGiPOwPYQJBAP+5dn1kmYtSK11mZVkzwHFbmezZNs/AQE6v6TErd7amBQpyCP9wwoEeOsreHDxt8/NiqbAvr43orgHHcOdAG0kCQQC9qfyEIIagCui3zM04tzN6WmclDXL8t+dHMRkt8Zmyu7WMqXoYjgwMcv5n7qNJaxi9V2MMA9Y+P3EyCCjFyON3AkAgrNv9Dbuo16j+2DSe/0PZuDCk2ORuC0ysQUH0rG8xgWkneoa3zPqtcOVmQpctCYegU70G7DDsWwzQ4WEFy2UxAkAJ0QKA1C4jmmzxFmiIxvKjIoq0ID4TVnFh/eiPuDF8TrrWzO7E1pVJD6qbNn69Dg35vswhkxFDp4T0UffKRNuBAkBe1e4IhJzFvXlTtjA2PBXvMrpIH8d7KAGvMvJ2MuWvwYCXOkHdBdO9Z9y9A4H2y3rjeFPtZqALVB9P/5yhqsfn
公钥加密后:nVwqASzu5zwJOy1ADkVC1Vji9LxCmgwVjd1+2S1KEsKvk/ru0Hmij5weZMjkgORxpxjwqF7Dx7xVUcOuUwGtDMY/mhJVkAMRUWfRZyLFZXb5C710EqpEkzWIFbCdhqXWpgDzc5W/enBjHednxVlZ7tlXJCivwgkxg+4l1+mN2Ho=
私钥钥加密后:什么是快乐
上述代码参考了,RSA加密与解密(Java实现)。但是它只有公钥加密,私钥解密。再此基础上我增加了私钥加密,公钥解密的方法。
前端加密
后端成功公钥私钥后下一步就是在前端对数据进行公钥加密
第一步 npm引入jsencrypt
npm i jsencrypt -S
第二步 创建一个js文件
/* 产引入jsencrypt实现数据RSA加密 */
import JSEncrypt from 'jsencrypt' // 处理长文本数据时报错 jsencrypt.js Message too long for RSA
/* 产引入encryptlong实现数据RSA加密 */
import Encrypt from 'encryptlong' // encryptlong是基于jsencrypt扩展的长文本分段加解密功能。
//加密,公钥加密
export function encrypt(data,publicKey) {
var encryptor = new Encrypt()
encryptor.setPublicKey(publicKey)
// 如果是对象/数组的话,需要先JSON.stringify转换成字符串
const result = encryptor.encryptLong(data)
return result
}
export function decrypt(data,publicKey) {
var encryptor = new Encrypt()
encryptor.setPrivateKey(publicKey)
// 如果是对象/数组的话,需要先JSON.stringify转换成字符串
var result = encryptor.decryptLong(data)
return result
}
第三、在vue文件中使用
引入上面的方法
import {encrypt,decrypt} from "../../assets/js/rsaUtil"
测试代码:
testAes: function () {
var s = '什么是快乐'
var pubKey='MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9dbooWM72qimN1WjxnkXTR2FNMX+EUDIA3BTPQQX2bn61bntD9hps+DOhNX4Wgb3l6wsqOEy/MhLXLojI0lBWfzf6Ch5MuAj9vnIINZsAEgcZw+d8F1R73czZIGk3f4OWCLYV3ZcXlxJChSGuTRKLOfuq1eMrJbfY9l0F1QZp7wIDAQAB'
var s1=encrypt(s,pubKey)
console.log('加密后的字符串:'+s1);
var priKey='MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL11uihYzvaqKY3VaPGeRdNHYU0xf4RQMgDcFM9BBfZufrVue0P2Gmz4M6E1fhaBveXrCyo4TL8yEtcuiMjSUFZ/N/oKHky4CP2+cgg1mwASBxnD53wXVHvdzNkgaTd/g5YIthXdlxeXEkKFIa5NEos5+6rV4yslt9j2XQXVBmnvAgMBAAECgYBvJ4zqcwAp87ehgR2q9ELfLDfIBExeInbTzktfNTT8WeN5KrKjMtZaLFyOLkYlikURiY7pXFKashQfryuNNubuczHvb3n+iyGpWySETx7QYgKeTIDy3/zYbcHOI2Y7yixfBQ29IrSv1SWhhG5UEQqIVJrs2QEWL6+FOsGiPOwPYQJBAP+5dn1kmYtSK11mZVkzwHFbmezZNs/AQE6v6TErd7amBQpyCP9wwoEeOsreHDxt8/NiqbAvr43orgHHcOdAG0kCQQC9qfyEIIagCui3zM04tzN6WmclDXL8t+dHMRkt8Zmyu7WMqXoYjgwMcv5n7qNJaxi9V2MMA9Y+P3EyCCjFyON3AkAgrNv9Dbuo16j+2DSe/0PZuDCk2ORuC0ysQUH0rG8xgWkneoa3zPqtcOVmQpctCYegU70G7DDsWwzQ4WEFy2UxAkAJ0QKA1C4jmmzxFmiIxvKjIoq0ID4TVnFh/eiPuDF8TrrWzO7E1pVJD6qbNn69Dg35vswhkxFDp4T0UffKRNuBAkBe1e4IhJzFvXlTtjA2PBXvMrpIH8d7KAGvMvJ2MuWvwYCXOkHdBdO9Z9y9A4H2y3rjeFPtZqALVB9P/5yhqsfn'
var s2=decrypt(s1,priKey)
console.log(s2);
}
运行结果
可以看到虽然前端和后端两者加密后的密文不一样,但是都能通过私钥成功解码。
在之后就是明文加密后保存数据库了。