You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2020/12/03 14:49:08 UTC

[mina-sshd] branch master updated (ac45b61 -> af6c8ed)

This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git.


    from ac45b61  Fixed SFTP directory scanners behavior documentation example
     new 5fc2eff  [SSHD-1107] Allow configuration of minimum DH group exchange key size via property or programmatically
     new af6c8ed  [SSHD-1108] Increased minimum default DH group exchange key size to 2048 (but support 1024)

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGES.md                                         |   4 +-
 README.md                                          |   7 +-
 docs/security-providers.md                         |  23 +++++
 .../sshd/common/util/security/SecurityUtils.java   |  85 +++++++++++-----
 .../SecurityProviderRegistrarTestSupport.java      |   3 -
 .../SecurityUtilsDHGEXGroupKeySizeTest.java        | 111 +++++++++++++++++++++
 .../common/util/security/SecurityUtilsTest.java    |  22 +---
 .../security/SecurityUtilsTestSupport.java}        |  35 +++----
 .../eddsa/EdDSASecurityProviderRegistrarTest.java  |   3 +
 .../org/apache/sshd/client/kex/DHGEXClient.java    |   2 +-
 .../org/apache/sshd/server/kex/DHGEXServer.java    |   8 +-
 .../org/apache/sshd/server/kex/ModuliTest.java     |   4 +-
 12 files changed, 234 insertions(+), 73 deletions(-)
 create mode 100644 sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsDHGEXGroupKeySizeTest.java
 copy sshd-common/src/test/java/org/apache/sshd/common/{file/nonefs/NoneFileSystemFactoryTest.java => util/security/SecurityUtilsTestSupport.java} (51%)


[mina-sshd] 02/02: [SSHD-1108] Increased minimum default DH group exchange key size to 2048 (but support 1024)

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit af6c8eddd897e18e8834c6439c48513cec150c8c
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Tue Dec 1 19:49:18 2020 +0200

    [SSHD-1108] Increased minimum default DH group exchange key size to 2048 (but support 1024)
---
 CHANGES.md                                                         | 3 ++-
 README.md                                                          | 7 ++++++-
 docs/security-providers.md                                         | 2 +-
 .../java/org/apache/sshd/common/util/security/SecurityUtils.java   | 6 +++---
 sshd-core/src/test/java/org/apache/sshd/server/kex/ModuliTest.java | 4 ++--
 5 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 333f354..0f0d63c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -62,4 +62,5 @@ or `-key-file` command line option.
 * [SSHD-1100](https://issues.apache.org/jira/browse/SSHD-1100) Updated used moduli for DH group KEX
 * [SSHD-1102](https://issues.apache.org/jira/browse/SSHD-1102) Provide filter support for SftpDirectoryStream
 * [SSHD-1104](https://issues.apache.org/jira/browse/SSHD-1104) Take into account possible key type aliases when using public key authentication
-* [SSHD-1107](https://issues.apache.org/jira/browse/SSHD-1107) Allow configuration of minimum DH group exchange key size via property or programmatically
\ No newline at end of file
+* [SSHD-1107](https://issues.apache.org/jira/browse/SSHD-1107) Allow configuration of minimum DH group exchange key size via property or programmatically
+* [SSHD-1108](https://issues.apache.org/jira/browse/SSHD-1108) Increased minimum default DH group exchange key size to 2048 (but support 1024)
\ No newline at end of file
diff --git a/README.md b/README.md
index 14ba78a..ab548c6 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,12 @@ the unsafe settings must do so **explicitly**. The following settings have been
 * [OpenSSH release notes](https://www.openssh.com/releasenotes.html) - usually a good indicator of de-facto practices
 * SHA-1 based key exchanges and signatures
 * MD5-based and truncated HMAC algorithms
-
+* [RFC 8270 - Increase the Secure Shell Minimum Recommended Diffie-Hellman Modulus Size to 2048 Bits](https://tools.ietf.org/html/rfc8270)  
+    **Note:** it still possible to use 1024 by initializing the value *programmatically* or via system property - 
+    see [Security providers setup](./docs/security-providers.md#diff-hellman-group-exchange-configuration).
+    The code still contains moduli for 1024 and will use them if user **explicitly** lowers the default minimum
+    to it.
+    
 **Caveat:**: According to [RFC 8332 - section 3.31](https://tools.ietf.org/html/rfc8332#section-3.3)
 >>
 >> Implementation experience has shown that there are servers that apply authentication penalties to clients
diff --git a/docs/security-providers.md b/docs/security-providers.md
index e9ed66d..d285a0a 100644
--- a/docs/security-providers.md
+++ b/docs/security-providers.md
@@ -101,7 +101,7 @@ In any case, the values are auto-detected by the code but the user can intervene
 **Note(s)**
 
 * The value should be a multiple of 1024 (not enforced)
-* The value should be between 1024 and 8192 (not enforced)
+* The value should be between 2048 and 8192 (not enforced - allows users to make an **explicit** decision to use shorter keys - especially the minimum).
 * The minimum must be less or equal to the maximum (enforced - if reversed then group exchange is **disabled**)
 * If a **negative** value is set in either one then group exchange is **disabled**
 * Setting a value of zero indicates a **lazy** auto-detection of the supported range the next time these values are needed.
\ No newline at end of file
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
index c300213..d26a1d1 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
@@ -119,10 +119,10 @@ public final class SecurityUtils {
      * The min. key size value used for testing whether Diffie-Hellman Group Exchange is supported or not. According to
      * <A HREF="https://tools.ietf.org/html/rfc4419">RFC 4419</A> section 3: &quot;Servers and clients SHOULD support
      * groups with a modulus length of k bits, where 1024 <= k <= 8192&quot;. </code>
+     *
+     * <B>Note: this has been amended by <A HREF="https://tools.ietf.org/html/rfc8270">RFC 8270</A>
      */
-    public static final int MIN_DHGEX_KEY_SIZE = 1024;
-    // Keys of size > 1024 are not supported by default with JCE
-    public static final int DEFAULT_DHGEX_KEY_SIZE = MIN_DHGEX_KEY_SIZE;
+    public static final int MIN_DHGEX_KEY_SIZE = 2048;
     public static final int PREFERRED_DHGEX_KEY_SIZE = 4096;
     public static final int MAX_DHGEX_KEY_SIZE = 8192;
 
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/kex/ModuliTest.java b/sshd-core/src/test/java/org/apache/sshd/server/kex/ModuliTest.java
index ced70b0..128a908 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/kex/ModuliTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/kex/ModuliTest.java
@@ -84,8 +84,8 @@ public class ModuliTest extends JUnitTestSupport {
         Collection<Integer> actualSizes = new TreeSet<>(Comparator.naturalOrder());
         for (DhGroup g : groups) {
             int size = g.getSize();
-            assertTrue("Size below min. required " + SecurityUtils.MIN_DHGEX_KEY_SIZE,
-                    size >= SecurityUtils.MIN_DHGEX_KEY_SIZE);
+            // SSHD-1108 - raised default minimum to 2048...
+            assertTrue("Size below min. required " + 1024 + ": " + size, size >= 1024);
             assertTrue("Size above max. allowed " + SecurityUtils.MAX_DHGEX_KEY_SIZE, size <= SecurityUtils.MAX_DHGEX_KEY_SIZE);
             actualSizes.add(size);
         }


[mina-sshd] 01/02: [SSHD-1107] Allow configuration of minimum DH group exchange key size via property or programmatically

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 5fc2eff001684626601b6855a609076ab22cd0a7
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Tue Dec 1 19:10:34 2020 +0200

    [SSHD-1107] Allow configuration of minimum DH group exchange key size via property or programmatically
---
 CHANGES.md                                         |   3 +-
 docs/security-providers.md                         |  23 +++++
 .../sshd/common/util/security/SecurityUtils.java   |  79 +++++++++++----
 .../SecurityProviderRegistrarTestSupport.java      |   3 -
 .../SecurityUtilsDHGEXGroupKeySizeTest.java        | 111 +++++++++++++++++++++
 .../common/util/security/SecurityUtilsTest.java    |  22 +---
 .../util/security/SecurityUtilsTestSupport.java    |  48 +++++++++
 .../eddsa/EdDSASecurityProviderRegistrarTest.java  |   3 +
 .../org/apache/sshd/client/kex/DHGEXClient.java    |   2 +-
 .../org/apache/sshd/server/kex/DHGEXServer.java    |   8 +-
 10 files changed, 254 insertions(+), 48 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 4375d17..333f354 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -61,4 +61,5 @@ or `-key-file` command line option.
 * [SSHD-1070](https://issues.apache.org/jira/browse/SSHD-1070) OutOfMemoryError when use async port forwarding
 * [SSHD-1100](https://issues.apache.org/jira/browse/SSHD-1100) Updated used moduli for DH group KEX
 * [SSHD-1102](https://issues.apache.org/jira/browse/SSHD-1102) Provide filter support for SftpDirectoryStream
-* [SSHD-1104](https://issues.apache.org/jira/browse/SSHD-1104) Take into account possible key type aliases when using public key authentication
\ No newline at end of file
+* [SSHD-1104](https://issues.apache.org/jira/browse/SSHD-1104) Take into account possible key type aliases when using public key authentication
+* [SSHD-1107](https://issues.apache.org/jira/browse/SSHD-1107) Allow configuration of minimum DH group exchange key size via property or programmatically
\ No newline at end of file
diff --git a/docs/security-providers.md b/docs/security-providers.md
index 71f9d52..e9ed66d 100644
--- a/docs/security-providers.md
+++ b/docs/security-providers.md
@@ -82,3 +82,26 @@ with the `Cipher` class as its argument.
 * Specifically for **ciphers** the argument to the support query contains a **transformation** (e.g., `AES/CBC/NoPadding`)
 so one should take that into account when parsing the input argument to decide which cipher is referenced - see
 `SecurityProviderRegistrar.getEffectiveSecurityEntityName(Class<?>, String)` helper method
+
+## Diff-Hellman group exchange configuration
+
+The [RFC 4419 - Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol](https://tools.ietf.org/html/rfc4419)
+specifies in section 3:
+
+>> Servers and clients SHOULD support groups with a modulus length of k bits, where 1024 <= k <= 8192.
+>> The recommended values for min and max are 1024 and 8192, respectively.
+
+This was subsequently amended in [RFC 8270 - Increase the Secure Shell Minimum Recommended Diffie-Hellman Modulus Size to 2048 Bits](https://tools.ietf.org/html/rfc8270).
+
+In any case, the values are auto-detected by the code but the user can intervene in 2 ways:
+
+1. Programmatically - by invoking `SecurityUtils#setMin/MaxDHGroupExchangeKeySize` respectively
+2. Via system property - by setting `org.apache.sshd.min/maxDHGexKeySize` system property respectively
+
+**Note(s)**
+
+* The value should be a multiple of 1024 (not enforced)
+* The value should be between 1024 and 8192 (not enforced)
+* The minimum must be less or equal to the maximum (enforced - if reversed then group exchange is **disabled**)
+* If a **negative** value is set in either one then group exchange is **disabled**
+* Setting a value of zero indicates a **lazy** auto-detection of the supported range the next time these values are needed.
\ No newline at end of file
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
index a538f8e..c300213 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
@@ -102,6 +102,13 @@ public final class SecurityUtils {
     public static final String CURVE_ED25519_SHA512 = "NONEwithEdDSA";
 
     /**
+     * System property used to configure the value for the minimum supported Diffie-Hellman Group Exchange key size. If
+     * not set, then an internal auto-discovery mechanism is employed. If set to negative value then Diffie-Hellman
+     * Group Exchange is disabled. If set to a negative value then Diffie-Hellman Group Exchange is disabled
+     */
+    public static final String MIN_DHGEX_KEY_SIZE_PROP = "org.apache.sshd.minDHGexKeySize";
+
+    /**
      * System property used to configure the value for the maximum supported Diffie-Hellman Group Exchange key size. If
      * not set, then an internal auto-discovery mechanism is employed. If set to negative value then Diffie-Hellman
      * Group Exchange is disabled. If set to a negative value then Diffie-Hellman Group Exchange is disabled
@@ -155,6 +162,7 @@ public final class SecurityUtils {
 
     public static final String PROP_DEFAULT_SECURITY_PROVIDER = "org.apache.sshd.security.defaultProvider";
 
+    private static final AtomicInteger MIN_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0);
     private static final AtomicInteger MAX_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0);
 
     /*
@@ -244,37 +252,78 @@ public final class SecurityUtils {
 
     /**
      * @return {@code true} if Diffie-Hellman Group Exchange is supported
+     * @see    #getMinDHGroupExchangeKeySize()
      * @see    #getMaxDHGroupExchangeKeySize()
      */
     public static boolean isDHGroupExchangeSupported() {
-        return getMaxDHGroupExchangeKeySize() > 0;
+        int maxSize = getMaxDHGroupExchangeKeySize();
+        int minSize = getMinDHGroupExchangeKeySize();
+        return (minSize > 0) && (maxSize > 0) && (minSize <= maxSize);
     }
 
     /**
      * @param  keySize The expected key size
      * @return         {@code true} if Oakely Diffie-Hellman Group Exchange is supported for the specified key size
+     * @see            #isDHGroupExchangeSupported()
      * @see            #getMaxDHGroupExchangeKeySize()
      */
     public static boolean isDHOakelyGroupSupported(int keySize) {
-        return getMaxDHGroupExchangeKeySize() >= keySize;
+        return isDHGroupExchangeSupported()
+                && (getMaxDHGroupExchangeKeySize() >= keySize);
+    }
+
+    /**
+     * @return The minimum supported Diffie-Hellman Group Exchange key size, or non-positive if not supported
+     */
+    public static int getMinDHGroupExchangeKeySize() {
+        return resolveDHGEXKeySizeValue(MIN_DHG_KEY_SIZE_HOLDER, MIN_DHGEX_KEY_SIZE_PROP, MIN_DHGEX_KEY_SIZE);
+    }
+
+    /**
+     * Set programmatically the reported value for {@link #getMinDHGroupExchangeKeySize()}
+     *
+     * @param keySize The reported key size - if zero, then it will be auto-detected, if negative then DH group exchange
+     *                will be disabled
+     */
+    public static void setMinDHGroupExchangeKeySize(int keySize) {
+        synchronized (MIN_DHG_KEY_SIZE_HOLDER) {
+            MIN_DHG_KEY_SIZE_HOLDER.set(keySize);
+        }
     }
 
     /**
      * @return The maximum supported Diffie-Hellman Group Exchange key size, or non-positive if not supported
      */
     public static int getMaxDHGroupExchangeKeySize() {
-        int maxSupportedKeySize;
+        return resolveDHGEXKeySizeValue(MAX_DHG_KEY_SIZE_HOLDER, MAX_DHGEX_KEY_SIZE_PROP, MAX_DHGEX_KEY_SIZE);
+    }
+
+    /**
+     * Set programmatically the reported value for {@link #getMaxDHGroupExchangeKeySize()}
+     *
+     * @param keySize The reported key size - if zero, then it will be auto-detected, if negative then DH group exchange
+     *                will be disabled
+     */
+    public static void setMaxDHGroupExchangeKeySize(int keySize) {
         synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
-            maxSupportedKeySize = MAX_DHG_KEY_SIZE_HOLDER.get();
+            MAX_DHG_KEY_SIZE_HOLDER.set(keySize);
+        }
+    }
+
+    private static int resolveDHGEXKeySizeValue(
+            AtomicInteger holder, String propName, int maxKeySize) {
+        int maxSupportedKeySize;
+        synchronized (holder) {
+            maxSupportedKeySize = holder.get();
             if (maxSupportedKeySize != 0) { // 1st time we are called ?
                 return maxSupportedKeySize;
             }
 
-            String propValue = System.getProperty(MAX_DHGEX_KEY_SIZE_PROP);
+            String propValue = System.getProperty(propName);
             if (GenericUtils.isEmpty(propValue)) {
                 maxSupportedKeySize = -1;
                 // Go down from max. to min. to ensure we stop at 1st maximum value success
-                for (int testKeySize = MAX_DHGEX_KEY_SIZE; testKeySize >= MIN_DHGEX_KEY_SIZE; testKeySize -= 1024) {
+                for (int testKeySize = maxKeySize; testKeySize >= MIN_DHGEX_KEY_SIZE; testKeySize -= 1024) {
                     if (isDHGroupExchangeSupported(testKeySize)) {
                         maxSupportedKeySize = testKeySize;
                         break;
@@ -282,31 +331,19 @@ public final class SecurityUtils {
                 }
             } else {
                 Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
-                logger.info("Override max. DH group exchange key size: " + propValue);
+                logger.info("Override DH group exchange key size via {}: {}", propName, propValue);
                 maxSupportedKeySize = Integer.parseInt(propValue);
                 // negative is OK - means user wants to disable DH group exchange
                 ValidateUtils.checkTrue(maxSupportedKeySize != 0,
-                        "Configured " + MAX_DHGEX_KEY_SIZE_PROP + " value must be non-zero: %d", maxSupportedKeySize);
+                        "Configured " + propName + " value must be non-zero: %d", maxSupportedKeySize);
             }
 
-            MAX_DHG_KEY_SIZE_HOLDER.set(maxSupportedKeySize);
+            holder.set(maxSupportedKeySize);
         }
 
         return maxSupportedKeySize;
     }
 
-    /**
-     * Set programmatically the reported value for {@link #getMaxDHGroupExchangeKeySize()}
-     *
-     * @param keySize The reported key size - if zero, then it will be auto-detected, if negative then DH group exchange
-     *                will be disabled
-     */
-    public static void setMaxDHGroupExchangeKeySize(int keySize) {
-        synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
-            MAX_DHG_KEY_SIZE_HOLDER.set(keySize);
-        }
-    }
-
     public static boolean isDHGroupExchangeSupported(int maxKeySize) {
         ValidateUtils.checkTrue(maxKeySize > Byte.SIZE, "Invalid max. key size: %d", maxKeySize);
 
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
index 4eeaafe..6f20183 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
@@ -23,13 +23,10 @@ import java.util.Arrays;
 import java.util.Collection;
 
 import org.apache.sshd.util.test.JUnitTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.experimental.categories.Category;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-@Category({ NoIoTestCase.class })
 public abstract class SecurityProviderRegistrarTestSupport extends JUnitTestSupport {
     protected SecurityProviderRegistrarTestSupport() {
         super();
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsDHGEXGroupKeySizeTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsDHGEXGroupKeySizeTest.java
new file mode 100644
index 0000000..c2ff548
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsDHGEXGroupKeySizeTest.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class SecurityUtilsDHGEXGroupKeySizeTest extends SecurityUtilsTestSupport {
+    private final int expected;
+
+    public SecurityUtilsDHGEXGroupKeySizeTest(int expected) {
+        this.expected = expected;
+    }
+
+    @Before
+    @After
+    public void resetDHGEXGroupKeySizes() {
+        System.clearProperty(SecurityUtils.MIN_DHGEX_KEY_SIZE_PROP);
+        SecurityUtils.setMinDHGroupExchangeKeySize(0); // force detection
+        System.clearProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP);
+        SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
+    }
+
+    @Parameters(name = "keySize={0}")
+    public static List<Object[]> parameters() {
+        System.clearProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP);
+        SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
+        try {
+            List<Object[]> values = new ArrayList<>();
+            int maxSupported = SecurityUtils.getMaxDHGroupExchangeKeySize();
+            for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE;
+                 expected <= maxSupported;
+                 expected += 1024) {
+                values.add(new Object[] { expected });
+            }
+            return values;
+        } finally {
+            SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
+        }
+    }
+
+    @Test
+    public void testSetMaxDHGroupExchangeKeySizeByProperty() {
+        System.setProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP, Integer.toString(expected));
+        assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
+        assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
+    }
+
+    @Test
+    public void testSetMaxDHGroupExchangeKeySizeProgrammatically() {
+        SecurityUtils.setMaxDHGroupExchangeKeySize(expected);
+        assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
+        assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
+    }
+
+    @Test
+    public void testSetMinDHGroupExchangeKeySizeByProperty() {
+        System.setProperty(SecurityUtils.MIN_DHGEX_KEY_SIZE_PROP, Integer.toString(expected));
+        assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
+        assertEquals("Mismatched values", expected, SecurityUtils.getMinDHGroupExchangeKeySize());
+    }
+
+    @Test
+    public void testSetMinDHGroupExchangeKeySizeProgrammatically() {
+        SecurityUtils.setMinDHGroupExchangeKeySize(expected);
+        assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
+        assertEquals("Mismatched values", expected, SecurityUtils.getMinDHGroupExchangeKeySize());
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[keySize=" + expected + "]";
+    }
+}
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
index ecf7ac8..0dba74f 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
@@ -46,11 +46,8 @@ import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.resource.PathResource;
-import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.AfterClass;
 import org.junit.Assume;
-import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -62,11 +59,7 @@ import org.junit.runners.MethodSorters;
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Category({ NoIoTestCase.class })
 @SuppressWarnings("checkstyle:MethodCount")
-public class SecurityUtilsTest extends JUnitTestSupport {
-    public static final String BC_NAMED_USAGE_PROP = SecurityProviderRegistrar.CONFIG_PROP_BASE
-                                                     + "." + SecurityUtils.BOUNCY_CASTLE
-                                                     + "." + SecurityProviderRegistrar.NAMED_PROVIDER_PROPERTY;
-
+public class SecurityUtilsTest extends SecurityUtilsTestSupport {
     private static final String DEFAULT_PASSWORD = "super secret passphrase";
     private static final FilePasswordProvider TEST_PASSWORD_PROVIDER = (session, file, index) -> DEFAULT_PASSWORD;
 
@@ -74,17 +67,6 @@ public class SecurityUtilsTest extends JUnitTestSupport {
         super();
     }
 
-    // NOTE: Using the BouncyCastle provider instead of the name does not work as expected so we take no chances
-    @BeforeClass
-    public static void useNamedBouncyCastleProvider() {
-        System.setProperty(BC_NAMED_USAGE_PROP, Boolean.TRUE.toString());
-    }
-
-    @AfterClass
-    public static void unsetBouncyCastleProviderUsagePreference() {
-        System.clearProperty(BC_NAMED_USAGE_PROP);
-    }
-
     @Test
     public void testLoadEncryptedDESPrivateKey() throws Exception {
         testLoadEncryptedRSAPrivateKey("DES-EDE3");
@@ -227,6 +209,7 @@ public class SecurityUtilsTest extends JUnitTestSupport {
                 }
             }
         } finally {
+            SecurityUtils.setMinDHGroupExchangeKeySize(0); // force detection
             SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
         }
     }
@@ -242,6 +225,7 @@ public class SecurityUtilsTest extends JUnitTestSupport {
                 assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
             }
         } finally {
+            SecurityUtils.setMinDHGroupExchangeKeySize(0); // force detection
             SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
         }
     }
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTestSupport.java
new file mode 100644
index 0000000..84a6c04
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTestSupport.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class SecurityUtilsTestSupport extends JUnitTestSupport {
+    public static final String BC_NAMED_USAGE_PROP = SecurityProviderRegistrar.CONFIG_PROP_BASE
+                                                     + "." + SecurityUtils.BOUNCY_CASTLE
+                                                     + "." + SecurityProviderRegistrar.NAMED_PROVIDER_PROPERTY;
+
+    protected SecurityUtilsTestSupport() {
+        super();
+    }
+
+    // NOTE: Using the BouncyCastle provider instead of the name does not work as expected so we take no chances
+    @BeforeClass
+    public static void useNamedBouncyCastleProvider() {
+        System.setProperty(BC_NAMED_USAGE_PROP, Boolean.TRUE.toString());
+    }
+
+    @AfterClass
+    public static void unsetBouncyCastleProviderUsagePreference() {
+        System.clearProperty(BC_NAMED_USAGE_PROP);
+    }
+}
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
index 4365dbf..075127b 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
@@ -30,16 +30,19 @@ import net.i2p.crypto.eddsa.EdDSASecurityProvider;
 import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
 import org.apache.sshd.common.util.security.SecurityProviderRegistrarTestSupport;
 import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.Assume;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
+import org.junit.experimental.categories.Category;
 import org.junit.runners.MethodSorters;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
 public class EdDSASecurityProviderRegistrarTest extends SecurityProviderRegistrarTestSupport {
     private static SecurityProviderRegistrar registrarInstance;
 
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
index 6c7923c..e689982 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
@@ -64,7 +64,7 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
 
         // SSHD-941 give the user a chance to intervene in the choice
         min = CoreModuleProperties.PROP_DHGEX_CLIENT_MIN_KEY.get(session)
-                .orElse(SecurityUtils.MIN_DHGEX_KEY_SIZE);
+                .orElse(SecurityUtils.getMinDHGroupExchangeKeySize());
         max = CoreModuleProperties.PROP_DHGEX_CLIENT_MAX_KEY.get(session)
                 .orElse(SecurityUtils.getMaxDHGroupExchangeKeySize());
         prf = CoreModuleProperties.PROP_DHGEX_CLIENT_PRF_KEY.get(session)
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
index 3e23da2..3403c1d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
@@ -114,7 +114,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
         if ((cmd == SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST_OLD)
                 && (expected == SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST)) {
             oldRequest = true;
-            min = CoreModuleProperties.PROP_DHGEX_SERVER_MIN_KEY.get(session).orElse(SecurityUtils.MIN_DHGEX_KEY_SIZE);
+            min = CoreModuleProperties.PROP_DHGEX_SERVER_MIN_KEY.get(session)
+                    .orElse(SecurityUtils.getMinDHGroupExchangeKeySize());
             prf = buffer.getInt();
             max = CoreModuleProperties.PROP_DHGEX_SERVER_MAX_KEY.get(session)
                     .orElse(SecurityUtils.getMaxDHGroupExchangeKeySize());
@@ -295,8 +296,9 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
             ServerSession session, int min, int prf, int max, List<Moduli.DhGroup> groups)
             throws Exception {
         int maxDHGroupExchangeKeySize = SecurityUtils.getMaxDHGroupExchangeKeySize();
-        min = Math.max(min, SecurityUtils.MIN_DHGEX_KEY_SIZE);
-        prf = Math.max(prf, SecurityUtils.MIN_DHGEX_KEY_SIZE);
+        int minDHGroupExchangeKeySize = SecurityUtils.getMinDHGroupExchangeKeySize();
+        min = Math.max(min, minDHGroupExchangeKeySize);
+        prf = Math.max(prf, minDHGroupExchangeKeySize);
         prf = Math.min(prf, maxDHGroupExchangeKeySize);
         max = Math.min(max, maxDHGroupExchangeKeySize);