All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.io7m.cardant.tls.CATLSContext Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2023 Mark Raynsford  https://www.io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.cardant.tls;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Objects;

/**
 * Functions to create custom SSL contexts.
 */

public final class CATLSContext
{
  private static final Logger LOG =
    LoggerFactory.getLogger(CATLSContext.class);

  private final String user;
  private final CATLSStoreConfiguration keyStoreConfiguration;
  private final KeyStore keyStore;
  private final CATLSStoreConfiguration trustStoreConfiguration;
  private final KeyStore trustStore;
  private final SSLContext context;

  private CATLSContext(
    final String inUser,
    final CATLSStoreConfiguration inKeyStoreConfiguration,
    final KeyStore inKeyStore,
    final CATLSStoreConfiguration inTrustStoreConfiguration,
    final KeyStore inTrustStore,
    final SSLContext inContext)
  {
    this.user =
      Objects.requireNonNull(inUser, "user");
    this.keyStoreConfiguration =
      Objects.requireNonNull(inKeyStoreConfiguration, "keyStoreConfiguration");
    this.keyStore =
      Objects.requireNonNull(inKeyStore, "keyStore");
    this.trustStoreConfiguration =
      Objects.requireNonNull(
        inTrustStoreConfiguration,
        "trustStoreConfiguration");
    this.trustStore =
      Objects.requireNonNull(inTrustStore, "trustStore");
    this.context =
      Objects.requireNonNull(inContext, "context");
  }

  /**
   * Create a new SSL context using the given keystore and truststore.
   *
   * @param user                    The part of the application creating the context
   * @param keyStoreConfiguration   The key store
   * @param trustStoreConfiguration The trust store
   *
   * @return A new SSL context
   *
   * @throws IOException              On I/O errors
   * @throws GeneralSecurityException On security errors
   */

  public static CATLSContext create(
    final String user,
    final CATLSStoreConfiguration keyStoreConfiguration,
    final CATLSStoreConfiguration trustStoreConfiguration)
    throws IOException, GeneralSecurityException
  {
    Objects.requireNonNull(user, "user");
    Objects.requireNonNull(keyStoreConfiguration, "keyStoreConfiguration");
    Objects.requireNonNull(trustStoreConfiguration, "trustStoreConfiguration");

    LOG.info(
      "KeyStore [{}] {} (Provider {}, Type {})",
      user,
      keyStoreConfiguration.storePath(),
      keyStoreConfiguration.storeProvider(),
      keyStoreConfiguration.storeType()
    );

    LOG.info(
      "TrustStore [{}] {} (Provider {}, Type {})",
      user,
      trustStoreConfiguration.storePath(),
      trustStoreConfiguration.storeProvider(),
      trustStoreConfiguration.storeType()
    );

    final var keyStore =
      KeyStore.getInstance(
        keyStoreConfiguration.storeType(),
        keyStoreConfiguration.storeProvider()
      );

    final var keyStorePassChars =
      keyStoreConfiguration.storePassword()
        .toCharArray();

    try (var stream =
           Files.newInputStream(keyStoreConfiguration.storePath())) {
      keyStore.load(stream, keyStorePassChars);
    }

    final var trustStore =
      KeyStore.getInstance(
        trustStoreConfiguration.storeType(),
        trustStoreConfiguration.storeProvider()
      );

    final var trustStorePassChars =
      trustStoreConfiguration.storePassword().toCharArray();

    try (var stream = Files.newInputStream(trustStoreConfiguration.storePath())) {
      trustStore.load(stream, trustStorePassChars);
    }

    final var keyManagerFactory =
      createKeyManagerFactory(keyStore, keyStorePassChars);
    final var trustManagerFactory =
      createTrustManagerFactory(trustStore);

    final var context =
      SSLContext.getInstance("TLSv1.3");

    context.init(
      keyManagerFactory.getKeyManagers(),
      trustManagerFactory.getTrustManagers(),
      SecureRandom.getInstanceStrong()
    );

    return new CATLSContext(
      user,
      keyStoreConfiguration,
      keyStore,
      trustStoreConfiguration,
      trustStore,
      context
    );
  }

  private static TrustManagerFactory createTrustManagerFactory(
    final KeyStore trustStore)
    throws GeneralSecurityException
  {
    final var trustManagerFactory =
      TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

    trustManagerFactory.init(trustStore);
    return trustManagerFactory;
  }

  private static KeyManagerFactory createKeyManagerFactory(
    final KeyStore keyStore,
    final char[] keyStorePassChars)
    throws GeneralSecurityException
  {
    final var keyManagerFactory =
      KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

    keyManagerFactory.init(keyStore, keyStorePassChars);
    return keyManagerFactory;
  }

  @Override
  public String toString()
  {
    return "[IdTLSContext 0x%x]".formatted(Integer.valueOf(this.hashCode()));
  }

  /**
   * Reload the key stores and associated SSL context.
   *
   * @throws IOException              On I/O errors
   * @throws GeneralSecurityException On security errors
   */

  public void reload()
    throws IOException, GeneralSecurityException
  {
    LOG.info(
      "KeyStore [{}] {} reloading",
      this.user,
      this.keyStoreConfiguration.storePath()
    );

    final var keyStorePassChars =
      this.keyStoreConfiguration.storePassword()
        .toCharArray();

    try (var stream =
           Files.newInputStream(this.keyStoreConfiguration.storePath())) {
      this.keyStore.load(stream, keyStorePassChars);
    }

    LOG.info(
      "TrustStore [{}] {} reloading",
      this.user,
      this.keyStoreConfiguration.storePath()
    );

    final var trustStorePassChars =
      this.trustStoreConfiguration.storePassword().toCharArray();

    try (var stream =
           Files.newInputStream(this.trustStoreConfiguration.storePath())) {
      this.trustStore.load(stream, trustStorePassChars);
    }

    final var keyManagerFactory =
      createKeyManagerFactory(this.keyStore, keyStorePassChars);
    final var trustManagerFactory =
      createTrustManagerFactory(this.trustStore);

    this.context.init(
      keyManagerFactory.getKeyManagers(),
      trustManagerFactory.getTrustManagers(),
      SecureRandom.getInstanceStrong()
    );
  }

  /**
   * @return The SSL context
   */

  public SSLContext context()
  {
    return this.context;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy