Java 对称加密与非对称加密:区别、用途及工具类实战

在Java开发中,加密是保障数据安全的核心手段,常用的加密方式主要分为对称加密非对称加密。很多开发者刚接触时会混淆两者的用法,这篇文章就用通俗的语言讲清它们的区别、适用场景,再附上可直接复用的工具类,新手也能快速上手。
加密算法工具类

一、核心区别:一张表看懂

对比维度对称加密非对称加密
密钥特点加密和解密用同一把密钥(密钥需严格保密)公钥+私钥成对工作(公钥可公开,私钥必须保密)
加密效率速度极快(适合大数据量加密)速度较慢(数学运算复杂,适合小数据量加密)
安全性安全性依赖密钥保管(密钥泄露则数据全暴露)安全性更高(私钥不对外传输,仅需保管好私钥)
密钥分发密钥分发困难(需安全通道传输,否则易被拦截)密钥分发简单(公钥可公开传播,无需保密)
典型算法AES、DES、3DESRSA、DSA、ECC

二、各自用途:什么时候用哪种?

1. 对称加密:适合“大数据量加密”场景

对称加密的核心优势是,所以主要用于对大量数据进行加密,比如:

  • 数据库中敏感字段加密(如用户手机号、身份证号);
  • 文件传输加密(如上传/下载的Excel、PDF文件);
  • 接口请求体加密(如APP向后端传输的用户行为数据)。

举个实际开发场景:用户在APP上提交实名认证信息(姓名+身份证号),后端接收后需要加密存储到MySQL。此时用AES对称加密,既能保证数据安全,又不会因为加密耗时影响接口响应速度。

2. 非对称加密:适合“密钥交换/数字签名”场景

非对称加密的核心优势是安全且密钥易分发,但速度慢,所以主要用于:

  • 密钥交换(如对称加密的密钥,通过非对称加密传输);
  • 数字签名(如接口防篡改、数据来源认证);
  • 小数据加密(如加密用户登录密码的验证码)。

举个实际开发场景:APP与后端首次建立连接时,需要协商一个对称加密的密钥(用于后续数据传输加密)。此时后端生成RSA公钥和私钥,将公钥传给APP,APP用公钥加密自己生成的AES密钥,后端用私钥解密得到AES密钥,后续就用AES加密传输数据——既解决了对称密钥的安全分发问题,又保证了传输效率。

三、Java工具类实战(可直接复制使用)

1. 对称加密工具类(AES算法)

AES是目前最常用的对称加密算法,安全性高、效率快,推荐优先使用。以下工具类支持AES-128/256加密,包含加密、解密、生成密钥功能:

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

/**
 * AES对称加密工具类(可直接复用)
 * 说明:128位密钥无需额外配置,256位密钥需安装JCE无限制权限文件
 */
public class AesEncryptUtil {

    // 加密算法:AES,模式:ECB,填充方式:PKCS5Padding(实际开发推荐用CBC模式,需加偏移量)
    private static final String ALGORITHM = "AES/ECB/PKCS5Padding";
    private static final String KEY_ALGORITHM = "AES";
    private static final int KEY_SIZE = 128; // 密钥长度:128/256

    /**
     * 生成AES密钥(返回Base64编码后的密钥字符串,方便存储)
     */
    public static String generateKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
        keyGenerator.init(KEY_SIZE);
        SecretKey secretKey = keyGenerator.generateKey();
        return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    }

    /**
     * 加密:明文 -> Base64编码的密文
     * @param plainText 明文(待加密的字符串)
     * @param keyStr Base64编码后的密钥
     */
    public static String encrypt(String plainText, String keyStr) throws Exception {
        // 1. 解码密钥
        byte[] keyBytes = Base64.getDecoder().decode(keyStr);
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
        // 2. 初始化加密器
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        // 3. 加密并返回Base64编码的密文
        byte[] encryptBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(encryptBytes);
    }

    /**
     * 解密:Base64编码的密文 -> 明文
     * @param cipherText Base64编码后的密文
     * @param keyStr Base64编码后的密钥
     */
    public static String decrypt(String cipherText, String keyStr) throws Exception {
        // 1. 解码密钥和密文
        byte[] keyBytes = Base64.getDecoder().decode(keyStr);
        byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
        // 2. 初始化解密器
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        // 3. 解密并返回明文
        byte[] decryptBytes = cipher.doFinal(cipherBytes);
        return new String(decryptBytes, "UTF-8");
    }

    // 测试方法
    public static void main(String[] args) throws Exception {
        // 1. 生成密钥(实际开发中密钥需妥善保管,如配置在Nacos、Vault中)
        String key = generateKey();
        System.out.println("AES密钥(Base64编码):" + key);

        // 2. 加密
        String plainText = "用户身份证号:110101199001011234";
        String cipherText = encrypt(plainText, key);
        System.out.println("加密后的密文:" + cipherText);

        // 3. 解密
        String decryptText = decrypt(cipherText, key);
        System.out.println("解密后的明文:" + decryptText);
    }
}

2. 非对称加密工具类(RSA算法)

RSA是最常用的非对称加密算法,适合密钥交换和数字签名。以下工具类包含密钥对生成、公钥加密、私钥解密、私钥签名、公钥验签功能:

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * RSA非对称加密工具类(可直接复用)
 * 说明:密钥长度推荐2048位(安全性足够),4096位加密速度较慢
 */
public class RsaEncryptUtil {

    private static final String ALGORITHM = "RSA";
    private static final int KEY_SIZE = 2048; // 密钥长度:2048/4096
    // 签名算法:SHA256withRSA(SHA256哈希+RSA签名,安全性更高)
    private static final String SIGN_ALGORITHM = "SHA256withRSA";

    /**
     * 生成RSA密钥对(公钥+私钥),返回Base64编码后的字符串
     * @return 数组:[0]公钥,[1]私钥
     */
    public static String[] generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 公钥编码为Base64字符串
        String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
        // 私钥编码为Base64字符串
        String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());

        return new String[]{publicKey, privateKey};
    }

    /**
     * 公钥加密(适合加密小数据,如AES密钥)
     * @param plainText 明文(待加密的字符串)
     * @param publicKeyStr Base64编码后的公钥
     */
    public static String encryptByPublicKey(String plainText, String publicKeyStr) throws Exception {
        // 1. 解码公钥
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);

        // 2. 初始化加密器
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        // 3. 加密(RSA加密有长度限制,需分段加密,这里简化处理短字符串)
        byte[] encryptBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(encryptBytes);
    }

    /**
     * 私钥解密
     * @param cipherText Base64编码后的密文
     * @param privateKeyStr Base64编码后的私钥
     */
    public static String decryptByPrivateKey(String cipherText, String privateKeyStr) throws Exception {
        // 1. 解码私钥
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        // 2. 初始化解密器
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        // 3. 解密
        byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
        byte[] decryptBytes = cipher.doFinal(cipherBytes);
        return new String(decryptBytes, "UTF-8");
    }

    /**
     * 私钥签名(用于数据防篡改、来源认证)
     * @param data 待签名的数据
     * @param privateKeyStr Base64编码后的私钥
     */
    public static String sign(String data, String privateKeyStr) throws Exception {
        // 1. 解码私钥
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        // 2. 生成签名
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data.getBytes("UTF-8"));
        byte[] signBytes = signature.sign();
        return Base64.getEncoder().encodeToString(signBytes);
    }

    /**
     * 公钥验签(验证数据是否被篡改、签名是否有效)
     * @param data 原始数据
     * @param sign 签名值(Base64编码)
     * @param publicKeyStr Base64编码后的公钥
     */
    public static boolean verifySign(String data, String sign, String publicKeyStr) throws Exception {
        // 1. 解码公钥
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);

        // 2. 验证签名
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(data.getBytes("UTF-8"));
        return signature.verify(Base64.getDecoder().decode(sign));
    }

    // 测试方法
    public static void main(String[] args) throws Exception {
        // 1. 生成密钥对
        String[] keyPair = generateKeyPair();
        String publicKey = keyPair[0];
        String privateKey = keyPair[1];
        System.out.println("RSA公钥(Base64编码):" + publicKey);
        System.out.println("RSA私钥(Base64编码):" + privateKey);

        // 2. 加密解密(加密AES密钥示例)
        String aesKey = "1234567890abcdef"; // 假设生成的AES密钥
        String encryptAesKey = encryptByPublicKey(aesKey, publicKey);
        System.out.println("公钥加密后的AES密钥:" + encryptAesKey);
        String decryptAesKey = decryptByPrivateKey(encryptAesKey, privateKey);
        System.out.println("私钥解密后的AES密钥:" + decryptAesKey);

        // 3. 签名验签
        String data = "接口返回数据:{\"code\":200,\"msg\":\"success\"}";
        String sign = sign(data, privateKey);
        System.out.println("私钥签名值:" + sign);
        boolean verifyResult = verifySign(data, sign, publicKey);
        System.out.println("公钥验签结果:" + verifyResult); // true表示验签通过
    }
}

四、开发注意事项

  1. 密钥保管:对称加密的密钥和非对称加密的私钥,不能硬编码在代码里,建议存储在配置中心(如Nacos)或密钥管理工具(如Vault);
  2. 算法选择:对称加密优先用AES(避免用DES,安全性较低),非对称加密优先用RSA-2048(兼顾安全性和效率);
  3. 数据长度:RSA加密有长度限制(如2048位密钥最多加密245字节),加密大数据需分段,或用“非对称加密+对称加密”组合方案;
  4. 填充方式:对称加密推荐用PKCS5Padding(兼容性好),非对称加密无需手动指定填充方式(Java默认实现已优化)。
    加密算法工具类
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值