summaryrefslogtreecommitdiff
path: root/src/org/fox/ttrss/billing/BillingSecurity.java
diff options
context:
space:
mode:
authorAndrew Dolgov <fox@madoka.volgo-balt.ru>2012-08-13 16:00:27 +0400
committerAndrew Dolgov <fox@madoka.volgo-balt.ru>2012-08-13 16:00:27 +0400
commit77352728e7085bf9883c2c7d94501bce1ab8e4cf (patch)
tree8502f62777c5e12c35863f50b90b1c41e13bbfdc /src/org/fox/ttrss/billing/BillingSecurity.java
parente68c90cbceab13193597f203ad9f15a6d919af14 (diff)
remove in-app billing
add spinner-like triangles for headlines fragment context menu bump version
Diffstat (limited to 'src/org/fox/ttrss/billing/BillingSecurity.java')
-rw-r--r--src/org/fox/ttrss/billing/BillingSecurity.java258
1 files changed, 0 insertions, 258 deletions
diff --git a/src/org/fox/ttrss/billing/BillingSecurity.java b/src/org/fox/ttrss/billing/BillingSecurity.java
deleted file mode 100644
index 513d6f34..00000000
--- a/src/org/fox/ttrss/billing/BillingSecurity.java
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-
-package org.fox.ttrss.billing;
-
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.ArrayList;
-import java.util.HashSet;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import org.fox.ttrss.billing.BillingConstants.PurchaseState;
-import org.fox.ttrss.util.Base64;
-import org.fox.ttrss.util.Base64DecoderException;
-
-/**
- * Security-related methods. For a secure implementation, all of this code
- * should be implemented on a server that communicates with the application on
- * the device. For the sake of simplicity and clarity of this example, this code
- * is included here and is executed on the device. If you must verify the
- * purchases on the phone, you should obfuscate this code to make it harder for
- * an attacker to replace the code with stubs that treat all purchases as
- * verified.
- */
-public class BillingSecurity {
- private static final String TAG = "BillingService";
-
- private static final String KEY_FACTORY_ALGORITHM = "RSA";
- private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
- private static final SecureRandom RANDOM = new SecureRandom();
-
- /**
- * This keeps track of the nonces that we generated and sent to the server.
- * We need to keep track of these until we get back the purchase state and
- * send a confirmation message back to Android Market. If we are killed and
- * lose this list of nonces, it is not fatal. Android Market will send us a
- * new "notify" message and we will re-generate a new nonce. This has to be
- * "static" so that the {@link BillingReceiver} can check if a nonce exists.
- */
- private static HashSet<Long> sKnownNonces = new HashSet<Long>();
-
- /**
- * A class to hold the verified purchase information.
- */
- public static class VerifiedPurchase {
- public PurchaseState purchaseState;
- public String notificationId;
- public String productId;
- public String orderId;
- public long purchaseTime;
- public String developerPayload;
-
- public VerifiedPurchase(PurchaseState purchaseState, String notificationId, String productId, String orderId, long purchaseTime,
- String developerPayload) {
- this.purchaseState = purchaseState;
- this.notificationId = notificationId;
- this.productId = productId;
- this.orderId = orderId;
- this.purchaseTime = purchaseTime;
- this.developerPayload = developerPayload;
- }
-
- public boolean isPurchased(){
- return purchaseState.equals(PurchaseState.PURCHASED);
- }
-
-
- }
-
- /** Generates a nonce (a random number used once). */
- public static long generateNonce() {
- long nonce = RANDOM.nextLong();
- Log.i(TAG, "Nonce generateD: "+nonce);
- sKnownNonces.add(nonce);
- return nonce;
- }
-
- public static void removeNonce(long nonce) {
- sKnownNonces.remove(nonce);
- }
-
- public static boolean isNonceKnown(long nonce) {
- return sKnownNonces.contains(nonce);
- }
-
- /**
- * Verifies that the data was signed with the given signature, and returns
- * the list of verified purchases. The data is in JSON format and contains a
- * nonce (number used once) that we generated and that was signed (as part
- * of the whole data string) with a private key. The data also contains the
- * {@link PurchaseState} and product ID of the purchase. In the general
- * case, there can be an array of purchase transactions because there may be
- * delays in processing the purchase on the backend and then several
- * purchases can be batched together.
- *
- * @param signedData
- * the signed JSON string (signed, not encrypted)
- * @param signature
- * the signature for the data, signed with the private key
- */
- public static ArrayList<VerifiedPurchase> verifyPurchase(String signedData, String signature) {
- if (signedData == null) {
- Log.e(TAG, "data is null");
- return null;
- }
- Log.i(TAG, "signedData: " + signedData);
- boolean verified = false;
- if (!TextUtils.isEmpty(signature)) {
- /**
- * Compute your public key (that you got from the Android Market
- * publisher site).
- *
- * Instead of just storing the entire literal string here embedded
- * in the program, construct the key at runtime from pieces or use
- * bit manipulation (for example, XOR with some other string) to
- * hide the actual key. The key itself is not secret information,
- * but we don't want to make it easy for an adversary to replace the
- * public key with one of their own and then fake messages from the
- * server.
- *
- * Generally, encryption keys / passwords should only be kept in
- * memory long enough to perform the operation they need to perform.
- */
- String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApLWBv8eFC4f7h6gz3VE87XX2nqJB2KL2yNnNawmgaL/0nd6nXvVRiZ3iXLLP9k8RpLJ6rZPV778z8WzDLZATV3b2nh21KgjSNoG4em1oSf7pW4+AujqjLfNVRsXoJIWG+OMMd9o9l/D2YJTCXzSvgFIfF5EJRg6APZHEVrVJo8iXwnYM1tFfLjPfp10MtjLmD5tZW8o3hTmXJ3ZMDI12PL22G4KaE+BuQqI6PZ22m/pA85R6AuhNo2IUSE4XFUE8i7ANWDvdfDzQ5J0TTWAeHmUQCstdZ48z+6AjqD3L2omS/dKoBnlYxEUZms3iUa1/Co40nWU7sc2hqpmfNiG5oQIDAQAB";
- PublicKey key = BillingSecurity.generatePublicKey(base64EncodedPublicKey);
- verified = BillingSecurity.verify(key, signedData, signature);
- if (!verified) {
- Log.w(TAG, "signature does not match data.");
- return null;
- }
- }
-
- JSONObject jObject;
- JSONArray jTransactionsArray = null;
- int numTransactions = 0;
- long nonce = 0L;
- try {
- jObject = new JSONObject(signedData);
-
- // The nonce might be null if the user backed out of the buy page.
- nonce = jObject.optLong("nonce");
- jTransactionsArray = jObject.optJSONArray("orders");
- if (jTransactionsArray != null) {
- numTransactions = jTransactionsArray.length();
- }
- } catch (JSONException e) {
- return null;
- }
-
- if (!BillingSecurity.isNonceKnown(nonce)) {
- Log.w(TAG, "Nonce not found: " + nonce);
- return null;
- }
-
- ArrayList<VerifiedPurchase> purchases = new ArrayList<VerifiedPurchase>();
- try {
- for (int i = 0; i < numTransactions; i++) {
- JSONObject jElement = jTransactionsArray.getJSONObject(i);
- int response = jElement.getInt("purchaseState");
- PurchaseState purchaseState = PurchaseState.valueOf(response);
- String productId = jElement.getString("productId");
- String packageName = jElement.getString("packageName");
- long purchaseTime = jElement.getLong("purchaseTime");
- String orderId = jElement.optString("orderId", "");
- String notifyId = null;
- if (jElement.has("notificationId")) {
- notifyId = jElement.getString("notificationId");
- }
- String developerPayload = jElement.optString("developerPayload", null);
-
- // If the purchase state is PURCHASED, then we require a
- // verified nonce.
- if (purchaseState == PurchaseState.PURCHASED && !verified) {
- continue;
- }
- purchases.add(new VerifiedPurchase(purchaseState, notifyId, productId, orderId, purchaseTime, developerPayload));
- }
- } catch (JSONException e) {
- Log.e(TAG, "JSON exception: ", e);
- return null;
- }
- removeNonce(nonce);
- return purchases;
- }
-
- /**
- * 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) {
- Log.e(TAG, "Invalid key specification.");
- throw new IllegalArgumentException(e);
- } catch (Base64DecoderException e) {
- Log.e(TAG, "Base64DecoderException.", e);
- return null;
- }
- }
-
- /**
- * 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) {
- Log.i(TAG, "signature: " + signature);
- Signature sig;
- try {
- sig = Signature.getInstance(SIGNATURE_ALGORITHM);
- sig.initVerify(publicKey);
- sig.update(signedData.getBytes());
- if (!sig.verify(Base64.decode(signature))) {
- 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 (Base64DecoderException e) {
- Log.e(TAG, "Base64DecoderException.", e);
- }
- return false;
- }
-}