Skip to content

java.lang.NoSuchMethodError for Base64.decodeBase64(String) #131

Closed

Description

@medavox

Hi, while using your library I encountered the following runtime exception, which crashes my app:

E/AndroidRuntime: FATAL EXCEPTION: main
	 Process: com.xxxx.yyyy, PID: 17510
	 java.lang.NoSuchMethodError: No static method decodeBase64(Ljava/lang/String;)[B in class Lorg/apache/commons/codec/binary/Base64; or its super classes (declaration of 'org.apache.commons.codec.binary.Base64' appears in /system/framework/org.apache.http.legacy.boot.jar)
		 at com.xxxx.yyyy.network.RealNetworkController$2.onResponse(RealNetworkController.java:172)
		 at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:68)
		 at android.os.Handler.handleCallback(Handler.java:739)
		 at android.os.Handler.dispatchMessage(Handler.java:95)
		 at android.os.Looper.loop(Looper.java:158)
		 at android.app.ActivityThread.main(ActivityThread.java:7224)
		 at java.lang.reflect.Method.invoke(Native Method)
		 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
		 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

This is caused by your use of the Apache Commons Codec library, in JWTDecoder.java:38:

headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[0]));
            payloadJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[1]));

I have investigated the issue, and I believe it's down to an obscure problem with Android.

The Problem

Until recently, Android was bundled with Apache's HttpClient, which depended on Commons Codec.

However, the version originally bundled with Android (1.3) does not contain the static decodeBase64(String) method. Furthermore, this ancient version overrides the version you specify in your lib's build.gradle.

For a better explanation, see this blog post, and these stack overflow posts:

  1. http://stackoverflow.com/a/34473643
  2. http://stackoverflow.com/a/5902269
  3. http://stackoverflow.com/questions/24306695/base64-dependencies-issue-in-android-studio

The reason you (the developers of java-jwt) may not have been experiencing this error and I have, is because this ancient dependency has been removed from android entirely, after SDK 23 (version 6.0), removing the problem.

Unfortunately for me, my app's minimum version is 4.4 (SDK 19), so I suspect that the legacy code is still in my classpath (this is corroborated by the exception message I pasted above:

declaration of 'org.apache.commons.codec.binary.Base64' appears in /system/framework/org.apache.http.legacy.boot.jar

This effectively gives your library an undocumented requirement for Android SDK 23 and above!

The Solution

Some people have been using a workaround, but I would recommend that you simply replace your Commons Codec call with the android-internal Base64 implementation. I will be submitting a pull request to this effect, if/when I have time.

TL;DR

You have an obscure bug to do with historical dependencies in Android on Apache Commons Codec.

I'll submit a PR when I can.

