com.helger.security.keystore.KeyStoreHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ph-security Show documentation
Show all versions of ph-security Show documentation
Special Java 1.8+ Library with security related functionality
/*
* Copyright (C) 2014-2024 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.Provider;
import java.security.UnrecoverableKeyException;
import java.util.Enumeration;
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.functional.IThrowingConsumer;
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 RW_LOCK = new SimpleReadWriteLock ();
@GuardedBy ("RW_LOCK")
private static IReadableResourceProvider s_aResourceProvider = new ReadableResourceProviderChain (new FileSystemResourceProvider ().setCanReadRelativePaths (true),
new ClassPathResourceProvider ());
@PresentForCodeCoverage
private static final KeyStoreHelper INSTANCE = new KeyStoreHelper ();
private KeyStoreHelper ()
{}
@Nonnull
public static IReadableResourceProvider getResourceProvider ()
{
return RW_LOCK.readLockedGet ( () -> s_aResourceProvider);
}
public static void setResourceProvider (@Nonnull final IReadableResourceProvider aResourceProvider)
{
ValueEnforcer.notNull (aResourceProvider, "ResourceProvider");
RW_LOCK.writeLocked ( () -> s_aResourceProvider = aResourceProvider);
}
@Nonnull
public static KeyStore getSimiliarKeyStore (@Nonnull final KeyStore aOther) throws KeyStoreException
{
ValueEnforcer.notNull (aOther, "Other");
return getSimiliarKeyStore (aOther, null);
}
@Nonnull
public static KeyStore getSimiliarKeyStore (@Nonnull final KeyStore aOther,
@Nullable final Provider aSecurityProvider) throws KeyStoreException
{
ValueEnforcer.notNull (aOther, "Other");
return KeyStore.getInstance (aOther.getType (),
aSecurityProvider != null ? aSecurityProvider : 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 only be null
* for {@link EKeyStoreType#PKCS11}.
* @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
* @deprecated Use the version with char[] as password type
*/
@Nonnull
@Deprecated (forRemoval = true, since = "11.1.9")
public static KeyStore loadKeyStoreDirect (@Nonnull final IKeyStoreType aKeyStoreType,
@Nullable final String sKeyStorePath,
@Nullable final String sKeyStorePassword) throws GeneralSecurityException,
IOException
{
return loadKeyStoreDirect (aKeyStoreType, sKeyStorePath, sKeyStorePassword, null);
}
/**
* 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 only be null
* for {@link EKeyStoreType#PKCS11}.
* @param sKeyStorePassword
* The key store password. May be null
to indicate that no
* password is required.
* @param aSecurityProvider
* The Security Provider to use. May be null
.
* @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
* @since 11.1.1
* @deprecated Use the version with char[] as password type
*/
@Nonnull
@Deprecated (forRemoval = true, since = "11.1.9")
public static KeyStore loadKeyStoreDirect (@Nonnull final IKeyStoreType aKeyStoreType,
@Nullable final String sKeyStorePath,
@Nullable final String sKeyStorePassword,
@Nullable final Provider aSecurityProvider) throws GeneralSecurityException,
IOException
{
return loadKeyStoreDirect (aKeyStoreType,
sKeyStorePath,
sKeyStorePassword == null ? null : sKeyStorePassword.toCharArray (),
aSecurityProvider);
}
/**
* 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 only be null
* for {@link EKeyStoreType#PKCS11} or other key store types that don't
* require a path.
* @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,
@Nullable final String sKeyStorePath,
@Nullable final char [] aKeyStorePassword) throws GeneralSecurityException,
IOException
{
return loadKeyStoreDirect (aKeyStoreType, sKeyStorePath, aKeyStorePassword, null);
}
/**
* 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 only be null
* for {@link EKeyStoreType#PKCS11} or other key store types that don't
* require a path.
* @param aKeyStorePassword
* The key store password. May be null
to indicate that no
* password is required.
* @param aSecurityProvider
* The Security Provider to use. May be null
.
* @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
* @since 11.1.1
*/
@Nonnull
public static KeyStore loadKeyStoreDirect (@Nonnull final IKeyStoreType aKeyStoreType,
@Nullable final String sKeyStorePath,
@Nullable final char [] aKeyStorePassword,
@Nullable final Provider aSecurityProvider) throws GeneralSecurityException,
IOException
{
ValueEnforcer.notNull (aKeyStoreType, "KeyStoreType");
final InputStream aIS;
if (aKeyStoreType.isKeyStorePathRequired ())
{
ValueEnforcer.notNull (sKeyStorePath, "KeyStorePath");
// Open the resource stream
aIS = getResourceProvider ().getInputStream (sKeyStorePath);
if (aIS == null)
throw new IllegalArgumentException ("Failed to open key store '" + sKeyStorePath + "'");
}
else
{
aIS = null;
}
try
{
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Trying to load key store from path '" +
sKeyStorePath +
"' using type " +
aKeyStoreType.getID ());
final KeyStore aKeyStore = aSecurityProvider != null ? aKeyStoreType.getKeyStore (aSecurityProvider)
: 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
{
return createKeyStoreWithOnlyOneItem (aBaseKeyStore, sAliasToCopy, aAliasPassword, null);
}
/**
* 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
* @param aSecurityProvider
* The Security Provider to use. May be null
.
* @return The created in-memory key store
* @throws GeneralSecurityException
* In case of a key store error
* @throws IOException
* In case key store loading fails
* @since 11.1.1
*/
@Nonnull
public static KeyStore createKeyStoreWithOnlyOneItem (@Nonnull final KeyStore aBaseKeyStore,
@Nonnull final String sAliasToCopy,
@Nullable final char [] aAliasPassword,
@Nullable final Provider aSecurityProvider) throws GeneralSecurityException,
IOException
{
ValueEnforcer.notNull (aBaseKeyStore, "BaseKeyStore");
ValueEnforcer.notNull (sAliasToCopy, "AliasToCopy");
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Create a new key store using type " + aBaseKeyStore.getType ());
final KeyStore aKeyStore = getSimiliarKeyStore (aBaseKeyStore, aSecurityProvider);
// 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);
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Copying alias '" + sAliasToCopy + "' from old key store to new key store");
aKeyStore.setEntry (sAliasToCopy, aBaseKeyStore.getEntry (sAliasToCopy, aPP), aPP);
return aKeyStore;
}
private static boolean _isInvalidPasswordException (@Nonnull final Exception ex)
{
return ex instanceof IOException && ex.getCause () instanceof UnrecoverableKeyException;
}
/**
* 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
for all key
* store types that require a path.
* @param sKeyStorePassword
* Password for the key store. May not be null
to succeed.
* @return The key store loading result. Never null
.
* @deprecated Use the version with char[] as password type
*/
@Nonnull
@Deprecated (forRemoval = true, since = "11.1.9")
public static LoadedKeyStore loadKeyStore (@Nonnull final IKeyStoreType aKeyStoreType,
@Nullable final String sKeyStorePath,
@Nullable final String sKeyStorePassword)
{
return loadKeyStore (aKeyStoreType, sKeyStorePath, sKeyStorePassword, null);
}
/**
* 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
for all key
* store types that require a path.
* @param sKeyStorePassword
* Password for the key store. May not be null
to succeed.
* @param aSecurityProvider
* The Security Provider to use. May be null
.
* @return The key store loading result. Never null
.
* @since 11.1.1
* @deprecated Use the version with char[] as password type
*/
@Nonnull
@Deprecated (forRemoval = true, since = "11.1.9")
public static LoadedKeyStore loadKeyStore (@Nonnull final IKeyStoreType aKeyStoreType,
@Nullable final String sKeyStorePath,
@Nullable final String sKeyStorePassword,
@Nullable final Provider aSecurityProvider)
{
return loadKeyStore (aKeyStoreType,
sKeyStorePath,
sKeyStorePassword == null ? null : sKeyStorePassword.toCharArray (),
null);
}
/**
* 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
for all key
* store types that require a path.
* @param aKeyStorePassword
* Password for the key store. May not be null
to succeed.
* @return The key store loading result. Never null
.
* @since 11.1.9
*/
@Nonnull
public static LoadedKeyStore loadKeyStore (@Nonnull final IKeyStoreType aKeyStoreType,
@Nullable final String sKeyStorePath,
@Nullable final char [] aKeyStorePassword)
{
return loadKeyStore (aKeyStoreType, sKeyStorePath, aKeyStorePassword, null);
}
/**
* 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
for all key
* store types that require a path.
* @param aKeyStorePassword
* Password for the key store. May not be null
to succeed.
* @param aSecurityProvider
* The Security Provider to use. May be null
.
* @return The key store loading result. Never null
.
* @since 11.1.9
*/
@Nonnull
public static LoadedKeyStore loadKeyStore (@Nonnull final IKeyStoreType aKeyStoreType,
@Nullable final String sKeyStorePath,
@Nullable final char [] aKeyStorePassword,
@Nullable final Provider aSecurityProvider)
{
ValueEnforcer.notNull (aKeyStoreType, "KeyStoreType");
// Get the parameters for the key store
if (aKeyStoreType.isKeyStorePathRequired () && StringHelper.hasNoText (sKeyStorePath))
return new LoadedKeyStore (null, EKeyStoreLoadError.KEYSTORE_NO_PATH);
final KeyStore aKeyStore;
// Try to load key store
try
{
aKeyStore = loadKeyStoreDirect (aKeyStoreType, sKeyStorePath, aKeyStorePassword, aSecurityProvider);
}
catch (final IllegalArgumentException ex)
{
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 = _isInvalidPasswordException (ex);
LOGGER.warn ("Failed to load key store '" +
sKeyStorePath +
"' of type " +
aKeyStoreType.getID () +
": " +
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");
ValueEnforcer.notNull (aTargetClass, "TargetClass");
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.
final T aKeyEntry;
try
{
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Trying to access key store entry with alias '" +
sKeyStoreKeyAlias +
"' as a " +
aTargetClass.getName ());
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 not 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 not 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 not 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);
}
/**
* Helper method to iterate all aliases of a key store
*
* @param aKeyStore
* The key store to be iterated. May not be null
.
* @param aAliasConsumer
* The consumer for each alias. May not be null
.
* @since 11.1.10
*/
public static void iterateKeyStore (@Nonnull final KeyStore aKeyStore,
@Nonnull final IThrowingConsumer aAliasConsumer)
{
ValueEnforcer.notNull (aKeyStore, "KeyStore");
ValueEnforcer.notNull (aAliasConsumer, "AliasConsumer");
try
{
final Enumeration aAliases = aKeyStore.aliases ();
while (aAliases.hasMoreElements ())
{
final String sAlias = aAliases.nextElement ();
aAliasConsumer.accept (sAlias);
}
}
catch (final KeyStoreException ex)
{
LOGGER.warn ("Failed to iterate key store", ex);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy