title: Signature verification, Android in app billing failing after previously working
body: <p><em>Background:</em></p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p><em>Question:</em> </p>
  
  <p>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?</p>
  
  <p><em>Further Information</em></p>
  
  <p>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.</p>
  
  <p>To aide development, we use the net.robotmedia.billing library to handle the IAB.</p>
  
  <p>Below is the server verification code for data returned from Play Store </p>
  
  <p>where encodePublicKey => our public key from Play Store</p>
  
  <p>signedData => base64 encoded receiptData as return from Play Store purchase</p>
  
  <p>signature => signature as returned from Play Store</p>
  
  <pre><code>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;
      }
  
  }
  </code></pre>