Giter Club home page Giter Club logo

compositejks's Introduction

CompositeJKS

Load a custom Java KeyStore into the SSL Context without replacing the system CA list.

To use this library, add the following to your Maven pom.xml:

<dependency>
  <groupId>com.oneandone</groupId>
  <artifactId>composite-jks</artifactId>
  <version>1.0</version>
</dependency>

Usecase samples

CompositeJKS allows you to load a custom Java KeyStore into the SSL Context without replacing the system CA list. The system and the custom KeyStores are merged into a composite view:

SslContextUtils.mergeWithSystem("/path/to/my/cacerts");

CompositeJKS also supports loading JKS files embedded in the JAR. Place your file in src/main/resources/ to let Maven embed it and then use a call like:

SslContextUtils.mergeWithSystem(
        getClass().getClassLoader().getResourceAsStream("keystore.jks"));

The full story

Many companies host their own internal Certificate Authority (CA). These services issues X.509 certificates, e.g. for use in HTTPS connections. In order for web browsers and programmatic clients to trust connections to servers using such internal certificates, the appropriate root certificate needs to be imported into a "trusted root certificates" list.

The precise location and format of this list depends on the operating system, programming language and tool in use. For example, Internet Explorer and Google Chrome use the Windows certificate store when running on Windows while Mozilla Firefox uses its own private certificate store regardless of the operating system is running on.

Here, I would like to illustrate the particular challenges that arise when consuming internally-signed web-services in Java clients. Like Mozilla Firefox, Java uses its own certificate store rather than relying on an operating system implementation. On Debian-based Linux distributions this file is usually located at /etc/ssl/certs/java/cacerts. On Windows the file can be found at path like C:\Program Files\Java\jre1.8.0_77\lib\security\cacerts.

These files use the Java KeyStore file format and can be modified using the keytool command-line tool. To add your own CA to the list of trusted roots you can run:

keytool -import -trustcacerts -file yourca.pem -alias yourca -keystore [Location of the certificate store as described above]

When prompted for a password use the default password set by Java: changeit

Changing this password is neither required nor recommended, since this particular Java KeyStore file only contains public keys of CAs and therefore stores no private data that required protection.

Unfortunately, there are a few problems with modifying this global KeyStore. On Windows installation of new Java versions will replace the modified file with the default again. On both Windows and Linux this modification requires administrative privileges. These issues motivate the need for a way to apply application-specific modifications to the list of trusted CAs.

The Java system property javax.net.ssl.trustStore can be used to specify an alternate path to load the cacerts file. You can create your own local copy of the default file and apply modifications to it using keytool. Then you can launch your application like this:

java -Djavax.net.ssl.trustStore=/path/to/my/cacerts -jar myapp.jar

If the application in question is a web-service running in Tomcat you can instead add this line to /etc/default/tomcat8:

JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStore=/path/to/my/cacerts"

Another possibility is to set the Java system property within the startup code of the application itself using:

System.setProperty("javax.net.ssl.trustStore", "/path/to/my/cacerts");

Note that this last option requires the KeyStore to be a real file on-disk and not a file embedded within a JAR.

All of the aforementioned options have one major drawback: By effectively forking the upstream default cacerts file your application does not get any future updates to "public" CAs. Similarly, application-specific KeyStores will not include any customizations administrators make to the global KeyStore. CompositeJKS fixes all this by allowing you to load a custom Java KeyStore into the SSL Context without replacing the system CA list.

compositejks's People

Contributors

bastianeicher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

compositejks's Issues

SslContextUtils methods fail when the indicated KeyStore contains PrivateKeyEntry objects

I haven't tested this extensively yet but in a nutshell, lets assume there is a KeyStore holding two entries, one of type PrivateKeyEntry and the other of trustedCertEntry. Additionally the store is protected by a password.

Retrieving the KeyStore by calling one of the methods on KeyStoreLoader that takes a password everything works as expected.

However once trying to call SslContextUtils.buildMergedWithSystem(KeyStore) the method fails due to a missing password – namely the password of the PrivateKeyEntry (which happens to be different from the store password). Here's the stack:

