diff --git a/ph-security/src/main/java/com/helger/security/keystore/IKeyStoreAndKeyDescriptor.java b/ph-security/src/main/java/com/helger/security/keystore/IKeyStoreAndKeyDescriptor.java
new file mode 100644
index 000000000..a522f9ba6
--- /dev/null
+++ b/ph-security/src/main/java/com/helger/security/keystore/IKeyStoreAndKeyDescriptor.java
@@ -0,0 +1,57 @@
+/*
+ * 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.security.KeyStore.PrivateKeyEntry;
+
+import javax.annotation.Nonnull;
+
+import com.helger.commons.annotation.Nonempty;
+
+/**
+ * Interface describing the parameters needed to reference a key store with a
+ * private key.
+ *
+ * @author Philip Helger
+ * @since 11.1.10
+ */
+public interface IKeyStoreAndKeyDescriptor extends IKeyStoreDescriptor
+{
+ /**
+ * Note: the case sensitivity of the key alias depends on the key store type.
+ *
+ * @return The alias of the key inside a key store. May neither be
+ * null
nor empty.
+ */
+ @Nonnull
+ @Nonempty
+ String getKeyAlias ();
+
+ /**
+ * @return The password required to access the key inside the key store. May
+ * not be null
but may be empty.
+ */
+ @Nonnull
+ char [] getKeyPassword ();
+
+ /**
+ * @return The loaded key based on the loaded key store and the parameters in
+ * this descriptor.
+ */
+ @Nonnull
+ LoadedKey loadKey ();
+}
diff --git a/ph-security/src/main/java/com/helger/security/keystore/IKeyStoreDescriptor.java b/ph-security/src/main/java/com/helger/security/keystore/IKeyStoreDescriptor.java
new file mode 100644
index 000000000..f1d02ca6e
--- /dev/null
+++ b/ph-security/src/main/java/com/helger/security/keystore/IKeyStoreDescriptor.java
@@ -0,0 +1,69 @@
+/*
+ * 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.security.Provider;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.helger.commons.annotation.Nonempty;
+
+/**
+ * Interface describing the parameters needed to reference a key store (without
+ * a private key).
+ *
+ * @author Philip Helger
+ * @since 11.1.10
+ */
+public interface IKeyStoreDescriptor
+{
+ /**
+ * @return The type of the key store. May not be null
.
+ */
+ @Nonnull
+ IKeyStoreType getKeyStoreType ();
+
+ /**
+ * @return The path to the key store. May neither be null
nor
+ * empty. The interpretation of the path is implementation dependent.
+ */
+ @Nonnull
+ @Nonempty
+ String getKeyStorePath ();
+
+ /**
+ * @return The password required to open the key store. May not be
+ * null
but may be empty.
+ */
+ @Nonnull
+ char [] getKeyStorePassword ();
+
+ /**
+ * @return The Java security provider for loading the key store. May be
+ * null
.
+ */
+ @Nullable
+ Provider getProvider ();
+
+ /**
+ * @return The loaded key store based on the parameters in this descriptor.
+ * Never null
.
+ */
+ @Nonnull
+ LoadedKeyStore loadKeyStore ();
+}
diff --git a/ph-security/src/main/java/com/helger/security/keystore/ITrustStoreDescriptor.java b/ph-security/src/main/java/com/helger/security/keystore/ITrustStoreDescriptor.java
new file mode 100644
index 000000000..7a2f89af5
--- /dev/null
+++ b/ph-security/src/main/java/com/helger/security/keystore/ITrustStoreDescriptor.java
@@ -0,0 +1,68 @@
+/*
+ * 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.security.Provider;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.helger.commons.annotation.Nonempty;
+
+/**
+ * Interface describing the parameters needed to reference a trust store.
+ *
+ * @author Philip Helger
+ * @since 11.1.10
+ */
+public interface ITrustStoreDescriptor
+{
+ /**
+ * @return The type of the trust store. May not be null
.
+ */
+ @Nonnull
+ IKeyStoreType getTrustStoreType ();
+
+ /**
+ * @return The path to the trust store. May neither be null
nor
+ * empty. The interpretation of the path is implementation dependent.
+ */
+ @Nonnull
+ @Nonempty
+ String getTrustStorePath ();
+
+ /**
+ * @return The password required to open the trust store. May not be
+ * null
but may be empty.
+ */
+ @Nonnull
+ char [] getTrustStorePassword ();
+
+ /**
+ * @return The Java security provider for loading the trust store. May be
+ * null
.
+ */
+ @Nullable
+ Provider getProvider ();
+
+ /**
+ * @return The loaded trust store based on the parameters in this descriptor.
+ * Never null
.
+ */
+ @Nonnull
+ LoadedKeyStore loadTrustStore ();
+}
diff --git a/ph-security/src/main/java/com/helger/security/keystore/KeyStoreAndKeyDescriptor.java b/ph-security/src/main/java/com/helger/security/keystore/KeyStoreAndKeyDescriptor.java
new file mode 100644
index 000000000..7b67e98d5
--- /dev/null
+++ b/ph-security/src/main/java/com/helger/security/keystore/KeyStoreAndKeyDescriptor.java
@@ -0,0 +1,267 @@
+/*
+ * 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.security.KeyStore.PrivateKeyEntry;
+import java.security.Provider;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.helger.commons.ValueEnforcer;
+import com.helger.commons.annotation.Nonempty;
+import com.helger.commons.annotation.ReturnsMutableObject;
+import com.helger.commons.builder.IBuilder;
+import com.helger.commons.string.StringHelper;
+import com.helger.commons.string.ToStringGenerator;
+
+/**
+ * The default implementation of {@link IKeyStoreAndKeyDescriptor}.
+ *
+ * @author Philip Helger
+ * @since 11.1.10
+ */
+public class KeyStoreAndKeyDescriptor implements IKeyStoreAndKeyDescriptor
+{
+ private final IKeyStoreType m_aType;
+ private final String m_sPath;
+ private final char [] m_aPassword;
+ private final Provider m_aProvider;
+ private final String m_sKeyAlias;
+ private final char [] m_aKeyPassword;
+ // Lazily initialized
+ private LoadedKeyStore m_aLKS;
+ private LoadedKey m_aLK;
+
+ public KeyStoreAndKeyDescriptor (@Nonnull final IKeyStoreType aType,
+ @Nonnull @Nonempty final String sPath,
+ @Nonnull final char [] aPassword,
+ @Nullable final Provider aProvider,
+ @Nonnull @Nonempty final String sKeyAlias,
+ @Nonnull final char [] aKeyPassword)
+ {
+ ValueEnforcer.notNull (aType, "Type");
+ ValueEnforcer.notEmpty (sPath, "Path");
+ ValueEnforcer.notNull (aPassword, "Password");
+ ValueEnforcer.notEmpty (sKeyAlias, "KeyAlias");
+ ValueEnforcer.notNull (aKeyPassword, "KeyPassword");
+ m_aType = aType;
+ m_sPath = sPath;
+ m_aPassword = aPassword;
+ m_aProvider = aProvider;
+ m_sKeyAlias = sKeyAlias;
+ m_aKeyPassword = aKeyPassword;
+ }
+
+ @Nonnull
+ public final IKeyStoreType getKeyStoreType ()
+ {
+ return m_aType;
+ }
+
+ @Nonnull
+ @Nonempty
+ public final String getKeyStorePath ()
+ {
+ return m_sPath;
+ }
+
+ @Nonnull
+ @ReturnsMutableObject
+ public final char [] getKeyStorePassword ()
+ {
+ return m_aPassword;
+ }
+
+ @Nullable
+ public final Provider getProvider ()
+ {
+ return m_aProvider;
+ }
+
+ @Nonnull
+ public LoadedKeyStore loadKeyStore ()
+ {
+ LoadedKeyStore ret = m_aLKS;
+ if (ret == null)
+ ret = m_aLKS = KeyStoreHelper.loadKeyStore (m_aType, m_sPath, m_aPassword, m_aProvider);
+ return ret;
+ }
+
+ @Nonnull
+ @Nonempty
+ public final String getKeyAlias ()
+ {
+ return m_sKeyAlias;
+ }
+
+ @Nonnull
+ @ReturnsMutableObject
+ public final char [] getKeyPassword ()
+ {
+ return m_aKeyPassword;
+ }
+
+ @Nonnull
+ public LoadedKey loadKey ()
+ {
+ LoadedKey ret = m_aLK;
+ if (ret == null)
+ {
+ ret = m_aLK = KeyStoreHelper.loadPrivateKey (loadKeyStore ().getKeyStore (),
+ m_sPath,
+ m_sKeyAlias,
+ m_aKeyPassword);
+ }
+ return ret;
+ }
+
+ @Override
+ public String toString ()
+ {
+ return new ToStringGenerator (null).append ("Type", m_aType)
+ .append ("Path", m_sPath)
+ .appendPassword ("Password")
+ .appendIfNotNull ("Provider", m_aProvider)
+ .append ("KeyAlias", m_sKeyAlias)
+ .appendPassword ("KeyPassword")
+ .getToString ();
+ }
+
+ /**
+ * @return A new builder for {@link KeyStoreAndKeyDescriptor} objects. Never
+ * null
.
+ */
+ @Nonnull
+ public static KeyStoreAndKeyDescriptorBuilder builder ()
+ {
+ return new KeyStoreAndKeyDescriptorBuilder ();
+ }
+
+ /**
+ * Create a new builder using the provided descriptor.
+ *
+ * @param a
+ * The existing descriptor. May not be null
.
+ * @return A new builder for {@link KeyStoreAndKeyDescriptor} objects. Never
+ * null
.
+ */
+ @Nonnull
+ public static KeyStoreAndKeyDescriptorBuilder builder (@Nonnull final KeyStoreAndKeyDescriptor a)
+ {
+ return new KeyStoreAndKeyDescriptorBuilder (a);
+ }
+
+ /**
+ * Builder class for class {@link KeyStoreAndKeyDescriptor}.
+ *
+ * @author Philip Helger
+ */
+ public static class KeyStoreAndKeyDescriptorBuilder implements IBuilder
+ {
+ private IKeyStoreType m_aType;
+ private String m_sPath;
+ private char [] m_aPassword;
+ private Provider m_aProvider;
+ private String m_sKeyAlias;
+ private char [] m_aKeyPassword;
+
+ public KeyStoreAndKeyDescriptorBuilder ()
+ {}
+
+ public KeyStoreAndKeyDescriptorBuilder (@Nonnull final KeyStoreAndKeyDescriptor aSrc)
+ {
+ type (aSrc.m_aType).path (aSrc.m_sPath)
+ .password (aSrc.m_aPassword)
+ .provider (aSrc.m_aProvider)
+ .keyAlias (aSrc.m_sKeyAlias)
+ .keyPassword (aSrc.m_aKeyPassword);
+ }
+
+ @Nonnull
+ public final KeyStoreAndKeyDescriptorBuilder type (@Nullable final IKeyStoreType a)
+ {
+ m_aType = a;
+ return this;
+ }
+
+ @Nonnull
+ public final KeyStoreAndKeyDescriptorBuilder path (@Nullable final String s)
+ {
+ m_sPath = s;
+ return this;
+ }
+
+ @Nonnull
+ public final KeyStoreAndKeyDescriptorBuilder password (@Nullable final String s)
+ {
+ return password (s == null ? null : s.toCharArray ());
+ }
+
+ @Nonnull
+ public final KeyStoreAndKeyDescriptorBuilder password (@Nullable final char [] a)
+ {
+ m_aPassword = a;
+ return this;
+ }
+
+ @Nonnull
+ public final KeyStoreAndKeyDescriptorBuilder provider (@Nullable final Provider a)
+ {
+ m_aProvider = a;
+ return this;
+ }
+
+ @Nonnull
+ public final KeyStoreAndKeyDescriptorBuilder keyAlias (@Nullable final String s)
+ {
+ m_sKeyAlias = s;
+ return this;
+ }
+
+ @Nonnull
+ public final KeyStoreAndKeyDescriptorBuilder keyPassword (@Nullable final String s)
+ {
+ return keyPassword (s == null ? null : s.toCharArray ());
+ }
+
+ @Nonnull
+ public final KeyStoreAndKeyDescriptorBuilder keyPassword (@Nullable final char [] a)
+ {
+ m_aKeyPassword = a;
+ return this;
+ }
+
+ @Nonnull
+ public KeyStoreAndKeyDescriptor build () throws IllegalStateException
+ {
+ if (m_aType == null)
+ throw new IllegalStateException ("Type is missing");
+ if (StringHelper.hasNoText (m_sPath))
+ throw new IllegalStateException ("Path is empty");
+ if (m_aPassword == null)
+ throw new IllegalStateException ("Password is missing");
+ // Provider may be null
+ if (StringHelper.hasNoText (m_sKeyAlias))
+ throw new IllegalStateException ("KeyAlias is empty");
+ if (m_aKeyPassword == null)
+ throw new IllegalStateException ("KeyPassword is missing");
+
+ return new KeyStoreAndKeyDescriptor (m_aType, m_sPath, m_aPassword, m_aProvider, m_sKeyAlias, m_aKeyPassword);
+ }
+ }
+}
diff --git a/ph-security/src/main/java/com/helger/security/keystore/KeyStoreDescriptor.java b/ph-security/src/main/java/com/helger/security/keystore/KeyStoreDescriptor.java
new file mode 100644
index 000000000..c26dbadb7
--- /dev/null
+++ b/ph-security/src/main/java/com/helger/security/keystore/KeyStoreDescriptor.java
@@ -0,0 +1,197 @@
+/*
+ * 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.security.Provider;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.helger.commons.ValueEnforcer;
+import com.helger.commons.annotation.Nonempty;
+import com.helger.commons.annotation.ReturnsMutableObject;
+import com.helger.commons.builder.IBuilder;
+import com.helger.commons.string.StringHelper;
+import com.helger.commons.string.ToStringGenerator;
+
+/**
+ * The default implementation of {@link IKeyStoreDescriptor}.
+ *
+ * @author Philip Helger
+ * @since 11.1.10
+ */
+public class KeyStoreDescriptor implements IKeyStoreDescriptor
+{
+ private final IKeyStoreType m_aType;
+ private final String m_sPath;
+ private final char [] m_aPassword;
+ private final Provider m_aProvider;
+ // Lazily initialized
+ private LoadedKeyStore m_aLKS;
+
+ public KeyStoreDescriptor (@Nonnull final IKeyStoreType aType,
+ @Nonnull @Nonempty final String sPath,
+ @Nonnull final char [] aPassword,
+ @Nullable final Provider aProvider)
+ {
+ ValueEnforcer.notNull (aType, "Type");
+ ValueEnforcer.notEmpty (sPath, "Path");
+ ValueEnforcer.notNull (aPassword, "Password");
+ m_aType = aType;
+ m_sPath = sPath;
+ m_aPassword = aPassword;
+ m_aProvider = aProvider;
+ }
+
+ @Nonnull
+ public final IKeyStoreType getKeyStoreType ()
+ {
+ return m_aType;
+ }
+
+ @Nonnull
+ @Nonempty
+ public final String getKeyStorePath ()
+ {
+ return m_sPath;
+ }
+
+ @Nonnull
+ @ReturnsMutableObject
+ public final char [] getKeyStorePassword ()
+ {
+ return m_aPassword;
+ }
+
+ @Nullable
+ public final Provider getProvider ()
+ {
+ return m_aProvider;
+ }
+
+ @Nonnull
+ public LoadedKeyStore loadKeyStore ()
+ {
+ LoadedKeyStore ret = m_aLKS;
+ if (ret == null)
+ ret = m_aLKS = KeyStoreHelper.loadKeyStore (m_aType, m_sPath, m_aPassword, m_aProvider);
+ return ret;
+ }
+
+ @Override
+ public String toString ()
+ {
+ return new ToStringGenerator (null).append ("Type", m_aType)
+ .append ("Path", m_sPath)
+ .appendPassword ("Password")
+ .appendIfNotNull ("Provider", m_aProvider)
+ .getToString ();
+ }
+
+ /**
+ * @return A new builder for {@link KeyStoreDescriptor} objects. Never
+ * null
.
+ */
+ @Nonnull
+ public static KeyStoreDescriptorBuilder builder ()
+ {
+ return new KeyStoreDescriptorBuilder ();
+ }
+
+ /**
+ * Create a new builder using the provided descriptor.
+ *
+ * @param a
+ * The existing descriptor. May not be null
.
+ * @return A new builder for {@link KeyStoreDescriptor} objects. Never
+ * null
.
+ */
+ @Nonnull
+ public static KeyStoreDescriptorBuilder builder (@Nonnull final KeyStoreDescriptor a)
+ {
+ return new KeyStoreDescriptorBuilder (a);
+ }
+
+ /**
+ * Builder class for class {@link KeyStoreDescriptor}.
+ *
+ * @author Philip Helger
+ */
+ public static class KeyStoreDescriptorBuilder implements IBuilder
+ {
+ private IKeyStoreType m_aType;
+ private String m_sPath;
+ private char [] m_aPassword;
+ private Provider m_aProvider;
+
+ public KeyStoreDescriptorBuilder ()
+ {}
+
+ public KeyStoreDescriptorBuilder (@Nonnull final KeyStoreDescriptor aSrc)
+ {
+ type (aSrc.m_aType).path (aSrc.m_sPath).password (aSrc.m_aPassword).provider (aSrc.m_aProvider);
+ }
+
+ @Nonnull
+ public final KeyStoreDescriptorBuilder type (@Nullable final IKeyStoreType a)
+ {
+ m_aType = a;
+ return this;
+ }
+
+ @Nonnull
+ public final KeyStoreDescriptorBuilder path (@Nullable final String s)
+ {
+ m_sPath = s;
+ return this;
+ }
+
+ @Nonnull
+ public final KeyStoreDescriptorBuilder password (@Nullable final String s)
+ {
+ return password (s == null ? null : s.toCharArray ());
+ }
+
+ @Nonnull
+ public final KeyStoreDescriptorBuilder password (@Nullable final char [] a)
+ {
+ m_aPassword = a;
+ return this;
+ }
+
+ @Nonnull
+ public final KeyStoreDescriptorBuilder provider (@Nullable final Provider a)
+ {
+ m_aProvider = a;
+ return this;
+ }
+
+ @Nonnull
+ public KeyStoreDescriptor build () throws IllegalStateException
+ {
+ if (m_aType == null)
+ throw new IllegalStateException ("Type is missing");
+ if (StringHelper.hasNoText (m_sPath))
+ throw new IllegalStateException ("Path is empty");
+ if (m_aPassword == null)
+ throw new IllegalStateException ("Password is missing");
+ // Provider may be null
+
+ return new KeyStoreDescriptor (m_aType, m_sPath, m_aPassword, m_aProvider);
+ }
+ }
+}
diff --git a/ph-security/src/main/java/com/helger/security/keystore/KeyStoreHelper.java b/ph-security/src/main/java/com/helger/security/keystore/KeyStoreHelper.java
index e27e7f553..79ad1665b 100644
--- a/ph-security/src/main/java/com/helger/security/keystore/KeyStoreHelper.java
+++ b/ph-security/src/main/java/com/helger/security/keystore/KeyStoreHelper.java
@@ -25,6 +25,7 @@
import java.security.KeyStoreException;
import java.security.Provider;
import java.security.UnrecoverableKeyException;
+import java.util.Enumeration;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -37,6 +38,7 @@
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;
@@ -625,4 +627,34 @@ public static LoadedKey loadTrustedCertificat
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);
+ }
+ }
}
diff --git a/ph-security/src/main/java/com/helger/security/keystore/TrustStoreDescriptor.java b/ph-security/src/main/java/com/helger/security/keystore/TrustStoreDescriptor.java
new file mode 100644
index 000000000..cd09477ce
--- /dev/null
+++ b/ph-security/src/main/java/com/helger/security/keystore/TrustStoreDescriptor.java
@@ -0,0 +1,197 @@
+/*
+ * 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.security.Provider;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.helger.commons.ValueEnforcer;
+import com.helger.commons.annotation.Nonempty;
+import com.helger.commons.annotation.ReturnsMutableObject;
+import com.helger.commons.builder.IBuilder;
+import com.helger.commons.string.StringHelper;
+import com.helger.commons.string.ToStringGenerator;
+
+/**
+ * The default implementation of {@link ITrustStoreDescriptor}.
+ *
+ * @author Philip Helger
+ * @since 11.1.10
+ */
+public class TrustStoreDescriptor implements ITrustStoreDescriptor
+{
+ private final IKeyStoreType m_aType;
+ private final String m_sPath;
+ private final char [] m_aPassword;
+ private final Provider m_aProvider;
+ // Lazily initialized
+ private LoadedKeyStore m_aLTS;
+
+ public TrustStoreDescriptor (@Nonnull final IKeyStoreType aType,
+ @Nonnull @Nonempty final String sPath,
+ @Nonnull final char [] aPassword,
+ @Nullable final Provider aProvider)
+ {
+ ValueEnforcer.notNull (aType, "Type");
+ ValueEnforcer.notEmpty (sPath, "Path");
+ ValueEnforcer.notNull (aPassword, "Password");
+ m_aType = aType;
+ m_sPath = sPath;
+ m_aPassword = aPassword;
+ m_aProvider = aProvider;
+ }
+
+ @Nonnull
+ public final IKeyStoreType getTrustStoreType ()
+ {
+ return m_aType;
+ }
+
+ @Nonnull
+ @Nonempty
+ public final String getTrustStorePath ()
+ {
+ return m_sPath;
+ }
+
+ @Nonnull
+ @ReturnsMutableObject
+ public final char [] getTrustStorePassword ()
+ {
+ return m_aPassword;
+ }
+
+ @Nullable
+ public final Provider getProvider ()
+ {
+ return m_aProvider;
+ }
+
+ @Nonnull
+ public LoadedKeyStore loadTrustStore ()
+ {
+ LoadedKeyStore ret = m_aLTS;
+ if (ret == null)
+ ret = m_aLTS = KeyStoreHelper.loadKeyStore (m_aType, m_sPath, m_aPassword, m_aProvider);
+ return ret;
+ }
+
+ @Override
+ public String toString ()
+ {
+ return new ToStringGenerator (null).append ("Type", m_aType)
+ .append ("Path", m_sPath)
+ .appendPassword ("Password")
+ .appendIfNotNull ("Provider", m_aProvider)
+ .getToString ();
+ }
+
+ /**
+ * @return A new builder for {@link TrustStoreDescriptor} objects. Never
+ * null
.
+ */
+ @Nonnull
+ public static TrustStoreDescriptorBuilder builder ()
+ {
+ return new TrustStoreDescriptorBuilder ();
+ }
+
+ /**
+ * Create a new builder using the provided descriptor.
+ *
+ * @param a
+ * The existing descriptor. May not be null
.
+ * @return A new builder for {@link TrustStoreDescriptor} objects. Never
+ * null
.
+ */
+ @Nonnull
+ public static TrustStoreDescriptorBuilder builder (@Nonnull final TrustStoreDescriptor a)
+ {
+ return new TrustStoreDescriptorBuilder (a);
+ }
+
+ /**
+ * Builder class for class {@link TrustStoreDescriptor}.
+ *
+ * @author Philip Helger
+ */
+ public static class TrustStoreDescriptorBuilder implements IBuilder
+ {
+ private IKeyStoreType m_aType;
+ private String m_sPath;
+ private char [] m_aPassword;
+ private Provider m_aProvider;
+
+ public TrustStoreDescriptorBuilder ()
+ {}
+
+ public TrustStoreDescriptorBuilder (@Nonnull final TrustStoreDescriptor aSrc)
+ {
+ type (aSrc.m_aType).path (aSrc.m_sPath).password (aSrc.m_aPassword).provider (aSrc.m_aProvider);
+ }
+
+ @Nonnull
+ public final TrustStoreDescriptorBuilder type (@Nullable final IKeyStoreType a)
+ {
+ m_aType = a;
+ return this;
+ }
+
+ @Nonnull
+ public final TrustStoreDescriptorBuilder path (@Nullable final String s)
+ {
+ m_sPath = s;
+ return this;
+ }
+
+ @Nonnull
+ public final TrustStoreDescriptorBuilder password (@Nullable final String s)
+ {
+ return password (s == null ? null : s.toCharArray ());
+ }
+
+ @Nonnull
+ public final TrustStoreDescriptorBuilder password (@Nullable final char [] a)
+ {
+ m_aPassword = a;
+ return this;
+ }
+
+ @Nonnull
+ public final TrustStoreDescriptorBuilder provider (@Nullable final Provider a)
+ {
+ m_aProvider = a;
+ return this;
+ }
+
+ @Nonnull
+ public TrustStoreDescriptor build () throws IllegalStateException
+ {
+ if (m_aType == null)
+ throw new IllegalStateException ("Type is missing");
+ if (StringHelper.hasNoText (m_sPath))
+ throw new IllegalStateException ("Path is empty");
+ if (m_aPassword == null)
+ throw new IllegalStateException ("Password is missing");
+ // Provider may be null
+
+ return new TrustStoreDescriptor (m_aType, m_sPath, m_aPassword, m_aProvider);
+ }
+ }
+}
diff --git a/ph-security/src/test/java/com/helger/security/keystore/KeyStoreAndKeyDescriptorTest.java b/ph-security/src/test/java/com/helger/security/keystore/KeyStoreAndKeyDescriptorTest.java
new file mode 100644
index 000000000..c4b351f5b
--- /dev/null
+++ b/ph-security/src/test/java/com/helger/security/keystore/KeyStoreAndKeyDescriptorTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.Test;
+
+/**
+ * Test class for class {@link KeyStoreAndKeyDescriptor}.
+ *
+ * @author Philip Helger
+ */
+public final class KeyStoreAndKeyDescriptorTest
+{
+ @Test
+ public void testBasic ()
+ {
+ final KeyStoreAndKeyDescriptor aKD = KeyStoreAndKeyDescriptor.builder ()
+ .type (EKeyStoreType.BKS)
+ .path ("keystores/keystore-pw-test.bks")
+ .password ("test")
+ .provider (new BouncyCastleProvider ())
+ .keyAlias ("alias")
+ .keyPassword ("test")
+ .build ();
+ assertNotNull (aKD);
+ assertNotNull (aKD.loadKeyStore ());
+ assertTrue (aKD.loadKeyStore ().isSuccess ());
+ assertNotNull (aKD.loadKey ());
+ assertTrue (aKD.loadKey ().isSuccess ());
+ assertNotNull (aKD.toString ());
+
+ // Copy
+ final KeyStoreAndKeyDescriptor aKD2 = KeyStoreAndKeyDescriptor.builder (aKD).build ();
+ assertNotNull (aKD2);
+ assertNotNull (aKD2.loadKeyStore ());
+ assertTrue (aKD2.loadKeyStore ().isSuccess ());
+ assertNotNull (aKD2.loadKey ());
+ assertTrue (aKD2.loadKey ().isSuccess ());
+ }
+}
diff --git a/ph-security/src/test/java/com/helger/security/keystore/KeyStoreDescriptorTest.java b/ph-security/src/test/java/com/helger/security/keystore/KeyStoreDescriptorTest.java
new file mode 100644
index 000000000..619540038
--- /dev/null
+++ b/ph-security/src/test/java/com/helger/security/keystore/KeyStoreDescriptorTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Test class for class {@link KeyStoreDescriptor}.
+ *
+ * @author Philip Helger
+ */
+public final class KeyStoreDescriptorTest
+{
+ @Test
+ public void testBasic ()
+ {
+ final KeyStoreDescriptor aKD = KeyStoreDescriptor.builder ()
+ .type (EKeyStoreType.JKS)
+ .path ("keystores/keystore-no-pw.jks")
+ .password ("")
+ .build ();
+ assertNotNull (aKD);
+ assertNotNull (aKD.loadKeyStore ());
+ assertTrue (aKD.loadKeyStore ().isSuccess ());
+ assertNotNull (aKD.toString ());
+
+ // Copy
+ final KeyStoreDescriptor aKD2 = KeyStoreDescriptor.builder (aKD).build ();
+ assertNotNull (aKD2);
+ assertNotNull (aKD2.loadKeyStore ());
+ assertTrue (aKD2.loadKeyStore ().isSuccess ());
+ }
+}
diff --git a/ph-security/src/test/java/com/helger/security/keystore/TrustStoreDescriptorTest.java b/ph-security/src/test/java/com/helger/security/keystore/TrustStoreDescriptorTest.java
new file mode 100644
index 000000000..2714f0cfb
--- /dev/null
+++ b/ph-security/src/test/java/com/helger/security/keystore/TrustStoreDescriptorTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Test class for class {@link TrustStoreDescriptor}.
+ *
+ * @author Philip Helger
+ */
+public final class TrustStoreDescriptorTest
+{
+ @Test
+ public void testBasic ()
+ {
+ final TrustStoreDescriptor aTD = TrustStoreDescriptor.builder ()
+ .type (EKeyStoreType.JKS)
+ .path ("keystores/truststore-peppol-pilot.jks")
+ .password ("peppol")
+ .build ();
+ assertNotNull (aTD);
+ assertNotNull (aTD.loadTrustStore ());
+ assertTrue (aTD.loadTrustStore ().isSuccess ());
+ assertNotNull (aTD.toString ());
+
+ // Copy
+ final TrustStoreDescriptor aTD2 = TrustStoreDescriptor.builder (aTD).build ();
+ assertNotNull (aTD2);
+ assertNotNull (aTD2.loadTrustStore ());
+ assertTrue (aTD2.loadTrustStore ().isSuccess ());
+ }
+}