Skip to content

A serviceloader based SPI service and java agent for JCA providers.

License

Notifications You must be signed in to change notification settings

tersesystems/jcaprovider

Repository files navigation

JCA Provider Service

Bintray

This is a very small package which allows a JCA security provider to be loaded into a JVM automatically, using a Java agent.

This is because even if you want to use a custom JSSE provider, the JVM does not make it especially easy to swap out or configure. The means of configuration is obscure, barely documented, and unlike anything else.

Meanwhile, Java agents and service loaders are very well documented and standardized, and only require a single library dependency addition for implementation, with no need to modify source code.

Installation

The core of JCA provider service is a small java agent and some ServiceLoader code.

Currently the project is published through bintray:

<repositories>
    <repository>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
        <id>bintray-tersesystems-maven</id>
        <name>bintray</name>
        <url>https://dl.bintray.com/tersesystems/maven</url>
    </repository>
</repositories>

and then you add the dependency:

<dependencies>
    <dependency>
        <groupId>com.tersesystems.jcaprovider</groupId>
        <artifactId>jcaprovider-core</artifactId>
        <version>0.1.0-SNAPSHOT</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

Add the java agent, to your program at runtime.

java -javaagent:/home/wsargent/.m2/repository/com/tersesystems/jcaprovider/jcaprovider-core/0.1.0-SNAPSHOT/jcaprovider-core-0.1.0-SNAPSHOT.jar

Or by adding it to JAVA_TOOL_OPTIONS:

export JAVA_TOOL_OPTIONS="-javaagent:/home/wsargent/.m2/repository/com/tersesystems/jcaprovider/jcaprovider-core/0.1.0-SNAPSHOT/jcaprovider-core-0.1.0-SNAPSHOT.jar"

This will then load any JCA service provider implementation that you have in your classpath automatically

Manual Installation

If you do not have the core loaded as a Java agent, then you can load any providers manually by doing the following:

import com.tersesystems.jcaprovider.JcaProviderService;

JcaProviderService instance = JcaProviderService.getInstance();
instance.insertProviders(); // Calls Security.insertProviderAt(provider, 1) for listed providers

Specifying a JCA Provider implementation

You specify the provider implementation in one of several ways.

Using Pre-built Service Provider

There are several subprojects which have implementations available. You add these to your project, and then you're done.

DebugJSSE

The DebugJSSE Provider:

<dependency>
    <groupId>com.tersesystems.jcaprovider</groupId>
    <artifactId>jcaprovider-debugjsse</artifactId>
    <version>0.1.0-SNAPSHOT</version>
    <scope>compile</scope>
</dependency>

CloudFoundry Provider

The CloudFoundry Provider:

<dependency>
    <groupId>com.tersesystems.jcaprovider</groupId>
    <artifactId>jcaprovider-cloudfoundry</artifactId>
    <version>0.1.0-SNAPSHOT</version>
    <scope>compile</scope>
</dependency>

BouncyCastle Provider

The BouncyCastle Provider:

<dependency>
    <groupId>com.tersesystems.jcaprovider</groupId>
    <artifactId>jcaprovider-bouncycastle</artifactId>
    <version>0.1.0-SNAPSHOT</version>
    <scope>compile</scope>
</dependency>

BouncyCastle TLS

The BouncyCastle TLS Provider:

<dependency>
    <groupId>com.tersesystems.jcaprovider</groupId>
    <artifactId>jcaprovider-bouncycastle-tls</artifactId>
    <version>0.1.0-SNAPSHOT</version>
    <scope>compile</scope>
</dependency>

Using Reflection

You can also specify a provider through reflection, using the com.tersesystems.jcaprovider.name system property.

java -Dcom.tersesystems.jcaprovider.name=com.tersesystems.debugjsse.DebugJSSEProvider

Writing a custom service provider

Finally, you can always specify a provider yourself with the service loader pattern, which is as simple as implementing the JcaProvider interface:

package mypackage;

public class MyJcaProvider implements com.tersesystems.jcaprovider.spi.JcaProvider {
    @Override
    public Provider apply() {
        return new MyProvider();
    }
}

and a META-INF/services/com.tersesystems.jcaprovider.spi.JcaProvider file containing

mypackage.MyJcaProvider

Examples

Using Maven

TODO

Using SBT

You can specify the java agent and implementation directly in build.sbt and a couple of plugins:

addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.4")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.15")

and then in build.sbt:

// https://github.com/sbt/sbt-javaagent / sbt native packager handles java agent packaging
lazy val root = (project in file(".")).enablePlugins(JavaAppPackaging, JavaAgent)
  .settings(
    inThisBuild(List(
      organization := "com.example",
      version      := "0.1.0-SNAPSHOT"
    )),
    name := "project-with-jcaprovider",
    mainClass := Some("com.example.SecurityProgram"),
    resolvers += Resolver.jcenterRepo,
    javaAgents += "com.tersesystems.jcaprovider" % "jcaprovider-core" % "0.1.0-SNAPSHOT" % "dist;compile;test",
    libraryDependencies += "com.tersesystems.jcaprovider" % "jcaprovider-debugjsse" % "0.1.0-SNAPSHOT" // use debugjsse as the implementation
  )

Providers in Java 9

If you are using Java 9 or higher, then in theory you can provide a java.security.Provider under the ServiceLoader pattern, when you add the File java.security.Provider to Use the ServiceLoader Class to Search for Providers.

There is more documentation available under Step 4: Create a Module Declaration for Your Provider with follow up in Step 8.1: Configure the Provider and Step 10: Run Your Test Programs.

This code is different from the service loader pattern in that it defines an interface which you implement yourself. In Java 9, you must extend the Provider abstract class, which means that you must implement a constructor and working inside the instantiation of the provider, and you give up control over instantation yourself.

Adding a Provider by Hand

For the sake of completeness, I'll describe how to add a JCA provider by hand, the old-school way:

You must declare the JCA provider in a security file

Here, the bit before the pipe | is the name of the provider, followed by the class.

security.provider.1=debugJSSE|com.tersesystems.debugjsse.DebugJSSEProvider

You must then append your security file by using the java.security.properties security property with a single equals sign:

java -Djava.security.properties=my.java.security

If you want to do reordering, you have to specify everything.

This means you have to go to $JAVA_HOME/jre/lib/security/java.security and copy all of the providers over:

security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
security.provider.3=sun.security.ec.SunEC
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=com.sun.security.sasl.Provider
security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.9=sun.security.smartcardio.SunPCSC

But this is not something I've had to do.

About

A serviceloader based SPI service and java agent for JCA providers.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages