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 2015/05/07 13:59:01 UTC

[2/2] mina-sshd git commit: [SSHD-455] Allow users to register "extensions" to some of the builtin factories

[SSHD-455] Allow users to register "extensions" to some of the builtin factories


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/7ccbd066
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/7ccbd066
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/7ccbd066

Branch: refs/heads/master
Commit: 7ccbd066355bde4240cc5ccee0ab06f891921310
Parents: cca7cc7
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Thu May 7 14:58:48 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Thu May 7 14:58:48 2015 +0300

----------------------------------------------------------------------
 .../main/java/org/apache/sshd/SshBuilder.java   |  84 +++++-----
 .../main/java/org/apache/sshd/SshServer.java    |   1 +
 .../org/apache/sshd/common/NamedFactory.java    |  15 +-
 .../org/apache/sshd/common/NamedResource.java   |  28 ++++
 .../org/apache/sshd/common/Transformer.java     |  58 +++++++
 .../apache/sshd/common/cipher/BaseCipher.java   |   4 +
 .../sshd/common/cipher/BuiltinCiphers.java      |  86 +++++++++-
 .../sshd/common/cipher/CipherFactory.java       |  38 +++++
 .../apache/sshd/common/cipher/CipherNone.java   |   5 +-
 .../org/apache/sshd/common/cipher/ECCurves.java |  12 +-
 .../common/compression/BuiltinCompressions.java | 133 +++++++++++++++-
 .../compression/CompressionDelayedZlib.java     |   1 +
 .../common/compression/CompressionFactory.java  |  37 +++++
 .../common/compression/CompressionZlib.java     |   4 +
 .../common/config/CompressionConfigValue.java   |  12 +-
 .../sshd/common/config/SshConfigFileReader.java | 156 +++++++++++++++----
 .../sshd/common/kex/BuiltinDHFactories.java     |  76 ++++++++-
 .../org/apache/sshd/common/kex/DHFactory.java   |   3 +-
 .../org/apache/sshd/common/mac/BaseMac.java     |   5 +
 .../org/apache/sshd/common/mac/BuiltinMacs.java |  92 +++++++++--
 .../org/apache/sshd/common/mac/MacFactory.java  |  38 +++++
 .../common/signature/AbstractSignature.java     |   2 +
 .../common/signature/BuiltinSignatures.java     | 100 +++++++++---
 .../sshd/common/signature/SignatureDSA.java     |   2 +
 .../sshd/common/signature/SignatureECDSA.java   |   8 +-
 .../sshd/common/signature/SignatureFactory.java |  38 +++++
 .../sshd/common/signature/SignatureRSA.java     |   6 +-
 .../apache/sshd/common/util/GenericUtils.java   |  40 +++++
 .../src/test/java/org/apache/sshd/LoadTest.java |   3 +-
 .../org/apache/sshd/client/kex/KexTest.java     |   2 +-
 .../sshd/common/cipher/BaseCipherTest.java      |   3 +-
 .../sshd/common/cipher/BuiltinCiphersTest.java  |  69 ++++++++
 .../compression/BuiltinCompressionsTest.java    | 114 ++++++++++++++
 .../common/config/SshConfigFileReaderTest.java  |  43 ++++-
 .../sshd/common/kex/BuiltinDHFactoriesTest.java |  57 +++++++
 .../apache/sshd/common/mac/BuiltinMacsTest.java |  70 +++++++++
 .../common/signature/BuiltinSignaturesTest.java |  70 +++++++++
 37 files changed, 1365 insertions(+), 150 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java b/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
index 4daf088..89e9762 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
@@ -18,7 +18,6 @@
  */
 package org.apache.sshd;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -39,6 +38,7 @@ import org.apache.sshd.common.Random;
 import org.apache.sshd.common.RequestHandler;
 import org.apache.sshd.common.Signature;
 import org.apache.sshd.common.TcpipForwarderFactory;
