带有RSA签名的SHA256在各种Android设备上返回不同的输出

时间:2012-06-28 18:52:22

标签: java android rsa digital-signature

我正在开发Android应用,我需要使用java Signature class进行数据身份验证。

在每台Android设备上,我都可以对数据进行签名并验证其签名。但是,给定一定数量的数据进行签名,一个明确的模数,一个明确的私有指数和一个明确的公共指数,我的签名输出会有所不同,具体取决于设备。我尝试使用大量设备,我获得了Android 3.2和3.2.1的相同签名,但是对于Android 2.2.x设备却是不同的。

我从先前在java项目中使用带有RSA的KeyFactory生成的常量字段计算这些签名。密钥大小为2048位。

以下是我用于调用签名和验证的代码的引用。

public byte[] signData(byte[] data, PrivateKey privateKey) throws ... {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
}

public boolean verifyData(byte[] data, byte[] sigBytes, PublicKey publicKey) throws ... {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(data);
        return signature.verify(sigBytes);
}

如果我没有误会,使用带有RSA的SHA256的签名是确定性的。那我怎么解释这样的行为呢?另一个有趣的问题是,我怎样才能使这项工作跨设备,即签名是相同的,不管我使用哪种设备?

提前谢谢你,弗兰克!

1 个答案:

答案 0 :(得分:11)

是的,SHA256withRSA完全是确定性的。

理论上,您可能会受到其中一个Android版本上发现的旧修改过的BouncyCastle库版本中的错误(see an example)的影响。如果您使用SHA512withRSA代替,那么这样的错误可能会被消除,好吧,至少会引用一个。

但是,在开始深入挖掘哈希算法之前,请先查看回家。

也许您通过调用String.getBytes获得了字节数组。此调用取决于Android 2.2和Android 2.3之间的默认平台编码different。这意味着虽然在两种情况下字符串都相同,但字节数组可能不是。

要控制编码并使代码平台独立,请将编码指定为参数:

plainText.getBytes("UTF-8")

如果做不到这一点,还有一些策略可以实现独立于平台的实施。

  • 等到2.2,大概有错误的库已经消失
  • 使用您的软件分发一个已知的好库(jar)。如果那将是BouncyCastle,那么你将无法确保加载你的Android类而不是Android类。该解决方案称为SpongyCastle。
  • 使用对齐/填充。尝试通过添加自己的固定填充来使消息长度以字节为单位与0,55,56或63模64一致,并希望其中一个选项将开始提供可移植签名。选择这些值以与可疑算法的最外层部分交互,该算法填充到512位块。