【Java】全网最详细的非对称加密RSA详解

前言

  RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
  RSA算法主要用于加密少量数据(如对称密钥)和生成数字签名,RSA 加密算法是一种非对称加密算法,它使用一对密钥——公钥和私钥。公钥用于加密数据,而私钥用于解密数据。这种机制使得RSA非常适合用于安全通信,特别是当双方之前没有共享秘密的情况下。

RSA原理

  RSA公开密钥密码体制的原理是:根据数论,寻求两个大素数比较简单,而将它们的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥

安全性

  RSA的安全性依赖于大数分解,但是否等同于大数分解一直未能得到理论上的证明,也并没有从理论上证明破译。RSA的难度与大数分解难度等价。因为没有证明破解RSA就一定需要做大数分解。假设存在一种无须分解大数的算法,那它肯定可以修改成为大数分解算法,即RSA的重大缺陷是无法从理论上把握它的保密性能如何,而且密码学界多数人士倾向于因子分解不是NPC问题 。
  目前,RSA的一些变种算法已被证明等价于大数分解。不管怎样,分解n是最显然的攻击方法。现在,人们已能分解140多个十进制位的大素数。因此,模数n必须选大些,视具体适用情况而定 。
  RSA算法的保密强度随其密钥的长度增加而增强。但是,密钥越长,其加解密所耗用的时间也越长。因此,要根据所保护信息的敏感程度与攻击者破解所要花费的代价值不值得以及系统所要求的反应时间来综合考虑,尤其对于商业信息领域更是如此

运算速度

  由于进行的都是大数计算,使得RSA最快的情况也比DES慢上好几倍,无论是软件还是硬件实现。速度一直是RSA的缺陷。一般来说只用于少量数据加密。RSA的速度比对应同样安全级别的对称密码算法要慢1000倍左右
以上来自百度百科: RSA算法

秘钥长度

  选择合理的密钥长度非常重要,密钥长度越长,加密和解密操作所需的时间就越长。虽然理论上可以使用非常长的密钥,但在实际应用中,密钥长度通常不会超过4096位,因为这已经提供了足够的安全性,而更长的密钥带来的边际安全效益并不明显,同时会显著影响性能。因此,在某些需要高速处理的应用场景下,可能需要在安全性与性能之间做出权衡。
  推荐RSA密钥长度至少为2048位。这是因为随着计算能力的进步,更短的密钥长度(如1024位)已经被认为不够安全。
  对于长期的安全保障,推荐使用3072位或更长的密钥长度。对于最高级别的安全性,可以考虑4096位。
  通常,2048位到4096位之间的密钥长度是目前较为普遍的选择,既能够提供良好的安全性,又不至于严重影响性能。

  RSA密钥长度之所以通常是1024位、2048位这样的数字,主要是出于实现简便性和兼容性的考虑。这里有几个关键点:
实现简便性
  二进制表示:计算机内部使用二进制表示数据,因此密钥长度通常以位(bit)为单位。1024位、2048位等都是2的幂次方,这在计算机科学中是非常常见的,便于处理。
效率:在实现上,使用2的幂次方作为密钥长度可以简化某些算法的实现,提高运算效率。例如,使用快速模幂算法时,2的幂次方可以让位移操作更简单高效。
兼容性
  标准化:为了确保不同系统之间的互操作性,通常会采用标准的密钥长度。例如,PKCS#1标准定义了一些常用的密钥长度,如1024位、2048位、3072位等。
  软件库支持:大多数加密库和协议支持特定的密钥长度,这些长度往往也是标准化的。使用非标准长度可能导致与其他系统的兼容性问题。

填充模式

常见的RSA填充模式包括:
RSA_PKCS1_PADDING
描述:使用PKCS#1 v1.5标准的填充模式。这种模式在加密时会对数据进行随机填充,使得相同的明文会产生不同的密文。
参数:无。
适用场景:广泛用于加密和数字签名,但由于存在一些已知的安全问题(例如padding oracle攻击),建议在新应用中使用更安全的模式。
RSA_PKCS1_OAEP_PADDING
描述:使用OAEP(Optimal Asymmetric Encryption Padding)的填充模式,这是PKCS#1 v2.0引入的一种更安全的填充方式。OAEP使用两个随机化函数,增加了破解难度。
参数:通常需要指定一个哈希函数(如SHA-256、SHA-384等)和一个掩码生成函数(如MGF1)。
适用场景:推荐用于新的应用,因为它提供了更好的安全性。

使用案例

  下面例子将使用RSA进行加密和解密,大家有需要可以直接复制到自己的项目当中使用


