You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kafka.apache.org by sh...@apache.org on 2026/10/07 07:27:33 UTC
(kafka) branch 4.2 updated: KAFKA-20997: Improve KeyFactory loading (#23361) (#23672)
This is an automated email from the ASF dual-hosted git repository.
showuon pushed a commit to branch 4.2
in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/4.2 by this push:
new 10ae2e219f3 KAFKA-20997: Improve KeyFactory loading (#23361) (#23672)
10ae2e219f3 is described below
commit 10ae2e219f3f69253fc76df189d2f7dd0fafcdc0
Author: Maros Orsak <ma...@gmail.com>
AuthorDate: Wed Oct 7 09:27:24 2026 +0200
KAFKA-20997: Improve KeyFactory loading (#23361) (#23672)
Gracefully handle missing KeyFactory algorithms and only fail when
trying to use a missing KeyFactory (cherry picked from commit
06e2901cb871e8bf5692294cdf7b1caab9b55c77)
Reviewers: Luke Chen <sh...@gmail.com>, Andy Huang
(github:andyhuangdev)
Co-authored-by: Mickael Maison <mi...@users.noreply.github.com>
---
.../security/ssl/DefaultSslEngineFactory.java | 32 ++++++++++++----------
.../security/ssl/DefaultSslEngineFactoryTest.java | 26 ++++++++++++++++++
.../kafka/common/security/ssl/SslFactoryTest.java | 26 ++++++++++++++++++
3 files changed, 70 insertions(+), 14 deletions(-)
diff --git a/clients/src/main/java/org/apache/kafka/common/security/ssl/DefaultSslEngineFactory.java b/clients/src/main/java/org/apache/kafka/common/security/ssl/DefaultSslEngineFactory.java
index a5068dc83ab..82407e10612 100644
--- a/clients/src/main/java/org/apache/kafka/common/security/ssl/DefaultSslEngineFactory.java
+++ b/clients/src/main/java/org/apache/kafka/common/security/ssl/DefaultSslEngineFactory.java
@@ -48,7 +48,6 @@ import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.Date;
@@ -385,7 +384,7 @@ public class DefaultSslEngineFactory implements SslEngineFactory {
try {
return Files.getLastModifiedTime(Paths.get(path)).toMillis();
} catch (IOException e) {
- log.error("Modification time of key store could not be obtained: " + path, e);
+ log.error("Modification time of key store could not be obtained: {}", path, e);
return null;
}
}
@@ -424,11 +423,7 @@ public class DefaultSslEngineFactory implements SslEngineFactory {
static class PemStore implements SecurityStore {
private static final PemParser CERTIFICATE_PARSER = new PemParser("CERTIFICATE");
private static final PemParser PRIVATE_KEY_PARSER = new PemParser("PRIVATE KEY");
- private static final List<KeyFactory> KEY_FACTORIES = Arrays.asList(
- keyFactory("RSA"),
- keyFactory("DSA"),
- keyFactory("EC")
- );
+ private static final List<KeyFactory> KEY_FACTORIES = keyFactories(List.of("RSA", "DSA", "EC"));
private final char[] keyPassword;
private final KeyStore keyStore;
@@ -458,6 +453,18 @@ public class DefaultSslEngineFactory implements SslEngineFactory {
return false;
}
+ static List<KeyFactory> keyFactories(List<String> algorithms) {
+ List<KeyFactory> keyFactories = new ArrayList<>();
+ for (String algorithm : algorithms) {
+ try {
+ keyFactories.add(KeyFactory.getInstance(algorithm));
+ } catch (Exception e) {
+ log.info("Could not create key factory for algorithm {}", algorithm, e);
+ }
+ }
+ return keyFactories;
+ }
+
private KeyStore createKeyStoreFromPem(String privateKeyPem, String certChainPem, char[] keyPassword) {
try {
KeyStore ks = KeyStore.getInstance("PKCS12");
@@ -521,6 +528,10 @@ public class DefaultSslEngineFactory implements SslEngineFactory {
keySpec = keyInfo.getKeySpec(cipher);
}
+ if (KEY_FACTORIES.isEmpty()) {
+ throw new InvalidConfigurationException("No key factories available to load private key");
+ }
+
InvalidKeySpecException firstException = null;
for (KeyFactory factory : KEY_FACTORIES) {
try {
@@ -533,13 +544,6 @@ public class DefaultSslEngineFactory implements SslEngineFactory {
throw new InvalidConfigurationException("Private key could not be loaded", firstException);
}
- private static KeyFactory keyFactory(String algorithm) {
- try {
- return KeyFactory.getInstance(algorithm);
- } catch (Exception e) {
- throw new InvalidConfigurationException("Could not create key factory for algorithm " + algorithm, e);
- }
- }
}
/**
diff --git a/clients/src/test/java/org/apache/kafka/common/security/ssl/DefaultSslEngineFactoryTest.java b/clients/src/test/java/org/apache/kafka/common/security/ssl/DefaultSslEngineFactoryTest.java
index ed76495fa03..52be0c4357e 100644
--- a/clients/src/test/java/org/apache/kafka/common/security/ssl/DefaultSslEngineFactoryTest.java
+++ b/clients/src/test/java/org/apache/kafka/common/security/ssl/DefaultSslEngineFactoryTest.java
@@ -18,21 +18,26 @@ package org.apache.kafka.common.security.ssl;
import org.apache.kafka.common.config.SslConfigs;
import org.apache.kafka.common.config.types.Password;
+import org.apache.kafka.common.errors.InvalidConfigurationException;
import org.apache.kafka.test.TestUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.security.KeyFactory;
import java.security.KeyStore;
import java.util.Arrays;
+import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
public class DefaultSslEngineFactoryTest {
@@ -321,6 +326,27 @@ public class DefaultSslEngineFactoryTest {
assertNotNull(keyStore.getKey("kafka", KEY_PASSWORD.value().toCharArray()), "Private key not found");
}
+ @Test
+ public void testUnavailableKeyFactoryAlgorithmDoesNotThrow() {
+ List<KeyFactory> factories = DefaultSslEngineFactory.PemStore.keyFactories(List.of("RSA", "DSA", "EC", "BOGUS_FACTORY"));
+ assertFalse(factories.stream().anyMatch(f -> f.getAlgorithm().equals("BOGUS_FACTORY")));
+ }
+
+ @Test
+ public void testPemKeyStoreWithUnparseablePrivateKey() {
+ String bogusKey = "-----BEGIN PRIVATE KEY-----\n"
+ + Base64.getEncoder().encodeToString("not a valid key".getBytes()) + "\n"
+ + "-----END PRIVATE KEY-----";
+
+ assertThrows(InvalidConfigurationException.class, () ->
+ new DefaultSslEngineFactory.PemStore(
+ new Password(CERTCHAIN),
+ new Password(bogusKey),
+ null
+ )
+ );
+ }
+
private String pemFilePath(String pem) throws Exception {
return TestUtils.tempFile(pem).getAbsolutePath();
}
diff --git a/clients/src/test/java/org/apache/kafka/common/security/ssl/SslFactoryTest.java b/clients/src/test/java/org/apache/kafka/common/security/ssl/SslFactoryTest.java
index 1c82dc62ff0..9e888c6ed6c 100644
--- a/clients/src/test/java/org/apache/kafka/common/security/ssl/SslFactoryTest.java
+++ b/clients/src/test/java/org/apache/kafka/common/security/ssl/SslFactoryTest.java
@@ -44,6 +44,7 @@ import java.security.KeyStore;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Arrays;
+import java.util.Base64;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -361,6 +362,31 @@ public abstract class SslFactoryTest {
assertNotSame(sslEngineFactory, sslFactory.sslEngineFactory(), "SslEngineFactory not recreated");
}
+ @Test
+ public void testPemReconfigurationWithInvalidKeyPreservesExistingFactory() throws Exception {
+ Properties props = new Properties();
+ props.putAll(sslConfigsBuilder(ConnectionMode.SERVER)
+ .createNewTrustStore(null)
+ .usePem(true)
+ .build());
+ TestSecurityConfig sslConfig = new TestSecurityConfig(props);
+
+ SslFactory sslFactory = new SslFactory(ConnectionMode.SERVER);
+ sslFactory.configure(sslConfig.values());
+ SslEngineFactory sslEngineFactory = sslFactory.sslEngineFactory();
+ assertNotNull(sslEngineFactory, "SslEngineFactory not created");
+
+ // Attempt reconfiguration with a PEM key that no KeyFactory can parse
+ String bogusKey = "-----BEGIN PRIVATE KEY-----\n"
+ + Base64.getEncoder().encodeToString("not a valid key".getBytes()) + "\n"
+ + "-----END PRIVATE KEY-----";
+ props.put(SslConfigs.SSL_KEYSTORE_KEY_CONFIG, new Password(bogusKey));
+ TestSecurityConfig badConfig = new TestSecurityConfig(props);
+
+ assertThrows(ConfigException.class, () -> sslFactory.validateReconfiguration(badConfig.values()));
+ assertSame(sslEngineFactory, sslFactory.sslEngineFactory());
+ }
+
@Test
public void testKeyStoreTrustStoreValidation() throws Exception {
File trustStoreFile = TestUtils.tempFile("truststore", ".jks");