Java RSA加密不可重复?

我一直无法使用RSA公钥加密。 以下是重现问题的示例JUnit代码:

public class CryptoTests { private static KeyPair keys; @BeforeClass public static void init() throws NoSuchAlgorithmException{ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); SecureRandom random = CryptoUtils.getSecureRandom(); keyGen.initialize(2176, random); keys = keyGen.generateKeyPair(); } @Test public void testRepeatabilityPlainRSAPublic() throws EdrmCryptoException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{ byte[] plaintext = new byte [10]; Random r = new Random(); r.nextBytes(plaintext); Cipher rsa = Cipher.getInstance("RSA"); rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic()); byte[] encrypted1 = rsa.doFinal(plaintext); rsa = Cipher.getInstance("RSA"); rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic()); byte[] encrypted2 = rsa.doFinal(plaintext); rsa = Cipher.getInstance("RSA"); rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic()); byte[] encrypted3 = rsa.doFinal(plaintext); assertArrayEquals(encrypted1, encrypted2); assertArrayEquals(encrypted1, encrypted3); } } 

结果? 断言失败了。

为什么会出现这种行为? 据我记得我的加密类,任何密钥都可以用于加密。 然而,这不是这里发生的事情。 我用私钥测试了同样的东西,我得到了一个可重复的输出。

如果出于某种原因,禁止使用公钥进行RSA加密,那么为什么我没有得到例外?

我该怎么做才能获得可重复的结果?

PS我的JDK是在Ubuntu 10.10盒子上运行的1.6.0_22。

我的猜测是,它正在应用随机填充,正是为了使其更安全。 来自RSA维基百科页面 :

由于RSA加密是一种确定性加密算法 – 即没有随机分量 – 攻击者可以通过加密公钥下的可能明文并测试它们是否等于密文,成功启动对密码系统的选定明文攻击。 如果攻击者无法区分两个加密,即使攻击者知道(或已选择)相应的明文,也会将密码系统称为语义安全。 如上所述,没有填充的RSA在语义上不安全。

为了避免这些问题,实际的RSA实现通常在加密之前将某种forms的结构化随机填充嵌入到值m中。 这种填充确保m不会落入不安全明文的范围内,并且一旦填充,给定消息将加密到大量不同的可能密文中的一个。

您可以通过使用字符串“RSA / ECB / NoPadding”初始化Cipher来确认正在发生的事情是添加了随机填充。 现在,您应该看到密文在每种情况下都是相同的(尽管出于另一个回答者所说的原因,您在实践中不应该这样做)。

为Jon的答案添加额外的细节:

当您执行Cipher.getInstance("...") ,您可能会收集许多选项。 标准算法名称指定这些是什么。

你要求的那个, RSA默认是PKCS1下的RSA,引用维基百科的文章:

加密和解密有两种方案:

  • RSAES-OAEP:改进的加密/解密方案; 基于Mihir Bellare和Phillip Rogaway提出的最优非对称加密填充方案。
  • RSAES-PKCS1-v1_5:较旧的加密/解密方案,首先在PKCS#1的1.5版本中标准化。

有关所述填充方案的详细信息,请参阅RSALab的PKCS1文档 。