SSL/TLS configuration library for Elasticsearch providing comprehensive security management utilities.
—
Comprehensive utility functions for keystore operations, PEM file parsing, certificate handling, SSL-related tasks, and low-level ASN.1/DER parsing with extensive validation and error handling.
Comprehensive utilities for keystore manipulation, type detection, and key/trust manager creation.
/**
* Utility methods for working with KeyStore instances
*/
public final class KeyStoreUtil {
/**
* Infers keystore type from file path extension
* @param path keystore file path
* @return inferred keystore type (JKS, PKCS12, etc.)
*/
public static String inferKeyStoreType(String path);
/**
* Reads a keystore from file with specified type and password
* @param path path to keystore file
* @param ksType keystore type (JKS, PKCS12, etc.)
* @param password keystore password
* @return loaded KeyStore instance
* @throws GeneralSecurityException if keystore loading fails
* @throws IOException if file reading fails
*/
public static KeyStore readKeyStore(Path path, String ksType, char[] password)
throws GeneralSecurityException, IOException;
/**
* Builds a keystore from certificate chain and private key
* @param certificateChain collection of certificates (first should be end entity)
* @param privateKey private key corresponding to end entity certificate
* @param password password for keystore and private key protection
* @return KeyStore containing the certificate chain and private key
* @throws GeneralSecurityException if keystore creation fails
*/
public static KeyStore buildKeyStore(Collection<Certificate> certificateChain,
PrivateKey privateKey, char[] password)
throws GeneralSecurityException;
/**
* Filters keystore entries based on predicate
* @param store original keystore
* @param filter predicate to test keystore entries
* @return new KeyStore containing only entries that match the filter
*/
public static KeyStore filter(KeyStore store, Predicate<KeyStoreEntry> filter);
/**
* Builds a truststore from collection of certificates
* @param certificates trusted certificates to include
* @return KeyStore configured as truststore
* @throws GeneralSecurityException if truststore creation fails
*/
public static KeyStore buildTrustStore(Iterable<Certificate> certificates)
throws GeneralSecurityException;
/**
* Builds a truststore with specific type from certificates
* @param certificates trusted certificates to include
* @param type keystore type for truststore
* @return KeyStore of specified type configured as truststore
* @throws GeneralSecurityException if truststore creation fails
*/
public static KeyStore buildTrustStore(Iterable<Certificate> certificates, String type)
throws GeneralSecurityException;
/**
* Creates key manager from certificate chain and private key
* @param certificateChain certificate chain (end entity first)
* @param privateKey private key for end entity certificate
* @param password password for key protection
* @return configured X509ExtendedKeyManager
* @throws GeneralSecurityException if key manager creation fails
* @throws IOException if certificate/key processing fails
*/
public static X509ExtendedKeyManager createKeyManager(Certificate[] certificateChain,
PrivateKey privateKey, char[] password)
throws GeneralSecurityException, IOException;
/**
* Creates key manager from keystore
* @param keyStore keystore containing private keys and certificates
* @param password password for private key access
* @param algorithm key manager algorithm (e.g., "SunX509", "PKIX")
* @return configured X509ExtendedKeyManager
* @throws GeneralSecurityException if key manager creation fails
*/
public static X509ExtendedKeyManager createKeyManager(KeyStore keyStore, char[] password,
String algorithm)
throws GeneralSecurityException;
/**
* Creates trust manager from truststore
* @param trustStore truststore containing trusted certificates (null for JDK defaults)
* @param algorithm trust manager algorithm (e.g., "PKIX", "SunX509")
* @return configured X509ExtendedTrustManager
* @throws NoSuchAlgorithmException if algorithm is not available
* @throws KeyStoreException if truststore access fails
*/
public static X509ExtendedTrustManager createTrustManager(@Nullable KeyStore trustStore,
String algorithm)
throws NoSuchAlgorithmException, KeyStoreException;
/**
* Creates trust manager from collection of certificates
* @param certificates trusted certificates
* @return configured X509ExtendedTrustManager
* @throws GeneralSecurityException if trust manager creation fails
*/
public static X509ExtendedTrustManager createTrustManager(Collection<Certificate> certificates)
throws GeneralSecurityException;
/**
* Creates stream of keystore entries for processing
* @param keyStore keystore to stream
* @param exceptionHandler function to handle GeneralSecurityException during streaming
* @return Stream of KeyStoreEntry objects
*/
public static Stream<KeyStoreEntry> stream(KeyStore keyStore,
Function<GeneralSecurityException, ? extends RuntimeException> exceptionHandler);
}Usage Examples:
import org.elasticsearch.common.ssl.KeyStoreUtil;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
// Infer keystore type
String type = KeyStoreUtil.inferKeyStoreType("keystore.p12"); // returns "PKCS12"
String jksType = KeyStoreUtil.inferKeyStoreType("keystore.jks"); // returns "JKS"
// Read keystore from file
KeyStore keystore = KeyStoreUtil.readKeyStore(
Paths.get("/etc/ssl/keystore.p12"),
"PKCS12",
"password".toCharArray()
);
// Build keystore from certificates and key
Collection<Certificate> certChain = List.of(endEntityCert, intermediateCert);
KeyStore builtKeystore = KeyStoreUtil.buildKeyStore(
certChain,
privateKey,
"password".toCharArray()
);
// Filter keystore entries
KeyStore filteredKeystore = KeyStoreUtil.filter(keystore, entry ->
entry.getAlias().startsWith("server-"));
// Build truststore from CA certificates
KeyStore truststore = KeyStoreUtil.buildTrustStore(caCertificates);
// Create key manager
X509ExtendedKeyManager keyManager = KeyStoreUtil.createKeyManager(
certChain.toArray(new Certificate[0]),
privateKey,
"password".toCharArray()
);
// Create trust manager from certificates
X509ExtendedTrustManager trustManager = KeyStoreUtil.createTrustManager(caCertificates);
// Stream keystore entries
KeyStoreUtil.stream(keystore, ex -> new RuntimeException(ex))
.filter(entry -> entry.isKeyEntry())
.forEach(entry -> {
System.out.println("Key alias: " + entry.getAlias());
X509Certificate cert = entry.getX509Certificate();
System.out.println("Subject: " + cert.getSubjectX500Principal());
});Wrapper class for individual keystore entries providing convenient access methods.
/**
* Wrapper for individual keystore entries
*/
public static class KeyStoreEntry {
/**
* Gets the alias of this keystore entry
* @return entry alias
*/
public String getAlias();
/**
* Gets the certificate as X509Certificate
* @return X509Certificate or null if not a certificate entry
*/
public X509Certificate getX509Certificate();
/**
* Checks if this entry contains a private key
* @return true if this is a key entry
*/
public boolean isKeyEntry();
/**
* Gets the private key from this entry
* @param password password for private key access
* @return PrivateKey or null if not a key entry or wrong password
*/
public PrivateKey getKey(char[] password);
/**
* Gets the certificate chain for this entry
* @return list of certificates in the chain
*/
public List<? extends X509Certificate> getX509CertificateChain();
/**
* Deletes this entry from the keystore
*/
public void delete();
}Utilities for parsing PEM formatted certificates and private keys with support for encrypted keys.
/**
* Utilities for parsing PEM formatted certificates and private keys
*/
public final class PemUtils {
/**
* Reads private key from PEM file with optional password
* @param path path to PEM private key file
* @param passwordSupplier supplier for key password (called only if key is encrypted)
* @return parsed PrivateKey
* @throws IOException if file reading fails
* @throws GeneralSecurityException if key parsing fails
*/
public static PrivateKey readPrivateKey(Path path, Supplier<char[]> passwordSupplier)
throws IOException, GeneralSecurityException;
/**
* Parses PKCS#8 PEM formatted private key string
* @param pemString PEM formatted private key string
* @return parsed PrivateKey
* @throws IOException if PEM parsing fails
* @throws GeneralSecurityException if key parsing fails
*/
public static PrivateKey parsePKCS8PemString(String pemString)
throws IOException, GeneralSecurityException;
/**
* Reads certificates from multiple PEM files
* @param certPaths collection of paths to PEM certificate files
* @return list of parsed certificates
* @throws CertificateException if certificate parsing fails
* @throws IOException if file reading fails
*/
public static List<Certificate> readCertificates(Collection<Path> certPaths)
throws CertificateException, IOException;
}Usage Examples:
import org.elasticsearch.common.ssl.PemUtils;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.nio.file.Paths;
import java.util.List;
import java.util.function.Supplier;
// Read unencrypted private key
PrivateKey privateKey = PemUtils.readPrivateKey(
Paths.get("/etc/ssl/private/server.key"),
() -> null // no password needed
);
// Read encrypted private key
PrivateKey encryptedKey = PemUtils.readPrivateKey(
Paths.get("/etc/ssl/private/encrypted.key"),
() -> "secret123".toCharArray() // provide password when needed
);
// Read encrypted key with interactive password prompt
PrivateKey interactiveKey = PemUtils.readPrivateKey(
Paths.get("/etc/ssl/private/secure.key"),
() -> {
System.out.print("Enter key password: ");
return System.console().readPassword();
}
);
// Parse PEM string directly
String pemKeyString = """
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
-----END PRIVATE KEY-----
""";
PrivateKey parsedKey = PemUtils.parsePKCS8PemString(pemKeyString);
// Read multiple certificate files
List<Certificate> certificates = PemUtils.readCertificates(List.of(
Paths.get("/etc/ssl/certs/ca.pem"),
Paths.get("/etc/ssl/certs/intermediate.pem"),
Paths.get("/etc/ssl/certs/server.pem")
));
// Read certificate chain from single file
List<Certificate> certChain = PemUtils.readCertificates(List.of(
Paths.get("/etc/ssl/certs/fullchain.pem") // file with multiple certificates
));General SSL-related utility methods for certificate operations.
/**
* SSL-related utility methods
*/
public final class SslUtil {
/**
* Calculates certificate fingerprint using specified algorithm
* @param certificate X509 certificate to fingerprint
* @param algorithm hash algorithm (e.g., "SHA-256", "SHA-1", "MD5")
* @return hex-encoded fingerprint string
* @throws CertificateEncodingException if certificate encoding fails
*/
public static String calculateFingerprint(X509Certificate certificate, String algorithm)
throws CertificateEncodingException;
}Usage Examples:
import org.elasticsearch.common.ssl.SslUtil;
import java.security.cert.X509Certificate;
// Calculate SHA-256 fingerprint
String sha256Fingerprint = SslUtil.calculateFingerprint(certificate, "SHA-256");
System.out.println("SHA-256: " + sha256Fingerprint);
// Calculate SHA-1 fingerprint
String sha1Fingerprint = SslUtil.calculateFingerprint(certificate, "SHA-1");
System.out.println("SHA-1: " + sha1Fingerprint);
// Calculate MD5 fingerprint (less secure, for compatibility)
String md5Fingerprint = SslUtil.calculateFingerprint(certificate, "MD5");
System.out.println("MD5: " + md5Fingerprint);Minimal ASN.1 DER decoder for PKCS#1 private keys, providing low-level parsing capabilities.
/**
* Minimal ASN.1 DER decoder for PKCS#1 private keys
*/
public final class DerParser {
/**
* Creates DER parser for byte array
* @param bytes DER encoded bytes to parse
*/
public DerParser(byte[] bytes);
/**
* Reads ASN.1 object with required type check
* @param requiredType expected ASN.1 type constant
* @return parsed ASN.1 object
* @throws IOException if parsing fails or type doesn't match
*/
public Asn1Object readAsn1Object(int requiredType) throws IOException;
/**
* Reads next ASN.1 object without type checking
* @return parsed ASN.1 object
* @throws IOException if parsing fails
*/
public Asn1Object readAsn1Object() throws IOException;
}Wrapper for parsed ASN.1 objects with convenient access methods.
/**
* Wrapper for parsed ASN.1 objects
*/
public static class Asn1Object {
/**
* Gets the ASN.1 type tag
* @return type tag value
*/
public int getType();
/**
* Gets the content length
* @return content length in bytes
*/
public int getLength();
/**
* Gets the raw content bytes
* @return content byte array
*/
public byte[] getValue();
/**
* Checks if this is a constructed (composite) object
* @return true if constructed
*/
public boolean isConstructed();
/**
* Gets parser for nested content (if constructed)
* @return DerParser for nested content
* @throws IOException if not constructed or parsing fails
*/
public DerParser getParser() throws IOException;
/**
* Interprets content as integer value
* @return BigInteger value
* @throws IOException if content is not a valid integer
*/
public BigInteger getInteger() throws IOException;
/**
* Interprets content as string value
* @return string value
* @throws IOException if content is not a valid string
*/
public String getString() throws IOException;
/**
* Interprets content as object identifier (OID)
* @return OID string (e.g., "1.2.840.113549.1.1.1")
* @throws IOException if content is not a valid OID
*/
public String getOid() throws IOException;
}Constants for ASN.1 type identification.
/**
* ASN.1 type constants
*/
public static class Type {
public static final int INTEGER = 0x02;
public static final int OCTET_STRING = 0x04;
public static final int OBJECT_OID = 0x06;
public static final int SEQUENCE = 0x30;
// Additional type constants...
}Usage Examples:
import org.elasticsearch.common.ssl.DerParser;
import org.elasticsearch.common.ssl.DerParser.Asn1Object;
import org.elasticsearch.common.ssl.DerParser.Type;
import java.math.BigInteger;
// Parse DER encoded private key
byte[] derBytes = // ... DER encoded PKCS#1 private key bytes
DerParser parser = new DerParser(derBytes);
// Read outer SEQUENCE
Asn1Object sequence = parser.readAsn1Object(Type.SEQUENCE);
DerParser seqParser = sequence.getParser();
// Read version (INTEGER)
Asn1Object version = seqParser.readAsn1Object(Type.INTEGER);
BigInteger versionNum = version.getInteger();
// Read modulus (large INTEGER)
Asn1Object modulus = seqParser.readAsn1Object(Type.INTEGER);
BigInteger modulusValue = modulus.getInteger();
// Continue parsing other RSA private key components...The library automatically detects keystore types:
// File extensions mapped to keystore types:
// .jks -> JKS
// .p12, .pfx -> PKCS12
// .bks -> BKS
// others -> JKS (default)
String type = KeyStoreUtil.inferKeyStoreType("mystore.p12"); // "PKCS12"When building keystores, ensure proper certificate chain ordering:
// Correct order: end entity certificate first, then intermediates, then root
List<Certificate> chainInOrder = List.of(
endEntityCert, // certificate for private key
intermediateCert, // intermediate CA
rootCert // root CA (optional)
);
KeyStore keystore = KeyStoreUtil.buildKeyStore(chainInOrder, privateKey, password);The PEM utilities handle various PEM formats:
// Supports multiple certificate formats:
// - Single certificate per file
// - Multiple certificates in one file (certificate chains)
// - Mixed certificate and key files (certificates only extracted)
// Supports multiple private key formats:
// - PKCS#8 (-----BEGIN PRIVATE KEY-----)
// - PKCS#8 encrypted (-----BEGIN ENCRYPTED PRIVATE KEY-----)
// - Legacy RSA format (-----BEGIN RSA PRIVATE KEY-----)Install with Tessl CLI
npx tessl i tessl/maven-org-elasticsearch--elasticsearch-ssl-config