常见加解密算法08 - RSA算法

各位 Full of benevolence and righteousness and Talent towering like eight bushels 的读者们好啊,今天讨论一下RSA算法的工作流程。

RSA 算法是一种非对称加密算法,它是一种基础数学理论而不是对称密码中的混淆与扩散。它得名于发明者Ron Rivest、Adi Shamir和Leonard Adleman的首字母。这种算法能够确保数据传输的安全,广泛用于互联网安全和电子商务领域。

RSA算法之所以安全,是因为它基于一个简单的数论事实:将两个大素数相乘很容易,但是要将它们的乘积分解回原来的两个素数却极其困难。

算法流程

在RSA算法中,每个人都有一对密钥:一个公钥和一个私钥。公钥是公开的,可以安全地传输和分享;而私钥则是保密的,只有密钥的拥有者知道。当你想要发送一个加密的消息时,你会使用接收者的公钥对消息加密。然后,只有拥有对应私钥的接收者才能解密那个消息。

生成RSA密钥对的过程涉及到几个步骤:

  1. 选择两个大的随机素数,记作p和q。

  2. 计算这两个素数的乘积n,n = p * q。n用于公钥和私钥,它的长度,也就是它的位数,是加密强度的关键。

  3. 计算n的欧拉函数φ(n) = (p-1)(q-1)

  4. 选择一个整数e作为公钥指数,它必须和φ(n)互质,并且1 < e < φ(n)

  5. 计算e对于φ(n)的乘法逆元d,即ed ≡ 1 (mod φ(n))。这个d就是私钥指数。

有了公钥(n, e)和私钥(n, d),就可以开始加密和解密消息了。要加密消息,先将消息转换为一个整数m。然后使用公钥计算密文c = m^e mod n。要解密密文,使用私钥计算m = c^d mod n

肯定又有靓仔说上面的步骤看不懂了,所以我又找了一个视频,里面举了一个例子,非常的易懂:

https://www.bilibili.com/video/BV1XP4y1A7Ui

明文长度限制

需要,注意一件事:明文分组m需要是整数,且必须小于n

RSA 算法明文长度的限制由选择的密钥长度(即模数 n 的位长)决定。明文必须在加密前转换为一个整数 m,并且这个整数必须小于模数 n 才能正确进行加密。通常,明文的长度会比密钥的长度短一些,因为在实际使用中,会加入填充方案来增强安全性,如 PKCS#1 在加密前对数据进行填充。

举例来说,如果你使用了一个 2048 位的 RSA 密钥,那么你的模数 n 大约有 2048/8=256 个字节大小。当引入必要的填充方案后,实际可用于明文的长度会减少。例如,PKCS#1 填充方案可能会占用你的明文空间中的一些字节,所以对于 2048 位的 RSA 密钥,明文可能需要限制在 200-250 字节以内。

为了加密较长的信息,通常会使用 RSA 来加密对称密钥(这个对称密钥比 RSA 密钥要短得多),然后用这个对称密钥通过对称加密算法来加密实际的信息,这种方法被称为混合加密系统。这样做的主要原因是对称算法比非对称算法像 RSA 在效率上要快得多。

安全性

对极大整数做因式分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法,那么RSA的可靠性就会极度下降,但找到这样的算法的可能性是非常小的。今天只有短的RSA密钥才可能被暴力破解。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。目前被破解的最长 RSA 密钥就是 768 位。实际应用中 RSA 的密钥长度为1024 位,重要场合 2048 位。

RSA中的填充

在 RSA 加密中,填充是一种十分重要的概念,主要是为了两个目的:安全性和兼容性。没有适当的填充,RSA 加密可能会非常容易受到一些攻击,如选择明文攻击(Chosen Plaintext Attack, CPA)。

填充机制可以提供额外的安全性:

  1. 随机性:填充可以提供一种方法,使得即使相同的数据被多次加密,每次生成的密文也都不同。这使得攻击者更难根据密文推测出原始信息。

  2. 结构性:通过填充,可以确保明文达到固定长度,这对于 RSA 加密的数据块大小要求是必须的。

  3. 完整性:某些填充方案能够提供消息完整性验证和/或身份验证的功能。

最常见的 RSA 填充方案有:

  • PKCS#1 v1.5:一种较旧的填充方案,虽然在实际应用中仍然广泛使用,但已知存在一些安全性问题,导致它可能受到如“漂白攻击”(Bleichenbacher Attack)的攻击。

  • **OAEP (Optimal Asymmetric Encryption Padding)**:这是一种更为现代的、安全性更高的填充方案,它使用双层哈希和随机性,提供抗攻击能力,特别设计用来加密相对较短的消息,如密钥和哈希值,是目前推荐使用的填充方案。

  • **PSS (Probabilistic Signature Scheme)**:这主要用于 RSA 数字签名的填充,而非加密,它同样提供较高的安全性。

OAEP 和 PSS 都是 PKCS#1 v2 中引入的方案,它们在理论和实践中都被认为是安全的,并且是目前在新系统中推荐使用的方案。每种填充方案都有其特定的数学结构和实施步骤,设计时都充分考虑了要抵御的潜在攻击。

Java 使用

KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
KeyPair pair = generator.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();

生成密钥对,储存起来。

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

KeyPairGenerator生成的私钥是pkcs8格式,KeyPairGenerator生成的公钥是x509格式,所以使用之前按需要转换一下格式。

String secretMessage = "Baeldung secret message";
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);

byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8);)
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);

加密。

RSAPrivateKey privateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKyeBytes));  

Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);

byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8);)
byte[] decryptedMessageBytes = decryptCipher.doFinal(secretMessageBytes);

解密。

C++使用

C++通常使用开源的 openssl 项目:

https://github.com/openssl/openssl

可以编译成 so 库,然后使用即可。

不过现在,Android 里面使用的是:

https://github.com/google/boringssl

项目里面也说了,这个就是 openssl 的 fork 库,用来满足 Google 的需求。Android 的话,可以使用这个项目来编译。

你可能感兴趣的:(算法,java,网络,前端)