diff options
| author | Andrew Dolgov <fox@madoka.volgo-balt.ru> | 2012-08-13 16:00:27 +0400 |
|---|---|---|
| committer | Andrew Dolgov <fox@madoka.volgo-balt.ru> | 2012-08-13 16:00:27 +0400 |
| commit | 77352728e7085bf9883c2c7d94501bce1ab8e4cf (patch) | |
| tree | 8502f62777c5e12c35863f50b90b1c41e13bbfdc /src/org/fox/ttrss/billing/BillingSecurity.java | |
| parent | e68c90cbceab13193597f203ad9f15a6d919af14 (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.java | 258 |
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; - } -} |