参考文章
常用算法
AES
高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图:
代码:
package com.qzn.demo.utils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Scanner;
/**
* AES对称加密和解密
*/
public class SecurityAESUtil {
private SecurityAESUtil() {
}
/**
* 加密
* 1.构造密钥生成器
* 2.根据ecnodeRules规则初始化密钥生成器
* 3.产生密钥
* 4.创建和初始化密码器
* 5.内容加密
* 6.返回字符串
*
* @param encodeRules 密钥
* @param content 明文
*/
public static String encode(String encodeRules, String content) {
try {
Cipher cipher = generateCipher(encodeRules, Cipher.ENCRYPT_MODE);
//8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byteEncode = content.getBytes(StandardCharsets.UTF_8);
//9.根据密码器的初始化方式--加密:将数据加密
byte[] byteAES = cipher.doFinal(byteEncode);
//10.将加密后的数据转换为字符串
return new BASE64Encoder().encode(byteAES);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 创建密码器
*
* @param encodeRules 密钥
* @param mode 加密/解密模式: 加密 Cipher.ENCRYPT_MODE,解密 Cipher.DECRYPT_MODE
*/
private static Cipher generateCipher(String encodeRules, Integer mode) {
try {
//1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
//2.根据ecnodeRules规则初始化密钥生成器生成一个128位的随机源,根据传入的字节数组
keygen.init(128, new SecureRandom(encodeRules.getBytes()));
//3.产生原始对称密钥
SecretKey originalKey = keygen.generateKey();
//4.获得原始对称密钥的字节数组
byte[] raw = originalKey.getEncoded();
//5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, "AES");
//6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance("AES");
//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(mode, key);
return cipher;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
/**
* 解密
* 1.同加密1-4步
* 2.将加密后的字符串反纺成byte[]数组
* 3.将加密内容解密
*
* @param encodeRules 密钥
* @param content 密文
*/
public static String decode(String encodeRules, String content) {
try {
Cipher cipher = generateCipher(encodeRules, Cipher.DECRYPT_MODE);
//8.将加密并编码后的内容解码成字节数组
byte[] byteContent = new BASE64Decoder().decodeBuffer(content);
//9.解密
byte[] byteDecode = cipher.doFinal(byteContent);
return new String(byteDecode, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
/*
* 加密
*/
System.out.println("使用AES对称加密,请输入加密的规则");
String encodeRules = scanner.next();
System.out.println("请输入要加密的内容:");
String content = scanner.next();
String encodeContent = SecurityAESUtil.encode(encodeRules, content);
System.out.println("根据输入的规则" + encodeRules + "加密后的密文是:" + encodeContent);
/*
* 解密
*/
System.out.println("使用AES对称解密,请输入加密的规则:(须与加密相同)");
encodeRules = scanner.next();
System.out.println("根据输入的规则" + encodeRules + "解密后的明文是:" + SecurityAESUtil.decode(encodeRules, encodeContent));
}
}
运行结果:
加密解密的密钥相等,如下:
加密解密的密钥不相等,如下:
RSA
RSA 加密算法是一种典型的非对称加密算法,它基于大数的因式分解数学难题,它也是应用最广泛的非对称加密算法。非对称加密是通过两个密钥(公钥-私钥)来实现对数据的加密和解密的。公钥用于加密,私钥用于解密
代码
package com.qzn.demo.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA 非对称加密
*/
@Slf4j
public class SecurityRsaUtil {
/**
* 公钥文件路径
*/
private static final String publicKeyFile = "D:/datas/temp/a/public_key.pem";
/**
* 私钥文件路径
*/
private static final String privateKeyFile = "D:/datas/temp/a/private_key.pem";
/**
* 随机生成密钥对。公钥发给别人,私钥秘密保留。
*/
public static void genKeyPair() {
try {
// 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())));
IOUtils.write(publicKeyString, new FileOutputStream(publicKeyFile), StandardCharsets.UTF_8);
IOUtils.write(privateKeyString, new FileOutputStream(privateKeyFile), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 加密
*
* @param text 明文
* @return 密文
*/
public static String encrypt(String text) {
try {
String publicKeyContent = IOUtils.toString(new FileInputStream(publicKeyFile), StandardCharsets.UTF_8);
byte[] publicKeyByte = Base64.decodeBase64(publicKeyContent);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyByte);
KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = rsaKeyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] clear = text.getBytes(StandardCharsets.UTF_8);
byte[] encrypted = cipher.doFinal(clear);
return Base64.encodeBase64String(encrypted);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
/**
* 解密
*
* @param text 密文
* @return 明文
*/
public static String decrypt(String text) {
try {
String privateKeyContent = IOUtils.toString(new FileInputStream(privateKeyFile), "UTF-8");
byte[] privateKeyByte = Base64.decodeBase64(privateKeyContent);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyByte);
KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = rsaKeyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encrypted = Base64.decodeBase64(text);
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
public static void main(String[] args) {
//产生公钥私钥
// genKeyPair();
String password = "hello@world@123";
System.out.println("加密前的明文=" + password);
String encode1 = SecurityRsaUtil.encrypt(password);
System.out.println("第一次加密后的密文=" + encode1);
String encode2 = SecurityRsaUtil.encrypt(password);
System.out.println("第二次加密后的密文=" + encode2);
String decode1 = SecurityRsaUtil.decrypt(encode1);
System.out.println("解密第一次的密文=" + decode1);
String decode2 = SecurityRsaUtil.decrypt(encode2);
System.out.println("解密第二次的密文=" + decode2);
}
}
运行:genKeyPair(); 可以得到公钥文件: private_key.pem,私钥文件:public_key.pem 。如下。把公钥给客户,私钥保留。
运行上面的代码结果如下:
可以看到RSA每次用公钥加密后的密文是不一样的。但每次解密的结果是一样的。
RSA 速度慢,只对少量数据加密。ASE 速度快,可以加密较大的数据。所以对大数据的时候ASE RSA混合使用。思路如下;
1,随机生成一个字符串,如UUID 。作为AES 的秘钥secretKey 。
2,对大数据用ASE加密,秘钥secretKey(因为ASE效率快),得到密文 secretData
3,通过RSA用公钥对秘钥secretKey加密,得到 secretKey2
4,把secretData,secretKey2,都传给服务端(都是密文,很安全)
5,服务端通过RSA用私钥解密secretKey2得到secretKey
6,服务端通过AES用秘钥secretKey对secretData解密,得到真实数据
CRC、MD5、SHA1
相同点:
CRC、MD5、SHA1都是通过对数据进行计算,来生成一个校验值,该校验值用来校验数据的完整性。具有不可逆,压缩性
不同点:
名称 | 算法 | 校验值长度 | 校验值称呼 | 安全性 | 效率 |
---|---|---|---|---|---|
CRC | 多项式除法 | 4位 | CRC值 | 一般 | 最高 |
MD5 | 替换、轮转等方法 | 32位 | 哈希值(Hash) | 较高 | 一般 |
SHA1 | 替换、轮转等方法 | 40位 | 哈希值(Hash) | 最高 | 一般 |
安全性:这里的安全性是指检错的能力,即数据的错误能通过校验位检测出来。CRC的安全性跟多项式有很大关系,相对于MD5和SHA1要弱很多;MD5的安全性很高,不过大概在04年的时候被山东大学的王小云破解了;SHA1的安全性最高。
用途:CRC的效率最高,不常用,一般用作通信数据的校验。D5和SHA1用于安全(Security)领域,比如文件校验、数字签名等
代码:
package com.qzn.demo.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
//import org.springframework.util.DigestUtils;
/**
* MD5、SHA1都是通过对数据进行计算,来生成一个校验值,该校验值用来校验数据的完整性。具有不可逆,压缩性
*/
@Slf4j
public class SecurityUtil {
/**
* 盐,用于混交
*/
private static final String slat = "&%5123***&&%%$$#@";
private SecurityUtil() {
}
/**
* MD5
*
* @param data 数据
*/
public static String getMD5(String data) {
String base = data + "/" + slat;
return DigestUtils.md5Hex(base);
}
/**
* CRC
*
* @param data 数据
*/
public static String getSHA1(String data) {
String base = data + "/" + slat;
return DigestUtils.sha1Hex(base);
}
public static void main(String[] args) {
String password = "hello@world@123123";
System.out.println("原数据=" + password);
String md5 = SecurityUtil.getMD5(password);
System.out.println("md5加密后=" + md5 + " ,位数=" + md5.length());
String sha1 = SecurityUtil.getSHA1(password);
System.out.println("sha1加密后=" + sha1 + " ,位数=" + sha1.length());
}
}
运行结果: