应用内结算:哪些公钥和私钥用于应用内结算验证?

时间:2018-02-18 16:46:07

标签: android google-play in-app-purchase in-app-billing android-billing

使用应用内结算时,您应通过使用Google Play商店中的base64编码公钥检查INAPP_PURCHASE_DATA是否已使用INAPP_DATA_SIGNATURE进行签名来验证购买数据。
请参阅INAPP_PURCHASE_DATA和INAPP_DATA_SIGNATURE here的说明。 您可以使用Security class来验证购买:

public class Security {
    private static final String TAG = "IABUtil/Security";

    private static final String KEY_FACTORY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    /**
     * Verifies that the data was signed with the given signature, and returns
     * the verified purchase. The data is in JSON format and signed
     * with a private key. The data also contains the {@link PurchaseState}
     * and product ID of the 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
     */
    public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
        if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
                TextUtils.isEmpty(signature)) {
            Log.e(TAG, "Purchase verification failed: missing data.");
            return false;
        }

        PublicKey key = Security.generatePublicKey(base64PublicKey);
        return Security.verify(key, signedData, signature);
    }

    /**
     * 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, Base64.DEFAULT);
            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);
        }
    }

    /**
     * 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) {
        byte[] signatureBytes;
        try {
            signatureBytes = Base64.decode(signature, Base64.DEFAULT);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Base64 decoding failed.");
            return false;
        }
        try {
            Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initVerify(publicKey);
            sig.update(signedData.getBytes());
            if (!sig.verify(signatureBytes)) {
                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.");
        }
        return false;
    }
}

您必须调用verifyPurchase并从Google Play传递购买数据,给定签名和base64PublicKey公钥。我可以在我的应用中使用wrapper implementation购买流程 如果您查看IabHelper实现,它们会在构造函数中传递用于验证的公钥。 ctor的文档说:

 * @param base64PublicKey Your application's public key, encoded in base64.
 *     This is used for verification of purchase signatures. You can find your app's base64-encoded
 *     public key in your application's page on Google Play Developer Console. Note that this
 *     is NOT your "developer public key".
 */

我猜他们的意思是许可& amp;中的Base64编码的RSA公钥。 Google Play中的应用内结算部分:

pixelized key

也许我对密码学知之甚少,但是如何使用Google Play的公钥来检查加密,这可能是用我的“开发人员私钥”进行的(请参阅第一个链接中的说明)。他们是否意味着我“用于签署应用程序的私钥”?我不这么认为,因为他们无法知道我的(本地)私钥(我用来签署我的应用)以及它与Google Play的这个公钥有什么关系,所以他们对“开发者的私钥”有何意义”。
所以我的问题是:

  • 我是否理解公钥是关键所在 许可&应用内结算

  • 我是否还需要add licensing to my app来获取此信息 验证工作或应该“开箱即用”,我也可以 省略这一步?

  • 谷歌用于签署的“开发者私钥”是什么? 购买数据,我在哪里看到它? (我需要运行一些单元测试 在我的服务器上检查我的实现,我想加密 INAPP_PURCHASE_DATA也可以获得INAPP_DATA_SIGNATURE 能够获得有效的安全检查,如果我用给定的验证它 公钥。

[UPDATE] 即可。显然,私钥是隐藏的:

  

Google Play控制台会向登录Play控制台的任何开发者公开许可公钥,但会将所有用户的私钥隐藏在安全位置。

请参阅:https://developer.android.com/google/play/licensing/adding-licensing.html

1 个答案:

答案 0 :(得分:2)

有两种类型的签名不对称&对称:

Asymm使用一对密钥,私有密钥和公共密钥,密钥在它们之间具有数学关系,用私钥签名的一个数据块可以用公共密钥验证。私钥永远不会发布,但公众是。

然后Google为您的应用内结算创建了一对密钥...但您只需要知道公众进行验证。 没有私钥,任何机构都不会生成有效签名。

相反,Symm在两侧使用相同的密钥,这会导致共享密钥的问题与被嗅探的风险,但它的优势在于比asymm更快。

<强>更新

  

我是否还需要向我的应用添加许可才能获得此验证   工作或应该“开箱即用”,我可以省略这一步吗?

取决于,如果您想知道该应用是否已从官方Google Play商店安装,那么您需要验证许可,如果您的应用是付费应用,则会更好地适用,相反,如果您的应用是免费的,但它有 - 应用程序产品重要的是要知道他们是否合法购买了该项目。

对我而言,在外部服务器验证购买更为重要,那里有一个很好的例子https://stackoverflow.com/a/48645216/7690376