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

fra*_*now 8 java android rsa digital-signature

我正在开发一个Android应用程序,我需要使用java Signature进行数据身份验证.

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

我从以前KeyFactory在java项目中使用RSA 生成的常量字段计算这些签名.密钥大小为2048bit.

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

先谢谢你,弗兰克!

Jir*_*ika 11

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

理论上,您可能会受到其中一个Android版本上发现的旧修改过的BouncyCastle库版本中的错误(请参阅示例)的影响.如果您使用了这样的错误,那么SHA512withRSA至少可以使用引用的错误.

但是,在开始深入挖掘哈希算法之前,请先检查一下home.

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

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

plainText.getBytes("UTF-8")
Run Code Online (Sandbox Code Playgroud)

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

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