+import org.apache.sshd.common.Transformer;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
@@ -83,17 +83,17 @@ public class SshBuilder {
      * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
      */
     public static class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder<T, S>> implements ObjectBuilder<T> {
-        protected Factory<T> factory = null;
-        protected List<NamedFactory<KeyExchange>> keyExchangeFactories = null;
-        protected List<NamedFactory<Cipher>> cipherFactories = null;
-        protected List<NamedFactory<Compression>> compressionFactories = null;
-        protected List<NamedFactory<Mac>> macFactories = null;
-        protected List<NamedFactory<Signature>> signatureFactories = null;
-        protected Factory<Random> randomFactory = null;
-        protected List<NamedFactory<Channel>> channelFactories = null;
-        protected FileSystemFactory fileSystemFactory = null;
-        protected TcpipForwarderFactory tcpipForwarderFactory = null;
-        protected List<RequestHandler<ConnectionService>> globalRequestHandlers = null;
+        protected Factory<T> factory;
+        protected List<NamedFactory<KeyExchange>> keyExchangeFactories;
+        protected List<NamedFactory<Cipher>> cipherFactories;
+        protected List<NamedFactory<Compression>> compressionFactories;
+        protected List<NamedFactory<Mac>> macFactories;
+        protected List<NamedFactory<Signature>> signatureFactories;
+        protected Factory<Random> randomFactory;
+        protected List<NamedFactory<Channel>> channelFactories;
+        protected FileSystemFactory fileSystemFactory;
+        protected TcpipForwarderFactory tcpipForwarderFactory;
+        protected List<RequestHandler<ConnectionService>> globalRequestHandlers;
 
         protected S fillWithDefaultValues() {
             if (signatureFactories == null) {
@@ -212,6 +212,7 @@ public class SshBuilder {
             return ssh;
         }
 
+        @Override
         public T build() {
             return build(true);
         }
@@ -343,7 +344,19 @@ public class SshBuilder {
      * SshClient builder
      */
     public static class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> {
-
+        public static final Transformer<DHFactory,NamedFactory<KeyExchange>> DH2KEX =
+                new Transformer<DHFactory, NamedFactory<KeyExchange>>() {
+                    @Override
+                    public NamedFactory<KeyExchange> transform(DHFactory factory) {
+                        if (factory == null) {
+                            return null;
+                        } else if (factory.isGroupExchange()) {
+                            return DHGEXClient.newFactory(factory);
+                        } else {
+                            return DHGClient.newFactory(factory);
+                        }
+                    }
+                };
         protected ServerKeyVerifier serverKeyVerifier;
 
         public ClientBuilder serverKeyVerifier(ServerKeyVerifier serverKeyVerifier) {
@@ -390,21 +403,7 @@ public class SshBuilder {
          * @see BuiltinDHFactories#isSupported()
          */
         public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
-            List<NamedFactory<KeyExchange>> avail = new ArrayList<>(DEFAULT_KEX_PREFERENCE.size());
-            for (BuiltinDHFactories f : BuiltinDHFactories.VALUES) {
-                if (ignoreUnsupported || f.isSupported()) {
-                    avail.add(getKeyExchangeFactory(f));
-                }
-            }
-            return avail;
-        }
-
-        public static NamedFactory<KeyExchange> getKeyExchangeFactory(DHFactory factory) {
-            if (factory.isGroupExchange()) {
-                return DHGEXClient.newFactory(factory);
-            } else {
-                return DHGClient.newFactory(factory);
-            }
+            return NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX);
         }
     }
 
@@ -412,6 +411,19 @@ public class SshBuilder {
      * SshServer builder
      */
     public static class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
+        public static final Transformer<DHFactory,NamedFactory<KeyExchange>>    DH2KEX = 
+                new Transformer<DHFactory, NamedFactory<KeyExchange>>() {
+                    @Override
+                    public NamedFactory<KeyExchange> transform(DHFactory factory) {
+                        if (factory == null) {
+                            return null;
+                        } else if (factory.isGroupExchange()) {
+                            return DHGEXServer.newFactory(factory);
+                        } else {
+                            return DHGServer.newFactory(factory);
+                        }
+                    }
+                };
 
         @Override
         protected ServerBuilder fillWithDefaultValues() {
@@ -450,21 +462,7 @@ public class SshBuilder {
          * @see BuiltinDHFactories#isSupported()
          */
         public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
-            List<NamedFactory<KeyExchange>> avail = new ArrayList<>(DEFAULT_KEX_PREFERENCE.size());
-            for (BuiltinDHFactories f : BuiltinDHFactories.VALUES) {
-                if (ignoreUnsupported || f.isSupported()) {
-                    avail.add(getKeyExchangeFactory(f));
-                }
-            }
-            return avail;
-        }
-
-        public static NamedFactory<KeyExchange> getKeyExchangeFactory(DHFactory factory) {
-            if (factory.isGroupExchange()) {
-                return DHGEXServer.newFactory(factory);
-            } else {
-                return DHGServer.newFactory(factory);
-            }
+            return NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
index 9e45c44..269176a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
@@ -93,6 +93,7 @@ import org.apache.sshd.server.shell.ProcessShellFactory;
 public class SshServer extends AbstractFactoryManager implements ServerFactoryManager, Closeable {
 
     public static final Factory<SshServer> DEFAULT_SSH_SERVER_FACTORY = new Factory<SshServer>() {
+        @Override
         public SshServer create() {
             return new SshServer();
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
index f12ca99..3337e46 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
@@ -112,7 +112,19 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
             }
             return null;
         }
-        
+
+        public static final <S extends OptionalFeature,T,E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories(
+                boolean ignoreUnsupported, Collection<? extends S> preferred, Transformer<? super S,? extends E> xform) {
+            List<NamedFactory<T>>   avail=new ArrayList<>(preferred.size());
+            for (S f : preferred) {
+                if (ignoreUnsupported || f.isSupported()) {
+                    avail.add(xform.transform(f));
+                }
+            }
+            
+            return avail;
+        }
+
         public static final <T,E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories(
                 boolean ignoreUnsupported, Collection<? extends E> preferred) {
             List<NamedFactory<T>>   avail=new ArrayList<>(preferred.size());
@@ -125,5 +137,4 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
             return avail;
         }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
index 494a1a9..d57f433 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
@@ -22,6 +22,7 @@ package org.apache.sshd.common;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import org.apache.sshd.common.util.GenericUtils;
@@ -34,6 +35,33 @@ public interface NamedResource {
      * @return The resource name
      */
     String getName();
+    
+    /**
+     * Compares 2 {@link NamedResource}s according to their {@link #getName()}
+     * value case <U>insensitive</U>
+     */
+    Comparator<NamedResource> BY_NAME_COMPARATOR=new Comparator<NamedResource>() {
+            @Override
+            public int compare(NamedResource r1, NamedResource r2) {
+                String  n1=r1.getName(), n2=r2.getName();
+                return String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
+            }
+        };
+
+    /**
+     * Returns the value of {@link #getName()} - or {@code null} if argument is {@code null}
+     */
+    Transformer<NamedResource,String> NAME_EXTRACTOR=new Transformer<NamedResource,String>() {
+            @Override
+            public String transform(NamedResource input) {
+                if (input == null) {
+                    return null;
+                } else {
+                    return input.getName();
+                }
+            }
+        };
+
     /**
      * Utility class to help using {@link NamedResource}s
      */

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java b/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
new file mode 100644
index 0000000..952b2c0
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Transformer<I, O> {
+    // TODO in JDK-8 replace this with Function
+    /**
+     * @param input Input value
+     * @return Transformed output value
+     */
+    O transform(I input);
+
+    /**
+     * Invokes {@link Objects#toString(Object)} on the argument
+     */
+    Transformer<Object,String> TOSTRING=new Transformer<Object,String>() {
+            @Override
+            public String transform(Object input) {
+                return Objects.toString(input);
+            }
+        };
+
+    /**
+     * Returns {@link Enum#name()} or {@code null} if argument is {@code null}
+     */
+    Transformer<Enum<?>,String> ENUM_NAME_EXTRACTOR=new Transformer<Enum<?>,String>() {
+            @Override
+            public String transform(Enum<?> input) {
+                if (input == null) {
+                    return null;
+                } else {
+                    return input.name();
+                }
+            }
+        };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
index 66a283c..58accbc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
@@ -45,14 +45,17 @@ public class BaseCipher implements Cipher {
         this.transformation = transformation;
     }
 
+    @Override
     public int getIVSize() {
         return ivsize;
     }
 
+    @Override
     public int getBlockSize() {
         return bsize;
     }
 
+    @Override
     public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
         key = resize(key, bsize);
         iv = resize(iv, ivsize);
@@ -68,6 +71,7 @@ public class BaseCipher implements Cipher {
         }
     }
 
+    @Override
     public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
         cipher.update(input, inputOffset, inputLen, input, inputOffset);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
index da8550d..68db3cd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
@@ -25,20 +25,24 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.sshd.common.Cipher;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.NamedFactoriesListParseResult;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Provides easy access to the currently implemented ciphers
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
+public enum BuiltinCiphers implements CipherFactory {
     none(Constants.NONE, 0, 0, "None", "None") {
         @Override
         public Cipher create() {
@@ -149,6 +153,52 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
 
     public static final Set<BuiltinCiphers> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinCiphers.class));
+    private static final Map<String,CipherFactory>   extensions =
+            new TreeMap<String,CipherFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(CipherFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<CipherFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final NamedFactory<Cipher> unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
 
     /**
      * @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -222,10 +272,10 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
             return ParseResult.EMPTY;
         }
         
-        List<NamedFactory<Cipher>>  factories=new ArrayList<NamedFactory<Cipher>>(ciphers.size());
-        List<String>                unknown=Collections.<String>emptyList();
+        List<CipherFactory> factories=new ArrayList<CipherFactory>(ciphers.size());
+        List<String>        unknown=Collections.<String>emptyList();
         for (String name : ciphers) {
-            BuiltinCiphers  c=fromFactoryName(name);
+            CipherFactory  c=resolveFactory(name);
             if (c != null) {
                 factories.add(c);
             } else {
@@ -241,13 +291,33 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
     }
 
     /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final CipherFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        CipherFactory  c=fromFactoryName(name);
+        if (c != null) {
+            return c;
+        }
+        
+        synchronized(extensions) {
+            return extensions.get(name);
+        }
+    }
+
+    /**
      * Holds the result of {@link BuiltinCiphers#parseCiphersList(String)}
      * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
      */
-    public static final class ParseResult extends NamedFactoriesListParseResult<Cipher,NamedFactory<Cipher>> {
-        public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Cipher>>emptyList(), Collections.<String>emptyList());
+    public static final class ParseResult extends NamedFactoriesListParseResult<Cipher,CipherFactory> {
+        public static final ParseResult EMPTY=new ParseResult(Collections.<CipherFactory>emptyList(), Collections.<String>emptyList());
         
-        public ParseResult(List<NamedFactory<Cipher>> parsed, List<String> unsupported) {
+        public ParseResult(List<CipherFactory> parsed, List<String> unsupported) {
             super(parsed, unsupported);
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
new file mode 100644
index 0000000..e536ac0
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cipher;
+
+import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CipherFactory extends NamedFactory<Cipher>, OptionalFeature {
+    // required because of generics issues
+    Transformer<CipherFactory,NamedFactory<Cipher>> FAC2NAMED=new Transformer<CipherFactory,NamedFactory<Cipher>>() {
+        @Override
+        public NamedFactory<Cipher> transform(CipherFactory input) {
+            return input;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
index 3484d93..41f467c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
@@ -19,7 +19,6 @@
 package org.apache.sshd.common.cipher;
 
 import org.apache.sshd.common.Cipher;
-import org.apache.sshd.common.NamedFactory;
 
 /**
  * Represents a no-op cipher.
@@ -31,18 +30,22 @@ import org.apache.sshd.common.NamedFactory;
  */
 public class CipherNone implements Cipher {
 
+    @Override
     public int getIVSize() {
         return 8;
     }
 
+    @Override
     public int getBlockSize() {
         return 16;
     }
 
+    @Override
     public void init(Mode mode, byte[] bytes, byte[] bytes1) throws Exception {
         // ignored - always succeeds
     }
 
+    @Override
     public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
         // ignored - always succeeds
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
index 927b94c..14d979b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -45,9 +45,9 @@ public class ECCurves {
 
     private static final Map<Integer, String> CURVE_SIZES = new TreeMap<Integer, String>();
     static {
-        CURVE_SIZES.put(256, NISTP256);
-        CURVE_SIZES.put(384, NISTP384);
-        CURVE_SIZES.put(521, NISTP521);
+        CURVE_SIZES.put(Integer.valueOf(256), NISTP256);
+        CURVE_SIZES.put(Integer.valueOf(384), NISTP384);
+        CURVE_SIZES.put(Integer.valueOf(521), NISTP521);
     }
 
     public static String getCurveName(ECParameterSpec params) {
@@ -60,11 +60,7 @@ public class ECCurves {
     }
 
     public static String getCurveName(int fieldSize) {
-        String curveName = CURVE_SIZES.get(fieldSize);
-        if (curveName == null) {
-            return null;
-        }
-        return curveName;
+        return CURVE_SIZES.get(Integer.valueOf(fieldSize));
     }
 
     public static int getCurveSize(ECParameterSpec params) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
index bb0c8f8..95128b8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
@@ -19,18 +19,27 @@
 
 package org.apache.sshd.common.compression;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFeature {
+public enum BuiltinCompressions implements CompressionFactory {
     none(Constants.NONE) {
             @Override
             public Compression create() {
@@ -62,6 +71,7 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
         return getName();
     }
 
+    @Override
     public final boolean isSupported() {
         return true;
     }
@@ -70,8 +80,55 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
         name = n;
     }
 
-    public static final Set<BuiltinCompressions> VALUES=
+    public static final Set<BuiltinCompressions> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinCompressions.class));
+    private static final Map<String,CompressionFactory>   extensions =
+            new TreeMap<String,CompressionFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(CompressionFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<CompressionFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final CompressionFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
+
     public static final BuiltinCompressions fromFactoryName(String name) {
         if (GenericUtils.isEmpty(name)) {
             return null;
@@ -85,6 +142,76 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
         
         return null;
     }
+    /**
+     * @param Compressions A comma-separated list of Compressions' names - ignored
+     * if {@code null}/empty
+     * @return A {@link ParseResult} containing the successfully parsed
+     * factories and the unknown ones. <B>Note:</B> it is up to caller to
+     * ensure that the lists do not contain duplicates
+     */
+    public static final ParseResult parseCompressionsList(String Compressions) {
+        return parseCompressionsList(GenericUtils.split(Compressions, ','));
+    }
+
+    public static final ParseResult parseCompressionsList(String ... Compressions) {
+        return parseCompressionsList(GenericUtils.isEmpty((Object[]) Compressions) ? Collections.<String>emptyList() : Arrays.asList(Compressions));
+    }
+
+    public static final ParseResult parseCompressionsList(Collection<String> Compressions) {
+        if (GenericUtils.isEmpty(Compressions)) {
+            return ParseResult.EMPTY;
+        }
+        
+        List<CompressionFactory>    factories=new ArrayList<CompressionFactory>(Compressions.size());
+        List<String>                unknown=Collections.<String>emptyList();
+        for (String name : Compressions) {
+            CompressionFactory  c=resolveFactory(name);
+            if (c != null) {
+                factories.add(c);
+            } else {
+                // replace the (unmodifiable) empty list with a real one
+                if (unknown.isEmpty()) {
+                    unknown = new ArrayList<String>();
+                }
+                unknown.add(name);
+            }
+        }
+        
+        return new ParseResult(factories, unknown);
+    }
+
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final CompressionFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        CompressionFactory  c=fromFactoryName(name);
+        if (c != null) {
+            return c;
+        }
+        
+        synchronized(extensions) {
+            return extensions.get(name);
+        }
+    }
+
+    /**
+     * Holds the result of {@link BuiltinCompressions#parseCompressionsList(String)}
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    public static final class ParseResult extends NamedFactoriesListParseResult<Compression,CompressionFactory> {
+        public static final ParseResult EMPTY=new ParseResult(Collections.<CompressionFactory>emptyList(), Collections.<String>emptyList());
+        
+        public ParseResult(List<CompressionFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
+        }
+    }
+
     public static final class Constants {
         public static final String  NONE="none";
         public static final String  ZLIB="zlib";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
index 231c214..5c4e1a0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
@@ -34,6 +34,7 @@ public class CompressionDelayedZlib extends CompressionZlib {
         super();
     }
 
+    @Override
     public boolean isDelayed() {
         return true;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
new file mode 100644
index 0000000..3af29d7
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
@@ -0,0 +1,37 @@
+/*
+ * 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.compression;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CompressionFactory extends NamedFactory<Compression>, OptionalFeature {
+    // required because of generics issues
+    Transformer<CompressionFactory,NamedFactory<Compression>> FAC2NAMED=new Transformer<CompressionFactory,NamedFactory<Compression>>() {
+        @Override
+        public NamedFactory<Compression> transform(CompressionFactory input) {
+            return input;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
index 1e8e921..5e9a56c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
@@ -43,15 +43,18 @@ public class CompressionZlib implements Compression {
     public CompressionZlib() {
     }
 
+    @Override
     public boolean isDelayed() {
         return false;
     }
 
+    @Override
     public void init(Type type, int level) {
         compresser = new Deflater(level);
         decompresser = new Inflater();
     }
 
+    @Override
     public void compress(Buffer buffer) throws IOException {
         compresser.setInput(buffer.array(), buffer.rpos(), buffer.available());
         buffer.wpos(buffer.rpos());
@@ -61,6 +64,7 @@ public class CompressionZlib implements Compression {
         }
     }
 
+    @Override
     public void uncompress(Buffer from, Buffer to) throws IOException {
         decompresser.setInput(from.array(), from.rpos(), from.available());
         int len;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
index bb94f57..fcad285 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
@@ -26,6 +26,7 @@ import java.util.Set;
 import org.apache.sshd.common.NamedFactory;
 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.util.GenericUtils;
 
 /**
@@ -33,12 +34,12 @@ import org.apache.sshd.common.util.GenericUtils;
  * actual {@link NamedFactory} for the {@link Compression}.
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum CompressionConfigValue implements NamedFactory<Compression> {
+public enum CompressionConfigValue implements CompressionFactory {
     YES(BuiltinCompressions.zlib),
     NO(BuiltinCompressions.none),
     DELAYED(BuiltinCompressions.delayedZlib);
 
-    private final NamedFactory<Compression> factory;
+    private final CompressionFactory factory;
 
     @Override
     public final String getName() { 
@@ -51,11 +52,16 @@ public enum CompressionConfigValue implements NamedFactory<Compression> {
     }
 
     @Override
+    public boolean isSupported() {
+        return factory.isSupported();
+    }
+
+    @Override
     public final String toString() {
         return getName();
     }
 
-    CompressionConfigValue(NamedFactory<Compression> delegate) {
+    CompressionConfigValue(CompressionFactory delegate) {
         factory = delegate;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
----------------------------------------------------------------------
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 38e0272..e31868c 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
@@ -31,21 +31,33 @@ import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.sshd.SshBuilder.ClientBuilder;
+import org.apache.sshd.SshBuilder.ServerBuilder;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.SshServer;
 import org.apache.sshd.common.AbstractFactoryManager;
 import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.KeyExchange;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.Mac;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.Transformer;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.CipherFactory;
+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.BuiltinDHFactories;
 import org.apache.sshd.common.kex.DHFactory;
 import org.apache.sshd.common.mac.BuiltinMacs;
+import org.apache.sshd.common.mac.MacFactory;
 import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.SignatureFactory;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
@@ -408,10 +420,22 @@ public class SshConfigFileReader {
      * @return The matching {@link NamedFactory} for the configured value.
      * {@code null} if no configuration or unknown name specified 
      */
-    public static final NamedFactory<Compression> getCompression(Properties props) {
+    public static final CompressionFactory getCompression(Properties props) {
         return CompressionConfigValue.fromName((props == null) ? null : props.getProperty(COMPRESSION_PROP));
     }
     
+    public static final <S extends SshServer> S configure(S server, Properties props, boolean lenient, boolean ignoreUnsupported) {
+        configure((AbstractFactoryManager) server, props, lenient, ignoreUnsupported);
+        configureKeyExchanges(server, props, lenient, ServerBuilder.DH2KEX, ignoreUnsupported);
+        return server;
+    }
+
+    public static final <C extends SshClient> C configure(C client, Properties props, boolean lenient, boolean ignoreUnsupported) {
+        configure((AbstractFactoryManager) client, props, lenient, ignoreUnsupported);
+        configureKeyExchanges(client, props, lenient, ClientBuilder.DH2KEX, ignoreUnsupported);
+        return client;
+    }
+
     /**
      * <P>Configures an {@link AbstractFactoryManager} with the values read from
      * some configuration. Currently it configures:</P></BR>
@@ -425,78 +449,158 @@ public class SshConfigFileReader {
      * @param props The {@link Properties} to use for configuration - <B>Note:</B>
      * if any known configuration value has a default and does not appear in the
      * properties, the default is used
-     * @param lenient If {@code true} then any unknown/unsupported configuration
-     * values are ignored. Otherwise an {@link IllegalArgumentException} is thrown
+     * @param lenient If {@code true} then any unknown configuration values are ignored.
+     * Otherwise an {@link IllegalArgumentException} is thrown
+     * @param ignoreUnsupported filter out unsupported configuration values (e.g., ciphers,
+     * key exchanges, etc..). <B>Note:</B> if after filtering out all the unknown
+     * or unsupported values there is an empty configuration exception is thrown
      * @return The configured manager
      */
-    public static final <M extends AbstractFactoryManager> M configure(M manager, Properties props, boolean lenient) {
-        configureCiphers(manager, props, lenient);
-        configureSignatures(manager, props, lenient);
-        configureMacs(manager, props, lenient);
-        configureCompression(manager, props, lenient);
+    public static final <M extends AbstractFactoryManager> M configure(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
+        configureCiphers(manager, props, lenient, ignoreUnsupported);
+        configureSignatures(manager, props, lenient, ignoreUnsupported);
+        configureMacs(manager, props, lenient, ignoreUnsupported);
+        configureCompression(manager, props, lenient, ignoreUnsupported);
 
         return manager;
     }
 
-    public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, Properties props, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
-        return configureCiphers(manager, props.getProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient);
+        return configureCiphers(manager, props.getProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient, ignoreUnsupported);
     }
 
-    public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
 
         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);
-        manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known ciphers(s): %s", value));
+
+        List<NamedFactory<Cipher>>  factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), CipherFactory.FAC2NAMED);
+        manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/unsupported ciphers(s): %s", value));
         return manager;
     }
 
-    public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, Properties props, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
-        return configureSignatures(manager, props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient);
+        return configureSignatures(manager, props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient, ignoreUnsupported);
     }
 
-    public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
 
         BuiltinSignatures.ParseResult   result=BuiltinSignatures.parseSignatureList(value);
         Collection<String>              unsupported=result.getUnsupportedFactories();
         ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported signatures (%s) in %s", unsupported, value);
-        manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known signatures: %s", value));
+        
+        List<NamedFactory<Signature>>   factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), SignatureFactory.FAC2NAMED);
+        manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported signatures: %s", value));
         return manager;
     }
     
-    public static final <M extends AbstractFactoryManager> M configureMacs(M manager, Properties props, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureMacs(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
-        return configureMacs(manager, props.getProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient);
+        return configureMacs(manager, props.getProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient, ignoreUnsupported);
     }
 
-    public static final <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
 
         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);
-        manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known MAC(s): %s", value));
+        
+        List<NamedFactory<Mac>> factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), MacFactory.FAC2NAMED);
+        manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported MAC(s): %s", value));
         return manager;
     }
 
-    // NOTE: if no compression is resolved it is OK since SSH can function without it
-    public static final <M extends AbstractFactoryManager> M configureCompression(M manager, Properties props, boolean lenient) {
+    /**
+     * @param manager The {@link AbstractFactoryManager} to set up (may not be {@code null})
+     * @param props The (non-{@code null}) {@link Properties} containing the configuration
+     * @param lenient If {@code true} then any unknown/unsupported configuration
+     * values are ignored. Otherwise an {@link IllegalArgumentException} is thrown
+     * @param xformer A {@link Transformer} to convert the configured {@link DHFactory}-ies
+     * to {@link NamedFactory}-ies of {@link KeyExchange}
+     * @param ignoreUnsupported Filter out any un-supported configurations - <B>Note:</B>
+     * if after ignoring the unknown and un-supported values the result is an empty
+     * list of factories and exception is thrown
+     * @return The configured manager
+     * @see #KEX_ALGORITHMS_CONFIG_PROP
+     * @see #DEFAULT_KEX_ALGORITHMS
+     */
+    public static final <M extends AbstractFactoryManager> M configureKeyExchanges(
+            M manager, Properties props, boolean lenient, Transformer<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
-        return configureCompression(manager, props.getProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION), lenient);
+        return configureKeyExchanges(manager, props.getProperty(KEX_ALGORITHMS_CONFIG_PROP, DEFAULT_KEX_ALGORITHMS), lenient, xformer, ignoreUnsupported);
     }
 
-    public static final <M extends AbstractFactoryManager> M configureCompression(M manager, String value, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureKeyExchanges(
+            M manager, String value, boolean lenient, Transformer<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkNotNull(xformer, "No DHFactory transformer", GenericUtils.EMPTY_OBJECT_ARRAY);
 
-        NamedFactory<Compression>   factory=CompressionConfigValue.fromName(value);
+        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);
+        
+        List<NamedFactory<KeyExchange>> factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), xformer);
+        manager.setKeyExchangeFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported KEXS(s): %s", value));
+        return manager;
+    }
+
+    /**
+     * Configure the factory manager using one of the known {@link CompressionConfigValue}s.
+     * @param manager The {@link AbstractFactoryManager} to configure
+     * @param props The configuration {@link Properties}
+     * @param lenient If {@code true} and an unknown value is provided then
+     * it is ignored
+     * @param ignoreUnsupported If {@code false} then check if the compression
+     * is currently supported before setting it
+     * @return The configured manager - <B>Note:</B> if the result of filtering due
+     * to lenient mode or ignored unsupported value is empty then no factories are set
+     */
+    public static final <M extends AbstractFactoryManager> M configureCompression(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
+        ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+        
+        String               value=props.getProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION);
+        CompressionFactory   factory=CompressionConfigValue.fromName(value);
         ValidateUtils.checkTrue(lenient || (factory != null), "Unsupported compression value: %s", value);
-        if (factory != null) {
+        if ((factory != null) && factory.isSupported()) {
             manager.setCompressionFactories(Collections.<NamedFactory<Compression>>singletonList(factory));
         }
+        
+        return manager;
+    }
+
+    // accepts BOTH CompressionConfigValue(s) and/or BuiltinCompressions - including extensions
+    public static final <M extends AbstractFactoryManager> M configureCompression(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
+        ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+
+        CompressionFactory   factory=CompressionConfigValue.fromName(value);
+        if (factory != null) {
+            // SSH can work without compression
+            if (ignoreUnsupported || factory.isSupported()) {
+                manager.setCompressionFactories(Collections.<NamedFactory<Compression>>singletonList(factory));
+            }
+        } else { 
+            BuiltinCompressions.ParseResult result=BuiltinCompressions.parseCompressionsList(value);
+            Collection<String>              unsupported=result.getUnsupportedFactories();
+            ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported compressions(s) (%s) in %s", unsupported, value);
+        
+            List<NamedFactory<Compression>> factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), CompressionFactory.FAC2NAMED);
+            // SSH can work without compression
+            if (GenericUtils.size(factories) > 0) {
+                manager.setCompressionFactories(factories);
+            }
+        }
 
         return manager;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
index be29fa2..9503486 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
@@ -26,19 +26,24 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.NamedResourceListParseResult;
 import org.apache.sshd.common.digest.BuiltinDigests;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
+public enum BuiltinDHFactories implements DHFactory {
     dhg1(Constants.DIFFIE_HELLMAN_GROUP1_SHA1) {
         @Override
         public DHG create(Object... params) throws Exception {
@@ -165,6 +170,52 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
 
     public static final Set<BuiltinDHFactories> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinDHFactories.class));
+    private static final Map<String,DHFactory>   extensions = 
+            new TreeMap<String,DHFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(DHFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<DHFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final DHFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
 
     /**
      * @param name The factory name - ignored if {@code null}/empty
@@ -216,7 +267,7 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
         List<DHFactory> factories=new ArrayList<DHFactory>(dhList.size());
         List<String>    unknown=Collections.<String>emptyList();
         for (String name : dhList) {
-            DHFactory  f=fromFactoryName(name);
+            DHFactory  f=resolveFactory(name);
             if (f != null) {
                 factories.add(f);
             } else {
@@ -230,6 +281,25 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
         
         return new ParseResult(factories, unknown);
     }
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final DHFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        DHFactory  s=fromFactoryName(name);
+        if (s != null) {
+            return s;
+        }
+        
+        synchronized(extensions) {
+            return extensions.get(name);
+        }
+    }
 
     /**
      * Represents the result of {@link BuiltinDHFactories#parseDHFactoriesList(String)}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
index ad0e295..81ded7e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
@@ -19,11 +19,12 @@
 package org.apache.sshd.common.kex;
 
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.OptionalFeature;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface DHFactory extends NamedResource {
+public interface DHFactory extends NamedResource, OptionalFeature {
     boolean isGroupExchange();
 
     AbstractDH create(Object... params) throws Exception;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
index 9478a06..77a5006 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
@@ -43,10 +43,12 @@ public class BaseMac implements Mac {
         this.tmp = new byte[defbsize];
     }
 
+    @Override
     public int getBlockSize() {
         return bsize;
     }
 
+    @Override
     public void init(byte[] key) throws Exception {
         if (key.length > defbsize) {
             byte[] tmp = new byte[defbsize];
@@ -59,6 +61,7 @@ public class BaseMac implements Mac {
         mac.init(skey);
     }
 
+    @Override
     public void updateUInt(long i) {
         tmp[0] = (byte) (i >>> 24);
         tmp[1] = (byte) (i >>> 16);
@@ -67,10 +70,12 @@ public class BaseMac implements Mac {
         update(tmp, 0, 4);
     }
 
+    @Override
     public void update(byte foo[], int s, int l) {
         mac.update(foo, s, l);
     }
 
+    @Override
     public void doFinal(byte[] buf, int offset) throws Exception {
         if (bsize != defbsize) {
             mac.doFinal(tmp, 0);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
index 5756dc5..ef920e4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
@@ -25,20 +25,25 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.sshd.common.Digest;
 import org.apache.sshd.common.Mac;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Provides easy access to the currently implemented macs
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
+public enum BuiltinMacs implements MacFactory {
     hmacmd5(Constants.HMAC_MD5) {
         @Override
         public Mac create() {
@@ -99,6 +104,52 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
 
     public static final Set<BuiltinMacs> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinMacs.class));
+    private static final Map<String,MacFactory>   extensions =
+            new TreeMap<String,MacFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(MacFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<MacFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final MacFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
 
     /**
      * @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -172,10 +223,10 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
             return ParseResult.EMPTY;
         }
         
-        List<NamedFactory<Mac>> factories=new ArrayList<NamedFactory<Mac>>(macs.size());
+        List<MacFactory> factories=new ArrayList<MacFactory>(macs.size());
         List<String>            unknown=Collections.<String>emptyList();
         for (String name : macs) {
-            BuiltinMacs  m=fromFactoryName(name);
+            MacFactory   m=resolveFactory(name);
             if (m != null) {
                 factories.add(m);
             } else {
@@ -190,22 +241,31 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
         return new ParseResult(factories, unknown);
     }
 
-    public static final class ParseResult {
-        public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Mac>>emptyList(), Collections.<String>emptyList());
-        private final List<NamedFactory<Mac>> parsed;
-        private final List<String> unsupported;
-        
-        public ParseResult(List<NamedFactory<Mac>> parsed, List<String> unsupported) {
-            this.parsed = parsed;
-            this.unsupported = unsupported;
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final MacFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        MacFactory  m=fromFactoryName(name);
+        if (m != null) {
+            return m;
         }
         
-        public List<NamedFactory<Mac>> getParsedFactories() {
-            return parsed;
+        synchronized(extensions) {
+            return extensions.get(name);
         }
+    }
+
+    public static final class ParseResult extends NamedFactoriesListParseResult<Mac,MacFactory> {
+        public static final ParseResult EMPTY=new ParseResult(Collections.<MacFactory>emptyList(), Collections.<String>emptyList());
         
-        public List<String> getUnsupportedFactories() {
-            return unsupported;
+        public ParseResult(List<MacFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
new file mode 100644
index 0000000..324405e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.mac;
+
+import org.apache.sshd.common.Mac;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MacFactory extends NamedFactory<Mac>, OptionalFeature {
+    // required because of generics issues
+    Transformer<MacFactory,NamedFactory<Mac>> FAC2NAMED=new Transformer<MacFactory,NamedFactory<Mac>>() {
+        @Override
+        public NamedFactory<Mac> transform(MacFactory input) {
+            return input;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
index ba906b6..f129703 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
@@ -38,6 +38,7 @@ public abstract class AbstractSignature implements Signature {
         this.algorithm = algorithm;
     }
 
+    @Override
     public void init(PublicKey pubkey, PrivateKey prvkey) throws Exception {
         signature = SecurityUtils.getSignature(algorithm);
         if (pubkey != null) {
@@ -48,6 +49,7 @@ public abstract class AbstractSignature implements Signature {
         }
     }
 
+    @Override
     public void update(byte[] foo, int off, int len) throws Exception {
         signature.update(foo, off, len);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
index 23f72c0..1da1952 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
@@ -26,22 +26,27 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.sshd.common.Digest;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.Signature;
 import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Provides easy access to the currently implemented signatures
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeature {
+public enum BuiltinSignatures implements SignatureFactory {
     dsa(KeyPairProvider.SSH_DSS) {
         @Override
         public Signature create() {
@@ -120,8 +125,54 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
         return true;
     }
 
-    public static final Set<BuiltinSignatures> VALUES =
+    public static final Set<BuiltinSignatures> VALUES = 
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinSignatures.class));
+    private static final Map<String,SignatureFactory>   extensions = 
+            new TreeMap<String,SignatureFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(SignatureFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<SignatureFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final SignatureFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
 
     /**
      * @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -198,10 +249,10 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
             return ParseResult.EMPTY;
         }
         
-        List<NamedFactory<Signature>>   factories=new ArrayList<NamedFactory<Signature>>(sigs.size());
-        List<String>                    unknown=Collections.<String>emptyList();
+        List<SignatureFactory>  factories=new ArrayList<SignatureFactory>(sigs.size());
+        List<String>            unknown=Collections.<String>emptyList();
         for (String name : sigs) {
-            BuiltinSignatures  s=fromFactoryName(name);
+            SignatureFactory s=resolveFactory(name);
             if (s != null) {
                 factories.add(s);
             } else {
@@ -216,22 +267,35 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
         return new ParseResult(factories, unknown);
     }
 
-    public static final class ParseResult {
-        public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Signature>>emptyList(), Collections.<String>emptyList());
-        private final List<NamedFactory<Signature>> parsed;
-        private final List<String> unsupported;
-        
-        public ParseResult(List<NamedFactory<Signature>> parsed, List<String> unsupported) {
-            this.parsed = parsed;
-            this.unsupported = unsupported;
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final SignatureFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        SignatureFactory  s=fromFactoryName(name);
+        if (s != null) {
+            return s;
         }
         
-        public List<NamedFactory<Signature>> getParsedFactories() {
-            return parsed;
+        synchronized(extensions) {
+            return extensions.get(name);
         }
+    }
+
+    /**
+     * Holds the result of the {@link BuiltinSignatures#parseSignatureList(String)}
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    public static final class ParseResult extends NamedFactoriesListParseResult<Signature,SignatureFactory> {
+        public static final ParseResult EMPTY=new ParseResult(Collections.<SignatureFactory>emptyList(), Collections.<String>emptyList());
         
-        public List<String> getUnsupportedFactories() {
-            return unsupported;
+        public ParseResult(List<SignatureFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
index d682b3a..b6ec18c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
@@ -30,6 +30,7 @@ public class SignatureDSA extends AbstractSignature {
 	    super(algorithm);
     }
 
+    @Override
     public byte[] sign() throws Exception {
         byte[] sig = signature.sign();
 
@@ -63,6 +64,7 @@ public class SignatureDSA extends AbstractSignature {
         return result;
     }
 
+    @Override
     public boolean verify(byte[] sig) throws Exception {
         sig = extractSig(sig);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
index b1e5a07..5801801 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
@@ -20,12 +20,6 @@ package org.apache.sshd.common.signature;
 
 import java.io.IOException;
 import java.math.BigInteger;
-import java.security.spec.ECParameterSpec;
-
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Signature;
-import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.util.Buffer;
 
 /**
@@ -39,6 +33,7 @@ public class SignatureECDSA extends AbstractSignature {
         super(algo);
     }
 
+    @Override
     public byte[] sign() throws Exception {
         byte[] sig = signature.sign();
 
@@ -73,6 +68,7 @@ public class SignatureECDSA extends AbstractSignature {
         return rsBuf.getCompactData();
     }
 
+    @Override
     public boolean verify(byte[] sig) throws Exception {
         sig = extractSig(sig);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
new file mode 100644
index 0000000..99510e6
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.signature;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SignatureFactory extends NamedFactory<Signature>, OptionalFeature {
+    // required because of generics issues
+    Transformer<SignatureFactory,NamedFactory<Signature>> FAC2NAMED=new Transformer<SignatureFactory,NamedFactory<Signature>>() {
+        @Override
+        public NamedFactory<Signature> transform(SignatureFactory input) {
+            return input;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
index 5c3558a..1cb117e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
@@ -18,10 +18,6 @@
  */
 package org.apache.sshd.common.signature;
 
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Signature;
-
 /**
  * RSA <code>Signature</code>
  *
@@ -33,10 +29,12 @@ public class SignatureRSA extends AbstractSignature {
         super("SHA1withRSA");
     }
 
+    @Override
     public byte[] sign() throws Exception {
         return signature.sign();
     }
 
+    @Override
     public boolean verify(byte[] sig) throws Exception {
         sig = extractSig(sig);
         return signature.verify(sig);