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 2019/09/24 15:52:15 UTC

[mina-sshd] branch master updated: [SSHD-943] Provide session instance when KEX factory is invoked in order to create a KeyExchange instance

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


The following commit(s) were added to refs/heads/master by this push:
     new f82e0db  [SSHD-943] Provide session instance when KEX factory is invoked in order to create a KeyExchange instance
f82e0db is described below

commit f82e0db2ca23f806d8e31ced9156132f76017483
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Tue Sep 24 10:54:22 2019 +0300

    [SSHD-943] Provide session instance when KEX factory is invoked in order to create a KeyExchange instance
---
 CHANGES.md                                         | 11 ++--
 .../java/org/apache/sshd/common/NamedFactory.java  |  7 +--
 .../keyprovider/MultiKeyIdentityIterator.java      |  4 +-
 .../sshd/common/session/SessionContextHolder.java  |  6 +--
 .../java/org/apache/sshd/client/ClientBuilder.java | 29 +++++++---
 .../client/kex/AbstractDHClientKeyExchange.java    | 10 +---
 .../java/org/apache/sshd/client/kex/DHGClient.java | 17 +++---
 .../org/apache/sshd/client/kex/DHGEXClient.java    | 29 +++++-----
 .../java/org/apache/sshd/common/BaseBuilder.java   | 22 ++++----
 .../sshd/common/config/SshConfigFileReader.java    | 61 ++++++++++++++--------
 .../sshd/common/kex/AbstractKexFactoryManager.java |  9 ++--
 .../apache/sshd/common/kex/KexFactoryManager.java  |  4 +-
 .../org/apache/sshd/common/kex/KeyExchange.java    |  6 +--
 .../apache/sshd/common/kex/KeyExchangeFactory.java | 15 ++++--
 .../sshd/common/kex/dh/AbstractDHKeyExchange.java  | 18 +++----
 .../apache/sshd/common/session/SessionHolder.java  |  7 ++-
 .../common/session/helpers/AbstractSession.java    | 14 ++---
 .../java/org/apache/sshd/server/ServerBuilder.java | 24 +++++++--
 .../server/kex/AbstractDHServerKeyExchange.java    | 10 +---
 .../org/apache/sshd/server/kex/DHGEXServer.java    | 11 ++--
 .../java/org/apache/sshd/server/kex/DHGServer.java | 11 ++--
 .../java/org/apache/sshd/KeyReExchangeTest.java    | 20 +++----
 .../sshd/client/ClientSessionListenerTest.java     | 36 ++++++++-----
 .../java/org/apache/sshd/client/kex/KexTest.java   | 12 +++--
 .../sshd/common/kex/KexFactoryManagerTest.java     |  4 +-
 .../sshd/server/ServerSessionListenerTest.java     | 42 +++++++++------
 26 files changed, 265 insertions(+), 174 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 2ec72a5..10f493f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -31,6 +31,9 @@ the standard does not specifically specify the behavior regarding symbolic links
 * `ChannelFactory` is a proper interface and it has been refactored to contain a
 `createChannel` method that accepts the session instance through which the request is made.
 
+* `KeyExchangeFactory` is a proper interface and it has been refactored to contain a
+`createKeyExchange` method that accepts the session instance through which the request is made.
+
 ## Minor code helpers
 
 * `SessionListener` supports `sessionPeerIdentificationReceived` that is invoked once successful
@@ -43,9 +46,9 @@ initialized in the past.
 * The internal moduli used in Diffie-Hellman group exchange are **cached** - lazy-loaded the 1st time such an exchange
 occurs. The cache can be invalidated (and thus force a re-load) by invoking `Moduli#clearInternalModuliCache`.
 
-* `DHGEXClient#init` implementation allows overriding the min./max. key sizes for a specific session Diffi-Helman group
-exchange via properties - see `DHGEXClient#PROP_DHGEX_CLIENT_MIN/MAX_KEY`. Similar applies for `DHGEXServer` but only for
-the message type=30.
+* `DHGEXClient` implementation allows overriding the min./max. key sizes for a specific session Diffi-Helman group
+exchange via properties - see `DHGEXClient#PROP_DHGEX_CLIENT_MIN/MAX/PRF_KEY`. Similar applies for `DHGEXServer` but only for
+the message type=30 (old request).
 
 ## Behavioral changes and enhancements
 
@@ -63,3 +66,5 @@ for the server's identification before sending its own.
 * [SSHD-941](https://issues.apache.org/jira/browse/SSHD-941) - Allow user to override min./max. key sizes for a specific session Diffi-Helman group
 exchange via properties.
 
+* [SSHD-943](https://issues.apache.org/jira/browse/SSHD-943) - Provide session instance when KEX factory is invoked in order to create a KeyExchange instance.
+
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
index 5386447..024c327 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
@@ -41,7 +41,8 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
      * @return a newly created object or {@code null} if the factory is not in the list
      */
     static <T> T create(Collection<? extends NamedFactory<? extends T>> factories, String name) {
-        NamedFactory<? extends T> f = NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, factories);
+        NamedFactory<? extends T> f =
+            NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, factories);
         if (f != null) {
             return f.create();
         } else {
@@ -49,7 +50,7 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
         }
     }
 
-    static <S extends OptionalFeature, T, E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories(
+    static <S extends OptionalFeature, E extends NamedResource> List<E> setUpTransformedFactories(
             boolean ignoreUnsupported, Collection<? extends S> preferred, Function<? super S, ? extends E> xform) {
         return preferred.stream()
             .filter(f -> ignoreUnsupported || f.isSupported())
@@ -57,7 +58,7 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
             .collect(Collectors.toList());
     }
 
-    static <T, E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories(
+    static <E extends NamedResource & OptionalFeature> List<E> setUpBuiltinFactories(
             boolean ignoreUnsupported, Collection<? extends E> preferred) {
         return preferred.stream()
             .filter(f -> ignoreUnsupported || f.isSupported())
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
index 09d5f29..3f01f18 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
@@ -26,6 +26,7 @@ import java.util.Iterator;
 import java.util.NoSuchElementException;
 
 import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.session.SessionContextHolder;
 
 /**
  * Iterates over several {@link KeyIdentityProvider}-s exhausting their
@@ -33,7 +34,7 @@ import org.apache.sshd.common.session.SessionContext;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class MultiKeyIdentityIterator implements Iterator<KeyPair> {
+public class MultiKeyIdentityIterator implements Iterator<KeyPair>, SessionContextHolder {
     protected Iterator<KeyPair> currentProvider;
     protected boolean finished;
     private final SessionContext sessionContext;
@@ -48,6 +49,7 @@ public class MultiKeyIdentityIterator implements Iterator<KeyPair> {
         return providers;
     }
 
+    @Override
     public SessionContext getSessionContext() {
         return sessionContext;
     }
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContextHolder.java
similarity index 86%
copy from sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java
copy to sshd-common/src/main/java/org/apache/sshd/common/session/SessionContextHolder.java
index 280bbf0..79edb7e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContextHolder.java
@@ -20,10 +20,8 @@
 package org.apache.sshd.common.session;
 
 /**
- * @param <S> Type of {@link Session} being held
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-@FunctionalInterface
-public interface SessionHolder<S extends Session> {
-    S getSession();
+public interface SessionContextHolder {
+    SessionContext getSessionContext();
 }
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
index 7c60a59..46d7ba9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
@@ -37,12 +37,15 @@ import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.channel.ChannelFactory;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.compression.BuiltinCompressions;
+import org.apache.sshd.common.compression.Compression;
 import org.apache.sshd.common.compression.CompressionFactory;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.kex.DHFactory;
 import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.server.forward.ForwardedTcpipFactory;
 
 /**
@@ -81,7 +84,9 @@ public class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> {
                 BuiltinSignatures.dsa
             ));
 
-    public static final Function<DHFactory, NamedFactory<KeyExchange>> DH2KEX = factory ->
+    @SuppressWarnings("checkstyle:Indentation")
+    public static final Function<DHFactory, KeyExchangeFactory> DH2KEX =
+        factory ->
             factory == null
                 ? null
                 : factory.isGroupExchange()
@@ -136,11 +141,11 @@ public class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> {
         super.fillWithDefaultValues();
 
         if (signatureFactories == null) {
-            signatureFactories = NamedFactory.setUpBuiltinFactories(false, DEFAULT_SIGNATURE_PREFERENCE);
+            signatureFactories = setUpDefaultSignatureFactories(false);
         }
 
         if (compressionFactories == null) {
-            compressionFactories = NamedFactory.setUpBuiltinFactories(false, DEFAULT_COMPRESSION_FACTORIES);
+            compressionFactories = setUpDefaultCompressionFactories(false);
         }
 
         if (keyExchangeFactories == null) {
@@ -188,11 +193,21 @@ public class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> {
         return client;
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })  // safe due to the hierarchy
+    public static List<NamedFactory<Signature>> setUpDefaultSignatureFactories(boolean ignoreUnsupported) {
+        return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_SIGNATURE_PREFERENCE);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })  // safe due to the hierarchy
+    public static List<NamedFactory<Compression>> setUpDefaultCompressionFactories(boolean ignoreUnsupported) {
+        return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_COMPRESSION_FACTORIES);
+    }
+
     /**
      * @param ignoreUnsupported If {@code true} then all the default
-     *                          key exchanges are included, regardless of whether they are currently
-     *                          supported by the JCE. Otherwise, only the supported ones out of the
-     *                          list are included
+     * key exchanges are included, regardless of whether they are currently
+     * supported by the JCE. Otherwise, only the supported ones out of the
+     * list are included
      * @return A {@link List} of the default {@link NamedFactory}
      * instances of the {@link KeyExchange}s according to the preference
      * order defined by {@link #DEFAULT_KEX_PREFERENCE}.
@@ -200,7 +215,7 @@ public class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> {
      * key exchanges according to the <tt>ignoreUnsupported</tt> parameter
      * @see org.apache.sshd.common.kex.BuiltinDHFactories#isSupported()
      */
-    public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
+    public static List<KeyExchangeFactory> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
         return NamedFactory.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX);
     }
 
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHClientKeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHClientKeyExchange.java
index c654b65..3158a2a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHClientKeyExchange.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHClientKeyExchange.java
@@ -33,8 +33,8 @@ import org.apache.sshd.common.util.ValidateUtils;
 public abstract class AbstractDHClientKeyExchange extends AbstractDHKeyExchange implements ClientSessionHolder {
     protected PublicKey serverKey;
 
-    protected AbstractDHClientKeyExchange() {
-        super();
+    protected AbstractDHClientKeyExchange(Session session) {
+        super(ValidateUtils.checkInstanceOf(session, ClientSession.class, "Using a client side KeyExchange on a server: %s", session));
     }
 
     @Override
@@ -43,12 +43,6 @@ public abstract class AbstractDHClientKeyExchange extends AbstractDHKeyExchange
     }
 
     @Override
-    public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
-        super.init(s, v_s, v_c, i_s, i_c);
-        ValidateUtils.checkInstanceOf(s, ClientSession.class, "Using a client side KeyExchange on a server: %s", s);
-    }
-
-    @Override
     public PublicKey getServerKey() {
         return serverKey;
     }
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
index bed61f3..810e352 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
@@ -46,7 +46,9 @@ public class DHGClient extends AbstractDHClientKeyExchange {
     protected final DHFactory factory;
     protected AbstractDH dh;
 
-    protected DHGClient(DHFactory factory) {
+    protected DHGClient(DHFactory factory, Session session) {
+        super(session);
+
         this.factory = Objects.requireNonNull(factory, "No factory");
     }
 
@@ -63,8 +65,8 @@ public class DHGClient extends AbstractDHClientKeyExchange {
             }
 
             @Override
-            public KeyExchange create() {
-                return new DHGClient(delegate);
+            public KeyExchange createKeyExchange(Session session) throws Exception {
+                return new DHGClient(delegate, session);
             }
 
             @Override
@@ -77,17 +79,20 @@ public class DHGClient extends AbstractDHClientKeyExchange {
     }
 
     @Override
-    public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
-        super.init(s, v_s, v_c, i_s, i_c);
+    public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
+        super.init(v_s, v_c, i_s, i_c);
+
         dh = getDH();
         hash = dh.getHash();
         hash.init();
         e = dh.getE();
 
+        Session s = getSession();
         if (log.isDebugEnabled()) {
             log.debug("init({})[{}] Send SSH_MSG_KEXDH_INIT", this, s);
         }
-        Buffer buffer = s.createBuffer(SshConstants.SSH_MSG_KEXDH_INIT, e.length + Integer.SIZE);
+        Buffer buffer =
+            s.createBuffer(SshConstants.SSH_MSG_KEXDH_INIT, e.length + Integer.SIZE);
         buffer.putMPInt(e);
 
         s.writePacket(buffer);
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 2e23683..c6dcc35 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
@@ -45,20 +45,28 @@ import org.apache.sshd.common.util.security.SecurityUtils;
 public class DHGEXClient extends AbstractDHClientKeyExchange {
     public static final String PROP_DHGEX_CLIENT_MIN_KEY = "dhgex-client-min";
     public static final String PROP_DHGEX_CLIENT_MAX_KEY = "dhgex-client-max";
+    public static final String PROP_DHGEX_CLIENT_PRF_KEY = "dhgex-client-prf";
 
     protected final DHFactory factory;
     protected byte expected;
-    protected int min = SecurityUtils.MIN_DHGEX_KEY_SIZE;
+    protected int min;
     protected int prf;
     protected int max;
     protected AbstractDH dh;
     protected byte[] p;
     protected byte[] g;
 
-    protected DHGEXClient(DHFactory factory) {
+    protected DHGEXClient(DHFactory factory, Session session) {
+        super(session);
         this.factory = Objects.requireNonNull(factory, "No factory");
-        this.max = SecurityUtils.getMaxDHGroupExchangeKeySize();
-        this.prf = Math.min(SecurityUtils.PREFERRED_DHGEX_KEY_SIZE, max);
+
+        // SSHD-941 give the user a chance to intervene in the choice
+        min = PropertyResolverUtils.getIntProperty(
+            session, PROP_DHGEX_CLIENT_MIN_KEY, SecurityUtils.MIN_DHGEX_KEY_SIZE);
+        max = PropertyResolverUtils.getIntProperty(
+            session, PROP_DHGEX_CLIENT_MAX_KEY, SecurityUtils.getMaxDHGroupExchangeKeySize());
+        prf = PropertyResolverUtils.getIntProperty(
+            session, PROP_DHGEX_CLIENT_PRF_KEY, Math.min(SecurityUtils.PREFERRED_DHGEX_KEY_SIZE, max));
     }
 
     @Override
@@ -74,8 +82,8 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
             }
 
             @Override
-            public KeyExchange create() {
-                return new DHGEXClient(delegate);
+            public KeyExchange createKeyExchange(Session session) throws Exception {
+                return new DHGEXClient(delegate, session);
             }
 
             @Override
@@ -88,13 +96,10 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
     }
 
     @Override
-    public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
-        super.init(s, v_s, v_c, i_s, i_c);
+    public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
+        super.init(v_s, v_c, i_s, i_c);
 
-        // SSHD-941 give the user a chance to intervene in the choice
-        min = PropertyResolverUtils.getIntProperty(s, PROP_DHGEX_CLIENT_MIN_KEY, min);
-        max = PropertyResolverUtils.getIntProperty(s, PROP_DHGEX_CLIENT_MAX_KEY, max);
-        prf = Math.min(SecurityUtils.PREFERRED_DHGEX_KEY_SIZE, max);
+        Session s = getSession();
         if (log.isDebugEnabled()) {
             log.debug("init({})[{}] Send SSH_MSG_KEX_DH_GEX_REQUEST - min={}, prf={}, max={}",
                 this, s, min, prf, max);
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
index 5dc33ce..904726c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
@@ -35,7 +35,7 @@ import org.apache.sshd.common.forward.DefaultForwarderFactory;
 import org.apache.sshd.common.forward.ForwardingFilterFactory;
 import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
-import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.mac.BuiltinMacs;
 import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.random.Random;
@@ -128,7 +128,7 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
             DefaultUnknownChannelReferenceHandler.INSTANCE;
 
     protected Factory<T> factory;
-    protected List<NamedFactory<KeyExchange>> keyExchangeFactories;
+    protected List<KeyExchangeFactory> keyExchangeFactories;
     protected List<NamedFactory<Cipher>> cipherFactories;
     protected List<NamedFactory<Compression>> compressionFactories;
     protected List<NamedFactory<Mac>> macFactories;
@@ -178,7 +178,7 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
         return me();
     }
 
-    public S keyExchangeFactories(List<NamedFactory<KeyExchange>> keyExchangeFactories) {
+    public S keyExchangeFactories(List<KeyExchangeFactory> keyExchangeFactories) {
         this.keyExchangeFactories = keyExchangeFactories;
         return me();
     }
@@ -283,9 +283,9 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
 
     /**
      * @param ignoreUnsupported If {@code true} then all the default
-     *                          ciphers are included, regardless of whether they are currently
-     *                          supported by the JCE. Otherwise, only the supported ones out of the
-     *                          list are included
+     * ciphers are included, regardless of whether they are currently
+     * supported by the JCE. Otherwise, only the supported ones out of the
+     * list are included
      * @return A {@link List} of the default {@link NamedFactory}
      * instances of the {@link Cipher}s according to the preference
      * order defined by {@link #DEFAULT_CIPHERS_PREFERENCE}.
@@ -293,14 +293,15 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
      * ciphers according to the <tt>ignoreUnsupported</tt> parameter
      * @see BuiltinCiphers#isSupported()
      */
+    @SuppressWarnings({ "unchecked", "rawtypes" })  // safe due to the hierarchy
     public static List<NamedFactory<Cipher>> setUpDefaultCiphers(boolean ignoreUnsupported) {
-        return NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_CIPHERS_PREFERENCE);
+        return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_CIPHERS_PREFERENCE);
     }
 
     /**
      * @param ignoreUnsupported If {@code true} all the available built-in
-     *                          {@link Mac} factories are added, otherwise only those that are supported
-     *                          by the current JDK setup
+     * {@link Mac} factories are added, otherwise only those that are supported
+     * by the current JDK setup
      * @return A {@link List} of the default {@link NamedFactory}
      * instances of the {@link Mac}s according to the preference
      * order defined by {@link #DEFAULT_MAC_PREFERENCE}.
@@ -308,7 +309,8 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
      * MACs according to the <tt>ignoreUnsupported</tt> parameter
      * @see BuiltinMacs#isSupported()
      */
+    @SuppressWarnings({ "unchecked", "rawtypes" })  // safe due to the hierarchy
     public static List<NamedFactory<Mac>> setUpDefaultMacs(boolean ignoreUnsupported) {
-        return NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_MAC_PREFERENCE);
+        return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_MAC_PREFERENCE);
     }
 }
\ No newline at end of file
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
index 46f34a1..c0205c7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
@@ -38,6 +38,7 @@ import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
 import org.apache.sshd.common.kex.DHFactory;
 import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.mac.BuiltinMacs;
 import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.signature.BuiltinSignatures;
@@ -162,11 +163,13 @@ public final class SshConfigFileReader {
         return manager;
     }
 
-    public static <M extends AbstractFactoryManager> M configureCiphers(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
+    public static <M extends AbstractFactoryManager> M configureCiphers(
+            M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(props, "No properties to configure");
         return configureCiphers(manager,
-            props.getStringProperty(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_CIPHERS),
-                lenient, ignoreUnsupported);
+            props.getStringProperty(
+                ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_CIPHERS),
+            lenient, ignoreUnsupported);
     }
 
     public static <M extends AbstractFactoryManager> M configureCiphers(
@@ -175,18 +178,22 @@ public final class SshConfigFileReader {
 
         BuiltinCiphers.ParseResult result = BuiltinCiphers.parseCiphersList(value);
         Collection<String> unsupported = result.getUnsupportedFactories();
-        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported cipher(s) (%s) in %s", unsupported, value);
+        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported),
+            "Unsupported cipher(s) (%s) in %s", unsupported, value);
 
         List<NamedFactory<Cipher>> factories =
-                BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories());
-        manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/unsupported ciphers(s): %s", value));
+            BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories());
+        manager.setCipherFactories(
+            ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/unsupported ciphers(s): %s", value));
         return manager;
     }
 
-    public static <M extends AbstractFactoryManager> M configureSignatures(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
+    public static <M extends AbstractFactoryManager> M configureSignatures(
+            M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(props, "No properties to configure");
         return configureSignatures(manager,
-            props.getStringProperty(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS),
+            props.getStringProperty(
+                ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS),
             lenient, ignoreUnsupported);
     }
 
@@ -196,11 +203,13 @@ public final class SshConfigFileReader {
 
         BuiltinSignatures.ParseResult result = BuiltinSignatures.parseSignatureList(value);
         Collection<String> unsupported = result.getUnsupportedFactories();
-        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported signatures (%s) in %s", unsupported, value);
+        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported),
+            "Unsupported signatures (%s) in %s", unsupported, value);
 
         List<NamedFactory<Signature>> factories =
-                BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories());
-        manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported signatures: %s", value));
+            BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories());
+        manager.setSignatureFactories(
+            ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported signatures: %s", value));
         return manager;
     }
 
@@ -208,7 +217,8 @@ public final class SshConfigFileReader {
             M manager, PropertyResolver resolver, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(resolver, "No properties to configure");
         return configureMacs(manager,
-            resolver.getStringProperty(ConfigFileReaderSupport.MACS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_MACS),
+            resolver.getStringProperty(
+                ConfigFileReaderSupport.MACS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_MACS),
             lenient, ignoreUnsupported);
     }
 