Activity

  1. hzalaz commented on Jan 6, 2017

    @hzalaz
    Contributor

    @medavox we didn't build this library to be used in Android apps

  2. medavox commented on Jan 6, 2017

    @medavox
    Author

    But it is, and has been. It's on jcenter, and it works (almost) without modification.

    If this is not OK then sorry, but i didn't know. It didn't feel too difficult to use, to be Java only. Was not obvious from my discovery path.

    Would you still accept a pull request to fix this that doesn't rely on android APIs, or should i find another library?

    EDIT: I've checked, and i can't see a separate listing for Android libraries on http://jwt.io/ .

    This means the main landing page for searches about JWT doesn't make a distinction between Java and Android JWT libraries, even though for you there is.

    Obviously, you don't have control of that site (unless you do), so you may wish to inform them of this.

  3. hzalaz commented on Jan 6, 2017

    @hzalaz
    Contributor

    There are more than android libraries in jcenter and not all are usable in an app since the JVM of android has some restrictions compared to regular JVM.

    I would accept a PR if it doesn't break any public API of the library and fixes the issue 😄

    Also just a quick note, we are not relying on any Android API, the error might be caused by something else, probably our base64 dependency that is not compatible with Android

  4. medavox commented on Jan 6, 2017

    @medavox
    Author

    I should really have put my above edit in another post, apologies.

    Thanks for your quick replies, I'll see if there is a mutually satisfactory fix.

  5. DuncanTyrell commented on Jan 10, 2017

    @DuncanTyrell

    @medavox did you find a fix to this yet? I've just hit the same issue and glad I checked in here. I've used other libs for handling JWTs on Android in the past and am wondering whether to abandon this in favour of one that works, or try and fix this one. If you fancy a catchup about the options some time message me duncan@tyrell.digital. We're also local by the way - I'm based up the road in Leeds.

  6. hzalaz commented on Jan 10, 2017

    @hzalaz
    Contributor

    @DuncanTyrell what do you need to do with the JWT? if it's only decode we already have a lib https://github.com/auth0/JWTDecode.Android to do that. It's unusual for an Android app to verify or even sign a token

  7. DuncanTyrell commented on Jan 10, 2017

    @DuncanTyrell

    Thanks for the swift response @hzalaz! I'll have a look at that library - much appreciated.

  8. DuncanTyrell commented on Jan 10, 2017

    @DuncanTyrell

    Yep that seems to do everything I need for now anyway @hzalaz - thanks again for your help and super-responsive turnaround.

  9. rakaadinugroho commented on Dec 25, 2017

    @rakaadinugroho

    i have solution for @medavox your problem, i have same problem. and to Sign with RSA256 Algorithm you must have Private Key. the Private Key must be PKCS#8 format. and save to Private.der , because Java cannot read private key except PKCS#8.

    and iam not using this library. i use this library https://github.com/jwtk/jjwt

  10. qwertukg commented on Jan 24, 2018

    @qwertukg

    Still have it on Spring application:

    java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.decodeBase64(Ljava/lang/String;)[B
    	at com.auth0.jwt.JWTDecoder.<init>(JWTDecoder.java:32)
    	at com.auth0.jwt.JWT.decode(JWT.java:21)
    	at com.auth0.jwt.JWTVerifier.verify(JWTVerifier.java:352)
    
  11. lbalmaceda commented on Jan 24, 2018

    @lbalmaceda
    Contributor

    @qwertukg please provide a repro project so we can narrow down the problem.

  12. qwertukg commented on Jan 24, 2018

    @qwertukg

    if u talking about repository of project, I can't to pass it. Maybe u need some more information ?

  13. lbalmaceda commented on Jan 24, 2018

    @lbalmaceda
    Contributor

    The original issue on this post is related to the library being used on an Android environment. Android is known to have a different Base64 implementation, and Apache Commons can't be used there because of the JVM. If you say the issue appears also on Spring that worries me, but if I don't know what you're doing I can't help you.
    I'm not talking about the original project where you're working on but a simple sample project you can create in the next few minutes and disclose to us where the issue can be reproduced, for us to narrow the cause. Please push it to a public repo. Without that I'll have to route you to https://support.auth0.com

  14. qwertukg commented on Jan 24, 2018

    @qwertukg

    Ok. Will try to reproduce it on a simple project tomorrow. Now just can to say what this project have a lot of dependencies and it's based on spring 3

  15. lbalmaceda commented on Jan 24, 2018

    @lbalmaceda
    Contributor

    Then it should work unless one of your project dependencies uses a different version of apache commons lib that doesn't have that class method. Try searching in the dependency tree if this is the case and see if removing that dependency fixes the problem.

  16. qwertukg commented on Jan 25, 2018

    @qwertukg

    It was commons-codec-1.2 there is no method with signature decodeBase64(String) only decodeBase64(Byte[])

  17. lbalmaceda commented on Jan 25, 2018

    @lbalmaceda
    Contributor

    @qwertukg Ah yes, that's from 2005. The latest version is the one we use.

  18. trajkovski731 commented on Jun 7, 2018

    @trajkovski731

    And did you fixed it? It has past a lot of time.

  19. lbalmaceda commented on Jun 7, 2018

    @lbalmaceda
    Contributor

    @trajkovski731 this library is not meant to be used on Android (we don't support it up-front). Is that your case or are you having a different error? If so, please post the details either here (if related) or in a separate new issue.

  20. nanangTriAndika commented on Jun 8, 2018

    @nanangTriAndika

    For anyone who struggle using apache common codec inline with android SDK ( especially when got conflict when using Base64 function ), you can use this library. Just pull and compiled the code or download through release tab for lazy programmer =P

    https://github.com/nanangTriAndika/customApacheCommonCodec

  21. qwertukg commented on Jun 11, 2018

    @qwertukg

    @trajkovski731 yep, i was fix it by excluding this lib from my dependency tree

  22. va26 commented on Feb 12, 2019

    @va26

    I am facing the same problem while trying to use the Google Speech-to-Text API given here. This is what I get in the logs:

        --------- beginning of crash
    02-12 12:35:22.504 29178 29178 E AndroidRuntime: FATAL EXCEPTION: main
    02-12 12:35:22.504 29178 29178 E AndroidRuntime: Process: com.google.cloud.examples.speechrecognition, PID: 29178
    02-12 12:35:22.504 29178 29178 E AndroidRuntime: java.lang.NoSuchMethodError: No static method decodeBase64(Ljava/lang/String;)[B in class Lorg/apache/commons/codec/binary/Base64; or its super classes (declaration of 'org.apache.commons.codec.binary.Base64' appears in /system/framework/org.apache.http.legacy.boot.jar)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.api.client.util.Base64.decodeBase64(Base64.java:101)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.api.client.util.PemReader.readNextSection(PemReader.java:106)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.api.client.util.PemReader.readFirstSectionAndClose(PemReader.java:135)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.auth.oauth2.ServiceAccountCredentials.privateKeyFromPkcs8(ServiceAccountCredentials.java:296)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.auth.oauth2.ServiceAccountCredentials.fromPkcs8(ServiceAccountCredentials.java:286)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.auth.oauth2.ServiceAccountCredentials.fromJson(ServiceAccountCredentials.java:210)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.auth.oauth2.GoogleCredentials.fromStream(GoogleCredentials.java:174)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.auth.oauth2.GoogleCredentials.fromStream(GoogleCredentials.java:141)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.examples.speechrecognition.MainActivity$mSpeechClient$2$1$1.getCredentials(MainActivity.kt:62)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.examples.speechrecognition.MainActivity$mSpeechClient$2$1$1.getCredentials(MainActivity.kt:45)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.api.gax.rpc.ClientContext.create(ClientContext.java:140)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.speech.v1.stub.GrpcSpeechStub.create(GrpcSpeechStub.java:94)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.speech.v1.stub.SpeechStubSettings.createStub(SpeechStubSettings.java:126)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.speech.v1.SpeechClient.<init>(SpeechClient.java:144)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.speech.v1.SpeechClient.create(SpeechClient.java:126)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.examples.speechrecognition.MainActivity$mSpeechClient$2.invoke(MainActivity.kt:61)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.examples.speechrecognition.MainActivity$mSpeechClient$2.invoke(MainActivity.kt:45)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.examples.speechrecognition.MainActivity.getMSpeechClient(Unknown Source:7)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.google.cloud.examples.speechrecognition.MainActivity.onResume(MainActivity.kt:101)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1363)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at android.app.Activity.performResume(Activity.java:7444)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3805)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3870)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1788)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at android.os.Handler.dispatchMessage(Handler.java:106)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at android.os.Looper.loop(Looper.java:164)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at android.app.ActivityThread.main(ActivityThread.java:7036)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at java.lang.reflect.Method.invoke(Native Method)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:441)
    02-12 12:35:22.504 29178 29178 E AndroidRuntime:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
    

    Please help me resolve this. Thank you.

  23. lbalmaceda commented on Feb 12, 2019

    @lbalmaceda
    Contributor

    @va26 As you see in your logs this is not related at all with a class from this SDK. You will have to report it in google's issue tracker instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions