From 1414f8993a616a8dc2d5430e3a646e3075b87b57 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Thu, 03 Sep 2026 08:07:08 +0000
Subject: [PATCH] [#905] Warn when a replication handshake fails and document CA-signed certificates (#906)

---
 opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java |   45 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 45 insertions(+), 0 deletions(-)

diff --git a/opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java
index 7f0e696..9958d39 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java
@@ -13,6 +13,7 @@
  *
  * Copyright 2008 Sun Microsystems, Inc.
  * Portions Copyright 2013-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC.
  */
 package org.opends.server.crypto;
 
@@ -22,6 +23,7 @@
 import static org.forgerock.opendj.ldap.SearchScope.*;
 import static org.opends.server.TestCaseUtils.*;
 import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.crypto.CryptoManagerImpl.CERT_NICKNAME_CHECK_INTERVAL_NANOS;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.protocols.internal.Requests.*;
 import static org.opends.server.types.Attributes.*;
@@ -52,6 +54,7 @@
 import org.opends.admin.ads.ADSContext;
 import org.opends.admin.ads.util.BlindTrustManager;
 import org.opends.server.TestCaseUtils;
+import org.opends.server.backends.TrustStoreBackend;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
@@ -86,6 +89,48 @@
     TestCaseUtils.restartServer();
   }
 
+  /**
+   The nickname check must answer for the trust store the SSL context is built from: the
+   instance key pair the server generated for itself is held under its alias, a nickname
+   which was never imported is not.
+   */
+  @Test
+  public void testTrustStoreKnowsWhichCertNicknamesItHolds() throws Exception
+  {
+    // Generates the ads-certificate key pair if the trust store does not hold it yet.
+    assertNotNull(CryptoManagerImpl.getInstanceKeyCertificateFromLocalTruststore());
+    final TrustStoreBackend trustStore = (TrustStoreBackend) getServerContext()
+        .getBackendConfigManager().getLocalBackendById(ID_ADS_TRUST_STORE_BACKEND);
+
+    assertThat(trustStore.containsKeyWithAlias(ADS_CERTIFICATE_ALIAS)).isTrue();
+    assertThat(trustStore.containsKeyWithAlias("no-such-nickname")).isFalse();
+  }
+
+  /**
+   A server which cannot present its certificate reconnects every 500 ms and a new SSL
+   context is built for every attempt, so a certificate nickname missing from the trust
+   store is looked up, and reported, at most once per interval and per component -- but
+   again on the next interval, for as long as it is missing.
+   */
+  @Test
+  public void testMissingCertNicknameIsReportedOncePerInterval()
+  {
+    final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
+    final String checked = "Replication Server:" + UUID.randomUUID();
+    final long start = System.nanoTime();
+
+    assertThat(cm.isCertNicknameCheckDue(checked, start))
+        .as("the nickname has never been looked up").isTrue();
+    assertThat(cm.isCertNicknameCheckDue(checked, start + 1))
+        .as("the next connection attempt does not look it up again").isFalse();
+    assertThat(cm.isCertNicknameCheckDue(checked, start + CERT_NICKNAME_CHECK_INTERVAL_NANOS - 1))
+        .as("nor does the last attempt of the interval").isFalse();
+    assertThat(cm.isCertNicknameCheckDue(checked, start + CERT_NICKNAME_CHECK_INTERVAL_NANOS))
+        .as("a whole interval later it is looked up again").isTrue();
+    assertThat(cm.isCertNicknameCheckDue(checked, start + CERT_NICKNAME_CHECK_INTERVAL_NANOS + 1))
+        .as("and the next interval starts from that look up").isFalse();
+  }
+
   @Test
   public void testImportKeysUsesLatestKey()
       throws Exception {

--
Gitblit v1.10.0