import javax.crypto.Cipher;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA工具类
 */
public  class  RSAUtil  {
    /**
     * 算法/模式/填充模式,ECB代表是块加密,尽管ECB通常用于对称加密算法(如AES),但在RSA中,ECB模式表示每个数据块独立加密。这是因为RSA本质上是一个块加密算法,每次只能加密固定长度的数据块。ECB模式在这里的作用是明确指出每个数据块独立处理。
     * RSA jdk1.8默认是RSA/ECB/PKCS1Padding
     * RSA/ECB/PKCS1Padding
     * RSA/ECB/OAEPWithSHA-256AndMGF1Padding,其中SHA-256可以是SHA-384、SHA-512,数字越大越安全,但同时性能肯能会越低,一般情况使用SHA-256即可
     */
    private static final String ENCRYPT_MODE = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
    private static final Integer KEY_SIZE = 2048;
    /**
     * 生成密钥对
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static Map<String, String> generateKeyPair() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        // 创建一个RSA密钥对生成器实例
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器,指定密钥长度
        keyPairGenerator.initialize(KEY_SIZE);
        // 生成RSA密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //私钥
        PrivateKey aPrivate = keyPair.getPrivate();
        String privateKey = new String(Base64.getEncoder().encode(aPrivate.getEncoded()),"UTF-8");
        //公钥
        PublicKey aPublic = keyPair.getPublic();
        String publicKey = new String(Base64.getEncoder().encode(aPublic.getEncoded()),"UTF-8");
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("privateKey",privateKey);
        resultMap.put("publicKey",publicKey);
        return resultMap;
    }

    /**
     * 使用公钥加密数据
     * @param data 明文数据
     * @param publicKeyStr 公钥
     * @return
     * @throws Exception
     */
        public static String encrypt(String data,String publicKeyStr)  throws  Exception  {
            //获取加解密对象
            Cipher cipher = Cipher.getInstance(ENCRYPT_MODE);
            //公钥base64解码
            byte[] publicKeyByte = Base64.getDecoder().decode(publicKeyStr);
            //使用 X509EncodedKeySpec 解码公钥
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyByte);
            //获取秘钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            //获取公钥
            PublicKey publicKey = keyFactory.generatePublic(keySpec);
            //初始化加密对象
            cipher.init(Cipher.ENCRYPT_MODE,  publicKey);
            //执行加密操作并进行base64编码
            String encryptData= Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
            return  encryptData;
        }

    /**
     * 使用私钥解密数据
     * @param encryptData 密文数据
     * @param privateKeyStr 私钥
     * @return
     * @throws Exception
     */
        public  static  String  decrypt(String  encryptData,  String  privateKeyStr)  throws  Exception  {
                //获取加解密对象
                Cipher  cipher  =  Cipher.getInstance(ENCRYPT_MODE);
                //私钥base64解码
                byte[] privateKeyByte = Base64.getDecoder().decode(privateKeyStr);
                //使用 PKCS8EncodedKeySpec 解码私钥
                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyByte);
                //获取秘钥工厂
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                //获取私钥
                PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
                //初始化解密对象
                cipher.init(Cipher.DECRYPT_MODE,  privateKey);
                //执行解密操作并进行base64解码
                byte[] decoderData = cipher.doFinal(Base64.getDecoder().decode(encryptData));
                //返回明文
                return  new String(decoderData);
        }

    public  static  void  main(String[]  args)  throws  Exception  {
        //生成RSA密钥对
        Map<String, String> stringStringMap = RSAUtil.generateKeyPair();
        String publicKey = stringStringMap.get("publicKey");
        String privateKey = stringStringMap.get("privateKey");
        System.out.println("公钥:"+publicKey);
        System.out.println("私钥:"+privateKey);
        //加密前的明文
        String  data  =  "Hello,world!";
        System.out.println("加密前的明文:"+data);
        //使用公钥加密数据
        String encrypt = RSAUtil.encrypt(data, publicKey);
        System.out.println("加密后的密文:"+encrypt);
        //使用私钥解密数据
        String  decryptedText  =  RSAUtil.decrypt(encrypt,  privateKey);
        System.out.println("解密后的明文:"  +  decryptedText);
    }
}
}

main方法执行测试结果:
在这里插入图片描述
有关对称加密AES大家可以看我另一篇: 全网最详细的对称加密AES详解

为了帮助更多像你一样的读者,我将持续在专栏中分享技术干货和实用技巧。如果你觉得这篇文章对你有帮助,可以考虑关注我的专栏,谢谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值