Bouncycastle, OSGi and uber-jars

I have recently tried to use SSHj in an OSGi bundle.
I had decided to wrap this library and its dependencies in my own bundle (something equivalent to an uber-jar but compliant with OSGi). In a classic context, you would use the Maven Shade plug-in. In an OSGi context, you can simply use the Maven Bundle plug-in, with the embedded-dependency directive.

Anyway, the difficulty here came because SSHj uses Bouncycastle as a security provider. And you cannot do what you want with it. The first attempt to build my all-in-one bundle resulted in a signature error during the Maven build.

Invalid signature file digest for Manifest main attributes

Indeed, some files in the JAR were signed, and some others were not. I solved it by using the Maven ANT plugin, removing signature files from my jar and repackaging it. The JAR could finally be built. Unfortunately, later at runtime, another error came out.

JCE cannot authenticate the provider BC

Looking at the logs of SSHj, no provider was found while the right classes were in the bundle’s class path. And everything was working outside of OSGi. So, there was no error with compilation levels or in my code. For some reason, Bouncycastle was not loaded by the JVM.

The explanation is that JCE (Java Cryptography Extension) providers are loaded in a special way by the JVM. First, they must be signed. And it seems not any certificate can be used (it must be approved by the JVM vendors). Bouncycastle is signed. But if you wrap it into another JAR, you will lose the signature. And then, these providers must be loaded at startup. If you apply this to an OSGi context, it means you cannot deploy Bouncycastle as a bundle whenever you want.

Finally, I solved my issue by…

  • … following Karaf’s documentation and making Bouncycastle a part of my Karaf distribution (copy it in lib/ext and updating some configuration files). See this POM to automate such a thing for you custom Karaf distribution.
  • … not importing org.bouncycastle.* in my packages. That’s because putting these libraries under lib/ext means these packages are considered as root classes (just like java.*). No need to import them then.
  • … making sure all the bundles that depend on Bouncycastle would use the same version. I achieved it by updating the dependencyManagement section in my parent POM.

And although it was not a real issue, I decided to put SSHj as Karaf feature. This way, no need to make an uber-jar. And I can reuse it as much as I want. See this file for a description of this feature (ssh4j-for-roboconf). The dependencies that are not already OSGi bundles are deployed through the wrap protocol.

I spent about a day to solve these issues. That’s why I thought an article might help.