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 2018/09/06 16:03:58 UTC

[48/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
new file mode 100644
index 0000000..6677685
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
@@ -0,0 +1,482 @@
+/*
+ * 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.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.TreeMap;
+
+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 final class PropertyResolverUtils {
+    private PropertyResolverUtils() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    /**
+     * @param resolver     The {@link PropertyResolver} instance - ignored if {@code null}
+     * @param name         The property name
+     * @param defaultValue The default value to return if the specified property
+     *                     does not exist in the properties map
+     * @return The resolved property
+     * @throws NumberFormatException if malformed value
+     * @see #toLong(Object, long)
+     */
+    public static long getLongProperty(PropertyResolver resolver, String name, long defaultValue) {
+        return toLong(resolvePropertyValue(resolver, name), defaultValue);
+    }
+
+    public static long getLongProperty(Map<String, ?> props, String name, long defaultValue) {
+        return toLong(resolvePropertyValue(props, name), defaultValue);
+    }
+
+    /**
+     * Converts a generic object value to a {@code long} if possible:
+     * <UL>
+     *      <LI>
+     *      If value is {@code null} the default is returned
+     *      </LI>
+     *
+     *      <LI>
+     *      If value is a {@link Number} then its {@link Number#longValue()} is returned
+     *      </LI>
+     *
+     *      <LI>
+     *      Otherwise, the value's {@link #toString()} is parsed as a {@code long}
+     *      </LI>
+     * </UL>
+     *
+     * @param value         The resolved value - may be {@code null}
+     * @param defaultValue  The default to use if {@code null} resolved value
+     * @return The resolved value
+     * @throws NumberFormatException if malformed value
+     * @see Long#parseLong(String)
+     */
+    public static long toLong(Object value, long defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        } else if (value instanceof Number) {
+            return ((Number) value).longValue();
+        } else {    // we parse the string in case it is not a valid long value
+            return Long.parseLong(value.toString());
+        }
+    }
+
+    /**
+     * @param resolver The {@link PropertyResolver} instance - ignored if {@code null}
+     * @param name     The property name
+     * @return The {@link Long} value or {@code null} if property not found
+     * @throws NumberFormatException if malformed value
+     * @see #toLong(Object)
+     */
+    public static Long getLong(PropertyResolver resolver, String name) {
+        return toLong(resolvePropertyValue(resolver, name));
+    }
+
+    public static Long getLong(Map<String, ?> props, String name) {
+        return toLong(resolvePropertyValue(props, name));
+    }
+
+    /**
+     * Converts a generic object into a {@link Long}:
+     * <UL>
+     *      <LI>
+     *      If the value is {@code null} then returns {@code null}.
+     *      </LI>
+     *
+     *      <LI>
+     *      If the value is already a {@link Long} then it is returned as such.
+     *      </LI>
+
+     *      <LI>
+     *      If value is a {@link Number} then its {@link Number#longValue()} is
+     *      wrapped as a {@link Long}
+     *      </LI>
+     *
+     *      <LI>
+     *      Otherwise, the value's {@link #toString()} is parsed as a {@link Long}
+     *      </LI>
+     * </UL>
+     *
+     * @param value The resolved value - may be {@code null}
+     * @return The {@link Long} value or {@code null} if property not found
+     * @throws NumberFormatException if malformed value
+     * @see Long#valueOf(long)
+     * @see Long#valueOf(String)
+     */
+    public static Long toLong(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Long) {
+            return (Long) value;
+        } else if (value instanceof Number) {
+            return ((Number) value).longValue();
+        } else {    // we parse the string in case it is not a valid long value
+            return Long.valueOf(value.toString());
+        }
+    }
+
+    /**
+     * Converts an enumerated configuration value:
+     * <UL>
+     *      <P><LI>
+     *      If value is {@code null} then return {@code null}
+     *      </LI></P>
+     *
+     *      <P><LI>
+     *      If value already of the expected type then simply
+     *      cast and return it.
+     *      </LI></P>
+     *
+     *      <P><LI>
+     *      If value is a {@link CharSequence} then convert it
+     *      to a string and look for a matching enumerated value
+     *      name - case <U>insensitive</U>.
+     *      </LI></P>>
+     * </UL>
+     *
+     * @param <E> Type of enumerated value
+     * @param enumType The enumerated class type
+     * @param value The configured value - ignored if {@code null}
+     * @param failIfNoMatch Whether to fail if no matching name found
+     * @param available The available values to compare the name
+     * @return The matching enumerated value - {@code null} if no match found
+     * @throws IllegalArgumentException If value is neither {@code null},
+     * nor the enumerated type nor a {@link CharSequence}
+     * @throws NoSuchElementException If no matching string name found and
+     * <tt>failIfNoMatch</tt> is {@code true}
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> enumType, Object value, boolean failIfNoMatch, Collection<E> available) {
+        if (value == null) {
+            return null;
+        } else if (enumType.isInstance(value)) {
+            return enumType.cast(value);
+        } else if (value instanceof CharSequence) {
+            String name = value.toString();
+            if (GenericUtils.size(available) > 0) {
+                for (E v : available) {
+                    if (name.equalsIgnoreCase(v.name())) {
+                        return v;
+                    }
+                }
+            }
+
+            if (failIfNoMatch) {
+                throw new NoSuchElementException("No match found for " + enumType.getSimpleName() + "[" + name + "]");
+            }
+
+            return null;
+        } else {
+            throw new IllegalArgumentException("Bad value type for enum conversion: " + value.getClass().getSimpleName());
+        }
+    }
+
+    public static Object updateProperty(PropertyResolver resolver, String name, long value) {
+        return updateProperty(resolver.getProperties(), name, value);
+    }
+
+    public static Object updateProperty(Map<String, Object> props, String name, long value) {
+        return updateProperty(props, name, Long.valueOf(value));
+    }
+
+    public static int getIntProperty(PropertyResolver resolver, String name, int defaultValue) {
+        return toInteger(resolvePropertyValue(resolver, name), defaultValue);
+    }
+
+    public static int getIntProperty(Map<String, ?> props, String name, int defaultValue) {
+        return toInteger(resolvePropertyValue(props, name), defaultValue);
+    }
+
+    public static int toInteger(Object value, int defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        } else if (value instanceof Number) {
+            return ((Number) value).intValue();
+        } else {    // we parse the string in case this is NOT an integer
+            return Integer.parseInt(value.toString());
+        }
+    }
+
+    public static Integer getInteger(PropertyResolver resolver, String name) {
+        return toInteger(resolvePropertyValue(resolver, name));
+    }
+
+    public static Integer getInteger(Map<String, ?> props, String name) {
+        return toInteger(resolvePropertyValue(props, name));
+    }
+
+    public static Integer toInteger(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Integer) {
+            return (Integer) value;
+        } else if (value instanceof Number) {
+            return ((Number) value).intValue();
+        } else {    // we parse the string in case this is NOT an integer
+            return Integer.valueOf(value.toString());
+        }
+    }
+
+    public static Object updateProperty(PropertyResolver resolver, String name, int value) {
+        return updateProperty(resolver.getProperties(), name, value);
+    }
+
+    public static Object updateProperty(Map<String, Object> props, String name, int value) {
+        return updateProperty(props, name, Integer.valueOf(value));
+    }
+
+    public static boolean getBooleanProperty(PropertyResolver resolver, String name, boolean defaultValue) {
+        return toBoolean(getObject(resolver, name), defaultValue);
+    }
+
+    public static boolean getBooleanProperty(Map<String, ?> props, String name, boolean defaultValue) {
+        return toBoolean(getObject(props, name), defaultValue);
+    }
+
+    public static boolean toBoolean(Object value, boolean defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        } else {
+            return toBoolean(value);
+        }
+    }
+
+    public static Boolean getBoolean(PropertyResolver resolver, String name) {
+        return toBoolean(resolvePropertyValue(resolver, name));
+    }
+
+    public static Boolean getBoolean(Map<String, ?> props, String name) {
+        return toBoolean(resolvePropertyValue(props, name));
+    }
+
+    public static Boolean toBoolean(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Boolean) {
+            return (Boolean) value;
+        } else {
+            return Boolean.valueOf(value.toString());
+        }
+    }
+
+    public static Object updateProperty(PropertyResolver resolver, String name, boolean value) {
+        return updateProperty(resolver.getProperties(), name, value);
+    }
+
+    public static Object updateProperty(Map<String, Object> props, String name, boolean value) {
+        return updateProperty(props, name, Boolean.valueOf(value));
+    }
+
+    /**
+     * @param resolver     The {@link PropertyResolver} to use - ignored if {@code null}
+     * @param name         The property name
+     * @param defaultValue The default value to return if property not set or empty
+     * @return The set value (if not {@code null}/empty) or default one
+     */
+    public static String getStringProperty(PropertyResolver resolver, String name, String defaultValue) {
+        String value = getString(resolver, name);
+        if (GenericUtils.isEmpty(value)) {
+            return defaultValue;
+        } else {
+            return value;
+        }
+    }
+
+    public static String getStringProperty(Map<String, ?> props, String name, String defaultValue) {
+        Object value = resolvePropertyValue(props, name);
+        if (value == null) {
+            return defaultValue;
+        } else {
+            return Objects.toString(value);
+        }
+    }
+
+    public static Charset getCharset(PropertyResolver resolver, String name, Charset defaultValue) {
+        Object value = getObject(resolver, name);
+        return (value == null) ? defaultValue : toCharset(value);
+    }
+
+    public static Charset getCharset(Map<String, ?> props, String name, Charset defaultValue) {
+        Object value = getObject(props, name);
+        return (value == null) ? defaultValue : toCharset(value);
+    }
+
+    public static Charset toCharset(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Charset) {
+            return (Charset) value;
+        } else if (value instanceof CharSequence) {
+            return Charset.forName(value.toString());
+        } else {
+            throw new IllegalArgumentException("Invalid charset conversion value: " + value);
+        }
+    }
+
+    public static String getString(PropertyResolver resolver, String name) {
+        Object value = getObject(resolver, name);
+        return Objects.toString(value, null);
+    }
+
+    public static String getString(Map<String, ?> props, String name) {
+        Object value = getObject(props, name);
+        return Objects.toString(value, null);
+    }
+
+    public static Object getObject(PropertyResolver resolver, String name) {
+        return resolvePropertyValue(resolver, name);
+    }
+
+    // for symmetrical reasons...
+    public static Object getObject(Map<String, ?> props, String name) {
+        return resolvePropertyValue(props, name);
+    }
+
+    public static Object resolvePropertyValue(Map<String, ?> props, String name) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        return props != null ? props.get(key) : null;
+    }
+
+    /**
+     * @param resolver The {@link PropertyResolver} instance
+     * @param name     The property name
+     * @param value    The new value - if {@code null} or an empty {@link CharSequence}
+     *                 the property is <U>removed</U>
+     * @return The previous value - {@code null} if none
+     */
+    public static Object updateProperty(PropertyResolver resolver, String name, Object value) {
+        return updateProperty(resolver.getProperties(), name, value);
+    }
+
+    public static Object updateProperty(Map<String, Object> props, String name, Object value) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        if ((value == null) || ((value instanceof CharSequence) && GenericUtils.isEmpty((CharSequence) value))) {
+            return props.remove(key);
+        } else {
+            return props.put(key, value);
+        }
+    }
+
+    /**
+     * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
+     * for the requested property or reached top. If still no value found and the key
+     * starts with &quot;org.apache.sshd&quot; then the system properties are also
+     * consulted
+     *
+     * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
+     * @param name     The requested property name
+     * @return The found value or {@code null}
+     */
+    public static Object resolvePropertyValue(PropertyResolver resolver, String name) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        for (PropertyResolver r = resolver; r != null; r = r.getParentPropertyResolver()) {
+            Map<String, ?> props = r.getProperties();
+            if (props != null) {
+                Object value = props.get(key);
+                if (value != null) {
+                    return value;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
+     * for the requested property or reached top.
+     *
+     * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
+     * @param name     The requested property name
+     * @return The found properties {@link Map} or {@code null}
+     */
+    public static Map<String, Object> resolvePropertiesSource(PropertyResolver resolver, String name) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        for (PropertyResolver r = resolver; r != null; r = r.getParentPropertyResolver()) {
+            Map<String, Object> props = r.getProperties();
+            if (props != null) {
+                Object value = props.get(key);
+                if (value != null) {
+                    return props;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public static PropertyResolver toPropertyResolver(Properties props) {
+        if (GenericUtils.isEmpty(props)) {
+            return PropertyResolver.EMPTY;
+        }
+
+        Map<String, Object> propsMap = new TreeMap<>(Comparator.naturalOrder());
+        Collection<String> names = props.stringPropertyNames();
+        for (String key : names) {
+            String value = props.getProperty(key);
+            if (value == null) {
+                continue;
+            }
+            propsMap.put(key, value);
+        }
+
+        return toPropertyResolver(propsMap);
+    }
+
+    /**
+     * Wraps a {@link Map} into a {@link PropertyResolver} so it can be used
+     * with these utilities
+     *
+     * @param props The properties map - may be {@code null}/empty if no properties
+     * are updated
+     * @return The resolver wrapper
+     */
+    public static PropertyResolver toPropertyResolver(Map<String, Object> props) {
+        return toPropertyResolver(props, null);
+    }
+
+    public static PropertyResolver toPropertyResolver(Map<String, Object> props, PropertyResolver parent) {
+        return new PropertyResolver() {
+            @Override
+            public PropertyResolver getParentPropertyResolver() {
+                return parent;
+            }
+
+            @Override
+            public Map<String, Object> getProperties() {
+                return props;
+            }
+
+            @Override
+            public String toString() {
+                return Objects.toString(props);
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java b/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
new file mode 100644
index 0000000..8c9164f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common;
+
+/**
+ * Exception used in the SSH client or server.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RuntimeSshException extends RuntimeException {
+    private static final long serialVersionUID = -2423550196146939503L;
+
+    public RuntimeSshException() {
+        this(null, null);
+    }
+
+    public RuntimeSshException(String message) {
+        this(message, null);
+    }
+
+    public RuntimeSshException(Throwable cause) {
+        this(null, cause);
+    }
+
+    public RuntimeSshException(String message, Throwable cause) {
+        super(message);
+        if (cause != null) {
+            initCause(cause);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
new file mode 100644
index 0000000..01e8ae7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
@@ -0,0 +1,245 @@
+/*
+ * 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.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.LoggingUtils;
+
+/**
+ * This interface defines constants for the SSH protocol.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SshConstants {
+    //
+    // SSH message identifiers
+    //
+
+    public static final byte SSH_MSG_DISCONNECT = 1;
+    public static final byte SSH_MSG_IGNORE = 2;
+    public static final byte SSH_MSG_UNIMPLEMENTED = 3;
+    public static final byte SSH_MSG_DEBUG = 4;
+    public static final byte SSH_MSG_SERVICE_REQUEST = 5;
+    public static final byte SSH_MSG_SERVICE_ACCEPT = 6;
+    public static final byte SSH_MSG_KEXINIT = 20;
+    public static final int MSG_KEX_COOKIE_SIZE = 16;
+    public static final byte SSH_MSG_NEWKEYS = 21;
+
+    public static final byte SSH_MSG_KEX_FIRST = 30;
+    public static final byte SSH_MSG_KEX_LAST = 49;
+
+    public static final byte SSH_MSG_KEXDH_INIT = 30;
+    public static final byte SSH_MSG_KEXDH_REPLY = 31;
+
+    public static final byte SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
+    public static final byte SSH_MSG_KEX_DH_GEX_GROUP = 31;
+    public static final byte SSH_MSG_KEX_DH_GEX_INIT = 32;
+    public static final byte SSH_MSG_KEX_DH_GEX_REPLY = 33;
+    public static final byte SSH_MSG_KEX_DH_GEX_REQUEST = 34;
+
+    public static final byte SSH_MSG_USERAUTH_REQUEST = 50;
+    public static final byte SSH_MSG_USERAUTH_FAILURE = 51;
+    public static final byte SSH_MSG_USERAUTH_SUCCESS = 52;
+    public static final byte SSH_MSG_USERAUTH_BANNER = 53;
+
+    public static final byte SSH_MSG_USERAUTH_INFO_REQUEST = 60;
+    public static final byte SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
+
+    public static final byte SSH_MSG_USERAUTH_PK_OK = 60;
+
+    public static final byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60;
+
+    public static final byte SSH_MSG_USERAUTH_GSSAPI_MIC = 66;
+
+    public static final byte SSH_MSG_GLOBAL_REQUEST = 80;
+    public static final byte SSH_MSG_REQUEST_SUCCESS = 81;
+    public static final byte SSH_MSG_REQUEST_FAILURE = 82;
+    public static final byte SSH_MSG_CHANNEL_OPEN = 90;
+    public static final byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
+    public static final byte SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
+    public static final byte SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
+    public static final byte SSH_MSG_CHANNEL_DATA = 94;
+    public static final byte SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
+    public static final byte SSH_MSG_CHANNEL_EOF = 96;
+    public static final byte SSH_MSG_CHANNEL_CLOSE = 97;
+    public static final byte SSH_MSG_CHANNEL_REQUEST = 98;
+    public static final byte SSH_MSG_CHANNEL_SUCCESS = 99;
+    public static final byte SSH_MSG_CHANNEL_FAILURE = 100;
+
+    //
+    // Disconnect error codes
+    //
+    public static final int SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1;
+    public static final int SSH2_DISCONNECT_PROTOCOL_ERROR = 2;
+    public static final int SSH2_DISCONNECT_KEY_EXCHANGE_FAILED = 3;
+    public static final int SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED = 4;
+    public static final int SSH2_DISCONNECT_RESERVED = 4;
+    public static final int SSH2_DISCONNECT_MAC_ERROR = 5;
+    public static final int SSH2_DISCONNECT_COMPRESSION_ERROR = 6;
+    public static final int SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE = 7;
+    public static final int SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8;
+    public static final int SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9;
+    public static final int SSH2_DISCONNECT_CONNECTION_LOST = 10;
+    public static final int SSH2_DISCONNECT_BY_APPLICATION = 11;
+    public static final int SSH2_DISCONNECT_TOO_MANY_CONNECTIONS = 12;
+    public static final int SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER = 13;
+    public static final int SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14;
+    public static final int SSH2_DISCONNECT_ILLEGAL_USER_NAME = 15;
+
+    //
+    // Open error codes
+    //
+
+    public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
+    public static final int SSH_OPEN_CONNECT_FAILED = 2;
+    public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
+    public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
+
+    // Some more constants
+    public static final int SSH_EXTENDED_DATA_STDERR = 1;   // see RFC4254 section 5.2
+    public static final int SSH_PACKET_HEADER_LEN = 5;  // 32-bit length + 8-bit pad length
+
+    private SshConstants() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    private static final class LazyAmbiguousOpcodesHolder {
+        private static final Set<Integer> AMBIGUOUS_OPCODES =
+            Collections.unmodifiableSet(
+                new HashSet<>(
+                    LoggingUtils.getAmbiguousMenmonics(SshConstants.class, "SSH_MSG_").values()));
+
+        private LazyAmbiguousOpcodesHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * @param cmd The command value
+     * @return {@code true} if this value is used by several <U>different</U> messages
+     * @see #getAmbiguousOpcodes()
+     */
+    public static boolean isAmbiguousOpcode(int cmd) {
+        Collection<Integer> ambiguousOpcodes = getAmbiguousOpcodes();
+        return ambiguousOpcodes.contains(cmd);
+    }
+
+    /**
+     * @return A {@link Set} of opcodes that are used by several <U>different</U> messages
+     */
+    @SuppressWarnings("synthetic-access")
+    public static Set<Integer> getAmbiguousOpcodes() {
+        return LazyAmbiguousOpcodesHolder.AMBIGUOUS_OPCODES;
+    }
+
+    private static final class LazyMessagesMapHolder {
+        private static final Map<Integer, String> MESSAGES_MAP =
+            LoggingUtils.generateMnemonicMap(SshConstants.class, f -> {
+                String name = f.getName();
+                if (!name.startsWith("SSH_MSG_")) {
+                    return false;
+                }
+
+                try {
+                    return !isAmbiguousOpcode(f.getByte(null));
+                } catch (Exception e) {
+                    return false;
+                }
+            });
+
+        private LazyMessagesMapHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * Converts a command value to a user-friendly name
+     *
+     * @param cmd The command value
+     * @return The user-friendly name - if not one of the defined {@code SSH_MSG_XXX}
+     * values then returns the string representation of the command's value
+     */
+    public static String getCommandMessageName(int cmd) {
+        @SuppressWarnings("synthetic-access")
+        String name = LazyMessagesMapHolder.MESSAGES_MAP.get(cmd);
+        if (GenericUtils.isEmpty(name)) {
+            return Integer.toString(cmd);
+        } else {
+            return name;
+        }
+    }
+
+    private static final class LazyReasonsMapHolder {
+        private static final Map<Integer, String> REASONS_MAP =
+            LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH2_DISCONNECT_");
+
+        private LazyReasonsMapHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * Converts a disconnect reason value to a user-friendly name
+     *
+     * @param reason The disconnect reason value
+     * @return The user-friendly name - if not one of the defined {@code SSH2_DISCONNECT_}
+     * values then returns the string representation of the reason's value
+     */
+    public static String getDisconnectReasonName(int reason) {
+        @SuppressWarnings("synthetic-access")
+        String name = LazyReasonsMapHolder.REASONS_MAP.get(reason);
+        if (GenericUtils.isEmpty(name)) {
+            return Integer.toString(reason);
+        } else {
+            return name;
+        }
+    }
+
+    private static final class LazyOpenCodesMapHolder {
+        private static final Map<Integer, String> OPEN_CODES_MAP =
+            LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH_OPEN_");
+
+        private LazyOpenCodesMapHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * Converts an open error value to a user-friendly name
+     *
+     * @param code The open error value
+     * @return The user-friendly name - if not one of the defined {@code SSH_OPEN_}
+     * values then returns the string representation of the reason's value
+     */
+    public static String getOpenErrorCodeName(int code) {
+        @SuppressWarnings("synthetic-access")
+        String name = LazyOpenCodesMapHolder.OPEN_CODES_MAP.get(code);
+        if (GenericUtils.isEmpty(name)) {
+            return Integer.toString(code);
+        } else {
+            return name;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SshException.java b/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
new file mode 100644
index 0000000..4280d08
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
@@ -0,0 +1,72 @@
+/*
+ * 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.io.IOException;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Represents an SSH related exception
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshException extends IOException {
+
+    private static final long serialVersionUID = -7349477687125144606L;
+
+    private final int disconnectCode;
+
+    public SshException(String message) {
+        this(message, null);
+    }
+
+    public SshException(Throwable cause) {
+        this(Objects.requireNonNull(cause, "No cause").getMessage(), cause);
+    }
+
+    public SshException(String message, Throwable cause) {
+        this(0, message, cause);
+    }
+
+    public SshException(int disconnectCode) {
+        this(disconnectCode, SshConstants.getDisconnectReasonName(disconnectCode));
+    }
+
+    public SshException(int disconnectCode, String message) {
+        this(disconnectCode, message, null);
+    }
+
+    public SshException(int disconnectCode, Throwable cause) {
+        this(disconnectCode, SshConstants.getDisconnectReasonName(disconnectCode), cause);
+    }
+
+    public SshException(int disconnectCode, String message, Throwable cause) {
+        super(GenericUtils.isEmpty(message) ? SshConstants.getDisconnectReasonName(disconnectCode) : message);
+        this.disconnectCode = disconnectCode;
+        if (cause != null) {
+            initCause(cause);
+        }
+    }
+
+    public int getDisconnectCode() {
+        return disconnectCode;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java b/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
new file mode 100644
index 0000000..4408be2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
@@ -0,0 +1,209 @@
+/*
+ * 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.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.MapEntryUtils;
+
+/**
+ * A wrapper that exposes a read-only {@link Map} access to the system
+ * properties. Any attempt to modify it will throw {@link UnsupportedOperationException}.
+ * The mapper uses the {@link #SYSPROPS_MAPPED_PREFIX} to filter and access'
+ * only these properties, ignoring all others
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SyspropsMapWrapper implements Map<String, Object> {
+    /**
+     * Prefix of properties used by the mapper to identify SSHD related settings
+     */
+    public static final String SYSPROPS_MAPPED_PREFIX = "org.apache.sshd.config";
+
+    /**
+     * The one and only wrapper instance
+     */
+    public static final SyspropsMapWrapper INSTANCE = new SyspropsMapWrapper();
+
+    /**
+     * A {@link PropertyResolver} with no parent that exposes the system properties
+     */
+    public static final PropertyResolver SYSPROPS_RESOLVER = new PropertyResolver() {
+        @Override
+        public Map<String, Object> getProperties() {
+            return SyspropsMapWrapper.INSTANCE;
+        }
+
+        @Override
+        public PropertyResolver getParentPropertyResolver() {
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "SYSPROPS";
+        }
+    };
+
+    private SyspropsMapWrapper() {
+        super();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException("sysprops#clear() N/A");
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return get(key) != null;
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        // not the most efficient implementation, but we do not expect it to be called much
+        Properties props = System.getProperties();
+        for (String key : props.stringPropertyNames()) {
+            if (!isMappedSyspropKey(key)) {
+                continue;
+            }
+
+            Object v = props.getProperty(key);
+            if (Objects.equals(v, value)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public Set<Entry<String, Object>> entrySet() {
+        Properties props = System.getProperties();
+        // return a copy in order to avoid concurrent modifications
+        Set<Entry<String, Object>> entries = new TreeSet<>(MapEntryUtils.byKeyEntryComparator());
+        for (String key : props.stringPropertyNames()) {
+            if (!isMappedSyspropKey(key)) {
+                continue;
+            }
+
+            Object v = props.getProperty(key);
+            if (v != null) {
+                entries.add(new SimpleImmutableEntry<>(getUnmappedSyspropKey(key), v));
+            }
+        }
+
+        return entries;
+    }
+
+    @Override
+    public Object get(Object key) {
+        return (key instanceof String) ? System.getProperty(getMappedSyspropKey(key)) : null;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return GenericUtils.isEmpty(keySet());
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return System.getProperties()
+                .stringPropertyNames().stream()
+                // filter out any non-SSHD properties
+                .filter(SyspropsMapWrapper::isMappedSyspropKey)
+                .map(SyspropsMapWrapper::getUnmappedSyspropKey)
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        throw new UnsupportedOperationException("sysprops#put(" + key + ")[" + value + "] N/A");
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ?> m) {
+        throw new UnsupportedOperationException("sysprops#putAll(" + m + ") N/A");
+    }
+
+    @Override
+    public Object remove(Object key) {
+        throw new UnsupportedOperationException("sysprops#remove(" + key + ") N/A");
+    }
+
+    @Override
+    public int size() {
+        return GenericUtils.size(keySet());
+    }
+
+    @Override
+    public Collection<Object> values() {
+        Properties props = System.getProperties();
+        // return a copy in order to avoid concurrent modifications
+        return props
+                .stringPropertyNames().stream()
+                .filter(SyspropsMapWrapper::isMappedSyspropKey)
+                .map(props::get)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toString(System.getProperties(), null);
+    }
+
+    /**
+     * @param key Key to be tested
+     * @return {@code true} if key starts with {@link #SYSPROPS_MAPPED_PREFIX}
+     * and continues with a dot followed by some characters
+     */
+    public static boolean isMappedSyspropKey(String key) {
+        return (GenericUtils.length(key) > (SYSPROPS_MAPPED_PREFIX.length() + 1))
+            && key.startsWith(SYSPROPS_MAPPED_PREFIX)
+            && (key.charAt(SYSPROPS_MAPPED_PREFIX.length()) == '.');
+    }
+
+    /**
+     * @param key Key to be transformed
+     * @return The &quot;pure&quot; key name if a mapped one, same as input otherwise
+     * @see #isMappedSyspropKey(String)
+     */
+    public static String getUnmappedSyspropKey(Object key) {
+        String s = Objects.toString(key);
+        return isMappedSyspropKey(s) ? s.substring(SYSPROPS_MAPPED_PREFIX.length() + 1 /* skip dot */) : s;
+    }
+
+    /**
+     * @param key The original key
+     * @return A key prefixed by {@link #SYSPROPS_MAPPED_PREFIX}
+     * @see #isMappedSyspropKey(String)
+     */
+    public static String getMappedSyspropKey(Object key) {
+        return SYSPROPS_MAPPED_PREFIX + "." + key;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
new file mode 100644
index 0000000..485f2f2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
@@ -0,0 +1,27 @@
+/*
+ * 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.auth;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MutableUserHolder extends UsernameHolder {
+    void setUsername(String username);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
new file mode 100644
index 0000000..7ee76ad
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
@@ -0,0 +1,32 @@
+/*
+ * 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.auth;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface UsernameHolder {
+    /**
+     * @return The attached username - may be {@code null}/empty if holder
+     * not yet initialized
+     */
+    String getUsername();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
new file mode 100644
index 0000000..c3d9426
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
@@ -0,0 +1,113 @@
+/*
+ * 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 javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Base class for all Cipher implementations delegating to the JCE provider.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseCipher implements Cipher {
+
+    protected javax.crypto.Cipher cipher;
+    private final int ivsize;
+    private final int bsize;
+    private final String algorithm;
+    private final String transformation;
+    private String s;
+
+    public BaseCipher(int ivsize, int bsize, String algorithm, String transformation) {
+        this.ivsize = ivsize;
+        this.bsize = bsize;
+        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
+        this.transformation = ValidateUtils.checkNotNullAndNotEmpty(transformation, "No transformation");
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public String getTransformation() {
+        return 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, getBlockSize());
+        iv = resize(iv, getIVSize());
+        try {
+            cipher = SecurityUtils.getCipher(getTransformation());
+            cipher.init(Mode.Encrypt.equals(mode) ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE,
+                    new SecretKeySpec(key, getAlgorithm()),
+                    new IvParameterSpec(iv));
+        } catch (Exception e) {
+            cipher = null;
+            throw new SshException("Unable to initialize cipher " + this, e);
+        }
+    }
+
+    @Override
+    public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
+        cipher.update(input, inputOffset, inputLen, input, inputOffset);
+    }
+
+    protected static byte[] resize(byte[] data, int size) {
+        if (data.length > size) {
+            byte[] tmp = new byte[size];
+            System.arraycopy(data, 0, tmp, 0, size);
+            data = tmp;
+        }
+        return data;
+    }
+
+    @Override
+    public String toString() {
+        synchronized (this) {
+            if (s == null) {
+                s = getClass().getSimpleName()
+                    + "[" + getAlgorithm()
+                    + "," + getIVSize()
+                    + "," + getBlockSize()
+                    + "," + getTransformation()
+                    + "]";
+            }
+        }
+
+        return s;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
new file mode 100644
index 0000000..2e3fc1e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
@@ -0,0 +1,54 @@
+/*
+ * 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 javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseRC4Cipher extends BaseCipher {
+
+    public static final int SKIP_SIZE = 1536;
+
+    public BaseRC4Cipher(int ivsize, int bsize) {
+        super(ivsize, bsize, "ARCFOUR", "RC4");
+    }
+
+    @Override
+    public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
+        key = resize(key, getBlockSize());
+        try {
+            cipher = SecurityUtils.getCipher(getTransformation());
+            cipher.init(Mode.Encrypt.equals(mode)  ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE,
+                    new SecretKeySpec(key, getAlgorithm()));
+
+            byte[] foo = new byte[1];
+            for (int i = 0; i < SKIP_SIZE; i++) {
+                cipher.update(foo, 0, 1, foo, 0);
+            }
+        } catch (Exception e) {
+            cipher = null;
+            throw e;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
new file mode 100644
index 0000000..8609d50
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
@@ -0,0 +1,348 @@
+/*
+ * 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 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.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.NamedFactory;
+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 CipherFactory {
+    none(Constants.NONE, 0, 0, "None", "None") {
+        @Override
+        public Cipher create() {
+            return new CipherNone();
+        }
+    },
+    aes128cbc(Constants.AES128_CBC, 16, 16, "AES", "AES/CBC/NoPadding"),
+    aes128ctr(Constants.AES128_CTR, 16, 16, "AES", "AES/CTR/NoPadding"),
+    aes192cbc(Constants.AES192_CBC, 16, 24, "AES", "AES/CBC/NoPadding"),
+    aes192ctr(Constants.AES192_CTR, 16, 24, "AES", "AES/CTR/NoPadding"),
+    aes256cbc(Constants.AES256_CBC, 16, 32, "AES", "AES/CBC/NoPadding"),
+    aes256ctr(Constants.AES256_CTR, 16, 32, "AES", "AES/CTR/NoPadding"),
+    arcfour128(Constants.ARCFOUR128, 8, 16, "ARCFOUR", "RC4") {
+        @Override
+        public Cipher create() {
+            return new BaseRC4Cipher(getIVSize(), getBlockSize());
+        }
+    },
+    arcfour256(Constants.ARCFOUR256, 8, 32, "ARCFOUR", "RC4") {
+        @Override
+        public Cipher create() {
+            return new BaseRC4Cipher(getIVSize(), getBlockSize());
+        }
+    },
+    blowfishcbc(Constants.BLOWFISH_CBC, 8, 16, "Blowfish", "Blowfish/CBC/NoPadding"),
+    tripledescbc(Constants.TRIPLE_DES_CBC, 8, 24, "DESede", "DESede/CBC/NoPadding");
+
+    public static final Set<BuiltinCiphers> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(BuiltinCiphers.class));
+
+    private static final Map<String, CipherFactory> EXTENSIONS =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private final String factoryName;
+    private final int ivsize;
+    private final int blocksize;
+    private final int keysize;
+    private final String algorithm;
+    private final String transformation;
+    private final boolean supported;
+
+    BuiltinCiphers(String factoryName, int ivsize, int blocksize, String algorithm, String transformation) {
+        this.factoryName = factoryName;
+        this.ivsize = ivsize;
+        this.blocksize = blocksize;
+        this.keysize = blocksize * Byte.SIZE;
+        this.algorithm = algorithm;
+        this.transformation = transformation;
+        /*
+         * This can be done once since in order to change the support the JVM
+         * needs to be stopped, some unlimited-strength files need be installed
+         * and then the JVM re-started. Therefore, the answer is not going to
+         * change while the JVM is running
+         */
+        this.supported = Constants.NONE.equals(factoryName) || Cipher.checkSupported(this.transformation, this.keysize);
+    }
+
+    @Override
+    public final String getName() {
+        return factoryName;
+    }
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
+    /**
+     * @return {@code true} if the current JVM configuration supports this
+     * cipher - e.g., AES-256 requires the <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/">
+     * Java Cryptography Extension (JCE)</A>
+     */
+    @Override
+    public boolean isSupported() {
+        return supported;
+    }
+
+    /**
+     * @return The key size (in bits) for the cipher
+     */
+    public int getKeySize() {
+        return keysize;
+    }
+
+    @Override
+    public int getIVSize() {
+        return ivsize;
+    }
+
+    @Override
+    public int getBlockSize() {
+        return blocksize;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public String getTransformation() {
+        return transformation;
+    }
+
+    @Override
+    public Cipher create() {
+        return new BaseCipher(getIVSize(), getBlockSize(), getAlgorithm(), getTransformation());
+    }
+
+    /**
+     * 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 void registerExtension(CipherFactory extension) {
+        String name = Objects.requireNonNull(extension, "No extension provided").getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized (EXTENSIONS) {
+            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %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 NavigableSet<CipherFactory> getRegisteredExtensions() {
+        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 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
+     * @return The matching {@link BuiltinCiphers} whose {@link Enum#name()} matches
+     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+     */
+    public static BuiltinCiphers fromString(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        for (BuiltinCiphers c : VALUES) {
+            if (s.equalsIgnoreCase(c.name())) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param factory The {@link NamedFactory} for the cipher - ignored if {@code null}
+     * @return The matching {@link BuiltinCiphers} whose factory name matches
+     * (case <U>insensitive</U>) the cipher factory name
+     * @see #fromFactoryName(String)
+     */
+    public static BuiltinCiphers fromFactory(NamedFactory<Cipher> factory) {
+        if (factory == null) {
+            return null;
+        } else {
+            return fromFactoryName(factory.getName());
+        }
+    }
+
+    /**
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The matching {@link BuiltinCiphers} whose factory name matches
+     * (case <U>insensitive</U>) the provided name - {@code null} if no match
+     */
+    public static BuiltinCiphers fromFactoryName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    /**
+     * @param ciphers A comma-separated list of ciphers' 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 ParseResult parseCiphersList(String ciphers) {
+        return parseCiphersList(GenericUtils.split(ciphers, ','));
+    }
+
+    public static ParseResult parseCiphersList(String... ciphers) {
+        return parseCiphersList(GenericUtils.isEmpty((Object[]) ciphers) ? Collections.emptyList() : Arrays.asList(ciphers));
+    }
+
+    public static ParseResult parseCiphersList(Collection<String> ciphers) {
+        if (GenericUtils.isEmpty(ciphers)) {
+            return ParseResult.EMPTY;
+        }
+
+        List<CipherFactory> factories = new ArrayList<>(ciphers.size());
+        List<String> unknown = Collections.emptyList();
+        for (String name : ciphers) {
+            CipherFactory 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<>();
+                }
+                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 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 class ParseResult extends NamedFactoriesListParseResult<Cipher, CipherFactory> {
+        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+        public ParseResult(List<CipherFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
+        }
+    }
+
+    public static final class Constants {
+        public static final String NONE = "none";
+        public static final Pattern NONE_CIPHER_PATTERN =
+                Pattern.compile("(^|.*,)" + NONE + "($|,.*)");
+
+        public static final String AES128_CBC = "aes128-cbc";
+        public static final String AES128_CTR = "aes128-ctr";
+        public static final String AES192_CBC = "aes192-cbc";
+        public static final String AES192_CTR = "aes192-ctr";
+        public static final String AES256_CBC = "aes256-cbc";
+        public static final String AES256_CTR = "aes256-ctr";
+        public static final String ARCFOUR128 = "arcfour128";
+        public static final String ARCFOUR256 = "arcfour256";
+        public static final String BLOWFISH_CBC = "blowfish-cbc";
+        public static final String TRIPLE_DES_CBC = "3des-cbc";
+
+        private Constants() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+
+        /**
+         * @param s A comma-separated list of ciphers - ignored if {@code null}/empty
+         * @return {@code true} if the {@link #NONE} cipher name appears in it
+         */
+        public static boolean isNoneCipherIncluded(String s) {
+            if (GenericUtils.isEmpty(s)) {
+                return false;
+            }
+            Matcher m = NONE_CIPHER_PATTERN.matcher(s);
+            return m.matches();
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
new file mode 100644
index 0000000..868e983
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
@@ -0,0 +1,89 @@
+/*
+ * 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.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Wrapper for a cryptographic cipher, used either for encryption
+ * or decryption.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Cipher extends CipherInformation {
+
+    enum Mode {
+        Encrypt, Decrypt
+    }
+
+    /**
+     * Initialize the cipher for encryption or decryption with
+     * the given key and initialization vector
+     *
+     * @param mode Encrypt/Decrypt initialization
+     * @param key  Key bytes
+     * @param iv   Initialization vector bytes
+     * @throws Exception If failed to initialize
+     */
+    void init(Mode mode, byte[] key, byte[] iv) throws Exception;
+
+    /**
+     * Performs in-place encryption or decryption on the given data.
+     *
+     * @param input The input/output bytes
+     * @throws Exception If failed to execute
+     * @see #update(byte[], int, int)
+     */
+    default void update(byte[] input) throws Exception {
+        update(input, 0, NumberUtils.length(input));
+    }
+
+    /**
+     * Performs in-place encryption or decryption on the given data.
+     *
+     * @param input       The input/output bytes
+     * @param inputOffset The offset of the data in the data buffer
+     * @param inputLen    The number of bytes to update - starting at the given offset
+     * @throws Exception If failed to execute
+     */
+    void update(byte[] input, int inputOffset, int inputLen) throws Exception;
+
+    /**
+     * @param xform The full cipher transformation - e.g., AES/CBC/NoPadding -
+     * never {@code null}/empty
+     * @param keyLength The required key length in bits - always positive
+     * @return {@code true} if the cipher transformation <U>and</U> required
+     * key length are supported
+     * @see javax.crypto.Cipher#getMaxAllowedKeyLength(String)
+     */
+    static boolean checkSupported(String xform, int keyLength) {
+        ValidateUtils.checkNotNullAndNotEmpty(xform, "No transformation");
+        if (keyLength <= 0) {
+            throw new IllegalArgumentException("Bad key length (" + keyLength + ") for cipher=" + xform);
+        }
+
+        try {
+            int maxKeyLength = javax.crypto.Cipher.getMaxAllowedKeyLength(xform);
+            return maxKeyLength >= keyLength;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
new file mode 100644
index 0000000..36909f3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface CipherFactory extends BuiltinFactory<Cipher>, CipherInformation {
+    // nothing extra
+}
+//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
new file mode 100644
index 0000000..f17fd16
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CipherInformation {
+    /**
+     * @return The cipher's algorithm
+     */
+    String getAlgorithm();
+
+    /**
+     * @return The actual transformation used - e.g., AES/CBC/NoPadding
+     */
+    String getTransformation();
+
+    /**
+     * @return Size of the initialization vector (in bytes)
+     */
+    int getIVSize();
+
+    /**
+     * @return The block size (in bytes) for this cipher
+     */
+    int getBlockSize();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
new file mode 100644
index 0000000..15b6e9f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+/**
+ * Represents a no-op cipher.
+ * This cipher can not really be used during authentication and should only
+ * be used after, so that authentication remains secured, but not the remaining
+ * of the exchanges.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CipherNone implements Cipher {
+    public CipherNone() {
+        super();
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return "none";
+    }
+
+    @Override
+    public String getTransformation() {
+        return "none";
+    }
+
+    @Override
+    public int getIVSize() {
+        return 8;   // dummy
+    }
+
+    @Override
+    public int getBlockSize() {
+        return 16;  // dummy
+    }
+
+    @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
+    }
+}