常见的加密解密算法,AES、RSA、Base64、MD5,别人的例子很多,自己亲测的,才是自己掌握的。
try {
BASE64Encoder base64Encoder = new BASE64Encoder();
//加密
String encodeString = base64Encoder.encode("生死看淡,不服来干!".getBytes("utf-8"));
System.out.println(encodeString);
//解密
BASE64Decoder base64Decoder = new BASE64Decoder();
String source = new String(base64Decoder.decodeBuffer(encodeString), "utf-8");
System.out.println(source);
} catch (Exception e) {
e.printStackTrace();
}
//输出:
55Sf5q2755yL5reh77yM5LiN5pyN5p2l5bmy77yB
生死看淡,不服来干!
Base64是一种编码的转换,编码规则是公开的,所以不能用做加密用途,它的主要作用不在于安全性,而在于让内容能在网络间无错的传输。比如URL里面的key-value包含了等号,或者用其它加密算法加密后的报文出现乱码或者不友好的特殊符号,这时候就可以使用Base64编码。
import org.springframework.util.DigestUtils;
/**
* @Author 哆啦的哆啦美
* @Description MD5加密与验证
* @Date 2019/6/17 15:51
* @Modify
*/
public class MD5Util {
public static void main(String[] args) {
try {
String sign = md5("生死看淡,不服就干!");
System.out.println("解密后:" + sign);
System.out.println(verify("生死看淡,不服就干!", sign));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* MD5方法
*
* @param content 明文
* @return 密文
* @throws Exception
*/
public static String md5(String content) throws Exception {
//加密后的字符串
String encodeStr = DigestUtils.md5DigestAsHex(content.getBytes("utf-8"));
return encodeStr;
}
/**
* MD5验证方法
*
* @param content 明文
* @param sign 密文
* @return true/false
* @throws Exception
*/
public static boolean verify(String content, String sign) throws Exception {
//根据传入的密钥进行验证
String md5Text = md5(content);
if (md5Text.equalsIgnoreCase(sign)) {
System.out.println("MD5验证通过");
return true;
}
return false;
}
}
输出内容:
解密后:11389f5464ddc5c00b854e1ff7bf347d
MD5验证通过
true
MD5是不可逆的,即加密后的字符串,是无法反推出原文是什么的,这决定了MD5通常用于加密验证,比如把用户密码进行MD5加密后存进数据库,要验证用户密码的时候也是进行MD5加密后再进行对比,这样子就算数据库被攻破,用户的密码也不会被泄露出去,因为无法解密。
此外,还经常用户保证数据的完整性,比如通过自己定义的一些规则,对报文进行DM5加密,服务器收到报文后,通过同样的规则进行加密,如果得到不同样的结果,则该报文被修改过,是不可信的。
网上通常的加密方法有:MD5(MD5(用户名+用户密码)+MD5(KEY+项目名+公司名))
这只是个例子,定义的方法可以千变万化,看个人习惯。
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
/**
* @Author 哆啦的哆啦美
* @Description AES加密解密
* @Date 2019/6/17 14:04
*/
public class AESUtil {
public static void main(String[] args) {
byte[] encrypt1 = AESUtil.encrypt("生死看淡,不服来战!", "123456");
byte[] encrypt2 = AESUtil.encrypt("生死看淡,不服来战!", "654321");
System.out.println("加密1:" + encrypt1);
System.out.println("加密2:" + encrypt2);
System.out.println("解密1:" + AESUtil.decrypt(encrypt1, "123456"));
System.out.println("解密2:" + AESUtil.decrypt(encrypt2, "654321"));
try {
System.out.println("密码错误:" + AESUtil.decrypt(encrypt2, "666666"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* AES加密字符串
*
* @param content 需要被加密的字符串
* @param password 加密需要的密码
* @return 密文
*/
public static byte[] encrypt(String content, String password) {
try {
// 创建AES的Key生产者
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
// 利用用户密码作为随机数初始化出128位的key生产者
keyGenerator.init(128, new SecureRandom(password.getBytes()));
// 根据用户密码,生成一个密钥
SecretKey secretKey = keyGenerator.generateKey();
// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回null
byte[] enCodeFormat = secretKey.getEncoded();
// 转换为AES专用密钥
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
// 创建密码器
Cipher cipher = Cipher.getInstance("AES");
// 初始化为加密模式的密码器
cipher.init(Cipher.ENCRYPT_MODE, key);
//加密内容转字节类型
byte[] byteContent = content.getBytes("utf-8");
//加密
byte[] result = cipher.doFinal(byteContent);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decrypt(byte[] content, String password) {
try {
// 创建AES的Key生产者
KeyGenerator kgen = KeyGenerator.getInstance("AES");
// 利用用户密码作为随机数初始化出128位的key生产者
kgen.init(128, new SecureRandom(password.getBytes()));
// 根据用户密码,生成一个密钥
SecretKey secretKey = kgen.generateKey();
// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回null
byte[] enCodeFormat = secretKey.getEncoded();
// 转换为AES专用密钥
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
// 创建密码器
Cipher cipher = Cipher.getInstance("AES");
// 初始化为解密模式的密码器
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
// 明文
return new String(result, "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//输出:
加密1:[B@546a03af
加密2:[B@721e0f4f
解密1:生死看淡,不服来战!
解密2:生死看淡,不服来战!
密码错误:null
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
AES 是一种对称加密算法,即加密和解密使用同一个密钥的加密方式。由于它的加密是可逆的,通常用来加密一些不想被别人看到的报文,只有持有密码的人才能看到,其他人无法解密。
AES的算法安全性好高,没有密码几乎是无法破解的,但是当它的密码是动态的,需要在网络上传输的时候,密码保存与传输就存在安全问题。所以它经常与非对称加密算法RSA配合着使用,可往下看RSA加密算法。
package com.xuliubin.springbase.utils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
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;
/**
* @Author 哆啦的哆啦美
* @Description RSA加密解密
* @Date 2019/6/17 14:04
* @Modify
*/
public class RSAUtil {
//用于封装随机产生的公钥与私钥
private static Map<Integer, String> keyMap = new HashMap<Integer, String>();
private static BASE64Decoder base64Decoder = new BASE64Decoder();
private static BASE64Encoder base64Encoder = new BASE64Encoder();
public static void main(String[] args) {
try {
//生成公钥和私钥
genKeyPair();
//加密字符串
String message = "生死看淡,不服来干!";
System.out.println("随机生成的公钥为:" + keyMap.get(0));
System.out.println("随机生成的私钥为:" + keyMap.get(1));
String messageEn = encrypt(message, keyMap.get(0));
System.out.println("加密前的字符串为:" + message);
System.out.println("加密后的字符串为:" + messageEn);
String messageDe = decrypt(messageEn, keyMap.get(1));
System.out.println("还原后的字符串为:" + messageDe);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* RSA加密字符串
*
* @param content 需要被加密的字符串
* @param publicKey Base64编码的公钥
* @return 密文
*/
public static String encrypt(String content, String publicKey) throws Exception {
//base64解码的公钥
byte[] decodedKey = base64Decoder.decodeBuffer(publicKey);
//RSA公钥对象
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decodedKey));
// 创建RSA密码器
Cipher cipher = Cipher.getInstance("RSA");
// 初始化为加密模式的密码器
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
//加密并转为Base64编码
String outStr = base64Encoder.encode(cipher.doFinal(content.getBytes("UTF-8")));
return outStr;
}
/**
* RSA解密字符串
*
* @param content Base64编码的密文
* @param privateKey Base64编码的私钥
* @return 明文
*/
public static String decrypt(String content, String privateKey) throws Exception {
BASE64Decoder base64Decoder = new BASE64Decoder();
//Base64解码后的密文
byte[] inputByte = base64Decoder.decodeBuffer(content);
//Base64解码后的私钥
byte[] decodedKey = base64Decoder.decodeBuffer(privateKey);
//RSA密钥对象
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decodedKey));
// 创建RSA密码器
Cipher cipher = Cipher.getInstance("RSA");
// 初始化为解密模式的密码器
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte), "utf-8");
return outStr;
}
/**
* 获取秘钥对
*
* @throws Exception
*/
public static void genKeyPair() throws Exception {
// 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();
// 得到公钥Base64编码的字符串
String publicKeyString = new String(base64Encoder.encodeBuffer(publicKey.getEncoded()));
// 得到私钥Base64编码的字符串
String privateKeyString = new String(base64Encoder.encodeBuffer((privateKey.getEncoded())));
// 将公钥和私钥保存到Map
keyMap.put(0, publicKeyString); //0表示公钥
keyMap.put(1, privateKeyString); //1表示私钥
}
}
//输出:
随机生成的公钥为:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCaX9W3UuabdK0f9C2yK5jYNG8D6dFhWDK8KID4
t0S/2kwwF3dPEQxt0mvHAhBgJ1BKNtZzZYOqI6KO3e+rqRJwKX8/l8t6/UnKZhg/iYhBoo8ENR1s
Ytphdj57lnfvQ/2Yj5da6cPXNf7bZiex1GiWIfrsCveHc/LUZYeP6YASQwIDAQAB
随机生成的私钥为:MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJpf1bdS5pt0rR/0LbIrmNg0bwPp
0WFYMrwogPi3RL/aTDAXd08RDG3Sa8cCEGAnUEo21nNlg6ojoo7d76upEnApfz+Xy3r9ScpmGD+J
iEGijwQ1HWxi2mF2PnuWd+9D/ZiPl1rpw9c1/ttmJ7HUaJYh+uwK94dz8tRlh4/pgBJDAgMBAAEC
gYBt+P2HpnAPqJO4Yru25GXzB49aa1Q0k584+WW/SNeaEMobrGvbEJsZFUCgObEuvnLtG5m0BUpB
hzdDrYkScdRmpU8De7POvsI5xeDuR20cw573cQkU87txi8yXT2xR6ZgDePYN+h5nSkPi63zLbDBk
widhX/tPeThrotPspeT20QJBAM3rLRBoArvJVD+v8ScPE/wGQm0qYbKAEn91KjjuQbwDVN8Lsikr
9HXocU88+VsHQ3EdwUShvn2QUI0//DsITRsCQQC/622pzOD09GobqtAE/GpJXlZtsktyrfzqak7C
vt1AxYOzvUtqCXRB9vHQ84LIER/KqYcYcxoTCSjXm4jyN2n5AkBeQrluOS6HJ7IZNUD+0DgltffS
YQQ34ciV79xUEzykBCJlt8Tmc+iD0NONoivDL5//t88wy5mXgOhewFuF6lbhAkEAmTe6YCI5wjXS
9G2lDyX5MMPI2mDKTS7MSNVANces2cYWBAvuxiPwFa2xlnCiC1loeSJowUM7INFiMudwKHfG+QJB
AMs4FLQlr/ePCafHkn5QZbPVzm8txo6SqEgj8DpLf42kJP/HeV9rso4ul+QyyRnyzNZ0y/CGWdi3
umoDB/nXWWE=
加密前的字符串为:生死看淡,不服来干!
加密后的字符串为:TPZbjRhJHLefpFb1Tbics0LKbgW5tIPsT5DhX6cSln0nOEDLwjHvS7kh9YDmedNbOE8NkQFdqCCx
YgV1tVherVnKkFxEgP8uVdXocPZJZ/DMLPmkk1ZlpotLCq7xCGG7aT4c9eZBEixF3+lZra2G+aUI
juqdUgfKBrN4m+KcUtQ=
还原后的字符串为:生死看淡,不服来干!
上面说到AES算法经常会配合着RAS一起使用,它们2个的安全性都很高,但是在性能方面AES会好很多,特别是加密的内容越大,RAS就越吃不消,另一方面AES的密码又存在会泄露的不安全,而RAS的公钥是不怕泄露,所以有了结合使用的方案。
我理解的具体场景如下:
客户端需要向服务端发送请求,首先用AES加密算法把所有的内容加密起来,这样子只要保证AES的密码安全给到服务器,那么报文的内容就安全了。
怎么保证密码安全给到服务器呢?首先客户端需要向服务器请求一个RAS的加密公钥,拿到公钥后用于对AES密码进行加密,这样子世界上除了拥有秘钥的服务器,其它人都休想知道AES的密码是多少,也就无法解开报文的内容了。最后安全地把AES加密的报文和RSA加密的密码,一起发给服务器。
以下再贴一下,Vue里面进行RAS加密的代码。(BTW,即使是相同的公钥和相同的加密内容,加密后的结果都是不一样的,但解密出来一样)
import { JSEncrypt } from 'jsencrypt'
var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCaX9W3UuabdK0f9C2yK5jYNG8D6dFhWDK8KID4\n" +
"t0S/2kwwF3dPEQxt0mvHAhBgJ1BKNtZzZYOqI6KO3e+rqRJwKX8/l8t6/UnKZhg/iYhBoo8ENR1s\n" +
"Ytphdj57lnfvQ/2Yj5da6cPXNf7bZiex1GiWIfrsCveHc/LUZYeP6YASQwIDAQAB";
var privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJpf1bdS5pt0rR/0LbIrmNg0bwPp\n" +
"0WFYMrwogPi3RL/aTDAXd08RDG3Sa8cCEGAnUEo21nNlg6ojoo7d76upEnApfz+Xy3r9ScpmGD+J\n" +
"iEGijwQ1HWxi2mF2PnuWd+9D/ZiPl1rpw9c1/ttmJ7HUaJYh+uwK94dz8tRlh4/pgBJDAgMBAAEC\n" +
"gYBt+P2HpnAPqJO4Yru25GXzB49aa1Q0k584+WW/SNeaEMobrGvbEJsZFUCgObEuvnLtG5m0BUpB\n" +
"hzdDrYkScdRmpU8De7POvsI5xeDuR20cw573cQkU87txi8yXT2xR6ZgDePYN+h5nSkPi63zLbDBk\n" +
"widhX/tPeThrotPspeT20QJBAM3rLRBoArvJVD+v8ScPE/wGQm0qYbKAEn91KjjuQbwDVN8Lsikr\n" +
"9HXocU88+VsHQ3EdwUShvn2QUI0//DsITRsCQQC/622pzOD09GobqtAE/GpJXlZtsktyrfzqak7C\n" +
"vt1AxYOzvUtqCXRB9vHQ84LIER/KqYcYcxoTCSjXm4jyN2n5AkBeQrluOS6HJ7IZNUD+0DgltffS\n" +
"YQQ34ciV79xUEzykBCJlt8Tmc+iD0NONoivDL5//t88wy5mXgOhewFuF6lbhAkEAmTe6YCI5wjXS\n" +
"9G2lDyX5MMPI2mDKTS7MSNVANces2cYWBAvuxiPwFa2xlnCiC1loeSJowUM7INFiMudwKHfG+QJB\n" +
"AMs4FLQlr/ePCafHkn5QZbPVzm8txo6SqEgj8DpLf42kJP/HeV9rso4ul+QyyRnyzNZ0y/CGWdi3\n" +
"umoDB/nXWWE=";
// 新建JSEncrypt对象
let encryptor = new JSEncrypt()
// 设置公钥
encryptor.setPublicKey(publicKey)
// 对密码进行加密
let rsaPassWord = encryptor.encrypt('生死看淡,不服来干!')
console.log('加密后字符串' + rsaPassWord);
var decrypt = new JSEncrypt();
decrypt.setPrivateKey(privateKey);
//解密数据
var uncrypted = decrypt.decrypt(rsaPassWord);
console.log('解密后字符串:' + uncrypted)
//输出内容:
加密后字符串Ayqvj167sewOze2ViNZgraiaxVc+wb0p1fF80XJ0bOo+5njlt73CLLtFBE/8n1cK2WnHTQbIOpn3/ZnZkkGhBQ7khRZ4h0IH+vFvmNIO9waw9FwETq193V12b3VgSimP2lBjte1WeBcYRxKP+hyBDq2CK2tmdGXENAOjbxAblgg=
解密后字符串:生死看淡,不服来干!
仅仅是个人浅薄见解,不妥之处,还望指出。