Base64, AES, RSA,SM2,SM4 对称加密、非对称加密

Base64, AES, RSA,SM2,SM4 对称加密、非对称加密

文章末尾附完整代码示例

加密技术概述

  • 对称加密
    • 使用同一把密钥进行加解密。代表算法:AES、SM4。
    • 优点:速度快、适合大数据量加密。
    • 缺点:密钥分发困难,易被截获后脱密。
  • 非对称加密
    • 使用公钥加密、私钥解密(或反之用于签名)。代表算法:RSA、SM2。
    • 优点:无需共享私钥,便于密钥交换与签名验签。
    • 缺点:速度慢,不适合直接加密大数据。
  • 典型应用场景与对比
    • 数据传输:对称加密加密数据,非对称加密加密对称密钥(混合加密)。
    • 数字签名:非对称加密(私钥签名、公钥验签)。
    • 批量文件/数据库加密:对称加密。
    • 密钥交换:非对称加密。

Base64编码原理

  • 编码规则与字符集
    • 将每 3 字节(24 位)拆成 4 组,每组 6 位,对应 64 个字符集(A–Z、a–z、0–9、+、/),末尾用 = 填充。
  • 用途
    • 传输兼容性:把二进制数据转为可打印文本,避免传输过程中的字符集问题。
    • 注意:Base64 不是加密,仅编码;常与加密算法的输出搭配使用(如密文 Base64 化)。

对称加密技术

AES(高级加密标准)

  • 算法特点
    • 分组密码。常用密钥长度:128/192/256 位(即 16/24/32 字节)。
  • 工作模式
    • 常见:ECB、CBC、GCM 等。
  • 应用场景
    • 文件/数据库加密、TLS(历史)、应用层数据保护。
  • 常见坑
    • 密钥长度不合法:需为 16/24/32 字节,否则抛出 IllegalArgumentException
    • 密文编码不匹配:Base64 与 Hex 不可混用。

SM4(国密算法)

  • 算法背景
    • 中国商用密码标准,分组长度与密钥长度均为 128 位。
  • 与 AES 对比
    • 安全强度与实现复杂度均相近,SM4在国内合规场景更常用。
  • 适用领域
    • 政务、金融、信创与国产化生态。

非对称加密技术

RSA 算法

  • 数学基础
    • 基于大整数分解困难问题。
  • 密钥生成
    • 随机生成大素数对 p、q,计算 n=pq 与 φ(n),选择 e 并求 d,使 ed≡1 (mod φ(n)),得到公钥(n,e)与私钥(n,d)。
  • 典型用途
    • 密钥交换(加密对称密钥)、数字签名(私钥签名、公钥验签)。

SM2(国密算法)

  • 基于 ECC
    • 椭圆曲线密码学(ECC),以较短密钥提供与 RSA 同等级别的安全性。
  • 对比 RSA
    • 通常在相同安全级别下具有更短密钥与更好性能。
  • 中国标准应用
    • 电子认证、金融、区块链、政务系统等。

安全实践建议

  • 混合加密方案
    • 用对称算法(AES/SM4)加密业务数据;用非对称算法(RSA/SM2)加密对称密钥;传输对称密钥密文与数据密文。
  • 国密合规性
    • 金融、政务、信创等场景优先选用 SM 系列算法(SM2/SM3/SM4)。
  • 密钥管理
    • 不硬编码到源码。
    • 使用 KMS/HSM/密钥托管服务或安全配置中心;按角色最小化访问权限;定期轮换与吊销;全链路加密与审计。
  • 模式与参数
    • 避免 ECB;优先 GCM(带认证)。妥善管理 IV/Nonce(随机、唯一、不可复用)。

性能与安全性权衡

  • 选择标准
    • 速度:AES/SM4 > SM2 ≈ ECC > RSA。
    • 安全强度:取决于密钥长度、模式、填充与实现。
    • 标准/合规:按行业规范(如国密)与国际标准(FIPS、NIST、ISO)。
  • 未来趋势:抗量子
    • 关注 NIST 甄选的 PQC(如 Kyber、Dilithium),并为“混合密钥协商”做技术储备。

示例代码

工具类(依赖hutool-crypto)

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-crypto</artifactId>
    <version>5.8.22</version>
</dependency>

Code

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;


public class EncryptUtils {
    /**
     * 公钥
     */
    public static final String PUBLIC_KEY = "publicKey";
    /**
     * 私钥
     */
    public static final String PRIVATE_KEY = "privateKey";

    /**
     * Base64加密
     *
     * @param data 待加密数据
     * @return 加密后字符串
     */
    public static String encryptByBase64(String data) {
        return Base64.encode(data, StandardCharsets.UTF_8);
    }

