常见加密算法介绍

一、介绍

数据加密是项目中非常常见的业务需求,封装好的三方组件也非常多,自己在工作时也经常会使用到,这次对主流常用的几种加密方式做一个梳理,会分别介绍其使用场景,以及如何使用。

二、对称加密

加密或者解密都需要使用相同的密钥,这意味着只要密钥泄露,数据就可以被随意解密,所以这种加密方式不适用于于三方公司合作或者前后端交互中使用。

一般情况下,对称加密用于数据存储时进行加密,数据在需要使用的时候进行解密,避免在数据库中明文存储的问题。

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
  1. 循环10万次(单位:毫秒)
文本16位密钥耗时24位密钥耗时32位密钥耗时
18位数字359532933487
18位随机字符串370535583492
36位随机字符串377837353683
128位随机字符串414536513766
  1. 大文件测试(加密一次,单位:毫秒)
文本16位密钥耗时24位密钥耗时32位密钥耗时
100M文本744879620

从上面的测试可以看到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位数字5931806
18位随机字符串5931878
36位随机字符串6232217
128位随机字符串7843564
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位数字541252093
18位随机字符串548378604
36位随机字符串544962476
128位随机字符串754172679

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位数字4661263
18位随机字符串4701288
36位随机字符串4841427
128位随机字符串5101627

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位数字5742195
18位随机字符串6162243
36位随机字符串5722262
128位随机字符串6833081

4.3、总结

MD5目前已经不再安全,但是依旧有一定破解难度,但是如果对密码安全性要求较高,可以使用SM3这种加密方式。

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值