From 1ae04c3850eb876c7f4d1956089741e747691b3a Mon Sep 17 00:00:00 2001 From: YuutaW <17158086+trumeet@users.noreply.github.com> Date: Thu, 16 May 2019 16:03:24 -0700 Subject: refactor: remove #Anti-Crack Signed-off-by: YuutaW <17158086+Trumeet@users.noreply.github.com> --- .../java/moe/yuuta/gplicense/LicenseChecker.java | 356 --------------------- 1 file changed, 356 deletions(-) delete mode 100644 app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java (limited to 'app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java') diff --git a/app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java b/app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java deleted file mode 100644 index e909aec..0000000 --- a/app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java +++ /dev/null @@ -1,356 +0,0 @@ -package moe.yuuta.gplicense; - -import android.annotation.SuppressLint; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.RemoteException; -import android.provider.Settings.Secure; -import com.elvishew.xlog.Logger; -import com.elvishew.xlog.XLog; -import moe.yuuta.ext.*; -import moe.yuuta.gplicense.util.Base64; -import moe.yuuta.gplicense.util.Base64DecoderException; -import moe.yuuta.workmode.BuildConfig; - -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; -import java.util.*; - -/** - * Client library for Google Play license verifications. - *

- * The LicenseChecker is configured via a {@link Policy} which contains the logic to determine - * whether a user should have access to the application. For example, the Policy can define a - * threshold for allowable number of server or client failures before the library reports the user - * as not having access. - *