Exception in thread "main" java.security.UnrecoverableKeyException: Cannot recover key
    at sun.security.provider.KeyProtector.recover(KeyProtector.java:328)
    at sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:146)
    at sun.security.provider.JavaKeyStore$JKS.engineGetKey(JavaKeyStore.java:56)
    at sun.security.provider.KeyStoreDelegator.engineGetKey(KeyStoreDelegator.java:96)
    at sun.security.provider.JavaKeyStore$DualFormatJKS.engineGetKey(JavaKeyStore.java:70)
    at java.security.KeyStore.getKey(KeyStore.java:1023)
    at sun.security.ssl.SunX509KeyManagerImpl.<init>(SunX509KeyManagerImpl.java:133)
    at sun.security.ssl.KeyManagerFactoryImpl$SunX509.engineInit(KeyManagerFactoryImpl.java:70)
    at javax.net.ssl.KeyManagerFactory.init(KeyManagerFactory.java:256)
    at com.oneandone.compositejks.SslContextUtils.getSystemKeyManager(SslContextUtils.java:86)

I've fixed this locally by not using CompositeX509KeyManager but just the default system key manager. But I'm not sure if this is to your intention :). If you want I'll make a PR …

SslContextUtils: getSystemTrustgetSystemKeyManagerManager

Hey,

i was looking into why the maven import wasn't working and found this error when i copied the classes over manually:

I think that
getSystemTrustgetSystemKeyManagerManager(X509Algorithm, keyStore),
should be
getSystemTrustManager(X509Algorithm, null),
in SslContextUtils.

SunX509 KeyManagerFactory not available - running IBM JVM

I have implemented the code for Composite-JKS as described for my java client. I used Maven to add the dependency and added the SslContextUtils.mergeWithSystem(getClass().getClassLoader().getResourceAsStream("trust.jks")); statement to my java client. When I execute this statement, I receive the following error:

java.security.NoSuchAlgorithmException: SunX509 KeyManagerFactory not available
at sun.security.jca.GetInstance.getInstance(GetInstance.java:171)
at javax.net.ssl.KeyManagerFactory.getInstance(KeyManagerFactory.java:12)
at com.oneandone.compositejks.SslContextUtils.getSystemKeyManager(SslContextUtils.java:85)
at com.oneandone.compositejks.SslContextUtils.buildMergedWithSystem(SslContextUtils.java:71)
at com.oneandone.compositejks.SslContextUtils.mergeWithSystem(SslContextUtils.java:27)
at com.oneandone.compositejks.SslContextUtils.mergeWithSystem(SslContextUtils.java:41)
at com.ibm.zss.client.boundary.ZssAPIResource.(ZssAPIResource.java:43)
at com.ibm.zss.client.boundary.ZssAPIService.(ZssAPIService.java:38)
at com.ibm.zss.client.ZssAPIServiceTest.setUpBeforeClass(ZssAPIServiceTest.java:32)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:95)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
at java.lang.reflect.Method.invoke(Method.java:508)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

I found similar issues for other projects which indicate that the problem is that the SunX509 KeyManagerFactory is not available on a system running the IBM JVM. The solution was to allow the algorithm to be configurable. (See https://code.google.com/archive/p/javapns/issues/39 and https://code.google.com/archive/p/javapns/issues/41)

Is this option already configurable for CompositeJKS or can you add this support?

improve the configuration (API)

the real potential of the library can be unlocked by improving the configuration and therefore making it a little bit more flexible. i guess it was created for internal usage and therefore made some assumptions like 1, 2 and not passing a password to SslContextUtils.buildMergedWithSystem, which is an issue i reported in #4.

since i needed something more flexible but also liked the basics it provided (CompositeX509KeyManager and CompositeX509TrustManager), i changed the entry point by leveraging a fluent api and step-builder. the classes can be found here. entry point is here. i also used default interface methods that use predefined default values without hardcoding them (example).

exemplary usage from our project.

other code example:

SslContextBuilder.builder()
                 .keyStoreFromFile("key store path without pwd")
                 .usingProtocol("SSL")
                 .usingSunX509()
                 .usingKeyManagerPassword("key manager pwd")
                 .buildMergedWithSystemAndSetDefault();

i would love to see my changes in this official repo. if desired, i can make a PR.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.