Skip to content

RFC: Allow any kind of keystore in vert.x core

Paulo Lopes edited this page Nov 24, 2020 · 1 revision

Introduction

Currently in vert.x core there are several ways to work with java security keystores. The 2 mostly used types are:

  • io.vertx.core.net.JksOptions
  • io.vertx.core.net.PfxOptions

This abstraction allows the configuration of a key store using from a polyglot perspective or using a JSON configuration. Although this has been working for a long time it does have a few limitations.

io.vertx.core.net.JksOptions

This type assumes that the configured keystore is of type "JKS" and the provider is the JDK (libSUNJSSE) setting the type or provider is not possible.

io.vertx.core.net.PfsOptions

This type assumes that the configured keystore is of type "PKCS12" and the provider is the JDK (libSUNJSSE) setting the type or provider is not possible.

Details

Both these classes extend a base class KeystoreOptions which currently allows setting the type of the keystore. One of the reasons for this was the request to support PKCS11 (hardware stores), however the base class is NOT used in any public API, this means that the end user is only aware of the 2 sub classes listed above.

Problem

For secure and certified application there is a requirement to support FIPS, among other items, this enforces strong rules on passwords and secret storage which translate in key store formats. In order to support this the vert.x end user must be able to configure the keystore type and in some cases specify the provider as generic OpenJDK does not ship with FIPS support enabled out of the box.

The solution should be to add getters and setters to both type and provider to KeystoreOptions, but this isn't enough. Although the sub classes would be able to inherit these, currently JksOptions hard codes the type to JKS and disallows mutating it, and PfxOptions does the same with type as PKCS12.

API wise, if this restriction would be removed, from a technical PoV, there would be no real difference between the 2 sub classes as types could be changed, so it would bring confusion to the end user. This would mean that "KeystoreOptions" would become "THE" way to load keystores, regardless of type and/or provider.

Having a closer look, this will require a deprecation/breaking change to many data objects, e.g.:

  • net.NetClientOptions
  • net.NetServerOptions
  • net.TCPSSLOptions
  • http.HttpClientOptions
  • eventbus.EventBusOptions

Which currently have APIs like:

  @Override
  public NetClientOptions setTrustStoreOptions(JksOptions options) {
    super.setTrustStoreOptions(options);
    return this;
  }

  @Override
  public NetClientOptions setPfxTrustOptions(PfxOptions options) {
    return (NetClientOptions) super.setPfxTrustOptions(options);
  }

Both setters "seem" to attempt to achieve the same result, but for the sake of type safety need to be declared as 2 independent setters. The proposed fix (with least breakage) would be in the following style:

  @Override
  public NetClientOptions setTrustStoreOptions(KeystoreOptions options) {
    super.setTrustStoreOptions(options);
    return this;
  }

  /**
   * @deprecated Use {@link #setTrustStoreOptions(KeystoreOptions)} instead.
   */
  @Override
  @Deprecated
  public NetClientOptions setPfxTrustOptions(PfxOptions options) {
    return this.setTrustStoreOptions((KeystoreOptions) options);
  }

However this will bring compilation issues as now the compiler will not be able to distinct between KeystoreOptions and PfxOptions so user code will require manual enforced casts e.g.: (KeystoreOptions) options.

A more drastic change would be to really remove the [set|get]PfxXYZOptions() which may require code changes to the user but probably less than the forced casting.

  @Override
  public NetClientOptions setTrustStoreOptions(KeystoreOptions options) {
    super.setTrustStoreOptions(options);
    return this;
  }

At a higher level this can become more tricky as most of the configurations in core and other sub modules (amqp as an example) do rely on these types too.

Non goals

A requested (downstream) feature is to handle password specific support for each key instead of relying on the store password itself. This will not solve it and it currently addressed on vertx-auth-common.

Clone this wiki locally