CertificateLoader.java

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2016 wcm.io
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package io.wcm.caravan.jaxws.consumer.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;

import org.apache.commons.lang3.StringUtils;

import io.wcm.caravan.jaxws.consumer.JaxWsClientInitializer;

/**
 * Helper class for loading certificates for SSL communication.
 */
public final class CertificateLoader {

  /**
   * Default SSL context type
   */
  public static final String SSL_CONTEXT_TYPE_DEFAULT = "TLS";

  /**
   * Default key manager type
   */
  public static final String KEY_MANAGER_TYPE_DEFAULT = "SunX509";

  /**
   * Default key store type
   */
  public static final String KEY_STORE_TYPE_DEFAULT = "PKCS12";

  /**
   * Default trust manager type
   */
  public static final String TRUST_MANAGER_TYPE_DEFAULT = "SunX509";

  /**
   * Default trust store type
   */
  public static final String TRUST_STORE_TYPE_DEFAULT = "JKS";


  private CertificateLoader() {
    // static methods only
  }

  /**
   * Get key manager factory
   * @param config Config
   * @return Key manager factory
   * @throws IOException
   * @throws GeneralSecurityException
   */
  public static KeyManagerFactory getKeyManagerFactory(JaxWsClientInitializer config)
      throws IOException, GeneralSecurityException {
    return getKeyManagerFactory(config.getKeyStorePath(), new StoreProperties(config.getKeyStorePassword(),
        config.getKeyManagerType(), config.getKeyStoreType(), config.getKeyStoreProvider()));
  }

  /**
   * Get key manager factory
   * @param keyStoreFilename Keystore file name
   * @param storeProperties store properties
   * @return Key manager factory
   * @throws IOException
   * @throws GeneralSecurityException
   */
  private static KeyManagerFactory getKeyManagerFactory(String keyStoreFilename, StoreProperties storeProperties)
      throws IOException, GeneralSecurityException {
    InputStream is = getResourceAsStream(keyStoreFilename);
    if (is == null) {
      throw new FileNotFoundException("Certificate file not found: " + getFilenameInfo(keyStoreFilename));
    }
    try {
      return getKeyManagerFactory(is, storeProperties);
    }
    finally {
      try {
        is.close();
      }
      catch (IOException ex) {
        // ignore
      }
    }
  }

  /**
   * Get key manager factory
   * @param keyStoreStream Keystore input stream
   * @param storeProperties store properties
   * @return Key manager factory
   * @throws IOException
   * @throws GeneralSecurityException
   */
  private static KeyManagerFactory getKeyManagerFactory(InputStream keyStoreStream, StoreProperties storeProperties)
      throws IOException, GeneralSecurityException {
    // use provider if given, otherwise use the first matching security provider
    final KeyStore ks;
    if (StringUtils.isNotBlank(storeProperties.getProvider())) {
      ks = KeyStore.getInstance(storeProperties.getType(), storeProperties.getProvider());
    }
    else {
      ks = KeyStore.getInstance(storeProperties.getType());
    }
    ks.load(keyStoreStream, storeProperties.getPassword().toCharArray());
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(storeProperties.getManagerType());
    kmf.init(ks, storeProperties.getPassword().toCharArray());
    return kmf;
  }

  /**
   * Build TrustManagerFactory.
   * @param config Config
   * @return TrustManagerFactory
   * @throws IOException
   * @throws GeneralSecurityException
   */
  public static TrustManagerFactory getTrustManagerFactory(JaxWsClientInitializer config)
      throws IOException, GeneralSecurityException {
    return getTrustManagerFactory(config.getTrustStorePath(), new StoreProperties(config.getTrustStorePassword(),
        config.getTrustManagerType(), config.getTrustStoreType(), config.getTrustStoreProvider()));
  }

  /**
   * Build TrustManagerFactory.
   * @param trustStoreFilename Truststore file name
   * @param storeProperties store properties
   * @return TrustManagerFactory
   * @throws IOException
   * @throws GeneralSecurityException
   */
  private static TrustManagerFactory getTrustManagerFactory(String trustStoreFilename, StoreProperties storeProperties)
      throws IOException, GeneralSecurityException {
    InputStream is = getResourceAsStream(trustStoreFilename);
    if (is == null) {
      throw new FileNotFoundException("Certificate file not found: " + getFilenameInfo(trustStoreFilename));
    }
    try {
      return getTrustManagerFactory(is, storeProperties);
    }
    finally {
      try {
        is.close();
      }
      catch (IOException ex) {
        // ignore
      }
    }
  }

  /**
   * Build TrustManagerFactory.
   * @param trustStoreStream Truststore input stream
   * @param storeProperties store properties
   * @return TrustManagerFactory
   * @throws IOException
   * @throws GeneralSecurityException
   */
  private static TrustManagerFactory getTrustManagerFactory(InputStream trustStoreStream, StoreProperties storeProperties)
      throws IOException, GeneralSecurityException {
    // use provider if given, otherwise use the first matching security provider
    final KeyStore ks;
    if (StringUtils.isNotBlank(storeProperties.getProvider())) {
      ks = KeyStore.getInstance(storeProperties.getType(), storeProperties.getProvider());
    }
    else {
      ks = KeyStore.getInstance(storeProperties.getType());
    }
    ks.load(trustStoreStream, storeProperties.getPassword().toCharArray());
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(storeProperties.getManagerType());
    tmf.init(ks);
    return tmf;
  }

  /**
   * Tries to load the given resource as file, or if no file exists as classpath resource.
   * @param path Filesystem or classpath path
   * @return InputStream or null if neither file nor classpath resource is found
   * @throws IOException
   */
  private static InputStream getResourceAsStream(String path) throws IOException {
    if (StringUtils.isEmpty(path)) {
      return null;
    }

    // first try to load as file
    File file = new File(path);
    if (file.exists() && file.isFile()) {
      return new FileInputStream(file);
    }

    // if not a file fallback to classloder resource
    return CertificateLoader.class.getResourceAsStream(path);
  }

  /**
   * Generate filename info for given path for error messages.
   * @param path Path
   * @return Absolute path
   */
  private static String getFilenameInfo(String path) {
    if (StringUtils.isEmpty(path)) {
      return null;
    }
    try {
      return new File(path).getCanonicalPath();
    }
    catch (IOException ex) {
      return new File(path).getAbsolutePath();
    }
  }

  /**
   * Checks whether a SSL key store is configured.
   * @param config Http client configuration
   * @return true if client certificates are enabled
   */
  public static boolean isSslKeyManagerEnabled(JaxWsClientInitializer config) {
    return StringUtils.isNotEmpty(config.getSslContextType())
        && StringUtils.isNotEmpty(config.getKeyManagerType())
        && StringUtils.isNotEmpty(config.getKeyStoreType())
        && StringUtils.isNotEmpty(config.getKeyStorePath());
  }

  /**
   * Checks whether a SSL trust store is configured.
   * @param config Http client configuration
   * @return true if client certificates are enabled
   */
  public static boolean isSslTrustStoreEnbaled(JaxWsClientInitializer config) {
    return StringUtils.isNotEmpty(config.getSslContextType())
        && StringUtils.isNotEmpty(config.getTrustManagerType())
        && StringUtils.isNotEmpty(config.getTrustStoreType())
        && StringUtils.isNotEmpty(config.getTrustStorePath());
  }

}