签名验证,在先前工作后,Android应用内计费失败

3

背景:

我们有一个Android应用程序,目前通过Google Play销售。为了使应用程序正常运行,用户必须通过应用内支付购买一个“令牌”。这个“令牌”是一次性的,使用一次后就不能再用了。为了验证该令牌,我们将购买数据发送到服务器,并使用标准的Java RSA安全代码来验证从Play商店返回的信息是否有效(如下所示)。 我们在发布应用程序之前进行了广泛的测试,即使应用程序已经上架,我们也进行了更多的测试。从Google返回的数据每次都通过了验证。然后,在12月初左右,签名验证开始失败。我们没有改变代码或商店中的应用程序,服务器上的验证代码也保持不变。

我调试了代码,并运行从Play Store返回的收据数据和签名数据,确实无法通过验证。我不知道发生了什么变化,或者为什么会出现验证失败的情况。

问题:

有人遇到过这种情况吗?即应用程序未更改时签名验证失败?您有哪些提示可以提供,以便开始查找问题的原因?

更多信息:

我唯一想到的更改是,Google发布了应用内支付API v3,但这不应影响我们使用的V2。

为了辅助开发,我们使用net.robotmedia.billing库来处理应用内支付。

下面是从Play商店返回的数据的服务器验证代码:

其中,encodePublicKey => 是我们从Play Store获取的公钥

signedData => 是从Play Store购买时返回的base64编码的收据数据

signature => 是从Play Store返回的签名

public class Security {

    public final static Logger logger = Logger.getLogger(Security.class.getName());

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

    /**
     * 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) {
            logger.error("Invalid key specification.", e);
            throw new IllegalArgumentException(e);
        }
        catch (Base64DecoderException e) {
            logger.error("Base64 decoding failed.", e);
            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) {
        Signature sig;
        try {
            sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initVerify(publicKey);
            sig.update(signedData.getBytes());
            byte[] decodedSig = Base64.decode(signature);
            if (!sig.verify(decodedSig)) {
                logger.error("Signature verification failed.");
                return false;
            }
            return true;
        }
        catch (NoSuchAlgorithmException e) {
            logger.error("NoSuchAlgorithmException.");
        }
        catch (InvalidKeyException e) {
            logger.error("Invalid key specification.");
        }
        catch (SignatureException e) {
            logger.error("Signature exception.");
        }
        catch (Base64DecoderException e) {
            logger.error("Base64 decoding failed.");
        }
        return false;
    }

}

也许现在已经使用新的每个应用程序密钥进行签名了?您尝试过使用该密钥进行验证吗?如果是这样,那对于将密钥嵌入应用程序中的人来说将是一个严重的问题。 - Nikolay Elenkov
密钥是相同的 - 旧的每个开发人员密钥似乎已经被继承了。 - JamesSugrue
我之前没有注意到,但你是对的。你的代码看起来没问题,除了一些编码问题(getBytes()等),它应该能够工作。剩下的问题可能是PlayStore服务器上的一些错误,你可以尝试联系他们。 - Nikolay Elenkov
可能是键匹配了,但版本号和字符串不匹配。 - Cory Trese
2个回答

1
对我来说,可能文件编码是问题所在, 改变了Eclipse工作空间后,它再次使用了Mac文件格式。
将其改为UTF-8并将密钥重新复制粘贴到项目中, 现在一切都正常了 :/ 浪费了几个小时 :/

我浪费了一天半的时间。根据文档,我将密钥分成两部分,因为文档说不应该让某个人轻易地猜出密钥是什么。我确保编码为utf-8,并将密钥作为单个字符串粘贴,结果它起作用了。哇... - bitrock

0

更新一下。我从未找到它为什么停止失败验证的原因。我们认为这可能是与Google Play服务器和我们的公钥有关的问题。

无论如何,解决方案就是实现In-App Billing v3 Api(顺便说一句,这个新版本比旧版本好多了),然后它就可以正常工作了。

所以,这并不是一个明确的答案,但可以算是一个修复方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接