2

Does the following code (sign.hashCode()) gives me the hashCode of my signature or the hash of the object in the memory?

try {
    PackageInfo packageInfo = getPackageManager().getPackageInfo(
            "com.klxx.as", PackageManager.GET_SIGNATURES);
    Signature[] signs = packageInfo.signatures;
    Signature sign = signs[0];
    Log.i("test", "hashCode : "+sign.hashCode());
} catch (Exception e) {
    e.printStackTrace();
}

The documentation (here) only says the following which is like any other object.

a hash code value for this object.

But I have seen the above snippet in multiple websites claiming that it shows the sign of the apk. Also some other sources have used the bytes of signature to create the hash by themself.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Hossein Shahdoost
  • 1,692
  • 18
  • 32

2 Answers2

3

While the other answers are technically correct, they are otherwise dangerously false:

Yes, Signature.hashCode() is overwritten and does indeed calculate some weak 32-bit hash value over the signature's bytes, which makes it deterministic.

But, you should not use this value as the basis for any kind of trust decision, which you usually want to do if you retrieve the signature in the first place. This is because it is extremely easy to produce a fake signature with the same value for hashCode(): Simply generating 2^32 random signing certificates gives you a good chance of finding a collision and is very feasible.

Instead, you should use a cryptographically secure hash function, such as SHA-256 and e.g. convert the resulting hash to Base64:

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(sign.toByteArray());
String hash = Base64.encodeToString(hashBytes, Base64.NO_WRAP);
Fabian Meumertzheim
  • 1,569
  • 1
  • 11
  • 21
  • You mean, I need to compare the resulting hash value with the SHA256 value I get from the keytool command? What should I do after this code if my SHA256 is "BB:FD:64:F5:1D:8B:2F:F8:34:83:55:5B:22:E9:D7:53:D0:5B:AA:7C:9E:D9:E6:92:02:60:48:48:28:B3:68:3D"? – Jenix May 28 '20 at 19:16
  • 1
    @Jenix The code snippet I included above would give you the SHA-256 hash encoded in Base64. Encoded in the same way, the hash you provided would look like this: https://gchq.github.io/CyberChef/#recipe=From_Hex('Colon')To_Base64('A-Za-z0-9%2B/%3D')&input=QkI6RkQ6NjQ6RjU6MUQ6OEI6MkY6Rjg6MzQ6ODM6NTU6NUI6MjI6RTk6RDc6NTM6RDA6NUI6QUE6N0M6OUU6RDk6RTY6OTI6MDI6NjA6NDg6NDg6Mjg6QjM6Njg6M0Q . You could then compare the resulting strings. – Fabian Meumertzheim Jun 05 '20 at 07:04
  • Yeah, I needed to encode my fingerprint into Base64, but when I commented here, I was looking for a way to encode your Base64 value into Hex value. Silly me! Thank you anyway :) – Jenix Jun 05 '20 at 09:10
  • @Jenix While Base64 is readily available on Android, convenient methods for conversion to hex strings seems to require additional dependencies. – Fabian Meumertzheim Jun 08 '20 at 07:05
  • Hello again! For my APK files locally built and tested, this check works great. But my AAB file downloaded from Google Play Store (which I uploaded, of course) fails to pass this check so I had to turn this feature off. Is it because the Google Play server does something to my AAB? Can't I use this method with apps published on Google Play then? – Jenix Nov 26 '20 at 10:05
1

Signature.hashCode() is overwritten and in this case is calculated on the content of the signature byte array.

You can see the source code to solve your doubts http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/content/pm/Signature.java#Signature.hashCode%28%29

pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • While this is technically correct, it is dangerous to leave this without the following big warning: The return value of hashCode() is a non-cryptographically secure 32-bit hash, which should NOT be used to verify the signatures of a package. See my answer below for details. – Fabian Meumertzheim Feb 26 '20 at 14:19