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

com.helger.security.keystore.KeyStoreHelper Maven / Gradle / Ivy

There is a newer version: 9.5.5
Show newest version
/**
 * Copyright (C) 2014-2019 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * 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.
 */
package com.helger.security.keystore;

import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStoreException;
import java.security.UnrecoverableKeyException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;

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

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.PresentForCodeCoverage;
import com.helger.commons.concurrent.SimpleReadWriteLock;
import com.helger.commons.io.resourceprovider.ClassPathResourceProvider;
import com.helger.commons.io.resourceprovider.FileSystemResourceProvider;
import com.helger.commons.io.resourceprovider.IReadableResourceProvider;
import com.helger.commons.io.resourceprovider.ReadableResourceProviderChain;
import com.helger.commons.io.stream.StreamHelper;
import com.helger.commons.lang.ClassHelper;
import com.helger.commons.string.StringHelper;

/**
 * Helper methods to access Java key stores of type JKS (Java KeyStore).
 *
 * @author PEPPOL.AT, BRZ, Philip Helger
 */
@ThreadSafe
public final class KeyStoreHelper
{
  private static final Logger LOGGER = LoggerFactory.getLogger (KeyStoreHelper.class);
  private static final SimpleReadWriteLock s_aRWLock = new SimpleReadWriteLock ();
  @GuardedBy ("s_aRWLock")
  private static IReadableResourceProvider s_aResourceProvider = new ReadableResourceProviderChain (new FileSystemResourceProvider ().setCanReadRelativePaths (true),
                                                                                                    new ClassPathResourceProvider ());

  @PresentForCodeCoverage
  private static final KeyStoreHelper s_aInstance = new KeyStoreHelper ();

  private KeyStoreHelper ()
  {}

  @Nonnull
  public static IReadableResourceProvider getResourceProvider ()
  {
    return s_aRWLock.readLocked ( () -> s_aResourceProvider);
  }

  public static void setResourceProvider (@Nonnull final IReadableResourceProvider aResourceProvider)
  {
    ValueEnforcer.notNull (aResourceProvider, "ResourceProvider");
    s_aRWLock.writeLocked ( () -> s_aResourceProvider = aResourceProvider);
  }

  @Nonnull
  public static KeyStore getSimiliarKeyStore (@Nonnull final KeyStore aOther) throws KeyStoreException
  {
    return KeyStore.getInstance (aOther.getType (), aOther.getProvider ());
  }

  /**
   * Load a key store from a resource.
   *
   * @param aKeyStoreType
   *        Type of key store. May not be null.
   * @param sKeyStorePath
   *        The path pointing to the key store. May not be null.
   * @param sKeyStorePassword
   *        The key store password. May be null to indicate that no
   *        password is required.
   * @return The Java key-store object.
   * @throws GeneralSecurityException
   *         In case of a key store error
   * @throws IOException
   *         In case key store loading fails
   * @throws IllegalArgumentException
   *         If the key store path is invalid
   */
  @Nonnull
  public static KeyStore loadKeyStoreDirect (@Nonnull final IKeyStoreType aKeyStoreType,
                                             @Nonnull final String sKeyStorePath,
                                             @Nullable final String sKeyStorePassword) throws GeneralSecurityException,
                                                                                       IOException
  {
    return loadKeyStoreDirect (aKeyStoreType,
                               sKeyStorePath,
                               sKeyStorePassword == null ? null : sKeyStorePassword.toCharArray ());
  }

  /**
   * Load a key store from a resource.
   *
   * @param aKeyStoreType
   *        Type of key store. May not be null.
   * @param sKeyStorePath
   *        The path pointing to the key store. May not be null.
   * @param aKeyStorePassword
   *        The key store password. May be null to indicate that no
   *        password is required.
   * @return The Java key-store object.
   * @see KeyStore#load(InputStream, char[])
   * @throws GeneralSecurityException
   *         In case of a key store error
   * @throws IOException
   *         In case key store loading fails
   * @throws IllegalArgumentException
   *         If the key store path is invalid
   */
  @Nonnull
  public static KeyStore loadKeyStoreDirect (@Nonnull final IKeyStoreType aKeyStoreType,
                                             @Nonnull final String sKeyStorePath,
                                             @Nullable final char [] aKeyStorePassword) throws GeneralSecurityException,
                                                                                        IOException
  {
    ValueEnforcer.notNull (aKeyStoreType, "KeyStoreType");
    ValueEnforcer.notNull (sKeyStorePath, "KeyStorePath");

    // Open the resource stream
    final InputStream aIS = getResourceProvider ().getInputStream (sKeyStorePath);
    if (aIS == null)
      throw new IllegalArgumentException ("Failed to open key store '" + sKeyStorePath + "'");

    try
    {
      final KeyStore aKeyStore = aKeyStoreType.getKeyStore ();
      aKeyStore.load (aIS, aKeyStorePassword);
      return aKeyStore;
    }
    catch (final KeyStoreException ex)
    {
      throw new IllegalStateException ("No provider can handle key stores of type " + aKeyStoreType, ex);
    }
    finally
    {
      StreamHelper.close (aIS);
    }
  }

  /**
   * Create a new key store based on an existing key store
   *
   * @param aBaseKeyStore
   *        The source key store. May not be null
   * @param sAliasToCopy
   *        The name of the alias in the source key store that should be put in
   *        the new key store
   * @param aAliasPassword
   *        The optional password to access the alias in the source key store.
   *        If it is not null the same password will be used in the
   *        created key store
   * @return The created in-memory key store
   * @throws GeneralSecurityException
   *         In case of a key store error
   * @throws IOException
   *         In case key store loading fails
   */
  @Nonnull
  public static KeyStore createKeyStoreWithOnlyOneItem (@Nonnull final KeyStore aBaseKeyStore,
                                                        @Nonnull final String sAliasToCopy,
                                                        @Nullable final char [] aAliasPassword) throws GeneralSecurityException,
                                                                                                IOException
  {
    ValueEnforcer.notNull (aBaseKeyStore, "BaseKeyStore");
    ValueEnforcer.notNull (sAliasToCopy, "AliasToCopy");

    final KeyStore aKeyStore = getSimiliarKeyStore (aBaseKeyStore);
    // null stream means: create new key store
    aKeyStore.load (null, null);

    // Do we need a password?
    ProtectionParameter aPP = null;
    if (aAliasPassword != null)
      aPP = new PasswordProtection (aAliasPassword);

    aKeyStore.setEntry (sAliasToCopy, aBaseKeyStore.getEntry (sAliasToCopy, aPP), aPP);
    return aKeyStore;
  }

  /**
   * Load the provided key store in a safe manner.
   *
   * @param aKeyStoreType
   *        Type of key store. May not be null.
   * @param sKeyStorePath
   *        Path to the key store. May not be null to succeed.
   * @param sKeyStorePassword
   *        Password for the key store. May not be null to succeed.
   * @return The key store loading result. Never null.
   */
  @Nonnull
  public static LoadedKeyStore loadKeyStore (@Nonnull final IKeyStoreType aKeyStoreType,
                                             @Nullable final String sKeyStorePath,
                                             @Nullable final String sKeyStorePassword)
  {
    ValueEnforcer.notNull (aKeyStoreType, "KeyStoreType");

    // Get the parameters for the key store
    if (StringHelper.hasNoText (sKeyStorePath))
      return new LoadedKeyStore (null, EKeyStoreLoadError.KEYSTORE_NO_PATH);

    KeyStore aKeyStore = null;
    // Try to load key store
    try
    {
      aKeyStore = loadKeyStoreDirect (aKeyStoreType, sKeyStorePath, sKeyStorePassword);
    }
    catch (final IllegalArgumentException ex)
    {
      if (LOGGER.isWarnEnabled ())
        LOGGER.warn ("No such key store '" + sKeyStorePath + "': " + ex.getMessage (), ex.getCause ());

      return new LoadedKeyStore (null,
                                 EKeyStoreLoadError.KEYSTORE_LOAD_ERROR_NON_EXISTING,
                                 sKeyStorePath,
                                 ex.getMessage ());
    }
    catch (final Exception ex)
    {
      final boolean bInvalidPW = ex instanceof IOException && ex.getCause () instanceof UnrecoverableKeyException;

      if (LOGGER.isWarnEnabled ())
        LOGGER.warn ("Failed to load key store '" + sKeyStorePath + "': " + ex.getMessage (),
                     bInvalidPW ? null : ex.getCause ());

      return new LoadedKeyStore (null,
                                 bInvalidPW ? EKeyStoreLoadError.KEYSTORE_INVALID_PASSWORD
                                            : EKeyStoreLoadError.KEYSTORE_LOAD_ERROR_FORMAT_ERROR,
                                 sKeyStorePath,
                                 ex.getMessage ());
    }

    // Finally success
    return new LoadedKeyStore (aKeyStore, null);
  }

  @Nonnull
  private static  LoadedKey  _loadKey (@Nonnull final KeyStore aKeyStore,
                                                                    @Nonnull final String sKeyStorePath,
                                                                    @Nullable final String sKeyStoreKeyAlias,
                                                                    @Nullable final char [] aKeyStoreKeyPassword,
                                                                    @Nonnull final Class  aTargetClass)
  {
    ValueEnforcer.notNull (aKeyStore, "KeyStore");
    ValueEnforcer.notNull (sKeyStorePath, "KeyStorePath");

    if (StringHelper.hasNoText (sKeyStoreKeyAlias))
      return new LoadedKey <> (null, EKeyStoreLoadError.KEY_NO_ALIAS, sKeyStorePath);

    if (aKeyStoreKeyPassword == null)
      return new LoadedKey <> (null, EKeyStoreLoadError.KEY_NO_PASSWORD, sKeyStoreKeyAlias, sKeyStorePath);

    // Try to load the key.
    T aKeyEntry = null;
    try
    {
      final KeyStore.ProtectionParameter aProtection = new KeyStore.PasswordProtection (aKeyStoreKeyPassword);
      final KeyStore.Entry aEntry = aKeyStore.getEntry (sKeyStoreKeyAlias, aProtection);
      if (aEntry == null)
      {
        // No such entry
        return new LoadedKey <> (null, EKeyStoreLoadError.KEY_INVALID_ALIAS, sKeyStoreKeyAlias, sKeyStorePath);
      }
      if (!aTargetClass.isAssignableFrom (aEntry.getClass ()))
      {
        // Not a matching
        return new LoadedKey <> (null,
                                 EKeyStoreLoadError.KEY_INVALID_TYPE,
                                 sKeyStoreKeyAlias,
                                 sKeyStorePath,
                                 ClassHelper.getClassName (aEntry));
      }
      aKeyEntry = aTargetClass.cast (aEntry);
    }
    catch (final UnrecoverableKeyException ex)
    {
      return new LoadedKey <> (null,
                               EKeyStoreLoadError.KEY_INVALID_PASSWORD,
                               sKeyStoreKeyAlias,
                               sKeyStorePath,
                               ex.getMessage ());
    }
    catch (final GeneralSecurityException ex)
    {
      return new LoadedKey <> (null,
                               EKeyStoreLoadError.KEY_LOAD_ERROR,
                               sKeyStoreKeyAlias,
                               sKeyStorePath,
                               ex.getMessage ());
    }

    // Finally success
    return new LoadedKey <> (aKeyEntry, null);
  }

  /**
   * Load the specified private key entry from the provided key store.
   *
   * @param aKeyStore
   *        The key store to load the key from. May not be null.
   * @param sKeyStorePath
   *        Key store path. For nice error messages only. May be
   *        null.
   * @param sKeyStoreKeyAlias
   *        The alias to be resolved in the key store. Must be non-
   *        null to succeed.
   * @param aKeyStoreKeyPassword
   *        The key password for the key store. Must be non-null to
   *        succeed.
   * @return The key loading result. Never null.
   */
  @Nonnull
  public static LoadedKey  loadPrivateKey (@Nonnull final KeyStore aKeyStore,
                                                                     @Nonnull final String sKeyStorePath,
                                                                     @Nullable final String sKeyStoreKeyAlias,
                                                                     @Nullable final char [] aKeyStoreKeyPassword)
  {
    return _loadKey (aKeyStore, sKeyStorePath, sKeyStoreKeyAlias, aKeyStoreKeyPassword, KeyStore.PrivateKeyEntry.class);
  }

  /**
   * Load the specified secret key entry from the provided key store.
   *
   * @param aKeyStore
   *        The key store to load the key from. May not be null.
   * @param sKeyStorePath
   *        Key store path. For nice error messages only. May be
   *        null.
   * @param sKeyStoreKeyAlias
   *        The alias to be resolved in the key store. Must be non-
   *        null to succeed.
   * @param aKeyStoreKeyPassword
   *        The key password for the key store. Must be non-null to
   *        succeed.
   * @return The key loading result. Never null.
   */
  @Nonnull
  public static LoadedKey  loadSecretKey (@Nonnull final KeyStore aKeyStore,
                                                                   @Nonnull final String sKeyStorePath,
                                                                   @Nullable final String sKeyStoreKeyAlias,
                                                                   @Nullable final char [] aKeyStoreKeyPassword)
  {
    return _loadKey (aKeyStore, sKeyStorePath, sKeyStoreKeyAlias, aKeyStoreKeyPassword, KeyStore.SecretKeyEntry.class);
  }

  /**
   * Load the specified private key entry from the provided key store.
   *
   * @param aKeyStore
   *        The key store to load the key from. May not be null.
   * @param sKeyStorePath
   *        Key store path. For nice error messages only. May be
   *        null.
   * @param sKeyStoreKeyAlias
   *        The alias to be resolved in the key store. Must be non-
   *        null to succeed.
   * @param aKeyStoreKeyPassword
   *        The key password for the key store. Must be non-null to
   *        succeed.
   * @return The key loading result. Never null.
   */
  @Nonnull
  public static LoadedKey  loadTrustedCertificateKey (@Nonnull final KeyStore aKeyStore,
                                                                                        @Nonnull final String sKeyStorePath,
                                                                                        @Nullable final String sKeyStoreKeyAlias,
                                                                                        @Nullable final char [] aKeyStoreKeyPassword)
  {
    return _loadKey (aKeyStore,
                     sKeyStorePath,
                     sKeyStoreKeyAlias,
                     aKeyStoreKeyPassword,
                     KeyStore.TrustedCertificateEntry.class);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy