Android Question Google Play In-app signature verification

peacemaker

Expert
Licensed User
Longtime User
HI, All
In-app are cheated by Freedom or LuckyPatcher application. So each purchase must be checked, verified that really made on Google side. Google signs his reply.
Please, help to make working module for this verification.

I read https://www.b4x.com/android/forum/threads/inapp_purchase_data-and-inapp_data_signature.46545/ , googled a lot and made this sketch:
No idea right or not way, please, suggest, i did not try yet.

B4X:
'Code module
'Subs in this code module will be accessible from all modules.
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private nativeMe As JavaObject
End Sub


'PublicKey = RSA public encription key like "MIIBIjANBgkqhkiG9w0BAQ....." from Google Play app API console
Sub Verify (PublicKey As String, Product As Purchase)
Dim OriginalJson, base64Signature As String

Dim jo As JavaObject = Product
base64Signature = jo.RunMethod("getSignature", Null)
OriginalJson = jo.RunMethod("getOriginalJson", Null)

nativeMe.InitializeContext
Dim Params(3) As String
Params(0) = PublicKey    'RSA public encription key like "MIIBIjANBgkqhkiG9w0BAQ....." from Google Play app API console
Params(1) = OriginalJson    'signedData
Params(2) = base64Signature    'signature
Dim result As Boolean = nativeMe.RunMethod("verify", Params)
Log("result=" & result)
End Sub

#If JAVA
public class Security {

    public final static Logger logger = Logger.getLogger(Security.class.getName());

    private static final String KEY_FACTORY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    /**
     * Generates a PublicKey instance from a string containing the
     * Base64-encoded public key.
     *
     * @param encodedPublicKey
     *            Base64-encoded public key
     * @throws IllegalArgumentException
     *             if encodedPublicKey is invalid
     */
    public static PublicKey generatePublicKey(String encodedPublicKey) {
        try {
            byte[] decodedKey = Base64.decode(encodedPublicKey);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
            return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
        }
        catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        catch (InvalidKeySpecException e) {
            logger.error("Invalid key specification.", e);
            throw new IllegalArgumentException(e);
        }
        catch (Base64DecoderException e) {
            logger.error("Base64 decoding failed.", e);
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Verifies that the signature from the server matches the computed
     * signature on the data. Returns true if the data is correctly signed.
     *
     * @param publicKey
     *            public key associated with the developer account
     * @param signedData
     *            signed data from server
     * @param signature
     *            server signature
     * @return true if the data and signature match
     */
    public static boolean verify(PublicKey publicKey, String signedData, String signature) {
        Signature sig;
        try {
            sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initVerify(publicKey);
            sig.update(signedData.getBytes());
            byte[] decodedSig = Base64.decode(signature);
            if (!sig.verify(decodedSig)) {
                logger.error("Signature verification failed.");
                return false;
            }
            return true;
        }
        catch (NoSuchAlgorithmException e) {
            logger.error("NoSuchAlgorithmException.");
        }
        catch (InvalidKeyException e) {
            logger.error("Invalid key specification.");
        }
        catch (SignatureException e) {
            logger.error("Signature exception.");
        }
        catch (Base64DecoderException e) {
            logger.error("Base64 decoding failed.");
        }
        return false;
    }

}
#End If
 

peacemaker

Expert
Licensed User
Longtime User
During compilation try i got errors:
1) Firstly
B4X:
error: cannot find symbol
    public final static Logger logger = Logger.getLogger(Security.class.getName());
                        ^
  symbol:   class Logger
  location: class googleplaypurchaseguard.Security

2) After commenting "logger" Java lines
B4X:
error: cannot find symbol
    public static PublicKey generatePublicKey(String encodedPublicKey) {
                  ^
  symbol:   class PublicKey
  location: class googleplaypurchaseguard.Security
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Upvote 0

peacemaker

Expert
Licensed User
Longtime User
Thanks for help !

I have added these imports after googling:
B4X:
    import java.util.logging.Logger;
    import java.security.KeyFactory;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.RSAPublicKeySpec;
import android.util.Base64;

Now it is:
B4X:
 error: Illegal static declaration in inner class googleplaypurchaseguard.Security
    public final static Logger logger = Logger.getLogger(Security.class.getName());
                               ^
  modifier 'static' is only allowed in constant variable declarations

OK, removed "static".

Now it is
B4X:
 cannot find symbol
            byte[] decodedKey = Base64.decode(encodedPublicKey);
                                ^
  symbol:   variable Base64

import android.util.Base64; has no "decode" with single argument...

B4X:
 cannot find symbol
        catch (Base64DecoderException e) {
               ^
  symbol:   class Base64DecoderException
 
Last edited:
Upvote 0

peacemaker

Expert
Licensed User
Longtime User
Oh, f...ng sh.t Java...
Seems here is better code, but it always gives error about "static" declaration or using non-static declaration in static context... or something...

B4X:
#If JAVA
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;

import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

class Security {
    private static final String TAG = "IABUtil/Security";

    private static final String KEY_FACTORY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
        if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
                TextUtils.isEmpty(signature)) {
            Log.e(TAG, "Purchase verification failed: missing data.");
            return false;
        }

        PublicKey key = Security.generatePublicKey(base64PublicKey);
        return Security.verify(key, signedData, signature);
    }

    public static PublicKey generatePublicKey(String encodedPublicKey) {
        try {
            byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
            return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeySpecException e) {
            Log.e(TAG, "Invalid key specification.");
            throw new IllegalArgumentException(e);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Base64 decoding failed.");
            throw e;
        }
    }
    Public boolean verify(PublicKey publicKey, String signedData, String signature) {
        Signature sig;
        try {
            sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initVerify(publicKey);
            sig.update(signedData.getBytes());
            if (!sig.verify(Base64.decode(signature, Base64.DEFAULT))) {
                Log.e(TAG, "Signature verification failed.");
                return false;
            }
            return true;
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "NoSuchAlgorithmException.");
        } catch (InvalidKeyException e) {
            Log.e(TAG, "Invalid key specification.");
        } catch (SignatureException e) {
            Log.e(TAG, "Signature exception.");
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Base64 decoding failed.");
        }
        return false;
    }
}
#End If
 
Upvote 0
Top