0

I'm trying to validate the authenticity of the android billing receipt that I receive when a purchase is made.. The verification is server sided but I'm thinking if sometimes the server might be down, I might check the signatures from the App itself..

here's how I am verifying the purchase on server..

<?php
// get data param
$data = $_GET['response'];

// get signature param
$signature = $_GET['signature'];

// get key
$key_64 = "MY Base64 KEY FROM DEVELOPER CONSOLE";



$key =  "-----BEGIN PUBLIC KEY-----\n".
        chunk_split($key_64, 64,"\n").
       '-----END PUBLIC KEY-----';   
//using PHP to create an RSA key
$key = openssl_get_publickey($key);


// state whether signature is okay or not
$ok = openssl_verify($data, base64_decode($signature), $key, OPENSSL_ALGO_SHA1);
if ($ok == 1) {
    echo "verified";
} elseif ($ok == 0) {
    echo "unverified";
} else {
    die ("fault, error checking signature");
}

// free the key from memory
openssl_free_key($key);

?>

So how to do the same on Android?

Darshan
  • 4,020
  • 2
  • 18
  • 49

1 Answers1

1

Here is one possible way to do it with the Android Billing Library. You will need the app's Base64-encoded RSA public license key from the play console. Instructions from here tell you how to find it:

To find the public key portion of this key pair, open your application's details in the Play Console, click Services & APIs [under developer tools], and review the field titled Your License Key for This Application.

Now for the method:

    private boolean check_purchase_signature(String json, String signature) {

        final String LICENSE_KEY = "LONG_STRING_FROM_THE_PLAY_STORE_CONSOLE";
        final String TAG = getApplication().getPackageName(); // or whatever

        try {
            java.security.Signature sig = Signature.getInstance("SHA1withRSA");
            sig.initVerify(KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(
                    (Base64.decode(LICENSE_KEY,
                            Base64.DEFAULT)))));
            sig.update(json.getBytes());
            if (sig.verify(Base64.decode(signature, Base64.DEFAULT))) {
                Log.e(TAG, "Signature verification passed.");
                return true;
            }
        } catch (Exception e) {
            Log.e(TAG, "Signature verification failed.", e);
        }
        return false;
    }

You can check the signature of any purchase like this:

 check_purchase_signature(purchase.getOriginalJson(), purchase.getSignature());

Note the information here originally came from the Security class of the ancient IABHelper. It's just boiled down into a single method, and hasn't been thoroughly tested.

Hope this helps!

fattire
  • 6,823
  • 3
  • 25
  • 38
  • This won't work if the App is Patched by Lucky Patcher. – Darshan Oct 27 '19 at 05:43
  • @DarShan is this spam? What does that (sketchy-looking IMO) app have to do with the original question or my answer, which is not about circumventing IAB licensing signatures but implementing the verification? – fattire Dec 05 '19 at 05:52
  • What I meant is that if you try server side verification, LP cant intercept it BUT if the above mentioned method is used to verify then LP does intercept it and marks it as a safe / authentic purchase, I have tried it. Don’t know if that still happens now. – Darshan Dec 05 '19 at 05:54