确定性生成RSA加密密钥对

5

我想在安卓设备上使用Java编写代码,以确定性的方式生成RSA密钥对。我的要求是不能存储密钥对,必须在运行时生成,并且与之前/未来的运行结果相同。

我的思路是确定性地种子化随机数生成器,并将该生成器传递给密钥生成函数。我的代码如下:

SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
random.setSeed(1234);   //something device specific will be used to set this
KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024, random);

KeyPair pair=keyGen.generateKeyPair();
PublicKey pub=pair.getPublic();
PrivateKey priv=pair.getPrivate();

每次运行生成的密钥都不同。但是,SecureRandom随机数在不同的运行中以及跨设备之间保持相同。

我错过了什么?如何可重复地生成这些密钥?

谢谢


你为什么想要这样做?如果你不想存储RSA密钥对,那么你也不会想要存储用于生成RSA密钥对的随机种子吧?虽然这段代码没有达到你的预期,但我认为你可能没有经过深思熟虑就开始了这个过程。 - Mike T
1个回答

9
你想做什么?即使这个方法可以工作,它也依赖于Android上SHA1PRNG实现的一个怪癖,所以它可能随时会出问题。通常情况下,setSeed()添加熵,因此即使您使用相同的种子对SecureRandom进行种子化,也不能保证您将获得相同的数字。如果在桌面Java上尝试此代码,它很可能会失败。到目前为止,它在大多数(所有?)当前Android版本上都可以工作,但这不是保证。
如果您想要可预测的密钥,则可能需要为每个设备提供预生成的密钥。如果需要安全地存储它们,请在ICS上使用KeyChain API或在pre-ICS设备上使用受密码保护的密钥库。即使您不存储实际密钥,如果有人知道密钥是如何生成的(种子),他们就可以生成相同的密钥,而您的密钥只能像种子一样安全。如果它是设备特定的,那么找到它并不太难。
关于为什么这不起作用,RSA密钥生成器基本上在循环中生成随机的BigIntegers并测试素数。素数测试是概率性的,因此每次运行可能会选择不同的素数。您可能需要获取SpongyCastle,在模拟器上运行并在RSAKeyPairGenerator.java中设置断点以检查发生了什么。

感谢您详细解释密钥生成器的工作原理以及SpongyCastle实现。这都是为了(错误地)试图减少应用内购买内容盗版而进行的尝试。我希望能够在服务器上对内容进行加密并将其传递给客户端,无需进行密钥交换,并在运行时解密内容。如您所建议的,我将不得不在服务器上生成密钥对、将私钥传输到客户端,然后使用KeyStore和一些算法生成的密码来增加难度。 - Aaron
我有点明白了。您正在尝试实现某种形式的数字版权管理(DRM)。RSA 可能不是最适合此用途的加密算法。如果您将密钥传输到客户端,自然需要确保这样做是安全的,而这可能并不容易。一个想法是将一些设备特定位(IMEI 等)放入 IAB developerPayload 中,并在服务器验证签名时获取它。然后使用它来生成绑定到该特定设备的对称(AES)密钥,加密内容并将其发送到设备。然后设备生成相同的对称密钥,并解密内容。 - Nikolay Elenkov
当然,您可能希望对IMEI等进行哈希处理,以便不收集实际的设备识别信息。 - Nikolay Elenkov
使用AES的观点非常好,可以使用PBKDF或类似方法从相同的哈希中轻松派生出相同的密钥。再次感谢。 - Aaron
确定性生成密钥对帮助我们存储种子并每次生成钱包密钥。 - devssh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接