    /**
     * Base64解密
     *
     * @param data 待解密数据
     * @return 解密后字符串
     */
    public static String decryptByBase64(String data) {
        return Base64.decodeStr(data, StandardCharsets.UTF_8);
    }

    /**
     * AES加密
     *
     * @param data     待加密数据
     * @param password 秘钥字符串
     * @return 加密后字符串, 采用Base64编码
     */
    public static String encryptByAes(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("AES需要传入秘钥信息");
        }
        // aes算法的秘钥要求是16位、24位、32位
        int[] array = {16, 24, 32};
        if (!ArrayUtil.contains(array, password.length())) {
            throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
        }
        return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
    }

    /**
     * AES加密
     *
     * @param data     待加密数据
     * @param password 秘钥字符串
     * @return 加密后字符串, 采用Hex编码
     */
    public static String encryptByAesHex(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("AES需要传入秘钥信息");
        }
        // aes算法的秘钥要求是16位、24位、32位
        int[] array = {16, 24, 32};
        if (!ArrayUtil.contains(array, password.length())) {
            throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
        }
        return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
    }

    /**
     * AES解密
     *
     * @param data     待解密数据
     * @param password 秘钥字符串
     * @return 解密后字符串
     */
    public static String decryptByAes(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("AES需要传入秘钥信息");
        }
        // aes算法的秘钥要求是16位、24位、32位
        int[] array = {16, 24, 32};
        if (!ArrayUtil.contains(array, password.length())) {
            throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
        }
        return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
    }

    /**
     * sm4加密
     *
     * @param data     待加密数据
     * @param password 秘钥字符串
     * @return 加密后字符串, 采用Base64编码
     */
    public static String encryptBySm4(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("SM4需要传入秘钥信息");
        }
        // sm4算法的秘钥要求是16位长度
        int sm4PasswordLength = 16;
        if (sm4PasswordLength != password.length()) {
            throw new IllegalArgumentException("SM4秘钥长度要求为16位");
        }
        return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
    }

    /**
     * sm4加密
     *
     * @param data     待加密数据
     * @param password 秘钥字符串
     * @return 加密后字符串, 采用Base64编码
     */
    public static String encryptBySm4Hex(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("SM4需要传入秘钥信息");
        }
        // sm4算法的秘钥要求是16位长度
        int sm4PasswordLength = 16;
        if (sm4PasswordLength != password.length()) {
            throw new IllegalArgumentException("SM4秘钥长度要求为16位");
        }
        return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
    }

    /**
     * sm4解密
     *
     * @param data     待解密数据
     * @param password 秘钥字符串
     * @return 解密后字符串
     */
    public static String decryptBySm4(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("SM4需要传入秘钥信息");
        }
        // sm4算法的秘钥要求是16位长度
        int sm4PasswordLength = 16;
        if (sm4PasswordLength != password.length()) {
            throw new IllegalArgumentException("SM4秘钥长度要求为16位");
        }
        return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
    }

    /**
     * 产生sm2加解密需要的公钥和私钥
     *
     * @return 公私钥Map
     */
    public static Map<String, String> generateSm2Key() {
        Map<String, String> keyMap = new HashMap<>(2);
        SM2 sm2 = SmUtil.sm2();
        keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
        keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
        return keyMap;
    }

    /**
     * sm2公钥加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return 加密后字符串, 采用Base64编码
     */
    public static String encryptBySm2(String data, String publicKey) {
        if (StrUtil.isBlank(publicKey)) {
            throw new IllegalArgumentException("SM2需要传入公钥进行加密");
        }
        SM2 sm2 = SmUtil.sm2(null, publicKey);
        return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
    }

    /**
     * sm2公钥加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return 加密后字符串, 采用Hex编码
     */
    public static String encryptBySm2Hex(String data, String publicKey) {
        if (StrUtil.isBlank(publicKey)) {
            throw new IllegalArgumentException("SM2需要传入公钥进行加密");
        }
        SM2 sm2 = SmUtil.sm2(null, publicKey);
        return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
    }

    /**
     * sm2私钥解密
     *
     * @param data       待加密数据
     * @param privateKey 私钥
     * @return 解密后字符串
     */
    public static String decryptBySm2(String data, String privateKey) {
        if (StrUtil.isBlank(privateKey)) {
            throw new IllegalArgumentException("SM2需要传入私钥进行解密");
        }
        SM2 sm2 = SmUtil.sm2(privateKey, null);
        return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
    }

    /**
     * 产生RSA加解密需要的公钥和私钥
     *
     * @return 公私钥Map
     */
    public static Map<String, String> generateRsaKey() {
        Map<String, String> keyMap = new HashMap<>(2);
        RSA rsa = SecureUtil.rsa();
        keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
        keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
        return keyMap;
    }

    /**
     * rsa公钥加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return 加密后字符串, 采用Base64编码
     */
    public static String encryptByRsa(String data, String publicKey) {
        if (StrUtil.isBlank(publicKey)) {
            throw new IllegalArgumentException("RSA需要传入公钥进行加密");
        }
        RSA rsa = SecureUtil.rsa(null, publicKey);
        return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
    }

    /**
     * rsa公钥加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return 加密后字符串, 采用Hex编码
     */
    public static String encryptByRsaHex(String data, String publicKey) {
        if (StrUtil.isBlank(publicKey)) {
            throw new IllegalArgumentException("RSA需要传入公钥进行加密");
        }
        RSA rsa = SecureUtil.rsa(null, publicKey);
        return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
    }

    /**
     * rsa私钥解密
     *
     * @param data       待加密数据
     * @param privateKey 私钥
     * @return 解密后字符串
     */
    public static String decryptByRsa(String data, String privateKey) {
        if (StrUtil.isBlank(privateKey)) {
            throw new IllegalArgumentException("RSA需要传入私钥进行解密");
        }
        RSA rsa = SecureUtil.rsa(privateKey, null);
        return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
    }
}

