java中使用RSA非对称加解密

2024-04-17 200点热度 0人点赞 0条评论

RSA加密算法是一种非对称加密算法
RSA是一对密钥。分别是公钥和私钥,这个公钥和私钥其实就是一组数字!
其二进制位长度可以是1024位或者2048位.长度越长其加密强度越大,
目前为止公之于众的能破解的最大长度为768位密钥,只要高于768位,相对就比较安全.

RSA加密的方式

  • 使用公钥加密的数据,利用私钥进行解密
  • 使用私钥加密的数据,利用公钥进行解密

算法优缺点

优点

  • 不需要进行密钥传递,提高了安全性
  • 可以进行数字签名认证

缺点

  • 加密解密效率不高,一般只适用于处理小量数据(如:密钥)
  • 容易遭受小指数攻击

代码 demo


import javax.crypto.Cipher;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RSAUtils {

    /**
     * 512、1024、2048
     */
    private static final int DEFAULT_KEY_SIZE = 2048;

    public static String RSA_ALGORITHM = "RSA";

    /**
     * 从文件中读取公钥
     *
     * @param file 公钥保存路径,获取的file
     * @return PublicKey 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(File file) throws Exception {
        return getPublicKey(readFile(file));
    }

    /**
     * 从文件中读取密钥
     *
     * @param file 私钥保存路径,获取的file
     * @return PrivateKey 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(File file) throws Exception {
        return getPrivateKey(readFile(file));
    }

    /**
     * 获取公钥
     * 公钥的字节形式。
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    private static PublicKey getPublicKey(byte[] bytes) throws Exception {
        //todo 打印调试
        System.out.println("公钥:" + new String(bytes));

        bytes = Base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //todo 打印调试
        System.out.println("私钥:" + new String(bytes));

        bytes = Base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     * ⚠️: 该方法是生成本地的 rsa 文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(new File(publicKeyFilename), publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(new File(privateKeyFilename), privateKeyBytes);
    }

    private static byte[] readFile(File file) throws Exception {
        return Files.readAllBytes(file.toPath());
    }

    private static void writeFile(File dest, byte[] bytes) throws IOException {
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }

    /**
     * 私钥加密
     *
     * @param data       待加密数据
     * @param privateKey 密钥
     * @return byte[] 加密数据
     */
    public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 私钥解密
     *
     * @param data       待解密数据
     * @param privateKey 密钥
     * @return byte[] 解密数据
     */
    public static byte[] decryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        //数据解密
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥加密
     *
     * @param data
     * @param publicKey
     * @return
     * @throws Exception
     */
    private static byte[] encryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥解密
     *
     * @param data   待解密数据
     * @param pubKey 密钥
     * @return byte[] 解密数据
     */
    public static byte[] decryptByPublicKey(byte[] data, PublicKey pubKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, pubKey);
        return cipher.doFinal(data);
    }

    public static void main(String[] args) throws Exception {
        //1. 生成本次测试的公钥私钥。(文件已经存在则可以跳过此步骤)
//        generateKey("./test_pub",
//                "./test_private",
//                "yourSecret",
//                2048);

        //获取 publicKey;
        PublicKey publicKey = getPublicKey(new File("./test_pub"));
        //如果密钥文件放在 resource目录下.(文件已经存在)参考 https://mylomen.com/archives/141
        //PublicKey publicKey = getPublicKey(ResourceFileUtils.getConfigLocation("classpath:test_pub").getFile());
        //获取 privateKey
        PrivateKey privateKey = getPrivateKey(new File("./test_private"));

        String password = "123456";
        //加密
        byte[] encryptByPublicKey = encryptByPublicKey(password.getBytes(), publicKey);
        System.out.println("使用公钥加密后的数据:" + new String(Base64.getEncoder().encode(encryptByPublicKey)));
        //解密
        byte[] decryptByPrivateKey2 = decryptByPrivateKey(encryptByPublicKey, privateKey);
        System.out.println("使用私钥解密后的数据:" + new String(decryptByPrivateKey2));

    }
}

参考

mylomen

本人从事 JAVA 开发10多年,将之前整理的笔记分享出来,希望能够帮助到努力的你。

文章评论