Android In App Billing: securing application public key
An alternative is to do some basic transforms on the key.
// Replace this with your encoded key.
String base64EncodedPublicKey = "";
// Get byte sequence to play with.
byte[] bytes = base64EncodedPublicKey.getBytes();
// Swap upper and lower case letters.
for (int i = 0; i < bytes.length; i++) {
if(bytes[i] >= 'A' && bytes[i] <= 'Z')
bytes[i] = (byte)( 'a' + (bytes[i] - 'A'));
else if(bytes[i] >= 'a' && bytes[i] <= 'z')
bytes[i] = (byte)( 'A' + (bytes[i] - 'a'));
}
// Assign back to string.
base64EncodedPublicKey = new String( bytes );
So the idea would be to put your original key in as base64EncodedPublicKey
and run the above code, it would swap lower and uppercase letters and put the result back in base64EncodedPublicKey
. You can then copy the result from the debugger and paste it into code as the original base64EncodedPublicKey
value. At this point your key will be transformed (upper and lower case switched) and at runtime it'll fix it back to the correct casing, and continue to work.
The above is obviously quite a basic transcode, but you can be more creative, reverse the ordering of A-Z, swap odd and even numbers, swap vowels for even numbers. The issue here is that if I put code in the above snippet that does a bunch of more interesting transcodes, and then everyone copy and pastes that into their projects, a cracker will easily be able to see and use the transcode themselves (from looking at this post)! So you just have to come up with a few transforms yourself.
I've purposely made the above work in both direction (so if you run it twice, you'll get your original value back) as it makes it easy to run the algorithm on your original key. I think it is kind of neat it looks like the real key is sitting there as plain text, a casual cracker may try to switch this and then be confused when it doesn't work.
What I did was to transform the key into a char array, split it in two and then reconstruct it when needed like this:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shop);
char[] base64KeyByteArray = ArrayUtils.addAll(getPublicKeyChunk1(), getPublicKeyChunk2());
Log.d(TAG, String.valueOf(base64KeyByteArray));
}
private char[] getPublicKeyChunk1() {
return new char[]{82, 73, 67, 66, 73, 106, 65, 78, 66, 103, 107, 113, 104, 107,
105, 71, 57, 119, 79, 66, 65, 81, 69, 70, 65, 65, 79, 67, 65, 81, 56, 65, 77, 73,
73, 66, 67, 103, 75, 67, 65, 81, 69, 65, 121, 55, 81, 76, 122, 67, 105, 80, 65,
110, 105, 101, 72, 66, 53, 57};
}
private char[] getPublicKeyChunk2() {
return new char[]{82, 43, 68, 47, 79, 121, 122, 110, 85, 67, 118, 89, 108, 120, 43, 49,
80, 100, 67, 108, 55, 90, 57, 103, 119, 57, 87, 78, 79, 111, 53, 101, 80, 71,
117, 74, 104, 82, 87, 97, 100};
}
Something like this:
String Base64EncodedPublicKey key = "Ak3jfkd" + GetMiddleBit() + "D349824";
or
String Base64EncodedPublicKey key =
DecrementEachletter("Bl4kgle") + GetMiddleBit() + ReverseString("D349824");
or anything that doesn't put the key in base64 plaintext in a single string. Probably also something that doesn't store the key in base64 would be a good idea too, since raw base64 text fragments are pretty easy to spot.
It's not a particularly GOOD way to protect the key. But it protects against a trivial attack where somebody just searches through literal strings in you APK looking for something that looks like a base64-encoded public key. At least you make the #$#$ers work a little bit.
Presumably evil people can do bad things if they identify your public key. Google seems to think so, apparently. I can guess what this step does, but I'm not sure I really want to speculate on that in an open forum, and give anyone any ideas. You want to do it though.
The basic plot summary would be that you're making it more difficult for somebody to write an application that programmatically de-LVLs an applciation.
One assumes that anyone who's doing this makes a living cracking 20 or 30,000 android apps and republishing them. Chances are, I suppose that they're not going to take the extra ten minutes to add your app to the list of 20,000 Android apps that have already been broken by a program, if they actually have to do a little bit of manual work. Unless you have a top tier application. And then the battle is potentially endless, and probably ultimately futile.
Splitting the key into consecutive chunks (as proposed in another answer) probably isn't good enough. Because the key will end up in consecutive strings in the string constant tables in the APK. Too easy to find that with a program.
You can split it into pieces like this
String piece1 = "SDFGJKGB4UIH234WE/FRT23RSDF/3DFUISDFVWE";
String piece2 = "SDFGJKGB4UIHUISDFVWE";
String piece3 = "BDYASGBDNAWGRET24IYE23das4saGBENWKD";
String piece4 = "432423SDF23R/+SDDS";
mHelper = new IabHelper(this, piece1 + piece2 + piece3 + piece4);
Any kind of manipulations will do.
You can't hide the public key perfectly from the attacker, you just need to manipulate the string to confuse a attacker a little bit
You can add some strings and remove it when it's needed or split it into chunks.