Java使用RSA加密解密

Java使用RSA加密解密

简介

​ 项目需求中要求前端传输的隐私信息需要进行加密传参,后台需要接收加密参数进行解密,所以查找资料学习如何解密。在过程中学到了相关知识,在这里记录下学习笔记。话不多说上代码。

package com.gxwljs.common.utils.sign;

import com.gxwljs.common.enums.CptError;
import com.gxwljs.common.exception.ServiceException;
import org.apache.commons.codec.binary.Base64;
import org.apache.poi.util.IOUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA 加密工具
 *
 * @author CPT
 * @date 2023/4/10 11:32
 */
public class RSAEncryptUtil {

    public static final String RSA_ALGORITHM = "RSA";

    /**
     * 生成公钥密钥
     *
     * @author CPT
     * @date 2023/4/10 16:15
     * @param keySize 密钥长度
     * @return 结果
     */
    public static Map<String, String> createKeys(int keySize) {
        try {
         	// 安全密钥生成器
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            // 初始化密钥长度 长度一般是2的n次方 
            // 需要注意的是,使用更长的密钥长度会增加加密和解密的计算量。
            // 同时,密钥长度也会影响密文的大小。
            // 例如,2048位的RSA密钥可以加密的最大数据块大小为245字节,而3072位的RSA密钥可以加密的最大数据块大小为371字节。
			// 因此,在选择RSA密钥长度时,需要综合考虑安全性、性能和应用需求等因素,并选择合适的密钥长度。
            keyPairGenerator.initialize(keySize);
			// 生成密钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            PublicKey publicKey = keyPair.getPublic();
            // 将字节数组进行URL安全的Base64编码
            String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
            PrivateKey privateKey = keyPair.getPrivate();
            String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
			// 返回数据
            Map<String, String> result = new HashMap<>();
            result.put("publicKey", publicKeyStr);
            result.put("privateKey" ,privateKeyStr);
            return result;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new ServiceException("No such algorithm -->[" + RSA_ALGORITHM + "]");
        }
    }

    /**
     * 获取公钥
     *
     * @author CPT
     * @date 2023/4/10 14:05
     * @param publicKey 公钥
     * @return 公钥
     */
    public static RSAPublicKey getPublicKey(String publicKey) {
        try {
           	// 获取密钥工厂实例
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            // 将公钥转换为一种标准格式
            // 因为我们创建密钥的时候是进行URL安全的Base64编码,所以在使用时需要解析原本的内容
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
            // 将上面的密钥规范转换成公钥对象,并返回
            return (RSAPublicKey) keyFactory.generatePublic(x509EncodedKeySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
            throw new ServiceException("Get public key error!");
        }
    }

    /**
     * 获取私钥
     *
     * @author CPT
     * @date 2023/4/10 14:05
     * @param privateKey 私钥
     * @return 私钥
     */
    public static RSAPrivateKey getPrivateKey(String privateKey) {
        try {
            // 获取密钥工厂实例
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            // 将私钥转换为一种标准格式
            // 因为我们拿到密钥后进行URL安全的Base64编码,所以在使用时需要解析原本的内容
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
            // 将上面的密钥规范转换成私钥对象,并返回
            return (RSAPrivateKey) keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
            throw new ServiceException("Get private key error!");
        }
    }

    /**
     * 加密
     *
     * @author CPT
     * @date 2023/4/10 14:45
     * @param text 明文
     * @return 密文
     */
    public static String encrypt(String text, RSAPublicKey publicKey) {
        try {
            // 获取一个Cipher对象,该对象可以执行各种加密和解密操作
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            // 第一个参数决定执行加密操作
            // 第二参数使用公钥执行
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // 将字节数组进行URL安全的Base64编码,并返回
            return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, text.getBytes(StandardCharsets.UTF_8),
                    publicKey.getModulus().bitLength()));

        } catch (NoSuchPaddingException | InvalidKeyException | NoSuchAlgorithmException  e) {
            throw new ServiceException(CptError.ENCRYPT_ERROR.getMsg(), CptError.ENCRYPT_ERROR.getCode());
        }
    }

    /**
     * 解密
     *
     * @author CPT
     * @date 2023/4/10 15:33
     * @param text 密文
     * @return 明文
     */
    public static String decrypt(String text, RSAPrivateKey privateKey) {
        try {
            // 获取一个Cipher对象,该对象可以执行各种加密和解密操作
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            // 第一个参数决定执行解密操作
            // 第二参数使用私钥执行
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            // 将解密出来的内容转成字符串
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, text.getBytes(StandardCharsets.UTF_8),
                    privateKey.getModulus().bitLength()));

        } catch (NoSuchPaddingException | InvalidKeyException | NoSuchAlgorithmException  e) {
            throw new ServiceException(CptError.DECRYPT_ERROR.getMsg(), CptError.DECRYPT_ERROR.getCode());
        }
    }

    /**
     * 分割加密/解密
     *
     * @author CPT
     * @date 2023/4/10 15:29
     * @param cipher 加密器,可以是加密模式或解密模式。
     * @param opMode 操作模式,可以是加密模式或解密模式。
     * @param data 待加密或解密的数据。
     * @param keySize 密钥的长度。
     * @return 加密后密文
     */
    private static byte[] rsaSplitCodec(Cipher cipher, int opMode, byte[] data, int keySize) {
        // 计算最大分块大小:如果是解密模式,则需要先对数据进行Base64解码;根据操作模式和密钥长度计算出最大分块大小。
        int maxBlock = 0;
        if (opMode == Cipher.DECRYPT_MODE) {
            data = Base64.decodeBase64(data);
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }
        // 初始化输出流和偏移量:准备一个字节数组输出流和偏移量变量。
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try {
            // 分块处理数据:使用cipher对数据进行分块加密或解密,分块大小为最大分块大小;将每个分块写入输出流。
            while (data.length > offSet) {
                if (data.length - offSet > maxBlock) {
                    buff = cipher.doFinal(data, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(data, offSet, data.length - offSet);
                }
                baos.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            e.printStackTrace();
            throw new RuntimeException("encrypt/decrypt error!");
        }
        // 返回处理后的数据:将输出流中的数据转换为字节数组并返回。
        byte[] resultData = baos.toByteArray();
        IOUtils.closeQuietly(baos);

        return resultData;
    }
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
RSA算法是一种非对称加密算法,它可以用于加密数据和数字签名。在Java中,可以使用Java Cryptography Architecture(JCA)提供的API来实现RSA加密和解密。下面是简单的示例代码: ```java import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; public class RSAEncryption { public static void main(String[] args) throws Exception { String plainText = "Hello World"; // 生成RSA公钥和私钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048, new SecureRandom()); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); // 使用公钥加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] cipherText = cipher.doFinal(plainText.getBytes()); // 使用私钥解密 cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedText = cipher.doFinal(cipherText); System.out.println("Plain Text: " + plainText); System.out.println("Encrypted Text: " + new String(cipherText)); System.out.println("Decrypted Text: " + new String(decryptedText)); } } ``` 上面的代码先生成了一个2048位的RSA公钥和私钥,然后使用公钥加密了一个字符串,再使用私钥解密获得原始字符串。在实际使用中,我们通常需要将公钥和私钥保存在文件中,以便在不同的应用程序中使用。可以使用PKCS8EncodedKeySpec和X509EncodedKeySpec类来将公钥和私钥转换为字节数组,以便在文件中保存。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值