Skip to content

Instantly share code, notes, and snippets.

@zachmargolis
Last active October 18, 2017 09:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zachmargolis/9752311 to your computer and use it in GitHub Desktop.
Save zachmargolis/9752311 to your computer and use it in GitHub Desktop.
Bouncy Castle Errors
target/*
*.iml
.DS_Store
.idea

Multiple Bouncy Castle: Errors

  • Goal: In JRuby, run and load a Java JAR that is compiled against bcprov 1.5

As currently configured, this project will load and run (see bouncy_castle_client.rb). However, if the version of BouncyCastle that is bundled with JRuby (1.4.7) is loaded (see bouncy_castle_collision.rb), the project fails to load the JAR correctly.

Instructions:

set_up_files.sh # Github gists don't allow directories
mvn package
ruby bouncy_castle_client.rb # ok
ruby bouncy_castle_collision.rb # errors
# Pristine environment: load the JAR in a clean environment, it runs against
# Bouncy Castle 1.5
require 'target/multiple-bouncy-castle-HEAD-SNAPSHOT-shaded.jar'
puts 'after require'
client = com.demo.BouncyCastleClient.new
puts client.getChaChaName
puts "java bouncy castle"
puts client.getProviderVersion
puts "ruby bouncy castle"
puts org.bouncycastle.jce.provider.BouncyCastleProvider.new["Provider.id version"]
# Dirty environment, fails with a missing class name (can't reload new stuff
# into the same space as old stuff?)
require 'bouncy-castle-java'
puts "ruby bouncy castle"
puts org.bouncycastle.jce.provider.BouncyCastleProvider.new["Provider.id version"]
require 'target/multiple-bouncy-castle-HEAD-SNAPSHOT-shaded.jar'
puts 'after require'
client = com.demo.BouncyCastleClient.new
puts client.getChaChaName
puts "java bouncy castle"
puts client.getProviderVersion
puts "ruby bouncy castle"
puts org.bouncycastle.jce.provider.BouncyCastleProvider.new["Provider.id version"]
package com.demo;
import java.security.Provider;
import java.security.Security;
import org.bouncycastle.crypto.engines.ChaChaEngine;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class BouncyCastleClient {
private static final Provider provider = new BouncyCastleProvider();
public String getProviderVersion() {
return provider.get("Provider.id version").toString();
}
public String getChaChaName() {
Security.addProvider(provider);
ChaChaEngine engine = new ChaChaEngine();
return engine.getAlgorithmName();
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.squareup</groupId>
<artifactId>multiple-bouncy-castle</artifactId>
<version>HEAD-SNAPSHOT</version>
<build>
<plugins>
<!-- build a shaded jar that we can include in the Ruby gem -->
<plugin>
<groupId>com.squareup.maven.plugins</groupId>
<artifactId>shade-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>shaded</shadedClassifierName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Class-Path>lib-signed/bcprov-jdk15on.jar</Class-Path>
</manifestEntries>
</transformer>
</transformers>
<artifactSet>
<excludes>
<exclude>org.bouncycastle:bcprov-jdk15on</exclude>
</excludes>
</artifactSet>
<!-- signatures from foreign jars are bad news -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib-signed</outputDirectory>
<includeArtifactIds>bcprov-jdk15on</includeArtifactIds>
<stripVersion>true</stripVersion>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.50</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk15on</artifactId>
<version>1.50</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.50</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcmail-jdk15on</artifactId>
<version>1.50</version>
</dependency>
</dependencies>
</project>
#!/usr/bin/env bash
mkdir -p src/main/java/com/demo
cp BouncyCastleClient.java src/main/java/com/demo
@jjowdy
Copy link

jjowdy commented Mar 25, 2014

In pom.xml, line 15, you can make this Square-agnostic:

<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.2</version>

(It still shows the same issue.)

Poking around a bit, I'm guessing the issue you're seeing at runtime is because, by the time you're running your code, openssl has already been required. That in turn loads shared/jopenssl/load.rb, which then requires the older versions of BouncyCastle (via require 'bouncy-castle-java').

E.g.,
~/.rvm/rubies/jruby-1.7.9/lib/ruby grep -rl "require 'openssl'" * 1.8/drb/ssl.rb 1.8/net/https.rb 1.8/net/smtp.rb 1.8/webrick/ssl.rb 1.9/digest/hmac.rb 1.9/drb/ssl.rb 1.9/net/https.rb 1.9/net/smtp.rb 1.9/webrick/ssl.rb 1.9/webrick.rb 2.0/drb/ssl.rb 2.0/net/https.rb 2.0/net/smtp.rb 2.0/openssl/digest.rb 2.0/ssl.rb 2.0/webrick/ssl.rb gems/shared/gems/activesupport-3.2.16/lib/active_support/message_encryptor.rb gems/shared/gems/activesupport-3.2.16/lib/active_support/message_verifier.rb gems/shared/gems/jruby-openssl-0.9.4/lib/jopenssl18/openssl/bn.rb gems/shared/gems/jruby-openssl-0.9.4/lib/jopenssl18/openssl/cipher.rb gems/shared/gems/jruby-openssl-0.9.4/lib/jopenssl18/openssl/config.rb gems/shared/gems/jruby-openssl-0.9.4/lib/jopenssl18/openssl/digest.rb gems/shared/gems/jruby-openssl-0.9.4/lib/jopenssl18/openssl/ssl.rb gems/shared/gems/jruby-openssl-0.9.4/lib/jopenssl18/openssl/x509.rb gems/shared/gems/jruby-openssl-0.9.4/lib/jopenssl19/openssl/ssl.rb gems/shared/gems/jruby-openssl-0.9.4/lib/jopenssl19/openssl/x509.rb gems/shared/gems/rack-1.4.5/lib/rack/session/cookie.rb shared/jopenssl18/openssl/bn.rb shared/jopenssl18/openssl/cipher.rb shared/jopenssl18/openssl/config.rb shared/jopenssl18/openssl/digest.rb shared/jopenssl18/openssl/ssl.rb shared/jopenssl18/openssl/x509.rb shared/jopenssl19/openssl/ssl.rb shared/jopenssl19/openssl/x509.rb shared/jopenssl21/openssl/digest.rb shared/rubygems/commands/cert_command.rb shared/rubygems/security/policy.rb shared/rubygems/security.rb

It's unclear who's loading BouncyCastle first in your runtime environment. I'd suspect it's either happening while loading a signed Gem (not jar!) or maybe as a result of some https/ssl stuff happening in Jetty or while bootstrapping some other communication library you need.

@jjowdy
Copy link

jjowdy commented Mar 25, 2014

Ideally, JRuby would be a little more modular and allow you to include a later version of BouncyCastle.

I think that's probably going to be the only way you can get this to work, short of some heroics with a custom ClassLoader.

Fixing JRuby might actually be fairly easy. It looks like the only explicit deps in the stock classes are in openssl/pkcs12.rb, though there appear to be a few Java incompatibilities deeper down. (Some 3rd party JRuby libs may depend on older BC library versions, though I doubt it.)

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