测试类

public static void main(String[] args) {
        // base64加密解密
        String originalText = "hello";
        System.out.println("原文: " + originalText);
        String s = EncryptUtils.encryptByBase64(originalText);
        System.out.println("base64 密文: " + s);
        System.out.println("base64 解密: " + EncryptUtils.decryptByBase64(s));
        System.out.println("==========\n");

        // aes 对称加密解密
        System.out.println("原文: " + originalText);
        String s1 = EncryptUtils.encryptByAes(originalText, "1234567890123456");
        String s2 = EncryptUtils.encryptByAesHex(originalText, "1234567890123456");
        System.out.println("aes密文: " + s1);
        System.out.println("aes hex密文: " + s2);
        System.out.println("aes 解密: " + EncryptUtils.decryptByAes(s1, "1234567890123456"));
        System.out.println("aes hex 解密: " + EncryptUtils.decryptByAes(s2, "1234567890123456"));
        System.out.println("==========\n");

        // sm4 对称加密解密
        System.out.println("原文: " + originalText);
        String s3 = EncryptUtils.encryptBySm4(originalText, "1234567890123456");
        String s4 = EncryptUtils.encryptBySm4Hex(originalText, "1234567890123456");
        System.out.println("sm4密文: " + s3);
        System.out.println("sm4 hex密文: " + s4);
        System.out.println("sm4 解密: " + EncryptUtils.decryptBySm4(s3, "1234567890123456"));
        System.out.println("sm4 hex 解密: " + EncryptUtils.decryptBySm4(s4, "1234567890123456"));
        System.out.println("==========\n");

        // sm2 非对称加密解密
        System.out.println("原文: " + originalText);
        Map<String, String> sm2Key = EncryptUtils.generateSm2Key();
        String s5 = EncryptUtils.encryptBySm2(originalText, sm2Key.get(EncryptUtils.PUBLIC_KEY));
        String s6 = EncryptUtils.encryptBySm2Hex(originalText, sm2Key.get(EncryptUtils.PUBLIC_KEY));
        System.out.println("sm2密文: " + s5);
        System.out.println("sm2 hex密文: " + s6);
        System.out.println("sm2 解密: " + EncryptUtils.decryptBySm2(s5, sm2Key.get(EncryptUtils.PRIVATE_KEY)));
        System.out.println("sm2 hex 解密: " + EncryptUtils.decryptBySm2(s6, sm2Key.get(EncryptUtils.PRIVATE_KEY)));
        System.out.println("==========\n");

        // rsa 非对称加密解密
        System.out.println("原文: " + originalText);
        Map<String, String> rsaKey = EncryptUtils.generateRsaKey();
        String s7 = EncryptUtils.encryptByRsa(originalText, rsaKey.get(EncryptUtils.PUBLIC_KEY));
        String s8 = EncryptUtils.encryptByRsaHex(originalText, rsaKey.get(EncryptUtils.PUBLIC_KEY));
        System.out.println("rsa密文: " + s7);
        System.out.println("rsa hex密文: " + s8);
        System.out.println("rsa 解密: " + EncryptUtils.decryptByRsa(s7, rsaKey.get(EncryptUtils.PRIVATE_KEY)));
        System.out.println("rsa hex 解密: " + EncryptUtils.decryptByRsa(s8, rsaKey.get(EncryptUtils.PRIVATE_KEY)));
        System.out.println("==========");
    }

结果

示例结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值