commit
4aca1cd577
@ -0,0 +1,127 @@ |
|||||||
|
package net.pokeranalytics.android.util.billing |
||||||
|
|
||||||
|
/* |
||||||
|
* Copyright (c) 2012 Google Inc. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import android.text.TextUtils |
||||||
|
import android.util.Base64 |
||||||
|
import com.android.billingclient.util.BillingHelper |
||||||
|
import java.io.IOException |
||||||
|
import java.security.* |
||||||
|
import java.security.spec.InvalidKeySpecException |
||||||
|
import java.security.spec.X509EncodedKeySpec |
||||||
|
|
||||||
|
/** |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
object Security { |
||||||
|
|
||||||
|
private val TAG = "IABUtil/Security" |
||||||
|
|
||||||
|
private val KEY_FACTORY_ALGORITHM = "RSA" |
||||||
|
private val SIGNATURE_ALGORITHM = "SHA1withRSA" |
||||||
|
|
||||||
|
/** |
||||||
|
* Verifies that the data was signed with the given signature, and returns the verified |
||||||
|
* purchase. |
||||||
|
* @param base64PublicKey the base64-encoded public key to use for verifying. |
||||||
|
* @param signedData the signed JSON string (signed, not encrypted) |
||||||
|
* @param signature the signature for the data, signed with the private key |
||||||
|
* @throws IOException if encoding algorithm is not supported or key specification |
||||||
|
* is invalid |
||||||
|
*/ |
||||||
|
@Throws(IOException::class) |
||||||
|
fun verifyPurchase( |
||||||
|
base64PublicKey: String, signedData: String, |
||||||
|
signature: String |
||||||
|
): Boolean { |
||||||
|
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) |
||||||
|
|| TextUtils.isEmpty(signature) |
||||||
|
) { |
||||||
|
BillingHelper.logWarn(TAG, "Purchase verification failed: missing data.") |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
val key = generatePublicKey(base64PublicKey) |
||||||
|
return verify(key, signedData, signature) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a PublicKey instance from a string containing the Base64-encoded public key. |
||||||
|
* |
||||||
|
* @param encodedPublicKey Base64-encoded public key |
||||||
|
* @throws IOException if encoding algorithm is not supported or key specification |
||||||
|
* is invalid |
||||||
|
*/ |
||||||
|
@Throws(IOException::class) |
||||||
|
fun generatePublicKey(encodedPublicKey: String): PublicKey { |
||||||
|
try { |
||||||
|
val decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT) |
||||||
|
val keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM) |
||||||
|
return keyFactory.generatePublic(X509EncodedKeySpec(decodedKey)) |
||||||
|
} catch (e: NoSuchAlgorithmException) { |
||||||
|
// "RSA" is guaranteed to be available. |
||||||
|
throw RuntimeException(e) |
||||||
|
} catch (e: InvalidKeySpecException) { |
||||||
|
val msg = "Invalid key specification: $e" |
||||||
|
BillingHelper.logWarn(TAG, msg) |
||||||
|
throw IOException(msg) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 |
||||||
|
*/ |
||||||
|
fun verify(publicKey: PublicKey, signedData: String, signature: String): Boolean { |
||||||
|
val signatureBytes: ByteArray |
||||||
|
try { |
||||||
|
signatureBytes = Base64.decode(signature, Base64.DEFAULT) |
||||||
|
} catch (e: IllegalArgumentException) { |
||||||
|
BillingHelper.logWarn(TAG, "Base64 decoding failed.") |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
val signatureAlgorithm = Signature.getInstance(SIGNATURE_ALGORITHM) |
||||||
|
signatureAlgorithm.initVerify(publicKey) |
||||||
|
signatureAlgorithm.update(signedData.toByteArray()) |
||||||
|
if (!signatureAlgorithm.verify(signatureBytes)) { |
||||||
|
BillingHelper.logWarn(TAG, "Signature verification failed.") |
||||||
|
return false |
||||||
|
} |
||||||
|
return true |
||||||
|
} catch (e: NoSuchAlgorithmException) { |
||||||
|
// "RSA" is guaranteed to be available. |
||||||
|
throw RuntimeException(e) |
||||||
|
} catch (e: InvalidKeyException) { |
||||||
|
BillingHelper.logWarn(TAG, "Invalid key specification.") |
||||||
|
} catch (e: SignatureException) { |
||||||
|
BillingHelper.logWarn(TAG, "Signature exception.") |
||||||
|
} |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue