8

我从我的崩溃报告中得到了这个异常:

java.lang.RuntimeException: Unable to start service com.problemio.BillingService@4132b868 with Intent { act=com.android.vending.billing.PURCHASE_STATE_CHANGED cmp=com.problemio/.BillingService (has extras) }: java.lang.IllegalArgumentException: utils.Base64DecoderException: single trailing character at offset 19
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2376)
at android.app.ActivityThread.access$1900(ActivityThread.java:123)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: utils.Base64DecoderException: single trailing character at offset 19
at utils.Security.generatePublicKey(Security.java:199)
at utils.Security.verifyPurchase(Security.java:118)
at com.problemio.BillingService.purchaseStateChanged(BillingService.java:545)
at com.problemio.BillingService.handleCommand(BillingService.java:421)
at com.problemio.BillingService.onStart(BillingService.java:398)
at android.app.Service.onStartCommand(Service.java:438)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2359)
... 10 more
Caused by: utils.Base64DecoderException: single trailing character at offset 19
at utils.Base64.decode(Base64.java:529)
at utils.Base64.decode(Base64.java:444)
at utils.Base64.decode(Base64.java:390)
at utils.Security.generatePublicKey(Security.java:189)
... 16 more
java.lang.IllegalArgumentException: utils.Base64DecoderException: single trailing character at offset 19
at utils.Security.generatePublicKey(Security.java:199)
at utils.Security.verifyPurchase(Security.java:118)
at com.problemio.BillingService.purchaseStateChanged(BillingService.java:545)
at com.problemio.BillingService.handleCommand(BillingService.java:421)
at com.problemio.BillingService.onStart(BillingService.java:398)
at android.app.Service.onStartCommand(Service.java:438)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2359)
at android.app.ActivityThread.access$1900(ActivityThread.java:123)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: utils.Base64DecoderException: single trailing character at offset 19
at utils.Base64.decode(Base64.java:529)
at utils.Base64.decode(Base64.java:444)
at utils.Base64.decode(Base64.java:390)
at utils.Security.generatePublicKey(Security.java:189)
... 16 more
utils.Base64DecoderException: single trailing character at offset 19
at utils.Base64.decode(Base64.java:529)
at utils.Base64.decode(Base64.java:444)
at utils.Base64.decode(Base64.java:390)
at utils.Security.generatePublicKey(Security.java:189)
at utils.Security.verifyPurchase(Security.java:118)
at com.problemio.BillingService.purchaseStateChanged(BillingService.java:545)
at com.problemio.BillingService.handleCommand(BillingService.java:421)
at com.problemio.BillingService.onStart(BillingService.java:398)
at android.app.Service.onStartCommand(Service.java:438)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2359)
at android.app.ActivityThread.access$1900(ActivityThread.java:123)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)

但我不太明白问题出在哪里。有什么建议么?

它指向这个方法:

  /**
   * Decodes Base64 content using the supplied decodabet and returns
   * the decoded byte array.
   *
   * @param source the Base64 encoded data
   * @param off the offset of where to begin decoding
   * @param len the length of characters to decode
   * @param decodabet the decodabet for decoding Base64 content
   * @return decoded data
   */
  public static byte[] decode(byte[] source, int off, int len, byte[] decodabet)
      throws Base64DecoderException {
    int len34 = len * 3 / 4;
    byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
    int outBuffPosn = 0;

    byte[] b4 = new byte[4];
    int b4Posn = 0;
    int i = 0;
    byte sbiCrop = 0;
    byte sbiDecode = 0;
    for (i = 0; i < len; i++) {
      sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits
      sbiDecode = decodabet[sbiCrop];

      if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better
        if (sbiDecode >= EQUALS_SIGN_ENC) {
          // An equals sign (for padding) must not occur at position 0 or 1
          // and must be the last byte[s] in the encoded value
          if (sbiCrop == EQUALS_SIGN) {
            int bytesLeft = len - i;
            byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
            if (b4Posn == 0 || b4Posn == 1) {
              throw new Base64DecoderException(
                  "invalid padding byte '=' at byte offset " + i);
            } else if ((b4Posn == 3 && bytesLeft > 2)
                || (b4Posn == 4 && bytesLeft > 1)) {
              throw new Base64DecoderException(
                  "padding byte '=' falsely signals end of encoded value "
                      + "at offset " + i);
            } else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
              throw new Base64DecoderException(
                  "encoded value has invalid trailing byte");
            }
            break;
          }

          b4[b4Posn++] = sbiCrop;
          if (b4Posn == 4) {
            outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
            b4Posn = 0;
          }
        }
      } else {
        throw new Base64DecoderException("Bad Base64 input character at " + i
            + ": " + source[i + off] + "(decimal)");
      }
    }

    // Because web safe encoding allows non padding base64 encodes, we
    // need to pad the rest of the b4 buffer with equal signs when
    // b4Posn != 0.  There can be at most 2 equal signs at the end of
    // four characters, so the b4 buffer must have two or three
    // characters.  This also catches the case where the input is
    // padded with EQUALS_SIGN
    if (b4Posn != 0) {
      if (b4Posn == 1) {
        throw new Base64DecoderException("single trailing character at offset "
            + (len - 1));
      }
      b4[b4Posn++] = EQUALS_SIGN;
      outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
    }

    byte[] out = new byte[outBuffPosn];
    System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
    return out;
  }