- * Must also provide the Base64-encoded RSA public key associated with your developer account. The - * public key is obtainable from the publisher site. - */ -public class LicenseChecker implements ConnCallback { - private static final Logger logger = XLog.tag("LCK").build(); - - private static final String KEY_FACTORY_ALGORITHM = "RSA"; - - // Timeout value (in milliseconds) for calls to service. - private static final int TIMEOUT_MS = 10 * 1000; - - private static final SecureRandom RANDOM = new SecureRandom(); - private static final boolean DEBUG_LICENSE_ERROR = BuildConfig.DEBUG; - - private IService mService; - - private PublicKey mPublicKey; - private final Context mContext; - private final Policy mPolicy; - /** - * A handler for running tasks on a background thread. We don't want license processing to block - * the UI thread. - */ - private Handler mHandler; - private final String mPackageName; - private final String mVersionCode; - private final Set mChecksInProgress = new HashSet(); - private final Queue mPendingChecks = new LinkedList(); - - private LicServiceConn mConn; - - /** - * @param context a Context - * @param policy implementation of Policy - * @param encodedPublicKey Base64-encoded RSA public key - * @throws IllegalArgumentException if encodedPublicKey is invalid - */ - public LicenseChecker(Context context, Policy policy, String encodedPublicKey) { - mContext = context; - mPolicy = policy; - mPublicKey = generatePublicKey(encodedPublicKey); - mPackageName = mContext.getPackageName(); - mVersionCode = getVersionCode(context, mPackageName); - HandlerThread handlerThread = new HandlerThread("background thread"); - handlerThread.start(); - mHandler = new Handler(handlerThread.getLooper()); - } - - /** - * 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 - */ - private 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) { - // This won't happen in an Android-compatible environment. - throw new RuntimeException(e); - } catch (Base64DecoderException e) { - logger.e("Could not decode from Base64."); - throw new IllegalArgumentException(e); - } catch (InvalidKeySpecException e) { - logger.e("Invalid key specification."); - throw new IllegalArgumentException(e); - } - } - - /** - * Checks if the user should have access to the app. Binds the service if necessary. - *

- * NOTE: This call uses a trivially obfuscated string (base64-encoded). For best security, we - * recommend obfuscating the string that is passed into bindService using another method of your - * own devising. - *

- * source string: "com.android.vending.licensing.IService" - *

- * - * @param callback - */ - public synchronized void checkAccess(LicenseCheckerCallback callback) { - // If we have a valid recent LICENSED response, we can skip asking - // Market. - if (mPolicy.allowAccess()) { - logger.i("Using cached response"); - callback.allow(Policy.LICENSED); - } else { - LicenseValidator validator = new LicenseValidator(mPolicy, new NullDeviceLimiter(), - callback, generateNonce(), mPackageName, mVersionCode); - - if (mService == null) { - logger.i("Binding to service."); - try { - mConn = new LicServiceConn(this); - boolean bindResult = mContext - .bindService( - new Intent( - new String( - // Base64 encoded - - // com.android.vending.licensing.IService - // Consider encoding this in another way in your - // code to improve security - Base64.decode( - "Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U="))) - // As of Android 5.0, implicit - // Service Intents are no longer - // allowed because it's not - // possible for the user to - // participate in disambiguating - // them. This does mean we break - // compatibility with Android - // Cupcake devices with this - // release, since setPackage was - // added in Donut. - .setPackage( - new String( - // Base64 - // encoded - - // com.android.vending - Base64.decode( - "Y29tLmFuZHJvaWQudmVuZGluZw=="))), - mConn, // ServiceConnection. - Context.BIND_AUTO_CREATE); - if (bindResult) { - mPendingChecks.offer(validator); - } else { - logger.e("Could not bind to service."); - handleServiceConnectionError(validator); - } - } catch (SecurityException e) { - callback.applicationError(LicenseCheckerCallback.ERROR_MISSING_PERMISSION); - } catch (Base64DecoderException e) { - e.printStackTrace(); - } - } else { - mPendingChecks.offer(validator); - runChecks(); - } - } - } - - private void runChecks() { - LicenseValidator validator; - while ((validator = mPendingChecks.poll()) != null) { - try { - logger.i("Executing on service for " + validator.getPackageName()); - mService.exec( - validator.getNonce(), validator.getPackageName(), - new IPCResultListener(new ResultListener(validator))); - mChecksInProgress.add(validator); - } catch (RemoteException e) { - logger.w("RemoteException in exec call.", e); - handleServiceConnectionError(validator); - } - } - } - - private synchronized void finishCheck(LicenseValidator validator) { - mChecksInProgress.remove(validator); - if (mChecksInProgress.isEmpty()) { - cleanupService(); - } - } - - private class ResultListener implements ResultCallback { - private final LicenseValidator mValidator; - private Runnable mOnTimeout; - - public ResultListener(LicenseValidator validator) { - mValidator = validator; - mOnTimeout = () -> { - logger.i("Check timed out."); - handleServiceConnectionError(mValidator); - finishCheck(mValidator); - }; - startTimeout(); - } - - private static final int ERROR_CONTACTING_SERVER = 0x101; - private static final int ERROR_INVALID_PACKAGE_NAME = 0x102; - private static final int ERROR_NON_MATCHING_UID = 0x103; - - // Runs in IPC thread pool. Post it to the Handler, so we can guarantee - // either this or the timeout runs. - @Override - public void verifyLicense(final int responseCode, final String signedData, - final String signature) { - mHandler.post(() -> { - logger.i("Received response. (code: " + responseCode + ")"); - // Make sure it hasn't already timed out. - if (mChecksInProgress.contains(mValidator)) { - clearTimeout(); - mValidator.verify(mPublicKey, responseCode, signedData, signature); - finishCheck(mValidator); - } - if (DEBUG_LICENSE_ERROR) { - boolean logResponse; - String stringError = null; - switch (responseCode) { - case ERROR_CONTACTING_SERVER: - logResponse = true; - stringError = "ERROR_CONTACTING_SERVER"; - break; - case ERROR_INVALID_PACKAGE_NAME: - logResponse = true; - stringError = "ERROR_INVALID_PACKAGE_NAME"; - break; - case ERROR_NON_MATCHING_UID: - logResponse = true; - stringError = "ERROR_NON_MATCHING_UID"; - break; - default: - logResponse = false; - } - - if (logResponse) { - @SuppressLint("HardwareIds") String android_id = Secure.getString(mContext.getContentResolver(), - Secure.ANDROID_ID); - Date date = new Date(); - logger.d("Server Failure: " + stringError); - logger.d("Android ID: " + android_id); - logger.d("Time: " + date.toGMTString()); - } - } - - }); - } - - private void startTimeout() { - logger.i("Start monitoring timeout."); - mHandler.postDelayed(mOnTimeout, TIMEOUT_MS); - } - - private void clearTimeout() { - logger.i("Clearing timeout."); - mHandler.removeCallbacks(mOnTimeout); - } - } - - @Override - public synchronized void onServiceConnected(ComponentName name, IBinder service) { - mService = IService.Stub.asInterface(service); - runChecks(); - } - - @Override - public synchronized void onServiceDisconnected(ComponentName name) { - // Called when the connection with the service has been - // unexpectedly disconnected. That is, Market crashed. - // If there are any checks in progress, the timeouts will handle them. - logger.w("Service unexpectedly disconnected."); - mService = null; - } - - /** - * Generates policy response for service connection errors, as a result of disconnections or - * timeouts. - */ - private synchronized void handleServiceConnectionError(LicenseValidator validator) { - mPolicy.processServerResponse(Policy.RETRY, null); - - if (mPolicy.allowAccess()) { - validator.getCallback().allow(Policy.RETRY); - } else { - validator.getCallback().dontAllow(Policy.RETRY); - } - } - - /** Unbinds service if necessary and removes reference to it. */ - private void cleanupService() { - if (mService != null) { - try { - mContext.unbindService(mConn); - } catch (IllegalArgumentException e) { - // Somehow we've already been unbound. This is a non-fatal - // error. - logger.e("Unable to unbind from licensing service (already unbound)"); - } - mService = null; - } - } - - /** - * Inform the library that the hostContext is about to be destroyed, so that any open connections - * can be cleaned up. - *

- * Failure to call this method can result in a crash under certain circumstances, such as during - * screen rotation if an Activity requests the license check or when the user exits the - * application. - */ - public synchronized void onDestroy() { - cleanupService(); - mHandler.getLooper().quit(); - } - - /** Generates a nonce (number used once). */ - private int generateNonce() { - return RANDOM.nextInt(); - } - - /** - * Get version code for the application package name. - * - * @param context - * @param packageName application package name - * @return the version code or empty string if package not found - */ - private static String getVersionCode(Context context, String packageName) { - try { - return String.valueOf( - context.getPackageManager().getPackageInfo(packageName, 0).versionCode); - } catch (NameNotFoundException e) { - logger.e("Package not found. could not get version code."); - return ""; - } - } -} \ No newline at end of file -- cgit v1.2.3