Search code examples
androidnoclassdeffounderrorbouncycastlegenymotionverifyerror

BouncyCastle PEMParser throws NoClassDefFound and VerifyError using GenyMotion Emulator


Due to Samsung devices being utterly terrible, I have switched to use GenyMotion as an Android emulator. However, code that worked on the Samsung Note I own crashes on the emulator with a NoClassDefFoundError.

06-10 09:02:15.630: E/AndroidRuntime(2231): FATAL EXCEPTION: main
06-10 09:02:15.630: E/AndroidRuntime(2231): java.lang.NoClassDefFoundError: org.bouncycastle.asn1.ASN1Primitive
06-10 09:02:15.630: E/AndroidRuntime(2231):     at org.bouncycastle.pkcs.PKCS10CertificationRequest.parseBytes(Unknown Source)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at org.bouncycastle.pkcs.PKCS10CertificationRequest.<init>(Unknown Source)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at org.bouncycastle.openssl.PEMParser$PKCS10CertificationRequestParser.parseObject(Unknown Source)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at org.bouncycastle.openssl.PEMParser.readObject(Unknown Source)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at com.example.MyFragment.convertPemToPKCS10CertificationRequest(MyFragment.java:254)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at com.example.MyFragment.testReadCertificateSigningRequest(MyFragment.java:145)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at com.example.MyFragment.onClick(MyFragment.java:87)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at android.view.View.performClick(View.java:2485)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at android.view.View$PerformClick.run(View.java:9080)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at android.os.Handler.handleCallback(Handler.java:587)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at android.os.Handler.dispatchMessage(Handler.java:92)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at android.os.Looper.loop(Looper.java:130)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at android.app.ActivityThread.main(ActivityThread.java:3683)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at java.lang.reflect.Method.invokeNative(Native Method)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at java.lang.reflect.Method.invoke(Method.java:507)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
06-10 09:02:15.630: E/AndroidRuntime(2231):     at dalvik.system.NativeStart.main(Native Method)

I've tried different versions too, it didn't really help, in fact, the PEMParser was not even available in 1.47 apparently. The code is the following:

   private final String csrPEM = "-----BEGIN CERTIFICATE REQUEST-----\n"
         + "MIICwjCCAaoCAQAwfTELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0Zsb3JpZGExEzAR\n"
         + "BgNVBAcTCkJvY2EgUmF0b24xGzAZBgNVBAoTEkxvb25leSBUb29ucywgSW5jLjEU\n"
         + "MBIGA1UECxMLRGV2ZWxvcG1lbnQxFDASBgNVBAMTC2V4YW1wbGUuY29tMIIBIjAN\n"
         + "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiJCj31d1Rp+aKz/GTkedaiS/VSCc\n"
         + "PRARYgXukobjgBHx46HjldAcfg/DoANn5lEQaFxaIZJLbZ/AdLUyw/hUbU0CjWXv\n"
         + "pN3Ep3o9XgRTPkIFoI22VOI/O2ZLjBq/E4DWyVmv+vG6BK0LRh7hykzPCw6KIRR9\n"
         + "NCmUMJMQX5d5P/r1lR5H399pnLcLsrHoWDwBSEDgkGWyxnvEB0+/bIz42T3qnlFt\n"
         + "7avarxlHG2p5DoRTf8GJ+6imY88ZeBW/Nk18aDINsAHWLv383JICIAsZ3VuMk8m/\n"
         + "Z/Z5b21zIuZECDJjZjvAAjr/shVLB+Pck5+HJy6tqj79MJOQu+jKIrK8VwIDAQAB\n"
         + "oAAwDQYJKoZIhvcNAQEFBQADggEBAGtuAAHG4OC9jSRjGWSqfMXTDMz9tgekDREA\n"
         + "SYv5QIrOXsMzwbgDw8LxRJZEskl4JJOnjwEvUXWUF1M6XmG2h358nOnrkOlsumHw\n"
         + "Tx5gGSr6S6aJO/HG46erctE8aWpnFZYMfuEkul4ApsIufL7Bxqs3NHZWcrWBlLIP\n"
         + "aVCKx1FPRMC36Tj3EslbuUB/iTRt90Nfq1IxHMIKiwCiSNJSqfRVLANhI8MUbOjB\n"
         + "CBly1wcH68WWNkyvHVvbcF/B9AfYG9AqWjZjygKpyf81VZWctXhDc8UtomqrblXN\n"
         + "mvz4RKpIhZQLuuxlBrdzJkPm2sOdtdZghebCRRVWdjsig4sylgQ=\n"
         + "-----END CERTIFICATE REQUEST-----";

   public void testReadCertificateSigningRequest()
   {

      PKCS10CertificationRequest csr = convertPemToPKCS10CertificationRequest(csrPEM);
      if (csr == null)
      {
         System.out.println("FAIL!");
      }
      else
      {
         X500Name x500Name = csr.getSubject();
         System.out.println("x500Name is: " + x500Name + "\n");

         // country is 2.5.4.6
         System.out.println("COUNTRY: " + getX500Field(COUNTRY, x500Name));
         // state is 2.5.4.8
         System.out.println("STATE: " + getX500Field(STATE, x500Name));
         // locale is 2.5.4.7
         System.out.println("LOCALE: " + getX500Field(LOCALE, x500Name));
      }
   }

   private String getX500Field(String asn1ObjectIdentifier, X500Name x500Name)
   {
      RDN[] rdnArray = x500Name.getRDNs(new ASN1ObjectIdentifier(asn1ObjectIdentifier));
      String retVal = null;
      for (RDN item : rdnArray)
      {
         retVal = item.getFirst().getValue().toString();
      }

      return retVal;
   }


   private PKCS10CertificationRequest convertPemToPKCS10CertificationRequest(String pem)
   {
      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
      PKCS10CertificationRequest csr = null;
      ByteArrayInputStream pemStream = null;
      try
      {
         pemStream = new ByteArrayInputStream(pem.getBytes("UTF-8"));
      }
      catch (UnsupportedEncodingException ex)
      {
         Log.e(getClass().getSimpleName(), "UnsupportedEncodingException, convertPemToPublicKey", ex);
      }

      Reader pemReader = new BufferedReader(new InputStreamReader(pemStream));
      PEMParser pemParser = null;
      try
      {
         pemParser = new PEMParser(pemReader);
         Object parsedObj = pemParser.readObject();
         System.out.println("PemParser returned: " + parsedObj);
         if (parsedObj instanceof PKCS10CertificationRequest)
         {
            csr = (PKCS10CertificationRequest) parsedObj;
         }
      }
      catch (IOException ex)
      {
         Log.e(getClass().getSimpleName(), "IOException, convertPemToPublicKey", ex);
      }
      finally
      {
         if (pemParser != null)
         {
            IOUtils.closeQuietly(pemParser);
         }
      }
      return csr;
   }

