一、介绍
数据加密是项目中非常常见的业务需求,封装好的三方组件也非常多,自己在工作时也经常会使用到,这次对主流常用的几种加密方式做一个梳理,会分别介绍其使用场景,以及如何使用。
二、对称加密
加密或者解密都需要使用相同的密钥,这意味着只要密钥泄露,数据就可以被随意解密,所以这种加密方式不适用于于三方公司合作或者前后端交互中使用。
一般情况下,对称加密用于数据存储时进行加密,数据在需要使用的时候进行解密,避免在数据库中明文存储的问题。
2.1、AES
2.1.1、 介绍
AES是一个块加密算法,意味着它会将明文数据分成固定大小的块(在AES中,每个块的大小是128位),然后对每个块进行加密。AES支持128位、192位和256位三种长度的密钥,这意味着AES的安全性非常高。
AES的工作过程包括多个加密轮次,每个轮次都包括一系列的操作,包括字节替换(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。这些操作一起确保了AES的高安全性。
AES的一个主要优点是它的效率很高,可以在各种不同的硬件和软件平台上实现,并且可以抵抗各种已知的攻击。因此,AES已经成为了全球最流行的对称加密算法之一,被广泛用于保护敏感的数据。
密钥长度必须是128、192或256位,这对应于16、24或32字节。
2.1.2、 使用
package com.xx.utils;import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;@Slf4j
public class AESUtil {/*** 算法定义*/private static final String AES_ALGORITHM = "AES";/*** 指定填充方式*/private static final String CIPHER_PADDING = "AES/ECB/PKCS5Padding";/*** AES加密** @param content 待加密内容* @param aesKey 密钥key* @return 加密后的内容*/public static String encrypt(String content, String aesKey) {//判断秘钥是否为16位try {//设置加密算法,生成秘钥SecretKeySpec keySpec = new SecretKeySpec(aesKey.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);// "算法/模式/补码方式"Cipher cipher = Cipher.getInstance(CIPHER_PADDING);//选择加密cipher.init(Cipher.ENCRYPT_MODE, keySpec);//根据待加密内容生成字节数组byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));//返回base64字符串return Base64Utils.encodeToString(encrypted);} catch (Exception e) {log.error("字符串【{}】加密失败:", content, e);return null;}}/*** 解密** @param content 待解密内容* @param aesKey 密钥Key* @return 密码*/public static String decrypt(String content, String aesKey) {try {SecretKeySpec keySpec = new SecretKeySpec(aesKey.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);Cipher cipher = Cipher.getInstance(CIPHER_PADDING);cipher.init(Cipher.DECRYPT_MODE, keySpec);byte[] decodeBase64 = Base64Utils.decodeFromString(content);return new String(cipher.doFinal(decodeBase64), StandardCharsets.UTF_8);} catch (Exception e) {log.error("字符串【{}】加密失败:", content, e);return null;}}public static void main(String[] args) {String key = "1111111111111111";String encrypt = encrypt("hello aes", key);String decrypt = decrypt(encrypt, key);System.out.println("encrypt:" + encrypt);System.out.println("decrypt:" + decrypt);}
}
2.1.3、 性能测试
测试代码
public static void main(String[] args) {String key_16 = "wakpzJ8dCjWmHSdd";String key_24 = "5r0pYWpkdExmhAajYQQhaDAQ";String key_32 = "81ZhAiXK1QGzQDGdEekr9JnAbTzC7EfE";// 模拟身份证长度String content = "123456789012345678";test(content, key_16, "16");test(content, key_24, "24");test(content, key_32, "32");}private static void test(String content, String key, String len) {long start = System.currentTimeMillis();for (int i = 0; i < 1000000; i++) {encrypt(content, key);}long end = System.currentTimeMillis();System.out.println(len + "位密钥长度耗时:" + (end - start));}
16位密钥长度耗时:3595
24位密钥长度耗时:3293
32位密钥长度耗时:3487
- 循环10万次(单位:毫秒)
文本 | 16位密钥耗时 | 24位密钥耗时 | 32位密钥耗时 |
---|---|---|---|
18位数字 | 3595 | 3293 | 3487 |
18位随机字符串 | 3705 | 3558 | 3492 |
36位随机字符串 | 3778 | 3735 | 3683 |
128位随机字符串 | 4145 | 3651 | 3766 |
- 大文件测试(加密一次,单位:毫秒)
文本 | 16位密钥耗时 | 24位密钥耗时 | 32位密钥耗时 |
---|---|---|---|
100M文本 | 744 | 879 | 620 |
从上面的测试可以看到AES的性能非常优秀,在服务器上的性能应该会更加优异。
2.2、SM4
2.2.1、 介绍
SM4是由中国国家密码管理局发布的对称加密密码算法,密钥长度固定为16位
2.2.2、 使用
这里直接使用hutool提供的工具类
package com.xx.utils;import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @author xx* @describe 国密* @since 2024/5/6 16:31*/
@Component
@Slf4j
public class SM4Utils {/*** sm4加密** @param text 待加密文本* @return 加密结果*/public static String encrypt(String key, String text) {SM4 sm4 = SmUtil.sm4(key.getBytes());return sm4.encryptHex(text);}/*** sm4解密** @param text 待解密文本* @return 解密结果*/public static String decrypt(String key, String text) {SM4 sm4 = SmUtil.sm4(key.getBytes());return sm4.decryptStr(text);}public static void main(String[] args) {String key = "wakpzJ8dCjWmHSdd";String text = "hello sm4";String encrypt = encrypt(key, text);String decrypt = decrypt(key, encrypt);System.out.println("encrypt:" + encrypt);System.out.println("decrypt:" + decrypt);}}
2.2.3、 性能测试
文本 | 循环10万次 | 循环100万次 | 执行一次 |
---|---|---|---|
18位数字 | 593 | 1806 | |
18位随机字符串 | 593 | 1878 | |
36位随机字符串 | 623 | 2217 | |
128位随机字符串 | 784 | 3564 | |
100M文件 | 2140 |
2.3、总结
AES提出的时间更早,所以其影响范围也更大,而SM4是由我国自主提出的加密算法,出现的更晚,知道的人也更少,但是其性能非常优异,从测试结果来看是AES的6到7倍。
三、非对称加密
非对称加密使用一组密钥对来进行加解密,公钥是公开的,任何人都可以使用公钥来加密信息。然而,只有拥有相应私钥的人才能解密这些信息。
非对称加密的主要优点是安全性高。因为即使攻击者获取到公钥,也无法解密加密的信息,除非他们能够获取到对应的私钥。这大大提高了数据的安全性。
3.1、RSA
3.1.1、 介绍
RSA是目前最为流行的非对称加密方式,常用于客户端向服务端提交敏感数据,或者于三方对接时保护数据安全。
3.1.2、 使用
这里依旧使用hutool工具包来操作
package com.xx.utils;import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;/*** @author xiaxing* @describe* @since 2024/7/5 16:26*/
public class RSAUtil {public static final String ENCRYPT_TYPE = "RSA";/*** 获取公钥的key*/private static final String PUBLIC_KEY = "RSAPublicKey";/*** 获取私钥的key*/private static final String PRIVATE_KEY = "RSAPrivateKey";public static Map<String, String> generateKeyPair() {try {KeyPair pair = SecureUtil.generateKeyPair(ENCRYPT_TYPE);PrivateKey privateKey = pair.getPrivate();PublicKey publicKey = pair.getPublic();// 获取 公钥和私钥 的 编码格式(通过该 编码格式 可以反过来 生成公钥和私钥对象)byte[] pubEncBytes = publicKey.getEncoded();byte[] priEncBytes = privateKey.getEncoded();// 把 公钥和私钥 的 编码格式 转换为 Base64文本 方便保存String pubEncBase64 = Base64.getEncoder().encodeToString(pubEncBytes);String priEncBase64 = Base64.getEncoder().encodeToString(priEncBytes);Map<String, String> map = new HashMap<String, String>(2);map.put(PUBLIC_KEY, pubEncBase64);map.put(PRIVATE_KEY, priEncBase64);return map;} catch (Exception e) {e.printStackTrace();}return null;}/*** 公钥加密** @param content 要加密的内容* @param publicKey 公钥*/public static String encrypt(String content, String publicKey) {try {RSA rsa = new RSA(null, publicKey);return rsa.encryptBase64(content, KeyType.PublicKey);} catch (Exception e) {e.printStackTrace();}return null;}/*** 私钥解密** @param content 要解密的内容* @param privateKey 私钥*/public static String decrypt(String content, String privateKey) {try {RSA rsa = new RSA(privateKey, null);return rsa.decryptStr(content, KeyType.PrivateKey);} catch (Exception e) {e.printStackTrace();}return null;}public static void main(String[] args) {Map<String, String> keyPair = generateKeyPair();String content = "hello rsa";String encrypt = encrypt(content, keyPair.get(PUBLIC_KEY));String decrypt = decrypt(encrypt, keyPair.get(PRIVATE_KEY));System.out.println("encrypt:" + encrypt);System.out.println("decrypt:" + decrypt);}
}
3.1.3、 性能测试
文本 | 循环10万次 | 循环100万次 |
---|---|---|
18位数字 | 5412 | 52093 |
18位随机字符串 | 5483 | 78604 |
36位随机字符串 | 5449 | 62476 |
128位随机字符串 | 7541 | 72679 |
3.2、SM2
3.2.1、 介绍
SM2是由中国国家密码管理局发布的非对称加密密码算法,其只支持公钥加密,私钥解密,不支持反转。
3.2.2、 使用
package com.xx.utils;import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;import java.security.KeyPair;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;/*** @author xiaxing* @describe* @since 2024/7/5 16:45*/
public class SM2Util {/*** 获取公钥的key*/private static final String PUBLIC_KEY = "SM2PublicKey";/*** 获取私钥的key*/private static final String PRIVATE_KEY = "SM2PrivateKey";public static Map<String, String> generateKeyPair() {KeyPair pair = SecureUtil.generateKeyPair("SM2");byte[] privateKey = pair.getPrivate().getEncoded();byte[] publicKey = pair.getPublic().getEncoded();Map<String, String> map = new HashMap<>(2);map.put(PUBLIC_KEY, Base64.getEncoder().encodeToString(publicKey));map.put(PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey));return map;}/*** sm4加密** @param text 待加密文本* @return 加密结果*/public static String encrypt(String key, String text) {SM2 sm2 = new SM2(null, key);return sm2.encryptBase64(text, KeyType.PublicKey);}/*** sm4解密** @param text 待解密文本* @return 解密结果*/public static String decrypt(String key, String text) {SM2 sm2 = new SM2(key, null);return sm2.decryptStr(text, KeyType.PrivateKey);}public static void main(String[] args) {String text = "hello sm2";Map<String, String> keyPair = generateKeyPair();String encrypt = encrypt(keyPair.get(PUBLIC_KEY), text);String decrypt = decrypt(keyPair.get(PRIVATE_KEY), encrypt);System.out.println(encrypt);System.out.println(decrypt);}
}
3.2.3、 性能测试
100万次的性能实在是太差了,这里不测评了
文本 | 循环10万次 |
---|---|
18位数字 | 45992 |
18位随机字符串 | 47378 |
36位随机字符串 | 48564 |
128位随机字符串 | 44770 |
3.3、总结
RSA和SM2都是最常用的非对称加密方式,常用于客户端和服务端交互,或者和一些不受信的三方交互。
四、摘要加密
摘要加密其加密的过程是不可逆的,一般被用于验证数据的完整性和一致性。
4.1、MD5
4.1.1、 介绍
MD5使用的范围非常广泛,一般的项目中用户密码都会采用MD5来进行加密。目前认为MD5已经不再是安全的,因为已经有多种方法可以快速生成MD5碰撞。但复杂的密码依旧是难以被暴力破解的
4.1.2、 使用
package com.xx.utils;import cn.hutool.crypto.digest.MD5;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.springframework.util.DigestUtils;import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;/*** @author xiaxing* @describe* @since 2024/7/8 15:01*/
public class MD5Util {public static String springMD5(String context) {return DigestUtils.md5DigestAsHex(context.getBytes(StandardCharsets.UTF_8));}public static String javaMD5(String context) throws NoSuchAlgorithmException {MessageDigest md5 = MessageDigest.getInstance("MD5");byte[] digest = md5.digest(context.getBytes(StandardCharsets.UTF_8));return ByteUtils.toHexString(digest);}public static String hutoolMD5(String context) {return MD5.create().digestHex(context);}public static void main(String[] args) throws NoSuchAlgorithmException {String context = "123456";System.out.println("spring:" + springMD5(context));System.out.println("java:" + javaMD5(context));System.out.println("hutool:" + hutoolMD5(context));}}
4.1.3、 性能测试
文本 | 循环10万次 | 循环100万次 |
---|---|---|
18位数字 | 466 | 1263 |
18位随机字符串 | 470 | 1288 |
36位随机字符串 | 484 | 1427 |
128位随机字符串 | 510 | 1627 |
4.2、SM3
4.2.1、 介绍
SM3是中国自主研发的密码哈希函数,它在密码学社区中也得到了广泛的认可。
4.2.2、 使用
package com.xx.utils;import cn.hutool.crypto.SmUtil;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.util.encoders.Hex;/*** @author xiaxing* @describe* @since 2024/7/8 15:22*/
public class SM3Util {public static String hutoolSM3(String context) {return SmUtil.sm3(context);}public static String bcprovSM3(String context) {SM3Digest sm3 = new SM3Digest();byte[] bytes = context.getBytes();sm3.update(bytes, 0, bytes.length);byte[] result = new byte[sm3.getDigestSize()];sm3.doFinal(result, 0);return Hex.toHexString(result);}public static void main(String[] args) {String context = "123456";System.out.println(hutoolSM3(context));System.out.println(bcprovSM3(context));}
}
4.2.3、 性能测试
文本 | 循环10万次 | 循环100万次 |
---|---|---|
18位数字 | 574 | 2195 |
18位随机字符串 | 616 | 2243 |
36位随机字符串 | 572 | 2262 |
128位随机字符串 | 683 | 3081 |
4.3、总结
MD5目前已经不再安全,但是依旧有一定破解难度,但是如果对密码安全性要求较高,可以使用SM3这种加密方式。