From e7d47565083b669883d7149fa911063921abd1af Mon Sep 17 00:00:00 2001 From: Javier Santos Date: Sun, 12 Mar 2017 02:30:21 +0100 Subject: [PATCH] Some changes to improve licensing --- ...icenseChecker.java => LibraryChecker.java} | 31 ++++++++++--------- ...lback.java => LibraryCheckerCallback.java} | 2 +- ...seValidator.java => LibraryValidator.java} | 28 ++++++++++------- .../github/javiersantos/licensing/Policy.java | 2 +- .../piracychecker/PiracyChecker.java | 10 +++--- 5 files changed, 39 insertions(+), 34 deletions(-) rename library/src/main/java/com/github/javiersantos/licensing/{LicenseChecker.java => LibraryChecker.java} (93%) rename library/src/main/java/com/github/javiersantos/licensing/{LicenseCheckerCallback.java => LibraryCheckerCallback.java} (98%) rename library/src/main/java/com/github/javiersantos/licensing/{LicenseValidator.java => LibraryValidator.java} (88%) diff --git a/library/src/main/java/com/github/javiersantos/licensing/LicenseChecker.java b/library/src/main/java/com/github/javiersantos/licensing/LibraryChecker.java similarity index 93% rename from library/src/main/java/com/github/javiersantos/licensing/LicenseChecker.java rename to library/src/main/java/com/github/javiersantos/licensing/LibraryChecker.java index 9d861a7..23dfa7f 100644 --- a/library/src/main/java/com/github/javiersantos/licensing/LicenseChecker.java +++ b/library/src/main/java/com/github/javiersantos/licensing/LibraryChecker.java @@ -42,6 +42,7 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; import java.util.HashSet; import java.util.LinkedList; @@ -49,7 +50,7 @@ import java.util.Set; /** - * Client library for Google Play license verifications.

The LicenseChecker is configured via a + * Client library for Google Play license verifications.

The LibraryChecker 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 @@ -57,8 +58,8 @@ * obtainable from the publisher site. */ @SuppressLint({"SimpleDateFormat", "HardwareIds"}) -public class LicenseChecker implements ServiceConnection { - private static final String TAG = "LicenseChecker"; +public class LibraryChecker implements ServiceConnection { + private static final String TAG = "LibraryChecker"; private static final String KEY_FACTORY_ALGORITHM = "RSA"; @@ -71,8 +72,8 @@ public class LicenseChecker implements ServiceConnection { private final Policy mPolicy; private final String mPackageName; private final String mVersionCode; - private final Set mChecksInProgress = new HashSet<>(); - private final Queue mPendingChecks = new LinkedList<>(); + private final Set mChecksInProgress = new HashSet<>(); + private final Queue mPendingChecks = new LinkedList<>(); private ILicensingService mService; private PublicKey mPublicKey; /** @@ -87,7 +88,7 @@ public class LicenseChecker implements ServiceConnection { * @param encodedPublicKey Base64-encoded RSA public key * @throws IllegalArgumentException if encodedPublicKey is invalid */ - public LicenseChecker(Context context, Policy policy, String encodedPublicKey) { + public LibraryChecker(Context context, Policy policy, String encodedPublicKey) { mContext = context; mPolicy = policy; mPublicKey = generatePublicKey(encodedPublicKey); @@ -144,14 +145,14 @@ private static String getVersionCode(Context context, String packageName) { * recommend obfuscating the string that is passed into bindService using another method of your * own devising.

source string: "com.android.vending.licensing.ILicensingService"

*/ - public synchronized void checkAccess(LicenseCheckerCallback callback) { + public synchronized void checkAccess(LibraryCheckerCallback callback) { // If we have a valid recent LICENSED response, we can skip asking // Market. if (mPolicy.allowAccess()) { Log.i(TAG, "Using cached license response"); callback.allow(Policy.LICENSED); } else { - LicenseValidator validator = new LicenseValidator(mPolicy, new NullDeviceLimiter(), + LibraryValidator validator = new LibraryValidator(mPolicy, new NullDeviceLimiter(), callback, generateNonce(), mPackageName, mVersionCode); if (mService == null) { @@ -194,7 +195,7 @@ public synchronized void checkAccess(LicenseCheckerCallback callback) { handleServiceConnectionError(validator); } } catch (SecurityException e) { - callback.applicationError(LicenseCheckerCallback.ERROR_MISSING_PERMISSION); + callback.applicationError(LibraryCheckerCallback.ERROR_MISSING_PERMISSION); } catch (Base64DecoderException e) { e.printStackTrace(); } @@ -206,7 +207,7 @@ public synchronized void checkAccess(LicenseCheckerCallback callback) { } private void runChecks() { - LicenseValidator validator; + LibraryValidator validator; while ((validator = mPendingChecks.poll()) != null) { try { Log.i(TAG, "Calling checkLicense on service for " + validator.getPackageName()); @@ -221,7 +222,7 @@ private void runChecks() { } } - private synchronized void finishCheck(LicenseValidator validator) { + private synchronized void finishCheck(LibraryValidator validator) { mChecksInProgress.remove(validator); if (mChecksInProgress.isEmpty()) { cleanupService(); @@ -245,7 +246,7 @@ public synchronized void onServiceDisconnected(ComponentName name) { * Generates policy response for service connection errors, as a result of disconnections or * timeouts. */ - private synchronized void handleServiceConnectionError(LicenseValidator validator) { + private synchronized void handleServiceConnectionError(LibraryValidator validator) { mPolicy.processServerResponse(Policy.RETRY, null); if (mPolicy.allowAccess()) { @@ -293,10 +294,10 @@ private class ResultListener extends ILicenseResultListener.Stub { 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; - private final LicenseValidator mValidator; + private final LibraryValidator mValidator; private Runnable mOnTimeout; - public ResultListener(LicenseValidator validator) { + public ResultListener(LibraryValidator validator) { mValidator = validator; mOnTimeout = new Runnable() { public void run() { @@ -318,7 +319,7 @@ public void run() { // Make sure it hasn't already timed out. if (mChecksInProgress.contains(mValidator)) { clearTimeout(); - mValidator.verify(mPublicKey, responseCode, signedData, signature); + mValidator.check(mPublicKey, responseCode, signedData, Calendar.getInstance(), signature); finishCheck(mValidator); } if (DEBUG_LICENSE_ERROR) { diff --git a/library/src/main/java/com/github/javiersantos/licensing/LicenseCheckerCallback.java b/library/src/main/java/com/github/javiersantos/licensing/LibraryCheckerCallback.java similarity index 98% rename from library/src/main/java/com/github/javiersantos/licensing/LicenseCheckerCallback.java rename to library/src/main/java/com/github/javiersantos/licensing/LibraryCheckerCallback.java index 3adfd57..25d08d8 100644 --- a/library/src/main/java/com/github/javiersantos/licensing/LicenseCheckerCallback.java +++ b/library/src/main/java/com/github/javiersantos/licensing/LibraryCheckerCallback.java @@ -26,7 +26,7 @@ * the policy, while in most cases Policy.NOT_LICENSED will call dontAllow and Policy.LICENSED will * Allow. */ -public interface LicenseCheckerCallback { +public interface LibraryCheckerCallback { /** * Application error codes. diff --git a/library/src/main/java/com/github/javiersantos/licensing/LicenseValidator.java b/library/src/main/java/com/github/javiersantos/licensing/LibraryValidator.java similarity index 88% rename from library/src/main/java/com/github/javiersantos/licensing/LicenseValidator.java rename to library/src/main/java/com/github/javiersantos/licensing/LibraryValidator.java index 34085bd..49242ae 100644 --- a/library/src/main/java/com/github/javiersantos/licensing/LicenseValidator.java +++ b/library/src/main/java/com/github/javiersantos/licensing/LibraryValidator.java @@ -27,12 +27,13 @@ import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; +import java.util.Calendar; /** - * Contains data related to a licensing request and methods to verify and process the response. + * Contains data related to a licensing request and methods to check and process the response. */ -class LicenseValidator { - private static final String TAG = "LicenseValidator"; +class LibraryValidator { + private static final String TAG = "LibraryValidator"; // Server response codes. private static final int LICENSED = 0x0; @@ -47,13 +48,13 @@ class LicenseValidator { private static final int ERROR_NON_MATCHING_UID = 0x103; private static final String SIGNATURE_ALGORITHM = "SHA1withRSA"; private final Policy mPolicy; - private final LicenseCheckerCallback mCallback; + private final LibraryCheckerCallback mCallback; private final int mNonce; private final String mPackageName; private final String mVersionCode; private final DeviceLimiter mDeviceLimiter; - LicenseValidator(Policy policy, DeviceLimiter deviceLimiter, LicenseCheckerCallback callback, + LibraryValidator(Policy policy, DeviceLimiter deviceLimiter, LibraryCheckerCallback callback, int nonce, String packageName, String versionCode) { mPolicy = policy; mDeviceLimiter = deviceLimiter; @@ -63,7 +64,7 @@ class LicenseValidator { mVersionCode = versionCode; } - public LicenseCheckerCallback getCallback() { + public LibraryCheckerCallback getCallback() { return mCallback; } @@ -83,11 +84,14 @@ public String getPackageName() { * @param signedData signed data from server * @param signature server signature */ - public void verify(PublicKey publicKey, int responseCode, String signedData, String signature) { + public void check(PublicKey publicKey, int responseCode, String signedData, Calendar calendar, String signature) { String userId = null; // Skip signature check for unsuccessful requests ResponseData data = null; - if (responseCode == LICENSED || responseCode == NOT_LICENSED || + + if (calendar == null) { + handleInvalidResponse(); + } else if (responseCode == LICENSED || responseCode == NOT_LICENSED || responseCode == LICENSED_OLD_KEY) { // Verify signature. try { @@ -111,7 +115,7 @@ public void verify(PublicKey publicKey, int responseCode, String signedData, Str // This can't happen on an Android compatible device. throw new RuntimeException(e); } catch (InvalidKeyException e) { - handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PUBLIC_KEY); + handleApplicationError(LibraryCheckerCallback.ERROR_INVALID_PUBLIC_KEY); return; } catch (Base64DecoderException e) { Log.e(TAG, "Could not Base64-decode signature."); @@ -183,13 +187,13 @@ public void verify(PublicKey publicKey, int responseCode, String signedData, Str handleResponse(Policy.RETRY, data); break; case ERROR_INVALID_PACKAGE_NAME: - handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PACKAGE_NAME); + handleApplicationError(LibraryCheckerCallback.ERROR_INVALID_PACKAGE_NAME); break; case ERROR_NON_MATCHING_UID: - handleApplicationError(LicenseCheckerCallback.ERROR_NON_MATCHING_UID); + handleApplicationError(LibraryCheckerCallback.ERROR_NON_MATCHING_UID); break; case ERROR_NOT_MARKET_MANAGED: - handleApplicationError(LicenseCheckerCallback.ERROR_NOT_MARKET_MANAGED); + handleApplicationError(LibraryCheckerCallback.ERROR_NOT_MARKET_MANAGED); break; default: Log.e(TAG, "Unknown response code for license check."); diff --git a/library/src/main/java/com/github/javiersantos/licensing/Policy.java b/library/src/main/java/com/github/javiersantos/licensing/Policy.java index 35612f5..3d65dd7 100644 --- a/library/src/main/java/com/github/javiersantos/licensing/Policy.java +++ b/library/src/main/java/com/github/javiersantos/licensing/Policy.java @@ -17,7 +17,7 @@ package com.github.javiersantos.licensing; /** - * Policy used by {@link LicenseChecker} to determine whether a user should have access to the + * Policy used by {@link LibraryChecker} to determine whether a user should have access to the * application. */ public interface Policy { diff --git a/library/src/main/java/com/github/javiersantos/piracychecker/PiracyChecker.java b/library/src/main/java/com/github/javiersantos/piracychecker/PiracyChecker.java index 3925dd2..aa05c02 100644 --- a/library/src/main/java/com/github/javiersantos/piracychecker/PiracyChecker.java +++ b/library/src/main/java/com/github/javiersantos/piracychecker/PiracyChecker.java @@ -1,8 +1,8 @@ package com.github.javiersantos.piracychecker; import com.github.javiersantos.licensing.AESObfuscator; -import com.github.javiersantos.licensing.LicenseChecker; -import com.github.javiersantos.licensing.LicenseCheckerCallback; +import com.github.javiersantos.licensing.LibraryChecker; +import com.github.javiersantos.licensing.LibraryCheckerCallback; import com.github.javiersantos.licensing.ServerManagedPolicy; import android.annotation.SuppressLint; @@ -92,7 +92,7 @@ public void dontAllow(PiracyCheckerError error) { } protected void verify(final PiracyCheckerCallback verifyCallback) { - // Library will verify first the non-LVL methods since LVL is asynchronous and could take + // Library will check first the non-LVL methods since LVL is asynchronous and could take // some seconds to give a result if (!verifySigningCertificate()) { verifyCallback.dontAllow(PiracyCheckerError.SIGNATURE_NOT_VALID); @@ -102,10 +102,10 @@ protected void verify(final PiracyCheckerCallback verifyCallback) { if (enableLVL) { String deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); - LicenseChecker licenseChecker = new LicenseChecker(context, new + LibraryChecker libraryChecker = new LibraryChecker(context, new ServerManagedPolicy(context, new AESObfuscator(LibraryUtils.SALT, context .getPackageName(), deviceId)), licenseBase64); - licenseChecker.checkAccess(new LicenseCheckerCallback() { + libraryChecker.checkAccess(new LibraryCheckerCallback() { @Override public void allow(int reason) { verifyCallback.allow();