This is an automated email from the ASF dual-hosted git repository.
pifta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 012ecd3316 HDDS-8178. CertificateClient and KeyStoresFactory support
multiple Sub-CA certificates in the trust chain (#4442)
012ecd3316 is described below
commit 012ecd331613f5b3b591dcd7573a32caf0cd75c1
Author: Sammi Chen <[email protected]>
AuthorDate: Wed Mar 29 06:24:23 2023 +0800
HDDS-8178. CertificateClient and KeyStoresFactory support multiple Sub-CA
certificates in the trust chain (#4442)
---
.../hdds/security/ssl/ReloadingX509KeyManager.java | 26 +++++++++++------
.../security/ssl/ReloadingX509TrustManager.java | 18 +++++++-----
.../x509/certificate/client/CertificateClient.java | 6 ++++
.../client/DefaultCertificateClient.java | 34 ++++++++++++++++++++++
.../ssl/TestReloadingX509TrustManager.java | 4 +--
.../client/CertificateClientTestImpl.java | 11 ++++++-
.../hadoop/ozone/TestSecureOzoneCluster.java | 3 +-
7 files changed, 82 insertions(+), 20 deletions(-)
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/ssl/ReloadingX509KeyManager.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/ssl/ReloadingX509KeyManager.java
index dc78dcb142..103bc462b8 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/ssl/ReloadingX509KeyManager.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/ssl/ReloadingX509KeyManager.java
@@ -33,8 +33,9 @@ import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
-import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
@@ -64,7 +65,7 @@ public class ReloadingX509KeyManager extends
X509ExtendedKeyManager {
* materials are changed.
*/
private PrivateKey currentPrivateKey;
- private String currentCertId;
+ private List<String> currentCertIdsList = new ArrayList<>();
/**
* Construct a <code>Reloading509KeystoreManager</code>.
@@ -150,11 +151,14 @@ public class ReloadingX509KeyManager extends
X509ExtendedKeyManager {
private X509ExtendedKeyManager loadKeyManager(CertificateClient caClient)
throws GeneralSecurityException, IOException {
PrivateKey privateKey = caClient.getPrivateKey();
- X509Certificate cert = caClient.getCertificate();
- String certId = cert.getSerialNumber().toString();
- // Security materials keep the same
- if (currentCertId != null && currentPrivateKey != null &&
- currentCertId.equals(certId) && currentPrivateKey.equals(privateKey)) {
+ List<X509Certificate> newCertList = caClient.getTrustChain();
+ if (currentPrivateKey != null && currentPrivateKey.equals(privateKey) &&
+ currentCertIdsList.size() > 0 &&
+ newCertList.size() == currentCertIdsList.size() &&
+ !newCertList.stream().filter(
+ c -> !currentCertIdsList.contains(c.getSerialNumber().toString()))
+ .findAny().isPresent()) {
+ // Security materials(key and certificates) keep the same.
return null;
}
@@ -163,7 +167,8 @@ public class ReloadingX509KeyManager extends
X509ExtendedKeyManager {
keystore.load(null, null);
keystore.setKeyEntry(caClient.getComponentName() + "_key",
- privateKey, EMPTY_PASSWORD, new Certificate[]{cert});
+ privateKey, EMPTY_PASSWORD,
+ newCertList.toArray(new X509Certificate[0]));
KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
@@ -176,7 +181,10 @@ public class ReloadingX509KeyManager extends
X509ExtendedKeyManager {
}
currentPrivateKey = privateKey;
- currentCertId = cert.getSerialNumber().toString();
+ currentCertIdsList.clear();
+ for (X509Certificate cert: newCertList) {
+ currentCertIdsList.add(cert.getSerialNumber().toString());
+ }
return keyManager;
}
}
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/ssl/ReloadingX509TrustManager.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/ssl/ReloadingX509TrustManager.java
index 5252c278dd..f2ca6a13c6 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/ssl/ReloadingX509TrustManager.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/ssl/ReloadingX509TrustManager.java
@@ -51,9 +51,9 @@ public final class ReloadingX509TrustManager implements
X509TrustManager {
private final String type;
private final AtomicReference<X509TrustManager> trustManagerRef;
/**
- * Current CA cert in trustManager, to detect if certificate is changed.
+ * Current Root CA cert in trustManager, to detect if certificate is changed.
*/
- private String currentCACertId = null;
+ private String currentRootCACertId = null;
/**
* Creates a reloadable trustmanager. The trustmanager reloads itself
@@ -124,17 +124,21 @@ public final class ReloadingX509TrustManager implements
X509TrustManager {
X509TrustManager loadTrustManager(CertificateClient caClient)
throws GeneralSecurityException, IOException {
- X509Certificate cert = caClient.getCACertificate();
- String certId = cert.getSerialNumber().toString();
+ // SCM certificate client sets root CA as CA cert instead of root CA cert
+ X509Certificate rootCACert = caClient.getRootCACertificate() != null ?
+ caClient.getRootCACertificate() : caClient.getCACertificate();
+
+ String rootCACertId = rootCACert.getSerialNumber().toString();
// Certificate keeps the same.
- if (currentCACertId != null && currentCACertId.equals(certId)) {
+ if (currentRootCACertId != null &&
+ currentRootCACertId.equals(rootCACertId)) {
return null;
}
X509TrustManager trustManager = null;
KeyStore ks = KeyStore.getInstance(type);
ks.load(null, null);
- ks.setCertificateEntry(certId, cert);
+ ks.setCertificateEntry(rootCACertId, rootCACert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
@@ -146,7 +150,7 @@ public final class ReloadingX509TrustManager implements
X509TrustManager {
break;
}
}
- currentCACertId = certId;
+ currentRootCACertId = rootCACertId;
return trustManager;
}
}
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
index 8c8a2df056..bd70001c9d 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
@@ -106,6 +106,12 @@ public interface CertificateClient extends Closeable {
*/
CertPath getCACertPath();
+ /**
+ * Return all certificates in this component's trust chain,
+ * the last one is the root CA certificate.
+ */
+ List<X509Certificate> getTrustChain();
+
/**
* Return the latest Root CA certificate known to the client.
* @return latest Root CA certificate known to the client.
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
index 2dfbce9dac..97c07f4e10 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
@@ -42,6 +42,7 @@ import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
+import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
@@ -331,6 +332,39 @@ public abstract class DefaultCertificateClient implements
CertificateClient {
return firstCertificateFrom(caCertPath);
}
+ /**
+ * Return all certificates in this component's trust chain,
+ * the last one is the root CA certificate.
+ */
+ @Override
+ public synchronized List<X509Certificate> getTrustChain() {
+ CertPath path = getCertPath();
+ if (path == null || path.getCertificates() == null) {
+ return null;
+ }
+
+ List<X509Certificate> chain = new ArrayList<>();
+ // certificate bundle case
+ if (path.getCertificates().size() > 1) {
+ for (int i = 0; i < path.getCertificates().size(); i++) {
+ chain.add((X509Certificate) path.getCertificates().get(i));
+ }
+ } else {
+ // case before certificate bundle is supported
+ chain.add(getCertificate());
+ X509Certificate cert = getCACertificate();
+ if (cert != null) {
+ chain.add(getCACertificate());
+ }
+ cert = getRootCACertificate();
+ if (cert != null) {
+ chain.add(cert);
+ }
+ }
+
+ return chain;
+ }
+
@Override
public synchronized CertPath getCACertPath() {
if (caCertId != null) {
diff --git
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/ssl/TestReloadingX509TrustManager.java
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/ssl/TestReloadingX509TrustManager.java
index 42f6de3281..d2af37db1f 100644
---
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/ssl/TestReloadingX509TrustManager.java
+++
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/ssl/TestReloadingX509TrustManager.java
@@ -50,13 +50,13 @@ public class TestReloadingX509TrustManager {
public void testReload() throws Exception {
TrustManager tm =
caClient.getServerKeyStoresFactory().getTrustManagers()[0];
- X509Certificate cert1 = caClient.getCACertificate();
+ X509Certificate cert1 = caClient.getRootCACertificate();
assertEquals(cert1,
((ReloadingX509TrustManager)tm).getAcceptedIssuers()[0]);
caClient.renewRootCA();
caClient.renewKey();
- X509Certificate cert2 = caClient.getCACertificate();
+ X509Certificate cert2 = caClient.getRootCACertificate();
assertNotEquals(cert1, cert2);
assertEquals(cert2,
diff --git
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClientTestImpl.java
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClientTestImpl.java
index 18136d600e..27aa596bd6 100644
---
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClientTestImpl.java
+++
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClientTestImpl.java
@@ -31,6 +31,7 @@ import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
+import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
@@ -207,6 +208,14 @@ public class CertificateClientTestImpl implements
CertificateClient {
return null;
}
+ @Override
+ public List<X509Certificate> getTrustChain() {
+ List<X509Certificate> list = new ArrayList<>();
+ list.add(x509Certificate);
+ list.add(rootCert);
+ return list;
+ }
+
@Override
public X509Certificate getCACertificate() {
return rootCert;
@@ -262,7 +271,7 @@ public class CertificateClientTestImpl implements
CertificateClient {
@Override
public X509Certificate getRootCACertificate() {
- return x509Certificate;
+ return rootCert;
}
@Override
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
index 7018e3fb88..1888d40931 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
@@ -359,6 +359,7 @@ public final class TestSecureOzoneCluster {
ScmInfo scmInfo = scm.getClientProtocolServer().getScmInfo();
assertEquals(clusterId, scmInfo.getClusterId());
assertEquals(scmId, scmInfo.getScmId());
+ assertEquals(2, scm.getScmCertificateClient().getTrustChain().size());
}
@Test
@@ -867,6 +868,7 @@ public final class TestSecureOzoneCluster {
assertNotNull(om.getCertificateClient().getPublicKey());
assertNotNull(om.getCertificateClient().getPrivateKey());
assertNotNull(om.getCertificateClient().getCertificate());
+ assertEquals(3, om.getCertificateClient().getTrustChain().size());
assertTrue(omLogs.getOutput().contains("Init response: GETCERT"));
assertTrue(omLogs.getOutput().contains("Successfully stored " +
"SCM signed certificate"));
@@ -903,7 +905,6 @@ public final class TestSecureOzoneCluster {
SecurityConfig securityConfig = new SecurityConfig(conf);
-
// save first cert
final int certificateLifetime = 20; // seconds
KeyCodec keyCodec =
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]