http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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 c26a2d1..46f34a1 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 @@ -19,25 +19,11 @@ package org.apache.sshd.common.config; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StreamCorruptedException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.OpenOption; -import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Properties; -import java.util.concurrent.TimeUnit; import java.util.function.Function; import org.apache.sshd.common.BuiltinFactory; @@ -52,17 +38,12 @@ import org.apache.sshd.common.helpers.AbstractFactoryManager; import org.apache.sshd.common.kex.BuiltinDHFactories; import org.apache.sshd.common.kex.DHFactory; import org.apache.sshd.common.kex.KeyExchange; -import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.mac.BuiltinMacs; import org.apache.sshd.common.mac.Mac; import org.apache.sshd.common.signature.BuiltinSignatures; import org.apache.sshd.common.signature.Signature; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseReader; -import org.apache.sshd.common.util.net.SshdSocketAddress; /** * Reads and interprets some useful configurations from an OpenSSH @@ -72,187 +53,10 @@ import org.apache.sshd.common.util.net.SshdSocketAddress; * @see <a href="https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5">ssh_config(5)</a> */ public final class SshConfigFileReader { - - public static final char COMMENT_CHAR = '#'; - - public static final String COMPRESSION_PROP = "Compression"; - public static final String DEFAULT_COMPRESSION = CompressionConfigValue.NO.getName(); - public static final String MAX_SESSIONS_CONFIG_PROP = "MaxSessions"; - public static final int DEFAULT_MAX_SESSIONS = 10; - public static final String PASSWORD_AUTH_CONFIG_PROP = "PasswordAuthentication"; - public static final String DEFAULT_PASSWORD_AUTH = "no"; - public static final boolean DEFAULT_PASSWORD_AUTH_VALUE = parseBooleanValue(DEFAULT_PASSWORD_AUTH); - public static final String LISTEN_ADDRESS_CONFIG_PROP = "ListenAddress"; - public static final String DEFAULT_BIND_ADDRESS = SshdSocketAddress.IPV4_ANYADDR; - public static final String PORT_CONFIG_PROP = "Port"; - public static final int DEFAULT_PORT = 22; - public static final String KEEP_ALIVE_CONFIG_PROP = "TCPKeepAlive"; - public static final boolean DEFAULT_KEEP_ALIVE = true; - public static final String USE_DNS_CONFIG_PROP = "UseDNS"; - // NOTE: the usual default is TRUE - public static final boolean DEFAULT_USE_DNS = true; - public static final String PUBKEY_AUTH_CONFIG_PROP = "PubkeyAuthentication"; - public static final String DEFAULT_PUBKEY_AUTH = "yes"; - public static final boolean DEFAULT_PUBKEY_AUTH_VALUE = parseBooleanValue(DEFAULT_PUBKEY_AUTH); - public static final String AUTH_KEYS_FILE_CONFIG_PROP = "AuthorizedKeysFile"; - public static final String MAX_AUTH_TRIES_CONFIG_PROP = "MaxAuthTries"; - public static final int DEFAULT_MAX_AUTH_TRIES = 6; - public static final String MAX_STARTUPS_CONFIG_PROP = "MaxStartups"; - public static final int DEFAULT_MAX_STARTUPS = 10; - public static final String LOGIN_GRACE_TIME_CONFIG_PROP = "LoginGraceTime"; - public static final long DEFAULT_LOGIN_GRACE_TIME = TimeUnit.SECONDS.toMillis(120); - public static final String KEY_REGENERATE_INTERVAL_CONFIG_PROP = "KeyRegenerationInterval"; - public static final long DEFAULT_REKEY_TIME_LIMIT = TimeUnit.HOURS.toMillis(1L); - // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html - public static final String CIPHERS_CONFIG_PROP = "Ciphers"; - public static final String DEFAULT_CIPHERS = - "aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour"; - // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html - public static final String MACS_CONFIG_PROP = "MACs"; - public static final String DEFAULT_MACS = - "hmac-md5,hmac-sha1,[email protected],hmac-ripemd160,hmac-sha1-96,hmac-md5-96,hmac-sha2-256,hmac-sha2-256-96,hmac-sha2-512,hmac-sha2-512-96"; - // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html - public static final String KEX_ALGORITHMS_CONFIG_PROP = "KexAlgorithms"; - public static final String DEFAULT_KEX_ALGORITHMS = - "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521" - + "," + "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1" - // RFC-8268 groups - + "," + "diffie-hellman-group18-sha512,diffie-hellman-group17-sha512" - + "," + "diffie-hellman-group16-sha512,diffie-hellman-group15-sha512" - + "," + "diffie-hellman-group14-sha256" - // Legacy groups - + "," + "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"; - // see http://linux.die.net/man/5/ssh_config - public static final String HOST_KEY_ALGORITHMS_CONFIG_PROP = "HostKeyAlgorithms"; - // see https://tools.ietf.org/html/rfc5656 - public static final String DEFAULT_HOST_KEY_ALGORITHMS = - KeyPairProvider.SSH_RSA + "," + KeyPairProvider.SSH_DSS; - // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html - public static final String LOG_LEVEL_CONFIG_PROP = "LogLevel"; - public static final LogLevelValue DEFAULT_LOG_LEVEL = LogLevelValue.INFO; - // see https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5 - public static final String SYSLOG_FACILITY_CONFIG_PROP = "SyslogFacility"; - public static final SyslogFacilityValue DEFAULT_SYSLOG_FACILITY = SyslogFacilityValue.AUTH; - public static final String SUBSYSTEM_CONFIG_PROP = "Subsystem"; - private SshConfigFileReader() { throw new UnsupportedOperationException("No instance allowed"); } - public static Properties readConfigFile(File file) throws IOException { - return readConfigFile(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS); - } - - public static Properties readConfigFile(Path path, OpenOption... options) throws IOException { - try (InputStream input = Files.newInputStream(path, options)) { - return readConfigFile(input, true); - } - } - - public static Properties readConfigFile(URL url) throws IOException { - try (InputStream input = url.openStream()) { - return readConfigFile(input, true); - } - } - - public static Properties readConfigFile(String path) throws IOException { - try (InputStream input = new FileInputStream(path)) { - return readConfigFile(input, true); - } - } - - public static Properties readConfigFile(InputStream input, boolean okToClose) throws IOException { - try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(input, okToClose), StandardCharsets.UTF_8)) { - return readConfigFile(reader, true); - } - } - - public static Properties readConfigFile(Reader reader, boolean okToClose) throws IOException { - try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(reader, okToClose))) { - return readConfigFile(buf); - } - } - - /** - * Reads the configuration file contents into a {@link Properties} instance. - * <B>Note:</B> multiple keys value are concatenated using a comma - it is up to - * the caller to know which keys are expected to have multiple values and handle - * the split accordingly - * - * @param rdr The {@link BufferedReader} for reading the file - * @return The read properties - * @throws IOException If failed to read or malformed content - */ - public static Properties readConfigFile(BufferedReader rdr) throws IOException { - Properties props = new Properties(); - int lineNumber = 1; - for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) { - line = GenericUtils.replaceWhitespaceAndTrim(line); - if (GenericUtils.isEmpty(line)) { - continue; - } - - int pos = line.indexOf(COMMENT_CHAR); - if (pos == 0) { - continue; - } - - if (pos > 0) { - line = line.substring(0, pos); - line = line.trim(); - } - - /* - * Some options use '=', others use ' ' - try both - * NOTE: we do not validate the format for each option separately - */ - pos = line.indexOf(' '); - if (pos < 0) { - pos = line.indexOf('='); - } - - if (pos < 0) { - throw new StreamCorruptedException("No delimiter at line " + lineNumber + ": " + line); - } - - String key = line.substring(0, pos); - String value = line.substring(pos + 1).trim(); - // see if need to concatenate multi-valued keys - String prev = props.getProperty(key); - if (!GenericUtils.isEmpty(prev)) { - value = prev + "," + value; - } - - props.setProperty(key, value); - } - - return props; - } - - /** - * @param v Checks if the value is "yes", "y" - * or "on" or "true". - * @return The result - <B>Note:</B> {@code null}/empty values are - * interpreted as {@code false} - */ - public static boolean parseBooleanValue(String v) { - return "yes".equalsIgnoreCase(v) - || "y".equalsIgnoreCase(v) - || "on".equalsIgnoreCase(v) - || "true".equalsIgnoreCase(v); - } - - /** - * Returns a "yes" or "no" value based on the input - * parameter - * - * @param flag The required state - * @return "yes" if {@code true}, "no" otherwise - */ - public static String yesNoValueOf(boolean flag) { - return flag ? "yes" : "no"; - } - /** * @param props The {@link PropertyResolver} - ignored if {@code null}/empty * @return A {@code ParseResult} of all the {@link NamedFactory}-ies @@ -261,11 +65,12 @@ public final class SshConfigFileReader { * is the same as the original order - bar the unknown ciphers. * <B>Note:</B> it is up to caller to ensure that the lists do not * contain duplicates - * @see #CIPHERS_CONFIG_PROP + * @see ConfigFileReaderSupport#CIPHERS_CONFIG_PROP CIPHERS_CONFIG_PROP * @see BuiltinCiphers#parseCiphersList(String) */ public static BuiltinCiphers.ParseResult getCiphers(PropertyResolver props) { - return BuiltinCiphers.parseCiphersList((props == null) ? null : props.getString(CIPHERS_CONFIG_PROP)); + return BuiltinCiphers.parseCiphersList( + (props == null) ? null : props.getString(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP)); } /** @@ -276,11 +81,12 @@ public final class SshConfigFileReader { * is the same as the original order - bar the unknown MACs. * <B>Note:</B> it is up to caller to ensure that the list does not * contain duplicates - * @see #MACS_CONFIG_PROP + * @see ConfigFileReaderSupport#MACS_CONFIG_PROP MACS_CONFIG_PROP * @see BuiltinMacs#parseMacsList(String) */ public static BuiltinMacs.ParseResult getMacs(PropertyResolver props) { - return BuiltinMacs.parseMacsList((props == null) ? null : props.getString(MACS_CONFIG_PROP)); + return BuiltinMacs.parseMacsList( + (props == null) ? null : props.getString(ConfigFileReaderSupport.MACS_CONFIG_PROP)); } /** @@ -290,11 +96,12 @@ public final class SshConfigFileReader { * unknown name is <U>ignored</U>. The order of the returned result is the * same as the original order - bar the unknown signatures. <B>Note:</B> it * is up to caller to ensure that the list does not contain duplicates - * @see #HOST_KEY_ALGORITHMS_CONFIG_PROP + * @see ConfigFileReaderSupport#HOST_KEY_ALGORITHMS_CONFIG_PROP HOST_KEY_ALGORITHMS_CONFIG_PROP * @see BuiltinSignatures#parseSignatureList(String) */ public static BuiltinSignatures.ParseResult getSignatures(PropertyResolver props) { - return BuiltinSignatures.parseSignatureList((props == null) ? null : props.getString(HOST_KEY_ALGORITHMS_CONFIG_PROP)); + return BuiltinSignatures.parseSignatureList( + (props == null) ? null : props.getString(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP)); } /** @@ -304,30 +111,33 @@ public final class SshConfigFileReader { * unknown name is <U>ignored</U>. The order of the returned result is the * same as the original order - bar the unknown ones. <B>Note:</B> it is * up to caller to ensure that the list does not contain duplicates - * @see #KEX_ALGORITHMS_CONFIG_PROP + * @see ConfigFileReaderSupport#KEX_ALGORITHMS_CONFIG_PROP KEX_ALGORITHMS_CONFIG_PROP * @see BuiltinDHFactories#parseDHFactoriesList(String) */ public static BuiltinDHFactories.ParseResult getKexFactories(PropertyResolver props) { - return BuiltinDHFactories.parseDHFactoriesList((props == null) ? null : props.getString(KEX_ALGORITHMS_CONFIG_PROP)); + return BuiltinDHFactories.parseDHFactoriesList( + (props == null) ? null : props.getString(ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP)); } /** * @param props The {@link PropertyResolver} - ignored if {@code null}/empty * @return The matching {@link NamedFactory} for the configured value. * {@code null} if no configuration or unknown name specified + * @see ConfigFileReaderSupport#COMPRESSION_PROP COMPRESSION_PROP */ public static CompressionFactory getCompression(PropertyResolver props) { - return CompressionConfigValue.fromName((props == null) ? null : props.getString(COMPRESSION_PROP)); + return CompressionConfigValue.fromName( + (props == null) ? null : props.getString(ConfigFileReaderSupport.COMPRESSION_PROP)); } /** * <P>Configures an {@link AbstractFactoryManager} with the values read from * some configuration. Currently it configures:</P> * <UL> - * <LI>The {@link Cipher}s - via the {@link #CIPHERS_CONFIG_PROP}</LI> - * <LI>The {@link Mac}s - via the {@link #MACS_CONFIG_PROP}</LI> - * <LI>The {@link Signature}s - via the {@link #HOST_KEY_ALGORITHMS_CONFIG_PROP}</LI> - * <LI>The {@link Compression} - via the {@link #COMPRESSION_PROP}</LI> + * <LI>The {@link Cipher}s - via the {@link ConfigFileReaderSupport#CIPHERS_CONFIG_PROP}</LI> + * <LI>The {@link Mac}s - via the {@link ConfigFileReaderSupport#MACS_CONFIG_PROP}</LI> + * <LI>The {@link Signature}s - via the {@link ConfigFileReaderSupport#HOST_KEY_ALGORITHMS_CONFIG_PROP}</LI> + * <LI>The {@link Compression} - via the {@link ConfigFileReaderSupport#COMPRESSION_PROP}</LI> * </UL> * * @param <M> The generic factory manager @@ -342,7 +152,8 @@ public final class SshConfigFileReader { * or unsupported values there is an empty configuration exception is thrown * @return The configured manager */ - public static <M extends AbstractFactoryManager> M configure(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configure( + M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { configureCiphers(manager, props, lenient, ignoreUnsupported); configureSignatures(manager, props, lenient, ignoreUnsupported); configureMacs(manager, props, lenient, ignoreUnsupported); @@ -353,10 +164,13 @@ public final class SshConfigFileReader { public static <M extends AbstractFactoryManager> M configureCiphers(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); - return configureCiphers(manager, props.getStringProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient, ignoreUnsupported); + return configureCiphers(manager, + props.getStringProperty(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_CIPHERS), + lenient, ignoreUnsupported); } - public static <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureCiphers( + M manager, String value, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(manager, "No manager to configure"); BuiltinCiphers.ParseResult result = BuiltinCiphers.parseCiphersList(value); @@ -371,10 +185,13 @@ public final class SshConfigFileReader { public static <M extends AbstractFactoryManager> M configureSignatures(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); - return configureSignatures(manager, props.getStringProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient, ignoreUnsupported); + return configureSignatures(manager, + props.getStringProperty(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS), + lenient, ignoreUnsupported); } - public static <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureSignatures( + M manager, String value, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(manager, "No manager to configure"); BuiltinSignatures.ParseResult result = BuiltinSignatures.parseSignatureList(value); @@ -387,12 +204,16 @@ public final class SshConfigFileReader { return manager; } - public static <M extends AbstractFactoryManager> M configureMacs(M manager, PropertyResolver resolver, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureMacs( + M manager, PropertyResolver resolver, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(resolver, "No properties to configure"); - return configureMacs(manager, resolver.getStringProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient, ignoreUnsupported); + return configureMacs(manager, + resolver.getStringProperty(ConfigFileReaderSupport.MACS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_MACS), + lenient, ignoreUnsupported); } - public static <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureMacs( + M manager, String value, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(manager, "No manager to configure"); BuiltinMacs.ParseResult result = BuiltinMacs.parseMacsList(value); @@ -417,13 +238,15 @@ public final class SshConfigFileReader { * 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 + * @see ConfigFileReaderSupport#KEX_ALGORITHMS_CONFIG_PROP KEX_ALGORITHMS_CONFIG_PROP + * @see ConfigFileReaderSupport#DEFAULT_KEX_ALGORITHMS DEFAULT_KEX_ALGORITHMS */ public static <M extends AbstractFactoryManager> M configureKeyExchanges( M manager, PropertyResolver props, boolean lenient, Function<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); - return configureKeyExchanges(manager, props.getStringProperty(KEX_ALGORITHMS_CONFIG_PROP, DEFAULT_KEX_ALGORITHMS), lenient, xformer, ignoreUnsupported); + return configureKeyExchanges(manager, + props.getStringProperty(ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_KEX_ALGORITHMS), + lenient, xformer, ignoreUnsupported); } public static <M extends AbstractFactoryManager> M configureKeyExchanges( @@ -454,11 +277,12 @@ public final class SshConfigFileReader { * @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 <M extends AbstractFactoryManager> M configureCompression(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureCompression( + M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(manager, "No manager to configure"); Objects.requireNonNull(props, "No properties to configure"); - String value = props.getStringProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION); + String value = props.getStringProperty(ConfigFileReaderSupport.COMPRESSION_PROP, ConfigFileReaderSupport.DEFAULT_COMPRESSION); CompressionFactory factory = CompressionConfigValue.fromName(value); ValidateUtils.checkTrue(lenient || (factory != null), "Unsupported compression value: %s", value); if ((factory != null) && factory.isSupported()) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java deleted file mode 100644 index f52ae36..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.config; - -import java.util.Collections; -import java.util.EnumSet; -import java.util.Set; - -import org.apache.sshd.common.util.GenericUtils; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - * @see <A HREF="https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5"><I>SyslogFacility</I> configuration value</A> - */ -public enum SyslogFacilityValue { - DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7; - - public static final Set<SyslogFacilityValue> VALUES = - Collections.unmodifiableSet(EnumSet.allOf(SyslogFacilityValue.class)); - - public static SyslogFacilityValue fromName(String n) { - if (GenericUtils.isEmpty(n)) { - return null; - } - - for (SyslogFacilityValue f : VALUES) { - if (n.equalsIgnoreCase(f.name())) { - return f; - } - } - - return null; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java b/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java deleted file mode 100644 index 222cf84..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.config; - -import java.util.Collections; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import org.apache.sshd.common.util.GenericUtils; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - * @see <A HREF="http://unixhelp.ed.ac.uk/CGI/man-cgi?sshd_config+5">Time formats for SSH configuration values</A> - */ -public enum TimeValueConfig { - SECONDS('s', 'S', TimeUnit.SECONDS.toMillis(1L)), - MINUTES('m', 'M', TimeUnit.MINUTES.toMillis(1L)), - HOURS('h', 'H', TimeUnit.HOURS.toMillis(1L)), - DAYS('d', 'D', TimeUnit.DAYS.toMillis(1L)), - WEEKS('w', 'W', TimeUnit.DAYS.toMillis(7L)); - - public static final Set<TimeValueConfig> VALUES = - Collections.unmodifiableSet(EnumSet.allOf(TimeValueConfig.class)); - - private final char loChar; - private final char hiChar; - private final long interval; - - TimeValueConfig(char lo, char hi, long interval) { - loChar = lo; - hiChar = hi; - this.interval = interval; - } - - public final char getLowerCaseValue() { - return loChar; - } - - public final char getUpperCaseValue() { - return hiChar; - } - - public final long getInterval() { - return interval; - } - - public static TimeValueConfig fromValueChar(char ch) { - if ((ch <= ' ') || (ch >= 0x7F)) { - return null; - } - - for (TimeValueConfig v : VALUES) { - if ((v.getLowerCaseValue() == ch) || (v.getUpperCaseValue() == ch)) { - return v; - } - } - - return null; - } - - /** - * @param s A time specification - * @return The specified duration in milliseconds - * @see #parse(String) - * @see #durationOf(Map) - */ - public static long durationOf(String s) { - Map<TimeValueConfig, Long> spec = parse(s); - return durationOf(spec); - } - - /** - * @param s An input time specification containing possibly mixed numbers - * and units - e.g., {@code 3h10m} to indicate 3 hours and 10 minutes - * @return A {@link Map} specifying for each time unit its count - * @throws NumberFormatException If bad numbers found - e.g., negative counts - * @throws IllegalArgumentException If bad format - e.g., unknown unit - */ - public static Map<TimeValueConfig, Long> parse(String s) throws IllegalArgumentException { - if (GenericUtils.isEmpty(s)) { - return Collections.emptyMap(); - } - - int lastPos = 0; - Map<TimeValueConfig, Long> spec = new EnumMap<>(TimeValueConfig.class); - for (int curPos = 0; curPos < s.length(); curPos++) { - char ch = s.charAt(curPos); - if ((ch >= '0') && (ch <= '9')) { - continue; - } - - if (curPos <= lastPos) { - throw new IllegalArgumentException("parse(" + s + ") missing count value at index=" + curPos); - } - - TimeValueConfig c = fromValueChar(ch); - if (c == null) { - throw new IllegalArgumentException("parse(" + s + ") unknown time value character: '" + ch + "'"); - } - - String v = s.substring(lastPos, curPos); - long count = Long.parseLong(v); - if (count < 0L) { - throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for " + c.name()); - } - - Long prev = spec.put(c, count); - if (prev != null) { - throw new IllegalArgumentException("parse(" + s + ") " + c.name() + " value re-specified: current=" + count + ", previous=" + prev); - } - - lastPos = curPos + 1; - if (lastPos >= s.length()) { - break; - } - } - - if (lastPos < s.length()) { - String v = s.substring(lastPos); - long count = Long.parseLong(v); - if (count < 0L) { - throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for last component"); - } - - Long prev = spec.put(SECONDS, count); - if (prev != null) { - throw new IllegalArgumentException("parse(" + s + ") last component (" + SECONDS.name() + ") value re-specified: current=" + count + ", previous=" + prev); - } - } - - return spec; - } - - /** - * @param spec The {@link Map} specifying the count for each {@link TimeValueConfig} - * @return The total duration in milliseconds - * @throws IllegalArgumentException If negative count for a time unit - */ - public static long durationOf(Map<TimeValueConfig, ? extends Number> spec) throws IllegalArgumentException { - if (GenericUtils.isEmpty(spec)) { - return -1L; - } - - long total = 0L; - // Cannot use forEach because the total value is not effectively final - for (Map.Entry<TimeValueConfig, ? extends Number> se : spec.entrySet()) { - TimeValueConfig v = se.getKey(); - Number c = se.getValue(); - long factor = c.longValue(); - if (factor < 0L) { - throw new IllegalArgumentException("valueOf(" + spec + ") bad factor (" + c + ") for " + v.name()); - } - - long added = v.getInterval() * factor; - total += added; - } - - return total; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/VersionProperties.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/VersionProperties.java b/sshd-core/src/main/java/org/apache/sshd/common/config/VersionProperties.java deleted file mode 100644 index 0e351a8..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/VersionProperties.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.config; - -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.Collections; -import java.util.NavigableMap; -import java.util.Properties; -import java.util.TreeMap; - -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.threads.ThreadUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public final class VersionProperties { - private static final class LazyVersionPropertiesHolder { - private static final NavigableMap<String, String> PROPERTIES = - Collections.unmodifiableNavigableMap(loadVersionProperties(LazyVersionPropertiesHolder.class)); - - private LazyVersionPropertiesHolder() { - throw new UnsupportedOperationException("No instance allowed"); - } - - private static NavigableMap<String, String> loadVersionProperties(Class<?> anchor) { - return loadVersionProperties(anchor, ThreadUtils.resolveDefaultClassLoader(anchor)); - } - - private static NavigableMap<String, String> loadVersionProperties(Class<?> anchor, ClassLoader loader) { - NavigableMap<String, String> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - try { - InputStream input = loader.getResourceAsStream("org/apache/sshd/sshd-version.properties"); - if (input == null) { - throw new FileNotFoundException("Version resource does not exist"); - } - - Properties props = new Properties(); - try { - props.load(input); - } finally { - input.close(); - } - - for (String key : props.stringPropertyNames()) { - String propValue = props.getProperty(key); - String value = GenericUtils.trimToEmpty(propValue); - if (GenericUtils.isEmpty(value)) { - continue; // we have no need for empty values - } - - String prev = result.put(key, value); - if (prev != null) { - Logger log = LoggerFactory.getLogger(anchor); - log.warn("Multiple values for key=" + key + ": current=" + value + ", previous=" + prev); - } - } - } catch (Exception e) { - Logger log = LoggerFactory.getLogger(anchor); - log.warn("Failed (" + e.getClass().getSimpleName() + ") to load version properties: " + e.getMessage()); - } - - return result; - } - } - - private VersionProperties() { - throw new UnsupportedOperationException("No instance"); - } - - /** - * @return A case <u>insensitive</u> un-modifiable {@link NavigableMap} of the {@code sshd-version.properties} data - */ - @SuppressWarnings("synthetic-access") - public static NavigableMap<String, String> getVersionProperties() { - return LazyVersionPropertiesHolder.PROPERTIES; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java deleted file mode 100644 index a763903..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java +++ /dev/null @@ -1,492 +0,0 @@ -/* - * 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.config.keys; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StreamCorruptedException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.security.GeneralSecurityException; -import java.security.PublicKey; -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.NavigableMap; -import java.util.TreeMap; - -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseReader; -import org.apache.sshd.server.auth.pubkey.KeySetPublickeyAuthenticator; -import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator; -import org.apache.sshd.server.auth.pubkey.RejectAllPublickeyAuthenticator; - -/** - * Represents an entry in the user's {@code authorized_keys} file according - * to the <A HREF="http://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#.7E.2F.ssh.2Fauthorized_keys">OpenSSH format</A>. - * <B>Note:</B> {@code equals/hashCode} check only the key type and data - the - * comment and/or login options are not considered part of equality - * - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - * @see <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A> - */ -public class AuthorizedKeyEntry extends PublicKeyEntry { - public static final char BOOLEAN_OPTION_NEGATION_INDICATOR = '!'; - - private static final long serialVersionUID = -9007505285002809156L; - - private String comment; - // for options that have no value, "true" is used - private Map<String, String> loginOptions = Collections.emptyMap(); - - public AuthorizedKeyEntry() { - super(); - } - - public String getComment() { - return comment; - } - - public void setComment(String value) { - this.comment = value; - } - - public Map<String, String> getLoginOptions() { - return loginOptions; - } - - public void setLoginOptions(Map<String, String> value) { - if (value == null) { - this.loginOptions = Collections.emptyMap(); - } else { - this.loginOptions = value; - } - } - - @Override - public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException { - Map<String, String> options = getLoginOptions(); - if (!GenericUtils.isEmpty(options)) { - int index = 0; - // Cannot use forEach because the index value is not effectively final - for (Map.Entry<String, String> oe : options.entrySet()) { - String key = oe.getKey(); - String value = oe.getValue(); - if (index > 0) { - sb.append(','); - } - sb.append(key); - // TODO figure out a way to remember which options where quoted - // TODO figure out a way to remember which options had no value - if (!Boolean.TRUE.toString().equals(value)) { - sb.append('=').append(value); - } - index++; - } - - if (index > 0) { - sb.append(' '); - } - } - - PublicKey key = super.appendPublicKey(sb, fallbackResolver); - String kc = getComment(); - if (!GenericUtils.isEmpty(kc)) { - sb.append(' ').append(kc); - } - - return key; - } - - @Override // to avoid Findbugs[EQ_DOESNT_OVERRIDE_EQUALS] - public int hashCode() { - return super.hashCode(); - } - - @Override // to avoid Findbugs[EQ_DOESNT_OVERRIDE_EQUALS] - public boolean equals(Object obj) { - return super.equals(obj); - } - - @Override - public String toString() { - String entry = super.toString(); - String kc = getComment(); - Map<?, ?> ko = getLoginOptions(); - return (GenericUtils.isEmpty(ko) ? "" : ko.toString() + " ") - + entry - + (GenericUtils.isEmpty(kc) ? "" : " " + kc); - } - - public static PublickeyAuthenticator fromAuthorizedEntries(PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries) - throws IOException, GeneralSecurityException { - Collection<PublicKey> keys = resolveAuthorizedKeys(fallbackResolver, entries); - if (GenericUtils.isEmpty(keys)) { - return RejectAllPublickeyAuthenticator.INSTANCE; - } else { - return new KeySetPublickeyAuthenticator(keys); - } - } - - public static List<PublicKey> resolveAuthorizedKeys(PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries) - throws IOException, GeneralSecurityException { - if (GenericUtils.isEmpty(entries)) { - return Collections.emptyList(); - } - - List<PublicKey> keys = new ArrayList<>(entries.size()); - for (AuthorizedKeyEntry e : entries) { - PublicKey k = e.resolvePublicKey(fallbackResolver); - if (k != null) { - keys.add(k); - } - } - - return keys; - } - - /** - * Reads read the contents of an <code>authorized_keys</code> file - * - * @param url The {@link URL} to read from - * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there - * @throws IOException If failed to read or parse the entries - * @see #readAuthorizedKeys(InputStream, boolean) - */ - public static List<AuthorizedKeyEntry> readAuthorizedKeys(URL url) throws IOException { - try (InputStream in = url.openStream()) { - return readAuthorizedKeys(in, true); - } - } - - /** - * Reads read the contents of an <code>authorized_keys</code> file - * - * @param file The {@link File} to read from - * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there - * @throws IOException If failed to read or parse the entries - * @see #readAuthorizedKeys(InputStream, boolean) - */ - public static List<AuthorizedKeyEntry> readAuthorizedKeys(File file) throws IOException { - try (InputStream in = new FileInputStream(file)) { - return readAuthorizedKeys(in, true); - } - } - - /** - * Reads read the contents of an <code>authorized_keys</code> file - * - * @param path {@link Path} to read from - * @param options The {@link OpenOption}s to use - if unspecified then appropriate - * defaults assumed - * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there - * @throws IOException If failed to read or parse the entries - * @see #readAuthorizedKeys(InputStream, boolean) - * @see Files#newInputStream(Path, OpenOption...) - */ - public static List<AuthorizedKeyEntry> readAuthorizedKeys(Path path, OpenOption... options) throws IOException { - try (InputStream in = Files.newInputStream(path, options)) { - return readAuthorizedKeys(in, true); - } - } - - /** - * Reads read the contents of an <code>authorized_keys</code> file - * - * @param filePath The file path to read from - * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there - * @throws IOException If failed to read or parse the entries - * @see #readAuthorizedKeys(InputStream, boolean) - */ - public static List<AuthorizedKeyEntry> readAuthorizedKeys(String filePath) throws IOException { - try (InputStream in = new FileInputStream(filePath)) { - return readAuthorizedKeys(in, true); - } - } - - /** - * Reads read the contents of an <code>authorized_keys</code> file - * - * @param in The {@link InputStream} - * @param okToClose <code>true</code> if method may close the input stream - * regardless of whether successful or failed - * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there - * @throws IOException If failed to read or parse the entries - * @see #readAuthorizedKeys(Reader, boolean) - */ - public static List<AuthorizedKeyEntry> readAuthorizedKeys(InputStream in, boolean okToClose) throws IOException { - try (Reader rdr = new InputStreamReader(NoCloseInputStream.resolveInputStream(in, okToClose), StandardCharsets.UTF_8)) { - return readAuthorizedKeys(rdr, true); - } - } - - /** - * Reads read the contents of an <code>authorized_keys</code> file - * - * @param rdr The {@link Reader} - * @param okToClose <code>true</code> if method may close the input stream - * regardless of whether successful or failed - * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there - * @throws IOException If failed to read or parse the entries - * @see #readAuthorizedKeys(BufferedReader) - */ - public static List<AuthorizedKeyEntry> readAuthorizedKeys(Reader rdr, boolean okToClose) throws IOException { - try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) { - return readAuthorizedKeys(buf); - } - } - - /** - * @param rdr The {@link BufferedReader} to use to read the contents of - * an <code>authorized_keys</code> file - * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there - * @throws IOException If failed to read or parse the entries - * @see #parseAuthorizedKeyEntry(String) - */ - public static List<AuthorizedKeyEntry> readAuthorizedKeys(BufferedReader rdr) throws IOException { - List<AuthorizedKeyEntry> entries = null; - - for (String line = rdr.readLine(); line != null; line = rdr.readLine()) { - AuthorizedKeyEntry entry; - try { - entry = parseAuthorizedKeyEntry(line); - if (entry == null) { // null, empty or comment line - continue; - } - } catch (RuntimeException | Error e) { - throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")" - + " to parse key entry=" + line + ": " + e.getMessage()); - } - - if (entries == null) { - entries = new ArrayList<>(); - } - - entries.add(entry); - } - - if (entries == null) { - return Collections.emptyList(); - } else { - return entries; - } - } - - /** - * @param value Original line from an <code>authorized_keys</code> file - * @return {@link AuthorizedKeyEntry} or {@code null} if the line is - * {@code null}/empty or a comment line - * @throws IllegalArgumentException If failed to parse/decode the line - * @see #COMMENT_CHAR - */ - public static AuthorizedKeyEntry parseAuthorizedKeyEntry(String value) throws IllegalArgumentException { - String line = GenericUtils.replaceWhitespaceAndTrim(value); - if (GenericUtils.isEmpty(line) || (line.charAt(0) == COMMENT_CHAR) /* comment ? */) { - return null; - } - - int startPos = line.indexOf(' '); - if (startPos <= 0) { - throw new IllegalArgumentException("Bad format (no key data delimiter): " + line); - } - - int endPos = line.indexOf(' ', startPos + 1); - if (endPos <= startPos) { - endPos = line.length(); - } - - String keyType = line.substring(0, startPos); - PublicKeyEntryDecoder<?, ?> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType); - AuthorizedKeyEntry entry; - // assume this is due to the fact that it starts with login options - if (decoder == null) { - Map.Entry<String, String> comps = resolveEntryComponents(line); - entry = parseAuthorizedKeyEntry(comps.getValue()); - ValidateUtils.checkTrue(entry != null, "Bad format (no key data after login options): %s", line); - entry.setLoginOptions(parseLoginOptions(comps.getKey())); - } else { - String encData = (endPos < (line.length() - 1)) ? line.substring(0, endPos).trim() : line; - String comment = (endPos < (line.length() - 1)) ? line.substring(endPos + 1).trim() : null; - entry = parsePublicKeyEntry(new AuthorizedKeyEntry(), encData); - entry.setComment(comment); - } - - return entry; - } - - /** - * Parses a single line from an <code>authorized_keys</code> file that is <U>known</U> - * to contain login options and separates it to the options and the rest of the line. - * - * @param entryLine The line to be parsed - * @return A {@link SimpleImmutableEntry} representing the parsed data where key=login options part - * and value=rest of the data - {@code null} if no data in line or line starts with comment character - * @see <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A> - */ - public static SimpleImmutableEntry<String, String> resolveEntryComponents(String entryLine) { - String line = GenericUtils.replaceWhitespaceAndTrim(entryLine); - if (GenericUtils.isEmpty(line) || (line.charAt(0) == COMMENT_CHAR) /* comment ? */) { - return null; - } - - for (int lastPos = 0; lastPos < line.length();) { - int startPos = line.indexOf(' ', lastPos); - if (startPos < lastPos) { - throw new IllegalArgumentException("Bad format (no key data delimiter): " + line); - } - - int quotePos = line.indexOf('"', startPos + 1); - // If found quotes after the space then assume part of a login option - if (quotePos > startPos) { - lastPos = quotePos + 1; - continue; - } - - String loginOptions = line.substring(0, startPos).trim(); - String remainder = line.substring(startPos + 1).trim(); - return new SimpleImmutableEntry<>(loginOptions, remainder); - } - - throw new IllegalArgumentException("Bad format (no key data contents): " + line); - } - - /** - * <P> - * Parses login options line according to - * <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A> - * guidelines. <B>Note:</B> - * </P> - * - * <UL> - * <P><LI> - * Options that have a value are automatically stripped of any surrounding double quotes./ - * </LI></P> - * - * <P><LI> - * Options that have no value are marked as {@code true/false} - according - * to the {@link #BOOLEAN_OPTION_NEGATION_INDICATOR}. - * </LI></P> - * - * <P><LI> - * Options that appear multiple times are simply concatenated using comma as separator. - * </LI></P> - * </UL> - * - * @param options The options line to parse - ignored if {@code null}/empty/blank - * @return A {@link NavigableMap} where key=case <U>insensitive</U> option name and value=the parsed value. - * @see #addLoginOption(Map, String) addLoginOption - */ - public static NavigableMap<String, String> parseLoginOptions(String options) { - String line = GenericUtils.replaceWhitespaceAndTrim(options); - int len = GenericUtils.length(line); - if (len <= 0) { - return Collections.emptyNavigableMap(); - } - - NavigableMap<String, String> optsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - int lastPos = 0; - for (int curPos = 0; curPos < len; curPos++) { - int nextPos = line.indexOf(',', curPos); - if (nextPos < curPos) { - break; - } - - // check if "true" comma or one inside quotes - int quotePos = line.indexOf('"', curPos); - if ((quotePos >= lastPos) && (quotePos < nextPos)) { - nextPos = line.indexOf('"', quotePos + 1); - if (nextPos <= quotePos) { - throw new IllegalArgumentException("Bad format (imbalanced quoted command): " + line); - } - - // Make sure either comma or no more options follow the 2nd quote - for (nextPos++; nextPos < len; nextPos++) { - char ch = line.charAt(nextPos); - if (ch == ',') { - break; - } - - if (ch != ' ') { - throw new IllegalArgumentException("Bad format (incorrect list format): " + line); - } - } - } - - addLoginOption(optsMap, line.substring(lastPos, nextPos)); - lastPos = nextPos + 1; - curPos = lastPos; - } - - // Any leftovers at end of line ? - if (lastPos < len) { - addLoginOption(optsMap, line.substring(lastPos)); - } - - return optsMap; - } - - /** - * Parses and adds a new option to the options map. If a valued option is re-specified then - * its value(s) are concatenated using comma as separator. - * - * @param optsMap Options map to add to - * @param option The option data to parse - ignored if {@code null}/empty/blank - * @return The updated entry - {@code null} if no option updated in the map - * @throws IllegalStateException If a boolean option is re-specified - */ - public static SimpleImmutableEntry<String, String> addLoginOption(Map<String, String> optsMap, String option) { - String p = GenericUtils.trimToEmpty(option); - if (GenericUtils.isEmpty(p)) { - return null; - } - - int pos = p.indexOf('='); - String name = (pos < 0) ? p : GenericUtils.trimToEmpty(p.substring(0, pos)); - CharSequence value = (pos < 0) ? null : GenericUtils.trimToEmpty(p.substring(pos + 1)); - value = GenericUtils.stripQuotes(value); - if (value == null) { - value = Boolean.toString(name.charAt(0) != BOOLEAN_OPTION_NEGATION_INDICATOR); - } - - SimpleImmutableEntry<String, String> entry = new SimpleImmutableEntry<>(name, value.toString()); - String prev = optsMap.put(entry.getKey(), entry.getValue()); - if (prev != null) { - if (pos < 0) { - throw new IllegalStateException("Bad format (boolean option (" + name + ") re-specified): " + p); - } - optsMap.put(entry.getKey(), prev + "," + entry.getValue()); - } - - return entry; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java deleted file mode 100644 index 53b6b44..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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.config.keys; - -import java.security.Key; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.interfaces.DSAPrivateKey; -import java.security.interfaces.DSAPublicKey; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.util.Collections; -import java.util.EnumSet; -import java.util.Objects; -import java.util.Set; - -import org.apache.sshd.common.NamedResource; -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.security.SecurityUtils; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public enum BuiltinIdentities implements Identity { - RSA(Constants.RSA, RSAPublicKey.class, RSAPrivateKey.class), - DSA(Constants.DSA, DSAPublicKey.class, DSAPrivateKey.class), - ECDSA(Constants.ECDSA, KeyUtils.EC_ALGORITHM, ECPublicKey.class, ECPrivateKey.class) { - @Override - public boolean isSupported() { - return SecurityUtils.isECCSupported(); - } - }, - ED25119(Constants.ED25519, SecurityUtils.EDDSA, SecurityUtils.getEDDSAPublicKeyType(), SecurityUtils.getEDDSAPrivateKeyType()) { - @Override - public boolean isSupported() { - return SecurityUtils.isEDDSACurveSupported(); - } - }; - - public static final Set<BuiltinIdentities> VALUES = - Collections.unmodifiableSet(EnumSet.allOf(BuiltinIdentities.class)); - - public static final Set<String> NAMES = - Collections.unmodifiableSet( - GenericUtils.asSortedSet( - String.CASE_INSENSITIVE_ORDER, NamedResource.getNameList(VALUES))); - - private final String name; - private final String algorithm; - private final Class<? extends PublicKey> pubType; - private final Class<? extends PrivateKey> prvType; - - BuiltinIdentities(String type, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) { - this(type, type, pubType, prvType); - } - - BuiltinIdentities(String name, String algorithm, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) { - this.name = name.toLowerCase(); - this.algorithm = algorithm.toUpperCase(); - this.pubType = pubType; - this.prvType = prvType; - } - - @Override - public final String getName() { - return name; - } - - @Override - public boolean isSupported() { - return true; - } - - @Override - public String getAlgorithm() { - return algorithm; - } - - @Override - public final Class<? extends PublicKey> getPublicKeyType() { - return pubType; - } - - @Override - public final Class<? extends PrivateKey> getPrivateKeyType() { - return prvType; - } - - /** - * @param name The identity name - ignored if {@code null}/empty - * @return The matching {@link BuiltinIdentities} whose {@link #getName()} - * value matches case <U>insensitive</U> or {@code null} if no match found - */ - public static BuiltinIdentities fromName(String name) { - return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES); - } - - /** - * @param algorithm The algorithm - ignored if {@code null}/empty - * @return The matching {@link BuiltinIdentities} whose {@link #getAlgorithm()} - * value matches case <U>insensitive</U> or {@code null} if no match found - */ - public static BuiltinIdentities fromAlgorithm(String algorithm) { - if (GenericUtils.isEmpty(algorithm)) { - return null; - } - - for (BuiltinIdentities id : VALUES) { - if (algorithm.equalsIgnoreCase(id.getAlgorithm())) { - return id; - } - } - - return null; - } - - /** - * @param kp The {@link KeyPair} - ignored if {@code null} - * @return The matching {@link BuiltinIdentities} provided <U>both</U> - * public and public keys are of the same type - {@code null} if no - * match could be found - * @see #fromKey(Key) - */ - public static BuiltinIdentities fromKeyPair(KeyPair kp) { - if (kp == null) { - return null; - } - - BuiltinIdentities i1 = fromKey(kp.getPublic()); - BuiltinIdentities i2 = fromKey(kp.getPrivate()); - if (Objects.equals(i1, i2)) { - return i1; - } else { - return null; // some kind of mixed keys... - } - } - - /** - * @param key The {@link Key} instance - ignored if {@code null} - * @return The matching {@link BuiltinIdentities} whose either public or - * private key type matches the requested one or {@code null} if no match found - * @see #fromKeyType(Class) - */ - public static BuiltinIdentities fromKey(Key key) { - return fromKeyType((key == null) ? null : key.getClass()); - } - - /** - * @param clazz The key type - ignored if {@code null} or not - * a {@link Key} class - * @return The matching {@link BuiltinIdentities} whose either public or - * private key type matches the requested one or {@code null} if no match found - * @see #getPublicKeyType() - * @see #getPrivateKeyType() - */ - public static BuiltinIdentities fromKeyType(Class<?> clazz) { - if ((clazz == null) || (!Key.class.isAssignableFrom(clazz))) { - return null; - } - - for (BuiltinIdentities id : VALUES) { - Class<?> pubType = id.getPublicKeyType(); - Class<?> prvType = id.getPrivateKeyType(); - // Ignore placeholder classes (e.g., if ed25519 is not supported) - if ((prvType == null) || (pubType == null)) { - continue; - } - if ((prvType == PrivateKey.class) || (pubType == PublicKey.class)) { - continue; - } - if (pubType.isAssignableFrom(clazz) || prvType.isAssignableFrom(clazz)) { - return id; - } - } - - return null; - } - - /** - * Contains the names of the identities - */ - public static final class Constants { - public static final String RSA = KeyUtils.RSA_ALGORITHM; - public static final String DSA = KeyUtils.DSS_ALGORITHM; - public static final String ECDSA = "ECDSA"; - public static final String ED25519 = "ED25519"; - - private Constants() { - throw new UnsupportedOperationException("No instance allowed"); - } - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java deleted file mode 100644 index 064f75c..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.config.keys; - -import java.io.IOException; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -@FunctionalInterface -public interface FilePasswordProvider { - /** - * An "empty" provider that returns {@code null} - i.e., unprotected key file - */ - FilePasswordProvider EMPTY = resourceKey -> null; - - /** - * @param resourceKey The resource key representing the <U>private</U> - * file - * @return The password - if {@code null}/empty then no password is required - * @throws IOException if cannot resolve password - */ - String getPassword(String resourceKey) throws IOException; - - static FilePasswordProvider of(String password) { - return r -> password; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/Identity.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/Identity.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/Identity.java deleted file mode 100644 index eaec413..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/Identity.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.config.keys; - -import java.security.PrivateKey; -import java.security.PublicKey; - -import org.apache.sshd.common.NamedResource; -import org.apache.sshd.common.OptionalFeature; - -/** - * Represents an SSH key type - * - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public interface Identity extends NamedResource, OptionalFeature { - /** - * @return The key algorithm - e.g., RSA, DSA, EC - */ - String getAlgorithm(); - - Class<? extends PublicKey> getPublicKeyType(); - - Class<? extends PrivateKey> getPrivateKeyType(); -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java deleted file mode 100644 index d826821..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.config.keys; - -import java.security.PrivateKey; -import java.security.PublicKey; -import java.util.Collection; - -/** - * @param <PUB> Type of {@link PublicKey} - * @param <PRV> Type of {@link PrivateKey} - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public interface IdentityResourceLoader<PUB extends PublicKey, PRV extends PrivateKey> { - /** - * @return The {@link Class} of the {@link PublicKey} that is the result - * of decoding - */ - Class<PUB> getPublicKeyType(); - - /** - * @return The {@link Class} of the {@link PrivateKey} that matches the - * public one - */ - Class<PRV> getPrivateKeyType(); - - /** - * @return The {@link Collection} of {@code OpenSSH} key type names that - * are supported by this decoder - e.g., ECDSA keys have several curve names. - * <B>Caveat:</B> this collection may be un-modifiable... - */ - Collection<String> getSupportedTypeNames(); -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java deleted file mode 100644 index fbc3ce7..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.config.keys; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -import org.apache.sshd.common.keyprovider.KeyPairProvider; -import org.apache.sshd.common.keyprovider.MappedKeyPairProvider; -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.security.SecurityUtils; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public final class IdentityUtils { - private IdentityUtils() { - throw new UnsupportedOperationException("No instance"); - } - - private static final class LazyDefaultUserHomeFolderHolder { - private static final Path PATH = - Paths.get(ValidateUtils.checkNotNullAndNotEmpty(System.getProperty("user.home"), "No user home")) - .toAbsolutePath() - .normalize(); - - private LazyDefaultUserHomeFolderHolder() { - throw new UnsupportedOperationException("No instance allowed"); - } - } - - /** - * @return The {@link Path} to the currently running user home - */ - @SuppressWarnings("synthetic-access") - public static Path getUserHomeFolder() { - return LazyDefaultUserHomeFolderHolder.PATH; - } - - /** - * @param prefix The file name prefix - ignored if {@code null}/empty - * @param type The identity type - ignored if {@code null}/empty - * @param suffix The file name suffix - ignored if {@code null}/empty - * @return The identity file name or {@code null} if no name - */ - public static String getIdentityFileName(String prefix, String type, String suffix) { - if (GenericUtils.isEmpty(type)) { - return null; - } else { - return GenericUtils.trimToEmpty(prefix) - + type.toLowerCase() + GenericUtils.trimToEmpty(suffix); - } - } - - /** - * @param ids A {@link Map} of the loaded identities where key=the identity type, - * value=the matching {@link KeyPair} - ignored if {@code null}/empty - * @param supportedOnly If {@code true} then ignore identities that are not - * supported internally - * @return A {@link KeyPair} for the identities - {@code null} if no identities - * available (e.g., after filtering unsupported ones) - * @see BuiltinIdentities - */ - public static KeyPairProvider createKeyPairProvider(Map<String, KeyPair> ids, boolean supportedOnly) { - if (GenericUtils.isEmpty(ids)) { - return null; - } - - Map<String, KeyPair> pairsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - ids.forEach((type, kp) -> { - BuiltinIdentities id = BuiltinIdentities.fromName(type); - if (id == null) { - id = BuiltinIdentities.fromKeyPair(kp); - } - - if (supportedOnly && ((id == null) || (!id.isSupported()))) { - return; - } - - String keyType = KeyUtils.getKeyType(kp); - if (GenericUtils.isEmpty(keyType)) { - return; - } - - KeyPair prev = pairsMap.put(keyType, kp); - if (prev != null) { - return; // less of an offense if 2 pairs mapped to same key type - } - }); - - if (GenericUtils.isEmpty(pairsMap)) { - return null; - } else { - return new MappedKeyPairProvider(pairsMap); - } - } - - /** - * @param paths A {@link Map} of the identities where key=identity type (case - * <U>insensitive</U>), value=the {@link Path} of file with the identity key - * @param provider A {@link FilePasswordProvider} - may be {@code null} - * if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument - * to {@link FilePasswordProvider#getPassword(String)} is the path of the - * file whose key is to be loaded - * @param options The {@link OpenOption}s to use when reading the key data - * @return A {@link Map} of the identities where key=identity type (case - * <U>insensitive</U>), value=the {@link KeyPair} of the identity - * @throws IOException If failed to access the file system - * @throws GeneralSecurityException If failed to load the keys - * @see SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider) - */ - public static Map<String, KeyPair> loadIdentities(Map<String, ? extends Path> paths, FilePasswordProvider provider, OpenOption... options) - throws IOException, GeneralSecurityException { - if (GenericUtils.isEmpty(paths)) { - return Collections.emptyMap(); - } - - Map<String, KeyPair> ids = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - // Cannot use forEach because the potential for IOExceptions being thrown - for (Map.Entry<String, ? extends Path> pe : paths.entrySet()) { - String type = pe.getKey(); - Path path = pe.getValue(); - try (InputStream inputStream = Files.newInputStream(path, options)) { - KeyPair kp = SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider); - KeyPair prev = ids.put(type, kp); - ValidateUtils.checkTrue(prev == null, "Multiple keys for type=%s", type); - } - } - - return ids; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java deleted file mode 100644 index 4bfbea0..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * 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.config.keys; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigInteger; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.PrivateKey; -import java.security.PublicKey; - -import org.apache.sshd.common.util.io.IoUtils; - -/** - * @param <PUB> Type of {@link PublicKey} - * @param <PRV> Type of {@link PrivateKey} - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public interface KeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey> extends IdentityResourceLoader<PUB, PRV> { - /** - * @param keySize Key size in bits - * @return A {@link KeyPair} with the specified key size - * @throws GeneralSecurityException if unable to generate the pair - */ - default KeyPair generateKeyPair(int keySize) throws GeneralSecurityException { - KeyPairGenerator gen = getKeyPairGenerator(); - gen.initialize(keySize); - return gen.generateKeyPair(); - } - - /** - * @param kp The {@link KeyPair} to be cloned - ignored if {@code null} - * @return A cloned pair (or {@code null} if no original pair) - * @throws GeneralSecurityException If failed to clone - e.g., provided key - * pair does not contain keys of the expected type - * @see #getPublicKeyType() - * @see #getPrivateKeyType() - */ - default KeyPair cloneKeyPair(KeyPair kp) throws GeneralSecurityException { - if (kp == null) { - return null; - } - - PUB pubCloned = null; - PublicKey pubOriginal = kp.getPublic(); - Class<PUB> pubExpected = getPublicKeyType(); - if (pubOriginal != null) { - Class<?> orgType = pubOriginal.getClass(); - if (!pubExpected.isAssignableFrom(orgType)) { - throw new InvalidKeyException("Mismatched public key types: expected=" + pubExpected.getSimpleName() + ", actual=" + orgType.getSimpleName()); - } - - pubCloned = clonePublicKey(pubExpected.cast(pubOriginal)); - } - - PRV prvCloned = null; - PrivateKey prvOriginal = kp.getPrivate(); - Class<PRV> prvExpected = getPrivateKeyType(); - if (prvOriginal != null) { - Class<?> orgType = prvOriginal.getClass(); - if (!prvExpected.isAssignableFrom(orgType)) { - throw new InvalidKeyException("Mismatched private key types: expected=" + prvExpected.getSimpleName() + ", actual=" + orgType.getSimpleName()); - } - - prvCloned = clonePrivateKey(prvExpected.cast(prvOriginal)); - } - - return new KeyPair(pubCloned, prvCloned); - } - - /** - * @param key The {@link PublicKey} to clone - ignored if {@code null} - * @return The cloned key (or {@code null} if no original key) - * @throws GeneralSecurityException If failed to clone the key - */ - PUB clonePublicKey(PUB key) throws GeneralSecurityException; - - /** - * @param key The {@link PrivateKey} to clone - ignored if {@code null} - * @return The cloned key (or {@code null} if no original key) - * @throws GeneralSecurityException If failed to clone the key - */ - PRV clonePrivateKey(PRV key) throws GeneralSecurityException; - - /** - * @return A {@link KeyPairGenerator} suitable for this decoder - * @throws GeneralSecurityException If failed to create the generator - */ - KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException; - - /** - * @return A {@link KeyFactory} suitable for the specific decoder type - * @throws GeneralSecurityException If failed to create one - */ - KeyFactory getKeyFactoryInstance() throws GeneralSecurityException; - - static int encodeString(OutputStream s, String v) throws IOException { - return encodeString(s, v, StandardCharsets.UTF_8); - } - - static int encodeString(OutputStream s, String v, String charset) throws IOException { - return encodeString(s, v, Charset.forName(charset)); - } - - static int encodeString(OutputStream s, String v, Charset cs) throws IOException { - return writeRLEBytes(s, v.getBytes(cs)); - } - - static int encodeBigInt(OutputStream s, BigInteger v) throws IOException { - return writeRLEBytes(s, v.toByteArray()); - } - - static int writeRLEBytes(OutputStream s, byte... bytes) throws IOException { - return writeRLEBytes(s, bytes, 0, bytes.length); - } - - static int writeRLEBytes(OutputStream s, byte[] bytes, int off, int len) throws IOException { - byte[] lenBytes = encodeInt(s, len); - s.write(bytes, off, len); - return lenBytes.length + len; - } - - static byte[] encodeInt(OutputStream s, int v) throws IOException { - byte[] bytes = { - (byte) ((v >> 24) & 0xFF), - (byte) ((v >> 16) & 0xFF), - (byte) ((v >> 8) & 0xFF), - (byte) (v & 0xFF) - }; - s.write(bytes); - return bytes; - } - - static String decodeString(InputStream s) throws IOException { - return decodeString(s, StandardCharsets.UTF_8); - } - - static String decodeString(InputStream s, String charset) throws IOException { - return decodeString(s, Charset.forName(charset)); - } - - static String decodeString(InputStream s, Charset cs) throws IOException { - byte[] bytes = readRLEBytes(s); - return new String(bytes, cs); - } - - static BigInteger decodeBigInt(InputStream s) throws IOException { - return new BigInteger(readRLEBytes(s)); - } - - static byte[] readRLEBytes(InputStream s) throws IOException { - int len = decodeInt(s); - byte[] bytes = new byte[len]; - IoUtils.readFully(s, bytes); - return bytes; - } - - static int decodeInt(InputStream s) throws IOException { - byte[] bytes = {0, 0, 0, 0}; - IoUtils.readFully(s, bytes); - return ((bytes[0] & 0xFF) << 24) - | ((bytes[1] & 0xFF) << 16) - | ((bytes[2] & 0xFF) << 8) - | (bytes[3] & 0xFF); - } -}
