Load and run JAR compiled against separate version of Bouncy Castle?

Hi there,

I would like to be able to load and run a JAR within a JRuby project.
The
JAR in particular is compiled and runs against Bouncy Castle 1.5, so it
is
not compatible with the version of Bouncy Castle bundled with JRuby
(1.47
in the JAR of JRuby that I have).

What is the right way to load this JAR and execute code from it?

I’ve created a small gist that reproduces the errors from my current
approach, loading the sample BouncyCastleClient from the JAR.

Here are a few of the errors from various approaches:

NameError: missing class or uppercase package name
(`com.demo.BouncyCastleClient’)
-

java.lang.SecurityException: class
“org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings”'s signer
information does not match signer information of other classes in the
same
package

Thanks in advance,

Zach Margolis

Zach -

I fear that I don’t understand your question.

You need to explicitly, in the Ruby code, pull in the 1.5 version of
Bouncy Castle before running (which may mean even loading) any code
that uses Bouncy Castle.

I’m not quite sure what your example is showing. Does

require 'target/multiple-bouncy-castle-HEAD-SNAPSHOT-shaded.jar'

include Bouncy Castle itself? What happens if you put that “require”
before this one:

require 'bouncy-castle-java'

Can you directly require the Bouncy Castle jar in your Ruby code and
make sure to do it early?

  • Bruce

On Mon, Mar 24, 2014, at 08:31 PM, Zach Margolis wrote:

Hi there,

I would like to be able to load and run a JAR within a JRuby project.
The JAR in particular is compiled and runs against Bouncy Castle 1.5,
so it is not compatible with the version of Bouncy Castle bundled with
JRuby (1.47 in the JAR of JRuby that I have).

What is the right way to load this JAR and execute code from it?

I’ve created a small gist that reproduces the errors from my current
approach, loading the sample BouncyCastleClient from the JAR.

[1]Bouncy Castle Errors · GitHub

Here are a few of the errors from various approaches:

  • NameError: missing class or uppercase package name
    (`com.demo.BouncyCastleClient’)
  • java.lang.SecurityException: class
    “org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings”‘s signer
    information does not match signer information of other classes in
    the same package

Thanks in advance,

Zach Margolis

References

  1. Bouncy Castle Errors · GitHub

Zach -

I don’t know. We struggled quite a bit with getting Bouncy Castle to
fit into to our mixed JRuby/Java Rails application. We felt really
fortunate when our use of Bouncy Castle was no longer needed.

One thing that was quite surprising for us (but doesn’t line up with
anything you’ve said) is that straight-up Jruby applications have a
noticeably different Java classloader than a Warble packaged .war file
running in a Java Servlet container (such as Tomcat). In the Servlet
container setup, Warbler has moved all of the Java jars into
WEB-INF/lib and the container gets those on the Java classpath before
JRuby starts and those jars are in a parent classloader, not JRuby’s
own classloader. A vanilla JRuby run doesn’t bring jars onto the
classpath until they are "require"d.

  • Bruce

On Mon, Mar 24, 2014, at 09:15 PM, Zach Margolis wrote:

Bruce, thanks for responding.

As far as I understand it, requiring the -shaded JAR will set up the
classpath, but not load Bouncy Castle. Since Bouncy Castle Provider is
a signed JAR, it can’t be packaged as part of a shaded JAR, so instead
we have maven configured to copy it into target/lib-signed and added to
the classpath of the shaded JAR.

Your suggestion works, it makes the “bad” file run requiring the bouncy
castle JAR directly, adding require
‘target/lib-signed/bcprov-jdk15on.jar’ ahead of the ruby
bouncy-castle-java works.

Unfortunately applying the same order load changes in the actual
project I’m trying to use this in does not work. Is there any way to
insulate this part of the code that depends on 1.5?

Zach Margolis

On Mon, Mar 24, 2014 at 5:48 PM, Bruce A. <[1][email protected]>
wrote:

Zach -

I fear that I don’t understand your question.

You need to explicitly, in the Ruby code, pull in the 1.5 version of
Bouncy Castle before running (which may mean even loading) any code
that uses Bouncy Castle.

I’m not quite sure what your example is showing. Does

require 'target/multiple-bouncy-castle-HEAD-SNAPSHOT-shaded.jar'

include Bouncy Castle itself? What happens if you put that “require”
before this one:

require 'bouncy-castle-java'

Can you directly require the Bouncy Castle jar in your Ruby code and
make sure to do it early?

  • Bruce

On Mon, Mar 24, 2014, at 08:31 PM, Zach Margolis wrote:

Hi there,

