CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-software-amazon-awssdk--http-client-spi

Service Provider Interface (SPI) for HTTP client implementations in the AWS SDK for Java v2

Overview
Eval results
Files

tls-configuration.mddocs/

TLS and SSL Configuration

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.

Capabilities

TlsKeyManagersProvider

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);

FileStoreTlsKeyManagersProvider

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);
}

SystemPropertyTlsKeyManagersProvider

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 \
//      MyApplication

TlsTrustManagersProvider

Functional 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 default

Configuration Patterns

Mutual TLS Authentication

Complete 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();

Environment-Based Configuration

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;
}

Certificate Validation Strategies

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();
        }
    }
};

Security Considerations

Best Practices

  1. 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]; }
        }
    };
  2. 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 code
  3. Certificate 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;
        }
    };
  4. 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;
            }
        }
    };

Keystore Format Support

The SPI supports various keystore formats:

  • JKS: Java KeyStore (legacy format)
  • PKCS12: Standard format (.p12, .pfx files)
  • JCEKS: Java Cryptography Extension KeyStore
  • BKS: Bouncy Castle KeyStore
  • UBER: Bouncy Castle UBER KeyStore

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

docs

content-streaming.md

http-clients.md

http-messages.md

index.md

metrics.md

service-discovery.md

tls-configuration.md

tile.json