0

I inherited an android app that has some security-related code that seems to basically be a no-op and that I'd like to remove. However, I'm concerned that my assessment of it as a no-op may be incorrect. The app sub-classes Application and, in its onCreate() method, gets the serial number of the certificate that the app was signed with:

ByteArrayInputStream bais = new ByteArrayInputStream(context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0].toByteArray()));

X509Certificate cert = CertificateFactory.getInstance("X509").generateCertificate(bais);

BigInteger sn = cert.getSerialNumber();

It then computes a hash of this value and compares it to an expected value that's embedded in a Java class as a byte[]. If the hashes don't match it throws an exception, crashing the app.

What this seems to prevent is someone stealing our source code, building the app and signing it themselves, then trying to install and run it. However, if someone has the source, they can simply remove the check at app launch. (Or change the embedded hash value to match the serial number of their certificate).

Is that accurate? Or is there some reason I'm missing that this code is useful?

jph
  • 2,181
  • 3
  • 30
  • 55

1 Answers1

0

I don't think it's to protect people from stealing your source code. It's to prevent people from replacing all the assets, thereby rebranding the app as their own, then re-signing it and putting it in the play store as their own.

However, the program shouldn't be verifying the serial number, which is easily modified. It should be verifying the signature.

If someone goes to the trouble of decompiling the app, as you said, they can remove that security check.

It's just another hoop for a cracker to jump through. Consider keeping it, but be sure to check the signature, not just the serial number.

Community
  • 1
  • 1
Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
  • From what I can tell, all the check does is validate (at run time) that the certificate with which the app was signed is the same one we "expect" it to have been signed with. So it would protect against someone taking the apk, altering it somehow, re-signing it, and then putting it back in the store as their own. However, in this case, I'd expect the attacker to be able to just remove the check. No? – jph Nov 19 '13 at 16:10
  • It doesn't validate that it is the same certificate...only that the certificate has the same serial number. I could create my own certificate with any serial number I want, and then sign this and the check would pass, if I understand correctly what your check is doing. A better check would be to hash the entire certificate and compare that hash. Or only trust certificates signed by specified trusted Certificate Authorities. But even then, if someone can decompile the app, they can of course change whatever security measure you put in the code, recompile it, and claim it as their own. – gtrig Nov 19 '13 at 21:17
  • @gtrig, you're right. I misread the question. They should be checking the signature, not the serial number. I'll update my answer. By the way, the signature is a hash of the certificate. – Marcus Adams Nov 19 '13 at 22:04
  • You're referring to the certificate signature, right? If it's a self-signed cert, this check is still meaningless. I can sign my own cert with whatever serial number I want, and the signature will be valid. :) If it's a cert issued by a CA that is trusted by the app, that is better, and a signature check in that case will actually be meaningful. – gtrig Nov 19 '13 at 22:50
  • @gtrig, if the signature matches, the certificate matches. The idea isn't to check for a **valid** signature (Android does that). It's to check for a **specific** signature. – Marcus Adams Nov 20 '13 at 00:28
  • Sorry Marcus, I guess I'm not completely following. Are you saying he should store a known good signature of the certificate he is expecting, and compare that with the signature in the certificate that is being checked? – gtrig Nov 20 '13 at 01:24
  • Basically the app stores a SHA-1 hash of the serial number of the "expected" certificate with which the app was originally signed, then checks at run-time that the hash of the serial number with which it's *actually* signed matches the expected value. I think the *intent* of the check (which it may not be fulfilling) is to make it more difficult for someone other than the publisher to re-sign the app and submit it to a non-Google marketplace as if it were their own creation. With this check in place they'd have to muck w/ the byte code instead of just re-signing. – jph Nov 20 '13 at 13:34
  • It's called certificate pinning. Look it up. – Marcus Adams Dec 09 '13 at 13:57