/// <summary> /// Verifies the response from server and calls appropriate callback method. /// </summary> /// <param name="publicKey"> /// public key associated with the developer account /// </param> /// <param name="responseCode"> /// server response code /// </param> /// <param name="signedData"> /// signed data from server /// </param> /// <param name="signature"> /// server signature /// </param> public void Verify(IPublicKey publicKey, ServerResponseCode responseCode, string signedData, string signature) { string userId = null; // Skip signature check for unsuccessful requests ResponseData data = null; if (responseCode == ServerResponseCode.Licensed || responseCode == ServerResponseCode.NotLicensed || responseCode == ServerResponseCode.LicensedOldKey) { // Verify signature. try { Signature sig = Signature.GetInstance(SignatureAlgorithm); sig.InitVerify(publicKey); sig.Update(new Java.Lang.String(signedData).GetBytes()); if (!sig.Verify(Convert.FromBase64String(signature))) { System.Diagnostics.Debug.WriteLine("Signature verification failed."); this.HandleInvalidResponse(); return; } } catch (NoSuchAlgorithmException e) { // This can't happen on an Android compatible device. throw new RuntimeException(e); } catch (InvalidKeyException) { this.HandleApplicationError(CallbackErrorCode.InvalidPublicKey); return; } catch (SignatureException e) { throw new RuntimeException(e); } catch (FormatException) { System.Diagnostics.Debug.WriteLine("Could not Base64-decode signature."); this.HandleInvalidResponse(); return; } // Parse and validate response. try { data = ResponseData.Parse(signedData); } catch (IllegalArgumentException) { System.Diagnostics.Debug.WriteLine("Could not parse response."); this.HandleInvalidResponse(); return; } if (data.ResponseCode != responseCode) { System.Diagnostics.Debug.WriteLine("Response codes don't match."); this.HandleInvalidResponse(); return; } if (data.NumberUsedOnce != this.numberUsedOnce) { System.Diagnostics.Debug.WriteLine("NumberUsedOnce doesn't match."); this.HandleInvalidResponse(); return; } if (data.PackageName != this.packageName) { System.Diagnostics.Debug.WriteLine("Package name doesn't match."); this.HandleInvalidResponse(); return; } if (data.VersionCode != this.versionCode) { System.Diagnostics.Debug.WriteLine("Version codes don't match."); this.HandleInvalidResponse(); return; } // Application-specific user identifier. userId = data.UserId; if (string.IsNullOrEmpty(userId)) { System.Diagnostics.Debug.WriteLine("User identifier is empty."); this.HandleInvalidResponse(); return; } } switch (responseCode) { case ServerResponseCode.Licensed: case ServerResponseCode.LicensedOldKey: PolicyServerResponse limiterResponse = this.deviceLimiter.IsDeviceAllowed(userId); this.HandleResponse(limiterResponse, data); break; case ServerResponseCode.NotLicensed: this.HandleResponse(PolicyServerResponse.NotLicensed, data); break; case ServerResponseCode.ErrorContactingServer: System.Diagnostics.Debug.WriteLine("Error contacting licensing server."); this.HandleResponse(PolicyServerResponse.Retry, data); break; case ServerResponseCode.ServerFailure: System.Diagnostics.Debug.WriteLine("An error has occurred on the licensing server."); this.HandleResponse(PolicyServerResponse.Retry, data); break; case ServerResponseCode.OverQuota: System.Diagnostics.Debug.WriteLine( "Licensing server is refusing to talk to this device, over quota."); this.HandleResponse(PolicyServerResponse.Retry, data); break; case ServerResponseCode.InvalidPackageName: this.HandleApplicationError(CallbackErrorCode.InvalidPackageName); break; case ServerResponseCode.NonMatchingUid: this.HandleApplicationError(CallbackErrorCode.ErrorNonMatchingUid); break; case ServerResponseCode.NotMarketManaged: this.HandleApplicationError(CallbackErrorCode.NotMarketManaged); break; default: System.Diagnostics.Debug.WriteLine("Unknown response code for license check."); this.HandleInvalidResponse(); break; } }
/// <summary> /// Parses response string into ResponseData. /// </summary> /// <param name="responseData"> /// response data string /// </param> /// <returns> /// ResponseData object /// </returns> /// <exception cref="ArgumentException"> /// upon parsing error /// </exception> public static ResponseData Parse(string responseData) { // Must parse out main response data and response-specific data. int index = responseData.IndexOf(':'); string mainData = responseData; string extraData = string.Empty; if (index != -1) { mainData = responseData.Substring(0, index); extraData = index < responseData.Length ? responseData.Substring(index + 1) : string.Empty; } string[] fields = mainData.Split('|'); if (fields.Length < 6) { throw new ArgumentException("Wrong number of fields."); } var data = new ResponseData { Extra = extraData, ResponseCode = (ServerResponseCode)Enum.Parse(typeof(ServerResponseCode), fields[0]), NumberUsedOnce = int.Parse(fields[1]), PackageName = fields[2], VersionCode = fields[3], // Application-specific user identifier. UserId = fields[4], TimeStamp = long.Parse(fields[5]) }; return data; }
/// <summary> /// Confers with policy and calls appropriate callback method. /// </summary> /// <param name="response"> /// The response. /// </param> /// <param name="rawData"> /// The raw Data. /// </param> private void HandleResponse(PolicyServerResponse response, ResponseData rawData) { // Update policy data and increment retry counter (if needed) this.policy.ProcessServerResponse(response, rawData); // Given everything we know, including cached data, ask the policy if we // should grant // access. if (this.policy.AllowAccess()) { this.licenseCheckerCallback.Allow(response); } else { this.licenseCheckerCallback.DontAllow(response); } }