@@ -218,11 +228,13 @@ public final class SshConfigFileReader {
 
         BuiltinMacs.ParseResult result = BuiltinMacs.parseMacsList(value);
         Collection<String> unsupported = result.getUnsupportedFactories();
-        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported MAC(s) (%s) in %s", unsupported, value);
+        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported),
+            "Unsupported MAC(s) (%s) in %s", unsupported, value);
 
         List<NamedFactory<Mac>> factories =
-                BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories());
-        manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported MAC(s): %s", value));
+            BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories());
+        manager.setMacFactories(
+            ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported MAC(s): %s", value));
         return manager;
     }
 
@@ -242,25 +254,30 @@ public final class SshConfigFileReader {
      * @see ConfigFileReaderSupport#DEFAULT_KEX_ALGORITHMS DEFAULT_KEX_ALGORITHMS
      */
     public static <M extends AbstractFactoryManager> M configureKeyExchanges(
-            M manager, PropertyResolver props, boolean lenient, Function<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
+            M manager, PropertyResolver props, boolean lenient,
+            Function<? super DHFactory, ? extends KeyExchangeFactory> xformer, boolean ignoreUnsupported) {
         Objects.requireNonNull(props, "No properties to configure");
         return configureKeyExchanges(manager,
-            props.getStringProperty(ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_KEX_ALGORITHMS),
+            props.getStringProperty(
+                ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_KEX_ALGORITHMS),
             lenient, xformer, ignoreUnsupported);
     }
 
     public static <M extends AbstractFactoryManager> M configureKeyExchanges(
-            M manager, String value, boolean lenient, Function<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
+            M manager, String value, boolean lenient,
+            Function<? super DHFactory, ? extends KeyExchangeFactory> xformer, boolean ignoreUnsupported) {
         Objects.requireNonNull(manager, "No manager to configure");
         Objects.requireNonNull(xformer, "No DHFactory transformer");
 
         BuiltinDHFactories.ParseResult result = BuiltinDHFactories.parseDHFactoriesList(value);
         Collection<String> unsupported = result.getUnsupportedFactories();
-        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported KEX(s) (%s) in %s", unsupported, value);
+        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported),
+            "Unsupported KEX(s) (%s) in %s", unsupported, value);
 
-        List<NamedFactory<KeyExchange>> factories =
-                NamedFactory.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), xformer);
-        manager.setKeyExchangeFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported KEXS(s): %s", value));
+        List<KeyExchangeFactory> factories =
+            NamedFactory.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), xformer);
+        manager.setKeyExchangeFactories(
+            ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported KEXS(s): %s", value));
         return manager;
     }
 
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java
index 8f1d159..b4cd952 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java
@@ -19,6 +19,7 @@
 
 package org.apache.sshd.common.kex;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -38,7 +39,7 @@ public abstract class AbstractKexFactoryManager
               extends AbstractInnerCloseable
               implements KexFactoryManager {
     private final KexFactoryManager delegate;
-    private List<NamedFactory<KeyExchange>> keyExchangeFactories;
+    private List<KeyExchangeFactory> keyExchangeFactories;
     private List<NamedFactory<Cipher>> cipherFactories;
     private List<NamedFactory<Compression>> compressionFactories;
     private List<NamedFactory<Mac>> macFactories;
@@ -58,14 +59,14 @@ public abstract class AbstractKexFactoryManager
     }
 
     @Override
-    public List<NamedFactory<KeyExchange>> getKeyExchangeFactories() {
+    public List<KeyExchangeFactory> getKeyExchangeFactories() {
         KexFactoryManager parent = getDelegate();
         return resolveEffectiveFactories(keyExchangeFactories,
             (parent == null) ? Collections.emptyList() : parent.getKeyExchangeFactories());
     }
 
     @Override
-    public void setKeyExchangeFactories(List<NamedFactory<KeyExchange>> keyExchangeFactories) {
+    public void setKeyExchangeFactories(List<KeyExchangeFactory> keyExchangeFactories) {
         this.keyExchangeFactories = keyExchangeFactories;
     }
 
@@ -129,7 +130,7 @@ public abstract class AbstractKexFactoryManager
         this.kexExtensionHandler = kexExtensionHandler;
     }
 
-    protected <V> List<V> resolveEffectiveFactories(List<V> local, List<V> inherited) {
+    protected <V, C extends Collection<V>> C resolveEffectiveFactories(C local, C inherited) {
         if (GenericUtils.isEmpty(local)) {
             return inherited;
         } else {
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java
index 6c873d2..a537227 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java
@@ -47,9 +47,9 @@ public interface KexFactoryManager extends SignatureFactoriesManager, KexExtensi
      *
      * @return a list of named <code>KeyExchange</code> factories, never {@code null}
      */
-    List<NamedFactory<KeyExchange>> getKeyExchangeFactories();
+    List<KeyExchangeFactory> getKeyExchangeFactories();
 
-    void setKeyExchangeFactories(List<NamedFactory<KeyExchange>> keyExchangeFactories);
+    void setKeyExchangeFactories(List<KeyExchangeFactory> keyExchangeFactories);
 
     /**
      * Retrieve the list of named factories for <code>Cipher</code>.
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java
index f1b23b6..b47f0bc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java
@@ -26,6 +26,7 @@ import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.digest.Digest;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionHolder;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.logging.LoggingUtils;
@@ -35,7 +36,7 @@ import org.apache.sshd.common.util.logging.LoggingUtils;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface KeyExchange extends NamedResource {
+public interface KeyExchange extends NamedResource, SessionHolder<Session> {
     NavigableMap<Integer, String> GROUP_KEX_OPCODES_MAP =
         Collections.unmodifiableNavigableMap(
             LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH_MSG_KEX_DH_GEX_"));
@@ -47,14 +48,13 @@ public interface KeyExchange extends NamedResource {
     /**
      * Initialize the key exchange algorithm.
      *
-     * @param session the session using this algorithm
      * @param v_s     the server identification string
      * @param v_c     the client identification string
      * @param i_s     the server key initialization packet
      * @param i_c     the client key initialization packet
      * @throws Exception if an error occurs
      */
-    void init(Session session, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception;
+    void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception;
 
     /**
      * Process the next packet
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchangeFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchangeFactory.java
index cea659d..f9288e2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchangeFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchangeFactory.java
@@ -19,13 +19,18 @@
 
 package org.apache.sshd.common.kex;
 
-import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.session.Session;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-// CHECKSTYLE:OFF
-public interface KeyExchangeFactory extends NamedFactory<KeyExchange> {
-    // nothing extra
+public interface KeyExchangeFactory extends NamedResource {
+    /**
+     * @param session The {@link Session} for which the factory is invoked
+     * @return The {@link KeyExchange} instance to be used
+     * @throws Exception If failed to create
+     */
+    KeyExchange createKeyExchange(Session session) throws Exception;
 }
-//CHECKSTYLE:ON
+
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java
index 8bcfdae..7f29782 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java
@@ -19,19 +19,18 @@
 
 package org.apache.sshd.common.kex.dh;
 
+import java.util.Objects;
+
 import org.apache.sshd.common.digest.Digest;
 import org.apache.sshd.common.kex.KeyExchange;
 import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.session.SessionHolder;
-import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public abstract class AbstractDHKeyExchange extends AbstractLoggingBean implements KeyExchange, SessionHolder<AbstractSession> {
-
+public abstract class AbstractDHKeyExchange extends AbstractLoggingBean implements KeyExchange {
     protected byte[] v_s;
     protected byte[] v_c;
     protected byte[] i_s;
@@ -42,15 +41,14 @@ public abstract class AbstractDHKeyExchange extends AbstractLoggingBean implemen
     protected byte[] k;
     protected byte[] h;
 
-    private AbstractSession session;
+    private final Session session;
 
-    protected AbstractDHKeyExchange() {
-        super();
+    protected AbstractDHKeyExchange(Session session) {
+        this.session = Objects.requireNonNull(session, "No session provided");
     }
 
     @Override
-    public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
-        this.session = ValidateUtils.checkInstanceOf(s, AbstractSession.class, "Not an abstract session: %s", s);
+    public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
         this.v_s = ValidateUtils.checkNotNullAndNotEmpty(v_s, "No v_s value");
         this.v_c = ValidateUtils.checkNotNullAndNotEmpty(v_c, "No v_c value");
         this.i_s = ValidateUtils.checkNotNullAndNotEmpty(i_s, "No i_s value");
@@ -58,7 +56,7 @@ public abstract class AbstractDHKeyExchange extends AbstractLoggingBean implemen
     }
 
     @Override
-    public AbstractSession getSession() {
+    public Session getSession() {
         return session;
     }
 
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java
index 280bbf0..3a6491d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java
@@ -24,6 +24,11 @@ package org.apache.sshd.common.session;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 @FunctionalInterface
-public interface SessionHolder<S extends Session> {
+public interface SessionHolder<S extends Session> extends SessionContextHolder {
+    @Override
+    default SessionContext getSessionContext() {
+        return getSession();
+    }
+
     S getSession();
 }
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index cc5c926..4bb43e2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -44,6 +44,7 @@ import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.RuntimeSshException;
 import org.apache.sshd.common.Service;
 import org.apache.sshd.common.SshConstants;
@@ -63,6 +64,7 @@ import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KexState;
 import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.kex.extension.KexExtensionHandler;
 import org.apache.sshd.common.kex.extension.KexExtensionHandler.AvailabilityPhase;
 import org.apache.sshd.common.kex.extension.KexExtensionHandler.KexPhase;
@@ -639,12 +641,12 @@ public abstract class AbstractSession extends SessionHelper {
 
         Map<KexProposalOption, String> result = negotiate();
         String kexAlgorithm = result.get(KexProposalOption.ALGORITHMS);
-        Collection<? extends NamedFactory<KeyExchange>> kexFactories = getKeyExchangeFactories();
+        Collection<? extends KeyExchangeFactory> kexFactories = getKeyExchangeFactories();
+        KeyExchangeFactory kexFactory = NamedResource.findByName(
+            kexAlgorithm, String.CASE_INSENSITIVE_ORDER, kexFactories);
+        ValidateUtils.checkNotNull(kexFactory, "Unknown negotiated KEX algorithm: %s", kexAlgorithm);
         synchronized (pendingPackets) {
-            kex = ValidateUtils.checkNotNull(
-                NamedFactory.create(kexFactories, kexAlgorithm),
-                "Unknown negotiated KEX algorithm: %s",
-                kexAlgorithm);
+            kex = kexFactory.createKeyExchange(this);
         }
 
         byte[] v_s = serverVersion.getBytes(StandardCharsets.UTF_8);
@@ -655,7 +657,7 @@ public abstract class AbstractSession extends SessionHelper {
             i_s = getServerKexData();
             i_c = getClientKexData();
         }
-        kex.init(this, v_s, v_c, i_s, i_c);
+        kex.init(v_s, v_c, i_s, i_c);
 
         signalSessionEvent(SessionListener.Event.KexCompleted);
     }
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
index 0510141..9dbfac1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
@@ -29,11 +29,14 @@ import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.channel.ChannelFactory;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.compression.BuiltinCompressions;
+import org.apache.sshd.common.compression.Compression;
 import org.apache.sshd.common.compression.CompressionFactory;
 import org.apache.sshd.common.kex.DHFactory;
 import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator;
 import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
 import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
@@ -52,8 +55,9 @@ import org.apache.sshd.server.kex.DHGServer;
  * SshServer builder
  */
 public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
-
-    public static final Function<DHFactory, NamedFactory<KeyExchange>> DH2KEX = factory ->
+    @SuppressWarnings("checkstyle:Indentation")
+    public static final Function<DHFactory, KeyExchangeFactory> DH2KEX =
+        factory ->
             factory == null
                 ? null
                 : factory.isGroupExchange()
@@ -126,11 +130,11 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
         super.fillWithDefaultValues();
 
         if (compressionFactories == null) {
-            compressionFactories = NamedFactory.setUpBuiltinFactories(false, DEFAULT_COMPRESSION_FACTORIES);
+            compressionFactories = setUpDefaultCompressionFactories(false);
         }
 
         if (signatureFactories == null) {
-            signatureFactories = NamedFactory.setUpBuiltinFactories(false, DEFAULT_SIGNATURE_PREFERENCE);
+            signatureFactories = setUpDefaultSignatureFactories(false);
         }
 
         if (keyExchangeFactories == null) {
@@ -168,6 +172,16 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
         return server;
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })  // safe due to the hierarchy
+    public static List<NamedFactory<Signature>> setUpDefaultSignatureFactories(boolean ignoreUnsupported) {
+        return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_SIGNATURE_PREFERENCE);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })  // safe due to the hierarchy
+    public static List<NamedFactory<Compression>> setUpDefaultCompressionFactories(boolean ignoreUnsupported) {
+        return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_COMPRESSION_FACTORIES);
+    }
+
     /**
      * @param ignoreUnsupported If {@code true} then all the default
      * key exchanges are included, regardless of whether they are currently
@@ -180,7 +194,7 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
      * key exchanges according to the <tt>ignoreUnsupported</tt> parameter
      * @see org.apache.sshd.common.kex.BuiltinDHFactories#isSupported()
      */
-    public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
+    public static List<KeyExchangeFactory> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
         return NamedFactory.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX);
     }
 
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHServerKeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHServerKeyExchange.java
index 831086c..7c11ec5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHServerKeyExchange.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHServerKeyExchange.java
@@ -32,8 +32,8 @@ import org.apache.sshd.server.session.ServerSessionHolder;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public abstract class AbstractDHServerKeyExchange extends AbstractDHKeyExchange implements ServerSessionHolder {
-    protected AbstractDHServerKeyExchange() {
-        super();
+    protected AbstractDHServerKeyExchange(Session s) {
+        super(ValidateUtils.checkInstanceOf(s, ServerSession.class, "Using a client side KeyExchange on a server: %s", s));
     }
 
     @Override
@@ -42,12 +42,6 @@ public abstract class AbstractDHServerKeyExchange extends AbstractDHKeyExchange
     }
 
     @Override
-    public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
-        super.init(s, v_s, v_c, i_s, i_c);
-        ValidateUtils.checkInstanceOf(s, ServerSession.class, "Using a server side KeyExchange on a client: %s", s);
-    }
-
-    @Override
     public PublicKey getServerKey() {
         ServerSession session = getServerSession();
         return Objects.requireNonNull(session.getHostKey(), "No server key pair available").getPublic();
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 7a4b8f2..27ed991 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
@@ -67,7 +67,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
     protected byte expected;
     protected boolean oldRequest;
 
-    protected DHGEXServer(DHFactory factory) {
+    protected DHGEXServer(DHFactory factory, Session session) {
+        super(session);
         this.factory = Objects.requireNonNull(factory, "No factory");
     }
 
@@ -79,8 +80,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
     public static KeyExchangeFactory newFactory(DHFactory factory) {
         return new KeyExchangeFactory() {
             @Override
-            public KeyExchange create() {
-                return new DHGEXServer(factory);
+            public KeyExchange createKeyExchange(Session session) throws Exception {
+                return new DHGEXServer(factory, session);
             }
 
             @Override
@@ -98,8 +99,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
     }
 
     @Override
-    public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
-        super.init(s, v_s, v_c, i_s, i_c);
+    public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
+        super.init(v_s, v_c, i_s, i_c);
         expected = SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST;
     }
 
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
index d74353b..e5dad22 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
@@ -45,7 +45,8 @@ public class DHGServer extends AbstractDHServerKeyExchange {
     protected final DHFactory factory;
     protected AbstractDH dh;
 
-    protected DHGServer(DHFactory factory) {
+    protected DHGServer(DHFactory factory, Session session) {
+        super(session);
         this.factory = Objects.requireNonNull(factory, "No factory");
     }
 
@@ -57,8 +58,8 @@ public class DHGServer extends AbstractDHServerKeyExchange {
     public static KeyExchangeFactory newFactory(final DHFactory factory) {
         return new KeyExchangeFactory() {
             @Override
-            public KeyExchange create() {
-                return new DHGServer(factory);
+            public KeyExchange createKeyExchange(Session session) throws Exception {
+                return new DHGServer(factory, session);
             }
 
             @Override
@@ -76,8 +77,8 @@ public class DHGServer extends AbstractDHServerKeyExchange {
     }
 
     @Override
-    public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
-        super.init(s, v_s, v_c, i_s, i_c);
+    public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
+        super.init(v_s, v_c, i_s, i_c);
         dh = factory.create();
         hash = dh.getHash();
         hash.init();
diff --git a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
index 9390383..13bcd56 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
@@ -42,13 +42,13 @@ import org.apache.sshd.client.channel.ClientChannel;
 import org.apache.sshd.client.channel.ClientChannelEvent;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.future.KeyExchangeFuture;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
 import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.util.io.NullOutputStream;
@@ -150,21 +150,21 @@ public class KeyReExchangeTest extends BaseTestSupport {
         try (SshClient client = setupTestClient()) {
             client.getCipherFactories().add(BuiltinCiphers.none);
             // replace the original KEX factories with wrapped ones that we can fail intentionally
-            List<NamedFactory<KeyExchange>> kexFactories = new ArrayList<>();
-            final AtomicBoolean successfulInit = new AtomicBoolean(true);
-            final AtomicBoolean successfulNext = new AtomicBoolean(true);
-            final ClassLoader loader = getClass().getClassLoader();
-            final Class<?>[] interfaces = {KeyExchange.class};
-            for (final NamedFactory<KeyExchange> factory : client.getKeyExchangeFactories()) {
-                kexFactories.add(new NamedFactory<KeyExchange>() {
+            List<KeyExchangeFactory> kexFactories = new ArrayList<>();
+            AtomicBoolean successfulInit = new AtomicBoolean(true);
+            AtomicBoolean successfulNext = new AtomicBoolean(true);
+            ClassLoader loader = getClass().getClassLoader();
+            Class<?>[] interfaces = {KeyExchange.class};
+            for (KeyExchangeFactory factory : client.getKeyExchangeFactories()) {
+                kexFactories.add(new KeyExchangeFactory() {
                     @Override
                     public String getName() {
                         return factory.getName();
                     }
 
                     @Override
-                    public KeyExchange create() {
-                        final KeyExchange proxiedInstance = factory.create();
+                    public KeyExchange createKeyExchange(Session s) throws Exception {
+                        KeyExchange proxiedInstance = factory.createKeyExchange(s);
                         return (KeyExchange) Proxy.newProxyInstance(loader, interfaces, (proxy, method, args) -> {
                             String name = method.getName();
                             if ("init".equals(name) && (!successfulInit.get())) {
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
index e5e928a..8669ba8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
@@ -31,10 +31,14 @@ import org.apache.sshd.client.auth.keyboard.UserInteraction;
 import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.Cipher;
+import org.apache.sshd.common.cipher.CipherFactory;
 import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.mac.Mac;
+import org.apache.sshd.common.mac.MacFactory;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.util.GenericUtils;
@@ -91,18 +95,21 @@ public class ClientSessionListenerTest extends BaseTestSupport {
 
     @Test
     public void testSessionListenerCanModifyKEXNegotiation() throws Exception {
-        final Map<KexProposalOption, NamedFactory<?>> kexParams = new EnumMap<>(KexProposalOption.class);
+        Map<KexProposalOption, NamedResource> kexParams = new EnumMap<>(KexProposalOption.class);
         kexParams.put(KexProposalOption.ALGORITHMS, getLeastFavorite(KeyExchange.class, client.getKeyExchangeFactories()));
-        kexParams.put(KexProposalOption.C2SENC, getLeastFavorite(Cipher.class, client.getCipherFactories()));
-        kexParams.put(KexProposalOption.C2SMAC, getLeastFavorite(Mac.class, client.getMacFactories()));
+        kexParams.put(KexProposalOption.C2SENC, getLeastFavorite(CipherFactory.class, client.getCipherFactories()));
+        kexParams.put(KexProposalOption.C2SMAC, getLeastFavorite(MacFactory.class, client.getMacFactories()));
 
         SessionListener listener = new SessionListener() {
             @Override
             @SuppressWarnings("unchecked")
             public void sessionCreated(Session session) {
-                session.setKeyExchangeFactories(Collections.singletonList((NamedFactory<KeyExchange>) kexParams.get(KexProposalOption.ALGORITHMS)));
-                session.setCipherFactories(Collections.singletonList((NamedFactory<Cipher>) kexParams.get(KexProposalOption.C2SENC)));
-                session.setMacFactories(Collections.singletonList((NamedFactory<Mac>) kexParams.get(KexProposalOption.C2SMAC)));
+                session.setKeyExchangeFactories(
+                    Collections.singletonList((KeyExchangeFactory) kexParams.get(KexProposalOption.ALGORITHMS)));
+                session.setCipherFactories(
+                    Collections.singletonList((NamedFactory<Cipher>) kexParams.get(KexProposalOption.C2SENC)));
+                session.setMacFactories(
+                    Collections.singletonList((NamedFactory<Mac>) kexParams.get(KexProposalOption.C2SMAC)));
             }
         };
         client.addSessionListener(listener);
@@ -120,16 +127,17 @@ public class ClientSessionListenerTest extends BaseTestSupport {
 
     @Test
     public void testSessionListenerCanInfluenceAuthentication() throws IOException {
-        final AtomicInteger verificationCount = new AtomicInteger();
-        final ServerKeyVerifier verifier = (sshClientSession, remoteAddress, serverKey) -> {
+        AtomicInteger verificationCount = new AtomicInteger();
+        ServerKeyVerifier verifier = (sshClientSession, remoteAddress, serverKey) -> {
             verificationCount.incrementAndGet();
             return true;
         };
-
         SessionListener listener = new SessionListener() {
             @Override
             public void sessionEvent(Session session, Event event) {
-                if ((!session.isAuthenticated()) && (session instanceof ClientSession) && Event.KexCompleted.equals(event)) {
+                if ((!session.isAuthenticated())
+                        && (session instanceof ClientSession)
+                        && Event.KexCompleted.equals(event)) {
                     ClientSession clientSession = (ClientSession) session;
                     clientSession.setServerKeyVerifier(verifier);
                     clientSession.setUserInteraction(UserInteraction.NONE);
@@ -149,14 +157,18 @@ public class ClientSessionListenerTest extends BaseTestSupport {
         }
     }
 
-    private static <V> NamedFactory<V> getLeastFavorite(Class<V> type, List<? extends NamedFactory<V>> factories) {
+    private static <V extends NamedResource> NamedResource getLeastFavorite(
+            Class<V> type, List<? extends NamedResource> factories) {
         int numFactories = GenericUtils.size(factories);
         assertTrue("No factories for " + type.getSimpleName(), numFactories > 0);
         return factories.get(numFactories - 1);
     }
 
     private ClientSession createTestClientSession() throws IOException {
-        ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession();
+        ClientSession session =
+            client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession();
         try {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
index 678521f..da599ba 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
@@ -34,10 +34,9 @@ import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.channel.ClientChannel;
 import org.apache.sshd.client.channel.ClientChannelEvent;
 import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
-import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
@@ -119,12 +118,15 @@ public class KexTest extends BaseTestSupport {
         testClient(ClientBuilder.DH2KEX.apply(factory));
     }
 
-    private void testClient(NamedFactory<KeyExchange> kex) throws Exception {
+    private void testClient(KeyExchangeFactory kex) throws Exception {
         try (ByteArrayOutputStream sent = new ByteArrayOutputStream();
              ByteArrayOutputStream out = new ByteArrayOutputStream()) {
 
             client.setKeyExchangeFactories(Collections.singletonList(kex));
-            try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession session =
+                    client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                        .verify(7L, TimeUnit.SECONDS)
+                        .getSession()) {
                 session.addPasswordIdentity(getCurrentTestName());
                 session.auth().verify(5L, TimeUnit.SECONDS);
 
@@ -153,7 +155,7 @@ public class KexTest extends BaseTestSupport {
                     teeOut.flush();
 
                     Collection<ClientChannelEvent> result =
-                            channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), TimeUnit.SECONDS.toMillis(15L));
+                        channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), TimeUnit.SECONDS.toMillis(15L));
                     assertFalse("Timeout while waiting for channel closure", result.contains(ClientChannelEvent.TIMEOUT));
                 }
             }
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java b/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java
index 0832ab4..acfd70a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java
@@ -140,12 +140,12 @@ public class KexFactoryManagerTest extends BaseTestSupport {
         }
 
         @Override
-        public List<NamedFactory<KeyExchange>> getKeyExchangeFactories() {
+        public List<KeyExchangeFactory> getKeyExchangeFactories() {
             return null;
         }
 
         @Override
-        public void setKeyExchangeFactories(List<NamedFactory<KeyExchange>> keyExchangeFactories) {
+        public void setKeyExchangeFactories(List<KeyExchangeFactory> keyExchangeFactories) {
             throw new UnsupportedOperationException("N/A");
         }
 
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
index 50d83ed..ffa54a4 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
@@ -32,11 +32,14 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.Cipher;
+import org.apache.sshd.common.cipher.CipherFactory;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.kex.KexProposalOption;
-import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.mac.Mac;
+import org.apache.sshd.common.mac.MacFactory;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.util.GenericUtils;
@@ -165,18 +168,21 @@ public class ServerSessionListenerTest extends BaseTestSupport {
 
     @Test
     public void testSessionListenerCanModifyKEXNegotiation() throws Exception {
-        final Map<KexProposalOption, NamedFactory<?>> kexParams = new EnumMap<>(KexProposalOption.class);
-        kexParams.put(KexProposalOption.ALGORITHMS, getLeastFavorite(KeyExchange.class, sshd.getKeyExchangeFactories()));
-        kexParams.put(KexProposalOption.S2CENC, getLeastFavorite(Cipher.class, sshd.getCipherFactories()));
-        kexParams.put(KexProposalOption.S2CMAC, getLeastFavorite(Mac.class, sshd.getMacFactories()));
+        Map<KexProposalOption, NamedResource> kexParams = new EnumMap<>(KexProposalOption.class);
+        kexParams.put(KexProposalOption.ALGORITHMS, getLeastFavorite(KeyExchangeFactory.class, sshd.getKeyExchangeFactories()));
+        kexParams.put(KexProposalOption.S2CENC, getLeastFavorite(CipherFactory.class, sshd.getCipherFactories()));
+        kexParams.put(KexProposalOption.S2CMAC, getLeastFavorite(MacFactory.class, sshd.getMacFactories()));
 
         SessionListener listener = new SessionListener() {
             @Override
             @SuppressWarnings("unchecked")
             public void sessionCreated(Session session) {
-                session.setKeyExchangeFactories(Collections.singletonList((NamedFactory<KeyExchange>) kexParams.get(KexProposalOption.ALGORITHMS)));
-                session.setCipherFactories(Collections.singletonList((NamedFactory<Cipher>) kexParams.get(KexProposalOption.S2CENC)));
-                session.setMacFactories(Collections.singletonList((NamedFactory<Mac>) kexParams.get(KexProposalOption.S2CMAC)));
+                session.setKeyExchangeFactories(
+                    Collections.singletonList((KeyExchangeFactory) kexParams.get(KexProposalOption.ALGORITHMS)));
+                session.setCipherFactories(
+                    Collections.singletonList((NamedFactory<Cipher>) kexParams.get(KexProposalOption.S2CENC)));
+                session.setMacFactories(
+                    Collections.singletonList((NamedFactory<Mac>) kexParams.get(KexProposalOption.S2CMAC)));
             }
         };
         sshd.addSessionListener(listener);
@@ -194,9 +200,9 @@ public class ServerSessionListenerTest extends BaseTestSupport {
 
     @Test
     public void testSessionListenerCanModifyAuthentication() throws Exception {
-        final AtomicInteger passCount = new AtomicInteger(0);
-        final PasswordAuthenticator defaultPassAuth = sshd.getPasswordAuthenticator();
-        final PasswordAuthenticator passAuth = (username, password, session) -> {
+        AtomicInteger passCount = new AtomicInteger(0);
+        PasswordAuthenticator defaultPassAuth = sshd.getPasswordAuthenticator();
+        PasswordAuthenticator passAuth = (username, password, session) -> {
             passCount.incrementAndGet();
             return defaultPassAuth.authenticate(username, password, session);
         };
@@ -215,22 +221,28 @@ public class ServerSessionListenerTest extends BaseTestSupport {
         sshd.addSessionListener(listener);
 
         try (ClientSession session = createTestClientSession()) {
-            assertNotSame("Mismatched default password authenticator", passAuth, sshd.getPasswordAuthenticator());
-            assertNotSame("Mismatched default kb authenticator", KeyboardInteractiveAuthenticator.NONE, sshd.getKeyboardInteractiveAuthenticator());
+            assertNotSame("Mismatched default password authenticator",
+                passAuth, sshd.getPasswordAuthenticator());
+            assertNotSame("Mismatched default kb authenticator",
+                KeyboardInteractiveAuthenticator.NONE, sshd.getKeyboardInteractiveAuthenticator());
             assertEquals("Authenticator override not invoked", 1, passCount.get());
         } finally {
             sshd.removeSessionListener(listener);
         }
     }
 
-    private static <V> NamedFactory<V> getLeastFavorite(Class<V> type, List<? extends NamedFactory<V>> factories) {
+    private static <V extends NamedResource> NamedResource getLeastFavorite(
+            Class<V> type, List<? extends NamedResource> factories) {
         int numFactories = GenericUtils.size(factories);
         assertTrue("No factories for " + type.getSimpleName(), numFactories > 0);
         return factories.get(numFactories - 1);
     }
 
     private ClientSession createTestClientSession() throws Exception {
-        ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession();
+        ClientSession session =
+            client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession();
         try {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(11L, TimeUnit.SECONDS);