Home » Android » java – SHA256 with RSA signature returns different outputs on various Android devices

java – SHA256 with RSA signature returns different outputs on various Android devices

Posted by: admin June 15, 2020 Leave a comment

Questions:

I am developping an Android app, and I need to use java Signature class for data authentication.

On each Android device, I can sign data and verify its signature. However, given a definite chunk of data to sign, a definite modulus, a definite private exponent and a definite public exponent, the outputs of my signatures are different, depending on devices. I did try with bunch of devices, and I obtain the same signatures for Android 3.2 and 3.2.1, but a differents for an Android 2.2.x device.

I compute these signature from constant fields that I generated previously using a KeyFactory with RSA in a java project. The keysize is 2048bit.

Here is a quote of the code that I use to invoque Signature and Verification.

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);
}

If I am not mistaking, the signature using SHA256 with RSA is deterministic. Then how can I explain such a behaviour? Another interesting question, how could I make that work cross-devices, i.e. the signatures would be same, no mather which device I use?

Thank you in advance, Franck!

How to&Answers:

Yes, SHA256withRSA is completely deterministic.

In theory you could be affected by a bug (see an example) in an old modified BouncyCastle library version found on one of the Android versions. Such a bug might be eliminated if you used SHA512withRSA instead, well, at least the referenced one would be.

However, before you start digging into the hash algorithm, check close to home.

Maybe you have obtained your byte array through a call to String.getBytes. This call depends on the default platform encoding which is different between Android 2.2 and Android 2.3. This implies that while your strings are the same in both cases, the byte arrays might not be.

To get encoding under control, and make your code platform independent, specify encoding as a parameter:

plainText.getBytes("UTF-8")

Failing this, there are a few more tactics to get a platform independent implementation.

  • wait until 2.2 with the presumably buggy library dies out
  • distribute a known good library (jar) with your software. If that would be BouncyCastle, you will have problems making sure that your and not Android’s classes are loaded. The solution is called SpongyCastle.
  • play with alignment/padding. Try to make the message length in bytes to be congruent with 0,55, 56 or 63 modulo 64 by adding your own fixed padding and hope that one of these options will start giving portable signatures. These values are chosen to interact with the outermost part of the suspect algorithm which is padding to 512 bit blocks.