Google Play on Android has a service for license checks, com.android.vending.licensing.ILicensingService
. It supports a callback interface, ILicenseResultListener
. It has a method verifyLicense
with three parameters. For a paid app that's been downloaded from Google Play, what comes in the second one, signedData
, please?
And this is why I'm wondering.
The answer is in android-sdk-windows\extras\google\market_licensing\library\src\com\google\android\vending\licensing\ResponseData.java, in the parse() method.
The signedData string is a sequence of 6 fields, separated by |, optionally followed by colon and a &-separated name=value collection (like a query string). For example:
0|17|com.acme.myapp|1|AAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHH==|1480563297411:GR=10&VT=1480570457760>=1481088857760
This one comes from a paid app.
The 6 fields in the first section are:
- Response code (int) - presumably the same as the
verifyLicense
's first parameter
- Nonce (int) - comes straight from the
checkLicense
call
- Package name
- Version code
- User ID, with a comment "Application-specific user identifier"
- Timestamp (long) - not sure of what. Definitely not purchase; repeat runs return a different, later value. Probably that of license check itself.
UserID seems to be a Base64-encoded string. Which user does it identify, and how? The license holder, one presumes; but how? Decoding gives a byte array, 25 bytes long.
For a sideloaded app, the basic fields is all there is. For a store bought app, there's extra data. The meaning of extra data fields can be found here. Specifically, with a freshly bought app, the following extra fields come:
- GR - max retry count
- VT - license validity timestamp; specifies when the license needs to be rechecked
- GT - grace period timestamp
Regarding UserID, I did some tests. I have three cases:
- Package A, side-loaded
- Package A, downloaded for a fee from Play
- Package B, downloaded for free from Play (by the same user)
In all three cases, the first 5 bytes of the decoded UserID match, the rest don't. So neither the package unique part nor the user unique part can be easily identified in the UserID. Notably, the remainder is 20 bytes long - could be an MD5 hash. The UserID definitely isn't a string in any sensible encoding, and it doesn't look like a structure with binary integers, either. The seemingly random bit pattern points at either a hash, or a cyphertext. The latter is unlikely.