Reputation: 15011
Background:
We have an Android app that is currently on sale via Google Play. For the app to function the user must purchase a "token" via In-App Billing. The "token" is a consumable item, eg used once and finished with. To verify the token, we send the purchase data to a server which uses standard Java RSA security code to verify the information returned from the Play Store is valid. (Code below). We did extensive testing prior to releasing the app, and even once the app is on the store, we did some more testing. The data being returned from Google passed verification every time. Then about the start of December the signature verification started failing. We haven't changed the code or the app in the store, and the verification code on the server has remained static.
I've debugged the code, and ran the receipt data and signature data being returned from the Play Store and it indeed now fails verification. I'm at a loss to explain what has changed, or why the verification started failing, when it was working fine.
Question:
Has anyone come across this before, where signature verification failed in an app that hasn't changed? Any tips on where to start looking to try and work out where the issues may be coming from?
Further Information
The only thing that I can think of changing, is Google released the in-app billing API v3, but that shouldn't effect V2, which is what we use.
To aide development, we use the net.robotmedia.billing library to handle the IAB.
Below is the server verification code for data returned from Play Store
where encodePublicKey => our public key from Play Store
signedData => base64 encoded receiptData as return from Play Store purchase
signature => signature as returned from 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;
}
}
Upvotes: 3
Views: 5245
Reputation: 5319
for me maybe file encoding was the problem, after changing eclipse workspace, it used mac file format again.
changing it to UTF-8 and copy&paste the key again into the project,
everything works fine now :/ wasted hours :/
Upvotes: 1
Reputation: 15011
Just an update. I never got to the bottom of why it stopped failing verification. We think it could be an issue with the Google Play servers and our Public Key.
Anyway the solution, as far as it is, is to implement the In-App Billing v3 Api (which is magnitudes nicer than the old version BTW) and it starting working again.
So, not really a definitive answer, but a fix as it were.
Upvotes: 0