docs
This documentation has been enhanced for AI coding agents with comprehensive examples, complete API signatures, thread safety notes, error handling patterns, and production-ready usage patterns.
| Component | Purpose | Thread Safety | Key Use Cases |
|---|---|---|---|
SslBundle | Complete SSL configuration bundle | Thread-safe (immutable) | Creating SSLContext for servers/clients |
SslStoreBundle | Key and trust store access | Thread-safe | Managing certificates and keys |
SslManagerBundle | KeyManager and TrustManager factory | Thread-safe | Creating SSL managers |
SslBundleKey | Key alias and password reference | Thread-safe (immutable) | Identifying specific keys in keystore |
SslOptions | Cipher suites and protocol config | Thread-safe (immutable) | Fine-tuning SSL settings |
SslBundleRegistry | Central bundle management | Thread-safe | Managing multiple SSL configurations |
| Class | Format | Thread Safety | Key Features |
|---|---|---|---|
PemSslStoreBundle | PEM-encoded | Thread-safe | Certificate chains, encrypted keys, file/classpath loading |
JksSslStoreBundle | JKS/PKCS12 | Thread-safe | Binary keystores, hardware PKCS11 support |
PemSslStore | PEM content | Thread-safe (immutable) | In-memory PEM storage with alias/password |
JksSslStoreDetails | JKS configuration | Thread-safe (record) | Keystore location, type, provider, password |
| Class | Purpose | Thread Safety | Key Operations |
|---|---|---|---|
PemContent | Parsed PEM content | Thread-safe | Load certificates, parse private keys |
PemCertificateParser | Parse X.509 certificates | Thread-safe | Extract certificate chains from PEM |
PemPrivateKeyParser | Parse encrypted keys | Thread-safe | Decrypt PKCS#1, PKCS#8 private keys |
PemSslStoreDetails | PEM store configuration | Thread-safe (record) | Certificate/key file paths |
| Method | Purpose | Thread Safety | Throws |
|---|---|---|---|
registerBundle(name, bundle) | Add new bundle | Thread-safe | IllegalStateException if exists |
updateBundle(name, bundle) | Update existing bundle | Thread-safe | NoSuchSslBundleException if missing |
getBundle(name) | Retrieve bundle | Thread-safe | NoSuchSslBundleException if missing |
addBundleUpdateHandler(name, handler) | Add update callback | Thread-safe | None |
Package: org.springframework.boot.ssl
Version: 4.0.0
Since: 3.1.0
Spring Boot provides a comprehensive SSL/TLS bundle management system that simplifies the configuration and management of SSL certificates, private keys, and trust stores. This system supports both PEM-encoded certificates and Java KeyStore (JKS) formats, providing a unified API for SSL configuration across different technologies.
The main interface representing a bundle of SSL configuration materials.
package org.springframework.boot.ssl;
/**
* A bundle of trust material that can be used to establish an SSL connection.
*
* @author Scott Frederick
* @author Moritz Halbritter
* @since 3.1.0
*/
public interface SslBundle {
/**
* The default protocol to use.
*/
String DEFAULT_PROTOCOL = "TLS";
/**
* Return the SslStoreBundle that can be used to access this bundle's key and
* trust stores.
*
* @return the SslStoreBundle instance for this bundle
*/
SslStoreBundle getStores();
/**
* Return a reference to the key that should be used for this bundle or
* SslBundleKey.NONE.
*
* @return a reference to the SSL key that should be used
*/
SslBundleKey getKey();
/**
* Return SslOptions that should be applied when establishing the SSL
* connection.
*
* @return the options that should be applied
*/
SslOptions getOptions();
/**
* Return the protocol to use when establishing the connection. Values should be
* supported by SSLContext.getInstance(String).
*
* @return the SSL protocol
* @see javax.net.ssl.SSLContext#getInstance(String)
*/
String getProtocol();
/**
* Return the SslManagerBundle that can be used to access this bundle's
* KeyManager and TrustManager instances.
*
* @return the SslManagerBundle instance for this bundle
*/
SslManagerBundle getManagers();
/**
* Factory method to create a new SSLContext for this bundle.
*
* @return a new SSLContext instance
*/
default SSLContext createSslContext() {
return getManagers().createSslContext(getProtocol());
}
/**
* Factory method to create a new SslBundle instance.
*
* @param stores the stores or null
* @return a new SslBundle instance
*/
static SslBundle of(@Nullable SslStoreBundle stores) {
return of(stores, null, null);
}
/**
* Factory method to create a new SslBundle instance.
*
* @param stores the stores or null
* @param key the key or null
* @return a new SslBundle instance
*/
static SslBundle of(@Nullable SslStoreBundle stores, @Nullable SslBundleKey key) {
return of(stores, key, null);
}
/**
* Factory method to create a new SslBundle instance.
*
* @param stores the stores or null
* @param key the key or null
* @param options the options or null
* @return a new SslBundle instance
*/
static SslBundle of(@Nullable SslStoreBundle stores, @Nullable SslBundleKey key, @Nullable SslOptions options) {
return of(stores, key, options, null);
}
/**
* Factory method to create a new SslBundle instance.
*
* @param stores the stores or null
* @param key the key or null
* @param options the options or null
* @param protocol the protocol or null
* @return a new SslBundle instance
*/
static SslBundle of(@Nullable SslStoreBundle stores, @Nullable SslBundleKey key,
@Nullable SslOptions options, @Nullable String protocol) {
return of(stores, key, options, protocol, null);
}
/**
* Factory method to create a new SslBundle instance.
*
* @param stores the stores or null
* @param key the key or null
* @param options the options or null
* @param protocol the protocol or null
* @param managers the managers or null
* @return a new SslBundle instance
*/
static SslBundle of(@Nullable SslStoreBundle stores, @Nullable SslBundleKey key,
@Nullable SslOptions options, @Nullable String protocol,
@Nullable SslManagerBundle managers) {
// Implementation provided by Spring Boot
}
/**
* Factory method to create a new SslBundle which uses the system defaults.
*
* @return a new SslBundle instance
* @since 3.5.0
*/
static SslBundle systemDefault() {
// Uses system KeyManagerFactory and TrustManagerFactory
}
}Interface for accessing a managed set of SSL bundles by name.
package org.springframework.boot.ssl;
/**
* A managed set of SslBundle instances that can be retrieved by name.
*
* @author Scott Frederick
* @author Moritz Halbritter
* @author Jonatan Ivanov
* @since 3.1.0
*/
public interface SslBundles {
/**
* Return an SslBundle with the provided name.
*
* @param name the bundle name
* @return the bundle
* @throws NoSuchSslBundleException if a bundle with the provided name does not exist
*/
SslBundle getBundle(String name) throws NoSuchSslBundleException;
/**
* Add a handler that will be called each time the named bundle is updated.
*
* @param name the bundle name
* @param updateHandler the handler that should be called
* @throws NoSuchSslBundleException if a bundle with the provided name does not exist
* @since 3.2.0
*/
void addBundleUpdateHandler(String name, Consumer<SslBundle> updateHandler)
throws NoSuchSslBundleException;
/**
* Add a handler that will be called each time a bundle is registered. The handler
* will be called with the bundle name and the bundle.
*
* @param registerHandler the handler that should be called
* @since 3.5.0
*/
void addBundleRegisterHandler(BiConsumer<String, SslBundle> registerHandler);
/**
* Return the names of all bundles managed by this instance.
*
* @return the bundle names
* @since 3.4.0
*/
List<String> getBundleNames();
}Interface for registering and updating SSL bundles.
package org.springframework.boot.ssl;
/**
* Interface that can be used to register an SslBundle for a given name.
*
* @author Scott Frederick
* @author Moritz Halbritter
* @since 3.1.0
*/
public interface SslBundleRegistry {
/**
* Register a named SslBundle.
*
* @param name the bundle name
* @param bundle the bundle
*/
void registerBundle(String name, SslBundle bundle);
/**
* Updates an SslBundle.
*
* @param name the bundle name
* @param updatedBundle the updated bundle
* @throws NoSuchSslBundleException if the bundle cannot be found
* @since 3.2.0
*/
void updateBundle(String name, SslBundle updatedBundle)
throws NoSuchSslBundleException;
}Default implementation combining registry and bundle access.
package org.springframework.boot.ssl;
/**
* Default SslBundleRegistry implementation.
*
* @author Scott Frederick
* @author Moritz Halbritter
* @author Phillip Webb
* @author Jonatan Ivanov
* @since 3.1.0
*/
public class DefaultSslBundleRegistry implements SslBundleRegistry, SslBundles {
/**
* Create a new empty DefaultSslBundleRegistry.
*/
public DefaultSslBundleRegistry() {
}
/**
* Create a new DefaultSslBundleRegistry with a single bundle.
*
* @param name the bundle name
* @param bundle the bundle
*/
public DefaultSslBundleRegistry(String name, SslBundle bundle) {
}
@Override
public void registerBundle(String name, SslBundle bundle) {
// Registers bundle, throws if bundle with same name already exists
}
@Override
public void updateBundle(String name, SslBundle updatedBundle) {
// Updates bundle, triggers registered update handlers
}
@Override
public SslBundle getBundle(String name) {
// Returns bundle or throws NoSuchSslBundleException
}
@Override
public void addBundleUpdateHandler(String name, Consumer<SslBundle> updateHandler)
throws NoSuchSslBundleException {
// Adds handler that will be called when bundle is updated
}
@Override
public void addBundleRegisterHandler(BiConsumer<String, SslBundle> registerHandler) {
// Adds handler that will be called when any bundle is registered
}
@Override
public List<String> getBundleNames() {
// Returns sorted, unmodifiable list of all bundle names
}
}Interface representing key and trust stores.
package org.springframework.boot.ssl;
/**
* A bundle of key and trust stores that can be used to establish an SSL connection.
*
* @author Scott Frederick
* @since 3.1.0
*/
public interface SslStoreBundle {
/**
* SslStoreBundle that returns null for each method.
*/
SslStoreBundle NONE = of(null, null, null);
/**
* Return a key store generated from the trust material or null.
*
* @return the key store
*/
@Nullable KeyStore getKeyStore();
/**
* Return the password for the key in the key store or null.
*
* @return the key password
*/
@Nullable String getKeyStorePassword();
/**
* Return a trust store generated from the trust material or null.
*
* @return the trust store
*/
@Nullable KeyStore getTrustStore();
/**
* Factory method to create a new SslStoreBundle instance.
*
* @param keyStore the key store or null
* @param keyStorePassword the key store password or null
* @param trustStore the trust store or null
* @return a new SslStoreBundle instance
*/
static SslStoreBundle of(KeyStore keyStore, String keyStorePassword,
KeyStore trustStore) {
return new SslStoreBundle() {
@Override
public KeyStore getKeyStore() {
return keyStore;
}
@Override
public KeyStore getTrustStore() {
return trustStore;
}
@Override
public String getKeyStorePassword() {
return keyStorePassword;
}
};
}
}Interface for accessing KeyManager and TrustManager instances.
package org.springframework.boot.ssl;
/**
* A bundle of key and trust managers that can be used to establish an SSL connection.
* Instances are usually created from an SslStoreBundle.
*
* @author Scott Frederick
* @author Moritz Halbritter
* @since 3.1.0
*/
public interface SslManagerBundle {
/**
* Return the KeyManager instances used to establish identity.
*
* @return the key managers
*/
default KeyManager[] getKeyManagers() {
return getKeyManagerFactory().getKeyManagers();
}
/**
* Return the KeyManagerFactory used to establish identity.
*
* @return the key manager factory
*/
KeyManagerFactory getKeyManagerFactory();
/**
* Return the TrustManager instances used to establish trust.
*
* @return the trust managers
*/
default TrustManager[] getTrustManagers() {
return getTrustManagerFactory().getTrustManagers();
}
/**
* Return the TrustManagerFactory used to establish trust.
*
* @return the trust manager factory
*/
TrustManagerFactory getTrustManagerFactory();
/**
* Factory method to create a new SSLContext for the key managers and
* trust managers managed by this instance.
*
* @param protocol the standard name of the SSL protocol
* @return a new SSLContext instance
* @see javax.net.ssl.SSLContext#getInstance(String)
*/
default SSLContext createSslContext(String protocol) {
try {
SSLContext sslContext = SSLContext.getInstance(protocol);
sslContext.init(getKeyManagers(), getTrustManagers(), null);
return sslContext;
}
catch (Exception ex) {
throw new IllegalStateException(
"Could not load SSL context: " + ex.getMessage(), ex);
}
}
/**
* Factory method to create a new SslManagerBundle instance.
*
* @param keyManagerFactory the key manager factory
* @param trustManagerFactory the trust manager factory
* @return a new SslManagerBundle instance
*/
static SslManagerBundle of(KeyManagerFactory keyManagerFactory,
TrustManagerFactory trustManagerFactory) {
return new SslManagerBundle() {
@Override
public KeyManagerFactory getKeyManagerFactory() {
return keyManagerFactory;
}
@Override
public TrustManagerFactory getTrustManagerFactory() {
return trustManagerFactory;
}
};
}
/**
* Factory method to create a new SslManagerBundle backed by the given
* SslStoreBundle and SslBundleKey.
*
* @param storeBundle the SSL store bundle
* @param key the key reference
* @return a new SslManagerBundle instance
*/
static SslManagerBundle from(SslStoreBundle storeBundle, SslBundleKey key) {
// Returns DefaultSslManagerBundle instance
// Creates KeyManagerFactory and TrustManagerFactory from stores
}
/**
* Factory method to create a new SslManagerBundle using the given
* TrustManagerFactory and the default KeyManagerFactory.
*
* @param trustManagerFactory the trust manager factory
* @return a new SslManagerBundle instance
* @since 3.5.0
*/
static SslManagerBundle from(TrustManagerFactory trustManagerFactory) {
// Creates default KeyManagerFactory initialized with (null, null)
// Returns SslManagerBundle.of(defaultKeyManagerFactory, trustManagerFactory)
}
/**
* Factory method to create a new SslManagerBundle using the given
* TrustManagers and the default KeyManagerFactory.
*
* @param trustManagers the trust managers to use
* @return a new SslManagerBundle instance
* @since 3.5.0
*/
static SslManagerBundle from(TrustManager... trustManagers) {
// Creates default KeyManagerFactory and TrustManagerFactory
// Wraps TrustManagerFactory with FixedTrustManagerFactory using provided managers
// Returns SslManagerBundle.of(defaultKeyManagerFactory, fixedTrustManagerFactory)
}
}Configuration options for SSL connections.
package org.springframework.boot.ssl;
/**
* Configuration options that should be applied when establishing an SSL connection.
*
* @author Scott Frederick
* @since 3.1.0
*/
public interface SslOptions {
/**
* SslOptions that returns null results.
*/
SslOptions NONE = of(null, (Set<String>) null);
/**
* Return if any SSL options have been specified.
*
* @return true if SSL options have been specified
*/
default boolean isSpecified() {
return (getCiphers() != null) || (getEnabledProtocols() != null);
}
/**
* Return the ciphers that can be used or null. The cipher names in this set
* should be compatible with those supported by SSLEngine.getSupportedCipherSuites().
*
* @return the ciphers that can be used or null
*/
String @Nullable [] getCiphers();
/**
* Return the protocols that should be enabled or null. The protocols names in
* this set should be compatible with those supported by SSLEngine.getSupportedProtocols().
*
* @return the protocols to enable or null
*/
String @Nullable [] getEnabledProtocols();
/**
* Factory method to create a new SslOptions instance.
*
* @param ciphers the ciphers
* @param enabledProtocols the enabled protocols
* @return a new SslOptions instance
*/
static SslOptions of(String[] ciphers, String[] enabledProtocols) {
return new SslOptions() {
@Override
public String[] getCiphers() {
return ciphers;
}
@Override
public String[] getEnabledProtocols() {
return enabledProtocols;
}
};
}
/**
* Factory method to create a new SslOptions instance.
*
* @param ciphers the ciphers
* @param enabledProtocols the enabled protocols
* @return a new SslOptions instance
*/
static SslOptions of(Set<String> ciphers, Set<String> enabledProtocols) {
// Converts Sets to arrays and delegates to of(String[], String[])
return of(toArray(ciphers), toArray(enabledProtocols));
}
/**
* Helper method that provides a null-safe way to convert a String[] to a
* Collection for client libraries to use.
*
* @param array the array to convert
* @return a collection or null
*/
static Set<String> asSet(String[] array) {
return (array != null) ?
Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(array))) : null;
}
}Reference to a specific key within a key store.
package org.springframework.boot.ssl;
/**
* A reference to a single key obtained via SslBundle.
*
* @author Phillip Webb
* @since 3.1.0
*/
public interface SslBundleKey {
/**
* SslBundleKey that returns no values.
*/
SslBundleKey NONE = of(null, null);
/**
* Return the password that should be used to access the key or null if no
* password is required.
*
* @return the key password
*/
@Nullable String getPassword();
/**
* Return the alias of the key or null if the key has no alias.
*
* @return the key alias
*/
@Nullable String getAlias();
/**
* Assert that the alias is contained in the given keystore.
*
* @param keyStore the keystore to check
*/
default void assertContainsAlias(@Nullable KeyStore keyStore) {
String alias = getAlias();
if (StringUtils.hasLength(alias) && keyStore != null) {
try {
Assert.state(keyStore.containsAlias(alias),
() -> String.format("Keystore does not contain alias '%s'", alias));
}
catch (KeyStoreException ex) {
throw new IllegalStateException(
String.format("Could not determine if keystore contains alias '%s'",
alias), ex);
}
}
}
/**
* Factory method to create a new SslBundleKey instance.
*
* @param password the password used to access the key
* @return a new SslBundleKey instance
*/
static SslBundleKey of(String password) {
return of(password, null);
}
/**
* Factory method to create a new SslBundleKey instance.
*
* @param password the password used to access the key
* @param alias the alias of the key
* @return a new SslBundleKey instance
*/
static SslBundleKey of(String password, String alias) {
return new SslBundleKey() {
@Override
public String getPassword() {
return password;
}
@Override
public String getAlias() {
return alias;
}
};
}
}The org.springframework.boot.ssl.pem package provides comprehensive support for PEM-encoded certificates and private keys.
SslStoreBundle implementation for PEM-encoded content.
package org.springframework.boot.ssl.pem;
/**
* SslStoreBundle backed by PEM-encoded certificates and private keys.
*
* @author Scott Frederick
* @author Phillip Webb
* @author Moritz Halbritter
* @since 3.1.0
*/
public class PemSslStoreBundle implements SslStoreBundle {
/**
* Create a new PemSslStoreBundle instance.
*
* @param keyStoreDetails the key store details
* @param trustStoreDetails the trust store details
*/
public PemSslStoreBundle(PemSslStoreDetails keyStoreDetails,
PemSslStoreDetails trustStoreDetails) {
}
/**
* Create a new PemSslStoreBundle instance.
*
* @param pemKeyStore the PEM key store
* @param pemTrustStore the PEM trust store
* @since 3.2.0
*/
public PemSslStoreBundle(PemSslStore pemKeyStore, PemSslStore pemTrustStore) {
}
@Override
public KeyStore getKeyStore() {
// Returns KeyStore created from PEM content
}
@Override
public String getKeyStorePassword() {
// Always returns null for PEM stores
}
@Override
public KeyStore getTrustStore() {
// Returns KeyStore created from PEM content
}
}Interface representing an individual PEM-based store.
package org.springframework.boot.ssl.pem;
/**
* An individual trust or key store that has been loaded from PEM content.
*
* @author Phillip Webb
* @since 3.2.0
*/
public interface PemSslStore {
/**
* The key store type, for example JKS or PKCS11. A null value
* will use KeyStore.getDefaultType().
*
* @return the key store type
*/
@Nullable String type();
/**
* The alias used when setting entries in the KeyStore.
*
* @return the alias
*/
@Nullable String alias();
/**
* The password used when setting key entries in the KeyStore.
*
* @return the password
*/
@Nullable String password();
/**
* The certificates for this store. When a private key is present the returned
* value is treated as a certificate chain, otherwise it is treated as a list of
* certificates that should all be registered.
*
* @return the X509 certificates
*/
@Nullable List<X509Certificate> certificates();
/**
* The private key for this store or null.
*
* @return the private key
*/
@Nullable PrivateKey privateKey();
/**
* Return a new PemSslStore instance with a new alias.
*
* @param alias the new alias
* @return a new PemSslStore instance
*/
default PemSslStore withAlias(@Nullable String alias) {
return of(type(), alias, password(), certificates(), privateKey());
}
/**
* Return a new PemSslStore instance with a new password.
*
* @param password the new password
* @return a new PemSslStore instance
*/
default PemSslStore withPassword(@Nullable String password) {
return of(type(), alias(), password, certificates(), privateKey());
}
/**
* Return a PemSslStore instance loaded using the given PemSslStoreDetails.
*
* @param details the PEM store details
* @return a loaded PemSslStore or null
*/
static @Nullable PemSslStore load(@Nullable PemSslStoreDetails details) {
// Loads PEM content from details using ApplicationResourceLoader
// Returns LoadedPemSslStore wrapping the details
return load(details, ApplicationResourceLoader.get());
}
/**
* Return a PemSslStore instance loaded using the given PemSslStoreDetails.
*
* @param details the PEM store details
* @param resourceLoader the resource loader used to load content
* @return a loaded PemSslStore or null
* @since 3.3.5
*/
static @Nullable PemSslStore load(@Nullable PemSslStoreDetails details, ResourceLoader resourceLoader) {
// Returns null if details is null or empty
// Otherwise returns new LoadedPemSslStore(details, resourceLoader)
return (details == null || details.isEmpty()) ? null
: new LoadedPemSslStore(details, resourceLoader);
}
/**
* Factory method that can be used to create a new PemSslStore with the given values.
*
* @param type the key store type
* @param certificates the certificates for this store
* @param privateKey the private key
* @return a new PemSslStore instance
*/
static PemSslStore of(@Nullable String type, List<X509Certificate> certificates,
@Nullable PrivateKey privateKey) {
return of(type, null, null, certificates, privateKey);
}
/**
* Factory method that can be used to create a new PemSslStore with the given values.
*
* @param certificates the certificates for this store
* @param privateKey the private key
* @return a new PemSslStore instance
*/
static PemSslStore of(List<X509Certificate> certificates, @Nullable PrivateKey privateKey) {
return of(null, null, null, certificates, privateKey);
}
/**
* Factory method that can be used to create a new PemSslStore with the given values.
*
* @param type the key store type
* @param alias the alias used when setting entries in the KeyStore
* @param password the password used setting key entries in the KeyStore
* @param certificates the certificates for this store
* @param privateKey the private key
* @return a new PemSslStore instance
*/
static PemSslStore of(String type, String alias, String password,
List<X509Certificate> certificates, PrivateKey privateKey) {
return new PemSslStore() {
@Override
public String type() {
return type;
}
@Override
public String alias() {
return alias;
}
@Override
public String password() {
return password;
}
@Override
public List<X509Certificate> certificates() {
return certificates;
}
@Override
public PrivateKey privateKey() {
return privateKey;
}
};
}
}Configuration details for PEM-based stores.
package org.springframework.boot.ssl.pem;
/**
* Details for an individual trust or key store in a PemSslStoreBundle.
*
* @param type the key store type, for example JKS or PKCS11. A null value
* will use KeyStore.getDefaultType()
* @param alias the alias used when setting entries in the KeyStore
* @param password the password used setting key entries in the KeyStore
* @param certificates the certificates content (either the PEM content itself or a
* reference to the resource to load). When a private key is present
* this value is treated as a certificate chain, otherwise it is
* treated as a list of certificates that should all be registered
* @param privateKey the private key content (either the PEM content itself or a
* reference to the resource to load)
* @param privateKeyPassword a password used to decrypt an encrypted private key
* @author Scott Frederick
* @author Phillip Webb
* @since 3.1.0
*/
public record PemSslStoreDetails(@Nullable String type, @Nullable String alias, @Nullable String password,
@Nullable String certificates, @Nullable String privateKey,
@Nullable String privateKeyPassword) {
/**
* Create a new PemSslStoreDetails instance.
*
* @param type the key store type
* @param certificate the certificate content
* @param privateKey the private key content
* @param privateKeyPassword a password used to decrypt an encrypted private key
*/
public PemSslStoreDetails(String type, String certificate, String privateKey,
String privateKeyPassword) {
this(type, null, null, certificate, privateKey, privateKeyPassword);
}
/**
* Create a new PemSslStoreDetails instance.
*
* @param type the key store type
* @param certificate the certificate content
* @param privateKey the private key content
*/
public PemSslStoreDetails(String type, String certificate, String privateKey) {
this(type, certificate, privateKey, null);
}
/**
* Return a new PemSslStoreDetails instance with a new alias.
*
* @param alias the new alias
* @return a new PemSslStoreDetails instance
* @since 3.2.0
*/
public PemSslStoreDetails withAlias(@Nullable String alias) {
return new PemSslStoreDetails(this.type, alias, this.password,
this.certificates, this.privateKey,
this.privateKeyPassword);
}
/**
* Return a new PemSslStoreDetails instance with a new password.
*
* @param password the new password
* @return a new PemSslStoreDetails instance
* @since 3.2.0
*/
public PemSslStoreDetails withPassword(@Nullable String password) {
return new PemSslStoreDetails(this.type, this.alias, password,
this.certificates, this.privateKey,
this.privateKeyPassword);
}
/**
* Return a new PemSslStoreDetails instance with a new private key.
*
* @param privateKey the new private key
* @return a new PemSslStoreDetails instance
*/
public PemSslStoreDetails withPrivateKey(@Nullable String privateKey) {
return new PemSslStoreDetails(this.type, this.alias, this.password,
this.certificates, privateKey,
this.privateKeyPassword);
}
/**
* Return a new PemSslStoreDetails instance with a new private key password.
*
* @param privateKeyPassword the new private key password
* @return a new PemSslStoreDetails instance
*/
public PemSslStoreDetails withPrivateKeyPassword(@Nullable String privateKeyPassword) {
return new PemSslStoreDetails(this.type, this.alias, this.password,
this.certificates, this.privateKey,
privateKeyPassword);
}
/**
* Factory method to create a new PemSslStoreDetails instance for the given
* certificate. Note: This method doesn't actually check if the provided value
* only contains a single certificate. It is functionally equivalent to
* forCertificates(String).
*
* @param certificate the certificate content
* @return a new PemSslStoreDetails instance
*/
public static PemSslStoreDetails forCertificate(@Nullable String certificate) {
return forCertificates(certificate);
}
/**
* Factory method to create a new PemSslStoreDetails instance for the given
* certificates.
*
* @param certificates the certificates content
* @return a new PemSslStoreDetails instance
* @since 3.2.0
*/
public static PemSslStoreDetails forCertificates(@Nullable String certificates) {
return new PemSslStoreDetails(null, certificates, null);
}
}Class for handling PEM-encoded content.
package org.springframework.boot.ssl.pem;
/**
* PEM encoded content that can provide X509Certificate certificates and
* PrivateKey private keys.
*
* @author Scott Frederick
* @author Phillip Webb
* @since 3.2.0
*/
public final class PemContent {
/**
* Parse and return all X509Certificate certificates from the PEM content.
* Most PEM files either contain a single certificate or a certificate chain.
*
* @return the certificates
* @throws IllegalStateException if no certificates could be loaded
*/
public List<X509Certificate> getCertificates() {
// Uses internal PemCertificateParser to extract X.509 certificates
// Supports multiple certificate formats within PEM content
// Returns immutable list of parsed certificates
}
/**
* Parse and return the PrivateKey private keys from the PEM content.
*
* @return the private keys
* @throws IllegalStateException if no private key could be loaded
*/
public @Nullable PrivateKey getPrivateKey() {
return getPrivateKey(null);
}
/**
* Parse and return the PrivateKey private keys from the PEM content or
* null if there is no private key.
*
* @param password the password to decrypt the private keys or null
* @return the private keys
*/
public @Nullable PrivateKey getPrivateKey(@Nullable String password) {
// Uses internal PemPrivateKeyParser to extract private key
// Supports PKCS#1 RSA, SEC1 EC, PKCS#8, and encrypted PKCS#8 formats
// Supports RSA, EC, DSA, EdDSA, and XDH key algorithms
// Handles encrypted keys using the provided password
}
/**
* Load PemContent from the given Path.
*
* @param path a path to load the content from
* @return the loaded PEM content
* @throws IOException on IO error
*/
public static PemContent load(Path path) throws IOException {
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) {
return load(in);
}
}
/**
* Load PemContent from the given InputStream.
*
* @param in an input stream to load the content from
* @return the loaded PEM content
* @throws IOException on IO error
*/
public static PemContent load(InputStream in) throws IOException {
// Reads stream to String using UTF-8 encoding
return of(StreamUtils.copyToString(in, StandardCharsets.UTF_8));
}
/**
* Return a new PemContent instance containing the given text.
*
* @param text the text containing PEM encoded content
* @return a new PemContent instance
*/
public static @Nullable PemContent of(@Nullable String text) {
// Normalizes text by trimming lines and joining with newlines
return (text != null) ? new PemContent(text) : null;
}
/**
* Return if PEM content is present in the given text.
*
* @param text the text to check
* @return if the text includes PEM encoded content
*/
public static boolean isPresentInText(@Nullable String text) {
// Checks for PEM header (BEGIN) and footer (END) markers using regex
return text != null && PEM_HEADER.matcher(text).find()
&& PEM_FOOTER.matcher(text).find();
}
}The org.springframework.boot.ssl.jks package provides support for Java KeyStore format.
SslStoreBundle implementation for JKS keystores.
package org.springframework.boot.ssl.jks;
/**
* SslStoreBundle backed by a Java keystore.
*
* @author Scott Frederick
* @author Phillip Webb
* @author Moritz Halbritter
* @since 3.1.0
*/
public class JksSslStoreBundle implements SslStoreBundle {
/**
* Create a new JksSslStoreBundle instance.
*
* @param keyStoreDetails the key store details
* @param trustStoreDetails the trust store details
*/
public JksSslStoreBundle(JksSslStoreDetails keyStoreDetails,
JksSslStoreDetails trustStoreDetails) {
}
/**
* Create a new JksSslStoreBundle instance.
*
* @param keyStoreDetails the key store details
* @param trustStoreDetails the trust store details
* @param resourceLoader the resource loader used to load content
* @since 3.3.5
*/
public JksSslStoreBundle(JksSslStoreDetails keyStoreDetails,
JksSslStoreDetails trustStoreDetails,
ResourceLoader resourceLoader) {
}
@Override
public KeyStore getKeyStore() {
// Returns KeyStore loaded from file or hardware store
}
@Override
public String getKeyStorePassword() {
// Returns password from details
}
@Override
public KeyStore getTrustStore() {
// Returns KeyStore loaded from file or hardware store
}
}Configuration details for JKS-based stores.
package org.springframework.boot.ssl.jks;
/**
* Details for an individual trust or key store in a JksSslStoreBundle.
*
* @param type the key store type, for example JKS or PKCS11. A null value
* will use KeyStore.getDefaultType()
* @param provider the name of the key store provider
* @param location the location of the key store file or null if using a
* PKCS11 hardware store
* @param password the password used to unlock the store or null
* @author Scott Frederick
* @author Phillip Webb
* @since 3.1.0
*/
public record JksSslStoreDetails(@Nullable String type, @Nullable String provider,
@Nullable String location, @Nullable String password) {
/**
* Return a new JksSslStoreDetails instance with a new password.
*
* @param password the new password
* @return a new JksSslStoreDetails instance
*/
public JksSslStoreDetails withPassword(String password) {
return new JksSslStoreDetails(this.type, this.provider,
this.location, password);
}
/**
* Factory method to create a new JksSslStoreDetails instance for the given location.
*
* @param location the location
* @return a new JksSslStoreDetails instance
*/
public static JksSslStoreDetails forLocation(@Nullable String location) {
return new JksSslStoreDetails(null, null, location, null);
}
}Exception thrown when a requested SSL bundle cannot be found.
package org.springframework.boot.ssl;
/**
* Exception indicating that an SslBundle was referenced with a name that does not
* match any registered bundle.
*
* @author Scott Frederick
* @since 3.1.0
*/
public class NoSuchSslBundleException extends RuntimeException {
/**
* Create a new NoSuchSslBundleException instance.
*
* @param bundleName the name of the bundle that could not be found
* @param message the exception message
*/
public NoSuchSslBundleException(String bundleName, String message) {
this(bundleName, message, null);
}
/**
* Create a new NoSuchSslBundleException instance.
*
* @param bundleName the name of the bundle that could not be found
* @param message the exception message
* @param cause the exception cause
*/
public NoSuchSslBundleException(String bundleName, String message, @Nullable Throwable cause) {
super(message, cause);
this.bundleName = bundleName;
}
/**
* Return the name of the bundle that was not found.
*
* @return the bundle name
*/
public String getBundleName() {
return this.bundleName;
}
}The SSL/TLS bundle management system uses several internal (package-private) implementation classes that are not part of the public API but are important for understanding the system architecture:
Internal implementation of SslManagerBundle that creates KeyManagerFactory and TrustManagerFactory instances from an SslStoreBundle and SslBundleKey. This class is used by the SslManagerBundle.from(SslStoreBundle, SslBundleKey) static factory method.
Internal implementation of PemSslStore that lazily loads PEM content from PemSslStoreDetails using a ResourceLoader. This class is used by PemSslStore.load() methods.
Internal utility for parsing X.509 certificates from PEM-encoded text. It:
Internal utility for parsing private keys from PEM-encoded text. It supports:
The parser handles:
Internal KeyManagerFactory wrapper that filters keys by alias. When an alias is specified in SslBundleKey, this factory wraps the standard KeyManagerFactory with an X509ExtendedKeyManager that always returns the specified alias for server operations.
Internal TrustManagerFactory implementation that uses a fixed set of TrustManager instances. This is used by SslManagerBundle.from(TrustManager...) to create a bundle with custom trust managers while using default key managers.
The PEM support automatically detects and handles:
The JKS support handles:
// Create PEM store details
PemSslStoreDetails keyStoreDetails = new PemSslStoreDetails(
null, // type (use default)
"classpath:certs/server-cert.pem", // certificate
"classpath:certs/server-key.pem", // private key
"key-password" // private key password
);
PemSslStoreDetails trustStoreDetails =
PemSslStoreDetails.forCertificate("classpath:certs/ca-cert.pem");
// Create SSL store bundle
PemSslStoreBundle storeBundle = new PemSslStoreBundle(
keyStoreDetails,
trustStoreDetails
);
// Create SSL bundle with options
SslBundleKey key = SslBundleKey.of("key-password");
SslOptions options = SslOptions.of(
new String[]{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"},
new String[]{"TLSv1.2", "TLSv1.3"}
);
SslBundle sslBundle = SslBundle.of(storeBundle, key, options, "TLS");
// Create SSL context
SSLContext sslContext = sslBundle.createSslContext();// Create JKS store details
JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails(
"JKS", // type
null, // provider (use default)
"classpath:keystore.jks", // location
"store-password" // password
);
JksSslStoreDetails trustStoreDetails = JksSslStoreDetails.forLocation(
"classpath:truststore.jks"
);
// Create SSL store bundle
JksSslStoreBundle storeBundle = new JksSslStoreBundle(
keyStoreDetails,
trustStoreDetails
);
// Create SSL bundle
SslBundleKey key = SslBundleKey.of("key-password", "server-key");
SslBundle sslBundle = SslBundle.of(storeBundle, key);// Create and register bundles
DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
// Register PEM bundle
PemSslStoreBundle pemStore = new PemSslStoreBundle(
pemKeyStoreDetails,
pemTrustStoreDetails
);
registry.registerBundle("pem-bundle", SslBundle.of(pemStore));
// Register JKS bundle
JksSslStoreBundle jksStore = new JksSslStoreBundle(
jksKeyStoreDetails,
jksTrustStoreDetails
);
registry.registerBundle("jks-bundle", SslBundle.of(jksStore));
// Retrieve and use bundle
SslBundle bundle = registry.getBundle("pem-bundle");
SSLContext context = bundle.createSslContext();
// Add update handler
registry.addBundleUpdateHandler("pem-bundle", updatedBundle -> {
System.out.println("Bundle updated!");
// Reinitialize SSL connections with updated bundle
});// Load from file
PemContent pemContent = PemContent.load(Path.of("/path/to/cert.pem"));
List<X509Certificate> certificates = pemContent.getCertificates();
PrivateKey privateKey = pemContent.getPrivateKey("password");
// Load from string
String pemText = """
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKZP...
-----END CERTIFICATE-----
""";
PemContent content = PemContent.of(pemText);
List<X509Certificate> certs = content.getCertificates();
// Check if text contains PEM content
boolean hasPem = PemContent.isPresentInText(pemText);DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
// Register initial bundle
SslBundle initialBundle = SslBundle.of(initialStoreBundle);
registry.registerBundle("dynamic-bundle", initialBundle);
// Add update handler to components using the bundle
registry.addBundleUpdateHandler("dynamic-bundle", updatedBundle -> {
// Update Tomcat connector
tomcatConnector.setSslContext(updatedBundle.createSslContext());
// Update other SSL components
sslClient.updateContext(updatedBundle.createSslContext());
});
// Later, update the bundle (e.g., after certificate renewal)
PemSslStoreBundle newStoreBundle = new PemSslStoreBundle(
newKeyStoreDetails,
newTrustStoreDetails
);
SslBundle updatedBundle = SslBundle.of(newStoreBundle);
registry.updateBundle("dynamic-bundle", updatedBundle);
// All registered handlers are automatically called// Create custom SSL options
SslOptions customOptions = SslOptions.of(
new String[]{
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
},
new String[]{"TLSv1.3"}
);
// Create bundle key with specific alias
SslBundleKey bundleKey = SslBundleKey.of("key-password", "my-server");
// Create complete bundle
SslBundle customBundle = SslBundle.of(
storeBundle, // Store bundle (PEM or JKS)
bundleKey, // Key reference
customOptions, // SSL options
"TLSv1.3" // Protocol
);
// Use with custom manager bundle
SslManagerBundle managerBundle = SslManagerBundle.from(storeBundle, bundleKey);
SslBundle bundleWithManagers = SslBundle.of(
storeBundle,
bundleKey,
customOptions,
"TLSv1.3",
managerBundle
);// Create PEM store programmatically
List<X509Certificate> certificates = loadCertificates();
PrivateKey privateKey = loadPrivateKey();
PemSslStore pemStore = PemSslStore.of(certificates, privateKey);
// Customize store
PemSslStore customStore = pemStore
.withAlias("my-server")
.withPassword("store-password");
// Create bundle from store
PemSslStoreBundle bundle = new PemSslStoreBundle(customStore, null);
KeyStore keyStore = bundle.getKeyStore();// Configure hardware keystore
JksSslStoreDetails hardwareDetails = new JksSslStoreDetails(
"PKCS11", // type
"SunPKCS11", // provider
null, // location (null for hardware)
"pin-password" // password/PIN
);
JksSslStoreBundle hardwareBundle = new JksSslStoreBundle(
hardwareDetails,
null // trust store
);
// Use hardware key
SslBundleKey hardwareKey = SslBundleKey.of("pin-password", "hardware-key-alias");
SslBundle bundle = SslBundle.of(hardwareBundle, hardwareKey);public interface SslBundle {
SslStoreBundle getStores(); // Key and trust stores
SslBundleKey getKey(); // Key reference
SslOptions getOptions(); // Cipher and protocol options
String getProtocol(); // SSL protocol (e.g., "TLS")
SslManagerBundle getManagers(); // Key and trust managers
SSLContext createSslContext(); // Creates ready-to-use SSLContext
}SslStoreBundle (interface)
├── PemSslStoreBundle (class)
│ └── Uses PemSslStore/PemSslStoreDetails
└── JksSslStoreBundle (class)
└── Uses JksSslStoreDetailspublic interface SslManagerBundle {
KeyManagerFactory getKeyManagerFactory();
KeyManager[] getKeyManagers();
TrustManagerFactory getTrustManagerFactory();
TrustManager[] getTrustManagers();
SSLContext createSslContext(String protocol);
}PemSslStoreDetails (configuration)
↓
PemSslStore (loaded content)
↓
PemContent (parsed PEM)
├→ PemCertificateParser → List<X509Certificate>
└→ PemPrivateKeyParser → PrivateKeyConfigure both server and client certificates for mutual authentication between microservices.
import org.springframework.boot.ssl.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.SSLContext;
@Configuration
public class MutualTlsConfiguration {
@Bean
public SslBundleRegistry sslBundleRegistry() {
DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
// Server bundle with server certificate and CA trust
PemSslStoreDetails serverKeyStore = PemSslStoreDetails.forCertificate(
"classpath:certs/server-cert.pem")
.withPrivateKey("classpath:certs/server-key.pem")
.withPrivateKeyPassword("server-key-password");
PemSslStoreDetails serverTrustStore = PemSslStoreDetails.forCertificate(
"classpath:certs/ca-cert.pem");
PemSslStoreBundle serverStoreBundle = new PemSslStoreBundle(
serverKeyStore, serverTrustStore);
// Require client authentication
SslOptions serverOptions = SslOptions.of(
new String[]{
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
},
new String[]{"TLSv1.3", "TLSv1.2"}
);
SslBundle serverBundle = SslBundle.of(
serverStoreBundle,
SslBundleKey.of("server-key-password"),
serverOptions,
"TLSv1.3"
);
registry.registerBundle("server", serverBundle);
// Client bundle with client certificate and CA trust
PemSslStoreDetails clientKeyStore = PemSslStoreDetails.forCertificate(
"classpath:certs/client-cert.pem")
.withPrivateKey("classpath:certs/client-key.pem")
.withPrivateKeyPassword("client-key-password");
PemSslStoreDetails clientTrustStore = PemSslStoreDetails.forCertificate(
"classpath:certs/ca-cert.pem");
PemSslStoreBundle clientStoreBundle = new PemSslStoreBundle(
clientKeyStore, clientTrustStore);
SslBundle clientBundle = SslBundle.of(clientStoreBundle,
SslBundleKey.of("client-key-password"));
registry.registerBundle("client", clientBundle);
return registry;
}
@Bean
public SSLContext serverSslContext(SslBundleRegistry registry) {
return registry.getBundle("server").createSslContext();
}
@Bean
public SSLContext clientSslContext(SslBundleRegistry registry) {
return registry.getBundle("client").createSslContext();
}
}Use Cases:
Implement automatic certificate renewal and hot-reload without service restart.
import org.springframework.boot.ssl.*;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
@Component
public class CertificateRotationService {
private static final Logger log = LoggerFactory.getLogger(CertificateRotationService.class);
private final SslBundleRegistry registry;
private final Path certPath = Path.of("/etc/letsencrypt/live/example.com/cert.pem");
private final Path keyPath = Path.of("/etc/letsencrypt/live/example.com/privkey.pem");
private final Path chainPath = Path.of("/etc/letsencrypt/live/example.com/chain.pem");
private FileTime lastModified;
public CertificateRotationService(SslBundleRegistry registry) {
this.registry = registry;
initializeBundle();
setupUpdateHandlers();
}
private void initializeBundle() {
try {
SslBundle initialBundle = createBundleFromFiles();
registry.registerBundle("letsencrypt", initialBundle);
lastModified = Files.getLastModifiedTime(certPath);
log.info("Initialized SSL bundle from Let's Encrypt certificates");
} catch (Exception e) {
log.error("Failed to initialize SSL bundle", e);
throw new IllegalStateException("SSL initialization failed", e);
}
}
private void setupUpdateHandlers() {
registry.addBundleUpdateHandler("letsencrypt", updatedBundle -> {
log.info("Certificate rotation detected - updating SSL context");
// Update embedded server
updateEmbeddedServer(updatedBundle);
// Update HTTP clients
updateHttpClients(updatedBundle);
log.info("SSL context updated successfully");
});
}
@Scheduled(fixedRate = 3600000) // Check every hour
public void checkForCertificateUpdates() {
try {
FileTime currentModified = Files.getLastModifiedTime(certPath);
if (currentModified.compareTo(lastModified) > 0) {
log.info("Certificate file updated - reloading");
SslBundle updatedBundle = createBundleFromFiles();
registry.updateBundle("letsencrypt", updatedBundle);
lastModified = currentModified;
log.info("Certificate rotation completed");
}
} catch (Exception e) {
log.error("Failed to check for certificate updates", e);
}
}
private SslBundle createBundleFromFiles() throws Exception {
PemSslStoreDetails keyStore = PemSslStoreDetails
.forCertificate(certPath.toString())
.withPrivateKey(keyPath.toString());
PemSslStoreDetails trustStore = PemSslStoreDetails
.forCertificate(chainPath.toString());
PemSslStoreBundle storeBundle = new PemSslStoreBundle(keyStore, trustStore);
return SslBundle.of(storeBundle);
}
private void updateEmbeddedServer(SslBundle bundle) {
// Implementation specific to embedded server (Tomcat, Jetty, etc.)
}
private void updateHttpClients(SslBundle bundle) {
// Update RestTemplate, WebClient, etc.
}
}Use Cases:
Manage different SSL configurations across development, staging, and production environments.
import org.springframework.boot.ssl.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
@Configuration
public class EnvironmentSpecificSslConfig {
@Bean
@Profile("development")
public SslBundleRegistry developmentSslRegistry() {
DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
// Use self-signed certificate for development
PemSslStoreDetails devKeyStore = PemSslStoreDetails
.forCertificate("classpath:dev-certs/localhost.pem")
.withPrivateKey("classpath:dev-certs/localhost-key.pem");
PemSslStoreBundle devBundle = new PemSslStoreBundle(devKeyStore, null);
SslBundle bundle = SslBundle.of(devBundle);
registry.registerBundle("default", bundle);
return registry;
}
@Bean
@Profile("staging")
public SslBundleRegistry stagingSslRegistry() {
DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
// Use staging certificate authority
PemSslStoreDetails stagingKeyStore = PemSslStoreDetails
.forCertificate("classpath:staging-certs/cert.pem")
.withPrivateKey("classpath:staging-certs/key.pem");
PemSslStoreDetails stagingTrustStore = PemSslStoreDetails
.forCertificate("classpath:staging-certs/staging-ca.pem");
PemSslStoreBundle storeBundle = new PemSslStoreBundle(
stagingKeyStore, stagingTrustStore);
// More relaxed SSL options for staging
SslOptions stagingOptions = SslOptions.of(
new String[]{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"},
new String[]{"TLSv1.2"}
);
SslBundle bundle = SslBundle.of(storeBundle, null, stagingOptions);
registry.registerBundle("default", bundle);
return registry;
}
@Bean
@Profile("production")
public SslBundleRegistry productionSslRegistry(Environment env) {
DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
// Load from secure external configuration
String certLocation = env.getRequiredProperty("ssl.certificate.location");
String keyLocation = env.getRequiredProperty("ssl.key.location");
String keyPassword = env.getRequiredProperty("ssl.key.password");
String trustLocation = env.getRequiredProperty("ssl.trust.location");
PemSslStoreDetails keyStore = PemSslStoreDetails
.forCertificate(certLocation)
.withPrivateKey(keyLocation)
.withPrivateKeyPassword(keyPassword);
PemSslStoreDetails trustStore = PemSslStoreDetails
.forCertificate(trustLocation);
PemSslStoreBundle storeBundle = new PemSslStoreBundle(keyStore, trustStore);
// Strict SSL options for production
SslOptions productionOptions = SslOptions.of(
new String[]{
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
},
new String[]{"TLSv1.3"}
);
SslBundle bundle = SslBundle.of(
storeBundle,
SslBundleKey.of(keyPassword),
productionOptions,
"TLSv1.3"
);
registry.registerBundle("default", bundle);
return registry;
}
}Use Cases:
Use hardware security modules for private key storage in high-security environments.
import org.springframework.boot.ssl.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.KeyManagerFactory;
import java.security.KeyStore;
import java.security.Provider;
import java.security.Security;
@Configuration
public class HsmSslConfiguration {
@Bean
public SslBundleRegistry hsmSslRegistry() throws Exception {
// Load PKCS11 provider for HSM
String pkcs11Config = """
name = HSM
library = /usr/lib/libpkcs11.so
slot = 0
""";
Provider hsm Provider = Security.getProvider("SunPKCS11");
hsmProvider = hsmProvider.configure(pkcs11Config);
Security.addProvider(hsmProvider);
// Create JKS bundle for HSM
JksSslStoreDetails hsmKeyStore = new JksSslStoreDetails(
"PKCS11", // type
"SunPKCS11", // provider
null, // location (null for hardware)
"hsm-pin" // HSM PIN
);
// Regular trust store from file
JksSslStoreDetails trustStore = new JksSslStoreDetails(
"JKS",
null,
"classpath:truststore.jks",
"trust-password"
);
JksSslStoreBundle storeBundle = new JksSslStoreBundle(
hsmKeyStore, trustStore);
// Key is stored in HSM with alias
SslBundleKey bundleKey = SslBundleKey.of("hsm-pin", "production-key");
// Create bundle
SslBundle hsmBundle = SslBundle.of(storeBundle, bundleKey);
DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
registry.registerBundle("hsm", hsmBundle);
return registry;
}
@Bean
public KeyManagerFactory hsmKeyManagerFactory(SslBundleRegistry registry) {
SslBundle bundle = registry.getBundle("hsm");
return bundle.getManagers().getKeyManagerFactory();
}
}Use Cases:
Implement comprehensive certificate monitoring with expiration alerts.
import org.springframework.boot.ssl.*;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
@Component
public class CertificateMonitoringService {
private static final Logger log = LoggerFactory.getLogger(CertificateMonitoringService.class);
private static final Duration WARNING_THRESHOLD = Duration.ofDays(30);
private static final Duration CRITICAL_THRESHOLD = Duration.ofDays(7);
private final SslBundleRegistry registry;
public CertificateMonitoringService(SslBundleRegistry registry) {
this.registry = registry;
}
@Scheduled(cron = "0 0 2 * * *") // Daily at 2 AM
public void monitorCertificates() {
log.info("Starting certificate monitoring check");
try {
SslBundle bundle = registry.getBundle("default");
validateCertificateChain(bundle);
} catch (NoSuchSslBundleException e) {
log.error("SSL bundle 'default' not found", e);
} catch (Exception e) {
log.error("Certificate monitoring failed", e);
}
}
private void validateCertificateChain(SslBundle bundle) throws Exception {
KeyStore keyStore = bundle.getStores().getKeyStore();
if (keyStore == null) {
log.warn("No key store available for certificate monitoring");
return;
}
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
Certificate[] chain = keyStore.getCertificateChain(alias);
if (chain == null) {
continue;
}
log.info("Checking certificate chain for alias: {}", alias);
for (int i = 0; i < chain.length; i++) {
if (chain[i] instanceof X509Certificate x509) {
checkCertificateExpiration(alias, i, x509);
validateCertificateProperties(alias, i, x509);
}
}
}
}
private void checkCertificateExpiration(String alias, int position,
X509Certificate cert) {
Instant now = Instant.now();
Instant notAfter = cert.getNotAfter().toInstant();
Duration timeUntilExpiry = Duration.between(now, notAfter);
if (timeUntilExpiry.isNegative()) {
log.error("EXPIRED: Certificate '{}' position {} expired on {}",
alias, position, cert.getNotAfter());
sendAlert(AlertLevel.CRITICAL, alias, cert, "Certificate expired");
} else if (timeUntilExpiry.compareTo(CRITICAL_THRESHOLD) < 0) {
log.error("CRITICAL: Certificate '{}' position {} expires in {} days",
alias, position, timeUntilExpiry.toDays());
sendAlert(AlertLevel.CRITICAL, alias, cert,
"Certificate expiring in " + timeUntilExpiry.toDays() + " days");
} else if (timeUntilExpiry.compareTo(WARNING_THRESHOLD) < 0) {
log.warn("WARNING: Certificate '{}' position {} expires in {} days",
alias, position, timeUntilExpiry.toDays());
sendAlert(AlertLevel.WARNING, alias, cert,
"Certificate expiring in " + timeUntilExpiry.toDays() + " days");
} else {
log.debug("Certificate '{}' position {} valid until {}",
alias, position, cert.getNotAfter());
}
}
private void validateCertificateProperties(String alias, int position,
X509Certificate cert) {
// Check key size
int keySize = cert.getPublicKey().getEncoded().length * 8;
if (keySize < 2048) {
log.warn("Weak key size {} for certificate '{}' position {}",
keySize, alias, position);
}
// Check signature algorithm
String sigAlg = cert.getSigAlgName();
if (sigAlg.contains("SHA1") || sigAlg.contains("MD5")) {
log.warn("Weak signature algorithm {} for certificate '{}' position {}",
sigAlg, alias, position);
}
// Log certificate details
log.info("Certificate '{}' position {}: Subject={}, Issuer={}, Valid until={}",
alias, position, cert.getSubjectX500Principal(),
cert.getIssuerX500Principal(), cert.getNotAfter());
}
private void sendAlert(AlertLevel level, String alias,
X509Certificate cert, String message) {
// Integrate with alerting system (PagerDuty, Slack, email, etc.)
log.info("Sending {} alert: {} for certificate {}",
level, message, alias);
}
enum AlertLevel {
WARNING, CRITICAL
}
}Use Cases:
assertContainsAlias() to verify key aliases exist before deploymentNoSuchSslBundleException and provide fallback behaviorSslBundle.systemDefault() for standard cases@Component
public class RobustSslConfiguration {
private final SslBundleRegistry registry;
public RobustSslConfiguration(SslBundleRegistry registry) {
this.registry = registry;
}
public SSLContext getSslContext(String bundleName) {
try {
SslBundle bundle = registry.getBundle(bundleName);
return bundle.createSslContext();
} catch (NoSuchSslBundleException e) {
log.error("SSL bundle '{}' not found, using system default", bundleName, e);
return SslBundle.systemDefault().createSslContext();
} catch (Exception e) {
log.error("Failed to create SSL context", e);
throw new IllegalStateException("SSL configuration failed", e);
}
}
}ssl.pem Package)The org.springframework.boot.ssl.pem package provides comprehensive support for PEM-encoded SSL/TLS certificates and private keys.
Represents PEM-encoded content that can provide X.509 certificates and private keys.
package org.springframework.boot.ssl.pem;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* PEM encoded content that can provide X509Certificate certificates and PrivateKey private keys.
*
* Thread Safety: Immutable and thread-safe after construction.
*
* @since 3.2.0
*/
public final class PemContent {
/**
* Parse and return all X509Certificate certificates from the PEM content.
* Most PEM files either contain a single certificate or a certificate chain.
*
* @return the certificates
* @throws IllegalStateException if no certificates could be loaded
*/
public List<X509Certificate> getCertificates();
/**
* Parse and return the PrivateKey from the PEM content.
*
* @return the private key or null
* @throws IllegalStateException if no private key could be loaded
*/
public @Nullable PrivateKey getPrivateKey();
/**
* Parse and return the PrivateKey from the PEM content with password decryption.
*
* @param password the password to decrypt the private key or null
* @return the private key or null
*/
public @Nullable PrivateKey getPrivateKey(@Nullable String password);
/**
* Load PemContent from the given Path.
*
* @param path a path to load the content from
* @return the loaded PEM content
* @throws IOException on IO error
*/
public static PemContent load(Path path) throws IOException;
/**
* Load PemContent from the given InputStream.
*
* @param in an input stream to load the content from
* @return the loaded PEM content
* @throws IOException on IO error
*/
public static PemContent load(InputStream in) throws IOException;
/**
* Return a new PemContent instance containing the given text.
*
* @param text the text containing PEM encoded content
* @return a new PemContent instance
*/
public static @Nullable PemContent of(@Nullable String text);
/**
* Return if PEM content is present in the given text.
*
* @param text the text to check
* @return true if the text includes PEM encoded content
*/
public static boolean isPresentInText(@Nullable String text);
}An individual trust or key store loaded from PEM content.
package org.springframework.boot.ssl.pem;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* An individual trust or key store that has been loaded from PEM content.
*
* Thread Safety: Immutable and thread-safe.
*
* @since 3.2.0
*/
public interface PemSslStore {
/**
* The key store type, for example JKS or PKCS11. A null value will use KeyStore.getDefaultType().
*
* @return the key store type
*/
@Nullable String type();
/**
* The alias used when setting entries in the KeyStore.
*
* @return the alias
*/
@Nullable String alias();
/**
* The password used when setting key entries in the KeyStore.
*
* @return the password
*/
@Nullable String password();
/**
* The certificates for this store. When a private key is present the returned value is treated
* as a certificate chain, otherwise it is treated as a list of certificates that should all be registered.
*
* @return the X509 certificates
*/
@Nullable List<X509Certificate> certificates();
/**
* The private key for this store or null.
*
* @return the private key
*/
@Nullable PrivateKey privateKey();
/**
* Return a new PemSslStore instance with a new alias.
*
* @param alias the new alias
* @return a new PemSslStore instance
*/
default PemSslStore withAlias(@Nullable String alias);
/**
* Return a new PemSslStore instance with a new password.
*
* @param password the new password
* @return a new PemSslStore instance
*/
default PemSslStore withPassword(@Nullable String password);
/**
* Return a PemSslStore instance loaded using the given PemSslStoreDetails.
*
* @param details the PEM store details
* @return a loaded PemSslStore or null
*/
static @Nullable PemSslStore load(@Nullable PemSslStoreDetails details);
/**
* Factory method to create a new PemSslStore with the given values.
*
* @param type the key store type
* @param certificates the certificates for this store
* @param privateKey the private key
* @return a new PemSslStore instance
*/
static PemSslStore of(@Nullable String type, List<X509Certificate> certificates, @Nullable PrivateKey privateKey);
}Details for configuring an individual trust or key store from PEM content.
package org.springframework.boot.ssl.pem;
/**
* Details for an individual trust or key store in a PemSslStoreBundle.
*
* Thread Safety: Immutable record, thread-safe.
*
* @since 3.1.0
*/
public record PemSslStoreDetails(
@Nullable String type,
@Nullable String alias,
@Nullable String password,
@Nullable String certificates,
@Nullable String privateKey,
@Nullable String privateKeyPassword
) {
/**
* Return a new PemSslStoreDetails instance with a new alias.
*
* @param alias the new alias
* @return a new PemSslStoreDetails instance
*/
public PemSslStoreDetails withAlias(@Nullable String alias);
/**
* Return a new PemSslStoreDetails instance with a new password.
*
* @param password the new password
* @return a new PemSslStoreDetails instance
*/
public PemSslStoreDetails withPassword(@Nullable String password);
/**
* Return a new PemSslStoreDetails instance with a new private key.
*
* @param privateKey the new private key
* @return a new PemSslStoreDetails instance
*/
public PemSslStoreDetails withPrivateKey(@Nullable String privateKey);
/**
* Factory method to create a new PemSslStoreDetails instance for the given certificates.
*
* @param certificates the certificates content (either the PEM content itself or a reference to the resource to load)
* @return a new PemSslStoreDetails instance
*/
public static PemSslStoreDetails forCertificates(@Nullable String certificates);
}SslStoreBundle backed by PEM-encoded certificates and private keys.
package org.springframework.boot.ssl.pem;
import org.springframework.boot.ssl.SslStoreBundle;
/**
* SslStoreBundle backed by PEM-encoded certificates and private keys.
*
* Thread Safety: Thread-safe. Lazy-loads KeyStore instances on first access.
*
* @since 3.1.0
*/
public class PemSslStoreBundle implements SslStoreBundle {
/**
* Create a new PemSslStoreBundle instance.
*
* @param keyStoreDetails the key store details
* @param trustStoreDetails the trust store details
*/
public PemSslStoreBundle(@Nullable PemSslStoreDetails keyStoreDetails, @Nullable PemSslStoreDetails trustStoreDetails);
/**
* Create a new PemSslStoreBundle instance.
*
* @param pemKeyStore the PEM key store
* @param pemTrustStore the PEM trust store
*/
public PemSslStoreBundle(@Nullable PemSslStore pemKeyStore, @Nullable PemSslStore pemTrustStore);
@Override
public @Nullable KeyStore getKeyStore();
@Override
public @Nullable String getKeyStorePassword();
@Override
public @Nullable KeyStore getTrustStore();
}Parser for X.509 certificates in PEM format (internal utility).
package org.springframework.boot.ssl.pem;
/**
* Parser for X.509 certificates in PEM format.
* Internal utility class for parsing PEM-encoded certificates.
*
* Supports standard PEM format with BEGIN/END CERTIFICATE markers.
*
* Thread Safety: Stateless utility, thread-safe.
*/
final class PemCertificateParser {
// Internal implementation - parses PEM certificates from text
}Parser for PKCS private key files in PEM format (internal utility).
package org.springframework.boot.ssl.pem;
/**
* Parser for PKCS private key files in PEM format.
* Internal utility class for parsing PEM-encoded private keys.
*
* Supports multiple formats:
* - PKCS#1 RSA private keys (BEGIN RSA PRIVATE KEY)
* - PKCS#8 private keys (BEGIN PRIVATE KEY)
* - PKCS#8 encrypted private keys (BEGIN ENCRYPTED PRIVATE KEY)
* - SEC1 EC private keys (BEGIN EC PRIVATE KEY)
*
* Algorithms supported: RSA, RSASSA-PSS, EC, DSA, EdDSA, XDH
*
* Thread Safety: Stateless utility, thread-safe.
*/
final class PemPrivateKeyParser {
// Internal implementation - parses various PEM private key formats
}Internal implementation of PemSslStore that lazy-loads content (internal class).
ssl.jks Package)The org.springframework.boot.ssl.jks package provides support for Java KeyStore (JKS) based SSL/TLS configuration.
Details for configuring an individual trust or key store from a JKS file.
package org.springframework.boot.ssl.jks;
/**
* Details for an individual trust or key store in a JksSslStoreBundle.
*
* Thread Safety: Immutable record, thread-safe.
*
* @since 3.1.0
*/
public record JksSslStoreDetails(
@Nullable String type,
@Nullable String provider,
@Nullable String location,
@Nullable String password
) {
/**
* Return a new JksSslStoreDetails instance with a new password.
*
* @param password the new password
* @return a new JksSslStoreDetails instance
*/
public JksSslStoreDetails withPassword(String password);
/**
* Factory method to create a new JksSslStoreDetails instance for the given location.
*
* @param location the location of the key store file
* @return a new JksSslStoreDetails instance
*/
public static JksSslStoreDetails forLocation(@Nullable String location);
}SslStoreBundle backed by Java KeyStore files.
package org.springframework.boot.ssl.jks;
import org.springframework.boot.ssl.SslStoreBundle;
/**
* SslStoreBundle backed by a Java keystore.
*
* Supports standard JKS keystores as well as PKCS11 hardware keystores.
*
* Thread Safety: Thread-safe. Lazy-loads KeyStore instances on first access.
*
* @since 3.1.0
*/
public class JksSslStoreBundle implements SslStoreBundle {
/**
* Create a new JksSslStoreBundle instance.
*
* @param keyStoreDetails the key store details
* @param trustStoreDetails the trust store details
*/
public JksSslStoreBundle(@Nullable JksSslStoreDetails keyStoreDetails, @Nullable JksSslStoreDetails trustStoreDetails);
/**
* Create a new JksSslStoreBundle instance with custom resource loader.
*
* @param keyStoreDetails the key store details
* @param trustStoreDetails the trust store details
* @param resourceLoader the resource loader used to load content
*/
public JksSslStoreBundle(@Nullable JksSslStoreDetails keyStoreDetails, @Nullable JksSslStoreDetails trustStoreDetails, ResourceLoader resourceLoader);
@Override
public @Nullable KeyStore getKeyStore();
@Override
public @Nullable String getKeyStorePassword();
@Override
public @Nullable KeyStore getTrustStore();
}JKS Usage Example:
// Configure JKS-based SSL bundle
JksSslStoreDetails keyStore = new JksSslStoreDetails(
"PKCS12", // type
null, // provider
"classpath:keystore.p12", // location
"changeit" // password
);
JksSslStoreDetails trustStore = new JksSslStoreDetails(
"JKS",
null,
"classpath:truststore.jks",
"changeit"
);
JksSslStoreBundle bundle = new JksSslStoreBundle(keyStore, trustStore);
KeyStore ks = bundle.getKeyStore();