Service Provider Interface (SPI) for HTTP client implementations in the AWS SDK for Java v2
Comprehensive TLS/SSL configuration providers for client authentication and server certificate validation. Includes file-based and system property-based key manager providers, supporting various keystore formats and authentication scenarios.
Functional interface for providing KeyManagers used in client TLS authentication. Implementations supply the key managers that contain client certificates and private keys.
/**
* Provider for KeyManagers used in client TLS authentication.
* KeyManagers contain the client certificates and private keys used
* for mutual TLS authentication with servers.
*/
@FunctionalInterface
public interface TlsKeyManagersProvider {
/**
* Provide key managers for TLS client authentication
* @return Array of KeyManagers, or null if no client authentication is needed
*/
KeyManager[] keyManagers();
/**
* Provider that returns null (no client authentication)
* @return TlsKeyManagersProvider that provides no key managers
*/
static TlsKeyManagersProvider noneProvider();
}Usage Example:
// Custom key manager provider
TlsKeyManagersProvider customProvider = () -> {
try {
KeyStore keyStore = loadClientKeyStore();
KeyManagerFactory factory = KeyManagerFactory.getInstance("SunX509");
factory.init(keyStore, "keystore-password".toCharArray());
return factory.getKeyManagers();
} catch (Exception e) {
throw new RuntimeException("Failed to load client key managers", e);
}
};
// No client authentication
TlsKeyManagersProvider noAuth = TlsKeyManagersProvider.noneProvider();
// Using with HTTP client builder
MyHttpClient.Builder builder = MyHttpClient.builder()
.tlsKeyManagersProvider(customProvider);Provider that loads key managers from a file-based keystore. Supports various keystore formats including JKS, PKCS12, and others.
/**
* Loads key managers from a file-based key store.
* Supports standard Java keystore formats (JKS, PKCS12, etc.).
*/
public final class FileStoreTlsKeyManagersProvider extends AbstractFileStoreTlsKeyManagersProvider {
/**
* Load key managers from the configured keystore file
* @return Array of KeyManagers loaded from the file store
*/
@Override
public KeyManager[] keyManagers();
/**
* Create a file store provider from keystore file
* @param path Path to the keystore file
* @param type Keystore type (e.g., "JKS", "PKCS12")
* @param password Password for the keystore
* @return FileStoreTlsKeyManagersProvider instance
*/
public static FileStoreTlsKeyManagersProvider create(Path path, String type, String password);
}Usage Examples:
// Load from JKS keystore
FileStoreTlsKeyManagersProvider jksProvider = FileStoreTlsKeyManagersProvider.create(
Paths.get("/path/to/client.jks"),
"JKS",
"keystore-password"
);
// Load from PKCS12 keystore
FileStoreTlsKeyManagersProvider p12Provider = FileStoreTlsKeyManagersProvider.create(
Paths.get("/path/to/client.p12"),
"PKCS12",
"keystore-password"
);
// Using with HTTP client
SdkHttpClient client = httpClientBuilder
.tlsKeyManagersProvider(jksProvider)
.build();
// Load from environment-specified location
String keystorePath = System.getenv("CLIENT_KEYSTORE_PATH");
String keystorePass = System.getenv("CLIENT_KEYSTORE_PASSWORD");
if (keystorePath != null && keystorePass != null) {
FileStoreTlsKeyManagersProvider envProvider = FileStoreTlsKeyManagersProvider.create(
Paths.get(keystorePath),
"PKCS12", // or detect from file extension
keystorePass
);
clientBuilder.tlsKeyManagersProvider(envProvider);
}Provider that loads key managers from standard JSSE system properties. Uses the same properties that the JVM uses for default SSL context configuration.
/**
* Loads key managers from standard JSSE system properties.
* Uses javax.net.ssl.keyStore, javax.net.ssl.keyStorePassword,
* and javax.net.ssl.keyStoreType system properties.
*/
public final class SystemPropertyTlsKeyManagersProvider extends AbstractFileStoreTlsKeyManagersProvider {
/**
* Load key managers from system properties
* @return Array of KeyManagers loaded from system property configuration
*/
@Override
public KeyManager[] keyManagers();
/**
* Create a system property provider
* @return SystemPropertyTlsKeyManagersProvider instance
*/
public static SystemPropertyTlsKeyManagersProvider create();
}Usage Example:
// Using system properties
SystemPropertyTlsKeyManagersProvider sysProvider = SystemPropertyTlsKeyManagersProvider.create();
// The provider will read these system properties:
// -Djavax.net.ssl.keyStore=/path/to/client.jks
// -Djavax.net.ssl.keyStorePassword=password
// -Djavax.net.ssl.keyStoreType=JKS
// Using with HTTP client
SdkHttpClient client = httpClientBuilder
.tlsKeyManagersProvider(sysProvider)
.build();
// Command line example:
// java -Djavax.net.ssl.keyStore=/etc/ssl/client.p12 \
// -Djavax.net.ssl.keyStorePassword=secret \
// -Djavax.net.ssl.keyStoreType=PKCS12 \
// MyApplicationFunctional interface for providing TrustManagers used in server certificate validation. Implementations supply trust managers that validate server certificates.
/**
* Provider for TrustManagers used in server certificate validation.
* TrustManagers determine which server certificates to trust during
* TLS handshake.
*/
@FunctionalInterface
public interface TlsTrustManagersProvider {
/**
* Provide trust managers for server certificate validation
* @return Array of TrustManagers, or null to use system default trust store
*/
TrustManager[] trustManagers();
}Usage Examples:
// Custom trust store
TlsTrustManagersProvider customTrust = () -> {
try {
KeyStore trustStore = loadCustomTrustStore();
TrustManagerFactory factory = TrustManagerFactory.getInstance("SunX509");
factory.init(trustStore);
return factory.getTrustManagers();
} catch (Exception e) {
throw new RuntimeException("Failed to load trust managers", e);
}
};
// Trust all certificates (DANGEROUS - for testing only)
TlsTrustManagersProvider trustAll = () -> new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// Accept all
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// Accept all
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
// System default trust store
TlsTrustManagersProvider systemDefault = () -> null; // Uses system defaultComplete setup for mutual TLS (client certificate authentication):
// Load client certificate and private key
FileStoreTlsKeyManagersProvider keyProvider = FileStoreTlsKeyManagersProvider.create(
Paths.get("/etc/ssl/private/client.p12"),
"PKCS12",
System.getenv("CLIENT_CERT_PASSWORD")
);
// Load custom CA certificates for server validation
TlsTrustManagersProvider trustProvider = () -> {
try {
KeyStore trustStore = KeyStore.getInstance("JKS");
try (InputStream is = Files.newInputStream(Paths.get("/etc/ssl/ca-bundle.jks"))) {
trustStore.load(is, "truststore-password".toCharArray());
}
TrustManagerFactory factory = TrustManagerFactory.getInstance("SunX509");
factory.init(trustStore);
return factory.getTrustManagers();
} catch (Exception e) {
throw new RuntimeException("Failed to load trust store", e);
}
};
// Configure HTTP client with mutual TLS
SdkHttpClient client = httpClientBuilder
.tlsKeyManagersProvider(keyProvider)
.tlsTrustManagersProvider(trustProvider)
.build();Configuration that adapts to deployment environment:
public static TlsKeyManagersProvider createKeyManagerProvider() {
// Try system properties first
if (System.getProperty("javax.net.ssl.keyStore") != null) {
return SystemPropertyTlsKeyManagersProvider.create();
}
// Try environment variables
String keystorePath = System.getenv("TLS_KEYSTORE_PATH");
String keystorePassword = System.getenv("TLS_KEYSTORE_PASSWORD");
String keystoreType = System.getenv("TLS_KEYSTORE_TYPE");
if (keystorePath != null && keystorePassword != null) {
return FileStoreTlsKeyManagersProvider.create(
Paths.get(keystorePath),
keystoreType != null ? keystoreType : "PKCS12",
keystorePassword
);
}
// No client authentication
return TlsKeyManagersProvider.noneProvider();
}
public static TlsTrustManagersProvider createTrustManagerProvider() {
String trustStorePath = System.getenv("TLS_TRUSTSTORE_PATH");
String trustStorePassword = System.getenv("TLS_TRUSTSTORE_PASSWORD");
if (trustStorePath != null) {
return () -> {
try {
KeyStore trustStore = KeyStore.getInstance("JKS");
try (InputStream is = Files.newInputStream(Paths.get(trustStorePath))) {
trustStore.load(is, trustStorePassword != null ?
trustStorePassword.toCharArray() : null);
}
TrustManagerFactory factory = TrustManagerFactory.getInstance("SunX509");
factory.init(trustStore);
return factory.getTrustManagers();
} catch (Exception e) {
throw new RuntimeException("Failed to load custom trust store", e);
}
};
}
// Use system default
return () -> null;
}Different approaches to server certificate validation:
// Strict validation (production)
TlsTrustManagersProvider strictValidation = () -> {
// Use system default trust store with standard validation
return null;
};
// Custom CA validation
TlsTrustManagersProvider customCaValidation = () -> {
// Load organization-specific CA certificates
return loadOrganizationTrustManagers();
};
// Hostname verification bypass (for testing with self-signed certs)
TlsTrustManagersProvider lenientValidation = () -> new TrustManager[] {
new X509TrustManager() {
private final X509TrustManager defaultTrustManager = getDefaultTrustManager();
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
defaultTrustManager.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
// Log warning but allow self-signed certificates in test environment
if (isTestEnvironment()) {
logger.warn("Accepting untrusted certificate in test environment", e);
} else {
throw e;
}
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return defaultTrustManager.getAcceptedIssuers();
}
}
};Never Trust All Certificates in Production:
// NEVER do this in production
TlsTrustManagersProvider dangerousTrustAll = () -> new TrustManager[] {
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
};Secure Password Handling:
// Read passwords from secure sources
String password = System.getenv("KEYSTORE_PASSWORD"); // Environment variable
// Or from secure configuration service
// Never hardcode passwords in source codeCertificate Rotation Support:
// Support certificate updates without restart
TlsKeyManagersProvider reloadableProvider = new TlsKeyManagersProvider() {
private volatile KeyManager[] cachedKeyManagers;
private volatile long lastLoadTime;
private final long reloadInterval = Duration.ofHours(1).toMillis();
@Override
public KeyManager[] keyManagers() {
long now = System.currentTimeMillis();
if (cachedKeyManagers == null || (now - lastLoadTime) > reloadInterval) {
synchronized (this) {
if (cachedKeyManagers == null || (now - lastLoadTime) > reloadInterval) {
cachedKeyManagers = loadKeyManagers();
lastLoadTime = now;
}
}
}
return cachedKeyManagers;
}
};Error Handling:
TlsKeyManagersProvider robustProvider = () -> {
try {
return loadKeyManagers();
} catch (Exception e) {
// Log error with sufficient detail for debugging
logger.error("Failed to load TLS key managers from {}", keystorePath, e);
// Decide whether to fail fast or fall back
if (isClientAuthRequired()) {
throw new RuntimeException("Client authentication required but key managers unavailable", e);
} else {
logger.warn("Proceeding without client authentication");
return null;
}
}
};The SPI supports various keystore formats:
Example format detection:
public static String detectKeystoreType(Path keystorePath) {
String filename = keystorePath.getFileName().toString().toLowerCase();
if (filename.endsWith(".p12") || filename.endsWith(".pfx")) {
return "PKCS12";
} else if (filename.endsWith(".jks")) {
return "JKS";
} else if (filename.endsWith(".jceks")) {
return "JCEKS";
} else {
// Default to PKCS12 for modern compatibility
return "PKCS12";
}
}Install with Tessl CLI
npx tessl i tessl/maven-software-amazon-awssdk--http-client-spi