java实践RSA+AES数据加密

说明

原理部分: 略
本文只关心实践。主要流程为:

  1. 生成AES Key:每次传输新生成一个或者一直用同一个。
  2. 生成RSA 公钥: 自己留着
  3. 生成RSA 私钥: 给到接收方,叫他藏起来
  4. 使用AES Key 对数据加密
  5. 使用RSA公钥对 AES Key加密
  6. 把加密后的数据/AES Key发送给接收方
  7. 接收方使用RSA 私钥把 AES Key 解密
  8. 接收方使用解密后的AES Key把数据解密
  9. 接收方over

注:本文使用的AES模式比较简单,可以使用更复杂的方式。不过有RSA的条件下也基本没问题了。不然连RSA私钥都被别人拿到了,AES也不可能再安全了。

依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.78.1</version>
</dependency>

代码

import static org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;

import lombok.SneakyThrows;

import org.apache.commons.lang3.tuple.Pair;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * @author siyang
 * @date 2024/4/29 11:29
 * @description 加密工具类
 */
public class EncryptUtils {
    /*
      将 BouncyCastleProvider 添加到Java的安全提供者列表中,之后就可以通过 "BC" 引用它来获取或实例化加密算法等组件
      如 Cipher.getInstance("AES/ECB/PKCS7Padding", "BC")
    */
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String AES_ALGORITHM = "AES";
    private static final String RSA_ALGORITHM = "RSA";
    private static final String AES_ALGORITHM_MODEL = "AES/ECB/PKCS5Padding";

    /* ============================== AES: 加密业务数据 ============================== */

    /**
     * 获取Aes密钥。
     *
     * @param keySize 密钥长度,可选值:128, 192;理论可填写256,由于返回时会对字节进行base64编码,无形中扩大了字节数,所以实际选择128、192
     * @return base64格式密钥
     */
    @SneakyThrows
    public static String createAesKey(int keySize) {
        KeyGenerator keygen = KeyGenerator.getInstance(AES_ALGORITHM);
        keygen.init(keySize);
        SecretKey secretKey = keygen.generateKey();
        byte[] byteKey = secretKey.getEncoded();
        return Base64.getEncoder().encodeToString(byteKey);
    }

    /**
     * AES 加密
     *
     * @param content 需要加密的内容
     * @param key aes密钥
     * @return 加密后的密文
     */
    @SneakyThrows
    public static String aesEncrypt(String content, String key) {
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM_MODEL, PROVIDER_NAME);
        SecretKeySpec secretKeySpec =
                new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

        byte[] encryptedBytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * AES 解密
     *
     * @param encryptedContent 已加密的密文
     * @param key aes密钥,必须与加密时使用的密钥一致
     * @return 解密后的内容
     */
    @SneakyThrows
    public static String aesDecrypt(String encryptedContent, String key) {
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM_MODEL, PROVIDER_NAME);
        SecretKeySpec secretKeySpec =
                new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);

        byte[] decodedBytes =
                Base64.getDecoder().decode(encryptedContent.getBytes(StandardCharsets.UTF_8));
        byte[] decryptedBytes = cipher.doFinal(decodedBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    /* ============================== RSA: 加密AES密钥 ============================== */

    /**
     * 生成rsa密钥对
     *
     * @param keySize 密钥长度
     * @return 密钥对:left:公钥; right:私钥
     */
    @SneakyThrows
    public static Pair<String, String> createPemRsaKeyPair(int keySize) {
        String pemPublicKeyFormat = "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----";
        String pemPrivateKeyFormat = "-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----";
        KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGORITHM, PROVIDER_NAME);
        generator.initialize(keySize);
        KeyPair kp = generator.generateKeyPair();
        String publicKey =
                Base64.getEncoder()
                        .encodeToString(kp.getPublic().getEncoded())
                        .replaceAll("(.{64})", "$1\n");
        String privateKey =
                Base64.getEncoder()
                        .encodeToString(kp.getPrivate().getEncoded())
                        .replaceAll("(.{64})", "$1\n");
        return Pair.of(
                String.format(pemPublicKeyFormat, publicKey),
                String.format(pemPrivateKeyFormat, privateKey));
    }

    /**
     * 使用公钥加密数据
     *
     * @param pemPublicKey pem格式公钥
     * @param content 待加密的数据
     * @return 加密后的数据Base64编码字符串
     */
    @SneakyThrows
    public static String rsaEncrypt(String content, String pemPublicKey) {
        byte[] publicKeyBytes = parsePemKey(pemPublicKey);
        KeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM, PROVIDER_NAME);
        PublicKey key = keyFactory.generatePublic(keySpec);

        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, key);

        byte[] encryptedBytes = cipher.doFinal(content.getBytes());
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * 使用私钥解密数据
     *
     * @param pemPrivateKey pem格式私钥
     * @param encryptedContent 加密后的数据Base64编码字符串
     * @return 原始数据字符串
     */
    @SneakyThrows
    public static String rsaDecrypt(String encryptedContent, String pemPrivateKey) {
        byte[] privateKeyBytes = parsePemKey(pemPrivateKey);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM, PROVIDER_NAME);
        PrivateKey key = keyFactory.generatePrivate(keySpec);

        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, key);

        byte[] encryptedBytes = Base64.getDecoder().decode(encryptedContent);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    @SneakyThrows
    public static byte[] parsePemKey(String pemKey) {
        PemReader pemReader = new PemReader(new StringReader(pemKey));
        PemObject pemObject = pemReader.readPemObject();
        pemReader.close();
        return pemObject.getContent();
    }

    public static void main(String[] args) {
        System.out.println("================准备各类密钥===================");
        String aesKey = createAesKey(192);
        System.out.println("AES key: " + aesKey);
        Pair<String, String> rsaKeyPair = createPemRsaKeyPair(1024);
        String rsaPublicKey = rsaKeyPair.getLeft();
        String rsaPrivateKey = rsaKeyPair.getRight();
        System.out.println("RSA public key: \n" + rsaPublicKey);
        System.out.println();
        System.out.println("RSA private key: \n" + rsaPrivateKey);

        System.out.println("\n================准备待传输(加密)数据===================");
        String data = "hello word! hello lrd!";
        System.out.println("待加密数据: " + data);

        System.out.println("\n================使用AES key对数据进行加密===================");
        String encryptedText = aesEncrypt(data, aesKey);
        System.out.println("AES 加密后的数据: " + encryptedText);

        System.out.println("\n================使用RSA公钥对AES key进行加密===================");
        String encryptAesKey = rsaEncrypt(aesKey, rsaPublicKey);
        System.out.println("加密后的AES key: " + encryptAesKey);

        System.out.println("\n================使用RSA私钥对AES key进行解密===================");
        String decryptAesKey = rsaDecrypt(encryptAesKey, rsaPrivateKey);
        System.out.println("解密后的AES key: " + decryptAesKey);

        System.out.println("\n================使用解密后的AES key进行数据解密===================");
        String decryptedText = aesDecrypt(encryptedText, decryptAesKey);
        System.out.println("解密后的数据: " + decryptedText);
    }
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值