错误似乎指向我的 BillingService.java 中的这些行

at com.problemio.BillingService.purchaseStateChanged(BillingService.java:585)
at com.problemio.BillingService.handleCommand(BillingService.java:461)
at com.problemio.BillingService.onStart(BillingService.java:438)

585上的线是线

purchases = Security.verifyPurchase(signedData, signature);

在这种方法中:

private void purchaseStateChanged(int startId, String signedData, String signature) {
    ArrayList<Security.VerifiedPurchase> purchases;
    purchases = Security.verifyPurchase(signedData, signature);
    if (purchases == null) {
        return;
    }

第 461 行是这一行:

purchaseStateChanged(startId, signedData, signature);

在这种方法中:

public void handleCommand(Intent intent, int startId) {
    String action = intent.getAction();
    if (Consts.DEBUG) {
        Log.i(TAG, "handleCommand() action: " + action);
    }
    if (Consts.ACTION_CONFIRM_NOTIFICATION.equals(action)) {
        String[] notifyIds = intent.getStringArrayExtra(Consts.NOTIFICATION_ID);
        confirmNotifications(startId, notifyIds);
    } else if (Consts.ACTION_GET_PURCHASE_INFORMATION.equals(action)) {
        String notifyId = intent.getStringExtra(Consts.NOTIFICATION_ID);
        getPurchaseInformation(startId, new String[] { notifyId });
    } else if (Consts.ACTION_PURCHASE_STATE_CHANGED.equals(action)) {
        String signedData = intent.getStringExtra(Consts.INAPP_SIGNED_DATA);
        String signature = intent.getStringExtra(Consts.INAPP_SIGNATURE);
        purchaseStateChanged(startId, signedData, signature);
    } else if (Consts.ACTION_RESPONSE_CODE.equals(action)) {
        long requestId = intent.getLongExtra(Consts.INAPP_REQUEST_ID, -1);
        int responseCodeIndex = intent.getIntExtra(Consts.INAPP_RESPONSE_CODE,
                ResponseCode.RESULT_ERROR.ordinal());
        ResponseCode responseCode = ResponseCode.valueOf(responseCodeIndex);
        checkResponseCode(requestId, responseCode);
    }
}

第 438 行是这一行:

handleCommand(intent, startId);

在这种方法中:

@Override
public void onStart(Intent intent, int startId) {
    handleCommand(intent, startId);
}

谢谢!

4

3 回答 3

4

这种情况是一直发生还是只发生一次/几次?您获得的签名purchaseStateChanged(startId, signedData, signature)也是 Base64 编码的,错误表示编码错误。如果它只发生一次或两次,则可能是 Google Play IAP 服务器出现故障。

于 2012-07-18T02:22:39.697 回答
1

对我来说,这是因为我使用了错误的公钥!确保您使用的公钥是您从市场上获得的公钥。

于 2015-10-18T10:44:44.910 回答
-2

我在构造函数
IabHelper(Context ctx, String base64PublicKey)时看到了上面的错误

被赋予了不正确的密钥。
解决办法是:
去Google Play,开发者控制台网页。
选择应用程序,
然后在“服务和 API”下,您将看到“您的此应用程序的许可证密钥”
使用该字符串。


另一种解决方案是进入 Security.java 文件并进行一些更改。请参阅我有评论“// ADD_THIS_LINE”的行

    public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
    if (signedData == null) {
        Log.e(TAG, "data is null");
        return false;
    }

    boolean verified = false;
    if (!TextUtils.isEmpty(signature)) {
        PublicKey key = Security.generatePublicKey(base64PublicKey);

        if( key != null) // ADD_THIS_LINE
            verified = Security.verify(key, signedData, signature);

        if (!verified) {
            Log.w(TAG, "signature does not match data.");
            return false;
        }
    }
    return true;
}    
////////////////////////////////////////////////////////////////////////
//  :     :     
///////////////////////////////////////////////////////////////////////
 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) {
        Log.e(TAG, "Invalid key specification.");
        throw new IllegalArgumentException(e);
    } catch (Base64DecoderException e) {
        Log.e(TAG, "Base64 decoding failed.");
        return null;  // ADD_THIS_LINE
        // COMMENT_OUT_THIS_LINE:   throw new IllegalArgumentException(e);
    }
}


//////////////////////////////////////////////////////////////
于 2013-04-18T09:28:42.100 回答