And I'm also getting VerifyError...

06-10 09:09:18.920: E/AndroidRuntime(2295): FATAL EXCEPTION: main
06-10 09:09:18.920: E/AndroidRuntime(2295): java.lang.VerifyError: org.bouncycastle.cert.X509CertificateHolder
06-10 09:09:18.920: E/AndroidRuntime(2295):     at org.bouncycastle.openssl.PEMParser$X509CertificateParser.parseObject(Unknown Source)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at org.bouncycastle.openssl.PEMParser.readObject(Unknown Source)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at com.example.MyFragment.convertPemToX509Certificate(MyFragment.java:214)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at com.example.MyFragment.testReadCertificateSigningRequest(MyFragment.java:146)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at com.example.MyFragment.onClick(MyFragment.java:87)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at android.view.View.performClick(View.java:2485)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at android.view.View$PerformClick.run(View.java:9080)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at android.os.Handler.handleCallback(Handler.java:587)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at android.os.Handler.dispatchMessage(Handler.java:92)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at android.os.Looper.loop(Looper.java:130)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at android.app.ActivityThread.main(ActivityThread.java:3683)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at java.lang.reflect.Method.invokeNative(Native Method)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at java.lang.reflect.Method.invoke(Method.java:507)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
06-10 09:09:18.920: E/AndroidRuntime(2295):     at dalvik.system.NativeStart.main(Native Method)

Where the code is the following:

   public void testReadCertificateSigningRequest()
   {
      X509CertificateHolder csr2 = convertPemToX509Certificate(testCSR);

      if (csr2 == null)
      {
         System.out.println("TOTAL FAIL!");
      }
      else
      {
         X500Name name = csr2.getSubject();
         System.out.println("x500Name is: " + name + "\n");
         System.out.println("Common Name: " + getX500Field(COMMON_NAME, name));
         // country is 2.5.4.6
         System.out.println("COUNTRY: " + getX500Field(COUNTRY, name));
         // state is 2.5.4.8
         System.out.println("STATE: " + getX500Field(STATE, name));
         // locale is 2.5.4.7
         System.out.println("LOCALE: " + getX500Field(LOCALE, name));
      }
   }

   private X509CertificateHolder convertPemToX509Certificate(String pem)
   {
      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
      X509CertificateHolder csr = null;
      ByteArrayInputStream pemStream = null;
      try
      {
         pemStream = new ByteArrayInputStream(pem.getBytes("UTF-8"));
      }
      catch (UnsupportedEncodingException ex)
      {
         Log.e(getClass().getSimpleName(), "UnsupportedEncodingException, convertPemToPublicKey", ex);
      }

      Reader pemReader = new BufferedReader(new InputStreamReader(pemStream));
      PEMParser pemParser = null;
      try
      {
         pemParser = new PEMParser(pemReader);
         Object parsedObj = pemParser.readObject();
         System.out.println("PemParser returned: " + parsedObj);
         if (parsedObj instanceof X509CertificateHolder)
         {
            csr = (X509CertificateHolder) parsedObj;
         }
      }
      catch (IOException ex)
      {
         Log.e(getClass().getSimpleName(), "IOException, convertPemToPublicKey", ex);
      }
      finally
      {
         if (pemParser != null)
         {
            IOUtils.closeQuietly(pemParser);
         }
      }
      return csr;
   }

So I can't really tell if this is an error with the emulated Google Nexus One, with GenyMotion, or something with my code. Both the NoClassDefFound and the VerifyError are distressing, and I don't know how to track it down.. :/


Solution

  • Disclaimer: I work for Genymotion

    It is not an Emulator issue here, but an Android one. Old Android versions (Gingerbread is now 4 years old) were shipped with really really old BouncyCastle implementations. see (https://code.google.com/p/android/issues/detail?id=3280)

    Your emulator is really trying to warn you that your code probably will NOT work on a 2.3.7 Android device.

    One solution is to ship an up-to-date BouncyCastle implementation with your app. But you will have collisions, as it will have the same package name as the one on the phone. To avoid this you could use SpongyCastle, an up-to-date BouncyCastle, but renamed to avoid package name collisions. see http://rtyley.github.io/spongycastle/

    I simply added dependencies to spongycastle to my gradle build file, fixed the import to use SpongyCastle, and your code worked perfectly on a Genymotion Nexus One.

    compile 'com.madgag.spongycastle:core:1.50.0.0'
    compile 'com.madgag.spongycastle:prov:1.50.0.0'
    compile 'com.madgag.spongycastle:pkix:1.50.0.0'