I would like to be able to load and run a JAR within a JRuby project.
The JAR in particular is compiled and runs against Bouncy Castle 1.5,
so it is not compatible with the version of Bouncy Castle bundled with
JRuby (1.47 in the JAR of JRuby that I have).

What is the right way to load this JAR and execute code from it?

I’ve created a small gist that reproduces the errors from my current
approach, loading the sample BouncyCastleClient from the JAR.

[2]Bouncy Castle Errors · GitHub

Here are a few of the errors from various approaches:

  • NameError: missing class or uppercase package name
    (`com.demo.BouncyCastleClient’)
  • java.lang.SecurityException: class
    “org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings”‘s signer
    information does not match signer information of other classes in
    the same package

Thanks in advance,

Zach Margolis

References

  1. mailto:[email protected]
  2. Bouncy Castle Errors · GitHub

Bruce, thanks for responding.

As far as I understand it, requiring the -shaded JAR will set up the
classpath, but not load Bouncy Castle. Since Bouncy Castle Provider is a
signed JAR, it can’t be packaged as part of a shaded JAR, so instead we
have maven configured to copy it into target/lib-signed and added to the
classpath of the shaded JAR.

Your suggestion works, it makes the “bad” file run requiring the bouncy
castle JAR directly, adding require
'target/lib-signed/bcprov-jdk15on.jar’ahead of the ruby
bouncy-castle-java works.

Unfortunately applying the same order load changes in the actual project
I’m trying to use this in does not work. Is there any way to insulate
this
part of the code that depends on 1.5?

Zach Margolis

Since, I kind of felt into ‘BC’ / OpenSSL internals lately :
openssl 'BC' (security provider) leak by kares · Pull Request #1543 · jruby/jruby · GitHub (WiP)

Here’s what I think is possible to be done (once the leak is avoided and
all tests are passing) in terms of BC “integration” :

  1. (again) upgrade to 1.48/1.49 (and than hopefully 1.50) which failed
    previously : JRuby 1.7.8 fails to initialize OpenSSL::PKey::RSA.new with encrypted key · Issue #1238 · jruby/jruby · GitHub
  2. if possible allow OpenSSL to work with multiple BC versions (at least
    the few latest)
  3. most importantly if BC provider is “globally” installed we should try
    to
    work with that one …
  4. some parts of OpenSSL work without BC already - in the long run maybe
    more of it could work e.g. by re-packaging some of the BC classes that
    do
    not depend on BC’s API that much … in the PR it’s done for some of
    those
    already to avoid BC assuming the provider is installed

Should be noted that Warbler has changed the packaging lately - not to
copy
the jars already in the jruby jar to WEB-INF/lib,
here’s how a packaged .war looks like these days :

Since Bundler requires OpenSSL it might get brought up on the CP but
that
should only happen when installing not on a require ‘bundler/setup’ …

Using BC 1.50 (pretty much any > 1.47) will break (parts of) OpenSSL but
if
you do not load OpenSSL it should work, check the $CLASSPATH global if
it’s
failing and review parts/gems that might be doing a require ‘openssl’ or

K.

Bruce -

we change the behaviour of warbler to NOT copy those jars by default
into
WEB-INF/lib directory to stay more in line with a vanilla jruby
execution.

Zach -

not sure if that is relevant, but as Karol pointed out jruby needs
bouncy-castle for its openssl implementation and it is kind of bounced
to
the version of BC

with jruby-9000.dev the jruby classloader separates from the underlying
parent classloader, that means the parent classloader can use another BC
version and jruby uses its version. that separation will improve with a
patch in process to NOT register BC as security provider and instead use
BC
directly (not through javax.security API) - not sure if that helps in
your
situation. but if your client code resides in parent-classloader (which
is
in a servlet containter roughly WEB-INF/lib/*) that jruby-9000.dev
helps.

there is the jruby-complete version 1.7.1 which uses a repackaged BC
internally which might be a way to go for you. (I do not know why this
was
dropped on later versions)

  • christian

Karol: Thanks for pointing out that Bundler uses OpenSSL, I think that
explains some of the issues I was seeing with Bouncy 1.47 in the
$CLASSPATH. I’m not sure we can get around that easily.

Christian: I’ll look into jruby-9000.dev, but given that it’s not a
release
yet, I’m a little hesitant to deploy that to production.

Also for reference, in our production setup, we don’t warble or build a
.war. We use Jetty + a few JARs and just plain Ruby source (
GitHub - square/jetpack: jet.pack: package your JRuby rack app for Jetty.).

Thanks for the responses everybody. At this point I think shelling out
to
OpenSSL might be a more viable option that trying to load a separate
version of BouncyCastle in a reliable fashion.

Zach Margolis